Visual Servoing Platform  version 3.6.1 under development (2024-04-20)
vpHistogram.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Gray level histogram manipulation.
33  *
34 *****************************************************************************/
35 
45 #include <stdlib.h>
46 #include <visp3/core/vpDisplay.h>
47 #include <visp3/core/vpHistogram.h>
48 #include <visp3/core/vpImageConvert.h>
49 
50 #if defined(VISP_HAVE_THREADS)
51 #include <thread>
52 
53 namespace
54 {
55 struct vpHistogram_Param_t
56 {
57  unsigned int m_start_index;
58  unsigned int m_end_index;
59 
60  unsigned int m_lut[256];
61  unsigned int *m_histogram;
62  const vpImage<unsigned char> *m_I;
63  const vpImage<bool> *m_mask;
64 
65  vpHistogram_Param_t() : m_start_index(0), m_end_index(0), m_lut(), m_histogram(nullptr), m_I(nullptr), m_mask(nullptr) { }
66 
67  vpHistogram_Param_t(unsigned int start_index, unsigned int end_index, const vpImage<unsigned char> *const I, const vpImage<bool> *const mask)
68  : m_start_index(start_index), m_end_index(end_index), m_lut(), m_histogram(nullptr), m_I(I), m_mask(mask)
69  { }
70 
71  ~vpHistogram_Param_t()
72  {
73  if (m_histogram != nullptr) {
74  delete[] m_histogram;
75  }
76  }
77 };
78 
79 void computeHistogramThread(vpHistogram_Param_t *histogram_param)
80 {
81  unsigned int start_index = histogram_param->m_start_index;
82  unsigned int end_index = histogram_param->m_end_index;
83 
84  const vpImage<unsigned char> *I = histogram_param->m_I;
85 
86  unsigned char *ptrStart = (unsigned char *)(I->bitmap) + start_index;
87  unsigned char *ptrEnd = (unsigned char *)(I->bitmap) + end_index;
88  unsigned char *ptrCurrent = ptrStart;
89 
90  const bool alwaysTrue = true;
91  const bool *ptrMaskCurrent = &alwaysTrue;
92  if (histogram_param->m_mask) {
93  ptrMaskCurrent = (const bool *)histogram_param->m_mask->bitmap + start_index;
94  }
95 
96  if (end_index >= 8 + start_index) {
97  // Unroll loop version
98  for (; ptrCurrent <= ptrEnd - 8;) {
99  if (*ptrMaskCurrent) {
100  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
101  }
102  ++ptrCurrent;
103  if (histogram_param->m_mask != nullptr) {
104  ++ptrMaskCurrent;
105  }
106 
107  if (*ptrMaskCurrent) {
108  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
109  }
110  ++ptrCurrent;
111  if (histogram_param->m_mask != nullptr) {
112  ++ptrMaskCurrent;
113  }
114 
115  if (*ptrMaskCurrent) {
116  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
117  }
118  ++ptrCurrent;
119  if (histogram_param->m_mask != nullptr) {
120  ++ptrMaskCurrent;
121  }
122 
123  if (*ptrMaskCurrent) {
124  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
125  }
126  ++ptrCurrent;
127  if (histogram_param->m_mask != nullptr) {
128  ++ptrMaskCurrent;
129  }
130 
131  if (*ptrMaskCurrent) {
132  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
133  }
134  ++ptrCurrent;
135  if (histogram_param->m_mask != nullptr) {
136  ++ptrMaskCurrent;
137  }
138 
139  if (*ptrMaskCurrent) {
140  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
141  }
142  ++ptrCurrent;
143  if (histogram_param->m_mask != nullptr) {
144  ++ptrMaskCurrent;
145  }
146 
147  if (*ptrMaskCurrent) {
148  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
149  }
150  ++ptrCurrent;
151  if (histogram_param->m_mask != nullptr) {
152  ++ptrMaskCurrent;
153  }
154 
155  if (*ptrMaskCurrent) {
156  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
157  }
158  ++ptrCurrent;
159  if (histogram_param->m_mask != nullptr) {
160  ++ptrMaskCurrent;
161  }
162  }
163  }
164 
165  for (; ptrCurrent != ptrEnd; ++ptrCurrent) {
166  if (*ptrMaskCurrent) {
167  histogram_param->m_histogram[histogram_param->m_lut[*ptrCurrent]]++;
168  }
169  if (histogram_param->m_mask != nullptr) {
170  ++ptrMaskCurrent;
171  }
172  }
173 }
174 } // namespace
175 #endif
176 
177 bool compare_vpHistogramPeak(vpHistogramPeak first, vpHistogramPeak second);
178 
179 // comparison,
180 bool compare_vpHistogramPeak(vpHistogramPeak first, vpHistogramPeak second)
181 {
182  if (first.getValue() > second.getValue()) {
183  return true;
184  }
185  else {
186  return false;
187  }
188 }
189 
193 vpHistogram::vpHistogram() : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0) { init(); }
194 
198 vpHistogram::vpHistogram(const vpHistogram &h) : m_histogram(nullptr), m_size(256), mp_mask(h.mp_mask), m_total(h.m_total)
199 {
200  init(h.m_size);
201  memcpy(m_histogram, h.m_histogram, m_size * sizeof(unsigned));
202 }
203 
211 vpHistogram::vpHistogram(const vpImage<unsigned char> &I) : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0)
212 {
213  init();
214 
215  calculate(I);
216 }
217 
226 vpHistogram::vpHistogram(const vpImage<unsigned char> &I, const vpImage<bool> *p_mask) : m_histogram(nullptr), m_size(256), mp_mask(nullptr), m_total(0)
227 {
228  init();
229  setMask(p_mask);
230  calculate(I);
231 }
232 
237 {
238  if (m_histogram != nullptr) {
239  delete[] m_histogram;
240  m_histogram = nullptr;
241  m_size = 0;
242  }
243 }
244 
259 {
260  init(h.m_size);
261  memcpy(m_histogram, h.m_histogram, m_size * sizeof(unsigned));
262  mp_mask = h.mp_mask;
263  m_total = h.m_total;
264 
265  return *this;
266 }
267 
273 void vpHistogram::init(unsigned size_)
274 {
275  if (m_histogram != nullptr) {
276  delete[] m_histogram;
277  m_histogram = nullptr;
278  }
279 
280  this->m_size = size_;
281  m_histogram = new unsigned[m_size];
282 
283  memset(m_histogram, 0, m_size * sizeof(unsigned));
284 
285  mp_mask = nullptr;
286  m_total = 0;
287 }
288 
297 void vpHistogram::calculate(const vpImage<unsigned char> &I, unsigned int nbins, unsigned int nbThreads)
298 {
299  if (m_size != nbins) {
300  if (m_histogram != nullptr) {
301  delete[] m_histogram;
302  m_histogram = nullptr;
303  }
304 
305  m_size = nbins > 256 ? 256 : (nbins > 0 ? nbins : 256);
306  if ((nbins > 256) || (nbins == 0)) {
307  std::cerr << "nbins=" << nbins << " , nbins should be between ]0 ; 256] ; use by default nbins=256" << std::endl;
308  }
309  m_histogram = new unsigned int[m_size];
310  }
311 
312  memset(m_histogram, 0, m_size * sizeof(unsigned int));
313 
314  bool use_single_thread;
315 #if !defined(VISP_HAVE_THREADS)
316  use_single_thread = true;
317 #else
318  use_single_thread = (nbThreads == 0 || nbThreads == 1);
319 #endif
320 
321  if ((!use_single_thread) && (I.getSize() <= nbThreads)) {
322  use_single_thread = true;
323  }
324 
325  unsigned int lut[256];
326  for (unsigned int i = 0; i < 256; ++i) {
327  lut[i] = static_cast<unsigned int>((i * m_size) / 256.0);
328  }
329 
330  if (use_single_thread) {
331  // Single thread
332  const bool alwaysTrue = true;
333  const bool *ptrMaskCurrent = &alwaysTrue;
334  if (mp_mask) {
335  ptrMaskCurrent = static_cast<const bool *>(mp_mask->bitmap);
336  }
337 
338  unsigned int size_ = I.getWidth() * I.getHeight();
339  unsigned char *ptrStart = static_cast<unsigned char *>(I.bitmap);
340  unsigned char *ptrEnd = ptrStart + size_;
341  unsigned char *ptrCurrent = ptrStart;
342 
343  m_total = 0;
344  while (ptrCurrent != ptrEnd) {
345  if (*ptrMaskCurrent) {
346  ++m_histogram[lut[*ptrCurrent]];
347  ++m_total;
348  }
349  ++ptrCurrent;
350  if (mp_mask) {
351  ++ptrMaskCurrent;
352  }
353  }
354  }
355  else {
356 #if defined(VISP_HAVE_THREADS)
357  // Multi-threads
358  std::vector<std::thread *> threadpool;
359  std::vector<vpHistogram_Param_t *> histogramParams;
360 
361  unsigned int image_size = I.getSize();
362  unsigned int step = image_size / nbThreads;
363  unsigned int last_step = image_size - step * (nbThreads - 1);
364 
365  for (unsigned int index = 0; index < nbThreads; ++index) {
366  unsigned int start_index = index * step;
367  unsigned int end_index = (index + 1) * step;
368 
369  if (index == nbThreads - 1) {
370  end_index = start_index + last_step;
371  }
372 
373  vpHistogram_Param_t *histogram_param = new vpHistogram_Param_t(start_index, end_index, &I, mp_mask);
374  histogram_param->m_histogram = new unsigned int[m_size];
375  histogram_param->m_mask = mp_mask;
376  memset(histogram_param->m_histogram, 0, m_size * sizeof(unsigned int));
377  memcpy(histogram_param->m_lut, lut, 256 * sizeof(unsigned int));
378 
379  histogramParams.push_back(histogram_param);
380 
381  // Start the threads
382  std::thread *histogram_thread = new std::thread(&computeHistogramThread, histogram_param);
383  threadpool.push_back(histogram_thread);
384  }
385 
386  for (size_t cpt = 0; cpt < threadpool.size(); ++cpt) {
387  // Wait until thread ends up
388  threadpool[cpt]->join();
389  }
390 
391  m_total = 0;
392  for (unsigned int cpt1 = 0; cpt1 < m_size; ++cpt1) {
393  unsigned int sum = 0;
394 
395  for (size_t cpt2 = 0; cpt2 < histogramParams.size(); ++cpt2) {
396  sum += histogramParams[cpt2]->m_histogram[cpt1];
397  }
398 
399  m_histogram[cpt1] = sum;
400  m_total += sum;
401  }
402 
403  // Delete
404  for (size_t cpt = 0; cpt < threadpool.size(); ++cpt) {
405  delete threadpool[cpt];
406  }
407 
408  for (size_t cpt = 0; cpt < histogramParams.size(); ++cpt) {
409  delete histogramParams[cpt];
410  }
411 #endif
412  }
413 }
414 
416 {
417  // Compute the histogram
418  calculate(I);
419 
420  // Calculate the cumulative distribution function
421  unsigned int cdf[256];
422  unsigned int cdfMin = std::numeric_limits<unsigned int>::max(), cdfMax = 0;
423  unsigned int minValue = std::numeric_limits<unsigned int>::max(), maxValue = 0;
424  cdf[0] = m_histogram[0];
425 
426  if ((cdf[0] < cdfMin) && (cdf[0] > 0)) {
427  cdfMin = cdf[0];
428  minValue = 0;
429  }
430 
431  for (unsigned int i = 1; i < 256; ++i) {
432  cdf[i] = cdf[i - 1] + m_histogram[i];
433 
434  if ((cdf[i] < cdfMin) && (cdf[i] > 0)) {
435  cdfMin = cdf[i];
436  minValue = i;
437  }
438 
439  if (cdf[i] > cdfMax) {
440  cdfMax = cdf[i];
441  maxValue = i;
442  }
443  }
444 
445  unsigned int nbPixels = I.getWidth() * I.getHeight();
446  if (nbPixels == cdfMin) {
447  // Only one brightness value in the image
448  return;
449  }
450 
451  // Construct the look-up table
452  unsigned char lut[256];
453  for (unsigned int x = minValue; x <= maxValue; ++x) {
454  lut[x] = vpMath::round((cdf[x] - cdfMin) / static_cast<double>(nbPixels - cdfMin) * 255.0);
455  }
456 
457  Iout = I;
458  Iout.performLut(lut);
459 }
460 
472 void vpHistogram::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness,
473  unsigned int maxValue_)
474 {
475  unsigned int width = I.getWidth(), height = I.getHeight();
476  // Minimal width and height are 36 px
477  if ((width < 36) || (height < 36)) {
478  std::cerr << "Image I must have at least width >= 36 && height >= 36 !" << std::endl;
479  return;
480  }
481 
482  unsigned int maxValue = maxValue_;
483  if (maxValue == 0) {
484  for (unsigned int i = 0; i < m_size; ++i) {
485  if (m_histogram[i] > maxValue) {
486  maxValue = m_histogram[i];
487  }
488  }
489  }
490 
491  if (maxValue == 0) {
492  throw(vpException(vpException::divideByZeroError, "Cannot display histogram; max value=0"));
493  }
494  // Keep 12 free pixels at the top
495  unsigned int max_height = height - 12;
496  double ratio_height = max_height / static_cast<double>(maxValue);
497  double ratio_width = (width - 1) / static_cast<double>(m_size - 1.0);
498 
499  for (unsigned int i = 1; i < m_size; ++i) {
500  unsigned int value1 = m_histogram[i - 1];
501  unsigned int value2 = m_histogram[i];
502 
503  vpImagePoint startPt((height - 1) - (value1 * ratio_height), (i - 1) * ratio_width);
504  vpImagePoint endPt((height - 1) - (value2 * ratio_height), (i * ratio_width));
505  vpDisplay::displayLine(I, startPt, endPt, color, thickness);
506  }
507 }
508 
527 void vpHistogram::smooth(unsigned int fsize)
528 {
529  if (m_histogram == nullptr) {
530  vpERROR_TRACE("Histogram array not initialised\n");
531  throw(vpImageException(vpImageException::notInitializedError, "Histogram array not initialised"));
532  }
533 
534  vpHistogram h;
535  h = *this;
536 
537  int hsize = static_cast<int>(fsize) / 2; // half filter size
538 
539  for (unsigned i = 0; i < m_size; ++i) {
540  unsigned int sum = 0;
541  unsigned int nb = 0;
542  for (int j = -hsize; j <= hsize; ++j) {
543  // exploitation of the overflow to detect negative value...
544  if (/*(i + j) >= 0 &&*/ (i + static_cast<unsigned int>(j)) < m_size) {
545  sum += h.m_histogram[i + static_cast<unsigned int>(j)];
546  ++nb;
547  }
548  }
549  m_histogram[i] = sum / nb;
550  }
551 }
552 
567 unsigned vpHistogram::getPeaks(std::list<vpHistogramPeak> &peaks)
568 {
569  if (m_histogram == nullptr) {
570  vpERROR_TRACE("Histogram array not initialised\n");
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  continue;
593  }
594 
595  // Peak detection
596  if ((prev_slope > 0) && (next_slope < 0)) {
597  sum_level += i;
598  ++cpt;
599 
600  unsigned int level = sum_level / cpt;
601  p.set(static_cast<unsigned char>(level), m_histogram[level]);
602  peaks.push_back(p);
603 
604  ++nbpeaks;
605  }
606 
607  prev_slope = next_slope;
608  sum_level = 0;
609  cpt = 0;
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  // Peak detection
719  if ((prev_slope > 0) && (next_slope < 0)) {
720  peak[nbpeaks++] = static_cast<unsigned char>(i);
721  }
722 
723  prev_slope = next_slope;
724  }
725  if (prev_slope > 0) {
726  peak[nbpeaks++] = static_cast<unsigned char>(m_size - 1);
727  }
728 
729  // Get the global maximum
730  index_highest_peak = 0;
731  for (unsigned int i = 0; i < nbpeaks; ++i) {
732  if (m_histogram[peak[i]] > m_histogram[peak[index_highest_peak]]) {
733  index_highest_peak = i;
734  }
735  }
736 
737  maxprof = 0;
738  index_second_peak = index_highest_peak;
739 
740  // Search second local maximum on the left of the global maximum
741  for (unsigned i = 0; i < index_highest_peak; ++i) {
742  if ((peak[index_highest_peak] - peak[i]) > dist) {
743  prof = 0;
744  for (int j = peak[i]; j <= (peak[index_highest_peak] - dist); ++j) {
745  if ((m_histogram[peak[i]] - m_histogram[j]) > prof) {
746  prof = m_histogram[peak[i]] - m_histogram[j];
747  }
748  }
749 
750  if (prof > maxprof) {
751  maxprof = prof;
752  index_second_peak = i;
753  }
754  }
755  }
756 
757  // Search second local maximum on the right of the global maximum
758  for (unsigned i = index_highest_peak + 1; i < nbpeaks; ++i) {
759  if ((peak[i] - peak[index_highest_peak]) > dist) {
760  prof = 0;
761  for (int j = peak[index_highest_peak] + dist; j <= peak[i]; ++j) {
762  if ((m_histogram[peak[i]] - m_histogram[j]) > prof) {
763  prof = m_histogram[peak[i]] - m_histogram[j];
764  }
765  }
766 
767  if (prof > maxprof) {
768  maxprof = prof;
769  index_second_peak = i;
770  }
771  }
772  }
773 
774  // Determine position of the first and second highest peaks
775  if (peak[index_highest_peak] < peak[index_second_peak]) {
776  peakr.set(peak[index_second_peak], m_histogram[peak[index_second_peak]]);
777  peakl.set(peak[index_highest_peak], m_histogram[peak[index_highest_peak]]);
778  }
779  else {
780  peakl.set(peak[index_second_peak], m_histogram[peak[index_second_peak]]);
781  peakr.set(peak[index_highest_peak], m_histogram[peak[index_highest_peak]]);
782  }
783 
784  if (peakl == peakr) {
785 
786  delete[] peak;
787 
788  return false; // Not a bimodal histogram
789  }
790 
791  // Search the valey
792  mini = peakl.getValue();
793  sumindmini = 0;
794  nbmini = 0;
795  unsigned peakr_level = peakr.getLevel();
796  for (unsigned i = peakl.getLevel(); i <= peakr_level; ++i) {
797  if (m_histogram[i] < mini) {
798  mini = m_histogram[i];
799  nbmini = 1;
800  sumindmini = i;
801  continue;
802  }
803  if (m_histogram[i] == mini) {
804  sumindmini += i;
805  ++nbmini;
806  }
807  }
808 
809  if (nbmini == 0) {
810  // no valey found
811  valey.set(0, 0);
812 
813  delete[] peak;
814 
815  return false;
816  }
817  else {
818  mini = sumindmini / nbmini; // mean
819  valey.set(static_cast<unsigned char>(mini), m_histogram[mini]);
820 
821  delete[] peak;
822 
823  return true;
824  }
825 }
826 
838 unsigned vpHistogram::getValey(std::list<vpHistogramValey> &valey)
839 {
840  if (m_histogram == nullptr) {
841  vpERROR_TRACE("Histogram array not initialised\n");
842  throw(vpImageException(vpImageException::notInitializedError, "Histogram array not initialised"));
843  }
844 
845  int prev_slope; // Previous histogram inclination
846  vpHistogramValey p; // An histogram valey
847  unsigned nbvaley; // Number of valey in the histogram (ie local minima)
848 
849  valey.clear();
850 
851  // Parse the histogram to get the local minima
852  unsigned cpt = 0;
853  unsigned sum_level = 0;
854  nbvaley = 0;
855  prev_slope = -1;
856 
857  for (unsigned i = 0; i < (m_size - 1); ++i) {
858  int next_slope = static_cast<int>(m_histogram[i + 1]) - static_cast<int>(m_histogram[i]); // Next histogram inclination
859 
860  if ((prev_slope < 0) && (next_slope == 0)) {
861  sum_level += i + 1;
862  ++cpt;
863  continue;
864  }
865 
866  // Valey detection
867  if ((prev_slope < 0) && (next_slope > 0)) {
868  sum_level += i;
869  ++cpt;
870 
871  unsigned int level = sum_level / cpt;
872  p.set(static_cast<unsigned char>(level), m_histogram[level]);
873  valey.push_back(p);
874 
875  ++nbvaley;
876  }
877 
878  prev_slope = next_slope;
879  sum_level = 0;
880  cpt = 0;
881  }
882  if (prev_slope < 0) {
883  p.set(static_cast<unsigned char>(m_size) - 1u, m_histogram[m_size - 1]);
884  valey.push_back(p);
885  ++nbvaley;
886  }
887 
888  return nbvaley;
889 }
890 
906 {
907 
908  // Set the left and right peaks
909  vpHistogramPeak peakl, peakr;
910  if (peak1.getLevel() > peak2.getLevel()) {
911  peakl = peak2;
912  peakr = peak1;
913  }
914  else {
915  peakl = peak1;
916  peakr = peak2;
917  }
918 
919  // Search the valey
920  unsigned int nbmini; // Minimum numbers
921  unsigned int sumindmini; // Sum
922  unsigned int mini; // current minimum
923 
924  mini = peakl.getValue();
925  sumindmini = 0;
926  nbmini = 0;
927  unsigned peakr_level = peakr.getLevel();
928  for (unsigned i = peakl.getLevel(); i <= peakr_level; ++i) {
929  if (m_histogram[i] < mini) {
930  mini = m_histogram[i];
931  nbmini = 1;
932  sumindmini = i;
933  continue;
934  }
935  if (m_histogram[i] == mini) {
936  sumindmini += i;
937  ++nbmini;
938  }
939  }
940 
941  if (nbmini == 0) {
942  // no valey found
943  valey.set(0, 0);
944 
945  return false;
946  }
947  else {
948  unsigned int minipos = sumindmini / nbmini; // position of the minimum
949 
950  valey.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
951  return true;
952  }
953 }
972 unsigned vpHistogram::getValey(unsigned char dist, const vpHistogramPeak &peak, vpHistogramValey &valeyl,
973  vpHistogramValey &valeyr)
974 {
975  unsigned int ret = 0x11;
976  unsigned int nbmini; // Minimum numbers
977  unsigned int sumindmini; // Sum
978  unsigned int mini; // current minimum
979  vpHistogramPeak peakr, peakl; // Left and right peaks of peak
980  std::list<vpHistogramPeak> peaks; // list of histogram peaks
981  // unsigned int nbpeaks=0; // Number of peaks in the histogram (ie local
982  // maxima)
983 
984  if (peak.getLevel() == 0) {
985  valeyl.set(0, 0);
986  ret &= 0x01;
987  }
988  if (peak.getLevel() == (m_size - 1)) {
989  valeyr.set(static_cast<unsigned char>(m_size - 1), 0);
990  ret &= 0x10;
991  }
992 
993  if (ret >> 1) // consider the left part of the requested peak
994  {
995  // If the list of peaks is empty, compute it
996  if (peaks.empty()) {
997  /* nbpeaks = */ getPeaks(peaks);
998  }
999 
1000  // Go to the requested peak in the list
1001  std::list<vpHistogramPeak>::const_iterator it;
1002  unsigned index = 0;
1003  std::list<vpHistogramPeak>::const_iterator peaks_end = peaks.end();
1004  for (it = peaks.begin(); it != peaks_end; ++it) {
1005  if (peak == *it) {
1006  // we are on the peak.
1007  break;
1008  }
1009  ++index;
1010  }
1011 
1012  bool found = false;
1013  if (index == 0) {
1014  // No chance to get a peak on the left
1015  // should not occur !
1016  peakl.set(0, 0);
1017  }
1018  else {
1019  // search for the nearest peak on the left that matches the distance
1020  std::list<vpHistogramPeak>::const_iterator lit; // left iterator
1021  for (lit = peaks.begin(); lit != it; ++lit) {
1022  if (abs((*lit).getLevel() - peak.getLevel()) > dist) {
1023  // peakl found
1024  peakl = *lit;
1025  found = true; // we cannot stop here, since the other peaks on the
1026  // right may exist
1027  }
1028  }
1029  }
1030  if (!found) {
1031  peakl.set(0, 0);
1032  }
1033 
1034  // Search the valey on the left
1035  mini = peak.getValue();
1036  sumindmini = 0;
1037  nbmini = 0;
1038  unsigned peak_level = peak.getLevel();
1039  for (unsigned i = peakl.getLevel(); i < peak_level; ++i) {
1040  if (m_histogram[i] < mini) {
1041  mini = m_histogram[i];
1042  nbmini = 1;
1043  sumindmini = i;
1044  continue;
1045  }
1046  if (m_histogram[i] == mini) {
1047  sumindmini += i;
1048  ++nbmini;
1049  }
1050  }
1051  if (nbmini == 0) {
1052  valeyl.set(0, 0);
1053  ret &= 0x01;
1054  }
1055  else {
1056  unsigned int minipos = sumindmini / nbmini; // position of the minimum
1057  valeyl.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
1058  ret &= 0x11;
1059  }
1060  }
1061 
1062  if (ret << 1) {
1063  // If the list of peaks is empty, compute it
1064  if (peaks.empty()) {
1065  /* nbpeaks = */ getPeaks(peaks);
1066  }
1067  // Go to the requested peak in the list
1068  std::list<vpHistogramPeak>::const_iterator it;
1069  std::list<vpHistogramPeak>::const_iterator peaks_end = peaks.end();
1070  for (it = peaks.begin(); it != peaks_end; ++it) {
1071  if (peak == *it) {
1072  // we are on the peak.
1073  break;
1074  }
1075  }
1076 
1077  bool found = false;
1078  // search for the nearest peak on the right that matches the distance
1079  std::list<vpHistogramPeak>::const_iterator rit; // right iterator
1080  std::list<vpHistogramPeak>::const_iterator peaks_end_s = peaks.end();
1081  for (rit = it; rit != peaks_end_s; ++rit) {
1082 
1083  if (abs((*rit).getLevel() - peak.getLevel()) > dist) {
1084  // peakr found
1085  peakr = *rit;
1086  found = true;
1087  break; // we can stop here
1088  }
1089  }
1090 
1091  if (!found) {
1092  peakr.set(static_cast<unsigned char>(m_size - 1), 0);
1093  }
1094 
1095  // Search the valey on the right
1096  mini = peak.getValue();
1097  sumindmini = 0;
1098  nbmini = 0;
1099  unsigned int peakr_level = static_cast<unsigned int>(peakr.getLevel());
1100  for (unsigned i = static_cast<unsigned int>(peak.getLevel()) + 1; i <= peakr_level; ++i) {
1101  if (m_histogram[i] < mini) {
1102  mini = m_histogram[i];
1103  nbmini = 1;
1104  sumindmini = i;
1105  continue;
1106  }
1107  if (m_histogram[i] == mini) {
1108  sumindmini += i;
1109  ++nbmini;
1110  }
1111  }
1112  if (nbmini == 0) {
1113  valeyr.set(static_cast<unsigned char>(m_size - 1), 0);
1114  ret &= 0x10;
1115  }
1116  else {
1117  unsigned int minipos = sumindmini / nbmini; // position of the minimum
1118  valeyr.set(static_cast<unsigned char>(minipos), m_histogram[minipos]);
1119  ret &= 0x11;
1120  }
1121  }
1122 
1123  return ret;
1124 }
1125 
1134 unsigned vpHistogram::sort(std::list<vpHistogramPeak> &peaks)
1135 {
1136 
1137  if (peaks.empty()) {
1138  return 0;
1139  }
1140 
1141  peaks.sort(compare_vpHistogramPeak);
1142 
1143  return static_cast<unsigned int>(peaks.size());
1144 }
1145 
1158 bool vpHistogram::write(const std::string &filename) { return (this->write(filename.c_str())); }
1159 
1172 bool vpHistogram::write(const char *filename)
1173 {
1174  FILE *fd = fopen(filename, "w");
1175  if (fd == nullptr) {
1176  return false;
1177  }
1178  for (unsigned i = 0; i < m_size; ++i) {
1179  fprintf(fd, "%u %u\n", i, m_histogram[i]);
1180  }
1181  fclose(fd);
1182 
1183  return true;
1184 }
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:152
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:59
@ divideByZeroError
Division by zero.
Definition: vpException.h:82
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:108
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:248
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:245
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage.h:2277
unsigned int getSize() const
Definition: vpImage.h:224
Type * bitmap
points toward the bitmap
Definition: vpImage.h:139
unsigned int getHeight() const
Definition: vpImage.h:184
static int round(double x)
Definition: vpMath.h:403
#define vpERROR_TRACE
Definition: vpDebug.h:382