Visual Servoing Platform version 3.6.0
Loading...
Searching...
No Matches
tutorial-circle-hough.cpp
1#include <iostream>
2
3// ViSP includes
4#include <visp3/core/vpConfig.h>
5#include <visp3/core/vpException.h>
6#include <visp3/core/vpImage.h>
7#include <visp3/core/vpImageConvert.h>
8#include <visp3/core/vpImageDraw.h>
9#include <visp3/core/vpIoTools.h>
10#include <visp3/core/vpTime.h>
11#include <visp3/imgproc/vpCircleHoughTransform.h>
12#include <visp3/imgproc/vpImgproc.h>
13#include <visp3/io/vpImageIo.h>
14#include <visp3/io/vpVideoReader.h>
15
16#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
17
18#include "drawingHelpers.h"
19
21typedef enum TypeInputImage
22{
23 FULL_DISKS = 0,
24 HALF_DISKS = 1,
25 QUARTER_DISKS = 2,
26 USER_IMG = 3
27}TypeInputImage;
28
29std::string typeInputImageToString(const TypeInputImage &type)
30{
31 std::string name;
32 switch (type) {
33 case FULL_DISKS:
34 name = "full_disks";
35 break;
36 case HALF_DISKS:
37 name = "half_disks";
38 break;
39 case QUARTER_DISKS:
40 name = "quarter_disks";
41 break;
42 case USER_IMG:
43 name = "path/to/your/image";
44 }
45 return name;
46}
48
49TypeInputImage typeInputImageFromString(const std::string &name)
50{
51 TypeInputImage type(USER_IMG);
52 bool hasFound(false);
53 for (unsigned int id = 0; id < USER_IMG && !hasFound; id++) {
54 TypeInputImage candidate = (TypeInputImage)id;
55 if (name == typeInputImageToString(candidate)) {
56 type = candidate;
57 hasFound = true;
58 }
59 }
60 return type;
61}
62
63std::string getAvailableTypeInputImage(const std::string &prefix = "<", const std::string &sep = " , ", const std::string &suffix = ">")
64{
65 std::string list(prefix);
66 for (unsigned int id = 0; id < USER_IMG; id++) {
67 list += typeInputImageToString((TypeInputImage)id) + sep;
68 }
69 list += typeInputImageToString(USER_IMG) + suffix;
70 return list;
71}
72
74void
75drawDisk(vpImage<unsigned char> &I, const vpImagePoint &center, const unsigned int &radius,
76 const unsigned int &borderColor, const unsigned int &fillingColor, const unsigned int &thickness, const unsigned int &bckg)
78{
79 vpImageDraw::drawCircle(I, center, radius, borderColor, thickness);
81 center,
82 bckg,
83 fillingColor,
85 );
86}
87
90generateImage(const TypeInputImage &inputType)
92{
93 // // Image dimensions and background
94 const unsigned int width = 640;
95 const unsigned int height = 480;
96 const unsigned int bckg = 0;
97
98 // // Disks parameters
99 const unsigned int circleColor = 128;
100 const unsigned int circleRadius = 50;
101 const unsigned int circleThickness = 1;
102
103 // // Disks position when full circles
104 const double topFull = height / 4;
105 const double bottomFull = 3 * height / 4;
106 const double leftFull = width / 4;
107 const double rightFull = 3 * width / 4;
108
109 // // Disks position when Half of circles
110 const double topHalf = 1; // m_centerThresh(25) , m_radiusBinSize(10) , m_radiusRatioThresh(50) , m_mergingDistanceThresh(15) , m_mergingRadiusDiffThresh(1.5 * (double) m_radiusBinSize)
111 const double bottomHalf = height - 1;
112 const double leftHalf = width / 4;
113 const double rightHalf = 3 * width / 4;
114
115 // // Disks position when Quarter of circles
116 const double topQuarter = 1; // m_centerThresh(15) , m_radiusBinSize(10) , m_radiusRatioThresh(50) , m_mergingDistanceThresh(15) , m_mergingRadiusDiffThresh(1.5 * (double) m_radiusBinSize)
117 const double bottomQuarter = height - 1;
118 const double leftQuarter = 1;
119 const double rightQuarter = width - 1;
120 vpImage<unsigned char> I_src(height, width, bckg);
121
122 // // Selecting position of the disks depending on their visibility
123 double top, left, bottom, right;
124 switch (inputType) {
125 case FULL_DISKS:
126 top = topFull;
127 left = leftFull;
128 bottom = bottomFull;
129 right = rightFull;
130 break;
131 case HALF_DISKS:
132 top = topHalf;
133 left = leftHalf;
134 bottom = bottomHalf;
135 right = rightHalf;
136 break;
137 case QUARTER_DISKS:
138 top = topQuarter;
139 left = leftQuarter;
140 bottom = bottomQuarter;
141 right = rightQuarter;
142 break;
143 default:
144 throw(vpException(vpException::badValue, "Using other type of input than the one that has been implemented to generate disks."));
145 break;
146 }
147
148 drawDisk(I_src, vpImagePoint(top, left), circleRadius, circleColor, circleColor, circleThickness, bckg);
149 drawDisk(I_src, vpImagePoint(top, left), circleRadius / 2, circleColor / 2, circleColor / 2, circleThickness, circleColor);
150 drawDisk(I_src, vpImagePoint(bottom, left), circleRadius, circleColor, circleColor, circleThickness, bckg);
151 drawDisk(I_src, vpImagePoint(bottom, left), circleRadius / 2, circleColor / 2, circleColor / 2, circleThickness, circleColor);
152 drawDisk(I_src, vpImagePoint(top, right), circleRadius, circleColor, circleColor, circleThickness, bckg);
153 drawDisk(I_src, vpImagePoint(top, right), circleRadius / 2, circleColor / 2, circleColor / 2, circleThickness, circleColor);
154 drawDisk(I_src, vpImagePoint(bottom, right), circleRadius, circleColor, circleColor, circleThickness, bckg);
155 drawDisk(I_src, vpImagePoint(bottom, right), circleRadius / 2, circleColor / 2, circleColor / 2, circleThickness, circleColor);
156
157 std::cout << "Done drawing" << std::endl << std::flush;
158 return I_src;
159}
160
161bool test_detection(const vpImage<unsigned char> &I_src, vpCircleHoughTransform &detector, const int &nbCirclesToDetect, const bool &blockingMode, const bool &displayCanny)
162{
163 double t0 = vpTime::measureTimeMicros();
165 std::vector<vpImageCircle> detectedCircles = detector.detect(I_src, nbCirclesToDetect);
167 double tF = vpTime::measureTimeMicros();
168 std::cout << "Process time = " << (tF - t0) * 0.001 << "ms" << std::endl << std::flush;
170 vpImageConvert::convert(I_src, I_disp);
171
172 unsigned int id = 0;
173 std::vector<vpColor> v_colors = { vpColor::red, vpColor::purple, vpColor::orange, vpColor::yellow, vpColor::blue };
174 unsigned int idColor = 0;
176 for (auto circleCandidate : detectedCircles) {
177 vpImageDraw::drawCircle(I_disp, circleCandidate, v_colors[idColor], 2);
178 std::cout << "Circle #" << id << ":" << std::endl;
179 std::cout << "\tCenter: (" << circleCandidate.getCenter() << ")" << std::endl;
180 std::cout << "\tRadius: (" << circleCandidate.getRadius() << ")" << std::endl;
181 id++;
182 idColor = (idColor + 1) % v_colors.size();
183 }
185
186 if (displayCanny) {
187 vpImage<unsigned char> edgeMap = detector.getEdgeMap();
188 drawingHelpers::display(edgeMap, "Edge map", true);
189 }
190 return drawingHelpers::display(I_disp, "Detection results", blockingMode);
191}
192
193int main(int argc, char **argv)
194{
195 const std::string def_input(typeInputImageToString(FULL_DISKS));
196 const std::string def_jsonFilePath = std::string("");
197 const int def_nbCirclesToDetect = -1;
198 const int def_gaussianKernelSize = 5;
199 const float def_gaussianSigma = 1.f;
200 const int def_sobelKernelSize = 3;
201#ifdef HAVE_OPENCV_IMGPROC
202 const float def_lowerCannyThresh = 50.f;
203 const float def_upperCannyThresh = 150.f;
204#else
205 const float def_lowerCannyThresh = 8.f;
206 const float def_upperCannyThresh = 25.f;
207#endif
208 const int def_nbEdgeFilteringIter = 2;
209 const std::pair<int, int> def_centerXlimits = std::pair<int, int>(0, 640);
210 const std::pair<int, int> def_centerYlimits = std::pair<int, int>(0, 480);
211 const unsigned int def_minRadius = 0;
212 const unsigned int def_maxRadius = 1000;
213 const int def_dilatationRepet = 1;
214 const float def_centerThresh = -1.f;
215 const float def_radiusThreshRatio = -1.f;
216 const float def_circlePerfectness = 0.85f;
217 const float def_centerDistanceThresh = 15.f;
218 const float def_radiusDifferenceThresh = 15.f;
219
220 std::string opt_input(def_input);
221 std::string opt_jsonFilePath = def_jsonFilePath;
222 int opt_nbCirclesToDetect = def_nbCirclesToDetect;
223 int opt_gaussianKernelSize = def_gaussianKernelSize;
224 float opt_gaussianSigma = def_gaussianSigma;
225 int opt_sobelKernelSize = def_sobelKernelSize;
226 float opt_lowerCannyThresh = def_lowerCannyThresh;
227 float opt_upperCannyThresh = def_upperCannyThresh;
228 int opt_nbEdgeFilteringIter = def_nbEdgeFilteringIter;
229 std::pair<int, int> opt_centerXlimits = def_centerXlimits;
230 std::pair<int, int> opt_centerYlimits = def_centerYlimits;
231 unsigned int opt_minRadius = def_minRadius;
232 unsigned int opt_maxRadius = def_maxRadius;
233 int opt_dilatationRepet = def_dilatationRepet;
234 float opt_centerThresh = def_centerThresh;
235 float opt_radiusThreshRatio = def_radiusThreshRatio;
236 float opt_circlePerfectness = def_circlePerfectness;
237 float opt_centerDistanceThresh = def_centerDistanceThresh;
238 float opt_radiusDifferenceThresh = def_radiusDifferenceThresh;
239 bool opt_displayCanny = false;
240
241 for (int i = 1; i < argc; i++) {
242 std::string argName(argv[i]);
243 if (argName == "--input" && i + 1 < argc) {
244 opt_input = std::string(argv[i + 1]);
245 i++;
246 }
247#ifdef VISP_HAVE_NLOHMANN_JSON
248 else if (argName == "--config" && i + 1 < argc) {
249 opt_jsonFilePath = std::string(argv[i + 1]);
250 i++;
251 }
252#endif
253 else if (argName == "--nb-circles" && i + 1 < argc) {
254 opt_nbCirclesToDetect = atoi(argv[i + 1]);
255 i++;
256 }
257 else if (argName == "--gaussian-kernel" && i + 1 < argc) {
258 opt_gaussianKernelSize = atoi(argv[i + 1]);
259 i++;
260 }
261 else if (argName == "--gaussian-sigma" && i + 1 < argc) {
262 opt_gaussianSigma = static_cast<float>(atof(argv[i + 1]));
263 i++;
264 }
265 else if (argName == "--sobel-kernel" && i + 1 < argc) {
266 opt_sobelKernelSize = atoi(argv[i + 1]);
267 i++;
268 }
269 else if (argName == "--canny-thresh" && i + 2 < argc) {
270 opt_lowerCannyThresh = static_cast<float>(atof(argv[i + 1]));
271 opt_upperCannyThresh = static_cast<float>(atof(argv[i + 2]));
272 i += 2;
273 }
274 else if (argName == "--edge-filter" && i + 1 < argc) {
275 opt_nbEdgeFilteringIter = atoi(argv[i + 1]);
276 i++;
277 }
278 else if (argName == "--dilatation-repet" && i + 1 < argc) {
279 opt_dilatationRepet = atoi(argv[i + 1]);
280 i++;
281 }
282 else if (argName == "--radius-limits" && i + 2 < argc) {
283 opt_minRadius = atoi(argv[i + 1]);
284 opt_maxRadius = atoi(argv[i + 2]);
285 i += 2;
286 }
287 else if (argName == "--center-thresh" && i + 1 < argc) {
288 opt_centerThresh = static_cast<float>(atof(argv[i + 1]));
289 i++;
290 }
291 else if (argName == "--center-xlim" && i + 2 < argc) {
292 opt_centerXlimits = std::pair<int, int>(atoi(argv[i + 1]), atoi(argv[i + 2]));
293 i += 2;
294 }
295 else if (argName == "--center-ylim" && i + 2 < argc) {
296 opt_centerYlimits = std::pair<int, int>(atoi(argv[i + 1]), atoi(argv[i + 2]));
297 i += 2;
298 }
299 else if (argName == "--radius-thresh" && i + 1 < argc) {
300 opt_radiusThreshRatio = static_cast<float>(atof(argv[i + 1]));
301 i++;
302 }
303 else if (argName == "--circle-perfectness" && i + 1 < argc) {
304 opt_circlePerfectness = static_cast<float>(atof(argv[i + 1]));
305 i++;
306 }
307 else if (argName == "--merging-thresh" && i + 2 < argc) {
308 opt_centerDistanceThresh = static_cast<float>(atof(argv[i + 1]));
309 opt_radiusDifferenceThresh = static_cast<float>(atof(argv[i + 2]));
310 i += 2;
311 }
312 else if (argName == "--display-edge-map") {
313 opt_displayCanny = true;
314 }
315 else if (argName == "--help" || argName == "-h") {
316 std::cout << "NAME" << std::endl;
317 std::cout << "\t" << argv[0] << " Test program for the home-made Hough Circle Detection algorithm" << std::endl
318 << std::endl;
319 std::cout << "SYNOPSIS" << std::endl;
320 std::cout << "\t" << argv[0]
321 << "\t [--input " << getAvailableTypeInputImage() << "]" << std::endl
322#ifdef VISP_HAVE_NLOHMANN_JSON
323 << "\t [--config <path/to/json/file>] (default: " << (def_jsonFilePath.empty() ? "unused" : def_jsonFilePath) << ")" << std::endl
324#endif
325 << "\t [--nb-circles <number-circles-to-detect>] (default: " << def_nbCirclesToDetect << ")" << std::endl
326 << "\t [--gaussian-kernel <kernel-size>] (default: " << def_gaussianKernelSize << ")" << std::endl
327 << "\t [--gaussian-sigma <stddev>] (default: " << def_gaussianSigma << ")" << std::endl
328 << "\t [--sobel-kernel <kernel-size>] (default: " << def_sobelKernelSize << ")" << std::endl
329 << "\t [--canny-thresh <lower-canny-thresh upper-canny-thresh>] (default: " << def_lowerCannyThresh << " ; " << def_upperCannyThresh << ")" << std::endl
330 << "\t [--edge-filter <nb-iter>] (default: " << def_nbEdgeFilteringIter << ")" << std::endl
331 << "\t [--radius-limits <radius-min> <radius-max>] (default: min = " << def_minRadius << ", max = " << def_maxRadius << ")" << std::endl
332 << "\t [--dilatation-repet <nb-repetitions>] (default: " << def_dilatationRepet << ")" << std::endl
333 << "\t [--center-thresh <center-detection-threshold>] (default: " << (def_centerThresh < 0 ? "auto" : std::to_string(def_centerThresh)) << ")" << std::endl
334 << "\t [--center-xlim <center-horizontal-min center-horizontal-max>] (default: " << def_centerXlimits.first << " , " << def_centerXlimits.second << ")" << std::endl
335 << "\t [--center-ylim <center-vertical-min center-vertical-max>] (default: " << def_centerYlimits.first << " , " << def_centerYlimits.second << ")" << std::endl
336 << "\t [--radius-thresh <radius-detection-threshold>] (default: " << (def_radiusThreshRatio < 0 ? "auto" : std::to_string(def_radiusThreshRatio)) << ")" << std::endl
337 << "\t [--circle-perfectness <circle-perfectness-threshold>] (default: " << def_radiusThreshRatio << ")" << std::endl
338 << "\t [--merging-thresh <center-distance-thresh> <radius-difference-thresh>] (default: centers distance threshold = " << def_centerDistanceThresh << ", radius difference threshold = " << def_radiusDifferenceThresh << ")" << std::endl
339 << "\t [--display-edge-map]" << std::endl
340 << "\t [--help, -h]" << std::endl
341 << std::endl;
342
343 std::cout << "DESCRIPTION" << std::endl
344 << "\t--input" << std::endl
345 << "\t\tPermit to choose the type of input of the Hough Circle Algorithm" << std::endl
346 << "\t\tDefault: " << def_input << std::endl
347 << std::endl
348#ifdef VISP_HAVE_NLOHMANN_JSON
349 << "\t--config" << std::endl
350 << "\t\tPermit to configure the Hough Circle Algorithm using a JSON file." << std::endl
351 << "\t\tDefault: " << (def_jsonFilePath.empty() ? "unused" : def_jsonFilePath) << std::endl
352 << std::endl
353#endif
354 << "\t--nb-circles" << std::endl
355 << "\t\tPermit to choose the number of circles we want to detect in the image" << std::endl
356 << "\t\tThe results will be the circles having the greatest number of votes." << std::endl
357 << "\t\tDefault: " << def_nbCirclesToDetect << std::endl
358 << std::endl
359 << "\t--gaussian-kernel" << std::endl
360 << "\t\tPermit to set the size of the Gaussian filter used to smooth the input image and compute its gradients." << std::endl
361 << "\t\tMust be an odd value." << std::endl
362 << "\t\tDefault: " << def_gaussianKernelSize << std::endl
363 << std::endl
364 << "\t--gaussian-sigma" << std::endl
365 << "\t\tPermit to set the standard deviation of the Gaussian filter." << std::endl
366 << "\t\tMust be a positive value." << std::endl
367 << "\t\tDefault: " << def_gaussianSigma << std::endl
368 << std::endl
369 << "\t--canny-thresh" << std::endl
370 << "\t\tPermit to set the lower and upper thresholds of the Canny edge detector." << std::endl
371 << "\t\tIf a value is negative, it will be automatically computed." << std::endl
372 << "\t\tDefault: " << def_upperCannyThresh << std::endl
373 << std::endl
374 << "\t--edge-filter" << std::endl
375 << "\t\tPermit to set the number of iteration of 8-neighbor filter iterations of the result of the Canny edge detector." << std::endl
376 << "\t\tIf negative, no filtering is performed." << std::endl
377 << "\t\tDefault: " << def_nbEdgeFilteringIter << std::endl
378 << std::endl
379 << "\t--radius-limits" << std::endl
380 << "\t\tPermit to set the minimum and maximum radii of the circles we are looking for." << std::endl
381 << "\t\tDefault: min = " << def_minRadius << ", max = " << def_maxRadius << std::endl
382 << std::endl
383 << "\t--dilatation-repet" << std::endl
384 << "\t\tPermit to set the number of iterations of the dilatation operation used to detect the maxima of the centers votes." << std::endl
385 << "\t\tMinimum tolerated value is 1." << std::endl
386 << "\t\tDefault: " << def_dilatationRepet << std::endl
387 << std::endl
388 << "\t--center-thresh" << std::endl
389 << "\t\tPermit to set the minimum number of votes a point must reach to be considered as a center candidate." << std::endl
390 << "\t\tIf the input is a real image, must be a positive value." << std::endl
391 << "\t\tOtherwise, if the input is a synthetic image and the value is negative, a fine-tuned value will be used." << std::endl
392 << "\t\tDefault: " << (def_centerThresh < 0 ? "auto" : std::to_string(def_centerThresh)) << std::endl
393 << std::endl
394 << "\t--center-xlim" << std::endl
395 << "\t\tPermit to set the minimum and maximum horizontal position to be considered as a center candidate." << std::endl
396 << "\t\tThe search area is limited to [-maxRadius; +image.width + maxRadius]." << std::endl
397 << "\t\tDefault: " << def_centerXlimits.first << " , " << def_centerXlimits.second << std::endl
398 << std::endl
399 << "\t--center-ylim" << std::endl
400 << "\t\tPermit to set the minimum and maximum vertical position to be considered as a center candidate." << std::endl
401 << "\t\tThe search area is limited to [-maxRadius; +image.height + maxRadius]." << std::endl
402 << "\t\tDefault: " << def_centerYlimits.first << " , " << def_centerYlimits.second << std::endl
403 << std::endl
404 << "\t--radius-thresh" << std::endl
405 << "\t\tPermit to to set the minimum number of votes per radian a radius must reach to be considered as a circle candidate a given pair (center candidate, radius candidate)." << std::endl
406 << "\t\tDefault: " << (def_radiusThreshRatio < 0 ? "auto" : std::to_string(def_radiusThreshRatio)) << std::endl
407 << std::endl
408 << "\t--circle-perfectness" << std::endl
409 << "\t\tPermit to set the set the circle perfectness threshold." << std::endl
410 << "\t\tThis parameter is used during the radius candidates computation." << std::endl
411 << "\t\tThe scalar product radius RC_ij . gradient(Ep_j) >= m_circlePerfectness * || RC_ij || * || gradient(Ep_j) || to add a vote for the radius RC_ij." << std::endl
412 << "\t\tDefault: " << def_circlePerfectness << std::endl
413 << std::endl
414 << "\t--merging-thresh" << std::endl
415 << "\t\tPermit to set the thresholds used during the merging stage of the algorithm." << std::endl
416 << "\t\tThe center distance threshold indicates the maximum distance the centers can be in order to be merged." << std::endl
417 << "\t\tThe radius difference threshold indicates the maximum absolute difference between the two circle candidates in order to be merged." << std::endl
418 << "\t\tTwo circle candidates must met these two conditions in order to be merged together." << std::endl
419 << "\t\tDefault: centers distance threshold = " << def_centerDistanceThresh << ", radius difference threshold = " << def_radiusDifferenceThresh << std::endl
420 << "\t--display-edge-map" << std::endl
421 << "\t\tPermit to display the edge map used to detect the circles" << std::endl
422 << "\t\tDefault: off" << std::endl
423 << std::endl;
424 return EXIT_SUCCESS;
425 }
426 }
427
428 if (opt_centerThresh < 0 && opt_jsonFilePath.empty()) {
429 // The user asked to use the parameter value that has been fine-tuned
430 TypeInputImage inputType = typeInputImageFromString(opt_input);
431 switch (inputType) {
432 case TypeInputImage::FULL_DISKS:
433#ifdef HAVE_OPENCV_IMGPROC
434 opt_centerThresh = 100.;
435#else
436 opt_centerThresh = 75.;
437#endif
438 break;
439 case TypeInputImage::HALF_DISKS:
440#ifdef HAVE_OPENCV_IMGPROC
441 opt_centerThresh = 50.;
442#else
443 opt_centerThresh = 25.;
444#endif
445 break;
446 case TypeInputImage::QUARTER_DISKS:
447#ifdef HAVE_OPENCV_IMGPROC
448 opt_centerThresh = 25.;
449#else
450 opt_centerThresh = 15.;
451#endif
452 break;
453 default:
454 throw(vpException(vpException::badValue, "Missing center threshold value to use with actual pictures as input. See the help for more information."));
455 }
456 }
457
458 if (opt_radiusThreshRatio < 0 && opt_jsonFilePath.empty()) {
459 // The user asked to use the parameter value that has been fine-tuned
460 TypeInputImage inputType = typeInputImageFromString(opt_input);
461 switch (inputType) {
462 case TypeInputImage::FULL_DISKS:
463#ifdef HAVE_OPENCV_IMGPROC
464 opt_radiusThreshRatio = 5.;
465#else
466 opt_radiusThreshRatio = 1.;
467#endif
468 break;
469 case TypeInputImage::HALF_DISKS:
470 opt_radiusThreshRatio = 2.;
471 break;
472 case TypeInputImage::QUARTER_DISKS:
473 opt_radiusThreshRatio = 1.;
474 break;
475 default:
476 throw(vpException(vpException::badValue, "Missing radius threshold value to use with actual pictures as input. See the help for more information."));
477 }
478 }
479
482 algoParams(opt_gaussianKernelSize
483 , opt_gaussianSigma
484 , opt_sobelKernelSize
485 , opt_lowerCannyThresh
486 , opt_upperCannyThresh
487 , opt_nbEdgeFilteringIter
488 , opt_centerXlimits
489 , opt_centerYlimits
490 , opt_minRadius
491 , opt_maxRadius
492 , opt_dilatationRepet
493 , opt_centerThresh
494 , opt_radiusThreshRatio
495 , opt_circlePerfectness
496 , opt_centerDistanceThresh
497 , opt_radiusDifferenceThresh
498 );
500
502 vpCircleHoughTransform detector;
503 if (opt_jsonFilePath.empty()) {
504 std::cout << "Initializing detector from the program arguments [...]" << std::endl;
505 detector.init(algoParams);
506 }
507 else {
508#ifdef VISP_HAVE_NLOHMANN_JSON
509 std::cout << "Initializing detector from JSON file \"" << opt_jsonFilePath << "\", some of the program arguments will be ignored [...]" << std::endl;
510 detector.initFromJSON(opt_jsonFilePath);
511#else
512 throw(vpException(vpException::functionNotImplementedError, "You must install nlohmann JSON library to use this feature, see https://visp-doc.inria.fr/doxygen/visp-daily/supported-third-parties.html#soft_tool_json for more information."));
513#endif
514 }
516 std::cout << detector;
517
519 TypeInputImage inputType = typeInputImageFromString(opt_input);
520 if (inputType == USER_IMG) {
522 if (opt_input.find("%") != std::string::npos) {
523 // The user wants to read a sequence of images from different files
524 bool hasToContinue = true;
526 g.setFileName(opt_input);
527 g.open(I_src);
528 while (!g.end() && hasToContinue) {
529 g.acquire(I_src);
530 hasToContinue = test_detection(I_src, detector, opt_nbCirclesToDetect, false, opt_displayCanny);
531 vpTime::wait(40);
532 }
533 }
535 else {
537 // Check if opt_input exists
538 if (!vpIoTools::checkFilename(opt_input)) {
539 throw(vpException(vpException::ioError, "Input file \"" + opt_input + "\" does not exist !"));
540 }
541 // Read the image and perform detection on it
542 vpImageIo::read(I_src, opt_input);
543 test_detection(I_src, detector, opt_nbCirclesToDetect, true, opt_displayCanny);
545 }
546 }
547 else {
549 I_src = generateImage(inputType);
550 test_detection(I_src, detector, opt_nbCirclesToDetect, true, opt_displayCanny);
552 }
553 return EXIT_SUCCESS;
554}
555#else
556int main()
557{
558 std::cout << "This tutorial needs to be build at least with cxx 11 standard!" << std::endl;
559 return EXIT_SUCCESS;
560}
561#endif
Class that permits to detect 2D circles in a image using the gradient-based Circle Hough transform....
vpImage< unsigned char > getEdgeMap()
Get the Edge Map computed thanks to the Canny edge filter.
std::vector< vpImageCircle > detect(const vpImage< vpRGBa > &I)
Convert the input image in a gray-scale image and then perform Circle Hough Transform to detect the c...
void init(const vpCircleHoughTransformParameters &algoParams)
Initialize all the algorithm parameters.
void initFromJSON(const std::string &jsonPath)
Initialize all the algorithm parameters using the JSON file whose path is jsonPath....
static const vpColor red
Definition vpColor.h:211
static const vpColor orange
Definition vpColor.h:221
static const vpColor blue
Definition vpColor.h:217
static const vpColor purple
Definition vpColor.h:222
static const vpColor yellow
Definition vpColor.h:219
error that can be emitted by ViSP classes.
Definition vpException.h:59
@ ioError
I/O error.
Definition vpException.h:79
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:85
@ functionNotImplementedError
Function not implemented.
Definition vpException.h:78
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void drawCircle(vpImage< unsigned char > &I, const vpImageCircle &circle, unsigned char color, unsigned int thickness=1)
static void read(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 ...
Definition of the vpImage class member functions.
Definition vpImage.h:135
static bool checkFilename(const std::string &filename)
Class that enables to manipulate easily a video file or a sequence of images. As it inherits from the...
void acquire(vpImage< vpRGBa > &I)
void open(vpImage< vpRGBa > &I)
void setFileName(const std::string &filename)
VISP_EXPORT void floodFill(vpImage< unsigned char > &I, const vpImagePoint &seedPoint, const unsigned char oldValue, const unsigned char newValue, const vpImageMorphology::vpConnexityType &connexity=vpImageMorphology::CONNEXITY_4)
bool display(vpImage< vpRGBa > &I, const std::string &title, const bool &blockingMode)
vpImage< vpRGBa > I_disp
VISP_EXPORT int wait(double t0, double t)
VISP_EXPORT double measureTimeMicros()