Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
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
38
39#include <visp3/core/vpHistogram.h>
40#include <visp3/core/vpImageTools.h>
41#include <visp3/imgproc/vpImgproc.h>
42
43namespace VISP_NAMESPACE_NAME
44{
45
46bool 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
201int 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
239int 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
253int 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
406 threshold = computeThresholdMean(histogram, I.getSize());
407 break;
408
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.
unsigned getSize() const
void set(const unsigned char level, unsigned int value)
static void binarise(vpImage< Type > &I, Type threshold1, Type threshold2, Type value1, Type value2, Type value3, bool useLUT=true)
Definition of the vpImage class member functions.
Definition vpImage.h:131
static bool nul(double x, double threshold=0.001)
Definition vpMath.h:453
static int round(double x)
Definition vpMath.h:413
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)
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)