/src/tesseract/src/ccstruct/otsuthr.cpp
Line | Count | Source |
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 <cstring> |
22 | | #include "helpers.h" |
23 | | #include "image.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 | 0 | std::vector<int> &hi_values) { |
37 | 0 | 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 | 0 | int best_hi_value = 1; |
41 | 0 | int best_hi_index = 0; |
42 | 0 | bool any_good_hivalue = false; |
43 | 0 | double best_hi_dist = 0.0; |
44 | 0 | thresholds.resize(num_channels); |
45 | 0 | hi_values.resize(num_channels); |
46 | |
|
47 | 0 | for (int ch = 0; ch < num_channels; ++ch) { |
48 | 0 | thresholds[ch] = -1; |
49 | 0 | hi_values[ch] = -1; |
50 | | // Compute the histogram of the image rectangle. |
51 | 0 | int histogram[kHistogramSize]; |
52 | 0 | HistogramRect(src_pix, ch, left, top, width, height, histogram); |
53 | 0 | int H; |
54 | 0 | int best_omega_0; |
55 | 0 | int best_t = OtsuStats(histogram, &H, &best_omega_0); |
56 | 0 | if (best_omega_0 == 0 || best_omega_0 == H) { |
57 | | // This channel is empty. |
58 | 0 | continue; |
59 | 0 | } |
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 | 0 | int hi_value = best_omega_0 < H * 0.5; |
64 | 0 | thresholds[ch] = best_t; |
65 | 0 | if (best_omega_0 > H * 0.75) { |
66 | 0 | any_good_hivalue = true; |
67 | 0 | hi_values[ch] = 0; |
68 | 0 | } else if (best_omega_0 < H * 0.25) { |
69 | 0 | any_good_hivalue = true; |
70 | 0 | hi_values[ch] = 1; |
71 | 0 | } else { |
72 | | // In case all channels are like this, keep the best of the bad lot. |
73 | 0 | double hi_dist = hi_value ? (H - best_omega_0) : best_omega_0; |
74 | 0 | if (hi_dist > best_hi_dist) { |
75 | 0 | best_hi_dist = hi_dist; |
76 | 0 | best_hi_value = hi_value; |
77 | 0 | best_hi_index = ch; |
78 | 0 | } |
79 | 0 | } |
80 | 0 | } |
81 | |
|
82 | 0 | if (!any_good_hivalue) { |
83 | | // Use the best of the ones that were not good enough. |
84 | 0 | hi_values[best_hi_index] = best_hi_value; |
85 | 0 | } |
86 | 0 | return num_channels; |
87 | 0 | } |
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 | 0 | int *histogram) { |
95 | 0 | int num_channels = pixGetDepth(src_pix) / 8; |
96 | 0 | channel = ClipToRange(channel, 0, num_channels - 1); |
97 | 0 | int bottom = top + height; |
98 | 0 | memset(histogram, 0, sizeof(*histogram) * kHistogramSize); |
99 | 0 | int src_wpl = pixGetWpl(src_pix); |
100 | 0 | l_uint32 *srcdata = pixGetData(src_pix); |
101 | 0 | for (int y = top; y < bottom; ++y) { |
102 | 0 | const l_uint32 *linedata = srcdata + y * src_wpl; |
103 | 0 | for (int x = 0; x < width; ++x) { |
104 | 0 | int pixel = Image::getDataByte(linedata, (x + left) * num_channels + channel); |
105 | 0 | ++histogram[pixel]; |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
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 | 0 | int OtsuStats(const int *histogram, int *H_out, int *omega0_out) { |
114 | 0 | int H = 0; |
115 | 0 | double mu_T = 0.0; |
116 | 0 | for (int i = 0; i < kHistogramSize; ++i) { |
117 | 0 | H += histogram[i]; |
118 | 0 | mu_T += static_cast<double>(i) * histogram[i]; |
119 | 0 | } |
120 | | |
121 | | // Now maximize sig_sq_B over t. |
122 | | // http://www.ctie.monash.edu.au/hargreave/Cornall_Terry_328.pdf |
123 | 0 | int best_t = -1; |
124 | 0 | int omega_0, omega_1; |
125 | 0 | int best_omega_0 = 0; |
126 | 0 | double best_sig_sq_B = 0.0; |
127 | 0 | double mu_0, mu_1, mu_t; |
128 | 0 | omega_0 = 0; |
129 | 0 | mu_t = 0.0; |
130 | 0 | for (int t = 0; t < kHistogramSize - 1; ++t) { |
131 | 0 | omega_0 += histogram[t]; |
132 | 0 | mu_t += t * static_cast<double>(histogram[t]); |
133 | 0 | if (omega_0 == 0) { |
134 | 0 | continue; |
135 | 0 | } |
136 | 0 | omega_1 = H - omega_0; |
137 | 0 | if (omega_1 == 0) { |
138 | 0 | break; |
139 | 0 | } |
140 | 0 | mu_0 = mu_t / omega_0; |
141 | 0 | mu_1 = (mu_T - mu_t) / omega_1; |
142 | 0 | double sig_sq_B = mu_1 - mu_0; |
143 | 0 | sig_sq_B *= sig_sq_B * omega_0 * omega_1; |
144 | 0 | if (best_t < 0 || sig_sq_B > best_sig_sq_B) { |
145 | 0 | best_sig_sq_B = sig_sq_B; |
146 | 0 | best_t = t; |
147 | 0 | best_omega_0 = omega_0; |
148 | 0 | } |
149 | 0 | } |
150 | 0 | if (H_out != nullptr) { |
151 | 0 | *H_out = H; |
152 | 0 | } |
153 | 0 | if (omega0_out != nullptr) { |
154 | 0 | *omega0_out = best_omega_0; |
155 | 0 | } |
156 | 0 | return best_t; |
157 | 0 | } |
158 | | |
159 | | } // namespace tesseract. |