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