diff --git a/doc/tutorial/imgproc/tutorial-imgproc-brightness.dox b/doc/tutorial/imgproc/tutorial-imgproc-brightness.dox
index 1797cf6fb1..eafd02e649 100644
--- a/doc/tutorial/imgproc/tutorial-imgproc-brightness.dox
+++ b/doc/tutorial/imgproc/tutorial-imgproc-brightness.dox
@@ -49,6 +49,13 @@ The result image is the following:
\image html img-tutorial-brighness-gamma-correction-3.5.png "Left: underexposed image - Right: image corrected with gamma=3.5"
+ViSP proposes the implementation of several automatic computation of the gamma factor.
+Most of these methods are designed for gray-shade images, so ViSP proposes different way
+of handling the colors.
+
+You can test the different methods using the `--gamma-method` option of the tutorial program
+and the different way of handling the colors using the `--gamma-color-handling` option.
+
\section imgproc_brightness_histogram_equalization Histogram equalization
Histogram equalization is an image processing method that will adjust the contrast of an image by stretching or shrinking the intensity distribution in order to have a linear cumulative histogram distribution.
diff --git a/modules/core/include/visp3/core/vpImage.h b/modules/core/include/visp3/core/vpImage.h
index 1184627d29..b3f82d0794 100644
--- a/modules/core/include/visp3/core/vpImage.h
+++ b/modules/core/include/visp3/core/vpImage.h
@@ -186,7 +186,13 @@ template class vpImage
// Return the maximum value within the bitmap
Type getMaxValue(bool onlyFiniteVal = true) const;
// Return the mean value of the bitmap
- Type getMeanValue() const;
+ double getMeanValue() const;
+ double getMeanValue(const vpImage *p_mask) const;
+ double getMeanValue(const vpImage *p_mask, unsigned int &nbValidPoints) const;
+ double getStdev() const;
+ double getStdev(const vpImage *p_mask) const;
+ double getStdev(const double &mean) const;
+ double getStdev(const double &mean, const unsigned int &nbValidPoints, const vpImage *p_mask) const;
// Return the minumum value within the bitmap
Type getMinValue(bool onlyFiniteVal = true) const;
// Look for the minumum and the maximum value within the bitmap
@@ -231,6 +237,7 @@ template class vpImage
// Get image pixels sum
double getSum() const;
+ double getSum(const vpImage *p_mask, unsigned int &nbValidPoints) const;
/*!
Get the image width.
@@ -939,14 +946,103 @@ template <> inline float vpImage::getMaxValue(bool onlyFiniteVal) const
/*!
\brief Return the mean value of the bitmap
*/
-template Type vpImage::getMeanValue() const
+template double vpImage::getMeanValue() const
{
- if ((height == 0) || (width == 0))
+ if ((height == 0) || (width == 0)) {
return 0.0;
+ }
return getSum() / (height * width);
}
+/*!
+ \brief Return the mean value of the bitmap
+
+ \param[in] p_mask A boolean mask that indicates which points must be considered, if set.
+*/
+template double vpImage::getMeanValue(const vpImage *p_mask) const
+{
+ unsigned int nbValidPoints = 0;
+ return getMeanValue(p_mask, nbValidPoints);
+}
+
+/*!
+ \brief Return the mean value of the bitmap
+
+ \param[in] p_mask A boolean mask that indicates which points must be considered, if set.
+ \param[out] nbValidPoints Number of points that are valid according to the boolean mask.
+*/
+template double vpImage::getMeanValue(const vpImage *p_mask, unsigned int &nbValidPoints) const
+{
+ nbValidPoints = 0;
+ if ((height == 0) || (width == 0)) {
+ return 0.0;
+ }
+
+ double sum = getSum(p_mask, nbValidPoints);
+ return sum / nbValidPoints;
+}
+
+/*!
+* \brief Return the standard deviation of the bitmap
+*/
+template double vpImage::getStdev() const
+{
+ double mean = getMeanValue();
+ return getStdev(mean);
+}
+
+/*!
+* \brief Return the standard deviation of the bitmap
+*
+* \param[in] p_mask A boolean mask that indicates which points must be considered, if set.
+*/
+template double vpImage::getStdev(const vpImage *p_mask) const
+{
+ unsigned int nbValidPoints = 0;
+ double mean = getMeanValue(p_mask, nbValidPoints);
+ return getStdev(mean, nbValidPoints, p_mask);
+}
+
+/*!
+* \brief Return the standard deviation of the bitmap
+*
+* \param[in] mean The mean of the image.
+*/
+template double vpImage::getStdev(const double &mean) const
+{
+ const unsigned int size = width * height;
+ double sum = 0.;
+ for (unsigned int i = 0; i < size; ++i) {
+ sum += (bitmap[i] - mean) * (bitmap[i] - mean);
+ }
+ sum /= static_cast(size);
+ return std::sqrt(sum);
+}
+
+/*!
+* \brief Return the standard deviation of the bitmap
+*
+* \param[in] mean The mean of the image.
+* \param[in] nbValidPoints Number of points that are valid according to the boolean mask.
+* \param[in] p_mask A boolean mask that indicates which points must be considered, if set.
+*/
+template double vpImage::getStdev(const double &mean, const unsigned int &nbValidPoints, const vpImage *p_mask) const
+{
+ if (p_mask == nullptr) {
+ return getStdev(mean);
+ }
+ const unsigned int size = width * height;
+ double sum = 0.;
+ for (unsigned int i = 0; i < size; ++i) {
+ if (p_mask->bitmap[i]) {
+ sum += (bitmap[i] - mean) * (bitmap[i] - mean);
+ }
+ }
+ sum /= static_cast(nbValidPoints);
+ return std::sqrt(sum);
+}
+
/*!
* \brief Return the minimum value within the bitmap
* \param onlyFiniteVal : This parameter is ignored for non double or non float bitmap.
@@ -1869,6 +1965,34 @@ template inline double vpImage::getSum() const
return res;
}
+/**
+ * Compute the sum of image intensities.
+ * For vpRGBa image type, compute the sum (R+G+B) of image intensities.
+ *
+ * \param[in] p_mask Boolean mask that indicates the valid points by a true flag.
+ * \param[out] nbValidPoints The number of valid points according to the \b p_mask.
+ */
+template inline double vpImage::getSum(const vpImage *p_mask, unsigned int &nbValidPoints) const
+{
+ if ((height == 0) || (width == 0))
+ return 0.0;
+ if (p_mask == nullptr) {
+ nbValidPoints = height * width;
+ return getSum();
+ }
+
+ double res = 0.0;
+ nbValidPoints = 0;
+ unsigned int size = height * width;
+ for (unsigned int i = 0; i < size; ++i) {
+ if (p_mask->bitmap[i]) {
+ res += static_cast(bitmap[i]);
+ ++nbValidPoints;
+ }
+ }
+ return res;
+}
+
/**
* \relates vpImage
*/
@@ -1884,6 +2008,32 @@ template <> inline double vpImage::getSum() const
return res;
}
+/**
+ * \relates vpImage
+ */
+template <> inline double vpImage::getSum(const vpImage *p_mask, unsigned int &nbValidPoints) const
+{
+ if ((height == 0) || (width == 0)) {
+ return 0.0;
+ }
+
+ if (p_mask == nullptr) {
+ nbValidPoints = height * width;
+ return getSum();
+ }
+
+ double res = 0.0;
+ nbValidPoints = 0;
+ unsigned int size = height * width;
+ for (unsigned int i = 0; i < size; ++i) {
+ if (p_mask->bitmap[i]) {
+ res += static_cast(bitmap[i].R) + static_cast(bitmap[i].G) + static_cast(bitmap[i].B);
+ ++nbValidPoints;
+ }
+ }
+ return res;
+}
+
/**
* \relates vpImage
*/
@@ -1899,6 +2049,31 @@ template <> inline double vpImage::getSum() const
return res;
}
+/**
+ * \relates vpImage
+ */
+template <> inline double vpImage::getSum(const vpImage *p_mask, unsigned int &nbValidPoints) const
+{
+ if ((height == 0) || (width == 0)) {
+ return 0.0;
+ }
+ if (p_mask == nullptr) {
+ nbValidPoints = height * width;
+ return getSum();
+ }
+
+ double res = 0.0;
+ nbValidPoints = 0;
+ unsigned int size = height * width;
+ for (unsigned int i = 0; i < size; ++i) {
+ if (p_mask->bitmap[i]) {
+ res += static_cast(bitmap[i].R) + static_cast(bitmap[i].G) + static_cast(bitmap[i].B);
+ ++nbValidPoints;
+ }
+ }
+ return res;
+}
+
/*!
Operation C = *this - B.
diff --git a/modules/imgproc/include/visp3/imgproc/vpImgproc.h b/modules/imgproc/include/visp3/imgproc/vpImgproc.h
index bd7f7cfdd9..e6b5ad16da 100644
--- a/modules/imgproc/include/visp3/imgproc/vpImgproc.h
+++ b/modules/imgproc/include/visp3/imgproc/vpImgproc.h
@@ -40,6 +40,7 @@
#ifndef _vpImgproc_h_
#define _vpImgproc_h_
+#include
#include
#include
#include
@@ -91,6 +92,93 @@ typedef enum
*/
} vpAutoThresholdMethod;
+/**
+ * \brief Gamma Correction automatic methods.
+ */
+typedef enum vpGammaMethod
+{
+ GAMMA_MANUAL = 0, /*!< User-defined constant positive gamma factor.*/
+ GAMMA_LOG_BASED = 1, /*!< Scott, J & Pusateri M (2009)"Towards Real-time Hardware
+ Gamma Correction for Dynamic Contrast Enhancement"
+ IEEE Applied Imagery Pattern Recognition Workshop (AIPR 2009)*/
+ GAMMA_NONLINEAR_BASED = 2, /*!< Shi, Y et al. (2007), "Reducing Illumination Based On Nonlinear Gamma Correction",
+ International Conference on Image Processing */
+ GAMMA_CDF_BASED = 3, /*!< Huang, SC et al. (2013),"Efficient Contrast Enhancement Using Adaptive
+ Gamma Correction With Weighting Distribution",
+ IEEE Trans. on Image Processing, VOL. 22, NO. 3, MARCH 2013. */
+ GAMMA_CLASSIFICATION_BASED = 4, /*!< Rahman, S et al. (2016), "An adaptive gamma correction for image
+ enhancement", EURASIP Journal on Image and Video Processing*/
+ GAMMA_SPATIAL_VARIANT_BASED = 5, /*!< Lee, S et al. (2010), "A Space-Variant Luminance Map based
+ Color Image Enhancement",
+ IEEE Trans. on Consumer Electronics, Vol. 56, No. 4, November 2010.*/
+ GAMMA_METHOD_COUNT = 6
+} vpGammaMethod;
+
+/**
+ * \brief Get the list of available vpGammaMethod.
+ *
+ * \param[in] pref The prefix of the list.
+ * \param[in] sep The separator between two elements of the list.
+ * \param[in] suf The suffix of the list.
+ * \return std::string The list of available items.
+ */
+VISP_EXPORT std::string vpGammaMethodList(const std::string &pref = "<", const std::string &sep = " , ",
+ const std::string &suf = ">");
+
+/**
+ * \brief Cast a \b vp::vpGammaMethod into a string, to know its name.
+ *
+ * \param[in] type The type that must be casted into a string.
+ * \return std::string The corresponding name.
+ */
+VISP_EXPORT std::string vpGammaMethodToString(const vpGammaMethod &type);
+
+/**
+ * \brief Cast a string into a \b vp::vpGammaMethod.
+ *
+ * \param[in] name The name of the backend.
+ * \return vp::vpGammaMethod The corresponding enumeration value.
+ */
+VISP_EXPORT vpGammaMethod vpGammaMethodFromString(const std::string &name);
+
+/**
+ * \brief How to handle color images when applying Gamma Correction.
+ */
+typedef enum vpGammaColorHandling
+{
+ GAMMA_RGB = 0, /*!< Gamma correction is apply to Red, Blue and Green channels individually.*/
+ GAMMA_HSV = 1, /*!< The input image is converted into HSV space, Gamma Correction is applied to Value channel and
+ then the image is converted back into RGBa space.*/
+ GAMMA_COLOR_HANDLING_COUNT = 2
+} vpGammaColorHandling;
+
+/**
+ * \brief Get the list of available vpGammaColorHandling.
+ *
+ * \param[in] pref The prefix of the list.
+ * \param[in] sep The separator between two elements of the list.
+ * \param[in] suf The suffix of the list.
+ * \return std::string The list of available items.
+ */
+VISP_EXPORT std::string vpGammaColorHandlingList(const std::string &pref = "<", const std::string &sep = " , ",
+ const std::string &suf = ">");
+
+/**
+ * \brief Cast a \b vp::vpGammaColorHandling into a string, to know its name.
+ *
+ * \param[in] type The type that must be casted into a string.
+ * \return std::string The corresponding name.
+ */
+VISP_EXPORT std::string vpGammaColorHandlingToString(const vpGammaColorHandling &type);
+
+/**
+ * \brief Cast a string into a \b vp::vpGammaColorHandling.
+ *
+ * \param[in] name The name of the backend.
+ * \return vp::vpGammaColorHandling The corresponding enumeration value.
+ */
+VISP_EXPORT vpGammaColorHandling vpGammaColorHandlingFromString(const std::string &name);
+
/*!
* \ingroup group_imgproc_brightness
*
@@ -262,42 +350,71 @@ VISP_EXPORT void equalizeHistogram(const vpImage &I1, vpImage &I
*
* Perform a gamma correction on a grayscale image.
*
- * \param I : The grayscale image to apply gamma correction.
- * \param gamma : Gamma value.
+ * \param[out] I : The grayscale image to apply gamma correction.
+ * \param[in] gamma : Gamma value. If equals to -1, use automatic Gamma correction based on a non-linear
+ * technique. If equals to -2, use automatic Gamma correction based on a logarithmic technique.
+ * If equals to -3, uses automatic Gamma correction based on classification. If equals to -4, uses automatic Gamma
+ * correction based on probalistics.
+ * \param[in] method: The method to use: either \b GAMMA_MANUAL if the user wants to use a positive constant \b gamma
+ * factor, or one of the automatic method if \b gamma is negative.
+ * \param[in] p_mask:if different from nullptr, permits to indicate which points must be taken into account by setting
+ * them to true.
*/
-VISP_EXPORT void gammaCorrection(vpImage &I, double gamma);
+VISP_EXPORT void gammaCorrection(vpImage &I, const float &gamma, const vpGammaMethod &method = vp::GAMMA_MANUAL,
+ const vpImage *p_mask = nullptr);
/*!
* \ingroup group_imgproc_gamma
*
* Perform a gamma correction on a grayscale image.
*
- * \param I1 : The first grayscale image.
- * \param I2 : The second grayscale image after gamma correction.
- * \param gamma : Gamma value.
+ * \param[in] I1 : The first grayscale image.
+ * \param[out] I2 : The second grayscale image after gamma correction.
+ * \param[in] gamma : Gamma value. If equals to -1, use automatic Gamma correction based on a non-linear
+ * technique. If equals to -2, use automatic Gamma correction based on a logarithmic technique.
+ * If equals to -3, uses automatic Gamma correction based on classification. If equals to -4, uses automatic Gamma
+ * correction based on probalistics.
+ * \param[in] method: The method to use: either \b GAMMA_MANUAL if the user wants to use a positive constant \b gamma
+ * factor, or one of the automatic method if \b gamma is negative.
+ * \param[in] p_mask:if different from nullptr, permits to indicate which points must be taken into account by setting
+ * them to true.
*/
-VISP_EXPORT void gammaCorrection(const vpImage &I1, vpImage &I2, double gamma);
+VISP_EXPORT void gammaCorrection(const vpImage &I1, vpImage &I2, const float &gamma,
+ const vpGammaMethod &method = vp::GAMMA_MANUAL, const vpImage *p_mask = nullptr);
/*!
* \ingroup group_imgproc_gamma
*
* Perform a gamma correction on a color image.
*
- * \param I : The color image to apply gamma correction.
- * \param gamma : Gamma value.
+ * \param[out] I : The color image to apply gamma correction.
+ * \param[in] gamma : Gamma value.
+ * \param[in] colorHandling : How to handle the colors of the image.
+ * \param[in] method : The method to use: either \b GAMMA_MANUAL if the user wants to use a positive constant \b gamma factor,
+ * or one of the automatic method if \b gamma is negative.
+ * \param[in] p_mask : if different from nullptr, permits to indicate which points must be taken into account by setting
+ * them to true.
*/
-VISP_EXPORT void gammaCorrection(vpImage &I, double gamma);
+VISP_EXPORT void gammaCorrection(vpImage &I, const float &gamma, const vpGammaColorHandling &colorHandling = vp::GAMMA_RGB,
+ const vpGammaMethod &method = vp::GAMMA_MANUAL, const vpImage *p_mask = nullptr);
/*!
* \ingroup group_imgproc_gamma
*
* Perform a gamma correction on a color image.
*
- * \param I1 : The first color image.
- * \param I2 : The second color image after gamma correction.
- * \param gamma : Gamma value.
+ * \param[in] I1 : The first color image.
+ * \param[out] I2 : The second color image after gamma correction.
+ * \param[in] gamma : Gamma value.
+ * \param[in] colorHandling : How to handle the colors of the image.
+ * \param[in] method: The method to use: either \b GAMMA_MANUAL if the user wants to use a positive constant \b gamma factor,
+ * or one of the automatic method if \b gamma is negative.
+ * \param[in] p_mask:if different from nullptr, permits to indicate which points must be taken into account by setting
+ * them to true.
*/
-VISP_EXPORT void gammaCorrection(const vpImage &I1, vpImage &I2, double gamma);
+VISP_EXPORT void gammaCorrection(const vpImage &I1, vpImage &I2, const float &gamma,
+ const vpGammaColorHandling &colorHandling = vp::GAMMA_RGB,
+ const vpGammaMethod &method = vp::GAMMA_MANUAL, const vpImage *p_mask = nullptr);
/*!
* \ingroup group_imgproc_retinex
diff --git a/modules/imgproc/src/vpImgproc.cpp b/modules/imgproc/src/vpImgproc.cpp
index f3adc5ddc3..8496c6ebd2 100644
--- a/modules/imgproc/src/vpImgproc.cpp
+++ b/modules/imgproc/src/vpImgproc.cpp
@@ -60,11 +60,120 @@
#include
#include
#include
+#include
#include
#include
namespace vp
{
+std::string vpGammaMethodList(const std::string &pref, const std::string &sep, const std::string &suf)
+{
+ std::string list(pref);
+ for (unsigned int i = 0; i < (GAMMA_METHOD_COUNT - 1); ++i) {
+ vpGammaMethod type = static_cast(i);
+ list += vpGammaMethodToString(type);
+ list += sep;
+ }
+ vpGammaMethod type = static_cast(GAMMA_METHOD_COUNT - 1);
+ list += vpGammaMethodToString(type);
+ list += suf;
+ return list;
+}
+
+std::string vpGammaMethodToString(const vpGammaMethod &type)
+{
+ std::string name;
+ switch (type) {
+ case GAMMA_MANUAL:
+ name = "gamma_manual";
+ break;
+ case GAMMA_LOG_BASED:
+ name = "gamma_log";
+ break;
+ case GAMMA_NONLINEAR_BASED:
+ name = "gamma_nonlinear";
+ break;
+ case GAMMA_CDF_BASED:
+ name = "gamma_cdf";
+ break;
+ case GAMMA_CLASSIFICATION_BASED:
+ name = "gamma_classification";
+ break;
+ case GAMMA_SPATIAL_VARIANT_BASED:
+ name = "gamma_spatial_variant";
+ break;
+ case GAMMA_METHOD_COUNT:
+ default:
+ name = "gamma_method_unknown";
+ }
+ return name;
+}
+
+vpGammaMethod vpGammaMethodFromString(const std::string &name)
+{
+ vpGammaMethod type(GAMMA_METHOD_COUNT);
+ unsigned int count = static_cast(GAMMA_METHOD_COUNT);
+ bool notFound = true;
+ unsigned int i = 0;
+ while ((i < count) && notFound) {
+ vpGammaMethod temp = static_cast(i);
+ if (name == vpGammaMethodToString(temp)) {
+ type = temp;
+ notFound = false;
+ }
+ ++i;
+ }
+ return type;
+}
+
+std::string vpGammaColorHandlingList(const std::string &pref, const std::string &sep, const std::string &suf)
+{
+ std::string list(pref);
+ for (unsigned int i = 0; i < (GAMMA_COLOR_HANDLING_COUNT - 1); ++i) {
+ vpGammaColorHandling type = static_cast(i);
+ list += vpGammaColorHandlingToString(type);
+ list += sep;
+ }
+ vpGammaColorHandling type = static_cast(GAMMA_COLOR_HANDLING_COUNT - 1);
+ list += vpGammaColorHandlingToString(type);
+ list += suf;
+ return list;
+}
+
+std::string vpGammaColorHandlingToString(const vpGammaColorHandling &type)
+{
+ std::string name;
+ switch (type) {
+ case GAMMA_RGB:
+ name = "gamma_color_rgb";
+ break;
+ case GAMMA_HSV:
+ name = "gamma_color_hsv";
+ break;
+ case GAMMA_COLOR_HANDLING_COUNT:
+ default:
+ name = "gamma_color_unknown";
+ }
+ return name;
+}
+
+vpGammaColorHandling vpGammaColorHandlingFromString(const std::string &name)
+{
+ vpGammaColorHandling type(GAMMA_COLOR_HANDLING_COUNT);
+ unsigned int count = static_cast(GAMMA_COLOR_HANDLING_COUNT);
+ bool notFound = true;
+ unsigned int i = 0;
+ while ((i < count) && notFound) {
+ vpGammaColorHandling temp = static_cast(i);
+ if (name == vpGammaColorHandlingToString(temp)) {
+ type = temp;
+ notFound = false;
+ }
+ ++i;
+ }
+ return type;
+}
+
void adjust(vpImage &I, double alpha, double beta)
{
// Construct the look-up table
@@ -193,57 +302,403 @@ void equalizeHistogram(const vpImage &I1, vpImage &I2, bool useH
vp::equalizeHistogram(I2, useHSV);
}
-void gammaCorrection(vpImage &I, double gamma)
+namespace
{
- double inverse_gamma = 1.0;
- if (gamma > 0) {
- inverse_gamma = 1.0 / gamma;
- }
- else {
- throw vpException(vpException::badValue, "The gamma value must be positive !");
- }
+/**
+ * \brief This method is an implementation of the article "Towards Real-time Hardware Gamma Correction
+ * for Dynamic Contrast Enhancement" by Jesse Scott, Michael Pusateri, IEEE Applied Imagery Pattern Recognition
+ * Workshop (AIPR 2009), 2009
+ *
+ * The gamma factor depends on the mean of the original image and its intensity range.
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionLogMethod(vpImage &I, const vpImage *p_mask)
+{
+ float mean = static_cast(I.getMeanValue(p_mask));
+ unsigned char inputMin = 0, inputMax = 0;
+ I.getMinMaxValue(inputMin, inputMax);
+ unsigned char inputRange = inputMax - inputMin;
+
+ float gamma_computed = (std::log(128.f) - std::log(256.f)) / (std::log(mean) - std::log(inputRange));
+ float inverse_gamma = 1.f / gamma_computed;
// Construct the look-up table
unsigned char lut[256];
+ float inputRangeAsFloat = static_cast(inputRange);
+ for (unsigned int i = inputMin; i <= inputMax; i++) {
+ lut[i] = vpMath::saturate(std::pow(static_cast(i - inputMin) / inputRangeAsFloat, inverse_gamma) * 255.f);
+ }
+
+ I.performLut(lut);
+}
+
+/**
+ * \brief This method is an implementation of the article "REDUCING ILLUMINATION BASED ON NONLINEAR GAMMA CORRECTION"
+ * by Yihua Shi, Jinfeng Yang, Renbiao Wu, International Conference on Image Processing ยท September 2007
+ *
+ * The gamma factor is the result of the sum of non-linear functions whose values depend on
+ * the pixel intensity.
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionNonLinearMethod(vpImage &I, const vpImage *p_mask)
+{
+ (void)p_mask;
+ const float a = 0.2f;
+ const float b = 0.3f;
+ const float c = 0.3f;
+ const float x_m = 127.5f;
+ const float alpha = std::atan2(-b, x_m);
+ const float rho = 0.1f;
+ unsigned char lut[256];
for (unsigned int i = 0; i < 256; i++) {
- lut[i] = vpMath::saturate(pow((double)i / 255.0, inverse_gamma) * 255.0);
+ float x = static_cast(i);
+ float phi = M_PIf * x / (2.f * x_m);
+ float f1 = a * std::cos(phi);
+ float k = rho * std::sin(4 * M_PIf * x / 255.f);
+ float f2 = (k + b)*std::cos(alpha) + x * std::sin(alpha);
+ float r = c * std::abs(x / x_m - 1.f);
+ float f3 = r * std::cos(3.f * M_PIf * x / 255.f);
+ float g = f1 + f2 + f3;
+ float gamma = 1 + g;
+ float inverse_gamma = 1.f / gamma;
+ lut[i] = vpMath::saturate(std::pow(static_cast(i) / 255.f, inverse_gamma) * 255.f);
}
+ I.performLut(lut);
+}
+/**
+ * \brief This method is an implementation of the article "An adaptive gamma correction for image
+ * enhancement", Shanto Rahman, Md Mostafijur Rahman, M. Abdullah-Al-Wadud, Golam Dastegir Al-Quaderi and
+ * Mohammad Shoyaib, EURASIP Journal on Image and Video Processing (2016)
+ *
+ * The gamma factor depends of the contrast of the image. The constant depends on the brightness of
+ * the image.
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionClassificationBasedMethod(vpImage &I, const vpImage *p_mask)
+{
+ unsigned int nbValidPoints = 0;
+ double mean = I.getMeanValue(p_mask, nbValidPoints);
+ double stdev = I.getStdev(mean, nbValidPoints, p_mask);
+ double meanNormalized = mean / 255.;
+ double stdevNormalized = stdev / 255.;
+ const float tau = 3.f;
+ bool isAlreadyHighContrast = (4. * stdevNormalized) > (1./tau);
+ unsigned char lut[256];
+ float gamma = 0.f;
+ if (isAlreadyHighContrast) {
+ // Case medium to high contrast image
+ gamma = std::exp((1.f - (meanNormalized + stdevNormalized))/2.f);
+ }
+ else {
+ // Case low contrast image
+#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
+ gamma = -std::log2(stdevNormalized);
+#else
+ gamma = -std::log(stdevNormalized) / std::log(2);
+#endif
+ }
+ if (meanNormalized < 0.5) {
+ // Case dark image
+ float meanPowerGamma = std::pow(meanNormalized, gamma);
+ for (unsigned int i = 0; i <= 255; i++) {
+ float iNormalized = static_cast(i)/255.f;
+ float iPowerGamma = std::pow(iNormalized, gamma);
+ lut[i] = vpMath::saturate(255.f * (iPowerGamma / (iPowerGamma + (1.f - iPowerGamma) * meanPowerGamma)));
+ }
+ }
+ else {
+ // Case bright image
+ for (unsigned int i = 0; i <= 255; i++) {
+ float iNormalized = static_cast(i)/255.f;
+ lut[i] = vpMath::saturate(std::pow(iNormalized, gamma) * 255.f);
+ }
+ }
I.performLut(lut);
}
-void gammaCorrection(const vpImage &I1, vpImage &I2, double gamma)
+/**
+ * \brief This technique comes from the article "Efficient Contrast Enhancement Using Adaptive
+ * Gamma Correction With Weighting Distribution" by Shih-Chia Huang, Fan-Chieh Cheng, and Yi-Sheng Chiu,
+ * IEEE TRANSACTIONS ON IMAGE PROCESSING, VOL. 22, NO. 3, MARCH 2013.
+ *
+ * It works well on globally dark images that must be brightened, but overcompensate
+ * images that are already "bright enough".
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionProbabilisticBased(vpImage &I, const vpImage *p_mask)
{
- I2 = I1;
- vp::gammaCorrection(I2, gamma);
+ const unsigned int nbBins = 256;
+ vpHistogram histo;
+ histo.setMask(p_mask);
+ histo.calculate(I, nbBins);
+ unsigned int totalNbPoints = histo.getTotal();
+ unsigned int minHisto = histo[0];
+ unsigned int maxHisto = histo[0];
+ for (unsigned int i = 1; i < nbBins; ++i) {
+ minHisto = std::min(minHisto, histo[i]);
+ maxHisto = std::max(maxHisto, histo[i]);
+ }
+ float pdfMin = static_cast(minHisto) / static_cast(totalNbPoints);
+ float pdfMax = static_cast(maxHisto) / static_cast(totalNbPoints);
+ float pdf_w[nbBins];
+ float sum_pdf_w = 0.f;
+ for (unsigned int i = 0; i < nbBins; ++i) {
+ float pdf = static_cast(histo[i])/static_cast(totalNbPoints);
+ pdf_w[i] = pdfMax * std::sqrt((pdf - pdfMin)/(pdfMax - pdfMin)); // alpha = 0.5
+ sum_pdf_w += pdf_w[i];
+ }
+ unsigned char lut[256];
+ float cdf_w = 0;
+ for (unsigned int i = 0; i <= 255; i++) {
+ cdf_w += pdf_w[i] / sum_pdf_w;
+ float gamma = 1.f - cdf_w;
+ float iNormalized = static_cast(i)/255.f;
+ lut[i] = vpMath::saturate(std::pow(iNormalized, gamma) * 255.f);
+ }
+ I.performLut(lut);
+}
+
+/**
+ * \brief This technique comes from the article "A Space-Variant Luminance Map based Color Image Enhancement" by Sungmok
+ * Lee, Homin Kwon, Hagyong Han, Gidong Lee, and Bongsoon Kang,
+ * IEEE Transactions on Consumer Electronics, Vol. 56, No. 4, November 2010.
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionSpatialBased(vpImage &I, const vpImage *p_mask)
+{
+ unsigned int width = I.getWidth(), height = I.getHeight();
+ vpImage I_2, I_4, I_8;
+ I.subsample(2, 2, I_2);
+ I.subsample(4, 4, I_4);
+ I.subsample(8, 8, I_8);
+ vpImage I_blur, I_2_blur, I_4_blur, I_8_blur;
+ const bool normalize = true;
+ vpImageFilter::gaussianBlur(I, I_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_2, I_2_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_4, I_4_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_8, I_8_blur, 3, 0.f, normalize, p_mask);
+ vpImage L, L_2, L_4, L_8;
+ vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ const float alpha = 0.5f;
+ unsigned int size = height * width;
+ float stdev = I.getStdev(p_mask);
+ float p;
+ if (stdev <= 40) {
+ p = 2.f;
+ }
+ else if (stdev <= 80) {
+ p = -0.025f * stdev + 3.f;
+ }
+ else {
+ p = 1.f;
+ }
+
+ for (unsigned int i = 0; i < size; ++i) {
+ bool hasToCompute = true;
+ if (p_mask != nullptr) {
+ hasToCompute = p_mask->bitmap[i];
+ }
+ if (hasToCompute) {
+ 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
+ float gamma = std::pow(alpha, (128.f - svlm)/128.f);
+ float iNormalized = static_cast(I.bitmap[i])/255.f;
+ float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
+ float r = svlm / o;
+ float e = std::pow(r, p);
+ float s = 255.f * std::pow(o / 255.f, e);
+ I.bitmap[i] = vpMath::saturate((s * static_cast(I.bitmap[i])) / o);
+ }
+ }
+}
+
+/**
+ * \brief This technique comes from the article "A Space-Variant Luminance Map based Color Image Enhancement" by Sungmok
+ * Lee, Homin Kwon, Hagyong Han, Gidong Lee, and Bongsoon Kang,
+ * IEEE Transactions on Consumer Electronics, Vol. 56, No. 4, November 2010.
+ *
+ * \param[out] I The image on which gamma correction must be applied.
+ * \param[in] p_mask Boolean that indicates which points must be taken into account (true value)
+ * or must be ignored (false value).
+ */
+void gammaCorrectionSpatialBased(vpImage &I, const vpImage *p_mask)
+{
+ unsigned int width = I.getWidth(), height = I.getHeight();
+ unsigned int size = height * width;
+ vpImage I_gray(height, width);
+ for (unsigned int i = 0; i < size; ++i) {
+ vpRGBa rgb = I.bitmap[i];
+ I_gray.bitmap[i] = 0.299 * rgb.R + 0.587 * rgb.G + 0.114 *rgb.B;
+ }
+ vpImage I_2, I_4, I_8;
+ I_gray.subsample(2, 2, I_2);
+ I_gray.subsample(4, 4, I_4);
+ I_gray.subsample(8, 8, I_8);
+ vpImage I_blur, I_2_blur, I_4_blur, I_8_blur;
+ const bool normalize = true;
+ vpImageFilter::gaussianBlur(I_gray, I_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_2, I_2_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_4, I_4_blur, 3, 0.f, normalize, p_mask);
+ vpImageFilter::gaussianBlur(I_8, I_8_blur, 3, 0.f, normalize, p_mask);
+ vpImage L, L_2, L_4, L_8;
+ vpImageTools::resize(I_blur, L, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_2_blur, L_2, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_4_blur, L_4, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ vpImageTools::resize(I_8_blur, L_8, width, height, vpImageTools::INTERPOLATION_CUBIC);
+ const float alpha = 0.5f;
+
+ float stdev = I.getStdev(p_mask);
+ float p;
+ if (stdev <= 40) {
+ p = 2.f;
+ }
+ else if (stdev <= 80) {
+ p = -0.025f * stdev + 3.f;
+ }
+ else {
+ p = 1.f;
+ }
+ for (unsigned int i = 0; i < size; ++i) {
+ bool hasToCompute = true;
+ if (p_mask != nullptr) {
+ hasToCompute = p_mask->bitmap[i];
+ }
+ if (hasToCompute) {
+ 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
+ float gamma = std::pow(alpha, (128.f - svlm)/128.f);
+ float iNormalized = static_cast(I_gray.bitmap[i])/255.f;
+ float o = std::pow(iNormalized, gamma) * 255.f; // Computation of the luminance
+ float r = svlm / o;
+ float e = std::pow(r, p);
+ float s = 255.f * std::pow(o / 255.f, e);
+ I.bitmap[i].R = vpMath::saturate((s * static_cast(I.bitmap[i].R)) / o);
+ I.bitmap[i].G = vpMath::saturate((s * static_cast(I.bitmap[i].G)) / o);
+ I.bitmap[i].B = vpMath::saturate((s * static_cast(I.bitmap[i].B)) / o);
+ }
+ }
+}
}
-void gammaCorrection(vpImage &I, double gamma)
+void gammaCorrection(vpImage &I, const float &gamma, const vpGammaMethod &method, const vpImage *p_mask)
{
- double inverse_gamma = 1.0;
- if (gamma > 0) {
+ float inverse_gamma = 1.0;
+ if ((gamma > 0) && (method == GAMMA_MANUAL)) {
inverse_gamma = 1.0 / gamma;
+ // Construct the look-up table
+ unsigned char lut[256];
+ for (unsigned int i = 0; i < 256; i++) {
+ lut[i] = vpMath::saturate(std::pow(static_cast(i) / 255.0, inverse_gamma) * 255.0);
+ }
+
+ I.performLut(lut);
+ }
+ else if (method == GAMMA_MANUAL) {
+ std::stringstream errMsg;
+ errMsg << "ERROR: gamma correction factor (";
+ errMsg << gamma << ") cannot be negative when using a constant user-defined factor." << std::endl;
+ throw(vpException(vpException::badValue, errMsg.str()));
+ }
+ else if (gamma > 0) {
+ std::stringstream errMsg;
+ errMsg << "ERROR: asking for automatic gamma correction but setting a user-defined factor (" << gamma << ")." << std::endl;
+ throw(vpException(vpException::badValue, errMsg.str()));
}
else {
- throw vpException(vpException::badValue, "The gamma value must be positive !");
+ if (method == GAMMA_NONLINEAR_BASED) {
+ gammaCorrectionNonLinearMethod(I, p_mask);
+ }
+ else if (method == GAMMA_LOG_BASED) {
+ gammaCorrectionLogMethod(I, p_mask);
+ }
+ else if (method == GAMMA_CLASSIFICATION_BASED) {
+ gammaCorrectionClassificationBasedMethod(I, p_mask);
+ }
+ else if (method == GAMMA_CDF_BASED) {
+ gammaCorrectionProbabilisticBased(I, p_mask);
+ }
+ else if (method == GAMMA_SPATIAL_VARIANT_BASED) {
+ gammaCorrectionSpatialBased(I, p_mask);
+ }
+ else {
+ std::stringstream errMsg;
+ errMsg << "Gamma automatic method \"" << vpGammaMethodToString(method) << "\" is not handled." << std::endl;
+ throw(vpException(vpException::badValue, errMsg.str()));
+ }
}
+}
- // Construct the look-up table
- vpRGBa lut[256];
- for (unsigned int i = 0; i < 256; i++) {
- lut[i].R = vpMath::saturate(pow((double)i / 255.0, inverse_gamma) * 255.0);
- lut[i].G = vpMath::saturate(pow((double)i / 255.0, inverse_gamma) * 255.0);
- lut[i].B = vpMath::saturate(pow((double)i / 255.0, inverse_gamma) * 255.0);
- lut[i].A = vpMath::saturate(pow((double)i / 255.0, inverse_gamma) * 255.0);
+void gammaCorrection(const vpImage &I1, vpImage &I2, const float &gamma,
+ const vpGammaMethod &method, const vpImage *p_mask)
+{
+ I2 = I1;
+ vp::gammaCorrection(I2, gamma, method, p_mask);
+}
+
+void gammaCorrection(vpImage &I, const float &gamma, const vpGammaColorHandling &colorHandling,
+ const vpGammaMethod &method, const vpImage *p_mask)
+{
+ if ((method == GAMMA_SPATIAL_VARIANT_BASED)) {
+ gammaCorrectionSpatialBased(I, p_mask);
}
+ else {
+ if (colorHandling == GAMMA_HSV) {
+ const unsigned int height = I.getHeight(), width = I.getWidth();
+ unsigned int size = height * width;
+ std::vector hue(size);
+ std::vector saturation(size);
+ std::vector value(size);
- I.performLut(lut);
+ vpImageConvert::RGBaToHSV((unsigned char *)I.bitmap, &hue.front(), &saturation.front(), &value.front(), size);
+ vpImage I_hue(&hue.front(), height, width);
+ vpImage I_saturation(&saturation.front(), height, width);
+ vpImage I_value(&value.front(), height, width);
+
+ gammaCorrection(I_value, gamma, method, p_mask);
+
+ vpImageConvert::HSVToRGBa(I_hue.bitmap, I_saturation.bitmap, I_value.bitmap, (unsigned char *)I.bitmap, size);
+ }
+ else if (colorHandling == GAMMA_RGB) {
+ vpImage pR, pG, pB, pa;
+ vpImageConvert::split(I, &pR, &pG, &pB, &pa);
+ gammaCorrection(pR, gamma, method, p_mask);
+ gammaCorrection(pG, gamma, method, p_mask);
+ gammaCorrection(pB, gamma, method, p_mask);
+ gammaCorrection(pa, gamma, method, p_mask);
+ vpImageConvert::merge(&pR, &pG, &pB, &pa, I);
+ }
+ else {
+ std::stringstream errMsg;
+ errMsg << "Gamma color handling mode \"" << vpGammaColorHandlingToString(colorHandling);
+ errMsg << "\" is not handled." << std::endl;
+ throw(vpException(vpException::badValue, errMsg.str()));
+ }
+ }
}
-void gammaCorrection(const vpImage &I1, vpImage &I2, double gamma)
+void gammaCorrection(const vpImage &I1, vpImage &I2, const float &gamma,
+ const vpGammaColorHandling &colorHandling, const vpGammaMethod &method,
+ const vpImage *p_mask)
{
I2 = I1;
- vp::gammaCorrection(I2, gamma);
+ vp::gammaCorrection(I2, gamma, colorHandling, method, p_mask);
}
void stretchContrast(vpImage &I)
diff --git a/modules/java/misc/imgproc/gen_dict.json b/modules/java/misc/imgproc/gen_dict.json
index c7a00a2fc2..e2f14d5906 100644
--- a/modules/java/misc/imgproc/gen_dict.json
+++ b/modules/java/misc/imgproc/gen_dict.json
@@ -124,6 +124,188 @@
" return;",
"}\n"
]
+ },
+ "gammaCorrection" : {
+ "j_code" : [
+ "//",
+ "// C++: static void fillHoles(vpImage_char I1, vpImage_char I2, float gamma vp_vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool * I_mask = 0)",
+ "//",
+ "\n",
+ "//javadoc: Vp::gammaCorrection(I1, I2, gamma)",
+ "\n",
+ "public static void gammaCorrection(VpImageUChar I1, VpImageUChar I2, float gamma)",
+ "{",
+ " gammaCorrection(I1.nativeObj, I2.nativeObj, gamma);",
+ " return;",
+ "}\n",
+ "//",
+ "// C++: static void fillHoles(vpImage_char I, float gamma vp_vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool * I_mask = 0)",
+ "//",
+ "\n",
+ "//javadoc: Vp::gammaCorrection(I1, gamma)",
+ "\n",
+ "public static void gammaCorrection(VpImageUChar I, float gamma)",
+ "{",
+ " gammaCorrection(I.nativeObj, gamma);",
+ " return;",
+ "}",
+ "\n"
+ ],
+ "jn_code" : [
+ "// C++: static void gammaCorrection(vpImage_char I1, vpImage_char I2, float gamma, vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool* p_mask = 0)",
+ "private static native void gammaCorrection(long I1_nativeobj, long I2_nativeobj, float gamma);\n",
+ "// C++: static void gammaCorrection(vpImage_char I, float gamma, vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool* p_mask = 0)",
+ "private static native void gammaCorrection(long I_nativeobj, float gamma);\n"
+ ],
+ "cpp_code" : [
+ "//",
+ "// manual port",
+ "// C++: static void gammaCorrection(vpImage_char I1, vpImage_char I2, float gamma, vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool* p_mask = 0)",
+ "//",
+ "//javadoc: Vp::gammaCorrection(I1, I2, gamma)",
+ "JNIEXPORT void JNICALL Java_org_visp_imgproc_VpImgproc_gammaCorrection_10 (JNIEnv*, jclass, jlong, jlong, jfloat);",
+ "JNIEXPORT void JNICALL Java_org_visp_imgproc_VpImgproc_gammaCorrection_10 (JNIEnv* env, jclass , jlong I1_nativeObj, jlong I2_nativeObj, jfloat gamma)",
+ "{",
+ " static const char method_name[] = \"imgproc::gammaCorrection_10()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " vpImage& I1 = *((vpImage*)I1_nativeObj);",
+ " vpImage& I2 = *((vpImage*)I2_nativeObj);",
+ " vp::gammaCorrection( I1, I2, (float)gamma, vp::GAMMA_MANUAL, 0 );",
+ " return;",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return;",
+ "}\n",
+ "//",
+ "// manual port",
+ "// C++: static void gammaCorrection(vpImage_char I, float gamma, vpGammaMethod method = vp::GAMMA_MANUAL, vpImage_bool* p_mask = 0)",
+ "//",
+ "//javadoc: Vp::gammaCorrection(I, gamma)\n",
+ "JNIEXPORT void JNICALL Java_org_visp_imgproc_VpImgproc_gammaCorrection_13 (JNIEnv*, jclass, jlong, jfloat);\n",
+ "JNIEXPORT void JNICALL Java_org_visp_imgproc_VpImgproc_gammaCorrection_13 (JNIEnv* env, jclass , jlong I_nativeObj, jfloat gamma)",
+ "{",
+ " static const char method_name[] = \"imgproc::gammaCorrection_13()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " vpImage& I = *((vpImage*)I_nativeObj);",
+ " vp::gammaCorrection( I, (float)gamma, vp::GAMMA_MANUAL, 0 );",
+ " return;",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return;",
+ "}\n\n"
+ ]
+ },
+ "vpGammaColorHandlingList" : {
+ "j_code" : [
+ "\n//",
+ "// manual port",
+ "// C++: std::string vpGammaColorHandlingList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n",
+ "//javadoc: Vp::vpGammaColorHandlingList(pref, sep, suf)\n",
+ "public static String vpGammaColorHandlingList(String pref, String sep, String suf)",
+ "{",
+ " return vpGammaColorHandlingList(pref, sep, suf);",
+ "}"
+ ],
+ "jn_code" : [
+ "\n// C++: std::string vpGammaColorHandlingList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n"
+ ],
+ "cpp_code" : [
+ "//",
+ "// manual port",
+ "// C++: std::string vpGammaColorHandlingList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n",
+ "//",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaColorHandlingList_10 (JNIEnv*, jclass, jstring);",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaColorHandlingList_10 (JNIEnv* env, jclass , jstring pref)",
+ "{",
+ " static const char method_name[] = \"imgproc::vpGammaColorHandlingList_10()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " const char* utf_pref = env->GetStringUTFChars(pref, 0); string n_pref( utf_pref ? utf_pref : \"\" ); env->ReleaseStringUTFChars(pref, utf_pref);",
+ " string _retval_ = vp::vpGammaColorHandlingList( n_pref );",
+ " return env->NewStringUTF(_retval_.c_str());",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return 0;",
+ "}\n",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaColorHandlingList_11 (JNIEnv*, jclass);",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaColorHandlingList_11 (JNIEnv* env, jclass )",
+ "{",
+ " static const char method_name[] = \"imgproc::vpGammaColorHandlingList_11()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " string _retval_ = vp::vpGammaColorHandlingList( );",
+ " return env->NewStringUTF(_retval_.c_str());",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return 0;",
+ "}\n"
+ ]
+ },
+ "vpGammaMethodList" : {
+ "j_code" : [
+ "\n//",
+ "// manual port",
+ "// C++: std::string vpGammaMethodList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n",
+ "//javadoc: Vp::vpGammaMethodList(pref, sep, suf)\n",
+ "public static String vpGammaMethodList(String pref, String sep, String suf)",
+ "{",
+ " return vpGammaMethodList(pref, sep, suf);",
+ "}"
+ ],
+ "jn_code" : [
+ "\n// C++: std::string vpGammaMethodList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n"
+ ],
+ "cpp_code" : [
+ "//",
+ "// manual port",
+ "// C++: std::string vpGammaMethodList(const std::string &pref = \"<\", const std::string &sep = \" , \", const std::string &suf = \">\")\n",
+ "//",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaMethodList_10 (JNIEnv*, jclass, jstring);",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaMethodList_10 (JNIEnv* env, jclass , jstring pref)",
+ "{",
+ " static const char method_name[] = \"imgproc::vpGammaMethodList_10()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " const char* utf_pref = env->GetStringUTFChars(pref, 0); string n_pref( utf_pref ? utf_pref : \"\" ); env->ReleaseStringUTFChars(pref, utf_pref);",
+ " string _retval_ = vp::vpGammaMethodList( n_pref );",
+ " return env->NewStringUTF(_retval_.c_str());",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return 0;",
+ "}\n",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaMethodList_11 (JNIEnv*, jclass);",
+ "JNIEXPORT jstring JNICALL Java_org_visp_imgproc_VpImgproc_vpGammaMethodList_11 (JNIEnv* env, jclass )",
+ "{",
+ " static const char method_name[] = \"imgproc::vpGammaMethodList_11()\";",
+ " try {",
+ " LOGD(\"%s\", method_name);",
+ " string _retval_ = vp::vpGammaMethodList( );",
+ " return env->NewStringUTF(_retval_.c_str());",
+ " } catch(const std::exception &e) {",
+ " throwJavaException(env, &e, method_name);",
+ " } catch (...) {",
+ " throwJavaException(env, 0, method_name);",
+ " }",
+ " return 0;",
+ "}\n"
+ ]
}
}
},
diff --git a/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp b/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp
index d62440741d..90ff929689 100644
--- a/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp
+++ b/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp
@@ -25,54 +25,82 @@ int main(int argc, const char **argv)
std::string input_filename = "Sample_low_brightness.png";
double alpha = 10.0, beta = 50.0;
double gamma = 3.5;
+ vp::vpGammaMethod method = vp::GAMMA_MANUAL;
+ vp::vpGammaColorHandling colorHandling = vp::GAMMA_HSV;
int scale = 240, scaleDiv = 3, level = 0, kernelSize = -1;
double dynamic = 3.0;
for (int i = 1; i < argc; i++) {
if (std::string(argv[i]) == "--input" && i + 1 < argc) {
input_filename = std::string(argv[i + 1]);
- } else if (std::string(argv[i]) == "--alpha" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--alpha" && i + 1 < argc) {
alpha = atof(argv[i + 1]);
- } else if (std::string(argv[i]) == "--beta" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--beta" && i + 1 < argc) {
beta = atof(argv[i + 1]);
- } else if (std::string(argv[i]) == "--gamma" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--gamma" && i + 1 < argc) {
gamma = atof(argv[i + 1]);
- } else if (std::string(argv[i]) == "--scale" && i + 1 < argc) {
+ }
+ else if ((std::string(argv[i]) == "--gamma-color-handling") && ((i + 1) < argc)) {
+ ++i;
+ colorHandling = vp::vpGammaColorHandlingFromString(argv[i]);
+ }
+ else if ((std::string(argv[i]) == "--gamma-method") && ((i + 1) < argc)) {
+ ++i;
+ method = vp::vpGammaMethodFromString(argv[i]);
+ }
+ else if (std::string(argv[i]) == "--scale" && i + 1 < argc) {
scale = atoi(argv[i + 1]);
- } else if (std::string(argv[i]) == "--scaleDiv" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--scaleDiv" && i + 1 < argc) {
scaleDiv = atoi(argv[i + 1]);
- } else if (std::string(argv[i]) == "--level" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--level" && i + 1 < argc) {
level = atoi(argv[i + 1]);
- } else if (std::string(argv[i]) == "--kernelSize" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--kernelSize" && i + 1 < argc) {
kernelSize = atoi(argv[i + 1]);
- } else if (std::string(argv[i]) == "--dynamic" && i + 1 < argc) {
+ }
+ else if (std::string(argv[i]) == "--dynamic" && i + 1 < argc) {
dynamic = atof(argv[i + 1]);
- } else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
+ }
+ else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") {
std::cout << "Usage: " << argv[0]
- << " [--input ]"
- " [--alpha ] [--beta ]"
- " [--gamma ]"
- " [--scale [--scaleDiv for "
- "vp::retinex()]"
- " [--level [--kernelSize "
- "]"
- " [--dynamic ] [--help]"
- << std::endl;
+ << " [--input ]"
+ " [--alpha ] [--beta ]"
+ " [--gamma ]"
+ " [--gamma-color-handling " << vp::vpGammaColorHandlingList() << "]"
+ " [--gamma-method " << vp::vpGammaMethodList() << "]"
+ " [--scale [--scaleDiv for "
+ "vp::retinex()]"
+ " [--level [--kernelSize "
+ "]"
+ " [--dynamic ] [--help]"
+ << std::endl;
return EXIT_SUCCESS;
}
}
vpImage I_color;
vpImageIo::read(I_color, input_filename);
+ vpImage I_gray;
+ vpImageConvert::convert(I_color, I_gray);
vpImage I_color_res(I_color.getHeight(), 2 * I_color.getWidth());
I_color_res.insert(I_color, vpImagePoint());
+ vpImage I_gray_res(I_gray.getHeight(), 2 * I_gray.getWidth());
+ I_gray_res.insert(I_gray, vpImagePoint());
#ifdef VISP_HAVE_X11
+ vpDisplayX d_gray(I_gray_res);
vpDisplayX d(I_color_res);
#elif defined(VISP_HAVE_GDI)
+ vpDisplayGDI d_gray(I_gray_res);
vpDisplayGDI d(I_color_res);
#elif defined(HAVE_OPENCV_HIGHGUI)
+ vpDisplayOpenCV d_gray(I_gray_res);
vpDisplayOpenCV d(I_color_res);
#endif
@@ -91,9 +119,30 @@ int main(int argc, const char **argv)
vpDisplay::getClick(I_color_res);
//! [Gamma correction]
+ if (method != vp::GAMMA_MANUAL) {
+ // If the user wants to use an automatic method, the gamma factor must be negative.
+ gamma = -1.;
+ }
+
+ if (gamma > 0.) {
+ // If the user wants to set a constant user-defined gamma factor, the method must be set to manual.
+ method = vp::GAMMA_MANUAL;
+ }
+ vpImage I_gray_gamma_correction;
+ vp::gammaCorrection(I_gray, I_gray_gamma_correction, gamma, method);
vpImage I_color_gamma_correction;
- vp::gammaCorrection(I_color, I_color_gamma_correction, gamma);
+ vp::gammaCorrection(I_color, I_color_gamma_correction, gamma, colorHandling, method);
//! [Gamma correction]
+ I_gray_res.insert(I_gray_gamma_correction, vpImagePoint(0, I_gray.getWidth()));
+ ss.str("");
+ ss << "Sample_low_brightness_gray.png";
+ vpImageIo::write(I_gray_res, ss.str());
+
+ vpDisplay::display(I_gray_res);
+ vpDisplay::displayText(I_gray_res, 20, 20, "Gamma correction on gray image. Click to continue.", vpColor::red);
+ vpDisplay::flush(I_gray_res);
+ vpDisplay::getClick(I_gray_res);
+
I_color_res.insert(I_color_gamma_correction, vpImagePoint(0, I_color.getWidth()));
ss.str("");
ss << "Sample_low_brightness_gamma=" << gamma << ".png";
@@ -126,7 +175,7 @@ int main(int argc, const char **argv)
ss.str("");
ss << "Sample_low_brightness_scale=" << scale << "_scaleDiv=" << scaleDiv << "_level=" << level
- << "_dynamic=" << dynamic << "_kernelSize=" << kernelSize << ".png";
+ << "_dynamic=" << dynamic << "_kernelSize=" << kernelSize << ".png";
vpImageIo::write(I_color_res, ss.str());
vpDisplay::display(I_color_res);