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