Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpThreshold.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * Automatic thresholding functions.
32  */
33 
39 #include <visp3/core/vpHistogram.h>
40 #include <visp3/core/vpImageTools.h>
41 #include <visp3/imgproc/vpImgproc.h>
42 
43 namespace VISP_NAMESPACE_NAME
44 {
45 
46 bool isBimodal(const std::vector<float> &hist_float)
47 {
48  int modes = 0;
49  const int bimodalVal = 2;
50  size_t hist_float_size_m_1 = hist_float.size() - 1;
51  for (size_t cpt = 1; cpt < hist_float_size_m_1; ++cpt) {
52  if ((hist_float[cpt - 1] < hist_float[cpt]) && (hist_float[cpt] > hist_float[cpt + 1])) {
53  ++modes;
54  }
55 
56  if (modes > bimodalVal) {
57  return false;
58  }
59  }
60 
61  return (modes == bimodalVal);
62 }
63 
65 {
66  // Code ported from the AutoThreshold ImageJ plugin:
67  // Implements Huang's fuzzy thresholding method
68  // Uses Shannon's entropy function (one can also use Yager's entropy
69  // function) Huang L.-K. and Wang M.-J.J. (1995) "Image Thresholding by
70  // Minimizing the Measures of Fuzziness" Pattern Recognition, 28(1): 41-51
71  // Reimplemented (to handle 16-bit efficiently) by Johannes Schindelin Jan
72  // 31, 2011
73 
74  // Find first and last non-empty bin
75  size_t first, last;
76  size_t hist_size = hist.getSize();
77  for (first = 0; (first < hist_size) && (hist[static_cast<unsigned char>(first)] == 0); ++first) {
78  // do nothing
79  }
80 
81  for (last = (hist_size - 1); (last > first) && (hist[static_cast<unsigned char>(last)] == 0); --last) {
82  // do nothing
83  }
84 
85  if (first == last) {
86  return 0;
87  }
88 
89  // Calculate the cumulative density and the weighted cumulative density
90  std::vector<float> S(last + 1);
91  std::vector<float> W(last + 1);
92 
93  S[0] = static_cast<float>(hist[0]);
94  W[0] = 0.0f;
95  for (size_t i = std::max<size_t>(static_cast<size_t>(1), first); i <= last; ++i) {
96  S[i] = S[i - 1] + hist[static_cast<unsigned char>(i)];
97  W[i] = W[i - 1] + (i * static_cast<float>(hist[static_cast<unsigned char>(i)]));
98  }
99 
100  // Precalculate the summands of the entropy given the absolute difference x
101  // - mu (integral)
102  float C = static_cast<float>(last - first);
103  std::vector<float> Smu((last + 1) - first);
104 
105  size_t smu_size = Smu.size();
106  for (size_t i = 1; i < smu_size; ++i) {
107  float mu = 1 / (1 + (i / C));
108  Smu[i] = (-mu * std::log(mu)) - ((1 - mu) * std::log(1 - mu));
109  }
110 
111  // Calculate the threshold
112  int bestThreshold = 0;
113  float bestEntropy = std::numeric_limits<float>::max();
114 
115  for (size_t threshold = first; threshold <= last; ++threshold) {
116  float entropy = 0;
117  int mu = vpMath::round(W[threshold] / S[threshold]);
118  for (size_t i = first; i <= threshold; ++i) {
119  entropy += Smu[static_cast<size_t>(std::abs(static_cast<int>(i) - mu))] * hist[static_cast<unsigned char>(i)];
120  }
121 
122  mu = vpMath::round((W[last] - W[threshold]) / (S[last] - S[threshold]));
123  for (size_t i = threshold + 1; i <= last; ++i) {
124  entropy += Smu[static_cast<size_t>(std::abs(static_cast<int>(i) - mu))] * hist[static_cast<unsigned char>(i)];
125  }
126 
127  if (bestEntropy > entropy) {
128  bestEntropy = entropy;
129  bestThreshold = static_cast<int>(threshold);
130  }
131  }
132 
133  return bestThreshold;
134 }
135 
137 {
138  const unsigned int minSize = 3;
139  if (hist.getSize() < minSize) {
140  return -1;
141  }
142 
143  // Code based on the AutoThreshold ImageJ plugin:
144  // J. M. S. Prewitt and M. L. Mendelsohn, "The analysis of cell images," in
145  // Annals of the New York Academy of Sciences, vol. 128, pp. 1035-1053,
146  // 1966. ported to ImageJ plugin by G.Landini from Antti Niemisto's Matlab
147  // code (GPL) Original Matlab code Copyright (C) 2004 Antti Niemisto See
148  // http://www.cs.tut.fi/~ant/histthresh/ for an excellent slide presentation
149  // and the original Matlab code.
150  //
151  // Assumes a bimodal histogram. The histogram needs is smoothed (using a
152  // running average of size 3, iteratively) until there are only two local
153  // maxima. j and k Threshold t is (j+k)/2. Images with histograms having
154  // extremely unequal peaks or a broad and flat valley are unsuitable for this
155  // method.
156 
157  std::vector<float> hist_float(hist.getSize());
158  unsigned int hist_size = hist.getSize();
159  for (unsigned int cpt = 0; cpt < hist_size; ++cpt) {
160  hist_float[cpt] = static_cast<float>(hist[cpt]);
161  }
162 
163  int iter = 0;
164  const float nbPtsAverage = 3.;
165  const int maxIter = 10000;
166  while (!isBimodal(hist_float)) {
167  // Smooth with a 3 point running mean filter
168  size_t hist_float_size_m_1 = hist_float.size() - 1;
169  for (size_t cpt = 1; cpt < hist_float_size_m_1; ++cpt) {
170  hist_float[cpt] = (hist_float[cpt - 1] + hist_float[cpt] + hist_float[cpt + 1]) / nbPtsAverage;
171  }
172 
173  // First value
174  hist_float[0] = (hist_float[0] + hist_float[1]) / 2.0f;
175 
176  // Last value
177  const size_t var2 = 2;
178  hist_float[hist_float.size() - 1] = (((hist_float.size() - var2) + hist_float.size()) - 1) / 2.0f;
179 
180  ++iter;
181 
182  if (iter > maxIter) {
183  std::cerr << "Intermodes Threshold not found after " << maxIter << " iterations!" << std::endl;
184  return -1;
185  }
186  }
187 
188  // The threshold is the mean between the two peaks.
189  int tt = 0;
190  size_t hist_float_size_m_1 = hist_float.size() - 1;
191  for (size_t cpt = 1; cpt < hist_float_size_m_1; ++cpt) {
192  if ((hist_float[cpt - 1] < hist_float[cpt]) && (hist_float[cpt] > hist_float[cpt + 1])) {
193  // Mode
194  tt += static_cast<int>(cpt);
195  }
196  }
197 
198  return static_cast<int>(std::floor(tt / 2.0)); // vpMath round of tt div 2.0
199 }
200 
201 int computeThresholdIsoData(const vpHistogram &hist, unsigned int imageSize)
202 {
203  int threshold = 0;
204 
205  // Code based on BSD Matlab isodata implementation by zephyr
206  // STEP 1: Compute mean intensity of image from histogram, set T=mean(I)
207  std::vector<float> cumsum(hist.getSize(), 0.0f);
208  std::vector<float> sum_ip(hist.getSize(), 0.0f);
209  cumsum[0] = static_cast<float>(hist[0]);
210 
211  unsigned int hist_size = hist.getSize();
212  for (unsigned int cpt = 1; cpt < hist_size; ++cpt) {
213  sum_ip[cpt] = (cpt * static_cast<float>(hist[cpt])) + sum_ip[cpt - 1];
214  cumsum[cpt] = static_cast<float>(hist[cpt]) + cumsum[cpt - 1];
215  }
216 
217  int T = vpMath::round(sum_ip[255] / imageSize);
218 
219  // STEP 2: compute Mean above T (MAT) and Mean below T (MBT) using T from
220  float MBT = sum_ip[static_cast<size_t>(T - 2)] / cumsum[static_cast<size_t>(T - 2)];
221  float MAT = (sum_ip.back() - sum_ip[static_cast<size_t>(T - 1)]) / (cumsum.back() - cumsum[static_cast<size_t>(T - 1)]);
222 
223  int T2 = vpMath::round((MAT + MBT) / 2.0f);
224 
225  //% STEP 3 to n: repeat step 2 if T(i)~=T(i-1)
226  while (std::abs(T2 - T) >= 1) {
227  const int val_2 = 2;
228  MBT = sum_ip[static_cast<size_t>(T2 - val_2)] / cumsum[static_cast<size_t>(T2 - val_2)];
229  MAT = (sum_ip.back() - sum_ip[static_cast<size_t>(T2 - 1)]) / (cumsum.back() - cumsum[static_cast<size_t>(T2 - 1)]);
230 
231  T = T2;
232  T2 = vpMath::round((MAT + MBT) / 2.0f);
233  threshold = T2;
234  }
235 
236  return threshold;
237 }
238 
239 int computeThresholdMean(const vpHistogram &hist, unsigned int imageSize)
240 {
241  // C. A. Glasbey, "An analysis of histogram-based thresholding algorithms,"
242  // CVGIP: Graphical Models and Image Processing, vol. 55, pp. 532-537, 1993.
243  // The threshold is the mean of the greyscale data
244  float sum_ip = 0.0f;
245  unsigned int hist_size = hist.getSize();
246  for (unsigned int cpt = 0; cpt < hist_size; ++cpt) {
247  sum_ip += cpt * static_cast<float>(hist[cpt]);
248  }
249 
250  return static_cast<int>(std::floor(sum_ip / imageSize));
251 }
252 
253 int computeThresholdOtsu(const vpHistogram &hist, unsigned int imageSize)
254 {
255  // Otsu, N (1979), "A threshold selection method from gray-level
256  // histograms", IEEE Trans. Sys., Man., Cyber. 9: 62-66,
257  // doi:10.1109/TSMC.1979.4310076
258 
259  float mu_T = 0.0f;
260  float sum_ip_all[256];
261  int hist_size = static_cast<int>(hist.getSize());
262  for (int cpt = 0; cpt < hist_size; ++cpt) {
263  mu_T += cpt * static_cast<float>(hist[cpt]);
264  sum_ip_all[cpt] = mu_T;
265  }
266 
267  // Weight Background / Foreground
268  float w_B = 0.0f, w_F = 0.0f;
269 
270  float max_sigma_b = 0.0f;
271  int threshold = 0;
272 
273  bool w_f_eq_nul = false;
274  int cpt = 0;
275  const int maxCpt = 256;
276  while ((cpt < maxCpt) && (!w_f_eq_nul)) {
277  w_B += hist[cpt];
278  bool w_b_eq_nul = vpMath::nul(w_B, std::numeric_limits<float>::epsilon());
279  if (!w_b_eq_nul) {
280 
281  w_F = static_cast<int>(imageSize) - w_B;
282  w_f_eq_nul = vpMath::nul(w_F, std::numeric_limits<float>::epsilon());
283  if (!w_f_eq_nul) {
284 
285  // Mean Background / Foreground
286  float mu_B = sum_ip_all[cpt] / static_cast<float>(w_B);
287  float mu_F = (mu_T - sum_ip_all[cpt]) / static_cast<float>(w_F);
288  // If there is a case where (w_B * w_F) exceed FLT_MAX, normalize
289  // histogram
290  float sigma_b_sqr = w_B * w_F * (mu_B - mu_F) * (mu_B - mu_F);
291 
292  if (sigma_b_sqr >= max_sigma_b) {
293  threshold = cpt;
294  max_sigma_b = sigma_b_sqr;
295  }
296  }
297  // else exit the loop
298  }
299  ++cpt;
300  }
301 
302  return threshold;
303 }
304 
306 {
307  int threshold = 0;
308 
309  // Zack, G. W., Rogers, W. E. and Latt, S. A., 1977,
310  // Automatic Measurement of Sister Chromatid Exchange Frequency,
311  // Journal of Histochemistry and Cytochemistry 25 (7), pp. 741-753
312 
313  int left_bound = -1, right_bound = -1, max_idx = -1, max_value = 0;
314  // Find max value index and left / right most index
315  int hist_size = static_cast<int>(hist.getSize());
316  for (int cpt = 0; cpt < hist_size; ++cpt) {
317  if ((left_bound == -1) && (hist[cpt] > 0)) {
318  left_bound = static_cast<int>(cpt);
319  }
320 
321  if ((right_bound == -1) && (hist[static_cast<int>(hist.getSize()) - 1 - cpt] > 0)) {
322  right_bound = static_cast<int>(hist.getSize()) - 1 - cpt;
323  }
324 
325  if (static_cast<int>(hist[cpt]) > max_value) {
326  max_value = static_cast<int>(hist[cpt]);
327  max_idx = cpt;
328  }
329  }
330 
331  // First / last index when hist(cpt) == 0
332  left_bound = left_bound > 0 ? (left_bound - 1) : left_bound;
333  right_bound = right_bound < (static_cast<int>(hist.getSize()) - 1) ? (right_bound + 1) : right_bound;
334 
335  // Use the largest bound
336  bool flip = false;
337  if ((max_idx - left_bound) < (right_bound - max_idx)) {
338  // Flip histogram to get the largest bound to the left
339  flip = true;
340 
341  int cpt_left = 0;
342  int cpt_right = static_cast<int>(hist.getSize()) - 1;
343  for (; cpt_left < cpt_right; ++cpt_left, --cpt_right) {
344  unsigned int temp = hist[cpt_left];
345  hist.set(cpt_left, hist[cpt_right]);
346  hist.set(cpt_right, temp);
347  }
348 
349  left_bound = static_cast<int>(hist.getSize()) - 1 - right_bound;
350  max_idx = static_cast<int>(hist.getSize()) - 1 - max_idx;
351  }
352 
353  // Distance from a point to a line defined by two points:
354  //\textbf{distance} \left ( P_1, P_2, \left ( x_0,y_0 \right ) \right )
355  // = \frac{ \left | \left ( y_2-y_1 \right ) x_0 - \left ( x_2-x_1 \right )
356  // y_0 + x_2 y_1 - y_2 x_1 \right | }
357  // { \sqrt{ \left ( y_2 - y_1 \right )^{2} + \left ( x_2 - x_1 \right
358  // )^{2} } }
359  // Constants are ignored
360  float a = static_cast<float>(max_value); // y_2 - y_1
361  float b = static_cast<float>(left_bound - max_idx); //-(x_2 - x_1)
362  float max_dist = 0.0f;
363 
364  for (int cpt = left_bound + 1; cpt <= max_idx; ++cpt) {
365  float dist = (a * cpt) + (b * hist[cpt]);
366 
367  if (dist > max_dist) {
368  max_dist = dist;
369  threshold = cpt;
370  }
371  }
372  --threshold;
373 
374  if (flip) {
375  threshold = static_cast<int>(hist.getSize()) - 1 - threshold;
376  }
377 
378  return threshold;
379 }
380 
382  const unsigned char backgroundValue, const unsigned char foregroundValue)
383 {
384  if (I.getSize() == 0) {
385  return 0;
386  }
387 
388  // Compute image histogram
389  vpHistogram histogram(I);
390  int threshold = -1;
391 
392  switch (method) {
394  threshold = computeThresholdHuang(histogram);
395  break;
396 
398  threshold = computeThresholdIntermodes(histogram);
399  break;
400 
402  threshold = computeThresholdIsoData(histogram, I.getSize());
403  break;
404 
405  case AUTO_THRESHOLD_MEAN:
406  threshold = computeThresholdMean(histogram, I.getSize());
407  break;
408 
409  case AUTO_THRESHOLD_OTSU:
410  threshold = computeThresholdOtsu(histogram, I.getSize());
411  break;
412 
414  threshold = computeThresholdTriangle(histogram);
415  break;
416 
417  default:
418  break;
419  }
420 
421  const unsigned char threshold2 = 255;
422  if (threshold != -1) {
423  // Threshold
424  vpImageTools::binarise(I, static_cast<unsigned char>(threshold), threshold2, backgroundValue, foregroundValue,
425  foregroundValue);
426  }
427 
428  return threshold;
429 }
430 
431 } // namespace
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned getSize() const
Definition: vpHistogram.h:278
void set(const unsigned char level, unsigned int value)
Definition: vpHistogram.h:224
static void binarise(vpImage< Type > &I, Type threshold1, Type threshold2, Type value1, Type value2, Type value3, bool useLUT=true)
Definition: vpImageTools.h:473
unsigned int getSize() const
Definition: vpImage.h:221
static bool nul(double x, double threshold=0.001)
Definition: vpMath.h:449
static int round(double x)
Definition: vpMath.h:409
VISP_EXPORT unsigned char autoThreshold(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const vpAutoThresholdMethod &method, const unsigned char backgroundValue=0, const unsigned char foregroundValue=255)
bool isBimodal(const std::vector< float > &hist_float)
Definition: vpThreshold.cpp:46
int computeThresholdIntermodes(const vpHistogram &hist)
int computeThresholdIsoData(const vpHistogram &hist, unsigned int imageSize)
int computeThresholdMean(const vpHistogram &hist, unsigned int imageSize)
int computeThresholdOtsu(const vpHistogram &hist, unsigned int imageSize)
int computeThresholdTriangle(vpHistogram &hist)
int computeThresholdHuang(const vpHistogram &hist)
Definition: vpThreshold.cpp:64