Visual Servoing Platform version 3.6.0
Loading...
Searching...
No Matches
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/vpMath.h>
64#include <visp3/imgproc/vpImgproc.h>
65
66namespace vp
67{
68void adjust(vpImage<unsigned char> &I, double alpha, double beta)
69{
70 // Construct the look-up table
71 unsigned char lut[256];
72 for (unsigned int i = 0; i < 256; i++) {
73 lut[i] = vpMath::saturate<unsigned char>(alpha * i + beta);
74 }
75
76 // Apply the transformation using a LUT
77 I.performLut(lut);
78}
79
80void adjust(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, double alpha, double beta)
81{
82 // Copy I1 to I2
83 I2 = I1;
84
85 vp::adjust(I2, alpha, beta);
86}
87
88void adjust(vpImage<vpRGBa> &I, double alpha, double beta)
89{
90 // Construct the look-up table
91 vpRGBa lut[256];
92 for (unsigned int i = 0; i < 256; i++) {
93 lut[i].R = vpMath::saturate<unsigned char>(alpha * i + beta);
94 lut[i].G = vpMath::saturate<unsigned char>(alpha * i + beta);
95 lut[i].B = vpMath::saturate<unsigned char>(alpha * i + beta);
96 lut[i].A = vpMath::saturate<unsigned char>(alpha * i + beta);
97 }
98
99 // Apply the transformation using a LUT
100 I.performLut(lut);
101}
102
103void adjust(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, double alpha, double beta)
104{
105 // Copy I1 to I2
106 I2 = I1;
107
108 vp::adjust(I2, alpha, beta);
109}
110
112{
113 if (I.getWidth() * I.getHeight() == 0) {
114 return;
115 }
116
117 // Calculate the histogram
118 vpHistogram hist;
119 hist.calculate(I);
120
121 // Calculate the cumulative distribution function
122 unsigned int cdf[256];
123 unsigned int cdfMin = /*std::numeric_limits<unsigned int>::max()*/ UINT_MAX, cdfMax = 0;
124 unsigned int minValue =
125 /*std::numeric_limits<unsigned int>::max()*/ UINT_MAX,
126 maxValue = 0;
127 cdf[0] = hist[0];
128
129 if (cdf[0] < cdfMin && cdf[0] > 0) {
130 cdfMin = cdf[0];
131 minValue = 0;
132 }
133
134 for (unsigned int i = 1; i < 256; i++) {
135 cdf[i] = cdf[i - 1] + hist[i];
136
137 if (cdf[i] < cdfMin && cdf[i] > 0) {
138 cdfMin = cdf[i];
139 minValue = i;
140 }
141
142 if (cdf[i] > cdfMax) {
143 cdfMax = cdf[i];
144 maxValue = i;
145 }
146 }
147
148 unsigned int nbPixels = I.getWidth() * I.getHeight();
149 if (nbPixels == cdfMin) {
150 // Only one brightness value in the image
151 return;
152 }
153
154 // Construct the look-up table
155 unsigned char lut[256];
156 for (unsigned int x = minValue; x <= maxValue; x++) {
157 lut[x] = vpMath::round((cdf[x] - cdfMin) / (double)(nbPixels - cdfMin) * 255.0);
158 }
159
160 I.performLut(lut);
161}
162
168
170{
171 if (I.getWidth() * I.getHeight() == 0) {
172 return;
173 }
174
175 if (!useHSV) {
176 // Split the RGBa image into 4 images
181
182 vpImageConvert::split(I, &pR, &pG, &pB, &pa);
183
184 // Apply histogram equalization for each channel
188
189 // Merge the result in I
190 unsigned int size = I.getWidth() * I.getHeight();
191 unsigned char *ptrStart = (unsigned char *)I.bitmap;
192 unsigned char *ptrEnd = ptrStart + size * 4;
193 unsigned char *ptrCurrent = ptrStart;
194
195 unsigned int cpt = 0;
196 while (ptrCurrent != ptrEnd) {
197 *ptrCurrent = pR.bitmap[cpt];
198 ++ptrCurrent;
199
200 *ptrCurrent = pG.bitmap[cpt];
201 ++ptrCurrent;
202
203 *ptrCurrent = pB.bitmap[cpt];
204 ++ptrCurrent;
205
206 *ptrCurrent = pa.bitmap[cpt];
207 ++ptrCurrent;
208
209 cpt++;
210 }
211 }
212 else {
214 vpImage<unsigned char> saturation(I.getHeight(), I.getWidth());
216
217 unsigned int size = I.getWidth() * I.getHeight();
218 // Convert from RGBa to HSV
219 vpImageConvert::RGBaToHSV((unsigned char *)I.bitmap, (unsigned char *)hue.bitmap,
220 (unsigned char *)saturation.bitmap, (unsigned char *)value.bitmap, size);
221
222 // Histogram equalization on the value plane
224
225 // Convert from HSV to RGBa
226 vpImageConvert::HSVToRGBa((unsigned char *)hue.bitmap, (unsigned char *)saturation.bitmap,
227 (unsigned char *)value.bitmap, (unsigned char *)I.bitmap, size);
228 }
229}
230
231void equalizeHistogram(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, bool useHSV)
232{
233 I2 = I1;
234 vp::equalizeHistogram(I2, useHSV);
235}
236
238{
239 double inverse_gamma = 1.0;
240 if (gamma > 0) {
241 inverse_gamma = 1.0 / gamma;
242 }
243 else {
244 throw vpException(vpException::badValue, "The gamma value must be positive !");
245 }
246
247 // Construct the look-up table
248 unsigned char lut[256];
249 for (unsigned int i = 0; i < 256; i++) {
250 lut[i] = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
251 }
252
253 I.performLut(lut);
254}
255
257{
258 I2 = I1;
259 vp::gammaCorrection(I2, gamma);
260}
261
262void gammaCorrection(vpImage<vpRGBa> &I, double gamma)
263{
264 double inverse_gamma = 1.0;
265 if (gamma > 0) {
266 inverse_gamma = 1.0 / gamma;
267 }
268 else {
269 throw vpException(vpException::badValue, "The gamma value must be positive !");
270 }
271
272 // Construct the look-up table
273 vpRGBa lut[256];
274 for (unsigned int i = 0; i < 256; i++) {
275 lut[i].R = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
276 lut[i].G = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
277 lut[i].B = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
278 lut[i].A = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
279 }
280
281 I.performLut(lut);
282}
283
284void gammaCorrection(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, double gamma)
285{
286 I2 = I1;
287 vp::gammaCorrection(I2, gamma);
288}
289
291{
292 // Find min and max intensity values
293 unsigned char min = 255, max = 0;
294 I.getMinMaxValue(min, max);
295
296 unsigned char range = max - min;
297
298 // Construct the look-up table
299 unsigned char lut[256];
300 if (range > 0) {
301 for (unsigned int x = min; x <= max; x++) {
302 lut[x] = 255 * (x - min) / range;
303 }
304 }
305 else {
306 lut[min] = min;
307 }
308
309 I.performLut(lut);
310}
311
313{
314 // Copy I1 to I2
315 I2 = I1;
317}
318
320{
321 // Find min and max intensity values
322 vpRGBa min = 255, max = 0;
323
324 // Split the RGBa image into 4 images
329
330 vpImageConvert::split(I, &pR, &pG, &pB, &pa);
331 // Min max values calculated for each channel
332 unsigned char minChannel, maxChannel;
333 pR.getMinMaxValue(minChannel, maxChannel);
334 min.R = minChannel;
335 max.R = maxChannel;
336
337 pG.getMinMaxValue(minChannel, maxChannel);
338 min.G = minChannel;
339 max.G = maxChannel;
340
341 pB.getMinMaxValue(minChannel, maxChannel);
342 min.B = minChannel;
343 max.B = maxChannel;
344
345 pa.getMinMaxValue(minChannel, maxChannel);
346 min.A = minChannel;
347 max.A = maxChannel;
348
349 // Construct the look-up table
350 vpRGBa lut[256];
351 unsigned char rangeR = max.R - min.R;
352 if (rangeR > 0) {
353 for (unsigned int x = min.R; x <= max.R; x++) {
354 lut[x].R = 255 * (x - min.R) / rangeR;
355 }
356 }
357 else {
358 lut[min.R].R = min.R;
359 }
360
361 unsigned char rangeG = max.G - min.G;
362 if (rangeG > 0) {
363 for (unsigned int x = min.G; x <= max.G; x++) {
364 lut[x].G = 255 * (x - min.G) / rangeG;
365 }
366 }
367 else {
368 lut[min.G].G = min.G;
369 }
370
371 unsigned char rangeB = max.B - min.B;
372 if (rangeB > 0) {
373 for (unsigned int x = min.B; x <= max.B; x++) {
374 lut[x].B = 255 * (x - min.B) / rangeB;
375 }
376 }
377 else {
378 lut[min.B].B = min.B;
379 }
380
381 unsigned char rangeA = max.A - min.A;
382 if (rangeA > 0) {
383 for (unsigned int x = min.A; x <= max.A; x++) {
384 lut[x].A = 255 * (x - min.A) / rangeA;
385 }
386 }
387 else {
388 lut[min.A].A = min.A;
389 }
390
391 I.performLut(lut);
392}
393
395{
396 // Copy I1 to I2
397 I2 = I1;
399}
400
402{
403 unsigned int size = I.getWidth() * I.getHeight();
404
405 // Convert RGB to HSV
406 vpImage<double> hueImage(I.getHeight(), I.getWidth()), saturationImage(I.getHeight(), I.getWidth()),
407 valueImage(I.getHeight(), I.getWidth());
408 vpImageConvert::RGBaToHSV((unsigned char *)I.bitmap, hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap,
409 size);
410
411 // Find min and max Saturation and Value
412 double minSaturation, maxSaturation, minValue, maxValue;
413 saturationImage.getMinMaxValue(minSaturation, maxSaturation);
414 valueImage.getMinMaxValue(minValue, maxValue);
415
416 double *ptrStart = saturationImage.bitmap;
417 double *ptrEnd = saturationImage.bitmap + size;
418 double *ptrCurrent = ptrStart;
419
420 // Stretch Saturation
421 if (maxSaturation - minSaturation > 0.0) {
422 while (ptrCurrent != ptrEnd) {
423 *ptrCurrent = (*ptrCurrent - minSaturation) / (maxSaturation - minSaturation);
424 ++ptrCurrent;
425 }
426 }
427
428 // Stretch Value
429 if (maxValue - minValue > 0.0) {
430 ptrStart = valueImage.bitmap;
431 ptrEnd = valueImage.bitmap + size;
432 ptrCurrent = ptrStart;
433
434 while (ptrCurrent != ptrEnd) {
435 *ptrCurrent = (*ptrCurrent - minValue) / (maxValue - minValue);
436 ++ptrCurrent;
437 }
438 }
439
440 // Convert HSV to RGBa
441 vpImageConvert::HSVToRGBa(hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap, (unsigned char *)I.bitmap,
442 size);
443}
444
446{
447 // Copy I1 to I2
448 I2 = I1;
450}
451
452void unsharpMask(vpImage<unsigned char> &I, float sigma, double weight)
453{
454 if (weight < 1.0 && weight >= 0.0) {
455 // Gaussian blurred image
456 vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
457 vpImage<unsigned char> I_blurred;
458 gaussian_filter.apply(I, I_blurred);
459
460 // Unsharp mask
461 for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
462 double val = (I.bitmap[cpt] - weight * I_blurred.bitmap[cpt]) / (1 - weight);
463 I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
464 }
465 }
466}
467
468void unsharpMask(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires, float sigma, double weight)
469{
470 // Copy I to Ires
471 Ires = I;
472 vp::unsharpMask(Ires, sigma, weight);
473}
474
475void unsharpMask(vpImage<vpRGBa> &I, float sigma, double weight)
476{
477 if (weight < 1.0 && weight >= 0.0) {
478 // Gaussian blurred image
479 vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
480 vpImage<vpRGBa> I_blurred;
481 gaussian_filter.apply(I, I_blurred);
482
483 // Unsharp mask
484 for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
485 double val_R = (I.bitmap[cpt].R - weight * I_blurred.bitmap[cpt].R) / (1 - weight);
486 double val_G = (I.bitmap[cpt].G - weight * I_blurred.bitmap[cpt].G) / (1 - weight);
487 double val_B = (I.bitmap[cpt].B - weight * I_blurred.bitmap[cpt].B) / (1 - weight);
488
489 I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
490 I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
491 I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
492 }
493 }
494}
495
496void unsharpMask(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, float sigma, double weight)
497{
498 // Copy I to Ires
499 Ires = I;
500 vp::unsharpMask(Ires, sigma, weight);
501}
502
503#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
516void unsharpMask(vpImage<unsigned char> &I, unsigned int size, double weight)
517{
518 if (weight < 1.0 && weight >= 0.0) {
519 // Gaussian blurred image
520 vpImage<double> I_blurred;
521 vpImageFilter::gaussianBlur(I, I_blurred, size);
522
523 // Unsharp mask
524 for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
525 double val = (I.bitmap[cpt] - weight * I_blurred.bitmap[cpt]) / (1 - weight);
526 I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
527 }
528 }
529}
530
544void unsharpMask(const vpImage<unsigned char> &I1, vpImage<unsigned char> &I2, unsigned int size, double weight)
545{
546 // Copy I1 to I2
547 I2 = I1;
548#if 0
549 vp::unsharpMask(I2, size, weight);
550#else
551 // To avoid:
552 // warning: ‘void vp::unsharpMask(vpImage<unsigned char>&, unsigned int, double)’ is deprecated
553 // [-Wdeprecated-declarations]
554 if (weight < 1.0 && weight >= 0.0) {
555 // Gaussian blurred image
556 vpImage<double> I_blurred;
557 vpImageFilter::gaussianBlur(I2, I_blurred, size);
558
559 // Unsharp mask
560 for (unsigned int cpt = 0; cpt < I2.getSize(); cpt++) {
561 double val = (I2.bitmap[cpt] - weight * I_blurred.bitmap[cpt]) / (1 - weight);
562 I2.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
563 }
564 }
565#endif
566}
567
580void unsharpMask(vpImage<vpRGBa> &I, unsigned int size, double weight)
581{
582 if (weight < 1.0 && weight >= 0.0) {
583 // Gaussian blurred image
584 vpImage<double> I_blurred_R, I_blurred_G, I_blurred_B;
585 vpImage<unsigned char> I_R, I_G, I_B;
586
587 vpImageConvert::split(I, &I_R, &I_G, &I_B);
588 vpImageFilter::gaussianBlur(I_R, I_blurred_R, size);
589 vpImageFilter::gaussianBlur(I_G, I_blurred_G, size);
590 vpImageFilter::gaussianBlur(I_B, I_blurred_B, size);
591
592 // Unsharp mask
593 for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
594 double val_R = (I.bitmap[cpt].R - weight * I_blurred_R.bitmap[cpt]) / (1 - weight);
595 double val_G = (I.bitmap[cpt].G - weight * I_blurred_G.bitmap[cpt]) / (1 - weight);
596 double val_B = (I.bitmap[cpt].B - weight * I_blurred_B.bitmap[cpt]) / (1 - weight);
597
598 I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
599 I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
600 I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
601 }
602 }
603}
604
618void unsharpMask(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, unsigned int size, double weight)
619{
620 // Copy I1 to I2
621 I2 = I1;
622#if 0
623 vp::unsharpMask(I2, size, weight);
624#else
625 // To avoid:
626 // warning: ‘void vp::unsharpMask(vpImage<vpRGBa>&, unsigned int, double)’ is deprecated [-Wdeprecated-declarations]
627 if (weight < 1.0 && weight >= 0.0) {
628 // Gaussian blurred image
629 vpImage<double> I_blurred_R, I_blurred_G, I_blurred_B;
630 vpImage<unsigned char> I_R, I_G, I_B;
631
632 vpImageConvert::split(I2, &I_R, &I_G, &I_B);
633 vpImageFilter::gaussianBlur(I_R, I_blurred_R, size);
634 vpImageFilter::gaussianBlur(I_G, I_blurred_G, size);
635 vpImageFilter::gaussianBlur(I_B, I_blurred_B, size);
636
637 // Unsharp mask
638 for (unsigned int cpt = 0; cpt < I2.getSize(); cpt++) {
639 double val_R = (I2.bitmap[cpt].R - weight * I_blurred_R.bitmap[cpt]) / (1 - weight);
640 double val_G = (I2.bitmap[cpt].G - weight * I_blurred_G.bitmap[cpt]) / (1 - weight);
641 double val_B = (I2.bitmap[cpt].B - weight * I_blurred_B.bitmap[cpt]) / (1 - weight);
642
643 I2.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
644 I2.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
645 I2.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
646 }
647 }
648#endif
649}
650#endif // Deprecated
651};
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.
void calculate(const vpImage< unsigned char > &I, unsigned int nbins=256, unsigned int nbThreads=1)
static void HSVToRGBa(const double *hue, const double *saturation, const double *value, unsigned char *rgba, unsigned int size)
static void split(const vpImage< vpRGBa > &src, vpImage< unsigned char > *pR, vpImage< unsigned char > *pG, vpImage< unsigned char > *pB, vpImage< unsigned char > *pa=NULL)
static void RGBaToHSV(const unsigned char *rgba, double *hue, double *saturation, double *value, unsigned int size)
static void gaussianBlur(const vpImage< unsigned char > &I, vpImage< FilterType > &GI, unsigned int size=7, FilterType sigma=0., bool normalize=true)
Definition of the vpImage class member functions.
Definition vpImage.h:135
unsigned int getWidth() const
Definition vpImage.h:242
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition vpImage.h:2017
unsigned int getSize() const
Definition vpImage.h:223
Type * bitmap
points toward the bitmap
Definition vpImage.h:139
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:1054
static int round(double x)
Definition vpMath.h:323
static _Tp saturate(unsigned char v)
Definition vpMath.h:221
unsigned char B
Blue component.
Definition vpRGBa.h:140
unsigned char R
Red component.
Definition vpRGBa.h:138
unsigned char G
Green component.
Definition vpRGBa.h:139
unsigned char A
Additionnal component.
Definition vpRGBa.h:141
VISP_EXPORT void adjust(vpImage< unsigned char > &I, double alpha, double beta)
Definition vpImgproc.cpp:68
VISP_EXPORT void stretchContrast(vpImage< unsigned char > &I)
VISP_EXPORT void stretchContrastHSV(vpImage< vpRGBa > &I)
VISP_EXPORT void gammaCorrection(vpImage< unsigned char > &I, double gamma)
VISP_EXPORT void equalizeHistogram(vpImage< unsigned char > &I)
vp_deprecated VISP_EXPORT void unsharpMask(vpImage< unsigned char > &I, unsigned int size=7, double weight=0.6)