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