Coverage Report

Created: 2026-10-28 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.