Visual Servoing Platform version 3.6.0
Loading...
Searching...
No Matches
vpCircleHoughTransform.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 */
30
31#include <visp3/core/vpImageConvert.h>
32#include <visp3/core/vpImageFilter.h>
33#include <visp3/core/vpImageMorphology.h>
34
35#include <visp3/imgproc/vpCircleHoughTransform.h>
36
37#if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
39 : m_algoParams()
40{
41 initGaussianFilters();
42}
43
45 : m_algoParams(algoParams)
46{
47 initGaussianFilters();
48}
49
50void
52{
53 m_algoParams = algoParams;
54 initGaussianFilters();
55}
56
59
60#ifdef VISP_HAVE_NLOHMANN_JSON
62{
63 initFromJSON(jsonPath);
64}
65
66void
67vpCircleHoughTransform::initFromJSON(const std::string &jsonPath)
68{
69 std::ifstream file(jsonPath);
70 if (!file.good()) {
71 std::stringstream ss;
72 ss << "Problem opening file " << jsonPath << ". Make sure it exists and is readable" << std::endl;
73 throw vpException(vpException::ioError, ss.str());
74 }
75 json j;
76 try {
77 j = json::parse(file);
78 }
79 catch (json::parse_error &e) {
80 std::stringstream msg;
81 msg << "Could not parse JSON file : \n";
82
83 msg << e.what() << std::endl;
84 msg << "Byte position of error: " << e.byte;
85 throw vpException(vpException::ioError, msg.str());
86 }
87 m_algoParams = j; // Call from_json(const json& j, vpDetectorDNN& *this) to read json
88 file.close();
89 initGaussianFilters();
90}
91
92void
93vpCircleHoughTransform::saveConfigurationInJSON(const std::string &jsonPath) const
94{
95 m_algoParams.saveConfigurationInJSON(jsonPath);
96}
97#endif
98
99void
100vpCircleHoughTransform::initGaussianFilters()
101{
102 m_fg.resize(1, (m_algoParams.m_gaussianKernelSize + 1)/2);
103 vpImageFilter::getGaussianKernel(m_fg.data, m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev, false);
104 m_fgDg.resize(1, (m_algoParams.m_gaussianKernelSize + 1)/2);
105 vpImageFilter::getGaussianDerivativeKernel(m_fgDg.data, m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev, false);
106 m_cannyVisp.setGaussianFilterParameters(m_algoParams.m_gaussianKernelSize, m_algoParams.m_gaussianStdev);
107}
108
109std::vector<vpImageCircle>
111{
113 vpImageConvert::convert(I, I_gray);
114 return detect(I_gray);
115}
116
117#ifdef HAVE_OPENCV_CORE
118std::vector<vpImageCircle>
119vpCircleHoughTransform::detect(const cv::Mat &cv_I)
120{
122 vpImageConvert::convert(cv_I, I_gray);
123 return detect(I_gray);
124}
125#endif
126
127std::vector<vpImageCircle>
129{
130 std::vector<vpImageCircle> detections = detect(I);
131 size_t nbDetections = detections.size();
132 std::vector<vpImageCircle> bestCircles;
133 std::vector<std::pair<vpImageCircle, unsigned int> > detectionsWithVotes;
134 for (size_t i = 0; i < nbDetections; i++) {
135 std::pair<vpImageCircle, unsigned int> detectionWithVote(detections[i], m_finalCircleVotes[i]);
136 detectionsWithVotes.push_back(detectionWithVote);
137 }
138
139 bool (*hasMoreVotes)(std::pair<vpImageCircle, unsigned int>, std::pair<vpImageCircle, unsigned int>)
140 = [](std::pair<vpImageCircle, unsigned int> a, std::pair<vpImageCircle, unsigned int> b) {
141 // We divide the number of votes by the radius to avoid to favour big circles
142 return (a.second / a.first.getRadius() > b.second / b.first.getRadius());
143 };
144
145 std::sort(detectionsWithVotes.begin(), detectionsWithVotes.end(), hasMoreVotes);
146
147 size_t limitMin;
148 if (nbCircles < 0) {
149 limitMin = nbDetections;
150 }
151 else {
152 limitMin = std::min(nbDetections, (size_t)nbCircles);
153 }
154 for (size_t i = 0; i < limitMin; i++) {
155 bestCircles.push_back(detectionsWithVotes[i].first);
156 }
157
158 return bestCircles;
159}
160
161std::vector<vpImageCircle>
163{
164 // Cleaning results of potential previous detection
165 m_centerCandidatesList.clear();
166 m_centerVotes.clear();
167 m_edgePointsList.clear();
168 m_circleCandidates.clear();
169 m_circleCandidatesVotes.clear();
170 m_finalCircles.clear();
171 m_finalCircleVotes.clear();
172
173 // First thing, we need to apply a Gaussian filter on the image to remove some spurious noise
174 // Then, we need to compute the image gradients in order to be able to perform edge detection
175 computeGradientsAfterGaussianSmoothing(I);
176
177 // Using the gradients, it is now possible to perform edge detection
178 // We rely on the Canny edge detector
179 // It will also give us the connected edged points
180 edgeDetection(I);
181
182 // From the edge map and gradient information, it is possible to compute
183 // the center point candidates
184 computeCenterCandidates();
185
186 // From the edge map and center point candidates, we can compute candidate
187 // circles. These candidate circles are circles whose center belong to
188 // the center point candidates and whose radius is a "radius bin" that got
189 // enough votes by computing the distance between each point of the edge map
190 // and the center point candidate
191 computeCircleCandidates();
192
193 // Finally, we perform a merging operation that permits to merge circles
194 // respecting similarity criteria (distance between centers and similar radius)
195 mergeCircleCandidates();
196
197 return m_finalCircles;
198}
199
200void
201vpCircleHoughTransform::computeGradientsAfterGaussianSmoothing(const vpImage<unsigned char> &I)
202{
204 m_dIx,
205 m_fg.data,
206 m_fgDg.data,
207 m_algoParams.m_gaussianKernelSize
208 );
210 m_dIy,
211 m_fg.data,
212 m_fgDg.data,
213 m_algoParams.m_gaussianKernelSize
214 );
215}
216
217void
218vpCircleHoughTransform::edgeDetection(const vpImage<unsigned char> &I)
219{
220#if defined(HAVE_OPENCV_IMGPROC)
221 float upperCannyThresh = m_algoParams.m_upperCannyThresh;
222 float lowerCannyThresh = m_algoParams.m_lowerCannyThresh;
223 // Apply the Canny edge operator to compute the edge map
224 // The canny method performs Gaussian blur and gradient computation
225 if (m_algoParams.m_upperCannyThresh < 0.) {
226 upperCannyThresh = vpImageFilter::computeCannyThreshold(I, lowerCannyThresh);
227 }
228 else if (m_algoParams.m_lowerCannyThresh < 0) {
229 lowerCannyThresh = upperCannyThresh / 3.f;
230 }
231 vpImageFilter::canny(I, m_edgeMap, m_algoParams.m_gaussianKernelSize, lowerCannyThresh, upperCannyThresh, m_algoParams.m_sobelKernelSize);
232#else
233 m_cannyVisp.setCannyThresholds(m_algoParams.m_lowerCannyThresh, m_algoParams.m_upperCannyThresh);
234 m_cannyVisp.setGradients(m_dIx, m_dIy);
235 m_edgeMap = m_cannyVisp.detect(I);
236#endif
237
238 for (int i = 0; i < m_algoParams.m_edgeMapFilteringNbIter; i++) {
239 filterEdgeMap();
240 }
241}
242
243void
244vpCircleHoughTransform::filterEdgeMap()
245{
246 vpImage<unsigned char> J = m_edgeMap;
247
248 for (unsigned int i = 1; i < J.getHeight() - 1; i++) {
249 for (unsigned int j = 1; j < J.getWidth() - 1; j++) {
250 if (J[i][j] == 255) {
251 // Consider 8 neighbors
252 int topLeftPixel = (int)J[i - 1][j - 1];
253 int topPixel = (int)J[i - 1][j];
254 int topRightPixel = (int)J[i - 1][j + 1];
255 int botLeftPixel = (int)J[i + 1][j - 1];
256 int bottomPixel = (int)J[i + 1][j];
257 int botRightPixel = (int)J[i + 1][j + 1];
258 int leftPixel = (int)J[i][j - 1];
259 int rightPixel = (int)J[i][j + 1];
260 if ((topLeftPixel + topPixel + topRightPixel
261 + botLeftPixel + bottomPixel + botRightPixel
262 + leftPixel + rightPixel
263 ) >= 2 * 255) {
264 // At least 2 of the 8-neighbor points are also an edge point
265 // so we keep the edge point
266 m_edgeMap[i][j] = 255;
267 }
268 else {
269 // The edge point is isolated => we erase it
270 m_edgeMap[i][j] = 0;
271 }
272 }
273 }
274 }
275}
276
277void
278vpCircleHoughTransform::computeCenterCandidates()
279{
280 // For each edge point EP_i, check the image gradient at EP_i
281 // Then, for each image point in the direction of the gradient,
282 // increment the accumulator
283 // We can perform bilinear interpolation in order not to vote for a "line" of
284 // points, but for an "area" of points
285 unsigned int nbRows = m_edgeMap.getRows();
286 unsigned int nbCols = m_edgeMap.getCols();
287
288 m_algoParams.m_maxRadius = std::min(m_algoParams.m_maxRadius, std::min(nbCols, nbRows));
289
290 // Computing the minimum and maximum horizontal position of the center candidates
291 // The miminum horizontal position of the center is at worst -maxRadius outside the image
292 // The maxinum horizontal position of the center is at worst +maxRadiusoutside the image
293 // The width of the accumulator is the difference between the max and the min
294 int minimumXposition = std::max(m_algoParams.m_centerXlimits.first, -1 * (int)m_algoParams.m_maxRadius);
295 int maximumXposition = std::min(m_algoParams.m_centerXlimits.second, (int)(m_algoParams.m_maxRadius + nbCols));
296 minimumXposition = std::min(minimumXposition, maximumXposition - 1);
297 float minimumXpositionFloat = static_cast<float>(minimumXposition);
298 int offsetX = minimumXposition;
299 int accumulatorWidth = maximumXposition - minimumXposition + 1;
300 if (accumulatorWidth <= 0) {
301 throw(vpException(vpException::dimensionError, "[vpCircleHoughTransform::computeCenterCandidates] Accumulator width <= 0!"));
302 }
303
304 // Computing the minimum and maximum vertical position of the center candidates
305 // The miminum vertical position of the center is at worst -maxRadius outside the image
306 // The maxinum vertical position of the center is at worst +maxRadiusoutside the image
307 // The height of the accumulator is the difference between the max and the min
308 int minimumYposition = std::max(m_algoParams.m_centerYlimits.first, -1 * (int)m_algoParams.m_maxRadius);
309 int maximumYposition = std::min(m_algoParams.m_centerYlimits.second, (int)(m_algoParams.m_maxRadius + nbRows));
310 minimumYposition = std::min(minimumYposition, maximumYposition - 1);
311 float minimumYpositionFloat = static_cast<float>(minimumYposition);
312 int offsetY = minimumYposition;
313 int accumulatorHeight = maximumYposition - minimumYposition + 1;
314 if (accumulatorHeight <= 0) {
315 throw(vpException(vpException::dimensionError, "[vpCircleHoughTransform::computeCenterCandidates] Accumulator height <= 0!"));
316 }
317
318 vpImage<float> centersAccum(accumulatorHeight, accumulatorWidth + 1, 0.);
320 for (unsigned int r = 0; r < nbRows; r++) {
321 for (unsigned int c = 0; c < nbCols; c++) {
322 if (m_edgeMap[r][c] == 255) {
323 // Saving the edge point for further use
324 m_edgePointsList.push_back(std::pair<unsigned int, unsigned int>(r, c));
325
326 // Voting for points in both direction of the gradient
327 // Step from min_radius to max_radius in both directions of the gradient
328 float mag = std::sqrt(m_dIx[r][c] * m_dIx[r][c] + m_dIy[r][c] * m_dIy[r][c]);
329
330 float sx = m_dIx[r][c] / mag;
331 float sy = m_dIy[r][c] / mag;
332
333 int int_minRad = (int)m_algoParams.m_minRadius;
334 int int_maxRad = (int)m_algoParams.m_maxRadius;
335
336 for (int k1 = 0; k1 < 2; k1++) {
337 bool hasToStopLoop = false;
338 for (int rad = int_minRad; rad <= int_maxRad && !hasToStopLoop; rad++) {
339 float x1 = (float)c + (float)rad * sx;
340 float y1 = (float)r + (float)rad * sy;
341
342 if (x1 < minimumXpositionFloat || y1 < minimumYpositionFloat) {
343 continue; // If either value is lower than maxRadius, it means that the center is outside the search region.
344 }
345
346 int x_low, x_high;
347 int y_low, y_high;
348
349 if (x1 > 0.) {
350 x_low = static_cast<int>(std::floor(x1));
351 x_high = static_cast<int>(std::ceil(x1));
352 }
353 else {
354 x_low = -(static_cast<int>(std::ceil(-x1)));
355 x_high = -(static_cast<int>(std::floor(-x1)));
356 }
357
358 if (y1 > 0.) {
359 y_low = static_cast<int>(std::floor(y1));
360 y_high = static_cast<int>(std::ceil(y1));
361 }
362 else {
363 y_low = -(static_cast<int>(std::ceil(-1. * y1)));
364 y_high = -(static_cast<int>(std::floor(-1. * y1)));
365 }
366
367 auto updateAccumulator =
368 [](const float &x_orig, const float &y_orig,
369 const unsigned int &x, const unsigned int &y,
370 const int &offsetX, const int &offsetY,
371 const unsigned int &nbCols, const unsigned int &nbRows,
372 vpImage<float> &accum, bool &hasToStop) {
373 if (x - offsetX >= nbCols ||
374 y - offsetY >= nbRows
375 ) {
376 hasToStop = true;
377 }
378 else {
379 float dx = (x_orig - (float)x);
380 float dy = (y_orig - (float)y);
381 accum[y - offsetY][x - offsetX] += std::abs(dx) + std::abs(dy);
382 }
383 };
384
385 updateAccumulator(x1, y1, x_low, y_low,
386 offsetX, offsetY,
387 accumulatorWidth, accumulatorHeight,
388 centersAccum, hasToStopLoop
389 );
390
391 updateAccumulator(x1, y1, x_high, y_high,
392 offsetX, offsetY,
393 accumulatorWidth, accumulatorHeight,
394 centersAccum, hasToStopLoop
395 );
396 }
397
398 sx = -sx;
399 sy = -sy;
400 }
401 }
402 }
403 }
404
405 // Use dilatation with large kernel in order to determine the
406 // accumulator maxima
407 vpImage<float> centerCandidatesMaxima = centersAccum;
408 int niters = std::max(m_algoParams.m_dilatationNbIter, 1); // Ensure at least one dilatation operation
409 for (int i = 0; i < niters; i++) {
411 }
412
413 // Look for the image points that correspond to the accumulator maxima
414 // These points will become the center candidates
415 // find the possible circle centers
416 int nbColsAccum = centersAccum.getCols();
417 int nbRowsAccum = centersAccum.getRows();
418 int nbVotes = -1;
419 for (int y = 0; y < nbRowsAccum; y++) {
420 int left = -1;
421 for (int x = 0; x < nbColsAccum; x++) {
422 if (centersAccum[y][x] >= m_algoParams.m_centerThresh
423 && centersAccum[y][x] == centerCandidatesMaxima[y][x]
424 && centersAccum[y][x] > centersAccum[y][x + 1]
425 ) {
426 if (left < 0)
427 left = x;
428 nbVotes = std::max(nbVotes, (int)centersAccum[y][x]);
429 }
430 else if (left >= 0) {
431 int cx = (int)((left + x - 1) * 0.5f);
432 m_centerCandidatesList.push_back(std::pair<int, int>(y + offsetY, cx + offsetX));
433 m_centerVotes.push_back(nbVotes);
434 left = -1;
435 nbVotes = -1;
436 }
437 }
438 }
439}
440
441void
442vpCircleHoughTransform::computeCircleCandidates()
443{
444 size_t nbCenterCandidates = m_centerCandidatesList.size();
445 unsigned int nbBins = static_cast<unsigned int>((m_algoParams.m_maxRadius - m_algoParams.m_minRadius + 1)/ m_algoParams.m_centerMinDist);
446 nbBins = std::max((unsigned int)1, nbBins); // Avoid having 0 bins, which causes segfault
447 std::vector<unsigned int> radiusAccumList;
448 std::vector<float> radiusActualValueList;
450 unsigned int rmin2 = m_algoParams.m_minRadius * m_algoParams.m_minRadius;
451 unsigned int rmax2 = static_cast<unsigned int>(m_algoParams.m_maxRadius * m_algoParams.m_maxRadius);
452 int circlePerfectness2 = static_cast<int>(m_algoParams.m_circlePerfectness * m_algoParams.m_circlePerfectness);
453
454 for (size_t i = 0; i < nbCenterCandidates; i++) {
455 std::pair<int, int> centerCandidate = m_centerCandidatesList[i];
456 // Initialize the radius accumulator of the candidate with 0s
457 radiusAccumList.clear();
458 radiusAccumList.resize(nbBins, 0);
459 radiusActualValueList.clear();
460 radiusActualValueList.resize(nbBins, 0.);
461
462 for (auto edgePoint : m_edgePointsList) {
463 // For each center candidate CeC_i, compute the distance with each edge point EP_j d_ij = dist(CeC_i; EP_j)
464 unsigned int rx = edgePoint.first - centerCandidate.first;
465 unsigned int ry = edgePoint.second - centerCandidate.second;
466 unsigned int r2 = rx * rx + ry * ry;
467
468 if ((r2 > rmin2) && (r2 < rmax2)) {
469 float gx = m_dIx[edgePoint.first][edgePoint.second];
470 float gy = m_dIy[edgePoint.first][edgePoint.second];
471 float grad2 = gx * gx + gy * gy;
472
473 float scalProd = rx * gx + ry * gy;
474 float scalProd2 = scalProd * scalProd;
475 if (scalProd2 >= circlePerfectness2 * r2 * grad2) {
476 // Look for the Radius Candidate Bin RCB_k to which d_ij is "the closest" will have an additionnal vote
477 float r = static_cast<float>(std::sqrt(r2));
478 unsigned int r_bin = static_cast<unsigned int>(std::ceil((r - m_algoParams.m_minRadius)/ m_algoParams.m_centerMinDist));
479 r_bin = std::min(r_bin, nbBins - 1);
480 radiusAccumList[r_bin]++;
481 radiusActualValueList[r_bin] += r;
482 }
483 }
484 }
485
486 for (unsigned int idBin = 0; idBin < nbBins; idBin++) {
487 // If the circle of center CeC_i and radius RCB_k has enough votes, it is added to the list
488 // of Circle Candidates
489 float r_effective = radiusActualValueList[idBin] / (float)radiusAccumList[idBin];
490 if ((float)radiusAccumList[idBin] / r_effective > m_algoParams.m_radiusRatioThresh) {
491 m_circleCandidates.push_back(vpImageCircle(vpImagePoint(centerCandidate.first, centerCandidate.second)
492 , r_effective
493 )
494 );
495 m_circleCandidatesVotes.push_back(radiusAccumList[idBin]);
496 }
497 }
498 }
499}
500
501void
502vpCircleHoughTransform::mergeCircleCandidates()
503{
504 // For each circle candidate CiC_i do:
505 std::vector<vpImageCircle> circleCandidates = m_circleCandidates;
506 std::vector<unsigned int> circleCandidatesVotes = m_circleCandidatesVotes;
507 size_t nbCandidates = m_circleCandidates.size();
508 for (size_t i = 0; i < nbCandidates; i++) {
509 vpImageCircle cic_i = circleCandidates[i];
510 // // For each other circle candidate CiC_j do:
511 for (size_t j = i + 1; j < nbCandidates; j++) {
512 vpImageCircle cic_j = circleCandidates[j];
513 // // // Compute the similarity between CiC_i and CiC_j
514 double distanceBetweenCenters = vpImagePoint::distance(cic_i.getCenter(), cic_j.getCenter());
515 double radiusDifference = std::abs(cic_i.getRadius() - cic_j.getRadius());
516 bool areCirclesSimilar = (distanceBetweenCenters < m_algoParams.m_centerMinDist
517 && radiusDifference < m_algoParams.m_mergingRadiusDiffThresh
518 );
519
520 if (areCirclesSimilar) {
521 // // // If the similarity exceeds a threshold, merge the circle candidates CiC_i and CiC_j and remove CiC_j of the list
522 unsigned int totalVotes = circleCandidatesVotes[i] + circleCandidatesVotes[j];
523 float newRadius = (cic_i.getRadius() * circleCandidatesVotes[i] + cic_j.getRadius() * circleCandidatesVotes[j]) / totalVotes;
524 vpImagePoint newCenter = (cic_i.getCenter() * circleCandidatesVotes[i]+ cic_j.getCenter() * circleCandidatesVotes[j]) / totalVotes;
525 cic_i = vpImageCircle(newCenter, newRadius);
526 circleCandidates[j] = circleCandidates[nbCandidates - 1];
527 circleCandidatesVotes[i] = totalVotes;
528 circleCandidatesVotes[j] = circleCandidatesVotes[nbCandidates - 1];
529 circleCandidates.pop_back();
530 circleCandidatesVotes.pop_back();
531 nbCandidates--;
532 j--;
533 }
534 }
535 // // Add the circle candidate CiC_i, potentially merged with other circle candidates, to the final list of detected circles
536 m_finalCircles.push_back(cic_i);
537 }
538
539 nbCandidates = m_finalCircles.size();
540 for (size_t i = 0; i < nbCandidates; i++) {
541 vpImageCircle cic_i = m_finalCircles[i];
542 // // For each other circle candidate CiC_j do:
543 for (size_t j = i + 1; j < nbCandidates; j++) {
544 vpImageCircle cic_j = m_finalCircles[j];
545 // // // Compute the similarity between CiC_i and CiC_j
546 double distanceBetweenCenters = vpImagePoint::distance(cic_i.getCenter(), cic_j.getCenter());
547 double radiusDifference = std::abs(cic_i.getRadius() - cic_j.getRadius());
548 bool areCirclesSimilar = (distanceBetweenCenters < m_algoParams.m_centerMinDist
549 && radiusDifference < m_algoParams.m_mergingRadiusDiffThresh
550 );
551
552 if (areCirclesSimilar) {
553 // // // If the similarity exceeds a threshold, merge the circle candidates CiC_i and CiC_j and remove CiC_j of the list
554 unsigned int totalVotes = circleCandidatesVotes[i] + circleCandidatesVotes[j];
555 vpImagePoint newCenter = (cic_i.getCenter() * circleCandidatesVotes[i]+ cic_j.getCenter() * circleCandidatesVotes[j]) / totalVotes;
556 float newRadius = (cic_i.getRadius() * circleCandidatesVotes[i] + cic_j.getRadius() * circleCandidatesVotes[j]) / totalVotes;
557 cic_i = vpImageCircle(newCenter, newRadius);
558 m_finalCircles[j] = m_finalCircles[nbCandidates - 1];
559 circleCandidatesVotes[i] = totalVotes;
560 circleCandidatesVotes[j] = circleCandidatesVotes[nbCandidates - 1];
561 m_finalCircles.pop_back();
562 circleCandidatesVotes.pop_back();
563 nbCandidates--;
564 j--;
565 }
566 }
567 // // Add the circle candidate CiC_i, potentially merged with other circle candidates, to the final list of detected circles
568 m_finalCircles[i] = cic_i;
569 }
570 m_finalCircleVotes = circleCandidatesVotes;
571}
572
573std::string
575{
576 return m_algoParams.toString();
577}
578
579std::ostream &operator<<(std::ostream &os, const vpCircleHoughTransform &detector)
580{
581 os << detector.toString();
582 return os;
583}
584
585#endif
Type * data
Address of the first element of the data array.
Definition vpArray2D.h:144
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:305
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.
void setCannyThresholds(const float &lowerThresh, const float &upperThresh)
Set the lower and upper Canny Thresholds used to qualify the edge point candidates....
void setGaussianFilterParameters(const int &kernelSize, const float &stdev)
Set the Gaussian Filters kernel size and standard deviation and initialize the aforementioned filters...
void saveConfigurationInJSON(const std::string &jsonPath) const
Save the configuration of the detector in a JSON file described by the path jsonPath....
Class that permits to detect 2D circles in a image using the gradient-based Circle Hough transform....
vpCircleHoughTransform()
Construct a new vpCircleHoughTransform object with default parameters.
virtual ~vpCircleHoughTransform()
Destroy the vp Circle Hough Transform object.
void saveConfigurationInJSON(const std::string &jsonPath) const
Save the configuration of the detector in a JSON file described by the path jsonPath....
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....
error that can be emitted by ViSP classes.
Definition vpException.h:59
@ ioError
I/O error.
Definition vpException.h:79
@ dimensionError
Bad dimension.
Definition vpException.h:83
Class that defines a 2D circle in an image.
float getRadius() const
vpImagePoint getCenter() const
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static float computeCannyThreshold(const cv::Mat &cv_I, const cv::Mat *p_cv_blur, float &lowerThresh)
Compute the upper Canny edge filter threshold.
static void getGradYGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIy, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size)
static void getGradXGauss2D(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *gaussianKernel, const FilterType *gaussianDerivativeKernel, unsigned int size)
static void getGaussianDerivativeKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static void canny(const vpImage< unsigned char > &I, vpImage< unsigned char > &Ic, unsigned int gaussianFilterSize, float thresholdCanny, unsigned int apertureSobel)
static void dilatation(vpImage< Type > &I, Type value, Type value_out, vpConnexityType connexity=CONNEXITY_4)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
static double distance(const vpImagePoint &iP1, const vpImagePoint &iP2)
Definition of the vpImage class member functions.
Definition vpImage.h:135
unsigned int getWidth() const
Definition vpImage.h:242
unsigned int getCols() const
Definition vpImage.h:175
unsigned int getHeight() const
Definition vpImage.h:184
unsigned int getRows() const
Definition vpImage.h:214