31 #ifndef _vpCircleHoughTransform_h_
32 #define _vpCircleHoughTransform_h_
39 #include <visp3/core/vpConfig.h>
40 #include <visp3/core/vpCannyEdgeDetection.h>
41 #include <visp3/core/vpImage.h>
42 #include <visp3/core/vpImageCircle.h>
43 #include <visp3/core/vpMatrix.h>
46 #ifdef VISP_HAVE_NLOHMANN_JSON
47 #include <nlohmann/json.hpp>
50 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
76 int m_gaussianKernelSize;
78 float m_gaussianStdev;
81 int m_gradientFilterKernelSize;
84 float m_lowerCannyThresh;
86 float m_upperCannyThresh;
88 int m_edgeMapFilteringNbIter;
90 float m_lowerCannyThreshRatio;
92 float m_upperCannyThreshRatio;
96 std::pair<int, int> m_centerXlimits;
97 std::pair<int, int> m_centerYlimits;
100 int m_dilatationKernelSize;
101 int m_averagingWindowSize;
103 float m_centerMinThresh;
104 int m_expectedNbCenters;
108 float m_circleProbaThresh;
109 float m_circlePerfectness;
112 float m_circleVisibilityRatioThresh;
113 bool m_recordVotingPoints;
116 float m_centerMinDist;
117 float m_mergingRadiusDiffThresh;
125 : m_filteringAndGradientType(
vpImageFilter::CANNY_GBLUR_SOBEL_FILTERING)
126 , m_gaussianKernelSize(5)
127 , m_gaussianStdev(1.f)
128 , m_gradientFilterKernelSize(3)
129 , m_lowerCannyThresh(-1.f)
130 , m_upperCannyThresh(-1.f)
131 , m_edgeMapFilteringNbIter(1)
133 , m_lowerCannyThreshRatio(0.6f)
134 , m_upperCannyThreshRatio(0.8f)
135 , m_centerXlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
136 , m_centerYlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
138 , m_maxRadius(1000.f)
139 , m_dilatationKernelSize(3)
140 , m_averagingWindowSize(5)
141 , m_centerMinThresh(50.f)
142 , m_expectedNbCenters(-1)
143 , m_circleProbaThresh(0.9f)
144 , m_circlePerfectness(0.9f)
145 , m_circleVisibilityRatioThresh(0.1f)
146 , m_recordVotingPoints(false)
147 , m_centerMinDist(15.f)
148 , m_mergingRadiusDiffThresh(1.5f * m_centerMinDist)
192 const int &gaussianKernelSize
193 ,
const float &gaussianStdev
194 ,
const int &gradientFilterKernelSize
195 ,
const float &lowerCannyThresh
196 ,
const float &upperCannyThresh
197 ,
const int &edgeMapFilterNbIter
198 ,
const std::pair<int, int> ¢erXlimits
199 ,
const std::pair<int, int> ¢erYlimits
200 ,
const float &minRadius
201 ,
const float &maxRadius
202 ,
const int &dilatationKernelSize
203 ,
const int &averagingWindowSize
204 ,
const float ¢erThresh
205 ,
const float &circleProbabilityThresh
206 ,
const float &circlePerfectness
207 ,
const float ¢erMinDistThresh
208 ,
const float &mergingRadiusDiffThresh
211 ,
const float &lowerCannyThreshRatio = 0.6f
212 ,
const float &upperCannyThreshRatio = 0.8f
213 ,
const int &expectedNbCenters = -1
214 ,
const bool &recordVotingPoints =
false
215 ,
const float &visibilityRatioThresh = 0.1f
217 : m_filteringAndGradientType(filteringAndGradientMethod)
218 , m_gaussianKernelSize(gaussianKernelSize)
219 , m_gaussianStdev(gaussianStdev)
220 , m_gradientFilterKernelSize(gradientFilterKernelSize)
221 , m_lowerCannyThresh(lowerCannyThresh)
222 , m_upperCannyThresh(upperCannyThresh)
223 , m_edgeMapFilteringNbIter(edgeMapFilterNbIter)
224 , m_cannyBackendType(backendType)
225 , m_lowerCannyThreshRatio(lowerCannyThreshRatio)
226 , m_upperCannyThreshRatio(upperCannyThreshRatio)
227 , m_centerXlimits(centerXlimits)
228 , m_centerYlimits(centerYlimits)
229 , m_minRadius(std::min<float>(minRadius, maxRadius))
230 , m_maxRadius(std::max<float>(minRadius, maxRadius))
231 , m_dilatationKernelSize(dilatationKernelSize)
232 , m_averagingWindowSize(averagingWindowSize)
233 , m_centerMinThresh(centerThresh)
234 , m_expectedNbCenters(expectedNbCenters)
235 , m_circleProbaThresh(circleProbabilityThresh)
236 , m_circlePerfectness(circlePerfectness)
237 , m_circleVisibilityRatioThresh(visibilityRatioThresh)
238 , m_recordVotingPoints(recordVotingPoints)
239 , m_centerMinDist(centerMinDistThresh)
240 , m_mergingRadiusDiffThresh(mergingRadiusDiffThresh)
250 return m_gaussianKernelSize;
260 return m_gaussianStdev;
270 return m_gradientFilterKernelSize;
281 return m_lowerCannyThresh;
292 return m_upperCannyThresh;
302 return m_edgeMapFilteringNbIter;
312 return m_centerXlimits;
322 return m_centerYlimits;
353 return m_dilatationKernelSize;
364 return m_averagingWindowSize;
374 return m_centerMinThresh;
385 return m_expectedNbCenters;
395 return m_circleProbaThresh;
405 return m_circleVisibilityRatioThresh;
417 return m_circlePerfectness;
427 return m_recordVotingPoints;
437 return m_centerMinDist;
447 return m_mergingRadiusDiffThresh;
455 std::stringstream txt;
456 txt <<
"Hough Circle Transform Configuration:\n";
458 txt <<
"\tGaussian filter kernel size = " << m_gaussianKernelSize <<
"\n";
459 txt <<
"\tGaussian filter standard deviation = " << m_gaussianStdev <<
"\n";
460 txt <<
"\tGradient filter kernel size = " << m_gradientFilterKernelSize <<
"\n";
462 txt <<
"\tCanny edge filter thresholds = [" << m_lowerCannyThresh <<
" ; " << m_upperCannyThresh <<
"]\n";
463 txt <<
"\tCanny edge filter thresholds ratio (for auto-thresholding) = [" << m_lowerCannyThreshRatio <<
" ; " << m_upperCannyThreshRatio <<
"]\n";
464 txt <<
"\tEdge map 8-neighbor connectivity filtering number of iterations = " << m_edgeMapFilteringNbIter <<
"\n";
465 txt <<
"\tCenter horizontal position limits: min = " << m_centerXlimits.first <<
"\tmax = " << m_centerXlimits.second <<
"\n";
466 txt <<
"\tCenter vertical position limits: min = " << m_centerYlimits.first <<
"\tmax = " << m_centerYlimits.second <<
"\n";
467 txt <<
"\tRadius limits: min = " << m_minRadius <<
"\tmax = " << m_maxRadius <<
"\n";
468 txt <<
"\tKernel size of the dilatation filter = " << m_dilatationKernelSize <<
"\n";
469 txt <<
"\tAveraging window size for center detection = " << m_averagingWindowSize <<
"\n";
470 txt <<
"\tCenters votes threshold = " << m_centerMinThresh <<
"\n";
471 txt <<
"\tExpected number of centers = ";
472 if (m_expectedNbCenters > 0) {
473 txt << m_expectedNbCenters;
479 txt <<
"\tCircle probability threshold = " << m_circleProbaThresh <<
"\n";
480 txt <<
"\tCircle visibility ratio threshold = " << m_circleVisibilityRatioThresh <<
"\n";
481 txt <<
"\tCircle perfectness threshold = " << m_circlePerfectness <<
"\n";
482 txt <<
"\tRecord voting points = " + (m_recordVotingPoints ? std::string(
"true") : std::string(
"false")) <<
"\n";
483 txt <<
"\tCenters minimum distance = " << m_centerMinDist <<
"\n";
484 txt <<
"\tRadius difference merging threshold = " << m_mergingRadiusDiffThresh <<
"\n";
489 #ifdef VISP_HAVE_NLOHMANN_JSON
498 using json = nlohmann::json;
500 std::ifstream file(jsonFile);
502 std::stringstream ss;
503 ss <<
"Problem opening file " << jsonFile <<
". Make sure it exists and is readable" << std::endl;
508 j = json::parse(file);
510 catch (json::parse_error &e) {
511 std::stringstream msg;
512 msg <<
"Could not parse JSON file : \n";
514 msg << e.what() << std::endl;
515 msg <<
"Byte position of error: " << e.byte;
532 using json = nlohmann::json;
533 std::ofstream file(jsonPath);
534 const json j = *
this;
549 filteringAndGradientName = j.value(
"filteringAndGradientType", filteringAndGradientName);
552 params.m_gaussianKernelSize = j.value(
"gaussianKernelSize", params.m_gaussianKernelSize);
553 if ((params.m_gaussianKernelSize % 2) != 1) {
557 params.m_gaussianStdev = j.value(
"gaussianStdev", params.m_gaussianStdev);
558 if (params.m_gaussianStdev <= 0) {
562 params.m_gradientFilterKernelSize = j.value(
"gradientFilterKernelSize", params.m_gradientFilterKernelSize);
563 if ((params.m_gradientFilterKernelSize % 2) != 1) {
568 cannyBackendName = j.value(
"cannyBackendType", cannyBackendName);
570 params.m_lowerCannyThresh = j.value(
"lowerCannyThresh", params.m_lowerCannyThresh);
571 params.m_lowerCannyThreshRatio = j.value(
"lowerThresholdRatio", params.m_lowerCannyThreshRatio);
572 params.m_upperCannyThresh = j.value(
"upperCannyThresh", params.m_upperCannyThresh);
573 params.m_upperCannyThreshRatio = j.value(
"upperThresholdRatio", params.m_upperCannyThreshRatio);
574 params.m_edgeMapFilteringNbIter = j.value(
"edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter);
576 params.m_centerXlimits = j.value(
"centerXlimits", params.m_centerXlimits);
577 params.m_centerYlimits = j.value(
"centerYlimits", params.m_centerYlimits);
578 std::pair<float, float> radiusLimits = j.value(
"radiusLimits", std::pair<float, float>(params.m_minRadius, params.m_maxRadius));
579 params.m_minRadius = std::min<float>(radiusLimits.first, radiusLimits.second);
580 params.m_maxRadius = std::max<float>(radiusLimits.first, radiusLimits.second);
582 params.m_dilatationKernelSize = j.value(
"dilatationKernelSize", params.m_dilatationKernelSize);
583 params.m_averagingWindowSize = j.value(
"averagingWindowSize", params.m_averagingWindowSize);
584 if (params.m_averagingWindowSize <= 0 || (params.m_averagingWindowSize % 2) == 0) {
588 params.m_centerMinThresh = j.value(
"centerThresh", params.m_centerMinThresh);
589 if (params.m_centerMinThresh <= 0) {
593 params.m_expectedNbCenters = j.value(
"expectedNbCenters", params.m_expectedNbCenters);
596 params.m_circleProbaThresh = j.value(
"circleProbabilityThreshold", params.m_circleProbaThresh);
597 params.m_circleVisibilityRatioThresh = j.value(
"circleVisibilityRatioThreshold", params.m_circleVisibilityRatioThresh);
599 params.m_circlePerfectness = j.value(
"circlePerfectnessThreshold", params.m_circlePerfectness);
601 if (params.m_circlePerfectness <= 0 || params.m_circlePerfectness > 1) {
605 params.m_recordVotingPoints = j.value(
"recordVotingPoints", params.m_recordVotingPoints);
607 params.m_centerMinDist = j.value(
"centerMinDistance", params.m_centerMinDist);
608 if (params.m_centerMinDist <= 0) {
612 params.m_mergingRadiusDiffThresh = j.value(
"mergingRadiusDiffThresh", params.m_mergingRadiusDiffThresh);
613 if (params.m_mergingRadiusDiffThresh <= 0) {
626 std::pair<float, float> radiusLimits = { params.m_minRadius, params.m_maxRadius };
630 {
"gaussianKernelSize", params.m_gaussianKernelSize},
631 {
"gaussianStdev", params.m_gaussianStdev},
632 {
"gradientFilterKernelSize", params.m_gradientFilterKernelSize},
634 {
"lowerCannyThresh", params.m_lowerCannyThresh},
635 {
"lowerThresholdRatio", params.m_lowerCannyThreshRatio},
636 {
"upperCannyThresh", params.m_upperCannyThresh},
637 {
"upperThresholdRatio", params.m_upperCannyThreshRatio},
638 {
"edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter},
639 {
"centerXlimits", params.m_centerXlimits},
640 {
"centerYlimits", params.m_centerYlimits},
641 {
"radiusLimits", radiusLimits},
642 {
"dilatationKernelSize", params.m_dilatationKernelSize},
643 {
"averagingWindowSize", params.m_averagingWindowSize},
644 {
"centerThresh", params.m_centerMinThresh},
645 {
"expectedNbCenters", params.m_expectedNbCenters},
646 {
"circleProbabilityThreshold", params.m_circleProbaThresh},
647 {
"circleVisibilityRatioThreshold", params.m_circleVisibilityRatioThresh},
648 {
"circlePerfectnessThreshold", params.m_circlePerfectness},
649 {
"recordVotingPoints", params.m_recordVotingPoints},
650 {
"centerMinDistance", params.m_centerMinDist},
651 {
"mergingRadiusDiffThresh", params.m_mergingRadiusDiffThresh} };
675 #ifdef HAVE_OPENCV_CORE
682 virtual std::vector<vpImageCircle>
detect(
const cv::Mat &cv_I);
721 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
723 std::optional<
vpImage<bool> > &mask, std::optional<std::vector<std::vector<std::pair<unsigned int, unsigned int>>>> &opt_votingPoints)
const;
726 vpImage<bool> **mask, std::vector<std::vector<std::pair<unsigned int, unsigned int> > > **opt_votingPoints)
const;
731 #ifdef VISP_HAVE_NLOHMANN_JSON
790 void init(
const vpCircleHoughTransformParameters &algoParams);
836 if ((
m_algoParams.m_gradientFilterKernelSize % 2) != 1) {
862 inline void setCannyThreshold(
const float &lowerCannyThreshold,
const float &upperCannyThreshold)
879 m_algoParams.m_lowerCannyThreshRatio = lowerThreshRatio;
880 m_algoParams.m_upperCannyThreshRatio = upperThreshRatio;
912 const int ¢er_min_y,
const int ¢er_max_y)
964 const int &averagingWindowSize = 5,
const int expectedNbCenters = -1)
968 m_algoParams.m_averagingWindowSize = averagingWindowSize;
974 else if ((
m_algoParams.m_dilatationKernelSize % 2) == 0) {
1009 m_algoParams.m_mergingRadiusDiffThresh = radiusDifferenceThresh;
1247 virtual void mergeCandidates(std::vector<vpImageCircle> &circleCandidates, std::vector<unsigned int> &circleCandidatesVotes,
1248 std::vector<float> &circleCandidatesProba, std::vector<std::vector<std::pair<unsigned int, unsigned int> > > &votingPoints);
void setFilteringAndGradientType(const vpImageFilter::vpCannyFilteringAndGradientType &type)
Set the Filtering And Gradient operators to apply to the image before the edge detection operation.
void setCannyThresholdsRatio(const float &lowerThreshRatio, const float &upperThreshRatio)
Set the lower and upper Canny Thresholds ratio that are used to compute them automatically....
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
Class that defines a 2D circle in an image.
Various image filter, convolution, etc...
static std::string vpCannyBackendTypeToString(const vpCannyBackendType &type)
Cast a vpImageFilter::vpCannyBackendTypeToString into a string, to know its name.
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.
static vpCannyFilteringAndGradientType vpCannyFilteringAndGradientTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyFilteringAndGradientType.
static std::string vpCannyFilteringAndGradientTypeToString(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
vpCannyBackendType
Canny filter backends for the edge detection operations.
@ CANNY_OPENCV_BACKEND
Use OpenCV.
static vpCannyBackendType vpCannyBackendTypeFromString(const std::string &name)
Cast a string into a vpImageFilter::vpCannyBackendTypeToString.