Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
tutorial-compare-auto-gamma.cpp
1
2
3#include <iostream>
4#include <visp3/core/vpConfig.h>
5#include <visp3/core/vpImage.h>
6#include <visp3/core/vpIoTools.h>
7#include <visp3/core/vpImageTools.h>
8#include <visp3/core/vpCannyEdgeDetection.h>
9#include <visp3/core/vpImageFilter.h>
10#include <visp3/core/vpFont.h>
11#include <visp3/io/vpImageIo.h>
12#include <visp3/io/vpVideoReader.h>
13
14// VISP_HAVE_SIMDLIB is required for INTERPOLATION_AREA
15#if defined(VISP_HAVE_MODULE_IMGPROC) && defined(VISP_HAVE_SIMDLIB) && (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
16#include <visp3/imgproc/vpImgproc.h>
17#include <memory>
18
19#ifdef ENABLE_VISP_NAMESPACE
20using namespace VISP_NAMESPACE_NAME;
21#endif
22
23namespace
24{
25template <class T>
26void computeMeanMaxStdev(const vpImage<T> &I, float &mean, float &max, float &stdev)
27{
28 max = std::numeric_limits<float>::epsilon();
29 mean = 0.;
30 stdev = 0.;
31 unsigned int nbRows = I.getRows();
32 unsigned int nbCols = I.getCols();
33 float scale = 1.f / (static_cast<float>(nbRows) * static_cast<float>(nbCols));
34 for (unsigned int r = 0; r < nbRows; r++) {
35 for (unsigned int c = 0; c < nbCols; c++) {
36 mean += I[r][c];
37 max = std::max<float>(max, static_cast<float>(I[r][c]));
38 }
39 }
40 mean *= scale;
41 for (unsigned int r = 0; r < nbRows; r++) {
42 for (unsigned int c = 0; c < nbCols; c++) {
43 stdev += (I[r][c] - mean) * (I[r][c] - mean);
44 }
45 }
46 stdev *= scale;
47 stdev = std::sqrt(stdev);
48}
49
50void computeCanny(const vpImage<unsigned char> &I, vpCannyEdgeDetection &cannyDetector, int gaussianKernelSize,
51 float gaussianStdev, int apertureSize, vpImageFilter::vpCannyFilteringAndGradientType filteringType,
52 vpImage<unsigned char> &dIxy_uchar, vpImage<unsigned char> &I_canny_visp)
53{
54 vpImage<float> dIx, dIy, dIxy(I.getHeight(), I.getWidth());
55 vpImageFilter::computePartialDerivatives(I, dIx, dIy, true, true, true, gaussianKernelSize, gaussianStdev,
56 apertureSize, filteringType);
57
58 for (unsigned int i = 0; i < dIx.getHeight(); i++) {
59 for (unsigned int j = 0; j < dIx.getWidth(); j++) {
60 dIxy[i][j] = std::sqrt(dIx[i][j]*dIx[i][j] + dIy[i][j]*dIy[i][j]);
61 }
62 }
63
64 float mean, max, stdev;
65 computeMeanMaxStdev(dIxy, mean, max, stdev);
66 vpImageConvert::convert(dIx, dIxy_uchar);
67
68 // Set the gradients of the vpCannyEdgeDetection
69 cannyDetector.setGradients(dIx, dIy);
70
71 I_canny_visp = cannyDetector.detect(I);
72}
73
74double computeImageEntropy(const vpImage<unsigned char> &I)
75{
76 // https://github.com/dengyueyun666/Image-Contrast-Enhancement/blob/cd2b1eb5bf6396e2fc3b94cd27f73933d5467147/src/Ying_2017_CAIP.cpp#L186-L207
77 std::vector<int> hist(256, 0);
78 for (unsigned int i = 0; i < I.getHeight(); i++) {
79 for (unsigned int j = 0; j < I.getWidth(); j++) {
80 int bin = I[i][j];
81 hist[bin]++;
82 }
83 }
84
85 double N = I.getSize();
86 double cost = 0;
87 for (size_t i = 0; i < hist.size(); i++) {
88 if (hist[i] == 0) {
89 continue;
90 }
91 double p = hist[i] / N;
92 cost += -p * std::log2(p);
93 }
94
95 return cost;
96}
97} // namespace
98
99int main(int argc, const char **argv)
100{
101 std::string input = "Sample_low_brightness.png";
102 std::string output = "Results";
103 int gaussianKernelSize = 3;
104 float gaussianStdev = 1.0f;
105 int apertureSize = 3;
106 unsigned int max_resolution = 800;
109
110 for (int i = 1; i < argc; i++) {
111 if (std::string(argv[i]) == "--input" && i + 1 < argc) {
112 ++i;
113 input = std::string(argv[i]);
114 }
115 else if (std::string(argv[i]) == "--max-resolution" && i + 1 < argc) {
116 ++i;
117 max_resolution = std::atoi(argv[i]);
118 }
119 else if (std::string(argv[i]) == "--gaussian-kernel-size" && i + 1 < argc) {
120 ++i;
121 gaussianKernelSize = std::atoi(argv[i]);
122 }
123 else if (std::string(argv[i]) == "--gaussian-std" && i + 1 < argc) {
124 ++i;
125 gaussianStdev = static_cast<float>(std::atof(argv[i]));
126 }
127 else if (std::string(argv[i]) == "--aperture-size" && i + 1 < argc) {
128 ++i;
129 apertureSize = std::atoi(argv[i]);
130 }
131 else if (std::string(argv[i]) == "--canny-filtering-type" && i + 1 < argc) {
132 ++i;
133 int type = std::atoi(argv[i]);
134 if (type == 1) {
136 }
137 }
138 else if (std::string(argv[i]) == "--gamma-rgb") {
139 gamma_colorspace = VISP_NAMESPACE_NAME::GAMMA_RGB;
140 }
141 else if (std::string(argv[i]) == "--output" && i + 1 < argc) {
142 ++i;
143 output = std::string(argv[i]);
144 }
145 else {
146 std::cout << "Usage: " << argv[0]
147 << " [--input <input path or image sequence pattern>]"
148 " [--max-resolution <resolution_in_px> (max image resolution, otherwise the input image is resized)]"
149 " [--gaussian-kernel-size <e.g. 3, 5, 7>]"
150 " [--gaussian-std <e.g. 1>]"
151 " [--aperture-size <e.g. 3>]"
152 " [--canny-filtering-type <0=CANNY_GBLUR_SOBEL_FILTERING, 1=CANNY_GBLUR_SCHARR_FILTERING>]"
153 " [--gamma-rgb (RGB colorspace, else HSV]"
154 " [--output <folder path> (to save the results)]"
155 << std::endl;
156 return EXIT_SUCCESS;
157 }
158 }
159
160 std::cout << "Input: " << input << std::endl;
161 std::cout << "Max image resolution: " << max_resolution << std::endl;
162 std::cout << "Gaussian kernel size: " << gaussianKernelSize << std::endl;
163 std::cout << "Gaussian standard deviation: " << gaussianStdev << std::endl;
164 std::cout << "Aperture size: " << apertureSize << std::endl;
165 std::cout << "Canny filtering type: " << filteringType << std::endl;
166 std::cout << "RGB colorspace? " << (gamma_colorspace == VISP_NAMESPACE_NAME::GAMMA_RGB) << std::endl;
167 std::cout << "Output result folder: " << output << std::endl;
168
169 // Canny parameters
170 float lowerThresh = -1.;
171 float upperThresh = -1.;
172 float lowerThreshRatio = 0.6f;
173 float upperThreshRatio = 0.8f;
174 vpCannyEdgeDetection cannyDetector(gaussianKernelSize, gaussianStdev, apertureSize,
175 lowerThresh, upperThresh, lowerThreshRatio, upperThreshRatio,
176 filteringType);
177
178 bool single_image = vpIoTools::checkFilename(input);
179 vpVideoReader reader;
180 vpImage<vpRGBa> I_color_ori, I_color;
181 if (single_image) {
182 vpImageIo::read(I_color_ori, input);
183 }
184 else {
185 reader.setFileName(input);
186 reader.open(I_color_ori);
187 }
188
189 if (I_color_ori.getWidth() > max_resolution || I_color_ori.getHeight() > max_resolution) {
190 float factor_w = I_color_ori.getWidth() / static_cast<float>(max_resolution);
191 float factor_h = I_color_ori.getHeight() / static_cast<float>(max_resolution);
192 int resize_factor_w = static_cast<int>(factor_w);
193 int resize_factor_h = static_cast<int>(factor_h);
194 int resize_factor = std::max(resize_factor_w, resize_factor_h);
195 vpImageTools::resize(I_color_ori, I_color, I_color_ori.getWidth()/resize_factor, I_color_ori.getHeight()/resize_factor,
197 }
198 else {
199 I_color = I_color_ori;
200 }
201
203
204 const int nb_methods = 1 + VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT - 1; // original img + all except GAMMA_MANUAL
205 std::vector<std::vector<double>> computation_times(nb_methods);
206 int nb_images = 0;
207
208 vpImage<vpRGBa> I_color_gamma_correction, I_res_stack;
209 vpImage<unsigned char> I_gray, I_gray_gamma_correction, dIxy_uchar, I_canny_visp;
210 vpImage<vpRGBa> dIxy_uchar_color, I_canny_visp_color;
211 vpFont font(32);
212 bool read_single_image = false;
213 while (!read_single_image && (single_image || !reader.end())) {
214 std::cout << "\n" << reader.getFrameIndex() << ")" << std::endl;
215 if (!single_image) {
216 reader.acquire(I_color_ori);
217 }
218 if (I_color_ori.getWidth() > max_resolution || I_color_ori.getHeight() > max_resolution) {
219 float factor_w = I_color_ori.getWidth() / static_cast<float>(max_resolution);
220 float factor_h = I_color_ori.getHeight() / static_cast<float>(max_resolution);
221 int resize_factor_w = static_cast<int>(factor_w);
222 int resize_factor_h = static_cast<int>(factor_h);
223 int resize_factor = std::max(resize_factor_w, resize_factor_h);
224 vpImageTools::resize(I_color_ori, I_color, I_color_ori.getWidth()/resize_factor, I_color_ori.getHeight()/resize_factor,
226 }
227 else {
228 I_color = I_color_ori;
229 }
230 nb_images++;
231
232 const int nb_cols = 4; // original + corrected + Canny + gradient
233 I_res_stack.init(nb_methods*I_color.getHeight(), nb_cols*I_color.getWidth());
234 dIxy_uchar.init(I_color.getHeight(), I_color.getWidth());
235 I_canny_visp.init(I_color.getHeight(), I_color.getWidth());
236
237 // Output results
238 int offset_text_start_y = 25;
239 int text_h = 40;
240 int offset_idx = 0;
241 double offset_text1 = 0.01;
242 double offset_text2 = 0.26;
243 double start_time = 0, end_time = 0;
244 char buffer[FILENAME_MAX];
245
246 vpImageConvert::convert(I_color, I_gray);
247
248 computeCanny(I_gray, cannyDetector, gaussianKernelSize, gaussianStdev, apertureSize,
249 filteringType, dIxy_uchar, I_canny_visp);
250 const double img_ori_Canny = I_canny_visp.getMeanValue();
251 const double img_ori_entropy = computeImageEntropy(I_gray);
252
253 // Original image
254 {
255 computation_times[offset_idx].push_back(-1);
256 vpImageConvert::convert(dIxy_uchar, dIxy_uchar_color);
257 vpImageConvert::convert(I_canny_visp, I_canny_visp_color);
258 I_res_stack.insert(I_color, vpImagePoint(offset_idx*I_color.getHeight(), 0));
259 I_res_stack.insert(I_color, vpImagePoint(offset_idx*I_color.getHeight(), I_color.getWidth()));
260 I_res_stack.insert(I_canny_visp_color, vpImagePoint(offset_idx*I_color.getHeight(), 2*I_color.getWidth()));
261 I_res_stack.insert(dIxy_uchar_color, vpImagePoint(offset_idx*I_color.getHeight(), 3*I_color.getWidth()));
262 // Entropy original
263 snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_ori_entropy);
264 font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y, offset_text1*I_res_stack.getWidth()), vpColor::red);
265 // Entropy
266 snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_ori_entropy);
267 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);
268 // Canny
269 snprintf(buffer, FILENAME_MAX, "Canny mean: %.2f", I_canny_visp.getMeanValue());
270 font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y, offset_text2*I_res_stack.getWidth()), vpColor::red);
271 offset_idx++;
272 }
273
274 for (int gamma_idx = 1; gamma_idx < VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT; ++gamma_idx, offset_idx++) {
275 VISP_NAMESPACE_NAME::vpGammaMethod gamma_method = static_cast<VISP_NAMESPACE_NAME::vpGammaMethod>(gamma_idx);
276 if (gamma_method == VISP_NAMESPACE_NAME::GAMMA_MANUAL) {
277 continue;
278 }
279
280 const double gamma = -1;
282 VISP_NAMESPACE_NAME::gammaCorrection(I_color, I_color_gamma_correction, static_cast<float>(gamma),
283 gamma_colorspace, gamma_method);
285 std::cout << "Computation time (" << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method)
286 << "): " << (end_time-start_time) << " ms" << std::endl;
287 computation_times[offset_idx].push_back(end_time-start_time);
288
289 vpImageConvert::convert(I_color_gamma_correction, I_gray_gamma_correction);
290 const double img_corrected_entropy = computeImageEntropy(I_gray_gamma_correction);
291 computeCanny(I_gray_gamma_correction, cannyDetector, gaussianKernelSize, gaussianStdev, apertureSize,
292 filteringType, dIxy_uchar, I_canny_visp);
293 vpImageConvert::convert(dIxy_uchar, dIxy_uchar_color);
294 vpImageConvert::convert(I_canny_visp, I_canny_visp_color);
295 I_res_stack.insert(I_color, vpImagePoint(offset_idx*I_color.getHeight(), 0));
296 I_res_stack.insert(I_color_gamma_correction, vpImagePoint(offset_idx*I_color.getHeight(), I_color.getWidth()));
297 I_res_stack.insert(I_canny_visp_color, vpImagePoint(offset_idx*I_color.getHeight(), 2*I_color.getWidth()));
298 I_res_stack.insert(dIxy_uchar_color, vpImagePoint(offset_idx*I_color.getHeight(), 3*I_color.getWidth()));
299 // Entropy original
300 snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_ori_entropy);
301 font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y, offset_text1*I_res_stack.getWidth()), vpColor::red);
302 // Computation time
303 std::ostringstream oss;
304 oss << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method) << " (%.2f ms)";
305 snprintf(buffer, FILENAME_MAX, oss.str().c_str(), (end_time-start_time));
306 font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y,
307 offset_text2*I_res_stack.getWidth()), vpColor::red);
308 // Entropy
309 snprintf(buffer, FILENAME_MAX, "Entropy: %.4f", img_corrected_entropy);
310 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);
311 // Canny
312 double canny_mean_perc = 100 * I_canny_visp.getMeanValue() / img_ori_Canny;
313 snprintf(buffer, FILENAME_MAX, "Canny mean: %.2f (%.2f%%)", I_canny_visp.getMeanValue(), canny_mean_perc);
314 font.drawText(I_res_stack, buffer, vpImagePoint(offset_idx*I_color.getHeight() + offset_text_start_y+2*text_h,
315 offset_text2*I_res_stack.getWidth()), vpColor::red);
316 }
317
318 if (!output.empty()) {
319 std::stringstream output_filename;
320 const std::string extension = ".jpeg";
321 if (single_image) {
322 output_filename << vpIoTools::createFilePath(output, vpIoTools::getNameWE(input)) << extension;
323 }
324 else {
325 output_filename << vpIoTools::createFilePath(output, vpIoTools::getNameWE(reader.getFrameName())) << extension;
326 }
327 std::cout << "Write result to: " << output_filename.str() << std::endl;
328 vpImageIo::write(I_res_stack, output_filename.str());
329 }
330
331 if (single_image) {
332 read_single_image = true;
333 }
334 }
335
336 std::cout << "\nStats:" << std::endl;
337 std::cout << "Nb images: " << nb_images << std::endl;
338
339 for (int gamma_idx = 1; gamma_idx < VISP_NAMESPACE_NAME::GAMMA_METHOD_COUNT; ++gamma_idx) {
340 VISP_NAMESPACE_NAME::vpGammaMethod gamma_method = static_cast<VISP_NAMESPACE_NAME::vpGammaMethod>(gamma_idx);
341 if (gamma_method == VISP_NAMESPACE_NAME::GAMMA_MANUAL) {
342 continue;
343 }
344 std::cout << VISP_NAMESPACE_NAME::vpGammaMethodToString(gamma_method) << ": mean="
345 << vpMath::getMean(computation_times[1 + gamma_idx-1]) << " ms ; median="
346 << vpMath::getMedian(computation_times[1 + gamma_idx-1]) << " ms" << std::endl; // index 0 is original img
347 }
348
349 return EXIT_SUCCESS;
350}
351#else
352int main()
353{
354 std::cerr << "C++11 is required." << std::endl;
355 return EXIT_SUCCESS;
356}
357#endif
Class that implements the Canny's edge detector. It is possible to use a boolean mask to ignore some ...
vpImage< unsigned char > detect(const vpImage< vpRGBa > &I_color)
Detect the edges in an image. Convert the color image into a gray-scale image.
void setGradients(const vpImage< float > &dIx, const vpImage< float > &dIy)
Set the Gradients of the image that will be processed.
static const vpColor red
Definition vpColor.h:198
Font drawing functions for image.
Definition vpFont.h:55
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
vpCannyFilteringAndGradientType
Canny filter and gradient operators to apply on the image before the edge detection stage.
@ CANNY_GBLUR_SOBEL_FILTERING
Apply Gaussian blur + Sobel operator on the input image.
@ CANNY_GBLUR_SCHARR_FILTERING
Apply Gaussian blur + Scharr operator on the input image.
static void computePartialDerivatives(const cv::Mat &cv_I, cv::Mat &cv_dIx, cv::Mat &cv_dIy, const bool &computeDx=true, const bool &computeDy=true, const bool &normalize=true, const unsigned int &gaussianKernelSize=5, const float &gaussianStdev=2.f, const unsigned int &apertureGradient=3, const vpCannyFilteringAndGradientType &filteringType=CANNY_GBLUR_SOBEL_FILTERING)
Compute the partial derivatives (i.e. horizontal and vertical gradients) of the input image.
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
static void resize(const vpImage< Type > &I, vpImage< Type > &Ires, unsigned int width, unsigned int height, const vpImageInterpolationType &method=INTERPOLATION_NEAREST, unsigned int nThreads=0)
Definition of the vpImage class member functions.
Definition vpImage.h:131
void init(unsigned int height, unsigned int width)
Set the size of the image.
Definition vpImage.h:387
unsigned int getWidth() const
Definition vpImage.h:242
void insert(const vpImage< Type > &src, const vpImagePoint &topLeft)
Definition vpImage.h:639
unsigned int getHeight() const
Definition vpImage.h:181
double getMeanValue(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Return the mean value of the bitmap.
static bool checkFilename(const std::string &filename)
static std::string createFilePath(const std::string &parent, const std::string &child)
static void makeDirectory(const std::string &dirname)
static std::string getNameWE(const std::string &pathname)
static double getMedian(const std::vector< double > &v)
Definition vpMath.cpp:343
static double getMean(const std::vector< double > &v)
Definition vpMath.cpp:323
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void open(vpImage< vpRGBa > &I) VP_OVERRIDE
void setFileName(const std::string &filename)
std::string getFrameName() const
long getFrameIndex() const
void acquire(vpImage< vpRGBa > &I) VP_OVERRIDE
VISP_EXPORT void gammaCorrection(VISP_NAMESPACE_ADDRESSING vpImage< unsigned char > &I, const float &gamma, const vpGammaMethod &method=GAMMA_MANUAL, const VISP_NAMESPACE_ADDRESSING vpImage< bool > *p_mask=nullptr)
vpGammaColorHandling
How to handle color images when applying Gamma Correction.
Definition vpImgproc.h:147
vpGammaMethod
Gamma Correction automatic methods.
Definition vpImgproc.h:98
VISP_EXPORT std::string vpGammaMethodToString(const vpGammaMethod &type)
Cast a vpGammaMethod into a string, to know its name.
Definition vpImgproc.cpp:87
VISP_EXPORT double measureTimeMs()