diff --git a/modules/imgproc/src/vpImgproc.cpp b/modules/imgproc/src/vpImgproc.cpp index f6f092d0eb..8e74f2ca23 100644 --- a/modules/imgproc/src/vpImgproc.cpp +++ b/modules/imgproc/src/vpImgproc.cpp @@ -329,13 +329,12 @@ void gammaCorrectionLogMethod(vpImage &I, const vpImage *p_ unsigned char inputRange = inputMax - inputMin; float gamma_computed = static_cast((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); + lut[i] = vpMath::saturate(std::pow(static_cast(i - inputMin) / inputRangeAsFloat, gamma_computed) * 255.f); } I.performLut(lut); diff --git a/tutorial/imgproc/brightness/CMakeLists.txt b/tutorial/imgproc/brightness/CMakeLists.txt index 625ad96907..283980c0f0 100644 --- a/tutorial/imgproc/brightness/CMakeLists.txt +++ b/tutorial/imgproc/brightness/CMakeLists.txt @@ -6,7 +6,9 @@ find_package(VISP REQUIRED visp_core visp_io visp_gui visp_imgproc) # set the list of source files set(tutorial_cpp - tutorial-brightness-adjustment.cpp) + tutorial-brightness-adjustment.cpp + tutorial-compare-auto-gamma.cpp +) list(APPEND tutorial_data "${CMAKE_CURRENT_SOURCE_DIR}/Sample_low_brightness.png") diff --git a/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp b/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp index f9e63c37c6..1645cef9ec 100644 --- a/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp +++ b/tutorial/imgproc/brightness/tutorial-brightness-adjustment.cpp @@ -4,9 +4,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #if defined(VISP_HAVE_MODULE_IMGPROC) @@ -15,17 +14,49 @@ //! [Include] #endif +#if ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) +#include +#endif + +#ifdef ENABLE_VISP_NAMESPACE +using namespace VISP_NAMESPACE_NAME; +#endif + +namespace +{ +void display(vpImage &I_display, vpImage &I_color_res, const vpImage &I_color_adjust, + vpImage &I_gray_res, vpImage &I_gray_adjust, vpImage &I_gray_display, + const std::string &title, const std::string &filename_color, const std::string &filename_gray, + const std::string &title_2 = "") +{ + I_color_res.insert(I_color_adjust, vpImagePoint(0, I_color_adjust.getWidth())); + I_display.insert(I_color_adjust, vpImagePoint(0, I_color_adjust.getWidth())); + + I_gray_res.insert(I_gray_adjust, vpImagePoint(0, I_gray_adjust.getWidth())); + vpImageConvert::convert(I_gray_adjust, I_gray_display); + I_display.insert(I_gray_display, vpImagePoint(I_color_adjust.getHeight(), I_color_adjust.getWidth())); + + vpImageIo::write(I_color_res, filename_color); + vpImageIo::write(I_gray_res, filename_gray); + + vpDisplay::display(I_display); + vpDisplay::displayText(I_display, 20, 20, title, vpColor::red); + if (!title_2.empty()) { + vpDisplay::displayText(I_display, 40, static_cast(I_color_adjust.getWidth()*0.85), + title_2, vpColor::green); + } + vpDisplay::flush(I_display); + vpDisplay::getClick(I_display); +} +} + int main(int argc, const char **argv) { -//! [Macro defined] -#if defined(VISP_HAVE_MODULE_IMGPROC) && \ - (defined(VISP_HAVE_X11) || defined(VISP_HAVE_GDI) || defined(VISP_HAVE_OPENCV)) && \ - (defined(VISP_HAVE_PNG) || defined(VISP_HAVE_OPENCV)) //! [Macro defined] - //! -#ifdef ENABLE_VISP_NAMESPACE - using namespace VISP_NAMESPACE_NAME; -#endif +#if defined(VISP_HAVE_MODULE_IMGPROC) && defined(VISP_HAVE_DISPLAY) && \ + (defined(VISP_HAVE_PNG) || defined(VISP_HAVE_OPENCV) || defined(VISP_HAVE_STBIMAGE) || defined(VISP_HAVE_SIMDLIB)) && \ + ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) + //! [Macro defined] std::string input_filename = "Sample_low_brightness.png"; double alpha = 10.0, beta = 50.0; @@ -34,19 +65,24 @@ int main(int argc, const char **argv) VISP_NAMESPACE_NAME::vpGammaColorHandling colorHandling = VISP_NAMESPACE_NAME::GAMMA_HSV; int scale = 240, scaleDiv = 3, level = 0, kernelSize = -1; double dynamic = 3.0; + int scale_display = 2; for (int i = 1; i < argc; i++) { if (std::string(argv[i]) == "--input" && i + 1 < argc) { - input_filename = std::string(argv[i + 1]); + ++i; + input_filename = std::string(argv[i]); } else if (std::string(argv[i]) == "--alpha" && i + 1 < argc) { - alpha = atof(argv[i + 1]); + ++i; + alpha = atof(argv[i]); } else if (std::string(argv[i]) == "--beta" && i + 1 < argc) { - beta = atof(argv[i + 1]); + ++i; + beta = atof(argv[i]); } else if (std::string(argv[i]) == "--gamma" && i + 1 < argc) { - gamma = atof(argv[i + 1]); + ++i; + gamma = atof(argv[i]); } else if ((std::string(argv[i]) == "--gamma-color-handling") && ((i + 1) < argc)) { ++i; @@ -57,71 +93,83 @@ int main(int argc, const char **argv) method = VISP_NAMESPACE_NAME::vpGammaMethodFromString(argv[i]); } else if (std::string(argv[i]) == "--scale" && i + 1 < argc) { - scale = atoi(argv[i + 1]); + ++i; + scale = atoi(argv[i]); } else if (std::string(argv[i]) == "--scaleDiv" && i + 1 < argc) { - scaleDiv = atoi(argv[i + 1]); + ++i; + scaleDiv = atoi(argv[i]); } else if (std::string(argv[i]) == "--level" && i + 1 < argc) { - level = atoi(argv[i + 1]); + ++i; + level = atoi(argv[i]); } else if (std::string(argv[i]) == "--kernelSize" && i + 1 < argc) { - kernelSize = atoi(argv[i + 1]); + ++i; + kernelSize = atoi(argv[i]); } else if (std::string(argv[i]) == "--dynamic" && i + 1 < argc) { - dynamic = atof(argv[i + 1]); + ++i; + dynamic = atof(argv[i]); + } + else if (std::string(argv[i]) == "--scale-display" && i + 1 < argc) { + ++i; + scale_display = atoi(argv[i]); } else if (std::string(argv[i]) == "--help" || std::string(argv[i]) == "-h") { std::cout << "Usage: " << argv[0] << " [--input ]" - " [--alpha ] [--beta ]" - " [--gamma ]" + " [--alpha ] [--beta ]" + " [--gamma ]" " [--gamma-color-handling " << VISP_NAMESPACE_NAME::vpGammaColorHandlingList() << "]" " [--gamma-method " << VISP_NAMESPACE_NAME::vpGammaMethodList() << "]" - " [--scale [--scaleDiv for " - "VISP_NAMESPACE_NAME::retinex()]" - " [--level [--kernelSize " - "]" - " [--dynamic ] [--help]" + " [--scale [--scaleDiv for retinex()]" + " [--level [--kernelSize ]" + " [--dynamic ] " + " [--scale-display ] " + " [--help]" << std::endl; return EXIT_SUCCESS; } } + // Filename without extension to save the results + const std::string input_name = vpIoTools::getNameWE(input_filename); + vpImage I_color; vpImageIo::read(I_color, input_filename); vpImage I_gray; vpImageConvert::convert(I_color, I_gray); + vpImage I_gray_display; + vpImageConvert::convert(I_gray, I_gray_display); + // Side-by-side images 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 + + // Side-by-side display for color (top) and gray (bottom) images + vpImage I_display(2 * I_color.getHeight(), 2 * I_color.getWidth()); + I_display.insert(I_color, vpImagePoint()); + I_display.insert(I_gray_display, vpImagePoint(I_color.getHeight(), 0)); + std::shared_ptr d = vpDisplayFactory::createDisplay(); + d->setDownScalingFactor(static_cast(scale_display)); + d->init(I_display, 10, 10, "Brightness adjustment results"); //! [Brightness contrast adjustment] vpImage I_color_adjust; VISP_NAMESPACE_NAME::adjust(I_color, I_color_adjust, alpha, beta); + vpImage I_gray_adjust; + VISP_NAMESPACE_NAME::adjust(I_gray, I_gray_adjust, alpha, beta); //! [Brightness contrast adjustment] - I_color_res.insert(I_color_adjust, vpImagePoint(0, I_color.getWidth())); - std::stringstream ss; - ss << "Sample_low_brightness_alpha=" << alpha << "_beta=" << beta << ".png"; - vpImageIo::write(I_color_res, ss.str()); - vpDisplay::display(I_color_res); - vpDisplay::displayText(I_color_res, 20, 20, "Brightness and contrast adjustment. Click to continue.", vpColor::red); - vpDisplay::flush(I_color_res); - vpDisplay::getClick(I_color_res); + std::stringstream ss_color; + ss_color << input_name << "_adjust_alpha=" << alpha << "_beta=" << beta << ".png"; + std::stringstream ss_gray; + ss_gray << input_name << "_adjust_alpha=" << alpha << "_beta=" << beta << "_gray.png"; + display(I_display, I_color_res, I_color_adjust, I_gray_res, I_gray_adjust, I_gray_display, + "Brightness and contrast adjustment. Click to continue.", ss_color.str(), ss_gray.str()); //! [Gamma correction] if (method != VISP_NAMESPACE_NAME::GAMMA_MANUAL) { @@ -133,60 +181,78 @@ int main(int argc, const char **argv) // If the user wants to set a constant user-defined gamma factor, the method must be set to manual. method = VISP_NAMESPACE_NAME::GAMMA_MANUAL; } - vpImage I_gray_gamma_correction; - VISP_NAMESPACE_NAME::gammaCorrection(I_gray, I_gray_gamma_correction, static_cast(gamma), method); vpImage I_color_gamma_correction; VISP_NAMESPACE_NAME::gammaCorrection(I_color, I_color_gamma_correction, static_cast(gamma), colorHandling, method); + vpImage I_gray_gamma_correction; + VISP_NAMESPACE_NAME::gammaCorrection(I_gray, I_gray_gamma_correction, static_cast(gamma), 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"; - vpImageIo::write(I_color_res, ss.str()); - - vpDisplay::display(I_color_res); - vpDisplay::displayText(I_color_res, 20, 20, "Gamma correction. Click to continue.", vpColor::red); - vpDisplay::flush(I_color_res); - vpDisplay::getClick(I_color_res); + + ss_color.str(""); + ss_color << input_name << "_gamma=" << gamma << ".png"; + ss_gray.str(""); + ss_gray << input_name << "_gamma=" << gamma << "_gray.png"; + display(I_display, I_color_res, I_color_gamma_correction, I_gray_res, I_gray_gamma_correction, I_gray_display, + "Gamma correction. Click to continue.", ss_color.str(), ss_gray.str()); + + // Display results for the different Gamma correction method + for (int gamma_idx = 0; gamma_idx < VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT; ++gamma_idx) { + gamma = -1.; + VISP_NAMESPACE_NAME::vpGammaMethod gamma_method = static_cast(gamma_idx); + if (gamma_method == VISP_NAMESPACE_NAME::GAMMA_MANUAL) { + continue; + } + + vpImage I_color_gamma_correction; + VISP_NAMESPACE_NAME::gammaCorrection(I_color, I_color_gamma_correction, static_cast(gamma), colorHandling, + gamma_method); + vpImage I_gray_gamma_correction; + VISP_NAMESPACE_NAME::gammaCorrection(I_gray, I_gray_gamma_correction, static_cast(gamma), gamma_method); + + const std::string gamma_name = VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method); + ss_color.str(""); + ss_color << input_name << "_" << gamma_name << ".png"; + ss_gray.str(""); + ss_gray << input_name << "_" << gamma_name << "_gray.png"; + display(I_display, I_color_res, I_color_gamma_correction, I_gray_res, I_gray_gamma_correction, I_gray_display, + "Gamma correction. Click to continue.", ss_color.str(), ss_gray.str(), gamma_name); + } //! [Histogram equalization] vpImage I_color_equalize_histogram; VISP_NAMESPACE_NAME::equalizeHistogram(I_color, I_color_equalize_histogram); + vpImage I_gray_equalize_histogram; + VISP_NAMESPACE_NAME::equalizeHistogram(I_gray, I_gray_equalize_histogram); //! [Histogram equalization] - I_color_res.insert(I_color_equalize_histogram, vpImagePoint(0, I_color.getWidth())); - ss.str(""); - ss << "Sample_low_brightness_eqHist.png"; - vpImageIo::write(I_color_res, ss.str()); - vpDisplay::display(I_color_res); - vpDisplay::displayText(I_color_res, 20, 20, "Histogram equalization. Click to continue.", vpColor::red); - vpDisplay::flush(I_color_res); - vpDisplay::getClick(I_color_res); + ss_color.str(""); + ss_color << input_name << "_eqHist.png"; + ss_gray.str(""); + ss_gray << input_name << "_eqHist_gray.png"; + display(I_display, I_color_res, I_color_equalize_histogram, I_gray_res, I_gray_equalize_histogram, I_gray_display, + "Histogram equalization. Click to continue.", ss_color.str(), ss_gray.str()); //! [Retinex] vpImage I_color_retinex; VISP_NAMESPACE_NAME::retinex(I_color, I_color_retinex, scale, scaleDiv, level, dynamic, kernelSize); + // Retinex uses color image as input + // Convert gray image into RGBa format for quick test + vpImage I_gray_color; + vpImageConvert::convert(I_gray, I_gray_color); + vpImage I_gray_color_retinex; + VISP_NAMESPACE_NAME::retinex(I_gray_color, I_gray_color_retinex, scale, scaleDiv, level, dynamic, kernelSize); + // Convert back to gray + vpImage I_gray_retinex; + vpImageConvert::convert(I_gray_color_retinex, I_gray_retinex); //! [Retinex] - I_color_res.insert(I_color_retinex, vpImagePoint(0, I_color.getWidth())); - ss.str(""); - ss << "Sample_low_brightness_scale=" << scale << "_scaleDiv=" << scaleDiv << "_level=" << level + ss_color.str(""); + ss_color << input_name << "_Retinex_scale=" << scale << "_scaleDiv=" << scaleDiv << "_level=" << level << "_dynamic=" << dynamic << "_kernelSize=" << kernelSize << ".png"; - vpImageIo::write(I_color_res, ss.str()); - - vpDisplay::display(I_color_res); - vpDisplay::displayText(I_color_res, 20, 20, "Retinex. Click to quit.", vpColor::red); - vpDisplay::flush(I_color_res); - vpDisplay::getClick(I_color_res); + ss_gray.str(""); + ss_gray << input_name << "_Retinex_scale=" << scale << "_scaleDiv=" << scaleDiv << "_level=" << level + << "_dynamic=" << dynamic << "_kernelSize=" << kernelSize << "_gray.png"; + display(I_display, I_color_res, I_color_retinex, I_gray_res, I_gray_retinex, I_gray_display, + "Retinex. Click to quit.", ss_color.str(), ss_gray.str()); #else (void)argc; (void)argv; diff --git a/tutorial/imgproc/brightness/tutorial-compare-auto-gamma.cpp b/tutorial/imgproc/brightness/tutorial-compare-auto-gamma.cpp new file mode 100755 index 0000000000..f25acdc3e8 --- /dev/null +++ b/tutorial/imgproc/brightness/tutorial-compare-auto-gamma.cpp @@ -0,0 +1,319 @@ +//! \example tutorial-compare-auto-gamma + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VISP_HAVE_SIMDLIB is required for INTERPOLATION_AREA +#if defined(VISP_HAVE_MODULE_IMGPROC) && defined(VISP_HAVE_SIMDLIB) && \ + ((__cplusplus >= 201103L) || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201103L))) +#include +#include + +#ifdef ENABLE_VISP_NAMESPACE +using namespace VISP_NAMESPACE_NAME; +#endif + +namespace +{ +template +void computeMeanMaxStdev(const vpImage &I, float &mean, float &max, float &stdev) +{ + max = std::numeric_limits::epsilon(); + mean = 0.; + stdev = 0.; + unsigned int nbRows = I.getRows(); + unsigned int nbCols = I.getCols(); + float scale = 1.f / (static_cast(nbRows) * static_cast(nbCols)); + for (unsigned int r = 0; r < nbRows; r++) { + for (unsigned int c = 0; c < nbCols; c++) { + mean += I[r][c]; + max = std::max(max, static_cast(I[r][c])); + } + } + mean *= scale; + for (unsigned int r = 0; r < nbRows; r++) { + for (unsigned int c = 0; c < nbCols; c++) { + stdev += (I[r][c] - mean) * (I[r][c] - mean); + } + } + stdev *= scale; + stdev = std::sqrt(stdev); +} + +void computeCanny(const vpImage &I, vpCannyEdgeDetection &cannyDetector, int gaussianKernelSize, + float gaussianStdev, int apertureSize, vpImageFilter::vpCannyFilteringAndGradientType filteringType, + vpImage &dIxy_uchar, vpImage &I_canny_visp) +{ + vpImage dIx, dIy, dIxy(I.getHeight(), I.getWidth()); + vpImageFilter::computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev, + apertureSize, filteringType); + + for (unsigned int i = 0; i < dIx.getHeight(); i++) { + for (unsigned int j = 0; j < dIx.getWidth(); j++) { + dIxy[i][j] = std::sqrt(dIx[i][j]*dIx[i][j] + dIy[i][j]*dIy[i][j]); + } + } + + float mean, max, stdev; + computeMeanMaxStdev(dIxy, mean, max, stdev); + vpImageConvert::convert(dIx, dIxy_uchar); + + // Set the gradients of the vpCannyEdgeDetection + cannyDetector.setGradients(dIx, dIy); + + I_canny_visp = cannyDetector.detect(I); +} + +double computeImageEntropy(const vpImage &I) +{ + // https://github.com/dengyueyun666/Image-Contrast-Enhancement/blob/cd2b1eb5bf6396e2fc3b94cd27f73933d5467147/src/Ying_2017_CAIP.cpp#L186-L207 + std::vector hist(256, 0); + for (unsigned int i = 0; i < I.getHeight(); i++) { + for (unsigned int j = 0; j < I.getWidth(); j++) { + int bin = I[i][j]; + hist[bin]++; + } + } + + double N = I.getSize(); + double cost = 0; + for (size_t i = 0; i < hist.size(); i++) { + if (hist[i] == 0) { + continue; + } + double p = hist[i] / N; + cost += -p * std::log2(p); + } + + return cost; +} +} // namespace + +int main(int argc, const char **argv) +{ + std::string input = "Sample_low_brightness.png"; + std::string output = "Results"; + int gaussianKernelSize = 3; + float gaussianStdev = 1.0f; + int apertureSize = 3; + bool half = false; + vpImageFilter::vpCannyFilteringAndGradientType filteringType = vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING; + VISP_NAMESPACE_NAME::vpGammaColorHandling gamma_colorspace = VISP_NAMESPACE_NAME::GAMMA_HSV; + + for (int i = 1; i < argc; i++) { + if (std::string(argv[i]) == "--input" && i + 1 < argc) { + ++i; + input = std::string(argv[i]); + } + else if (std::string(argv[i]) == "--half") { + half = true; + } + else if (std::string(argv[i]) == "--gaussian-kernel-size" && i + 1 < argc) { + ++i; + gaussianKernelSize = std::atoi(argv[i]); + } + else if (std::string(argv[i]) == "--gaussian-std" && i + 1 < argc) { + ++i; + gaussianStdev = std::atof(argv[i]); + } + else if (std::string(argv[i]) == "--aperture-size" && i + 1 < argc) { + ++i; + apertureSize = std::atoi(argv[i]); + } + else if (std::string(argv[i]) == "--canny-filtering-type" && i + 1 < argc) { + ++i; + int type = std::atoi(argv[i]); + if (type == 1) { + filteringType = vpImageFilter::CANNY_GBLUR_SCHARR_FILTERING; + } + } + else if (std::string(argv[i]) == "--gamma-rgb") { + gamma_colorspace = VISP_NAMESPACE_NAME::GAMMA_RGB; + } + else if (std::string(argv[i]) == "--output" && i + 1 < argc) { + ++i; + output = std::string(argv[i]); + } + else { + std::cout << "Usage: " << argv[0] + << " [--input ]" + " [--half (use half image resolution)]" + " [--gaussian-kernel-size ]" + " [--gaussian-std ]" + " [--aperture-size ]" + " [--canny-filtering-type <0=CANNY_GBLUR_SOBEL_FILTERING, 1=CANNY_GBLUR_SCHARR_FILTERING>]" + " [--gamma-rgb (RGB colorspace, else HSV]" + " [--output (to save results)]" + << std::endl; + return EXIT_SUCCESS; + } + } + + std::cout << "Input: " << input << std::endl; + std::cout << "Process on half image resolution? " << half << std::endl; + std::cout << "Gaussian kernel size: " << gaussianKernelSize << std::endl; + std::cout << "Gaussian standard deviation: " << gaussianStdev << std::endl; + std::cout << "Aperture size: " << apertureSize << std::endl; + std::cout << "Canny filtering type: " << filteringType << std::endl; + std::cout << "RGB colorspace? " << (gamma_colorspace == VISP_NAMESPACE_NAME::GAMMA_RGB) << std::endl; + std::cout << "Output result folder: " << output << std::endl; + + // Canny parameters + float lowerThresh = -1.; + float upperThresh = -1.; + float lowerThreshRatio = 0.6f; + float upperThreshRatio = 0.8f; + vpCannyEdgeDetection cannyDetector(gaussianKernelSize, gaussianStdev, apertureSize, + lowerThresh, upperThresh, lowerThreshRatio, upperThreshRatio, + filteringType); + + bool single_image = vpIoTools::checkFilename(input); + vpVideoReader reader; + vpImage I_color_ori, I_color; + if (single_image) { + vpImageIo::read(I_color_ori, input); + } + else { + reader.setFileName(input); + reader.open(I_color_ori); + } + if (half) { + vpImageTools::resize(I_color_ori, I_color, I_color_ori.getWidth()/2, I_color_ori.getHeight()/2, + vpImageTools::INTERPOLATION_AREA); + } + else { + I_color = I_color_ori; + } + + vpIoTools::makeDirectory(output); + + const int nb_methods = VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT - 1; // all except GAMMA_MANUAL + std::vector> computation_times(nb_methods); + int nb_images = 0; + + vpImage I_color_gamma_correction, I_res_stack; + vpImage I_gray, I_gray_gamma_correction, dIxy_uchar, I_canny_visp; + vpImage dIxy_uchar_color, I_canny_visp_color; + vpFont font(32); + bool read_single_image = false; + while (!read_single_image && (single_image || !reader.end())) { + if (!single_image) { + reader.acquire(I_color_ori); + } + if (half) { + vpImageTools::resize(I_color_ori, I_color, I_color_ori.getWidth()/2, I_color_ori.getHeight()/2, + vpImageTools::INTERPOLATION_AREA); + } + else { + I_color = I_color_ori; + } + nb_images++; + + const int nb_methods = VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT - 1; // all except GAMMA_MANUAL + I_res_stack.init(nb_methods*I_color.getHeight(), 4*I_color.getWidth()); + dIxy_uchar.init(I_color.getHeight(), I_color.getWidth()); + I_canny_visp.init(I_color.getHeight(), I_color.getWidth()); + + // Output results + int offset_text_start_y = 25; + int text_h = 40; + int offset_idx = 0; + double offset_text1 = 0.01; + double offset_text2 = 0.26; + double start_time = 0, end_time = 0; + char buffer[FILENAME_MAX]; + + vpImageConvert::convert(I_color, I_gray); + const double img_ori_entropy = computeImageEntropy(I_gray); + + for (int gamma_idx = 1; gamma_idx < VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT; ++gamma_idx, offset_idx++) { + VISP_NAMESPACE_NAME::vpGammaMethod gamma_method = static_cast(gamma_idx); + if (gamma_method == VISP_NAMESPACE_NAME::GAMMA_MANUAL) { + continue; + } + + const double gamma = -1; + start_time = vpTime::measureTimeMs(); + VISP_NAMESPACE_NAME::gammaCorrection(I_color, I_color_gamma_correction, static_cast(gamma), + gamma_colorspace, gamma_method); + end_time = vpTime::measureTimeMs(); + std::cout << "Computation time (" << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method) + << "): " << (end_time-start_time) << " ms" << std::endl; + computation_times[offset_idx].push_back(end_time-start_time); + + vpImageConvert::convert(I_color_gamma_correction, I_gray_gamma_correction); + const double img_corrected_entropy = computeImageEntropy(I_gray_gamma_correction); + computeCanny(I_gray_gamma_correction, cannyDetector, gaussianKernelSize, gaussianStdev, apertureSize, + filteringType, dIxy_uchar, I_canny_visp); + vpImageConvert::convert(dIxy_uchar, dIxy_uchar_color); + vpImageConvert::convert(I_canny_visp, I_canny_visp_color); + I_res_stack.insert(I_color, vpImagePoint(offset_idx*I_color.getHeight(), 0)); + I_res_stack.insert(I_color_gamma_correction, vpImagePoint(offset_idx*I_color.getHeight(), I_color.getWidth())); + I_res_stack.insert(I_canny_visp_color, vpImagePoint(offset_idx*I_color.getHeight(), 2*I_color.getWidth())); + I_res_stack.insert(dIxy_uchar_color, vpImagePoint(offset_idx*I_color.getHeight(), 3*I_color.getWidth())); + // Entropy original + snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_ori_entropy); + font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y, offset_text1*I_res_stack.getWidth()), vpColor::red); + // Computation time + std::ostringstream oss; + oss << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method) << " (%.2f ms)"; + snprintf(buffer, FILENAME_MAX, oss.str().c_str(), (end_time-start_time)); + font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y, + offset_text2*I_res_stack.getWidth()), vpColor::red); + // Canny + snprintf(buffer, FILENAME_MAX, "Canny mean: (%.2f)", I_canny_visp.getMeanValue()); + font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y+text_h, + offset_text2*I_res_stack.getWidth()), vpColor::red); + // Entropy + snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_corrected_entropy); + font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y+2*text_h, offset_text2*I_res_stack.getWidth()), vpColor::red); + } + + if (!output.empty()) { + std::stringstream output_filename; + const std::string extension = ".jpeg"; + if (single_image) { + output_filename << vpIoTools::createFilePath(output, vpIoTools::getNameWE(input)) << extension; + } + else { + output_filename << vpIoTools::createFilePath(output, vpIoTools::getNameWE(reader.getFrameName())) << extension; + } + std::cout << "Write result to: " << output_filename.str() << std::endl; + vpImageIo::write(I_res_stack, output_filename.str()); + } + + if (single_image) { + read_single_image = true; + } + } + + std::cout << "\nStats:" << std::endl; + std::cout << "Nb images: " << nb_images << std::endl; + + for (int gamma_idx = 1; gamma_idx < VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT; ++gamma_idx) { + VISP_NAMESPACE_NAME::vpGammaMethod gamma_method = static_cast(gamma_idx); + if (gamma_method == VISP_NAMESPACE_NAME::GAMMA_MANUAL) { + continue; + } + std::cout << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method) << ": mean=" + << vpMath::getMean(computation_times[gamma_idx-1]) << " ms ; median=" + << vpMath::getMedian(computation_times[gamma_idx-1]) << " ms" << std::endl; + } + + return EXIT_SUCCESS; +} +#else +int main() +{ + std::cerr << "C++11 is required." << std::endl; + return EXIT_SUCCESS; +} +#endif