Visual Servoing Platform  version 3.6.1 under development (2023-12-07)
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 
66 namespace vp
67 {
68 void 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 
80 void 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 
88 void 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 
103 void 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  vpImage<unsigned char> Icpy = I;
114  vp::equalizeHistogram(Icpy, I);
115 }
116 
118 {
119  if (I1.getWidth() * I1.getHeight() == 0) {
120  return;
121  }
122 
123  // Calculate the histogram
124  vpHistogram hist;
125  hist.equalize(I1, I2);
126 }
127 
128 void equalizeHistogram(vpImage<vpRGBa> &I, bool useHSV)
129 {
130  if (I.getWidth() * I.getHeight() == 0) {
131  return;
132  }
133 
134  if (!useHSV) {
135  // Split the RGBa image into 4 images
140 
141  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
142 
143  // Apply histogram equalization for each channel
147 
148  // Merge the result in I
149  unsigned int size = I.getWidth() * I.getHeight();
150  unsigned char *ptrStart = (unsigned char *)I.bitmap;
151  unsigned char *ptrEnd = ptrStart + size * 4;
152  unsigned char *ptrCurrent = ptrStart;
153 
154  unsigned int cpt = 0;
155  while (ptrCurrent != ptrEnd) {
156  *ptrCurrent = pR.bitmap[cpt];
157  ++ptrCurrent;
158 
159  *ptrCurrent = pG.bitmap[cpt];
160  ++ptrCurrent;
161 
162  *ptrCurrent = pB.bitmap[cpt];
163  ++ptrCurrent;
164 
165  *ptrCurrent = pa.bitmap[cpt];
166  ++ptrCurrent;
167 
168  cpt++;
169  }
170  }
171  else {
173  vpImage<unsigned char> saturation(I.getHeight(), I.getWidth());
174  vpImage<unsigned char> value(I.getHeight(), I.getWidth());
175 
176  unsigned int size = I.getWidth() * I.getHeight();
177  // Convert from RGBa to HSV
178  vpImageConvert::RGBaToHSV((unsigned char *)I.bitmap, (unsigned char *)hue.bitmap,
179  (unsigned char *)saturation.bitmap, (unsigned char *)value.bitmap, size);
180 
181  // Histogram equalization on the value plane
182  vp::equalizeHistogram(value);
183 
184  // Convert from HSV to RGBa
185  vpImageConvert::HSVToRGBa((unsigned char *)hue.bitmap, (unsigned char *)saturation.bitmap,
186  (unsigned char *)value.bitmap, (unsigned char *)I.bitmap, size);
187  }
188 }
189 
190 void equalizeHistogram(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, bool useHSV)
191 {
192  I2 = I1;
193  vp::equalizeHistogram(I2, useHSV);
194 }
195 
197 {
198  double inverse_gamma = 1.0;
199  if (gamma > 0) {
200  inverse_gamma = 1.0 / gamma;
201  }
202  else {
203  throw vpException(vpException::badValue, "The gamma value must be positive !");
204  }
205 
206  // Construct the look-up table
207  unsigned char lut[256];
208  for (unsigned int i = 0; i < 256; i++) {
209  lut[i] = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
210  }
211 
212  I.performLut(lut);
213 }
214 
216 {
217  I2 = I1;
218  vp::gammaCorrection(I2, gamma);
219 }
220 
221 void gammaCorrection(vpImage<vpRGBa> &I, double gamma)
222 {
223  double inverse_gamma = 1.0;
224  if (gamma > 0) {
225  inverse_gamma = 1.0 / gamma;
226  }
227  else {
228  throw vpException(vpException::badValue, "The gamma value must be positive !");
229  }
230 
231  // Construct the look-up table
232  vpRGBa lut[256];
233  for (unsigned int i = 0; i < 256; i++) {
234  lut[i].R = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
235  lut[i].G = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
236  lut[i].B = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
237  lut[i].A = vpMath::saturate<unsigned char>(pow((double)i / 255.0, inverse_gamma) * 255.0);
238  }
239 
240  I.performLut(lut);
241 }
242 
243 void gammaCorrection(const vpImage<vpRGBa> &I1, vpImage<vpRGBa> &I2, double gamma)
244 {
245  I2 = I1;
246  vp::gammaCorrection(I2, gamma);
247 }
248 
250 {
251  // Find min and max intensity values
252  unsigned char min = 255, max = 0;
253  I.getMinMaxValue(min, max);
254 
255  unsigned char range = max - min;
256 
257  // Construct the look-up table
258  unsigned char lut[256];
259  if (range > 0) {
260  for (unsigned int x = min; x <= max; x++) {
261  lut[x] = 255 * (x - min) / range;
262  }
263  }
264  else {
265  lut[min] = min;
266  }
267 
268  I.performLut(lut);
269 }
270 
272 {
273  // Copy I1 to I2
274  I2 = I1;
276 }
277 
279 {
280  // Find min and max intensity values
281  vpRGBa min = 255, max = 0;
282 
283  // Split the RGBa image into 4 images
288 
289  vpImageConvert::split(I, &pR, &pG, &pB, &pa);
290  // Min max values calculated for each channel
291  unsigned char minChannel, maxChannel;
292  pR.getMinMaxValue(minChannel, maxChannel);
293  min.R = minChannel;
294  max.R = maxChannel;
295 
296  pG.getMinMaxValue(minChannel, maxChannel);
297  min.G = minChannel;
298  max.G = maxChannel;
299 
300  pB.getMinMaxValue(minChannel, maxChannel);
301  min.B = minChannel;
302  max.B = maxChannel;
303 
304  pa.getMinMaxValue(minChannel, maxChannel);
305  min.A = minChannel;
306  max.A = maxChannel;
307 
308  // Construct the look-up table
309  vpRGBa lut[256];
310  unsigned char rangeR = max.R - min.R;
311  if (rangeR > 0) {
312  for (unsigned int x = min.R; x <= max.R; x++) {
313  lut[x].R = 255 * (x - min.R) / rangeR;
314  }
315  }
316  else {
317  lut[min.R].R = min.R;
318  }
319 
320  unsigned char rangeG = max.G - min.G;
321  if (rangeG > 0) {
322  for (unsigned int x = min.G; x <= max.G; x++) {
323  lut[x].G = 255 * (x - min.G) / rangeG;
324  }
325  }
326  else {
327  lut[min.G].G = min.G;
328  }
329 
330  unsigned char rangeB = max.B - min.B;
331  if (rangeB > 0) {
332  for (unsigned int x = min.B; x <= max.B; x++) {
333  lut[x].B = 255 * (x - min.B) / rangeB;
334  }
335  }
336  else {
337  lut[min.B].B = min.B;
338  }
339 
340  unsigned char rangeA = max.A - min.A;
341  if (rangeA > 0) {
342  for (unsigned int x = min.A; x <= max.A; x++) {
343  lut[x].A = 255 * (x - min.A) / rangeA;
344  }
345  }
346  else {
347  lut[min.A].A = min.A;
348  }
349 
350  I.performLut(lut);
351 }
352 
354 {
355  // Copy I1 to I2
356  I2 = I1;
358 }
359 
361 {
362  unsigned int size = I.getWidth() * I.getHeight();
363 
364  // Convert RGB to HSV
365  vpImage<double> hueImage(I.getHeight(), I.getWidth()), saturationImage(I.getHeight(), I.getWidth()),
366  valueImage(I.getHeight(), I.getWidth());
367  vpImageConvert::RGBaToHSV((unsigned char *)I.bitmap, hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap,
368  size);
369 
370  // Find min and max Saturation and Value
371  double minSaturation, maxSaturation, minValue, maxValue;
372  saturationImage.getMinMaxValue(minSaturation, maxSaturation);
373  valueImage.getMinMaxValue(minValue, maxValue);
374 
375  double *ptrStart = saturationImage.bitmap;
376  double *ptrEnd = saturationImage.bitmap + size;
377  double *ptrCurrent = ptrStart;
378 
379  // Stretch Saturation
380  if (maxSaturation - minSaturation > 0.0) {
381  while (ptrCurrent != ptrEnd) {
382  *ptrCurrent = (*ptrCurrent - minSaturation) / (maxSaturation - minSaturation);
383  ++ptrCurrent;
384  }
385  }
386 
387  // Stretch Value
388  if (maxValue - minValue > 0.0) {
389  ptrStart = valueImage.bitmap;
390  ptrEnd = valueImage.bitmap + size;
391  ptrCurrent = ptrStart;
392 
393  while (ptrCurrent != ptrEnd) {
394  *ptrCurrent = (*ptrCurrent - minValue) / (maxValue - minValue);
395  ++ptrCurrent;
396  }
397  }
398 
399  // Convert HSV to RGBa
400  vpImageConvert::HSVToRGBa(hueImage.bitmap, saturationImage.bitmap, valueImage.bitmap, (unsigned char *)I.bitmap,
401  size);
402 }
403 
405 {
406  // Copy I1 to I2
407  I2 = I1;
409 }
410 
411 void unsharpMask(vpImage<unsigned char> &I, float sigma, double weight)
412 {
413  if (weight < 1.0 && weight >= 0.0) {
414  // Gaussian blurred image
415  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
416  vpImage<unsigned char> I_blurred;
417  gaussian_filter.apply(I, I_blurred);
418 
419  // Unsharp mask
420  for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
421  double val = (I.bitmap[cpt] - weight * I_blurred.bitmap[cpt]) / (1 - weight);
422  I.bitmap[cpt] = vpMath::saturate<unsigned char>(val); // val > 255 ? 255 : (val < 0 ? 0 : val);
423  }
424  }
425 }
426 
427 void unsharpMask(const vpImage<unsigned char> &I, vpImage<unsigned char> &Ires, float sigma, double weight)
428 {
429  // Copy I to Ires
430  Ires = I;
431  vp::unsharpMask(Ires, sigma, weight);
432 }
433 
434 void unsharpMask(vpImage<vpRGBa> &I, float sigma, double weight)
435 {
436  if (weight < 1.0 && weight >= 0.0) {
437  // Gaussian blurred image
438  vpGaussianFilter gaussian_filter(I.getWidth(), I.getHeight(), sigma);
439  vpImage<vpRGBa> I_blurred;
440  gaussian_filter.apply(I, I_blurred);
441 
442  // Unsharp mask
443  for (unsigned int cpt = 0; cpt < I.getSize(); cpt++) {
444  double val_R = (I.bitmap[cpt].R - weight * I_blurred.bitmap[cpt].R) / (1 - weight);
445  double val_G = (I.bitmap[cpt].G - weight * I_blurred.bitmap[cpt].G) / (1 - weight);
446  double val_B = (I.bitmap[cpt].B - weight * I_blurred.bitmap[cpt].B) / (1 - weight);
447 
448  I.bitmap[cpt].R = vpMath::saturate<unsigned char>(val_R);
449  I.bitmap[cpt].G = vpMath::saturate<unsigned char>(val_G);
450  I.bitmap[cpt].B = vpMath::saturate<unsigned char>(val_B);
451  }
452  }
453 }
454 
455 void unsharpMask(const vpImage<vpRGBa> &I, vpImage<vpRGBa> &Ires, float sigma, double weight)
456 {
457  // Copy I to Ires
458  Ires = I;
459  vp::unsharpMask(Ires, sigma, weight);
460 }
461 
462 };
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
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 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)
unsigned int getWidth() const
Definition: vpImage.h:240
void performLut(const Type(&lut)[256], unsigned int nbThreads=1)
Definition: vpImage.h:2013
unsigned int getSize() const
Definition: vpImage.h:221
Type * bitmap
points toward the bitmap
Definition: vpImage.h:139
unsigned int getHeight() const
Definition: vpImage.h:182
void getMinMaxValue(Type &min, Type &max, bool onlyFiniteVal=true) const
Look for the minimum and the maximum value within the bitmap.
Definition: vpImage.h:1050
Definition: vpRGBa.h:61
unsigned char B
Blue component.
Definition: vpRGBa.h:137
unsigned char R
Red component.
Definition: vpRGBa.h:135
unsigned char G
Green component.
Definition: vpRGBa.h:136
unsigned char A
Additionnal component.
Definition: vpRGBa.h:138
VISP_EXPORT void adjust(vpImage< unsigned char > &I, double alpha, double beta)
Definition: vpImgproc.cpp:68
VISP_EXPORT void stretchContrast(vpImage< unsigned char > &I)
Definition: vpImgproc.cpp:249
VISP_EXPORT void stretchContrastHSV(vpImage< vpRGBa > &I)
Definition: vpImgproc.cpp:360
VISP_EXPORT void gammaCorrection(vpImage< unsigned char > &I, double gamma)
Definition: vpImgproc.cpp:196
VISP_EXPORT void equalizeHistogram(vpImage< unsigned char > &I)
Definition: vpImgproc.cpp:111
VISP_EXPORT void unsharpMask(vpImage< unsigned char > &I, float sigma, double weight=0.6)
Definition: vpImgproc.cpp:411