comparison mupdf-source/thirdparty/tesseract/src/ccstruct/otsuthr.cpp @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /**********************************************************************
2 * File: otsuthr.cpp
3 * Description: Simple Otsu thresholding for binarizing images.
4 * Author: Ray Smith
5 *
6 * (C) Copyright 2008, Google Inc.
7 ** Licensed under the Apache License, Version 2.0 (the "License");
8 ** you may not use this file except in compliance with the License.
9 ** You may obtain a copy of the License at
10 ** http://www.apache.org/licenses/LICENSE-2.0
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 *
17 **********************************************************************/
18
19 #include "otsuthr.h"
20
21 #include <allheaders.h>
22 #include <cstring>
23 #include "helpers.h"
24
25 namespace tesseract {
26
27 // Computes the Otsu threshold(s) for the given image rectangle, making one
28 // for each channel. Each channel is always one byte per pixel.
29 // Returns an array of threshold values and an array of hi_values, such
30 // that a pixel value >threshold[channel] is considered foreground if
31 // hi_values[channel] is 0 or background if 1. A hi_value of -1 indicates
32 // that there is no apparent foreground. At least one hi_value will not be -1.
33 // The return value is the number of channels in the input image, being
34 // the size of the output thresholds and hi_values arrays.
35 int OtsuThreshold(Image src_pix, int left, int top, int width, int height, std::vector<int> &thresholds,
36 std::vector<int> &hi_values) {
37 int num_channels = pixGetDepth(src_pix) / 8;
38 // Of all channels with no good hi_value, keep the best so we can always
39 // produce at least one answer.
40 int best_hi_value = 1;
41 int best_hi_index = 0;
42 bool any_good_hivalue = false;
43 double best_hi_dist = 0.0;
44 thresholds.resize(num_channels);
45 hi_values.resize(num_channels);
46
47 for (int ch = 0; ch < num_channels; ++ch) {
48 thresholds[ch] = -1;
49 hi_values[ch] = -1;
50 // Compute the histogram of the image rectangle.
51 int histogram[kHistogramSize];
52 HistogramRect(src_pix, ch, left, top, width, height, histogram);
53 int H;
54 int best_omega_0;
55 int best_t = OtsuStats(histogram, &H, &best_omega_0);
56 if (best_omega_0 == 0 || best_omega_0 == H) {
57 // This channel is empty.
58 continue;
59 }
60 // To be a convincing foreground we must have a small fraction of H
61 // or to be a convincing background we must have a large fraction of H.
62 // In between we assume this channel contains no thresholding information.
63 int hi_value = best_omega_0 < H * 0.5;
64 thresholds[ch] = best_t;
65 if (best_omega_0 > H * 0.75) {
66 any_good_hivalue = true;
67 hi_values[ch] = 0;
68 } else if (best_omega_0 < H * 0.25) {
69 any_good_hivalue = true;
70 hi_values[ch] = 1;
71 } else {
72 // In case all channels are like this, keep the best of the bad lot.
73 double hi_dist = hi_value ? (H - best_omega_0) : best_omega_0;
74 if (hi_dist > best_hi_dist) {
75 best_hi_dist = hi_dist;
76 best_hi_value = hi_value;
77 best_hi_index = ch;
78 }
79 }
80 }
81
82 if (!any_good_hivalue) {
83 // Use the best of the ones that were not good enough.
84 hi_values[best_hi_index] = best_hi_value;
85 }
86 return num_channels;
87 }
88
89 // Computes the histogram for the given image rectangle, and the given
90 // single channel. Each channel is always one byte per pixel.
91 // Histogram is always a kHistogramSize(256) element array to count
92 // occurrences of each pixel value.
93 void HistogramRect(Image src_pix, int channel, int left, int top, int width, int height,
94 int *histogram) {
95 int num_channels = pixGetDepth(src_pix) / 8;
96 channel = ClipToRange(channel, 0, num_channels - 1);
97 int bottom = top + height;
98 memset(histogram, 0, sizeof(*histogram) * kHistogramSize);
99 int src_wpl = pixGetWpl(src_pix);
100 l_uint32 *srcdata = pixGetData(src_pix);
101 for (int y = top; y < bottom; ++y) {
102 const l_uint32 *linedata = srcdata + y * src_wpl;
103 for (int x = 0; x < width; ++x) {
104 int pixel = GET_DATA_BYTE(linedata, (x + left) * num_channels + channel);
105 ++histogram[pixel];
106 }
107 }
108 }
109
110 // Computes the Otsu threshold(s) for the given histogram.
111 // Also returns H = total count in histogram, and
112 // omega0 = count of histogram below threshold.
113 int OtsuStats(const int *histogram, int *H_out, int *omega0_out) {
114 int H = 0;
115 double mu_T = 0.0;
116 for (int i = 0; i < kHistogramSize; ++i) {
117 H += histogram[i];
118 mu_T += static_cast<double>(i) * histogram[i];
119 }
120
121 // Now maximize sig_sq_B over t.
122 // http://www.ctie.monash.edu.au/hargreave/Cornall_Terry_328.pdf
123 int best_t = -1;
124 int omega_0, omega_1;
125 int best_omega_0 = 0;
126 double best_sig_sq_B = 0.0;
127 double mu_0, mu_1, mu_t;
128 omega_0 = 0;
129 mu_t = 0.0;
130 for (int t = 0; t < kHistogramSize - 1; ++t) {
131 omega_0 += histogram[t];
132 mu_t += t * static_cast<double>(histogram[t]);
133 if (omega_0 == 0) {
134 continue;
135 }
136 omega_1 = H - omega_0;
137 if (omega_1 == 0) {
138 break;
139 }
140 mu_0 = mu_t / omega_0;
141 mu_1 = (mu_T - mu_t) / omega_1;
142 double sig_sq_B = mu_1 - mu_0;
143 sig_sq_B *= sig_sq_B * omega_0 * omega_1;
144 if (best_t < 0 || sig_sq_B > best_sig_sq_B) {
145 best_sig_sq_B = sig_sq_B;
146 best_t = t;
147 best_omega_0 = omega_0;
148 }
149 }
150 if (H_out != nullptr) {
151 *H_out = H;
152 }
153 if (omega0_out != nullptr) {
154 *omega0_out = best_omega_0;
155 }
156 return best_t;
157 }
158
159 } // namespace tesseract.