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