Visual Servoing Platform  version 3.6.1 under development (2024-06-15)
vpImgproc.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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  unsigned char lut[256];
185  for (unsigned int i = 0; i < 256; ++i) {
186  lut[i] = vpMath::saturate<unsigned char>((alpha * i) + beta);
187  }
188 
189  // Apply the transformation using a LUT
190  I.performLut(lut);
191 }
192 
193 void adjust(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, double alpha, double beta)
194 {
195  // Copy I1 to I2
196  I2 = I1;
197 
198  adjust(I2, alpha, beta);
199 }
200 
201 void adjust(vpImage<vpRGBa> &I, double alpha, double beta)
202 {
203  // Construct the look-up table
204  vpRGBa lut[256];
205  for (unsigned int i = 0; i < 256; ++i) {
206  lut[i].R = vpMath::saturate<unsigned char>((alpha * i) + beta);
207  lut[i].G = vpMath::saturate<unsigned char>((alpha * i) + beta);
208  lut[i].B = vpMath::saturate<unsigned char>((alpha * i) + beta);
209  lut[i].A = vpMath::saturate<unsigned char>((alpha * i) + beta);
210  }
211 
212  // Apply the transformation using a LUT
213  I.performLut(lut);
214 }
215 
216 void adjust(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, double alpha, double beta)
217 {
218  // Copy I1 to I2
219  I2 = I1;
220 
221  adjust(I2, alpha, beta);
222 }
223 
225 {
226  vpImage<unsigned char> Icpy = I;
227  equalizeHistogram(Icpy, I, p_mask);
228 }
229 
231  const vpImage<bool> *p_mask)
232 {
233  if ((I1.getWidth() * I1.getHeight()) == 0) {
234  return;
235  }
236 
237  // Calculate the histogram
238  vpHistogram hist;
239  hist.setMask(p_mask);
240  hist.equalize(I1, I2);
241 }
242 
243 void equalizeHistogram(vpImage<vpRGBa> &I, bool useHSV)
244 {
245  if ((I.getWidth() * I.getHeight()) == 0) {
246  return;
247  }
248 
249  if (!useHSV) {
250  // Split the RGBa image into 4 images
255 
256  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
257 
258  // Apply histogram equalization for each channel
259  equalizeHistogram(pR);
260  equalizeHistogram(pG);
261  equalizeHistogram(pB);
262 
263  // Merge the result in I
264  unsigned int size = I.getWidth() * I.getHeight();
265  unsigned char *ptrStart = reinterpret_cast<unsigned char *>(I.bitmap);
266  unsigned char *ptrEnd = ptrStart + (size * 4);
267  unsigned char *ptrCurrent = ptrStart;
268 
269  unsigned int cpt = 0;
270  while (ptrCurrent != ptrEnd) {
271  *ptrCurrent = pR.bitmap[cpt];
272  ++ptrCurrent;
273 
274  *ptrCurrent = pG.bitmap[cpt];
275  ++ptrCurrent;
276 
277  *ptrCurrent = pB.bitmap[cpt];
278  ++ptrCurrent;
279 
280  *ptrCurrent = pa.bitmap[cpt];
281  ++ptrCurrent;
282 
283  ++cpt;
284  }
285  }
286  else {
288  vpImage<unsigned char> saturation(I.getHeight(), I.getWidth());
289  vpImage<unsigned char> value(I.getHeight(), I.getWidth());
290 
291  unsigned int size = I.getWidth() * I.getHeight();
292  // Convert from RGBa to HSV
293  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), reinterpret_cast<unsigned char *>(hue.bitmap),
294  reinterpret_cast<unsigned char *>(saturation.bitmap), reinterpret_cast<unsigned char *>(value.bitmap), size);
295 
296  // Histogram equalization on the value plane
297  equalizeHistogram(value);
298 
299  // Convert from HSV to RGBa
300  vpImageConvert::HSVToRGBa(reinterpret_cast<unsigned char *>(hue.bitmap), reinterpret_cast<unsigned char *>(saturation.bitmap),
301  reinterpret_cast<unsigned char *>(value.bitmap), reinterpret_cast<unsigned char *>(I.bitmap), size);
302  }
303 }
304 
305 void equalizeHistogram(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, bool useHSV)
306 {
307  I2 = I1;
308  equalizeHistogram(I2, useHSV);
309 }
310 
323 {
324  float mean = static_cast<float>(I.getMeanValue(p_mask));
325  unsigned char inputMin = 0, inputMax = 0;
326  I.getMinMaxValue(inputMin, inputMax);
327  unsigned char inputRange = inputMax - inputMin;
328 
329  float gamma_computed = static_cast<float>((std::log(128.f) - std::log(256.f)) / (std::log(mean) - std::log(inputRange)));
330  float inverse_gamma = 1.f / gamma_computed;
331 
332  // Construct the look-up table
333  unsigned char lut[256];
334  float inputRangeAsFloat = static_cast<float>(inputRange);
335  for (unsigned int i = inputMin; i <= inputMax; ++i) {
336  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i - inputMin) / inputRangeAsFloat, inverse_gamma) * 255.f);
337  }
338 
339  I.performLut(lut);
340 }
341 
354 {
355  (void)p_mask;
356  const float a = 0.2f;
357  const float b = 0.3f;
358  const float c = 0.3f;
359  const float x_m = 127.5f;
360  const float alpha = std::atan2(-b, x_m);
361  const float rho = 0.1f;
362  unsigned char lut[256];
363  for (unsigned int i = 0; i < 256; ++i) {
364  float x = static_cast<float>(i);
365  float phi = (M_PIf * x) / (2.f * x_m);
366  float f1 = a * std::cos(phi);
367  float k = rho * std::sin((4 * M_PIf * x) / 255.f);
368  float f2 = ((k + b)*std::cos(alpha)) + (x * std::sin(alpha));
369  float r = c * std::abs((x / x_m) - 1.f);
370  float f3 = r * std::cos((3.f * M_PIf * x) / 255.f);
371  float g = f1 + f2 + f3;
372  float gamma = 1 + g;
373  float inverse_gamma = 1.f / gamma;
374  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.f, inverse_gamma) * 255.f);
375  }
376  I.performLut(lut);
377 }
378 
392 {
393  double mean = I.getMeanValue(p_mask);
394  double stdev = I.getStdev(p_mask);
395  double meanNormalized = mean / 255.;
396  double stdevNormalized = stdev / 255.;
397  const float tau = 3.f;
398  bool isAlreadyHighContrast = (4. * stdevNormalized) > (1./tau);
399  unsigned char lut[256];
400  float gamma = 0.f;
401  if (isAlreadyHighContrast) {
402  // Case medium to high contrast image
403  gamma = static_cast<float>(std::exp((1.f - (meanNormalized + stdevNormalized))/2.f));
404  }
405  else {
406  // Case low contrast image
407 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
408  gamma = -static_cast<float>(std::log2(stdevNormalized));
409 #else
410  gamma = -static_cast<float>(std::log(stdevNormalized) / std::log(2));
411 #endif
412  }
413  if (meanNormalized < 0.5) {
414  // Case dark image
415  float meanPowerGamma = static_cast<float>(std::pow(meanNormalized, gamma));
416  for (unsigned int i = 0; i <= 255; ++i) {
417  float iNormalized = static_cast<float>(i)/255.f;
418  float iPowerGamma = std::pow(iNormalized, gamma);
419  lut[i] = vpMath::saturate<unsigned char>(255.f * (iPowerGamma / (iPowerGamma + ((1.f - iPowerGamma) * meanPowerGamma))));
420  }
421  }
422  else {
423  // Case bright image
424  for (unsigned int i = 0; i <= 255; ++i) {
425  float iNormalized = static_cast<float>(i)/255.f;
426  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
427  }
428  }
429  I.performLut(lut);
430 }
431 
445 {
446  const unsigned int nbBins = 256;
447  vpHistogram histo;
448  histo.setMask(p_mask);
449  histo.calculate(I, nbBins);
450  unsigned int totalNbPoints = histo.getTotal();
451  unsigned int minHisto = histo[0];
452  unsigned int maxHisto = histo[0];
453  for (unsigned int i = 1; i < nbBins; ++i) {
454  minHisto = std::min(minHisto, histo[i]);
455  maxHisto = std::max(maxHisto, histo[i]);
456  }
457  float pdfMin = static_cast<float>(minHisto) / static_cast<float>(totalNbPoints);
458  float pdfMax = static_cast<float>(maxHisto) / static_cast<float>(totalNbPoints);
459  float pdf_w[nbBins];
460  float sum_pdf_w = 0.f;
461  for (unsigned int i = 0; i < nbBins; ++i) {
462  float pdf = static_cast<float>(histo[i])/static_cast<float>(totalNbPoints);
463  pdf_w[i] = pdfMax * std::sqrt((pdf - pdfMin)/(pdfMax - pdfMin)); // alpha = 0.5
464  sum_pdf_w += pdf_w[i];
465  }
466  unsigned char lut[256];
467  float cdf_w = 0;
468  for (unsigned int i = 0; i <= 255; ++i) {
469  cdf_w += pdf_w[i] / sum_pdf_w;
470  float gamma = 1.f - cdf_w;
471  float iNormalized = static_cast<float>(i)/255.f;
472  lut[i] = vpMath::saturate<unsigned char>(std::pow(iNormalized, gamma) * 255.f);
473  }
474  I.performLut(lut);
475 }
476 
487 {
488  unsigned int width = I.getWidth(), height = I.getHeight();
489  vpImage<unsigned char> I_2, I_4, I_8;
490  I.subsample(2, 2, I_2);
491  I.subsample(4, 4, I_4);
492  I.subsample(8, 8, I_8);
493  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
494  const bool normalize = true;
495  vpImageFilter::gaussianBlur(I, I_blur, 3, 0.f, normalize, p_mask);
496  vpImageFilter::gaussianBlur(I_2, I_2_blur, 3, 0.f, normalize, p_mask);
497  vpImageFilter::gaussianBlur(I_4, I_4_blur, 3, 0.f, normalize, p_mask);
498  vpImageFilter::gaussianBlur(I_8, I_8_blur, 3, 0.f, normalize, p_mask);
499  vpImage<float> L, L_2, L_4, L_8;
500  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
501  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
502  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
503  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
504  const float alpha = 0.5f;
505  unsigned int size = height * width;
506  float stdev = static_cast<float>(I.getStdev(p_mask));
507  float p;
508  if (stdev <= 40) {
509  p = 2.f;
510  }
511  else if (stdev <= 80) {
512  p = (-0.025f * stdev) + 3.f;
513  }
514  else {
515  p = 1.f;
516  }
517 
518  for (unsigned int i = 0; i < size; ++i) {
519  bool hasToCompute = true;
520  if (p_mask != nullptr) {
521  hasToCompute = p_mask->bitmap[i];
522  }
523  if (hasToCompute) {
524  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
525  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
526  float iNormalized = static_cast<float>(I.bitmap[i])/255.f;
527  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
528  float r = svlm / o;
529  float e = std::pow(r, p);
530  float s = 255.f * std::pow(o / 255.f, e);
531  I.bitmap[i] = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i])) / o);
532  }
533  }
534 }
535 
546 {
547  unsigned int width = I.getWidth(), height = I.getHeight();
548  unsigned int size = height * width;
549  vpImage<unsigned char> I_gray(height, width);
550  for (unsigned int i = 0; i < size; ++i) {
551  vpRGBa rgb = I.bitmap[i];
552  I_gray.bitmap[i] = static_cast<unsigned char>((0.299 * rgb.R) + (0.587 * rgb.G) + (0.114 * rgb.B));
553  }
554  vpImage<unsigned char> I_2, I_4, I_8;
555  I_gray.subsample(2, 2, I_2);
556  I_gray.subsample(4, 4, I_4);
557  I_gray.subsample(8, 8, I_8);
558  vpImage<float> I_blur, I_2_blur, I_4_blur, I_8_blur;
559  const bool normalize = true;
560  vpImageFilter::gaussianBlur(I_gray, I_blur, 3, 0.f, normalize, p_mask);
561  vpImageFilter::gaussianBlur(I_2, I_2_blur, 3, 0.f, normalize, p_mask);
562  vpImageFilter::gaussianBlur(I_4, I_4_blur, 3, 0.f, normalize, p_mask);
563  vpImageFilter::gaussianBlur(I_8, I_8_blur, 3, 0.f, normalize, p_mask);
564  vpImage<float> L, L_2, L_4, L_8;
565  vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
566  vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
567  vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
568  vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
569  const float alpha = 0.5f;
570 
571  float stdev = static_cast<float>(I.getStdev(p_mask));
572  float p;
573  if (stdev <= 40) {
574  p = 2.f;
575  }
576  else if (stdev <= 80) {
577  p = (-0.025f * stdev) + 3.f;
578  }
579  else {
580  p = 1.f;
581  }
582  for (unsigned int i = 0; i < size; ++i) {
583  bool hasToCompute = true;
584  if (p_mask != nullptr) {
585  hasToCompute = p_mask->bitmap[i];
586  }
587  if (hasToCompute) {
588  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
589  float gamma = std::pow(alpha, (128.f - svlm)/128.f);
590  float iNormalized = static_cast<float>(I_gray.bitmap[i])/255.f;
591  float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
592  float r = svlm / o;
593  float e = std::pow(r, p);
594  float s = 255.f * std::pow(o / 255.f, e);
595  I.bitmap[i].R = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].R)) / o);
596  I.bitmap[i].G = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].G)) / o);
597  I.bitmap[i].B = vpMath::saturate<unsigned char>((s * static_cast<float>(I.bitmap[i].B)) / o);
598  }
599  }
600 }
601 
602 void gammaCorrection(vpImage<unsigned char> &I, const float &gamma, const vpGammaMethod &method, const vpImage<bool> *p_mask)
603 {
604  float inverse_gamma = 1.0;
605  if ((gamma > 0) && (method == GAMMA_MANUAL)) {
606  inverse_gamma = 1.0f / gamma;
607  // Construct the look-up table
608  unsigned char lut[256];
609  for (unsigned int i = 0; i < 256; ++i) {
610  lut[i] = vpMath::saturate<unsigned char>(std::pow(static_cast<float>(i) / 255.0, inverse_gamma) * 255.0);
611  }
612 
613  I.performLut(lut);
614  }
615  else if (method == GAMMA_MANUAL) {
616  std::stringstream errMsg;
617  errMsg << "ERROR: gamma correction factor (";
618  errMsg << gamma << ") cannot be negative when using a constant user-defined factor." << std::endl;
619  throw(vpException(vpException::badValue, errMsg.str()));
620  }
621  else if (gamma > 0) {
622  std::stringstream errMsg;
623  errMsg << "ERROR: asking for automatic gamma correction but setting a user-defined factor (" << gamma << ")." << std::endl;
624  throw(vpException(vpException::badValue, errMsg.str()));
625  }
626  else {
627  if (method == GAMMA_NONLINEAR_BASED) {
629  }
630  else if (method == GAMMA_LOG_BASED) {
631  gammaCorrectionLogMethod(I, p_mask);
632  }
633  else if (method == GAMMA_CLASSIFICATION_BASED) {
635  }
636  else if (method == GAMMA_CDF_BASED) {
638  }
639  else if (method == GAMMA_SPATIAL_VARIANT_BASED) {
640  gammaCorrectionSpatialBased(I, p_mask);
641  }
642  else {
643  std::stringstream errMsg;
644  errMsg << "Gamma automatic method \"" << vpGammaMethodToString(method) << "\" is not handled." << std::endl;
645  throw(vpException(vpException::badValue, errMsg.str()));
646  }
647  }
648 }
649 
650 void gammaCorrection(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, const float &gamma,
651  const vpGammaMethod &method, const vpImage<bool> *p_mask)
652 {
653  I2 = I1;
654  gammaCorrection(I2, gamma, method, p_mask);
655 }
656 
657 void gammaCorrection(vpImage<vpRGBa> &I, const float &gamma, const vpGammaColorHandling &colorHandling,
658  const vpGammaMethod &method, const vpImage<bool> *p_mask)
659 {
660  if (method == GAMMA_SPATIAL_VARIANT_BASED) {
661  gammaCorrectionSpatialBased(I, p_mask);
662  }
663  else {
664  if (colorHandling == GAMMA_HSV) {
665  const unsigned int height = I.getHeight(), width = I.getWidth();
666  unsigned int size = height * width;
667  std::vector<unsigned char> hue(size);
668  std::vector<unsigned char> saturation(size);
669  std::vector<unsigned char> value(size);
670 
671  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), &hue.front(), &saturation.front(), &value.front(), size);
672  vpImage<unsigned char> I_hue(&hue.front(), height, width);
673  vpImage<unsigned char> I_saturation(&saturation.front(), height, width);
674  vpImage<unsigned char> I_value(&value.front(), height, width);
675 
676  gammaCorrection(I_value, gamma, method, p_mask);
677 
678  vpImageConvert::HSVToRGBa(I_hue.bitmap, I_saturation.bitmap, I_value.bitmap, reinterpret_cast<unsigned char *>(I.bitmap), size);
679  }
680  else if (colorHandling == GAMMA_RGB) {
681  vpImage<unsigned char> pR, pG, pB, pa;
682  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
683  gammaCorrection(pR, gamma, method, p_mask);
684  gammaCorrection(pG, gamma, method, p_mask);
685  gammaCorrection(pB, gamma, method, p_mask);
686  gammaCorrection(pa, gamma, method, p_mask);
687  vpImageConvert::merge(&pR, &pG, &pB, &pa, I);
688  }
689  else {
690  std::stringstream errMsg;
691  errMsg << "Gamma color handling mode \"" << vpGammaColorHandlingToString(colorHandling);
692  errMsg << "\" is not handled." << std::endl;
693  throw(vpException(vpException::badValue, errMsg.str()));
694  }
695  }
696 }
697 
698 void gammaCorrection(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, const float &gamma,
699  const vpGammaColorHandling &colorHandling, const vpGammaMethod &method,
700  const vpImage<bool> *p_mask)
701 {
702  I2 = I1;
703  gammaCorrection(I2, gamma, colorHandling, method, p_mask);
704 }
705 
707 {
708  // Find min and max intensity values
709  unsigned char min = 255, max = 0;
710  I.getMinMaxValue(min, max);
711 
712  unsigned char range = max - min;
713 
714  // Construct the look-up table
715  unsigned char lut[256];
716  if (range > 0) {
717  for (unsigned int x = min; x <= max; ++x) {
718  lut[x] = (255 * (x - min)) / range;
719  }
720  }
721  else {
722  lut[min] = min;
723  }
724 
725  I.performLut(lut);
726 }
727 
729 {
730  // Copy I1 to I2
731  I2 = I1;
732  stretchContrast(I2);
733 }
734 
736 {
737  // Find min and max intensity values
738  vpRGBa min = 255, max = 0;
739 
740  // Split the RGBa image into 4 images
745 
746  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
747  // Min max values calculated for each channel
748  unsigned char minChannel, maxChannel;
749  pR.getMinMaxValue(minChannel, maxChannel);
750  min.R = minChannel;
751  max.R = maxChannel;
752 
753  pG.getMinMaxValue(minChannel, maxChannel);
754  min.G = minChannel;
755  max.G = maxChannel;
756 
757  pB.getMinMaxValue(minChannel, maxChannel);
758  min.B = minChannel;
759  max.B = maxChannel;
760 
761  pa.getMinMaxValue(minChannel, maxChannel);
762  min.A = minChannel;
763  max.A = maxChannel;
764 
765  // Construct the look-up table
766  vpRGBa lut[256];
767  unsigned char rangeR = max.R - min.R;
768  if (rangeR > 0) {
769  for (unsigned int x = min.R; x <= max.R; ++x) {
770  lut[x].R = (255 * (x - min.R)) / rangeR;
771  }
772  }
773  else {
774  lut[min.R].R = min.R;
775  }
776 
777  unsigned char rangeG = max.G - min.G;
778  if (rangeG > 0) {
779  for (unsigned int x = min.G; x <= max.G; ++x) {
780  lut[x].G = (255 * (x - min.G)) / rangeG;
781  }
782  }
783  else {
784  lut[min.G].G = min.G;
785  }
786 
787  unsigned char rangeB = max.B - min.B;
788  if (rangeB > 0) {
789  for (unsigned int x = min.B; x <= max.B; ++x) {
790  lut[x].B = (255 * (x - min.B)) / rangeB;
791  }
792  }
793  else {
794  lut[min.B].B = min.B;
795  }
796 
797  unsigned char rangeA = max.A - min.A;
798  if (rangeA > 0) {
799  for (unsigned int x = min.A; x <= max.A; ++x) {
800  lut[x].A = (255 * (x - min.A)) / rangeA;
801  }
802  }
803  else {
804  lut[min.A].A = min.A;
805  }
806 
807  I.performLut(lut);
808 }
809 
811 {
812  // Copy I1 to I2
813  I2 = I1;
814  stretchContrast(I2);
815 }
816 
818 {
819  unsigned int size = I.getWidth() * I.getHeight();
820 
821  // Convert RGB to HSV
822  vpImage<double> hueImage(I.getHeight(), I.getWidth()), saturationImage(I.getHeight(), I.getWidth()),
823  valueImage(I.getHeight(), I.getWidth());
824  vpImageConvert::RGBaToHSV(reinterpret_cast<unsigned char *>(I.bitmap), hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap,
825  size);
826 
827  // Find min and max Saturation and Value
828  double minSaturation, maxSaturation, minValue, maxValue;
829  saturationImage.getMinMaxValue(minSaturation, maxSaturation);
830  valueImage.getMinMaxValue(minValue, maxValue);
831 
832  double *ptrStart = saturationImage.bitmap;
833  double *ptrEnd = saturationImage.bitmap + size;
834  double *ptrCurrent = ptrStart;
835 
836  // Stretch Saturation
837  if ((maxSaturation - minSaturation) > 0.0) {
838  while (ptrCurrent != ptrEnd) {
839  *ptrCurrent = (*ptrCurrent - minSaturation) / (maxSaturation - minSaturation);
840  ++ptrCurrent;
841  }
842  }
843 
844  // Stretch Value
845  if ((maxValue - minValue) > 0.0) {
846  ptrStart = valueImage.bitmap;
847  ptrEnd = valueImage.bitmap + size;
848  ptrCurrent = ptrStart;
849 
850  while (ptrCurrent != ptrEnd) {
851  *ptrCurrent = (*ptrCurrent - minValue) / (maxValue - minValue);
852  ++ptrCurrent;
853  }
854  }
855 
856  // Convert HSV to RGBa
857  vpImageConvert::HSVToRGBa(hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap, reinterpret_cast<unsigned char *>(I.bitmap),
858  size);
859 }
860 
862 {
863  // Copy I1 to I2
864  I2 = I1;
865  stretchContrastHSV(I2);
866 }
867 
868 void unsharpMask(vpImage<unsigned char> &I, float sigma, double weight)
869 {
870  if ((weight < 1.0) && (weight >= 0.0)) {
871 #if defined(VISP_HAVE_SIMDLIB)
872  // Gaussian blurred image
873  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
874  vpImage<unsigned char> I_blurred;
875  gaussian_filter.apply(I, I_blurred);
876 #else
877  // Gaussian blurred image
878  vpImage<double> I_blurred;
879  unsigned int size = 7;
880  (void)sigma;
881  vpImageFilter::gaussianBlur(I, I_blurred, size);
882 #endif
883 
884  // Unsharp mask
885  unsigned int i_size = I.getSize();
886  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
887  double val = (I.bitmap[cpt] - (weight * I_blurred.bitmap[cpt])) / (1 - weight);
888  I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
889  }
890  }
891 }
892 
893 void unsharpMask(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires, float sigma, double weight)
894 {
895  // Copy I to Ires
896  Ires = I;
897  unsharpMask(Ires, sigma, weight);
898 }
899 
900 void unsharpMask(vpImage<vpRGBa> &I, float sigma, double weight)
901 {
902  if ((weight < 1.0) && (weight >= 0.0)) {
903 #if defined(VISP_HAVE_SIMDLIB)
904  // Gaussian blurred image
905  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
906  vpImage<vpRGBa> I_blurred;
907  gaussian_filter.apply(I, I_blurred);
908 #else
909  // Gaussian blurred image
910  vpImage<double> I_blurred_R, I_blurred_G, I_blurred_B;
911  vpImage<unsigned char> I_R, I_G, I_B;
912  unsigned int size = 7;
913  (void)sigma;
914 
915  vpImageConvert::split(I, &I_R, &I_G, &I_B);
916  vpImageFilter::gaussianBlur(I_R, I_blurred_R, size);
917  vpImageFilter::gaussianBlur(I_G, I_blurred_G, size);
918  vpImageFilter::gaussianBlur(I_B, I_blurred_B, size);
919 #endif
920 
921  // Unsharp mask
922  unsigned int i_size = I.getSize();
923  for (unsigned int cpt = 0; cpt < i_size; ++cpt) {
924 #if defined(VISP_HAVE_SIMDLIB)
925  double val_R = (I.bitmap[cpt].R - (weight * I_blurred.bitmap[cpt].R)) / (1 - weight);
926  double val_G = (I.bitmap[cpt].G - (weight * I_blurred.bitmap[cpt].G)) / (1 - weight);
927  double val_B = (I.bitmap[cpt].B - (weight * I_blurred.bitmap[cpt].B)) / (1 - weight);
928 #else
929  double val_R = (I.bitmap[cpt].R - (weight * I_blurred_R.bitmap[cpt])) / (1 - weight);
930  double val_G = (I.bitmap[cpt].G - (weight * I_blurred_G.bitmap[cpt])) / (1 - weight);
931  double val_B = (I.bitmap[cpt].B - (weight * I_blurred_B.bitmap[cpt])) / (1 - weight);
932 #endif
933  I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
934  I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
935  I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
936  }
937  }
938 }
939 
940 void unsharpMask(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, float sigma, double weight)
941 {
942  // Copy I to Ires
943  Ires = I;
944  unsharpMask(Ires, sigma, weight);
945 }
946 
947 } // 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:110
unsigned int getTotal()
Get the total number of pixels in the input image.
Definition: vpHistogram.h:312
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:250
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:1726
unsigned int getWidth() const
Definition: vpImage.h:246
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage.h:2383
unsigned int getSize() const
Definition: vpImage.h:225
Type * bitmap
points toward the bitmap
Definition: vpImage.h:140
double getStdev(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the standard deviation of the bitmap.
Definition: vpImage.h:1008
unsigned int getHeight() const
Definition: vpImage.h:185
void getMinMaxValue(Type &min, Type &max, bool onlyFiniteVal=true) const
Look for the minimum and the maximum value within the bitmap.
Definition: vpImage.h:1264
double getMeanValue(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the mean value of the bitmap.
Definition: vpImage.h:977
Definition: vpRGBa.h:63
unsigned char B
Blue component.
Definition: vpRGBa.h:141
unsigned char R
Red component.
Definition: vpRGBa.h:139
unsigned char G
Green component.
Definition: vpRGBa.h:140
unsigned char A
Additionnal component.
Definition: vpRGBa.h:142
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:391
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:353
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:486
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:444
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:322
VISP_EXPORT std::string vpGammaMethodToString(const vpGammaMethod &type)
Cast a vpGammaMethod into a string, to know its name.
Definition: vpImgproc.cpp:87