Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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  float inverse_gamma = 1.f / gamma_computed;
333 
334  // Construct the look-up table
335  unsigned char lut[256];
336  float inputRangeAsFloat = static_cast<float>(inputRange);
337  for (unsigned int i = inputMin; i <= inputMax; ++i) {
338  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i - inputMin) / inputRangeAsFloat, inverse_gamma) * 255.f);
339  }
340 
341  I.performLut(lut);
342 }
343 
356 {
357  (void)p_mask;
358  const float a = 0.2f;
359  const float b = 0.3f;
360  const float c = 0.3f;
361  const float x_m = 127.5f;
362  const float alpha = std::atan2(-b, x_m);
363  const float rho = 0.1f;
364  const unsigned int lutSize = 256;
365  unsigned char lut[lutSize];
366  for (unsigned int i = 0; i < lutSize; ++i) {
367  float x = static_cast<float>(i);
368  float phi = (M_PI_FLOAT * x) / (2.f * x_m);
369  float f1 = a * std::cos(phi);
370  float k = rho * std::sin((4 * M_PI_FLOAT * x) / 255.f);
371  float f2 = ((k + b)*std::cos(alpha)) + (x * std::sin(alpha));
372  float r = c * std::abs((x / x_m) - 1.f);
373  float f3 = r * std::cos((3.f * M_PI_FLOAT * x) / 255.f);
374  float g = f1 + f2 + f3;
375  float gamma = 1 + g;
376  float inverse_gamma = 1.f / gamma;
377  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.f, inverse_gamma) * 255.f);
378  }
379  I.performLut(lut);
380 }
381 
395 {
396  double mean = I.getMeanValue(p_mask);
397  double stdev = I.getStdev(p_mask);
398  double meanNormalized = mean / 255.;
399  double stdevNormalized = stdev / 255.;
400  const float tau = 3.f;
401  bool isAlreadyHighContrast = (4. * stdevNormalized) > (1./tau);
402  const unsigned int lutSize = 256;
403  unsigned char lut[lutSize];
404  float gamma = 0.f;
405  if (isAlreadyHighContrast) {
406  // Case medium to high contrast image
407  gamma = static_cast<float>(std::exp((1.f - (meanNormalized + stdevNormalized))/2.f));
408  }
409  else {
410  // Case low contrast image
411 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
412  gamma = -static_cast<float>(std::log2(stdevNormalized));
413 #else
414  gamma = -static_cast<float>(std::log(stdevNormalized) / std::log(2.f));
415 #endif
416  }
417  if (meanNormalized < 0.5) {
418  // Case dark image
419  float meanPowerGamma = static_cast<float>(std::pow(meanNormalized, gamma));
420  for (unsigned int i = 0; i < lutSize; ++i) {
421  float iNormalized = static_cast<float>(i)/255.f;
422  float iPowerGamma = std::pow(iNormalized, gamma);
423  lut[i] = vpMath::saturate<unsigned char>(255.f * (iPowerGamma / (iPowerGamma + ((1.f - iPowerGamma) * meanPowerGamma))));
424  }
425  }
426  else {
427  // Case bright image
428  for (unsigned int i = 0; i < lutSize; ++i) {
429  float iNormalized = static_cast<float>(i)/255.f;
430  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
431  }
432  }
433  I.performLut(lut);
434 }
435 
449 {
450  const unsigned int nbBins = 256;
451  vpHistogram histo;
452  histo.setMask(p_mask);
453  histo.calculate(I, nbBins);
454  unsigned int totalNbPoints = histo.getTotal();
455  unsigned int minHisto = histo[0];
456  unsigned int maxHisto = histo[0];
457  for (unsigned int i = 1; i < nbBins; ++i) {
458  minHisto = std::min(minHisto, histo[i]);
459  maxHisto = std::max(maxHisto, histo[i]);
460  }
461  float pdfMin = static_cast<float>(minHisto) / static_cast<float>(totalNbPoints);
462  float pdfMax = static_cast<float>(maxHisto) / static_cast<float>(totalNbPoints);
463  float pdf_w[nbBins];
464  float sum_pdf_w = 0.f;
465  for (unsigned int i = 0; i < nbBins; ++i) {
466  float pdf = static_cast<float>(histo[i])/static_cast<float>(totalNbPoints);
467  pdf_w[i] = pdfMax * std::sqrt((pdf - pdfMin)/(pdfMax - pdfMin)); // alpha = 0.5
468  sum_pdf_w += pdf_w[i];
469  }
470  unsigned char lut[nbBins];
471  float cdf_w = 0;
472  for (unsigned int i = 0; i < nbBins; ++i) {
473  cdf_w += pdf_w[i] / sum_pdf_w;
474  float gamma = 1.f - cdf_w;
475  float iNormalized = static_cast<float>(i)/255.f;
476  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
477  }
478  I.performLut(lut);
479 }
480 
491 {
492  unsigned int width = I.getWidth(), height = I.getHeight();
493  const unsigned int scale2 = 2, scale4 = 4, scale8 = 8;
494  vpImage<unsigned char> I_2, I_4, I_8;
495  I.subsample(scale2, scale2, I_2);
496  I.subsample(scale4, scale4, I_4);
497  I.subsample(scale8, scale8, I_8);
498  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
499  const bool normalize = true;
500  const unsigned int gaussKernelSize = 3;
501  vpImageFilter::gaussianBlur(I, I_blur, gaussKernelSize, 0.f, normalize, p_mask);
502  vpImageFilter::gaussianBlur(I_2, I_2_blur, gaussKernelSize, 0.f, normalize, p_mask);
503  vpImageFilter::gaussianBlur(I_4, I_4_blur, gaussKernelSize, 0.f, normalize, p_mask);
504  vpImageFilter::gaussianBlur(I_8, I_8_blur, gaussKernelSize, 0.f, normalize, p_mask);
505  vpImage<float> L, L_2, L_4, L_8;
506  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
507  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
508  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
509  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
510  const float alpha = 0.5f;
511  unsigned int size = height * width;
512  float stdev = static_cast<float>(I.getStdev(p_mask));
513  float p;
514  const float stdevThresh1 = 40., stdevThresh2 = 80.;
515  if (stdev <= stdevThresh1) {
516  p = 2.f;
517  }
518  else if (stdev <= stdevThresh2) {
519  p = (-0.025f * stdev) + 3.f;
520  }
521  else {
522  p = 1.f;
523  }
524 
525  for (unsigned int i = 0; i < size; ++i) {
526  bool hasToCompute = true;
527  if (p_mask != nullptr) {
528  hasToCompute = p_mask->bitmap[i];
529  }
530  if (hasToCompute) {
531  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
532  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
533  float iNormalized = static_cast<float>(I.bitmap[i])/255.f;
534  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
535  float r = svlm / o;
536  float e = std::pow(r, p);
537  float s = 255.f * std::pow(o / 255.f, e);
538  I.bitmap[i] = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i])) / o);
539  }
540  }
541 }
542 
553 {
554  unsigned int width = I.getWidth(), height = I.getHeight();
555  unsigned int size = height * width;
556  vpImage<unsigned char> I_gray(height, width);
557  for (unsigned int i = 0; i < size; ++i) {
558  vpRGBa rgb = I.bitmap[i];
559  I_gray.bitmap[i] = static_cast<unsigned char>((0.299 * rgb.R) + (0.587 * rgb.G) + (0.114 * rgb.B));
560  }
561  vpImage<unsigned char> I_2, I_4, I_8;
562  const unsigned int scale2 = 2, scale4 = 4, scale8 = 8;
563  I_gray.subsample(scale2, scale2, I_2);
564  I_gray.subsample(scale4, scale4, I_4);
565  I_gray.subsample(scale8, scale8, I_8);
566  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
567  const bool normalize = true;
568  const unsigned int gaussKernelSize = 3;
569  vpImageFilter::gaussianBlur(I_gray, I_blur, gaussKernelSize, 0.f, normalize, p_mask);
570  vpImageFilter::gaussianBlur(I_2, I_2_blur, gaussKernelSize, 0.f, normalize, p_mask);
571  vpImageFilter::gaussianBlur(I_4, I_4_blur, gaussKernelSize, 0.f, normalize, p_mask);
572  vpImageFilter::gaussianBlur(I_8, I_8_blur, gaussKernelSize, 0.f, normalize, p_mask);
573  vpImage<float> L, L_2, L_4, L_8;
574  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
575  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
576  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
577  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
578  const float alpha = 0.5f;
579 
580  float stdev = static_cast<float>(I.getStdev(p_mask));
581  float p;
582  const float stdevThresh1 = 40., stdevThresh2 = 80.;
583  if (stdev <= stdevThresh1) {
584  p = 2.f;
585  }
586  else if (stdev <= stdevThresh2) {
587  p = (-0.025f * stdev) + 3.f;
588  }
589  else {
590  p = 1.f;
591  }
592  for (unsigned int i = 0; i < size; ++i) {
593  bool hasToCompute = true;
594  if (p_mask != nullptr) {
595  hasToCompute = p_mask->bitmap[i];
596  }
597  if (hasToCompute) {
598  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
599  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
600  float iNormalized = static_cast<float>(I_gray.bitmap[i])/255.f;
601  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
602  float r = svlm / o;
603  float e = std::pow(r, p);
604  float s = 255.f * std::pow(o / 255.f, e);
605  I.bitmap[i].R = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].R)) / o);
606  I.bitmap[i].G = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].G)) / o);
607  I.bitmap[i].B = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].B)) / o);
608  }
609  }
610 }
611 
612 void gammaCorrection(vpImage<unsigned char> &I, const float &gamma, const vpGammaMethod &method, const vpImage<bool> *p_mask)
613 {
614  float inverse_gamma = 1.0;
615  if ((gamma > 0) && (method == GAMMA_MANUAL)) {
616  inverse_gamma = 1.0f / gamma;
617  // Construct the look-up table
618  const unsigned int lutSize = 256;
619  unsigned char lut[lutSize];
620  for (unsigned int i = 0; i < lutSize; ++i) {
621  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.0, inverse_gamma) * 255.0);
622  }
623 
624  I.performLut(lut);
625  }
626  else if (method == GAMMA_MANUAL) {
627  std::stringstream errMsg;
628  errMsg << "ERROR: gamma correction factor (";
629  errMsg << gamma << ") cannot be negative when using a constant user-defined factor." << std::endl;
630  throw(vpException(vpException::badValue, errMsg.str()));
631  }
632  else if (gamma > 0) {
633  std::stringstream errMsg;
634  errMsg << "ERROR: asking for automatic gamma correction but setting a user-defined factor (" << gamma << ")." << std::endl;
635  throw(vpException(vpException::badValue, errMsg.str()));
636  }
637  else {
638  if (method == GAMMA_NONLINEAR_BASED) {
640  }
641  else if (method == GAMMA_LOG_BASED) {
642  gammaCorrectionLogMethod(I, p_mask);
643  }
644  else if (method == GAMMA_CLASSIFICATION_BASED) {
646  }
647  else if (method == GAMMA_CDF_BASED) {
649  }
650  else if (method == GAMMA_SPATIAL_VARIANT_BASED) {
651  gammaCorrectionSpatialBased(I, p_mask);
652  }
653  else {
654  std::stringstream errMsg;
655  errMsg << "Gamma automatic method \"" << vpGammaMethodToString(method) << "\" is not handled." << std::endl;
656  throw(vpException(vpException::badValue, errMsg.str()));
657  }
658  }
659 }
660 
661 void gammaCorrection(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, const float &gamma,
662  const vpGammaMethod &method, const vpImage<bool> *p_mask)
663 {
664  I2 = I1;
665  gammaCorrection(I2, gamma, method, p_mask);
666 }
667 
668 void gammaCorrection(vpImage<vpRGBa> &I, const float &gamma, const vpGammaColorHandling &colorHandling,
669  const vpGammaMethod &method, const vpImage<bool> *p_mask)
670 {
671  if (method == GAMMA_SPATIAL_VARIANT_BASED) {
672  gammaCorrectionSpatialBased(I, p_mask);
673  }
674  else {
675  if (colorHandling == GAMMA_HSV) {
676  const unsigned int height = I.getHeight(), width = I.getWidth();
677  unsigned int size = height * width;
678  std::vector<unsigned char> hue(size);
679  std::vector<unsigned char> saturation(size);
680  std::vector<unsigned char> value(size);
681 
682  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), &hue.front(), &saturation.front(), &value.front(), size);
683  vpImage<unsigned char> I_hue(&hue.front(), height, width);
684  vpImage<unsigned char> I_saturation(&saturation.front(), height, width);
685  vpImage<unsigned char> I_value(&value.front(), height, width);
686 
687  gammaCorrection(I_value, gamma, method, p_mask);
688 
689  vpImageConvert::HSVToRGBa(I_hue.bitmap, I_saturation.bitmap, I_value.bitmap, reinterpret_cast<unsigned char *>(I.bitmap), size);
690  }
691  else if (colorHandling == GAMMA_RGB) {
692  vpImage<unsigned char> pR, pG, pB, pa;
693  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
694  gammaCorrection(pR, gamma, method, p_mask);
695  gammaCorrection(pG, gamma, method, p_mask);
696  gammaCorrection(pB, gamma, method, p_mask);
697  gammaCorrection(pa, gamma, method, p_mask);
698  vpImageConvert::merge(&pR, &pG, &pB, &pa, I);
699  }
700  else {
701  std::stringstream errMsg;
702  errMsg << "Gamma color handling mode \"" << vpGammaColorHandlingToString(colorHandling);
703  errMsg << "\" is not handled." << std::endl;
704  throw(vpException(vpException::badValue, errMsg.str()));
705  }
706  }
707 }
708 
709 void gammaCorrection(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, const float &gamma,
710  const vpGammaColorHandling &colorHandling, const vpGammaMethod &method,
711  const vpImage<bool> *p_mask)
712 {
713  I2 = I1;
714  gammaCorrection(I2, gamma, colorHandling, method, p_mask);
715 }
716 
718 {
719  // Find min and max intensity values
720  unsigned char min = 255, max = 0;
721  I.getMinMaxValue(min, max);
722 
723  unsigned char range = max - min;
724 
725  // Construct the look-up table
726  const unsigned int lutSize = 256, maxVal = lutSize - 1;
727  unsigned char lut[lutSize];
728  if (range > 0) {
729  for (unsigned int x = min; x <= max; ++x) {
730  lut[x] = (maxVal * (x - min)) / range;
731  }
732  }
733  else {
734  lut[min] = min;
735  }
736 
737  I.performLut(lut);
738 }
739 
741 {
742  // Copy I1 to I2
743  I2 = I1;
744  stretchContrast(I2);
745 }
746 
748 {
749  // Find min and max intensity values
750  vpRGBa min(255), max(0);
751 
752  // Split the RGBa image into 4 images
757 
758  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
759  // Min max values calculated for each channel
760  unsigned char minChannel, maxChannel;
761  pR.getMinMaxValue(minChannel, maxChannel);
762  min.R = minChannel;
763  max.R = maxChannel;
764 
765  pG.getMinMaxValue(minChannel, maxChannel);
766  min.G = minChannel;
767  max.G = maxChannel;
768 
769  pB.getMinMaxValue(minChannel, maxChannel);
770  min.B = minChannel;
771  max.B = maxChannel;
772 
773  pa.getMinMaxValue(minChannel, maxChannel);
774  min.A = minChannel;
775  max.A = maxChannel;
776 
777  // Construct the look-up table
778  const unsigned int lutSize = 256, maxVal = lutSize - 1;
779  vpRGBa lut[lutSize];
780  unsigned char rangeR = max.R - min.R;
781  if (rangeR > 0) {
782  for (unsigned int x = min.R; x <= max.R; ++x) {
783  lut[x].R = (maxVal * (x - min.R)) / rangeR;
784  }
785  }
786  else {
787  lut[min.R].R = min.R;
788  }
789 
790  unsigned char rangeG = max.G - min.G;
791  if (rangeG > 0) {
792  for (unsigned int x = min.G; x <= max.G; ++x) {
793  lut[x].G = (maxVal * (x - min.G)) / rangeG;
794  }
795  }
796  else {
797  lut[min.G].G = min.G;
798  }
799 
800  unsigned char rangeB = max.B - min.B;
801  if (rangeB > 0) {
802  for (unsigned int x = min.B; x <= max.B; ++x) {
803  lut[x].B = (maxVal * (x - min.B)) / rangeB;
804  }
805  }
806  else {
807  lut[min.B].B = min.B;
808  }
809 
810  unsigned char rangeA = max.A - min.A;
811  if (rangeA > 0) {
812  for (unsigned int x = min.A; x <= max.A; ++x) {
813  lut[x].A = (maxVal * (x - min.A)) / rangeA;
814  }
815  }
816  else {
817  lut[min.A].A = min.A;
818  }
819 
820  I.performLut(lut);
821 }
822 
824 {
825  // Copy I1 to I2
826  I2 = I1;
827  stretchContrast(I2);
828 }
829 
831 {
832  unsigned int size = I.getWidth() * I.getHeight();
833 
834  // Convert RGB to HSV
835  vpImage<double> hueImage(I.getHeight(), I.getWidth()), saturationImage(I.getHeight(), I.getWidth()),
836  valueImage(I.getHeight(), I.getWidth());
837  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap,
838  size);
839 
840  // Find min and max Saturation and Value
841  double minSaturation, maxSaturation, minValue, maxValue;
842  saturationImage.getMinMaxValue(minSaturation, maxSaturation);
843  valueImage.getMinMaxValue(minValue, maxValue);
844 
845  double *ptrStart = saturationImage.bitmap;
846  double *ptrEnd = saturationImage.bitmap + size;
847  double *ptrCurrent = ptrStart;
848 
849  // Stretch Saturation
850  if ((maxSaturation - minSaturation) > 0.0) {
851  while (ptrCurrent != ptrEnd) {
852  *ptrCurrent = (*ptrCurrent - minSaturation) / (maxSaturation - minSaturation);
853  ++ptrCurrent;
854  }
855  }
856 
857  // Stretch Value
858  if ((maxValue - minValue) > 0.0) {
859  ptrStart = valueImage.bitmap;
860  ptrEnd = valueImage.bitmap + size;
861  ptrCurrent = ptrStart;
862 
863  while (ptrCurrent != ptrEnd) {
864  *ptrCurrent = (*ptrCurrent - minValue) / (maxValue - minValue);
865  ++ptrCurrent;
866  }
867  }
868 
869  // Convert HSV to RGBa
870  vpImageConvert::HSVToRGBa(hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap, reinterpret_cast<unsigned char *>(I.bitmap),
871  size);
872 }
873 
875 {
876  // Copy I1 to I2
877  I2 = I1;
878  stretchContrastHSV(I2);
879 }
880 
881 void unsharpMask(vpImage<unsigned char> &I, float sigma, double weight)
882 {
883  if ((weight < 1.0) && (weight >= 0.0)) {
884 #if defined(VISP_HAVE_SIMDLIB)
885  // Gaussian blurred image
886  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
887  vpImage<unsigned char> I_blurred;
888  gaussian_filter.apply(I, I_blurred);
889 #else
890  // Gaussian blurred image
891  vpImage<double> I_blurred;
892  unsigned int size = 7;
893  (void)sigma;
894  vpImageFilter::gaussianBlur(I, I_blurred, size);
895 #endif
896 
897  // Unsharp mask
898  unsigned int i_size = I.getSize();
899  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
900  double val = (I.bitmap[cpt] - (weight * I_blurred.bitmap[cpt])) / (1 - weight);
901  I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
902  }
903  }
904 }
905 
906 void unsharpMask(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires, float sigma, double weight)
907 {
908  // Copy I to Ires
909  Ires = I;
910  unsharpMask(Ires, sigma, weight);
911 }
912 
913 void unsharpMask(vpImage<vpRGBa> &I, float sigma, double weight)
914 {
915  if ((weight < 1.0) && (weight >= 0.0)) {
916 #if defined(VISP_HAVE_SIMDLIB)
917  // Gaussian blurred image
918  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
919  vpImage<vpRGBa> I_blurred;
920  gaussian_filter.apply(I, I_blurred);
921 #else
922  // Gaussian blurred image
923  vpImage<double> I_blurred_R, I_blurred_G, I_blurred_B;
924  vpImage<unsigned char> I_R, I_G, I_B;
925  unsigned int size = 7;
926  (void)sigma;
927 
928  vpImageConvert::split(I, &I_R, &I_G, &I_B);
929  vpImageFilter::gaussianBlur(I_R, I_blurred_R, size);
930  vpImageFilter::gaussianBlur(I_G, I_blurred_G, size);
931  vpImageFilter::gaussianBlur(I_B, I_blurred_B, size);
932 #endif
933 
934  // Unsharp mask
935  unsigned int i_size = I.getSize();
936  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
937 #if defined(VISP_HAVE_SIMDLIB)
938  double val_R = (I.bitmap[cpt].R - (weight * I_blurred.bitmap[cpt].R)) / (1 - weight);
939  double val_G = (I.bitmap[cpt].G - (weight * I_blurred.bitmap[cpt].G)) / (1 - weight);
940  double val_B = (I.bitmap[cpt].B - (weight * I_blurred.bitmap[cpt].B)) / (1 - weight);
941 #else
942  double val_R = (I.bitmap[cpt].R - (weight * I_blurred_R.bitmap[cpt])) / (1 - weight);
943  double val_G = (I.bitmap[cpt].G - (weight * I_blurred_G.bitmap[cpt])) / (1 - weight);
944  double val_B = (I.bitmap[cpt].B - (weight * I_blurred_B.bitmap[cpt])) / (1 - weight);
945 #endif
946  I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
947  I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
948  I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
949  }
950  }
951 }
952 
953 void unsharpMask(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, float sigma, double weight)
954 {
955  // Copy I to Ires
956  Ires = I;
957  unsharpMask(Ires, sigma, weight);
958 }
959 
960 } // 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:749
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:394
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:355
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:490
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:448
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