Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
vpHistogram.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  * Gray level histogram manipulation.
32  *
33 *****************************************************************************/
34 
44 #include <stdlib.h>
45 #include <visp3/core/vpDisplay.h>
46 #include <visp3/core/vpHistogram.h>
47 #include <visp3/core/vpImageConvert.h>
48 
50 #if defined(VISP_HAVE_THREADS)
51 #include <thread>
52 
53 #ifndef DOXYGEN_SHOULD_SKIP_THIS
54 namespace
55 {
56 struct vpHistogram_Param_t
57 {
58  unsigned int m_start_index;
59  unsigned int m_end_index;
60 
61  unsigned int m_lut[256];
62  unsigned int *m_histogram;
63  const vpImage<unsigned char> *m_I;
64  const vpImage<bool> *m_mask;
65 
66  vpHistogram_Param_t() : m_start_index(0), m_end_index(0), m_lut(), m_histogram(nullptr), m_I(nullptr), m_mask(nullptr) { }
67 
68  vpHistogram_Param_t(unsigned int start_index, unsigned int end_index, const vpImage<unsigned char> *const I, const vpImage<bool> *const mask)
69  : m_start_index(start_index), m_end_index(end_index), m_lut(), m_histogram(nullptr), m_I(I), m_mask(mask)
70  { }
71 
72  ~vpHistogram_Param_t()
73  {
74  if (m_histogram != nullptr) {
75  delete[] m_histogram;
76  }
77  }
78 };
79 
80 void computeHistogramThread(vpHistogram_Param_t *histogram_param)
81 {
82  unsigned int start_index = histogram_param->m_start_index;
83  unsigned int end_index = histogram_param->m_end_index;
84 
85  const vpImage<unsigned char> *I = histogram_param->m_I;
86 
87  unsigned char *ptrStart = (unsigned char *)(I->bitmap) + start_index;
88  unsigned char *ptrEnd = (unsigned char *)(I->bitmap) + end_index;
89  unsigned char *ptrCurrent = ptrStart;
90 
91  const bool alwaysTrue = true;
92  const bool *ptrMaskCurrent = &alwaysTrue;
93  if (histogram_param->m_mask) {
94  ptrMaskCurrent = (const bool *)histogram_param->m_mask->bitmap + start_index;
95  }
96 
97  if (end_index >= 8 + start_index) {
98  // Unroll loop version
99  for (; ptrCurrent <= ptrEnd - 8;) {
100  if (*ptrMaskCurrent) {
101  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
102  }
103  ++ptrCurrent;
104  if (histogram_param->m_mask != nullptr) {
105  ++ptrMaskCurrent;
106  }
107 
108  if (*ptrMaskCurrent) {
109  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
110  }
111  ++ptrCurrent;
112  if (histogram_param->m_mask != nullptr) {
113  ++ptrMaskCurrent;
114  }
115 
116  if (*ptrMaskCurrent) {
117  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
118  }
119  ++ptrCurrent;
120  if (histogram_param->m_mask != nullptr) {
121  ++ptrMaskCurrent;
122  }
123 
124  if (*ptrMaskCurrent) {
125  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
126  }
127  ++ptrCurrent;
128  if (histogram_param->m_mask != nullptr) {
129  ++ptrMaskCurrent;
130  }
131 
132  if (*ptrMaskCurrent) {
133  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
134  }
135  ++ptrCurrent;
136  if (histogram_param->m_mask != nullptr) {
137  ++ptrMaskCurrent;
138  }
139 
140  if (*ptrMaskCurrent) {
141  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
142  }
143  ++ptrCurrent;
144  if (histogram_param->m_mask != nullptr) {
145  ++ptrMaskCurrent;
146  }
147 
148  if (*ptrMaskCurrent) {
149  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
150  }
151  ++ptrCurrent;
152  if (histogram_param->m_mask != nullptr) {
153  ++ptrMaskCurrent;
154  }
155 
156  if (*ptrMaskCurrent) {
157  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
158  }
159  ++ptrCurrent;
160  if (histogram_param->m_mask != nullptr) {
161  ++ptrMaskCurrent;
162  }
163  }
164  }
165 
166  for (; ptrCurrent != ptrEnd; ++ptrCurrent) {
167  if (*ptrMaskCurrent) {
168  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
169  }
170  if (histogram_param->m_mask != nullptr) {
171  ++ptrMaskCurrent;
172  }
173  }
174 }
175 } // namespace
176 #endif // DOXYGEN_SHOULD_SKIP_THIS
177 #endif
178 
179 bool compare_vpHistogramPeak(vpHistogramPeak first, vpHistogramPeak second);
180 
181 // comparison,
182 bool compare_vpHistogramPeak(vpHistogramPeak first, vpHistogramPeak second)
183 {
184  if (first.getValue() > second.getValue()) {
185  return true;
186  }
187  else {
188  return false;
189  }
190 }
191 
195 vpHistogram::vpHistogram() : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0) { init(); }
196 
200 vpHistogram::vpHistogram(const vpHistogram &h) : m_histogram(nullptr), m_size(256), mp_mask(h.mp_mask), m_total(h.m_total)
201 {
202  init(h.m_size);
203  memcpy(m_histogram, h.m_histogram, m_size * sizeof(unsigned));
204 }
205 
213 vpHistogram::vpHistogram(const vpImage<unsigned char> &I) : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0)
214 {
215  init();
216 
217  calculate(I);
218 }
219 
228 vpHistogram::vpHistogram(const vpImage<unsigned char> &I, const vpImage<bool> *p_mask) : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0)
229 {
230  init();
231  setMask(p_mask);
232  calculate(I);
233 }
234 
239 {
240  if (m_histogram != nullptr) {
241  delete[] m_histogram;
242  m_histogram = nullptr;
243  m_size = 0;
244  }
245 }
246 
261 {
262  init(h.m_size);
263  memcpy(m_histogram, h.m_histogram, m_size * sizeof(unsigned));
264  mp_mask = h.mp_mask;
265  m_total = h.m_total;
266 
267  return *this;
268 }
269 
275 void vpHistogram::init(unsigned size_)
276 {
277  if (m_histogram != nullptr) {
278  delete[] m_histogram;
279  m_histogram = nullptr;
280  }
281 
282  this->m_size = size_;
283  m_histogram = new unsigned[m_size];
284 
285  memset(m_histogram, 0, m_size * sizeof(unsigned));
286 
287  mp_mask = nullptr;
288  m_total = 0;
289 }
290 
299 void vpHistogram::calculate(const vpImage<unsigned char> &I, unsigned int nbins, unsigned int nbThreads)
300 {
301  if (m_size != nbins) {
302  if (m_histogram != nullptr) {
303  delete[] m_histogram;
304  m_histogram = nullptr;
305  }
306 
307  m_size = nbins > 256 ? 256 : (nbins > 0 ? nbins : 256);
308  if ((nbins > 256) || (nbins == 0)) {
309  std::cerr << "nbins=" << nbins << " , nbins should be between ]0 ; 256] ; use by default nbins=256" << std::endl;
310  }
311  m_histogram = new unsigned int[m_size];
312  }
313 
314  memset(m_histogram, 0, m_size * sizeof(unsigned int));
315 
316  bool use_single_thread;
317 #if !defined(VISP_HAVE_THREADS)
318  use_single_thread = true;
319 #else
320  use_single_thread = (nbThreads == 0 || nbThreads == 1);
321 #endif
322 
323  if ((!use_single_thread) && (I.getSize() <= nbThreads)) {
324  use_single_thread = true;
325  }
326 
327  unsigned int lut[256];
328  for (unsigned int i = 0; i < 256; ++i) {
329  lut[i] = static_cast<unsigned int>((i * m_size) / 256.0);
330  }
331 
332  if (use_single_thread) {
333  // Single thread
334  const bool alwaysTrue = true;
335  const bool *ptrMaskCurrent = &alwaysTrue;
336  if (mp_mask) {
337  ptrMaskCurrent = static_cast<const bool *>(mp_mask->bitmap);
338  }
339 
340  unsigned int size_ = I.getWidth() * I.getHeight();
341  unsigned char *ptrStart = static_cast<unsigned char *>(I.bitmap);
342  unsigned char *ptrEnd = ptrStart + size_;
343  unsigned char *ptrCurrent = ptrStart;
344 
345  m_total = 0;
346  while (ptrCurrent != ptrEnd) {
347  if (*ptrMaskCurrent) {
348  ++m_histogram[lut[*ptrCurrent]];
349  ++m_total;
350  }
351  ++ptrCurrent;
352  if (mp_mask) {
353  ++ptrMaskCurrent;
354  }
355  }
356  }
357  else {
358 #if defined(VISP_HAVE_THREADS)
359  // Multi-threads
360  std::vector<std::thread *> threadpool;
361  std::vector<vpHistogram_Param_t *> histogramParams;
362 
363  unsigned int image_size = I.getSize();
364  unsigned int step = image_size / nbThreads;
365  unsigned int last_step = image_size - step * (nbThreads - 1);
366 
367  for (unsigned int index = 0; index < nbThreads; ++index) {
368  unsigned int start_index = index * step;
369  unsigned int end_index = (index + 1) * step;
370 
371  if (index == nbThreads - 1) {
372  end_index = start_index + last_step;
373  }
374 
375  vpHistogram_Param_t *histogram_param = new vpHistogram_Param_t(start_index, end_index, &I, mp_mask);
376  histogram_param->m_histogram = new unsigned int[m_size];
377  histogram_param->m_mask = mp_mask;
378  memset(histogram_param->m_histogram, 0, m_size * sizeof(unsigned int));
379  memcpy(histogram_param->m_lut, lut, 256 * sizeof(unsigned int));
380 
381  histogramParams.push_back(histogram_param);
382 
383  // Start the threads
384  std::thread *histogram_thread = new std::thread(&computeHistogramThread, histogram_param);
385  threadpool.push_back(histogram_thread);
386  }
387 
388  for (size_t cpt = 0; cpt < threadpool.size(); ++cpt) {
389  // Wait until thread ends up
390  threadpool[cpt]->join();
391  }
392 
393  m_total = 0;
394  for (unsigned int cpt1 = 0; cpt1 < m_size; ++cpt1) {
395  unsigned int sum = 0;
396 
397  for (size_t cpt2 = 0; cpt2 < histogramParams.size(); ++cpt2) {
398  sum += histogramParams[cpt2]->m_histogram[cpt1];
399  }
400 
401  m_histogram[cpt1] = sum;
402  m_total += sum;
403  }
404 
405  // Delete
406  for (size_t cpt = 0; cpt < threadpool.size(); ++cpt) {
407  delete threadpool[cpt];
408  }
409 
410  for (size_t cpt = 0; cpt < histogramParams.size(); ++cpt) {
411  delete histogramParams[cpt];
412  }
413 #endif
414  }
415 }
416 
418 {
419  // Compute the histogram
420  calculate(I);
421 
422  // Calculate the cumulative distribution function
423  unsigned int cdf[256];
424  unsigned int cdfMin = std::numeric_limits<unsigned int>::max(), cdfMax = 0;
425  unsigned int minValue = std::numeric_limits<unsigned int>::max(), maxValue = 0;
426  cdf[0] = m_histogram[0];
427 
428  if ((cdf[0] < cdfMin) && (cdf[0] > 0)) {
429  cdfMin = cdf[0];
430  minValue = 0;
431  }
432 
433  for (unsigned int i = 1; i < 256; ++i) {
434  cdf[i] = cdf[i - 1] + m_histogram[i];
435 
436  if ((cdf[i] < cdfMin) && (cdf[i] > 0)) {
437  cdfMin = cdf[i];
438  minValue = i;
439  }
440 
441  if (cdf[i] > cdfMax) {
442  cdfMax = cdf[i];
443  maxValue = i;
444  }
445  }
446 
447  unsigned int nbPixels = I.getWidth() * I.getHeight();
448  if (nbPixels == cdfMin) {
449  // Only one brightness value in the image
450  return;
451  }
452 
453  // Construct the look-up table
454  unsigned char lut[256];
455  for (unsigned int x = minValue; x <= maxValue; ++x) {
456  lut[x] = vpMath::round(((cdf[x] - cdfMin) / static_cast<double>(nbPixels - cdfMin)) * 255.0);
457  }
458 
459  Iout = I;
460  Iout.performLut(lut);
461 }
462 
474 void vpHistogram::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness,
475  unsigned int maxValue_)
476 {
477  unsigned int width = I.getWidth(), height = I.getHeight();
478  // Minimal width and height are 36 px
479  if ((width < 36) || (height < 36)) {
480  std::cerr << "Image I must have at least width >= 36 && height >= 36 !" << std::endl;
481  return;
482  }
483 
484  unsigned int maxValue = maxValue_;
485  if (maxValue == 0) {
486  for (unsigned int i = 0; i < m_size; ++i) {
487  if (m_histogram[i] > maxValue) {
488  maxValue = m_histogram[i];
489  }
490  }
491  }
492 
493  if (maxValue == 0) {
494  throw(vpException(vpException::divideByZeroError, "Cannot display histogram; max value=0"));
495  }
496  // Keep 12 free pixels at the top
497  unsigned int max_height = height - 12;
498  double ratio_height = max_height / static_cast<double>(maxValue);
499  double ratio_width = (width - 1) / static_cast<double>(m_size - 1.0);
500 
501  for (unsigned int i = 1; i < m_size; ++i) {
502  unsigned int value1 = m_histogram[i - 1];
503  unsigned int value2 = m_histogram[i];
504 
505  vpImagePoint startPt((height - 1) - (value1 * ratio_height), (i - 1) * ratio_width);
506  vpImagePoint endPt((height - 1) - (value2 * ratio_height), (i * ratio_width));
507  vpDisplay::displayLine(I, startPt, endPt, color, thickness);
508  }
509 }
510 
529 void vpHistogram::smooth(unsigned int fsize)
530 {
531  if (m_histogram == nullptr) {
532  throw(vpImageException(vpImageException::notInitializedError, "Histogram array not initialised"));
533  }
534 
535  vpHistogram h;
536  h = *this;
537 
538  int hsize = static_cast<int>(fsize) / 2; // half filter size
539 
540  for (unsigned i = 0; i < m_size; ++i) {
541  unsigned int sum = 0;
542  unsigned int nb = 0;
543  for (int j = -hsize; j <= hsize; ++j) {
544  // exploitation of the overflow to detect negative value...
545  if (/*(i + j) >= 0 &&*/ (i + static_cast<unsigned int>(j)) < m_size) {
546  sum += h.m_histogram[i + static_cast<unsigned int>(j)];
547  ++nb;
548  }
549  }
550  m_histogram[i] = sum / nb;
551  }
552 }
553 
568 unsigned vpHistogram::getPeaks(std::list<vpHistogramPeak> &peaks)
569 {
570  if (m_histogram == nullptr) {
571  throw(vpImageException(vpImageException::notInitializedError, "Histogram array not initialised"));
572  }
573 
574  int prev_slope; // Previous histogram inclination
575  vpHistogramPeak p; // An histogram peak
576  unsigned nbpeaks; // Number of peaks in the histogram (ie local maxima)
577 
578  peaks.clear();
579 
580  // Parse the histogram to get the local maxima
581  unsigned cpt = 0;
582  unsigned sum_level = 0;
583  nbpeaks = 0;
584  prev_slope = 1;
585 
586  for (unsigned i = 0; i < (m_size - 1); ++i) {
587  int next_slope = static_cast<int>(m_histogram[i + 1]) - static_cast<int>(m_histogram[i]); // Next histogram inclination
588 
589  if ((prev_slope > 0) && (next_slope == 0)) {
590  sum_level += i + 1;
591  ++cpt;
592  }
593  else {
594  // Peak detection
595  if ((prev_slope > 0) && (next_slope < 0)) {
596  sum_level += i;
597  ++cpt;
598 
599  unsigned int level = sum_level / cpt;
600  p.set(static_cast<unsigned char>(level), m_histogram[level]);
601  peaks.push_back(p);
602 
603  ++nbpeaks;
604  }
605 
606  prev_slope = next_slope;
607  sum_level = 0;
608  cpt = 0;
609  }
610  }
611  if (prev_slope > 0) {
612  p.set(static_cast<unsigned char>(m_size) - 1u, m_histogram[m_size - 1]);
613  peaks.push_back(p);
614  ++nbpeaks;
615  }
616 
617  return nbpeaks;
618 }
619 
637 unsigned vpHistogram::getPeaks(unsigned char dist, vpHistogramPeak &peak1, vpHistogramPeak &peak2)
638 {
639  std::list<vpHistogramPeak> peaks;
640  unsigned nbpeaks; // Number of peaks in the histogram (ie local maxima)
641 
642  nbpeaks = getPeaks(peaks);
643  sort(peaks);
644 
645  if (nbpeaks == 0) {
646  peak1.set(0, 0);
647  peak2.set(0, 0);
648  return 0;
649  }
650 
651  if (nbpeaks == 1) {
652  peak1 = peaks.front();
653  peak2.set(0, 0);
654  return 1;
655  }
656 
657  // Parse the peaks list to get the peak with a distance greater
658  // than dist to the highest
659  peak1 = peaks.front();
660  std::list<vpHistogramPeak>::const_iterator peaks_end = peaks.end();
661  for (std::list<vpHistogramPeak>::const_iterator it = peaks.begin(); it != peaks_end; ++it) {
662  vpHistogramPeak p = *it;
663  if (abs(p.getLevel() - peak1.getLevel()) > dist) {
664  // The second peak is found
665  peak2 = p;
666  return 2;
667  }
668  }
669 
670  // No second peak found
671  peak2.set(0, 0);
672  return 1;
673 }
674 
690 bool vpHistogram::getPeaks(unsigned char dist, vpHistogramPeak &peakl, vpHistogramPeak &peakr, vpHistogramValey &valey)
691 {
692  unsigned char *peak; // Local maxima values
693  int prev_slope; // Previous histogram inclination
694  unsigned index_highest_peak; // Index in peak[] array of the highest peak
695  unsigned index_second_peak; // Index in peak[] array of the second peak
696 
697  unsigned int prof;
698  unsigned int maxprof; // Nb pixels difference between 2 maxi peaks
699  unsigned int nbmini; // Minimum numbers
700  unsigned int sumindmini; // Sum
701  unsigned int mini; // current minimum
702  unsigned int nbpeaks; // Number of peaks in the histogram (ie local maxima)
703 
704  // Init the valey
705  valey.set(0, 0);
706 
707  // Allocation for the
708  peak = new unsigned char[m_size];
709 
710  // Parse the histogram to get the local maxima
711  nbpeaks = 0;
712  prev_slope = 1;
713  for (unsigned i = 0; i < (m_size - 1); ++i) {
714  int next_slope = static_cast<int>(m_histogram[i + 1]) - static_cast<int>(m_histogram[i]); // Next histogram inclination
715  if (next_slope == 0) {
716  // continue
717  }
718  else {
719  // Peak detection
720  if ((prev_slope > 0) && (next_slope < 0)) {
721  peak[nbpeaks++] = static_cast<unsigned char>(i);
722  }
723 
724  prev_slope = next_slope;
725  }
726  }
727  if (prev_slope > 0) {
728  peak[nbpeaks++] = static_cast<unsigned char>(m_size - 1);
729  }
730 
731  // Get the global maximum
732  index_highest_peak = 0;
733  for (unsigned int i = 0; i < nbpeaks; ++i) {
734  if (m_histogram[peak[i]] > m_histogram[peak[index_highest_peak]]) {
735  index_highest_peak = i;
736  }
737  }
738 
739  maxprof = 0;
740  index_second_peak = index_highest_peak;
741 
742  // Search second local maximum on the left of the global maximum
743  for (unsigned i = 0; i < index_highest_peak; ++i) {
744  if ((peak[index_highest_peak] - peak[i]) > dist) {
745  prof = 0;
746  for (int j = peak[i]; j <= (peak[index_highest_peak] - dist); ++j) {
747  if ((m_histogram[peak[i]] - m_histogram[j]) > prof) {
748  prof = m_histogram[peak[i]] - m_histogram[j];
749  }
750  }
751 
752  if (prof > maxprof) {
753  maxprof = prof;
754  index_second_peak = i;
755  }
756  }
757  }
758 
759  // Search second local maximum on the right of the global maximum
760  for (unsigned i = index_highest_peak + 1; i < nbpeaks; ++i) {
761  if ((peak[i] - peak[index_highest_peak]) > dist) {
762  prof = 0;
763  for (int j = peak[index_highest_peak] + dist; j <= peak[i]; ++j) {
764  if ((m_histogram[peak[i]] - m_histogram[j]) > prof) {
765  prof = m_histogram[peak[i]] - m_histogram[j];
766  }
767  }
768 
769  if (prof > maxprof) {
770  maxprof = prof;
771  index_second_peak = i;
772  }
773  }
774  }
775 
776  // Determine position of the first and second highest peaks
777  if (peak[index_highest_peak] < peak[index_second_peak]) {
778  peakr.set(peak[index_second_peak], m_histogram[peak[index_second_peak]]);
779  peakl.set(peak[index_highest_peak], m_histogram[peak[index_highest_peak]]);
780  }
781  else {
782  peakl.set(peak[index_second_peak], m_histogram[peak[index_second_peak]]);
783  peakr.set(peak[index_highest_peak], m_histogram[peak[index_highest_peak]]);
784  }
785 
786  if (peakl == peakr) {
787 
788  delete[] peak;
789 
790  return false; // Not a bimodal histogram
791  }
792 
793  // Search the valey
794  mini = peakl.getValue();
795  sumindmini = 0;
796  nbmini = 0;
797  unsigned peakr_level = peakr.getLevel();
798  for (unsigned i = peakl.getLevel(); i <= peakr_level; ++i) {
799  if (m_histogram[i] < mini) {
800  mini = m_histogram[i];
801  nbmini = 1;
802  sumindmini = i;
803  // continue
804  }
805  else {
806  if (m_histogram[i] == mini) {
807  sumindmini += i;
808  ++nbmini;
809  }
810  }
811  }
812 
813  if (nbmini == 0) {
814  // no valey found
815  valey.set(0, 0);
816 
817  delete[] peak;
818 
819  return false;
820  }
821  else {
822  mini = sumindmini / nbmini; // mean
823  valey.set(static_cast<unsigned char>(mini), m_histogram[mini]);
824 
825  delete[] peak;
826 
827  return true;
828  }
829 }
830 
842 unsigned vpHistogram::getValey(std::list<vpHistogramValey> &valey)
843 {
844  if (m_histogram == nullptr) {
845  throw(vpImageException(vpImageException::notInitializedError, "Histogram array not initialised"));
846  }
847 
848  int prev_slope; // Previous histogram inclination
849  vpHistogramValey p; // An histogram valey
850  unsigned nbvaley; // Number of valey in the histogram (ie local minima)
851 
852  valey.clear();
853 
854  // Parse the histogram to get the local minima
855  unsigned cpt = 0;
856  unsigned sum_level = 0;
857  nbvaley = 0;
858  prev_slope = -1;
859 
860  for (unsigned i = 0; i < (m_size - 1); ++i) {
861  int next_slope = static_cast<int>(m_histogram[i + 1]) - static_cast<int>(m_histogram[i]); // Next histogram inclination
862 
863  if ((prev_slope < 0) && (next_slope == 0)) {
864  sum_level += i + 1;
865  ++cpt;
866  // continue
867  }
868  else {
869  // Valey detection
870  if ((prev_slope < 0) && (next_slope > 0)) {
871  sum_level += i;
872  ++cpt;
873 
874  unsigned int level = sum_level / cpt;
875  p.set(static_cast<unsigned char>(level), m_histogram[level]);
876  valey.push_back(p);
877 
878  ++nbvaley;
879  }
880 
881  prev_slope = next_slope;
882  sum_level = 0;
883  cpt = 0;
884  }
885  }
886  if (prev_slope < 0) {
887  p.set(static_cast<unsigned char>(m_size) - 1u, m_histogram[m_size - 1]);
888  valey.push_back(p);
889  ++nbvaley;
890  }
891 
892  return nbvaley;
893 }
894 
910 {
911 
912  // Set the left and right peaks
913  vpHistogramPeak peakl, peakr;
914  if (peak1.getLevel() > peak2.getLevel()) {
915  peakl = peak2;
916  peakr = peak1;
917  }
918  else {
919  peakl = peak1;
920  peakr = peak2;
921  }
922 
923  // Search the valey
924  unsigned int nbmini; // Minimum numbers
925  unsigned int sumindmini; // Sum
926  unsigned int mini; // current minimum
927 
928  mini = peakl.getValue();
929  sumindmini = 0;
930  nbmini = 0;
931  unsigned peakr_level = peakr.getLevel();
932  for (unsigned i = peakl.getLevel(); i <= peakr_level; ++i) {
933  if (m_histogram[i] < mini) {
934  mini = m_histogram[i];
935  nbmini = 1;
936  sumindmini = i;
937  // continue
938  }
939  else {
940  if (m_histogram[i] == mini) {
941  sumindmini += i;
942  ++nbmini;
943  }
944  }
945  }
946 
947  if (nbmini == 0) {
948  // no valey found
949  valey.set(0, 0);
950 
951  return false;
952  }
953  else {
954  unsigned int minipos = sumindmini / nbmini; // position of the minimum
955 
956  valey.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
957  return true;
958  }
959 }
978 unsigned vpHistogram::getValey(unsigned char dist, const vpHistogramPeak &peak, vpHistogramValey &valeyl,
979  vpHistogramValey &valeyr)
980 {
981  unsigned int ret = 0x11;
982  unsigned int nbmini; // Minimum numbers
983  unsigned int sumindmini; // Sum
984  unsigned int mini; // current minimum
985  vpHistogramPeak peakr, peakl; // Left and right peaks of peak
986  std::list<vpHistogramPeak> peaks; // list of histogram peaks
987  // unsigned int nbpeaks=0; // Number of peaks in the histogram (ie local
988  // maxima)
989 
990  if (peak.getLevel() == 0) {
991  valeyl.set(0, 0);
992  ret &= 0x01;
993  }
994  if (peak.getLevel() == (m_size - 1)) {
995  valeyr.set(static_cast<unsigned char>(m_size - 1), 0);
996  ret &= 0x10;
997  }
998 
999  if (ret >> 1) // consider the left part of the requested peak
1000  {
1001  // If the list of peaks is empty, compute it
1002  if (peaks.empty()) {
1003  /* nbpeaks = */ getPeaks(peaks);
1004  }
1005 
1006  // Go to the requested peak in the list
1007  std::list<vpHistogramPeak>::const_iterator it;
1008  unsigned index = 0;
1009  std::list<vpHistogramPeak>::const_iterator peaks_end = peaks.end();
1010  for (it = peaks.begin(); it != peaks_end; ++it) {
1011  if (peak == *it) {
1012  // we are on the peak.
1013  break;
1014  }
1015  ++index;
1016  }
1017 
1018  bool found = false;
1019  if (index == 0) {
1020  // No chance to get a peak on the left
1021  // should not occur !
1022  peakl.set(0, 0);
1023  }
1024  else {
1025  // search for the nearest peak on the left that matches the distance
1026  std::list<vpHistogramPeak>::const_iterator lit; // left iterator
1027  for (lit = peaks.begin(); lit != it; ++lit) {
1028  if (abs((*lit).getLevel() - peak.getLevel()) > dist) {
1029  // peakl found
1030  peakl = *lit;
1031  found = true; // we cannot stop here, since the other peaks on the
1032  // right may exist
1033  }
1034  }
1035  }
1036  if (!found) {
1037  peakl.set(0, 0);
1038  }
1039 
1040  // Search the valey on the left
1041  mini = peak.getValue();
1042  sumindmini = 0;
1043  nbmini = 0;
1044  unsigned peak_level = peak.getLevel();
1045  for (unsigned i = peakl.getLevel(); i < peak_level; ++i) {
1046  if (m_histogram[i] < mini) {
1047  mini = m_histogram[i];
1048  nbmini = 1;
1049  sumindmini = i;
1050  // continue
1051  }
1052  else {
1053  if (m_histogram[i] == mini) {
1054  sumindmini += i;
1055  ++nbmini;
1056  }
1057  }
1058  }
1059  if (nbmini == 0) {
1060  valeyl.set(0, 0);
1061  ret &= 0x01;
1062  }
1063  else {
1064  unsigned int minipos = sumindmini / nbmini; // position of the minimum
1065  valeyl.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
1066  ret &= 0x11;
1067  }
1068  }
1069 
1070  if (ret << 1) {
1071  // If the list of peaks is empty, compute it
1072  if (peaks.empty()) {
1073  /* nbpeaks = */ getPeaks(peaks);
1074  }
1075  // Go to the requested peak in the list
1076  std::list<vpHistogramPeak>::const_iterator it;
1077  std::list<vpHistogramPeak>::const_iterator peaks_end = peaks.end();
1078  for (it = peaks.begin(); it != peaks_end; ++it) {
1079  if (peak == *it) {
1080  // we are on the peak.
1081  break;
1082  }
1083  }
1084 
1085  bool found = false;
1086  // search for the nearest peak on the right that matches the distance
1087  std::list<vpHistogramPeak>::const_iterator rit; // right iterator
1088  std::list<vpHistogramPeak>::const_iterator peaks_end_s = peaks.end();
1089  for (rit = it; rit != peaks_end_s; ++rit) {
1090 
1091  if (abs((*rit).getLevel() - peak.getLevel()) > dist) {
1092  // peakr found
1093  peakr = *rit;
1094  found = true;
1095  break; // we can stop here
1096  }
1097  }
1098 
1099  if (!found) {
1100  peakr.set(static_cast<unsigned char>(m_size - 1), 0);
1101  }
1102 
1103  // Search the valey on the right
1104  mini = peak.getValue();
1105  sumindmini = 0;
1106  nbmini = 0;
1107  unsigned int peakr_level = static_cast<unsigned int>(peakr.getLevel());
1108  for (unsigned i = static_cast<unsigned int>(peak.getLevel()) + 1; i <= peakr_level; ++i) {
1109  if (m_histogram[i] < mini) {
1110  mini = m_histogram[i];
1111  nbmini = 1;
1112  sumindmini = i;
1113  // continue
1114  }
1115  else {
1116  if (m_histogram[i] == mini) {
1117  sumindmini += i;
1118  ++nbmini;
1119  }
1120  }
1121  }
1122  if (nbmini == 0) {
1123  valeyr.set(static_cast<unsigned char>(m_size - 1), 0);
1124  ret &= 0x10;
1125  }
1126  else {
1127  unsigned int minipos = sumindmini / nbmini; // position of the minimum
1128  valeyr.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
1129  ret &= 0x11;
1130  }
1131  }
1132 
1133  return ret;
1134 }
1135 
1144 unsigned vpHistogram::sort(std::list<vpHistogramPeak> &peaks)
1145 {
1146 
1147  if (peaks.empty()) {
1148  return 0;
1149  }
1150 
1151  peaks.sort(compare_vpHistogramPeak);
1152 
1153  return static_cast<unsigned int>(peaks.size());
1154 }
1155 
1168 bool vpHistogram::write(const std::string &filename) { return (this->write(filename.c_str())); }
1169 
1182 bool vpHistogram::write(const char *filename)
1183 {
1184  FILE *fd = fopen(filename, "w");
1185  if (fd == nullptr) {
1186  return false;
1187  }
1188  for (unsigned i = 0; i < m_size; ++i) {
1189  fprintf(fd, "%u %u\n", i, m_histogram[i]);
1190  }
1191  fclose(fd);
1192 
1193  return true;
1194 }
1195 END_VISP_NAMESPACE
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static void displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=true)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ divideByZeroError
Division by zero.
Definition: vpException.h:70
Declaration of the peak (maximum value) in a gray level image histogram.
void set(unsigned char lvl, unsigned val)
unsigned getValue() const
unsigned char getLevel() const
Declaration of the valey (minimum value) in a gray level image histogram.
void set(unsigned char lvl, unsigned val)
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned getPeaks(std::list< vpHistogramPeak > &peaks)
void smooth(unsigned int fsize=3)
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
vpHistogram & operator=(const vpHistogram &h)
unsigned sort(std::list< vpHistogramPeak > &peaks)
virtual ~vpHistogram()
unsigned getValey(std::list< vpHistogramValey > &valey)
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
Definition: vpHistogram.h:246
void equalize(const vpImage< unsigned char > &I, vpImage< unsigned char > &Iout)
bool write(const std::string &filename)
void display(const vpImage< unsigned char > &I, const vpColor &color=vpColor::white, unsigned int thickness=2, unsigned int maxValue_=0)
Error that can be emitted by the vpImage class and its derivatives.
@ notInitializedError
Image not initialized.
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
unsigned int getWidth() const
Definition: vpImage.h:242
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage_lut.h:176
unsigned int getSize() const
Definition: vpImage.h:221
Type * bitmap
points toward the bitmap
Definition: vpImage.h:135
unsigned int getHeight() const
Definition: vpImage.h:181
static int round(double x)
Definition: vpMath.h:409