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/vpImageDraw.h>
44#include <visp3/core/vpImageFilter.h>
45#include <visp3/core/vpImagePoint.h>
46#include <visp3/core/vpMatrix.h>
47#include <visp3/core/vpRect.h>
49#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
52#ifdef VISP_HAVE_NLOHMANN_JSON
53#include <nlohmann/json.hpp>
55using json = nlohmann::json;
76 int m_gaussianKernelSize;
77 float m_gaussianStdev;
80 int m_sobelKernelSize;
83 float m_lowerCannyThresh;
85 float m_upperCannyThresh;
87 int m_edgeMapFilteringNbIter;
90 std::pair<int, int> m_centerXlimits;
91 std::pair<int, int> m_centerYlimits;
92 unsigned int m_minRadius;
93 unsigned int m_maxRadius;
94 int m_dilatationNbIter;
98 float m_radiusRatioThresh;
99 float m_circlePerfectness;
102 float m_centerMinDist;
103 float m_mergingRadiusDiffThresh;
111 : m_gaussianKernelSize(5)
112 , m_gaussianStdev(1.f)
113 , m_sobelKernelSize(3)
114 , m_lowerCannyThresh(-1.f)
115 , m_upperCannyThresh(-1.f)
116 , m_edgeMapFilteringNbIter(1)
117 , m_centerXlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
118 , m_centerYlimits(std::pair<int, int>(std::numeric_limits<int>::min(), std::numeric_limits<int>::max()))
121 , m_dilatationNbIter(1)
122 , m_centerThresh(50.f)
123 , m_radiusRatioThresh(2.f)
124 , m_circlePerfectness(0.9f)
125 , m_centerMinDist(15.f)
126 , m_mergingRadiusDiffThresh(1.5f * m_centerMinDist)
154 const int &gaussianKernelSize
155 ,
const float &gaussianStdev
156 ,
const int &sobelKernelSize
157 ,
const float &lowerCannyThresh
158 ,
const float &upperCannyThresh
159 ,
const int &edgeMapFilterNbIter
160 ,
const std::pair<int, int> ¢erXlimits
161 ,
const std::pair<int, int> ¢erYlimits
162 ,
const unsigned int &minRadius
163 ,
const unsigned int &maxRadius
164 ,
const int &dilatationNbIter
165 ,
const float ¢erThresh
166 ,
const float &radiusThreshRatio
167 ,
const float &circlePerfectness
168 ,
const float ¢erMinDistThresh
169 ,
const float &mergingRadiusDiffThresh
171 : m_gaussianKernelSize(gaussianKernelSize)
172 , m_gaussianStdev(gaussianStdev)
173 , m_sobelKernelSize(sobelKernelSize)
174 , m_lowerCannyThresh(lowerCannyThresh)
175 , m_upperCannyThresh(upperCannyThresh)
176 , m_edgeMapFilteringNbIter(edgeMapFilterNbIter)
177 , m_centerXlimits(centerXlimits)
178 , m_centerYlimits(centerYlimits)
179 , m_minRadius(std::min(minRadius, maxRadius))
180 , m_maxRadius(std::max(minRadius, maxRadius))
181 , m_dilatationNbIter(dilatationNbIter)
182 , m_centerThresh(centerThresh)
183 , m_radiusRatioThresh(radiusThreshRatio)
184 , m_circlePerfectness(circlePerfectness)
185 , m_centerMinDist(centerMinDistThresh)
186 , m_mergingRadiusDiffThresh(mergingRadiusDiffThresh)
196 std::string txt(
"Hough Circle Transform Configuration:\n");
197 txt +=
"\tGaussian filter kernel size = " + std::to_string(m_gaussianKernelSize) +
"\n";
198 txt +=
"\tGaussian filter standard deviation = " + std::to_string(m_gaussianStdev) +
"\n";
199 txt +=
"\tSobel filter kernel size = " + std::to_string(m_sobelKernelSize) +
"\n";
200 txt +=
"\tCanny edge filter thresholds = [" + std::to_string(m_lowerCannyThresh) +
" ; " + std::to_string(m_upperCannyThresh) +
"]\n";
201 txt +=
"\tEdge map 8-neighbor connectivity filtering number of iterations = " + std::to_string(m_edgeMapFilteringNbIter) +
"\n";
202 txt +=
"\tCenter horizontal position limits: min = " + std::to_string(m_centerXlimits.first) +
"\tmax = " + std::to_string(m_centerXlimits.second) +
"\n";
203 txt +=
"\tCenter vertical position limits: min = " + std::to_string(m_centerYlimits.first) +
"\tmax = " + std::to_string(m_centerYlimits.second) +
"\n";
204 txt +=
"\tRadius limits: min = " + std::to_string(m_minRadius) +
"\tmax = " + std::to_string(m_maxRadius) +
"\n";
205 txt +=
"\tNumber of repetitions of the dilatation filter = " + std::to_string(m_dilatationNbIter) +
"\n";
206 txt +=
"\tCenters votes threshold = " + std::to_string(m_centerThresh) +
"\n";
207 txt +=
"\tRadius votes per radian threshold = " + std::to_string(m_radiusRatioThresh) +
"\n";
208 txt +=
"\tCircle perfectness threshold = " + std::to_string(m_circlePerfectness) +
"\n";
209 txt +=
"\tCenters minimum distance = " + std::to_string(m_centerMinDist) +
"\n";
210 txt +=
"\tRadius difference merging threshold = " + std::to_string(m_mergingRadiusDiffThresh) +
"\n";
215#ifdef VISP_HAVE_NLOHMANN_JSON
224 std::ifstream file(jsonFile);
226 std::stringstream ss;
227 ss <<
"Problem opening file " << jsonFile <<
". Make sure it exists and is readable" << std::endl;
232 j = json::parse(file);
234 catch (json::parse_error &e) {
235 std::stringstream msg;
236 msg <<
"Could not parse JSON file : \n";
238 msg << e.what() << std::endl;
239 msg <<
"Byte position of error: " << e.byte;
256 std::ofstream file(jsonPath);
257 const json j = *
this;
271 params.m_gaussianKernelSize = j.value(
"gaussianKernelSize", params.m_gaussianKernelSize);
272 if ((params.m_gaussianKernelSize % 2) != 1) {
276 params.m_gaussianStdev = j.value(
"gaussianStdev", params.m_gaussianStdev);
277 if (params.m_gaussianStdev <= 0) {
281 params.m_sobelKernelSize = j.value(
"sobelKernelSize", params.m_sobelKernelSize);
282 if ((params.m_sobelKernelSize % 2) != 1) {
286 params.m_lowerCannyThresh = j.value(
"lowerCannyThresh", params.m_lowerCannyThresh);
287 params.m_upperCannyThresh = j.value(
"upperCannyThresh", params.m_upperCannyThresh);
288 params.m_edgeMapFilteringNbIter = j.value(
"edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter);
290 params.m_centerXlimits = j.value(
"centerXlimits", params.m_centerXlimits);
291 params.m_centerYlimits = j.value(
"centerYlimits", params.m_centerYlimits);
292 std::pair<unsigned int, unsigned int> radiusLimits = j.value(
"radiusLimits", std::pair<unsigned int, unsigned int>(params.m_minRadius, params.m_maxRadius));
293 params.m_minRadius = std::min(radiusLimits.first, radiusLimits.second);
294 params.m_maxRadius = std::max(radiusLimits.first, radiusLimits.second);
296 params.m_dilatationNbIter = j.value(
"dilatationNbIter", params.m_dilatationNbIter);
298 params.m_centerThresh = j.value(
"centerThresh", params.m_centerThresh);
299 if (params.m_centerThresh <= 0) {
303 params.m_radiusRatioThresh = j.value(
"radiusThreshRatio", params.m_radiusRatioThresh);
305 params.m_circlePerfectness = j.value(
"circlePerfectnessThreshold", params.m_circlePerfectness);
307 if (params.m_circlePerfectness <= 0 || params.m_circlePerfectness > 1) {
311 params.m_centerMinDist = j.value(
"centerMinDistance", params.m_centerMinDist);
312 if (params.m_centerMinDist <= 0) {
316 params.m_mergingRadiusDiffThresh = j.value(
"mergingRadiusDiffThresh", params.m_mergingRadiusDiffThresh);
317 if (params.m_mergingRadiusDiffThresh <= 0) {
330 std::pair<unsigned int, unsigned int> radiusLimits = { params.m_minRadius, params.m_maxRadius };
333 {
"gaussianKernelSize", params.m_gaussianKernelSize},
334 {
"gaussianStdev", params.m_gaussianStdev},
335 {
"sobelKernelSize", params.m_sobelKernelSize},
336 {
"lowerCannyThresh", params.m_lowerCannyThresh},
337 {
"upperCannyThresh", params.m_upperCannyThresh},
338 {
"edgeMapFilteringNbIter", params.m_edgeMapFilteringNbIter},
339 {
"centerXlimits", params.m_centerXlimits},
340 {
"centerYlimits", params.m_centerYlimits},
341 {
"radiusLimits", radiusLimits},
342 {
"dilatationNbIter", params.m_dilatationNbIter},
343 {
"centerThresh", params.m_centerThresh},
344 {
"radiusThreshRatio", params.m_radiusRatioThresh},
345 {
"circlePerfectnessThreshold", params.m_circlePerfectness},
346 {
"centerMinDistance", params.m_centerMinDist},
347 {
"mergingRadiusDiffThresh", params.m_mergingRadiusDiffThresh} };
371#ifdef HAVE_OPENCV_CORE
378 std::vector<vpImageCircle> detect(
const cv::Mat &cv_I);
398#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
413#ifdef VISP_HAVE_NLOHMANN_JSON
429 void initFromJSON(
const std::string &jsonPath);
438 void saveConfigurationInJSON(
const std::string &jsonPath)
const;
449 detector.m_algoParams = j;
460 j = detector.m_algoParams;
470 void init(
const vpCircleHoughTransformParameters &algoParams);
481 m_algoParams.m_gaussianKernelSize = kernelSize;
482 m_algoParams.m_gaussianStdev = stdev;
484 if ((m_algoParams.m_gaussianKernelSize % 2) != 1) {
488 if (m_algoParams.m_gaussianStdev <= 0) {
492 initGaussianFilters();
504 inline void setCannyThreshold(
const float &lowerCannyThreshold,
const float &upperCannyThreshold)
506 m_algoParams.m_lowerCannyThresh = lowerCannyThreshold;
507 m_algoParams.m_upperCannyThresh = upperCannyThreshold;
518 m_algoParams.m_centerMinDist = center_min_dist;
520 if (m_algoParams.m_centerMinDist <= 0) {
538 const int ¢er_min_y,
const int ¢er_max_y)
540 m_algoParams.m_centerXlimits.first = center_min_x;
541 m_algoParams.m_centerXlimits.second = center_max_x;
542 m_algoParams.m_centerYlimits.first = center_min_y;
543 m_algoParams.m_centerYlimits.second = center_max_y;
552 m_algoParams.m_minRadius =
static_cast<unsigned int>(circle_min_radius);
561 m_algoParams.m_maxRadius =
static_cast<unsigned int>(circle_max_radius);
570 m_algoParams.m_circlePerfectness = circle_perfectness;
571 if (m_algoParams.m_circlePerfectness <= 0 || m_algoParams.m_circlePerfectness > 1) {
584 m_algoParams.m_dilatationNbIter = dilatationRepet;
585 m_algoParams.m_centerThresh = centerThresh;
587 if (m_algoParams.m_centerThresh <= 0) {
599 m_algoParams.m_radiusRatioThresh = radiusRatioThresh;
601 if (m_algoParams.m_radiusRatioThresh <= 0) {
614 m_algoParams.m_mergingRadiusDiffThresh = radiusDifferenceThresh;
616 if (m_algoParams.m_mergingRadiusDiffThresh <= 0) {
630 return m_centerCandidatesList;
640 return m_centerVotes;
651 return m_circleCandidates;
661 return m_circleCandidatesVotes;
700 return m_algoParams.m_upperCannyThresh;
708 return m_algoParams.m_centerMinDist;
716 return m_algoParams.m_minRadius;
724 return m_algoParams.m_maxRadius;
730 std::string toString()
const;
742 void initGaussianFilters();
764 void filterEdgeMap();
771 void computeCenterCandidates();
781 void computeCircleCandidates();
790 void mergeCircleCandidates();
793 vpCircleHoughTransformParameters m_algoParams;
808 std::vector<std::pair<unsigned int, unsigned int> > m_edgePointsList;
809 std::vector<std::pair<int, int> > m_centerCandidatesList;
810 std::vector<int> m_centerVotes;
813 std::vector<vpImageCircle> m_circleCandidates;
814 std::vector<unsigned int> m_circleCandidatesVotes;
817 std::vector<vpImageCircle> m_finalCircles;
818 std::vector<unsigned int> m_finalCircleVotes;
Implementation of a generic 2D array used as base class for matrices and vectors.
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
Definition of the vpImage class member functions.