Visual Servoing Platform  version 3.6.1 under development (2024-11-21)
vpImgproc.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  * Convert image types.
32  */
33 /* Autostretch HSV 0.10 --- image filter plug-in for GIMP
34  *
35  * Copyright (C) 1997 Scott Goehring
36  * Copyright (C) 1996 Federico Mena Quintero
37  *
38  * You can contact me at scott@poverty.bloomington.in.us
39  *
40  * This program is free software: you can redistribute it and/or modify
41  * it under the terms of the GNU General Public License as published by
42  * the Free Software Foundation; either version 3 of the License, or
43  * (at your option) any later version.
44  *
45  * This program is distributed in the hope that it will be useful,
46  * but WITHOUT ANY WARRANTY; without even the implied warranty of
47  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48  * GNU General Public License for more details.
49  *
50  * You should have received a copy of the GNU General Public License
51  * along with this program. If not, see <http://www.gnu.org/licenses/>.
52 */
53 
59 #include <visp3/core/vpGaussianFilter.h>
60 #include <visp3/core/vpHistogram.h>
61 #include <visp3/core/vpImageConvert.h>
62 #include <visp3/core/vpImageFilter.h>
63 #include <visp3/core/vpImageTools.h>
64 #include <visp3/core/vpMath.h>
65 #include <visp3/imgproc/vpImgproc.h>
66 
67 namespace VISP_NAMESPACE_NAME
68 {
69 #if defined(VISP_BUILD_DEPRECATED_FUNCTIONS) && defined(ENABLE_VISP_NAMESPACE)
70 using namespace VISP_NAMESPACE_NAME;
71 #endif
72 
73 std::string vpGammaMethodList(const std::string &pref, const std::string &sep, const std::string &suf)
74 {
75  std::string list(pref);
76  for (unsigned int i = 0; i < (GAMMA_METHOD_COUNT - 1); ++i) {
77  vpGammaMethod type = static_cast<vpGammaMethod>(i);
78  list += vpGammaMethodToString(type);
79  list += sep;
80  }
81  vpGammaMethod type = static_cast<vpGammaMethod>(GAMMA_METHOD_COUNT - 1);
82  list += vpGammaMethodToString(type);
83  list += suf;
84  return list;
85 }
86 
87 std::string vpGammaMethodToString(const vpGammaMethod &type)
88 {
89  std::string name;
90  switch (type) {
91  case GAMMA_MANUAL:
92  name = "gamma_manual";
93  break;
94  case GAMMA_LOG_BASED:
95  name = "gamma_log";
96  break;
98  name = "gamma_nonlinear";
99  break;
100  case GAMMA_CDF_BASED:
101  name = "gamma_cdf";
102  break;
104  name = "gamma_classification";
105  break;
107  name = "gamma_spatial_variant";
108  break;
109  case GAMMA_METHOD_COUNT:
110  default:
111  name = "gamma_method_unknown";
112  }
113  return name;
114 }
115 
116 vpGammaMethod vpGammaMethodFromString(const std::string &name)
117 {
119  unsigned int count = static_cast<unsigned int>(GAMMA_METHOD_COUNT);
120  bool notFound = true;
121  unsigned int i = 0;
122  while ((i < count) && notFound) {
123  vpGammaMethod temp = static_cast<vpGammaMethod>(i);
124  if (name == vpGammaMethodToString(temp)) {
125  type = temp;
126  notFound = false;
127  }
128  ++i;
129  }
130  return type;
131 }
132 
133 std::string vpGammaColorHandlingList(const std::string &pref, const std::string &sep, const std::string &suf)
134 {
135  std::string list(pref);
136  for (unsigned int i = 0; i < (GAMMA_COLOR_HANDLING_COUNT - 1); ++i) {
137  vpGammaColorHandling type = static_cast<vpGammaColorHandling>(i);
138  list += vpGammaColorHandlingToString(type);
139  list += sep;
140  }
142  list += vpGammaColorHandlingToString(type);
143  list += suf;
144  return list;
145 }
146 
148 {
149  std::string name;
150  switch (type) {
151  case GAMMA_RGB:
152  name = "gamma_color_rgb";
153  break;
154  case GAMMA_HSV:
155  name = "gamma_color_hsv";
156  break;
158  default:
159  name = "gamma_color_unknown";
160  }
161  return name;
162 }
163 
165 {
167  unsigned int count = static_cast<unsigned int>(GAMMA_COLOR_HANDLING_COUNT);
168  bool notFound = true;
169  unsigned int i = 0;
170  while ((i < count) && notFound) {
171  vpGammaColorHandling temp = static_cast<vpGammaColorHandling>(i);
172  if (name == vpGammaColorHandlingToString(temp)) {
173  type = temp;
174  notFound = false;
175  }
176  ++i;
177  }
178  return type;
179 }
180 
181 void adjust(vpImage<unsigned char> &I, double alpha, double beta)
182 {
183  // Construct the look-up table
184  const unsigned int lutSize = 256;
185  unsigned char lut[lutSize];
186  for (unsigned int i = 0; i < lutSize; ++i) {
187  lut[i] = vpMath::saturate<unsigned char>((alpha * i) + beta);
188  }
189 
190  // Apply the transformation using a LUT
191  I.performLut(lut);
192 }
193 
194 void adjust(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, double alpha, double beta)
195 {
196  // Copy I1 to I2
197  I2 = I1;
198 
199  adjust(I2, alpha, beta);
200 }
201 
202 void adjust(vpImage<vpRGBa> &I, double alpha, double beta)
203 {
204  // Construct the look-up table
205  const unsigned int lutSize = 256;
206  vpRGBa lut[lutSize];
207  for (unsigned int i = 0; i < lutSize; ++i) {
208  lut[i].R = vpMath::saturate<unsigned char>((alpha * i) + beta);
209  lut[i].G = vpMath::saturate<unsigned char>((alpha * i) + beta);
210  lut[i].B = vpMath::saturate<unsigned char>((alpha * i) + beta);
211  lut[i].A = vpMath::saturate<unsigned char>((alpha * i) + beta);
212  }
213 
214  // Apply the transformation using a LUT
215  I.performLut(lut);
216 }
217 
218 void adjust(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, double alpha, double beta)
219 {
220  // Copy I1 to I2
221  I2 = I1;
222 
223  adjust(I2, alpha, beta);
224 }
225 
227 {
228  vpImage<unsigned char> Icpy = I;
229  equalizeHistogram(Icpy, I, p_mask);
230 }
231 
233  const vpImage<bool> *p_mask)
234 {
235  if ((I1.getWidth() * I1.getHeight()) == 0) {
236  return;
237  }
238 
239  // Calculate the histogram
240  vpHistogram hist;
241  hist.setMask(p_mask);
242  hist.equalize(I1, I2);
243 }
244 
245 void equalizeHistogram(vpImage<vpRGBa> &I, bool useHSV)
246 {
247  if ((I.getWidth() * I.getHeight()) == 0) {
248  return;
249  }
250 
251  if (!useHSV) {
252  // Split the RGBa image into 4 images
257 
258  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
259 
260  // Apply histogram equalization for each channel
261  equalizeHistogram(pR);
262  equalizeHistogram(pG);
263  equalizeHistogram(pB);
264 
265  // Merge the result in I
266  unsigned int size = I.getWidth() * I.getHeight();
267  unsigned char *ptrStart = reinterpret_cast<unsigned char *>(I.bitmap);
268  unsigned char *ptrEnd = ptrStart + (size * 4);
269  unsigned char *ptrCurrent = ptrStart;
270 
271  unsigned int cpt = 0;
272  while (ptrCurrent != ptrEnd) {
273  *ptrCurrent = pR.bitmap[cpt];
274  ++ptrCurrent;
275 
276  *ptrCurrent = pG.bitmap[cpt];
277  ++ptrCurrent;
278 
279  *ptrCurrent = pB.bitmap[cpt];
280  ++ptrCurrent;
281 
282  *ptrCurrent = pa.bitmap[cpt];
283  ++ptrCurrent;
284 
285  ++cpt;
286  }
287  }
288  else {
290  vpImage<unsigned char> saturation(I.getHeight(), I.getWidth());
291  vpImage<unsigned char> value(I.getHeight(), I.getWidth());
292 
293  unsigned int size = I.getWidth() * I.getHeight();
294  // Convert from RGBa to HSV
295  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), reinterpret_cast<unsigned char *>(hue.bitmap),
296  reinterpret_cast<unsigned char *>(saturation.bitmap), reinterpret_cast<unsigned char *>(value.bitmap), size);
297 
298  // Histogram equalization on the value plane
299  equalizeHistogram(value);
300 
301  // Convert from HSV to RGBa
302  vpImageConvert::HSVToRGBa(reinterpret_cast<unsigned char *>(hue.bitmap), reinterpret_cast<unsigned char *>(saturation.bitmap),
303  reinterpret_cast<unsigned char *>(value.bitmap), reinterpret_cast<unsigned char *>(I.bitmap), size);
304  }
305 }
306 
307 void equalizeHistogram(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, bool useHSV)
308 {
309  I2 = I1;
310  equalizeHistogram(I2, useHSV);
311 }
312 
325 {
326  float mean = static_cast<float>(I.getMeanValue(p_mask));
327  unsigned char inputMin = 0, inputMax = 0;
328  I.getMinMaxValue(inputMin, inputMax);
329  unsigned char inputRange = inputMax - inputMin;
330 
331  float gamma_computed = static_cast<float>((std::log(128.f) - std::log(256.f)) / (std::log(mean) - std::log(inputRange)));
332 
333  // Construct the look-up table
334  unsigned char lut[256];
335  float inputRangeAsFloat = static_cast<float>(inputRange);
336  for (unsigned int i = inputMin; i <= inputMax; ++i) {
337  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i - inputMin) / inputRangeAsFloat, gamma_computed) * 255.f);
338  }
339 
340  I.performLut(lut);
341 }
342 
355 {
356  (void)p_mask;
357  const float a = 0.2f;
358  const float b = 0.3f;
359  const float c = 0.3f;
360  const float x_m = 127.5f;
361  const float alpha = std::atan2(-b, x_m);
362  const float rho = 0.1f;
363  const unsigned int lutSize = 256;
364  unsigned char lut[lutSize];
365  for (unsigned int i = 0; i < lutSize; ++i) {
366  float x = static_cast<float>(i);
367  float phi = (M_PI_FLOAT * x) / (2.f * x_m);
368  float f1 = a * std::cos(phi);
369  float k = rho * std::sin((4 * M_PI_FLOAT * x) / 255.f);
370  float f2 = ((k + b)*std::cos(alpha)) + (x * std::sin(alpha));
371  float r = c * std::abs((x / x_m) - 1.f);
372  float f3 = r * std::cos((3.f * M_PI_FLOAT * x) / 255.f);
373  float g = f1 + f2 + f3;
374  float gamma = 1 + g;
375  float inverse_gamma = 1.f / gamma;
376  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.f, inverse_gamma) * 255.f);
377  }
378  I.performLut(lut);
379 }
380 
394 {
395  double mean = I.getMeanValue(p_mask);
396  double stdev = I.getStdev(p_mask);
397  double meanNormalized = mean / 255.;
398  double stdevNormalized = stdev / 255.;
399  const float tau = 3.f;
400  bool isAlreadyHighContrast = (4. * stdevNormalized) > (1./tau);
401  const unsigned int lutSize = 256;
402  unsigned char lut[lutSize];
403  float gamma = 0.f;
404  if (isAlreadyHighContrast) {
405  // Case medium to high contrast image
406  gamma = static_cast<float>(std::exp((1.f - (meanNormalized + stdevNormalized))/2.f));
407  }
408  else {
409  // Case low contrast image
410 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
411  gamma = -static_cast<float>(std::log2(stdevNormalized));
412 #else
413  gamma = -static_cast<float>(std::log(stdevNormalized) / std::log(2.f));
414 #endif
415  }
416  if (meanNormalized < 0.5) {
417  // Case dark image
418  float meanPowerGamma = static_cast<float>(std::pow(meanNormalized, gamma));
419  for (unsigned int i = 0; i < lutSize; ++i) {
420  float iNormalized = static_cast<float>(i)/255.f;
421  float iPowerGamma = std::pow(iNormalized, gamma);
422  lut[i] = vpMath::saturate<unsigned char>(255.f * (iPowerGamma / (iPowerGamma + ((1.f - iPowerGamma) * meanPowerGamma))));
423  }
424  }
425  else {
426  // Case bright image
427  for (unsigned int i = 0; i < lutSize; ++i) {
428  float iNormalized = static_cast<float>(i)/255.f;
429  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
430  }
431  }
432  I.performLut(lut);
433 }
434 
448 {
449  const unsigned int nbBins = 256;
450  vpHistogram histo;
451  histo.setMask(p_mask);
452  histo.calculate(I, nbBins);
453  unsigned int totalNbPoints = histo.getTotal();
454  unsigned int minHisto = histo[0];
455  unsigned int maxHisto = histo[0];
456  for (unsigned int i = 1; i < nbBins; ++i) {
457  minHisto = std::min(minHisto, histo[i]);
458  maxHisto = std::max(maxHisto, histo[i]);
459  }
460  float pdfMin = static_cast<float>(minHisto) / static_cast<float>(totalNbPoints);
461  float pdfMax = static_cast<float>(maxHisto) / static_cast<float>(totalNbPoints);
462  float pdf_w[nbBins];
463  float sum_pdf_w = 0.f;
464  for (unsigned int i = 0; i < nbBins; ++i) {
465  float pdf = static_cast<float>(histo[i])/static_cast<float>(totalNbPoints);
466  pdf_w[i] = pdfMax * std::sqrt((pdf - pdfMin)/(pdfMax - pdfMin)); // alpha = 0.5
467  sum_pdf_w += pdf_w[i];
468  }
469  unsigned char lut[nbBins];
470  float cdf_w = 0;
471  for (unsigned int i = 0; i < nbBins; ++i) {
472  cdf_w += pdf_w[i] / sum_pdf_w;
473  float gamma = 1.f - cdf_w;
474  float iNormalized = static_cast<float>(i)/255.f;
475  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
476  }
477  I.performLut(lut);
478 }
479 
490 {
491  unsigned int width = I.getWidth(), height = I.getHeight();
492  const unsigned int scale2 = 2, scale4 = 4, scale8 = 8;
493  vpImage<unsigned char> I_2, I_4, I_8;
494  I.subsample(scale2, scale2, I_2);
495  I.subsample(scale4, scale4, I_4);
496  I.subsample(scale8, scale8, I_8);
497  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
498  const bool normalize = true;
499  const unsigned int gaussKernelSize = 3;
500  vpImageFilter::gaussianBlur(I, I_blur, gaussKernelSize, 0.f, normalize, p_mask);
501  vpImageFilter::gaussianBlur(I_2, I_2_blur, gaussKernelSize, 0.f, normalize, p_mask);
502  vpImageFilter::gaussianBlur(I_4, I_4_blur, gaussKernelSize, 0.f, normalize, p_mask);
503  vpImageFilter::gaussianBlur(I_8, I_8_blur, gaussKernelSize, 0.f, normalize, p_mask);
504  vpImage<float> L, L_2, L_4, L_8;
505  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
506  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
507  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
508  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
509  const float alpha = 0.5f;
510  unsigned int size = height * width;
511  float stdev = static_cast<float>(I.getStdev(p_mask));
512  float p;
513  const float stdevThresh1 = 40., stdevThresh2 = 80.;
514  if (stdev <= stdevThresh1) {
515  p = 2.f;
516  }
517  else if (stdev <= stdevThresh2) {
518  p = (-0.025f * stdev) + 3.f;
519  }
520  else {
521  p = 1.f;
522  }
523 
524  for (unsigned int i = 0; i < size; ++i) {
525  bool hasToCompute = true;
526  if (p_mask != nullptr) {
527  hasToCompute = p_mask->bitmap[i];
528  }
529  if (hasToCompute) {
530  float svlm = (L.bitmap[i] + L_2.bitmap[i] + L_4.bitmap[i] + L_8.bitmap[i]) / 4.f; // Computation of the space-variant luminance map
531  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
532  float iNormalized = static_cast<float>(I.bitmap[i])/255.f;
533  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
534  float r = svlm / o;
535  float e = std::pow(r, p);
536  float s = 255.f * std::pow(o / 255.f, e);
537  I.bitmap[i] = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i])) / o);
538  }
539  }
540 }
541 
552 {
553  unsigned int width = I.getWidth(), height = I.getHeight();
554  unsigned int size = height * width;
555  vpImage<unsigned char> I_gray(height, width);
556  for (unsigned int i = 0; i < size; ++i) {
557  vpRGBa rgb = I.bitmap[i];
558  I_gray.bitmap[i] = static_cast<unsigned char>((0.299 * rgb.R) + (0.587 * rgb.G) + (0.114 * rgb.B));
559  }
560  vpImage<unsigned char> I_2, I_4, I_8;
561  const unsigned int scale2 = 2, scale4 = 4, scale8 = 8;
562  I_gray.subsample(scale2, scale2, I_2);
563  I_gray.subsample(scale4, scale4, I_4);
564  I_gray.subsample(scale8, scale8, I_8);
565  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
566  const bool normalize = true;
567  const unsigned int gaussKernelSize = 3;
568  vpImageFilter::gaussianBlur(I_gray, I_blur, gaussKernelSize, 0.f, normalize, p_mask);
569  vpImageFilter::gaussianBlur(I_2, I_2_blur, gaussKernelSize, 0.f, normalize, p_mask);
570  vpImageFilter::gaussianBlur(I_4, I_4_blur, gaussKernelSize, 0.f, normalize, p_mask);
571  vpImageFilter::gaussianBlur(I_8, I_8_blur, gaussKernelSize, 0.f, normalize, p_mask);
572  vpImage<float> L, L_2, L_4, L_8;
573  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
574  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
575  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
576  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
577  const float alpha = 0.5f;
578 
579  float stdev = static_cast<float>(I.getStdev(p_mask));
580  float p;
581  const float stdevThresh1 = 40., stdevThresh2 = 80.;
582  if (stdev <= stdevThresh1) {
583  p = 2.f;
584  }
585  else if (stdev <= stdevThresh2) {
586  p = (-0.025f * stdev) + 3.f;
587  }
588  else {
589  p = 1.f;
590  }
591  for (unsigned int i = 0; i < size; ++i) {
592  bool hasToCompute = true;
593  if (p_mask != nullptr) {
594  hasToCompute = p_mask->bitmap[i];
595  }
596  if (hasToCompute) {
597  float svlm = (L.bitmap[i] + L_2.bitmap[i] + L_4.bitmap[i] + L_8.bitmap[i]) / 4.f; // Computation of the space-variant luminance map
598  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
599  float iNormalized = static_cast<float>(I_gray.bitmap[i])/255.f;
600  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
601  float r = svlm / o;
602  float e = std::pow(r, p);
603  float s = 255.f * std::pow(o / 255.f, e);
604  I.bitmap[i].R = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].R)) / o);
605  I.bitmap[i].G = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].G)) / o);
606  I.bitmap[i].B = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].B)) / o);
607  }
608  }
609 }
610 
611 void gammaCorrection(vpImage<unsigned char> &I, const float &gamma, const vpGammaMethod &method, const vpImage<bool> *p_mask)
612 {
613  float inverse_gamma = 1.0;
614  if ((gamma > 0) && (method == GAMMA_MANUAL)) {
615  inverse_gamma = 1.0f / gamma;
616  // Construct the look-up table
617  const unsigned int lutSize = 256;
618  unsigned char lut[lutSize];
619  for (unsigned int i = 0; i < lutSize; ++i) {
620  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.0, inverse_gamma) * 255.0);
621  }
622 
623  I.performLut(lut);
624  }
625  else if (method == GAMMA_MANUAL) {
626  std::stringstream errMsg;
627  errMsg << "ERROR: gamma correction factor (";
628  errMsg << gamma << ") cannot be negative when using a constant user-defined factor." << std::endl;
629  throw(vpException(vpException::badValue, errMsg.str()));
630  }
631  else if (gamma > 0) {
632  std::stringstream errMsg;
633  errMsg << "ERROR: asking for automatic gamma correction but setting a user-defined factor (" << gamma << ")." << std::endl;
634  throw(vpException(vpException::badValue, errMsg.str()));
635  }
636  else {
637  if (method == GAMMA_NONLINEAR_BASED) {
639  }
640  else if (method == GAMMA_LOG_BASED) {
641  gammaCorrectionLogMethod(I, p_mask);
642  }
643  else if (method == GAMMA_CLASSIFICATION_BASED) {
645  }
646  else if (method == GAMMA_CDF_BASED) {
648  }
649  else if (method == GAMMA_SPATIAL_VARIANT_BASED) {
650  gammaCorrectionSpatialBased(I, p_mask);
651  }
652  else {
653  std::stringstream errMsg;
654  errMsg << "Gamma automatic method \"" << vpGammaMethodToString(method) << "\" is not handled." << std::endl;
655  throw(vpException(vpException::badValue, errMsg.str()));
656  }
657  }
658 }
659 
660 void gammaCorrection(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, const float &gamma,
661  const vpGammaMethod &method, const vpImage<bool> *p_mask)
662 {
663  I2 = I1;
664  gammaCorrection(I2, gamma, method, p_mask);
665 }
666 
667 void gammaCorrection(vpImage<vpRGBa> &I, const float &gamma, const vpGammaColorHandling &colorHandling,
668  const vpGammaMethod &method, const vpImage<bool> *p_mask)
669 {
670  if (method == GAMMA_SPATIAL_VARIANT_BASED) {
671  gammaCorrectionSpatialBased(I, p_mask);
672  }
673  else {
674  if (colorHandling == GAMMA_HSV) {
675  const unsigned int height = I.getHeight(), width = I.getWidth();
676  unsigned int size = height * width;
677  std::vector<unsigned char> hue(size);
678  std::vector<unsigned char> saturation(size);
679  std::vector<unsigned char> value(size);
680 
681  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), &hue.front(), &saturation.front(), &value.front(), size);
682  vpImage<unsigned char> I_hue(&hue.front(), height, width);
683  vpImage<unsigned char> I_saturation(&saturation.front(), height, width);
684  vpImage<unsigned char> I_value(&value.front(), height, width);
685 
686  gammaCorrection(I_value, gamma, method, p_mask);
687 
688  vpImageConvert::HSVToRGBa(I_hue.bitmap, I_saturation.bitmap, I_value.bitmap, reinterpret_cast<unsigned char *>(I.bitmap), size);
689  }
690  else if (colorHandling == GAMMA_RGB) {
691  vpImage<unsigned char> pR, pG, pB, pa;
692  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
693  gammaCorrection(pR, gamma, method, p_mask);
694  gammaCorrection(pG, gamma, method, p_mask);
695  gammaCorrection(pB, gamma, method, p_mask);
696  gammaCorrection(pa, gamma, method, p_mask);
697  vpImageConvert::merge(&pR, &pG, &pB, &pa, I);
698  }
699  else {
700  std::stringstream errMsg;
701  errMsg << "Gamma color handling mode \"" << vpGammaColorHandlingToString(colorHandling);
702  errMsg << "\" is not handled." << std::endl;
703  throw(vpException(vpException::badValue, errMsg.str()));
704  }
705  }
706 }
707 
708 void gammaCorrection(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, const float &gamma,
709  const vpGammaColorHandling &colorHandling, const vpGammaMethod &method,
710  const vpImage<bool> *p_mask)
711 {
712  I2 = I1;
713  gammaCorrection(I2, gamma, colorHandling, method, p_mask);
714 }
715 
717 {
718  // Find min and max intensity values
719  unsigned char min = 255, max = 0;
720  I.getMinMaxValue(min, max);
721 
722  unsigned char range = max - min;
723 
724  // Construct the look-up table
725  const unsigned int lutSize = 256, maxVal = lutSize - 1;
726  unsigned char lut[lutSize];
727  if (range > 0) {
728  for (unsigned int x = min; x <= max; ++x) {
729  lut[x] = (maxVal * (x - min)) / range;
730  }
731  }
732  else {
733  lut[min] = min;
734  }
735 
736  I.performLut(lut);
737 }
738 
740 {
741  // Copy I1 to I2
742  I2 = I1;
743  stretchContrast(I2);
744 }
745 
747 {
748  // Find min and max intensity values
749  vpRGBa min(255), max(0);
750 
751  // Split the RGBa image into 4 images
756 
757  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
758  // Min max values calculated for each channel
759  unsigned char minChannel, maxChannel;
760  pR.getMinMaxValue(minChannel, maxChannel);
761  min.R = minChannel;
762  max.R = maxChannel;
763 
764  pG.getMinMaxValue(minChannel, maxChannel);
765  min.G = minChannel;
766  max.G = maxChannel;
767 
768  pB.getMinMaxValue(minChannel, maxChannel);
769  min.B = minChannel;
770  max.B = maxChannel;
771 
772  pa.getMinMaxValue(minChannel, maxChannel);
773  min.A = minChannel;
774  max.A = maxChannel;
775 
776  // Construct the look-up table
777  const unsigned int lutSize = 256, maxVal = lutSize - 1;
778  vpRGBa lut[lutSize];
779  unsigned char rangeR = max.R - min.R;
780  if (rangeR > 0) {
781  for (unsigned int x = min.R; x <= max.R; ++x) {
782  lut[x].R = (maxVal * (x - min.R)) / rangeR;
783  }
784  }
785  else {
786  lut[min.R].R = min.R;
787  }
788 
789  unsigned char rangeG = max.G - min.G;
790  if (rangeG > 0) {
791  for (unsigned int x = min.G; x <= max.G; ++x) {
792  lut[x].G = (maxVal * (x - min.G)) / rangeG;
793  }
794  }
795  else {
796  lut[min.G].G = min.G;
797  }
798 
799  unsigned char rangeB = max.B - min.B;
800  if (rangeB > 0) {
801  for (unsigned int x = min.B; x <= max.B; ++x) {
802  lut[x].B = (maxVal * (x - min.B)) / rangeB;
803  }
804  }
805  else {
806  lut[min.B].B = min.B;
807  }
808 
809  unsigned char rangeA = max.A - min.A;
810  if (rangeA > 0) {
811  for (unsigned int x = min.A; x <= max.A; ++x) {
812  lut[x].A = (maxVal * (x - min.A)) / rangeA;
813  }
814  }
815  else {
816  lut[min.A].A = min.A;
817  }
818 
819  I.performLut(lut);
820 }
821 
823 {
824  // Copy I1 to I2
825  I2 = I1;
826  stretchContrast(I2);
827 }
828 
830 {
831  unsigned int size = I.getWidth() * I.getHeight();
832 
833  // Convert RGB to HSV
834  vpImage<double> hueImage(I.getHeight(), I.getWidth()), saturationImage(I.getHeight(), I.getWidth()),
835  valueImage(I.getHeight(), I.getWidth());
836  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap,
837  size);
838 
839  // Find min and max Saturation and Value
840  double minSaturation, maxSaturation, minValue, maxValue;
841  saturationImage.getMinMaxValue(minSaturation, maxSaturation);
842  valueImage.getMinMaxValue(minValue, maxValue);
843 
844  double *ptrStart = saturationImage.bitmap;
845  double *ptrEnd = saturationImage.bitmap + size;
846  double *ptrCurrent = ptrStart;
847 
848  // Stretch Saturation
849  if ((maxSaturation - minSaturation) > 0.0) {
850  while (ptrCurrent != ptrEnd) {
851  *ptrCurrent = (*ptrCurrent - minSaturation) / (maxSaturation - minSaturation);
852  ++ptrCurrent;
853  }
854  }
855 
856  // Stretch Value
857  if ((maxValue - minValue) > 0.0) {
858  ptrStart = valueImage.bitmap;
859  ptrEnd = valueImage.bitmap + size;
860  ptrCurrent = ptrStart;
861 
862  while (ptrCurrent != ptrEnd) {
863  *ptrCurrent = (*ptrCurrent - minValue) / (maxValue - minValue);
864  ++ptrCurrent;
865  }
866  }
867 
868  // Convert HSV to RGBa
869  vpImageConvert::HSVToRGBa(hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap, reinterpret_cast<unsigned char *>(I.bitmap),
870  size);
871 }
872 
874 {
875  // Copy I1 to I2
876  I2 = I1;
877  stretchContrastHSV(I2);
878 }
879 
880 void unsharpMask(vpImage<unsigned char> &I, float sigma, double weight)
881 {
882  if ((weight < 1.0) && (weight >= 0.0)) {
883 #if defined(VISP_HAVE_SIMDLIB)
884  // Gaussian blurred image
885  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
886  vpImage<unsigned char> I_blurred;
887  gaussian_filter.apply(I, I_blurred);
888 #else
889  // Gaussian blurred image
890  vpImage<double> I_blurred;
891  unsigned int size = 7;
892  (void)sigma;
893  vpImageFilter::gaussianBlur(I, I_blurred, size);
894 #endif
895 
896  // Unsharp mask
897  unsigned int i_size = I.getSize();
898  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
899  double val = (I.bitmap[cpt] - (weight * I_blurred.bitmap[cpt])) / (1 - weight);
900  I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
901  }
902  }
903 }
904 
905 void unsharpMask(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires, float sigma, double weight)
906 {
907  // Copy I to Ires
908  Ires = I;
909  unsharpMask(Ires, sigma, weight);
910 }
911 
912 void unsharpMask(vpImage<vpRGBa> &I, float sigma, double weight)
913 {
914  if ((weight < 1.0) && (weight >= 0.0)) {
915 #if defined(VISP_HAVE_SIMDLIB)
916  // Gaussian blurred image
917  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
918  vpImage<vpRGBa> I_blurred;
919  gaussian_filter.apply(I, I_blurred);
920 #else
921  // Gaussian blurred image
922  vpImage<double> I_blurred_R, I_blurred_G, I_blurred_B;
923  vpImage<unsigned char> I_R, I_G, I_B;
924  unsigned int size = 7;
925  (void)sigma;
926 
927  vpImageConvert::split(I, &I_R, &I_G, &I_B);
928  vpImageFilter::gaussianBlur(I_R, I_blurred_R, size);
929  vpImageFilter::gaussianBlur(I_G, I_blurred_G, size);
930  vpImageFilter::gaussianBlur(I_B, I_blurred_B, size);
931 #endif
932 
933  // Unsharp mask
934  unsigned int i_size = I.getSize();
935  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
936 #if defined(VISP_HAVE_SIMDLIB)
937  double val_R = (I.bitmap[cpt].R - (weight * I_blurred.bitmap[cpt].R)) / (1 - weight);
938  double val_G = (I.bitmap[cpt].G - (weight * I_blurred.bitmap[cpt].G)) / (1 - weight);
939  double val_B = (I.bitmap[cpt].B - (weight * I_blurred.bitmap[cpt].B)) / (1 - weight);
940 #else
941  double val_R = (I.bitmap[cpt].R - (weight * I_blurred_R.bitmap[cpt])) / (1 - weight);
942  double val_G = (I.bitmap[cpt].G - (weight * I_blurred_G.bitmap[cpt])) / (1 - weight);
943  double val_B = (I.bitmap[cpt].B - (weight * I_blurred_B.bitmap[cpt])) / (1 - weight);
944 #endif
945  I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
946  I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
947  I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
948  }
949  }
950 }
951 
952 void unsharpMask(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, float sigma, double weight)
953 {
954  // Copy I to Ires
955  Ires = I;
956  unsharpMask(Ires, sigma, weight);
957 }
958 
959 } // namespace
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
Gaussian filter class.
void apply(const vpImage< unsigned char > &I, vpImage< unsigned char > &I_blur)
Class to compute a gray level image histogram.
Definition: vpHistogram.h:106
unsigned int getTotal()
Get the total number of pixels in the input image.
Definition: vpHistogram.h:308
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
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)
static void HSVToRGBa(const double *hue, const double *saturation, const double *value, unsigned char *rgba, unsigned int size)
static void merge(const vpImage< unsigned char > *R, const vpImage< unsigned char > *G, const vpImage< unsigned char > *B, const vpImage< unsigned char > *a, vpImage< vpRGBa > &RGBa)
static void split(const vpImage< vpRGBa > &src, vpImage< unsigned char > *pR, vpImage< unsigned char > *pG, vpImage< unsigned char > *pB, vpImage< unsigned char > *pa=nullptr)
static void RGBaToHSV(const unsigned char *rgba, double *hue, double *saturation, double *value, unsigned int size)
static void gaussianBlur(const vpImage< ImageType > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true, const vpImage< bool > *p_mask=nullptr)
static void resize(const vpImage< Type > &I, vpImage< Type > &Ires, unsigned int width, unsigned int height, const vpImageInterpolationType &method=INTERPOLATION_NEAREST, unsigned int nThreads=0)
void subsample(unsigned int v_scale, unsigned int h_scale, vpImage< Type > &sampled) const
Definition: vpImage.h:753
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
double getStdev(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the standard deviation of the bitmap.
unsigned int getHeight() const
Definition: vpImage.h:181
void getMinMaxValue(Type &min, Type &max, bool onlyFiniteVal=true) const
Look for the minimum and the maximum value within the bitmap.
double getMeanValue(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the mean value of the bitmap.
Definition: vpRGBa.h:65
unsigned char B
Blue component.
Definition: vpRGBa.h:169
unsigned char R
Red component.
Definition: vpRGBa.h:167
unsigned char G
Green component.
Definition: vpRGBa.h:168
unsigned char A
Additionnal component.
Definition: vpRGBa.h:170
VISP_EXPORT void adjust(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, double alpha, double beta)
VISP_EXPORT void stretchContrast(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I)
VISP_EXPORT void stretchContrastHSV(VISP_NAMESPACE_ADDRESSING vpImage< VISP_NAMESPACE_ADDRESSING vpRGBa > &I)
VISP_EXPORT void gammaCorrection(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const float &gamma, const vpGammaMethod &method=GAMMA_MANUAL, const VISP_NAMESPACE_ADDRESSING vpImage< bool > *p_mask=nullptr)
VISP_EXPORT void equalizeHistogram(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const VISP_NAMESPACE_ADDRESSING vpImage< bool > *p_mask=nullptr)
VISP_EXPORT void unsharpMask(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, float sigma, double weight=0.6)
VISP_EXPORT vpGammaMethod vpGammaMethodFromString(const std::string &name)
Cast a string into a vpGammaMethod.
Definition: vpImgproc.cpp:116
void gammaCorrectionClassBasedMethod(vpImage< unsigned char > &I, const vpImage< bool > *p_mask)
This method is an implementation of the article "An adaptive gamma correction for image enhancement",...
Definition: vpImgproc.cpp:393
void gammaCorrectionNonLinearMethod(vpImage< unsigned char > &I, const vpImage< bool > *p_mask)
This method is an implementation of the article "REDUCING ILLUMINATION BASED ON NONLINEAR GAMMA CORRE...
Definition: vpImgproc.cpp:354
VISP_EXPORT vpGammaColorHandling vpGammaColorHandlingFromString(const std::string &name)
Cast a string into a vpGammaColorHandling.
Definition: vpImgproc.cpp:164
vpGammaColorHandling
How to handle color images when applying Gamma Correction.
Definition: vpImgproc.h:149
VISP_EXPORT std::string vpGammaColorHandlingToString(const vpGammaColorHandling &type)
Cast a vpGammaColorHandling into a string, to know its name.
Definition: vpImgproc.cpp:147
VISP_EXPORT std::string vpGammaMethodList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpGammaMethod.
Definition: vpImgproc.cpp:73
VISP_EXPORT std::string vpGammaColorHandlingList(const std::string &pref="<", const std::string &sep=" , ", const std::string &suf=">")
Get the list of available vpGammaColorHandling.
Definition: vpImgproc.cpp:133
void gammaCorrectionSpatialBased(vpImage< unsigned char > &I, const vpImage< bool > *p_mask)
This technique comes from the article "A Space-Variant Luminance Map based Color Image Enhancement" b...
Definition: vpImgproc.cpp:489
void gammaCorrectionProbBasedMethod(vpImage< unsigned char > &I, const vpImage< bool > *p_mask)
This technique comes from the article "Efficient Contrast Enhancement Using Adaptive Gamma Correction...
Definition: vpImgproc.cpp:447
vpGammaMethod
Gamma Correction automatic methods.
Definition: vpImgproc.h:100
void gammaCorrectionLogMethod(vpImage< unsigned char > &I, const vpImage< bool > *p_mask)
This method is an implementation of the article "Towards Real-time Hardware Gamma Correction for Dyna...
Definition: vpImgproc.cpp:324
VISP_EXPORT std::string vpGammaMethodToString(const vpGammaMethod &type)
Cast a vpGammaMethod into a string, to know its name.
Definition: vpImgproc.cpp:87