31 #include <visp3/core/vpImageConvert.h>
32 #include <visp3/core/vpImageMorphology.h>
34 #include <visp3/imgproc/vpCircleHoughTransform.h>
40 #if (VISP_CXX_STANDARD == VISP_CXX_STANDARD_98)
44 bool hasBetterProba(std::pair<size_t, float> a, std::pair<size_t, float> b)
46 return (a.second > b.second);
49 void updateAccumulator(
const float &x_orig,
const float &y_orig,
50 const int &x,
const int &y,
51 const int &offsetX,
const int &offsetY,
52 const int &nbCols,
const int &nbRows,
55 if (((x - offsetX) < 0) ||
56 ((x - offsetX) >= nbCols) ||
57 ((y - offsetY) < 0) ||
58 ((y - offsetY) >= nbRows)
63 float dx = (x_orig -
static_cast<float>(x));
64 float dy = (y_orig -
static_cast<float>(y));
65 accum[y - offsetY][x - offsetX] += std::abs(dx) + std::abs(dy);
69 bool sortingCenters(
const std::pair<std::pair<float, float>,
float> &position_vote_a,
70 const std::pair<std::pair<float, float>,
float> &position_vote_b)
72 return position_vote_a.second > position_vote_b.second;
75 float computeEffectiveRadius(
const float &votes,
const float &weigthedSumRadius)
77 float r_effective = -1.f;
78 if (votes > std::numeric_limits<float>::epsilon()) {
79 r_effective = weigthedSumRadius / votes;
87 unsigned int nbRows = filter.
getRows();
88 unsigned int nbCols = filter.
getCols();
89 for (
unsigned int r = 0; r < nbRows; ++r) {
90 for (
unsigned int c = 0; c < nbCols; ++c) {
91 filter[r][c] = filter[r][c] * scale;
107 : m_algoParams(algoParams)
125 #ifdef VISP_HAVE_NLOHMANN_JSON
126 using json = nlohmann::json;
136 std::ifstream file(jsonPath);
138 std::stringstream ss;
139 ss <<
"Problem opening file " << jsonPath <<
". Make sure it exists and is readable" << std::endl;
144 j = json::parse(file);
146 catch (json::parse_error &e) {
147 std::stringstream msg;
148 msg <<
"Could not parse JSON file : \n";
150 msg << e.what() << std::endl;
151 msg <<
"Byte position of error: " << e.byte;
170 const int filterHalfSize = (
m_algoParams.m_gaussianKernelSize + 1) / 2;
179 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
182 const unsigned int nbRows = filter.
getRows();
183 const unsigned int nbCols = filter.
getCols();
184 for (
unsigned int r = 0; r < nbRows; ++r) {
185 for (
unsigned int c = 0; c < nbCols; ++c) {
186 filter[r][c] = filter[r][c] * scale;
192 if ((
m_algoParams.m_gradientFilterKernelSize % 2) != 1) {
201 unsigned int filterHalfSize = (
m_algoParams.m_gradientFilterKernelSize - 1) / 2;
214 std::string errMsg =
"[vpCircleHoughTransform::initGradientFilters] Error: gradient filtering method \"";
216 errMsg +=
"\" has not been implemented yet\n";
223 std::vector<vpImageCircle>
231 #ifdef HAVE_OPENCV_CORE
232 std::vector<vpImageCircle>
241 std::vector<vpImageCircle>
244 std::vector<vpImageCircle> detections =
detect(I);
245 size_t nbDetections = detections.size();
248 std::vector<std::pair<size_t, float> > v_id_proba;
249 for (
size_t i = 0; i < nbDetections; ++i) {
251 v_id_proba.push_back(id_proba);
254 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
257 = [](std::pair<size_t, float> a, std::pair<size_t, float> b) {
258 return (a.second > b.second);
261 std::sort(v_id_proba.begin(), v_id_proba.end(), hasBetterProba);
267 limitMin = nbDetections;
270 limitMin = std::min(nbDetections,
static_cast<size_t>(nbCircles));
273 std::vector<vpImageCircle> bestCircles;
278 for (
size_t i = 0; i < nbDetections; ++i) {
279 size_t id = v_id_proba[i].first;
294 std::vector<vpImageCircle>
309 const float minRadiusDiff = 3.f;
323 const float minCenterPositionDiff = 3.f;
325 m_algoParams.m_centerXlimits.second +=
static_cast<int>(minCenterPositionDiff / 2.f);
326 m_algoParams.m_centerXlimits.first -=
static_cast<int>(minCenterPositionDiff / 2.f);
329 m_algoParams.m_centerYlimits.second +=
static_cast<int>(minCenterPositionDiff / 2.f);
330 m_algoParams.m_centerYlimits.first -=
static_cast<int>(minCenterPositionDiff / 2.f);
365 bool haveSameCenter = (std::abs(aCenter.
get_u() - bCenter.
get_u())
366 + std::abs(aCenter.
get_v() - bCenter.
get_v())) <= (2. * std::numeric_limits<double>::epsilon());
367 bool haveSameRadius = std::abs(a.
getRadius() - b.
getRadius()) <= (2.f * std::numeric_limits<float>::epsilon());
368 return (haveSameCenter && haveSameRadius);
371 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
373 std::optional<
vpImage<bool> > &mask, std::optional<std::vector<std::vector<std::pair<unsigned int, unsigned int>>>> &opt_votingPoints)
const
376 vpImage<bool> **mask, std::vector<std::vector<std::pair<unsigned int, unsigned int> > > **opt_votingPoints)
const
379 if (!m_algoParams.m_recordVotingPoints) {
381 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
383 opt_votingPoints = std::nullopt;
386 *opt_votingPoints =
nullptr;
391 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
393 opt_votingPoints = std::vector<std::vector<std::pair<unsigned int, unsigned int>>>();
396 *opt_votingPoints =
new std::vector<std::vector<std::pair<unsigned int, unsigned int> > >();
399 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
400 for (
const auto &detection : detections)
402 const size_t nbDetections = detections.size();
403 for (
size_t i = 0; i < nbDetections; ++i)
406 bool hasFoundSimilarCircle =
false;
407 unsigned int nbPreviouslyDetected =
static_cast<unsigned int>(m_finalCircles.size());
410 while ((
id < nbPreviouslyDetected) && (!hasFoundSimilarCircle)) {
412 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
413 if (previouslyDetected == detection)
415 if (previouslyDetected == detections[i])
418 hasFoundSimilarCircle =
true;
420 const unsigned int nbVotingPoints =
static_cast<unsigned int>(m_finalCirclesVotingPoints[id].size());
421 for (
unsigned int idPoint = 0; idPoint < nbVotingPoints; ++idPoint) {
422 const std::pair<unsigned int, unsigned int> &votingPoint = m_finalCirclesVotingPoints[id][idPoint];
423 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
424 (*mask)[votingPoint.first][votingPoint.second] =
true;
426 (**mask)[votingPoint.first][votingPoint.second] =
true;
429 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_17)
430 opt_votingPoints->push_back(m_finalCirclesVotingPoints[
id]);
432 (**opt_votingPoints).push_back(m_finalCirclesVotingPoints[
id]);
455 std::string errMsg(
"[computeGradients] The filtering + gradient operators \"");
457 errMsg +=
"\" is not implemented (yet).";
479 unsigned int nbRows = I_masked.
getHeight();
480 unsigned int nbCols = I_masked.
getWidth();
481 for (
unsigned int r = 0; r < nbRows; ++r) {
482 for (
unsigned int c = 0; c < nbCols; ++c) {
504 for (
int i = 0; i <
m_algoParams.m_edgeMapFilteringNbIter; ++i) {
513 const unsigned int height = J.
getHeight();
514 const unsigned int width = J.
getWidth();
515 const int minNbContiguousPts = 2;
517 for (
unsigned int i = 1; i < (height - 1); ++i) {
518 for (
unsigned int j = 1; j < (width - 1); ++j) {
521 int topLeftPixel =
static_cast<int>(J[i - 1][j - 1]);
522 int topPixel =
static_cast<int>(J[i - 1][j]);
523 int topRightPixel =
static_cast<int>(J[i - 1][j + 1]);
524 int botLeftPixel =
static_cast<int>(J[i + 1][j - 1]);
525 int bottomPixel =
static_cast<int>(J[i + 1][j]);
526 int botRightPixel =
static_cast<int>(J[i + 1][j + 1]);
527 int leftPixel =
static_cast<int>(J[i][j - 1]);
528 int rightPixel =
static_cast<int>(J[i][j + 1]);
529 if ((topLeftPixel + topPixel + topRightPixel
530 + botLeftPixel + bottomPixel + botRightPixel
531 + leftPixel + rightPixel
561 int minimumXposition = std::max<int>(
m_algoParams.m_centerXlimits.first, -1 *
static_cast<int>(
m_algoParams.m_maxRadius));
562 int maximumXposition = std::min<int>(
m_algoParams.m_centerXlimits.second,
static_cast<int>(
m_algoParams.m_maxRadius + nbCols));
563 minimumXposition = std::min<int>(minimumXposition, maximumXposition - 1);
564 float minimumXpositionFloat =
static_cast<float>(minimumXposition);
565 float maximumXpositionFloat =
static_cast<float>(maximumXposition);
566 int offsetX = minimumXposition;
567 int accumulatorWidth = (maximumXposition - minimumXposition) + 1;
568 if (accumulatorWidth <= 0) {
576 int minimumYposition = std::max<int>(
m_algoParams.m_centerYlimits.first, -1 *
static_cast<int>(
m_algoParams.m_maxRadius));
577 int maximumYposition = std::min<int>(
m_algoParams.m_centerYlimits.second,
static_cast<int>(
m_algoParams.m_maxRadius + nbRows));
578 minimumYposition = std::min<int>(minimumYposition, maximumYposition - 1);
579 float minimumYpositionFloat =
static_cast<float>(minimumYposition);
580 float maximumYpositionFloat =
static_cast<float>(maximumYposition);
581 int offsetY = minimumYposition;
582 int accumulatorHeight = (maximumYposition - minimumYposition) + 1;
583 if (accumulatorHeight <= 0) {
584 std::string errMsg(
"[vpCircleHoughTransform::computeCenterCandidates] Accumulator height <= 0!");
588 vpImage<float> centersAccum(accumulatorHeight, accumulatorWidth + 1, 0.);
590 const int nbDirections = 2;
591 for (
unsigned int r = 0; r < nbRows; ++r) {
592 for (
unsigned int c = 0; c < nbCols; ++c) {
598 float sx = 0.f, sy = 0.f;
599 if (std::abs(mag) >= std::numeric_limits<float>::epsilon()) {
600 sx =
m_dIx[r][c] / mag;
601 sy =
m_dIy[r][c] / mag;
610 for (
int k1 = 0; k1 < nbDirections; ++k1) {
611 bool hasToStopLoop =
false;
612 int x_low_prev = std::numeric_limits<int>::max(), y_low_prev, y_high_prev;
613 int x_high_prev = (y_low_prev = (y_high_prev = x_low_prev));
616 float min_minus_c = minimumXpositionFloat -
static_cast<float>(c);
617 float min_minus_r = minimumYpositionFloat -
static_cast<float>(r);
618 float max_minus_c = maximumXpositionFloat -
static_cast<float>(c);
619 float max_minus_r = maximumYpositionFloat -
static_cast<float>(r);
621 float rmin = min_minus_c / sx;
622 rstart = std::max<float>(rmin,
m_algoParams.m_minRadius);
623 float rmax = max_minus_c / sx;
624 rstop = std::min<float>(rmax,
m_algoParams.m_maxRadius);
627 float rmin = max_minus_c / sx;
628 rstart = std::max<float>(rmin,
m_algoParams.m_minRadius);
629 float rmax = min_minus_c / sx;
630 rstop = std::min<float>(rmax,
m_algoParams.m_maxRadius);
634 float rmin = min_minus_r / sy;
635 rstart = std::max<float>(rmin, rstart);
636 float rmax = max_minus_r / sy;
637 rstop = std::min<float>(rmax, rstop);
640 float rmin = max_minus_r / sy;
641 rstart = std::max<float>(rmin, rstart);
642 float rmax = min_minus_r / sy;
643 rstop = std::min<float>(rmax, rstop);
646 float deltar_x = 1.f / std::abs(sx);
647 float deltar_y = 1.f / std::abs(sy);
648 float deltar = std::min<float>(deltar_x, deltar_y);
651 while ((rad <= rstop) && (!hasToStopLoop)) {
652 float x1 =
static_cast<float>(c) + (rad * sx);
653 float y1 =
static_cast<float>(r) + (rad * sy);
656 if ((x1 < minimumXpositionFloat) || (y1 < minimumYpositionFloat)
657 || (x1 > maximumXpositionFloat) || (y1 > maximumYpositionFloat)) {
665 x_low =
static_cast<int>(std::floor(x1));
666 x_high =
static_cast<int>(std::ceil(x1));
669 x_low = -(
static_cast<int>(std::ceil(-x1)));
670 x_high = -(
static_cast<int>(std::floor(-x1)));
674 y_low =
static_cast<int>(std::floor(y1));
675 y_high =
static_cast<int>(std::ceil(y1));
678 y_low = -(
static_cast<int>(std::ceil(-1. * y1)));
679 y_high = -(
static_cast<int>(std::floor(-1. * y1)));
682 if ((x_low_prev == x_low) && (x_high_prev == x_high)
683 && (y_low_prev == y_low) && (y_high_prev == y_high)) {
689 x_high_prev = x_high;
691 y_high_prev = y_high;
694 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
695 auto updateAccumulator =
696 [](
const float &x_orig,
const float &y_orig,
697 const int &x,
const int &y,
698 const int &offsetX,
const int &offsetY,
699 const int &nbCols,
const int &nbRows,
701 if (((x - offsetX) < 0) ||
702 ((x - offsetX) >= nbCols) ||
703 ((y - offsetY) < 0) ||
704 ((y - offsetY) >= nbRows)
709 float dx = (x_orig -
static_cast<float>(x));
710 float dy = (y_orig -
static_cast<float>(y));
711 accum[y - offsetY][x - offsetX] += std::abs(dx) + std::abs(dy);
716 updateAccumulator(x1, y1, x_low, y_low,
718 accumulatorWidth, accumulatorHeight,
719 centersAccum, hasToStopLoop
722 updateAccumulator(x1, y1, x_high, y_high,
724 accumulatorWidth, accumulatorHeight,
725 centersAccum, hasToStopLoop
739 int dilatationKernelSize = std::max<int>(
m_algoParams.m_dilatationKernelSize, 3);
745 int nbColsAccum = centersAccum.
getCols();
746 int nbRowsAccum = centersAccum.
getRows();
748 std::vector<std::pair<std::pair<float, float>,
float> > peak_positions_votes;
750 for (
int y = 0; y < nbRowsAccum; ++y) {
752 for (
int x = 0; x < nbColsAccum; ++x) {
753 if ((centersAccum[y][x] >=
m_algoParams.m_centerMinThresh)
754 && (
vpMath::equal(centersAccum[y][x], centerCandidatesMaxima[y][x]))
755 && (centersAccum[y][x] > centersAccum[y][x + 1])
760 nbVotes = std::max<int>(nbVotes,
static_cast<int>(centersAccum[y][x]));
762 else if (left >= 0) {
763 int cx =
static_cast<int>(((left + x) - 1) * 0.5f);
765 float x_avg = 0., y_avg = 0.;
766 int averagingWindowHalfSize =
m_algoParams.m_averagingWindowSize / 2;
767 int startingRow = std::max<int>(0, y - averagingWindowHalfSize);
768 int startingCol = std::max<int>(0, cx - averagingWindowHalfSize);
769 int endRow = std::min<int>(accumulatorHeight, y + averagingWindowHalfSize + 1);
770 int endCol = std::min<int>(accumulatorWidth, cx + averagingWindowHalfSize + 1);
771 for (
int r = startingRow; r < endRow; ++r) {
772 for (
int c = startingCol; c < endCol; ++c) {
773 sumVotes += centersAccum[r][c];
774 x_avg += centersAccum[r][c] * c;
775 y_avg += centersAccum[r][c] * r;
778 float avgVotes = sumVotes /
static_cast<float>(
m_algoParams.m_averagingWindowSize *
m_algoParams.m_averagingWindowSize);
780 x_avg /=
static_cast<float>(sumVotes);
781 y_avg /=
static_cast<float>(sumVotes);
782 std::pair<float, float> position(y_avg +
static_cast<float>(offsetY), x_avg +
static_cast<float>(offsetX));
783 std::pair<std::pair<float, float>,
float> position_vote(position, avgVotes);
784 peak_positions_votes.push_back(position_vote);
787 std::stringstream errMsg;
788 errMsg <<
"nbVotes (" << nbVotes <<
") < 0, thresh = " <<
m_algoParams.m_centerMinThresh;
797 unsigned int nbPeaks =
static_cast<unsigned int>(peak_positions_votes.size());
799 std::vector<bool> has_been_merged(nbPeaks,
false);
800 std::vector<std::pair<std::pair<float, float>,
float> > merged_peaks_position_votes;
802 for (
unsigned int idPeak = 0; idPeak < nbPeaks; ++idPeak) {
803 float votes = peak_positions_votes[idPeak].second;
804 if (has_been_merged[idPeak]) {
810 has_been_merged[idPeak] =
true;
813 std::pair<float, float> position = peak_positions_votes[idPeak].first;
814 std::pair<float, float> barycenter;
815 barycenter.first = position.first * peak_positions_votes[idPeak].second;
816 barycenter.second = position.second * peak_positions_votes[idPeak].second;
817 float total_votes = peak_positions_votes[idPeak].second;
818 float nb_electors = 1.f;
820 for (
unsigned int idCandidate = idPeak + 1; idCandidate < nbPeaks; ++idCandidate) {
821 float votes_candidate = peak_positions_votes[idCandidate].second;
822 if (has_been_merged[idCandidate]) {
825 else if (votes_candidate <
m_algoParams.m_centerMinThresh) {
827 has_been_merged[idCandidate] =
true;
831 std::pair<float, float> position_candidate = peak_positions_votes[idCandidate].first;
832 float squared_distance = ((position.first - position_candidate.first) * (position.first - position_candidate.first))
833 + ((position.second - position_candidate.second) * (position.second - position_candidate.second));
836 if (squared_distance < squared_distance_max) {
837 barycenter.first += position_candidate.first * votes_candidate;
838 barycenter.second += position_candidate.second * votes_candidate;
839 total_votes += votes_candidate;
841 has_been_merged[idCandidate] =
true;
845 float avg_votes = total_votes / nb_electors;
848 barycenter.first /= total_votes;
849 barycenter.second /= total_votes;
850 std::pair<std::pair<float, float>,
float> barycenter_votes(barycenter, avg_votes);
851 merged_peaks_position_votes.push_back(barycenter_votes);
855 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
856 auto sortingCenters = [](
const std::pair<std::pair<float, float>,
float> &position_vote_a,
857 const std::pair<std::pair<float, float>,
float> &position_vote_b) {
858 return position_vote_a.second > position_vote_b.second;
862 std::sort(merged_peaks_position_votes.begin(), merged_peaks_position_votes.end(), sortingCenters);
864 nbPeaks =
static_cast<unsigned int>(merged_peaks_position_votes.size());
865 int nbPeaksToKeep = (
m_algoParams.m_expectedNbCenters > 0 ?
m_algoParams.m_expectedNbCenters :
static_cast<int>(nbPeaks));
866 nbPeaksToKeep = std::min<int>(nbPeaksToKeep,
static_cast<int>(nbPeaks));
867 for (
int i = 0; i < nbPeaksToKeep; ++i) {
869 m_centerVotes.push_back(
static_cast<int>(merged_peaks_position_votes[i].second));
879 nbBins = std::max<int>(
static_cast<int>(1), nbBins);
880 std::vector<float> radiusAccumList;
881 std::vector<float> radiusActualValueList;
882 std::vector<std::vector<std::pair<unsigned int, unsigned int> > > votingPoints(nbBins);
888 for (
size_t i = 0; i < nbCenterCandidates; ++i) {
891 radiusAccumList.clear();
892 radiusAccumList.resize(nbBins, 0);
893 radiusActualValueList.clear();
894 radiusActualValueList.resize(nbBins, 0.);
896 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
903 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
904 float rx = edgePoint.second - centerCandidate.second;
905 float ry = edgePoint.first - centerCandidate.first;
910 float r2 = (rx * rx) + (ry * ry);
911 if ((r2 > rmin2) && (r2 < rmax2)) {
912 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
913 float gx =
m_dIx[edgePoint.first][edgePoint.second];
914 float gy =
m_dIy[edgePoint.first][edgePoint.second];
919 float grad2 = (gx * gx) + (gy * gy);
921 float scalProd = (rx * gx) + (ry * gy);
922 float scalProd2 = scalProd * scalProd;
923 if (scalProd2 >= (circlePerfectness2 * r2 * grad2)) {
925 float r =
static_cast<float>(std::sqrt(r2));
927 r_bin = std::min<int>(r_bin, nbBins - 1);
929 || (r >(
m_algoParams.m_minRadius + (
m_algoParams.m_mergingRadiusDiffThresh * (
static_cast<float>(nbBins - 1) + 0.5f))))) {
931 radiusAccumList[r_bin] += 1.f;
932 radiusActualValueList[r_bin] += r;
934 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
935 votingPoints[r_bin].push_back(edgePoint);
942 float midRadiusPrevBin =
m_algoParams.m_minRadius + (
m_algoParams.m_mergingRadiusDiffThresh * ((r_bin - 1.f) + 0.5f));
944 float midRadiusNextBin =
m_algoParams.m_minRadius + (
m_algoParams.m_mergingRadiusDiffThresh * (r_bin + 1.f + 0.5f));
946 if ((r >= midRadiusCurBin) && (r <= midRadiusNextBin)) {
948 float voteCurBin = (midRadiusNextBin - r) /
m_algoParams.m_mergingRadiusDiffThresh;
949 float voteNextBin = 1.f - voteCurBin;
950 radiusAccumList[r_bin] += voteCurBin;
951 radiusActualValueList[r_bin] += r * voteCurBin;
952 radiusAccumList[r_bin + 1] += voteNextBin;
953 radiusActualValueList[r_bin + 1] += r * voteNextBin;
955 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
956 votingPoints[r_bin].push_back(edgePoint);
957 votingPoints[r_bin + 1].push_back(edgePoint);
966 float votePrevBin = (r - midRadiusPrevBin) /
m_algoParams.m_mergingRadiusDiffThresh;
967 float voteCurBin = 1.f - votePrevBin;
968 radiusAccumList[r_bin] += voteCurBin;
969 radiusActualValueList[r_bin] += r * voteCurBin;
970 radiusAccumList[r_bin - 1] += votePrevBin;
971 radiusActualValueList[r_bin - 1] += r * votePrevBin;
973 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
974 votingPoints[r_bin].push_back(edgePoint);
975 votingPoints[r_bin - 1].push_back(edgePoint);
987 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
989 auto computeEffectiveRadius = [](
const float &votes,
const float &weigthedSumRadius) {
990 float r_effective = -1.f;
991 if (votes > std::numeric_limits<float>::epsilon()) {
992 r_effective = weigthedSumRadius / votes;
999 std::vector<float> v_r_effective;
1000 std::vector<float> v_votes_effective;
1001 std::vector<std::vector<std::pair<unsigned int, unsigned int> > > v_votingPoints_effective;
1002 std::vector<bool> v_hasMerged_effective;
1003 for (
int idBin = 0; idBin < nbBins; ++idBin) {
1004 float r_effective = computeEffectiveRadius(radiusAccumList[idBin], radiusActualValueList[idBin]);
1005 float votes_effective = radiusAccumList[idBin];
1006 std::vector<std::pair<unsigned int, unsigned int> > votingPoints_effective = votingPoints[idBin];
1007 bool is_r_effective_similar = (r_effective > 0.f);
1010 int idCandidate = idBin + 1;
1011 bool hasMerged =
false;
1012 while ((idCandidate < nbBins) && is_r_effective_similar) {
1013 float r_effective_candidate = computeEffectiveRadius(radiusAccumList[idCandidate], radiusActualValueList[idCandidate]);
1014 if (std::abs(r_effective_candidate - r_effective) <
m_algoParams.m_mergingRadiusDiffThresh) {
1015 r_effective = ((r_effective * votes_effective) + (r_effective_candidate * radiusAccumList[idCandidate])) / (votes_effective + radiusAccumList[idCandidate]);
1016 votes_effective += radiusAccumList[idCandidate];
1017 radiusAccumList[idCandidate] = -.1f;
1018 radiusActualValueList[idCandidate] = -1.f;
1019 is_r_effective_similar =
true;
1023 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
1024 votingPoints_effective.insert(
1025 votingPoints_effective.end(),
1026 std::make_move_iterator(votingPoints[idCandidate].begin()),
1027 std::make_move_iterator(votingPoints[idCandidate].end())
1030 votingPoints_effective.insert(
1031 votingPoints_effective.end(),
1032 votingPoints[idCandidate].begin(),
1033 votingPoints[idCandidate].end()
1040 is_r_effective_similar =
false;
1045 if ((votes_effective >
m_algoParams.m_centerMinThresh) && (votes_effective >= (
m_algoParams.m_circleVisibilityRatioThresh * 2.f * M_PIf * r_effective))) {
1047 v_r_effective.push_back(r_effective);
1048 v_votes_effective.push_back(votes_effective);
1050 v_votingPoints_effective.push_back(votingPoints_effective);
1051 v_hasMerged_effective.push_back(hasMerged);
1056 unsigned int nbCandidates =
static_cast<unsigned int>(v_r_effective.size());
1057 for (
unsigned int idBin = 0; idBin < nbCandidates; ++idBin) {
1060 float r_effective = v_r_effective[idBin];
1068 if (v_hasMerged_effective[idBin]) {
1070 std::sort(v_votingPoints_effective[idBin].begin(), v_votingPoints_effective[idBin].end());
1071 v_votingPoints_effective[idBin].erase(std::unique(v_votingPoints_effective[idBin].begin(), v_votingPoints_effective[idBin].end()), v_votingPoints_effective[idBin].end());
1085 float visibleArc(
static_cast<float>(nbVotes));
1086 float theoreticalLenght;
1093 if (theoreticalLenght < std::numeric_limits<float>::epsilon()) {
1097 proba = std::min(visibleArc / theoreticalLenght, 1.f);
1110 mergeCandidates(circleCandidates, circleCandidatesVotes, circleCandidatesProba, circleCandidatesVotingPoints);
1113 mergeCandidates(circleCandidates, circleCandidatesVotes, circleCandidatesProba, circleCandidatesVotingPoints);
1124 std::vector<float> &circleCandidatesProba, std::vector<std::vector<std::pair<unsigned int, unsigned int> > > &votingPoints)
1126 size_t nbCandidates = circleCandidates.size();
1128 while (i < nbCandidates) {
1130 bool hasPerformedMerge =
false;
1133 while (j < nbCandidates) {
1138 bool areCirclesSimilar = ((distanceBetweenCenters <
m_algoParams.m_centerMinDist)
1139 && (radiusDifference <
m_algoParams.m_mergingRadiusDiffThresh)
1142 if (areCirclesSimilar) {
1143 hasPerformedMerge =
true;
1145 unsigned int totalVotes = circleCandidatesVotes[i] + circleCandidatesVotes[j];
1146 float totalProba = circleCandidatesProba[i] + circleCandidatesProba[j];
1147 float newProba = 0.5f * totalProba;
1148 float newRadius = ((cic_i.
getRadius() * circleCandidatesProba[i]) + (cic_j.
getRadius() * circleCandidatesProba[j])) / totalProba;
1151 circleCandidates[j] = circleCandidates[nbCandidates - 1];
1152 circleCandidatesVotes[i] = totalVotes / 2;
1153 circleCandidatesVotes[j] = circleCandidatesVotes[nbCandidates - 1];
1154 circleCandidatesProba[i] = newProba;
1155 circleCandidatesProba[j] = circleCandidatesProba[nbCandidates - 1];
1157 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
1158 votingPoints[i].insert(
1159 votingPoints[i].end(),
1160 std::make_move_iterator(votingPoints[j].begin()),
1161 std::make_move_iterator(votingPoints[j].end())
1164 votingPoints[i].insert(
1165 votingPoints[i].end(),
1166 votingPoints[j].begin(),
1167 votingPoints[j].end()
1170 votingPoints.pop_back();
1172 circleCandidates.pop_back();
1173 circleCandidatesVotes.pop_back();
1174 circleCandidatesProba.pop_back();
1184 circleCandidates[i] = cic_i;
1185 if (hasPerformedMerge &&
m_algoParams.m_recordVotingPoints) {
1187 std::sort(votingPoints[i].begin(), votingPoints[i].end());
1188 votingPoints[i].erase(std::unique(votingPoints[i].begin(), votingPoints[i].end()), votingPoints[i].end());
bool operator==(const vpArray2D< double > &A) const
unsigned int getCols() const
Type * data
Address of the first element of the data array.
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
friend std::ostream & operator<<(std::ostream &s, const vpArray2D< Type > &A)
unsigned int getRows() const
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 setFilteringAndGradientType(const vpImageFilter::vpCannyFilteringAndGradientType &type)
Set the Filtering And Gradient operators to apply to the image before the edge detection operation.
void setMask(const vpImage< bool > *p_mask)
Set a mask to ignore pixels for which the mask is false.
void setCannyThresholdsRatio(const float &lowerThreshRatio, const float &upperThreshRatio)
Set the lower and upper Canny Thresholds ratio that are used to compute them automatically....
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 setGradientFilterAperture(const unsigned int &apertureSize)
Set the parameters of the gradient filter (Sobel or Scharr) kernel size filters.
void setGaussianFilterParameters(const int &kernelSize, const float &stdev)
Set the Gaussian Filters kernel size and standard deviation and initialize the aforementioned filters...
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
@ dimensionError
Bad dimension.
@ notImplementedError
Not implemented.
Class that defines a 2D circle in an image.
vpImagePoint getCenter() const
float computeArcLengthInRoI(const vpRect &roi, const float &roundingTolerance=0.001f) const
unsigned int computePixelsInMask(const vpImage< bool > &mask) const
Count the number of pixels of the circle whose value in the mask is true.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
static void canny(const vpImage< unsigned char > &I, vpImage< unsigned char > &Ic, const unsigned int &gaussianFilterSize, const float &thresholdCanny, const unsigned int &apertureSobel)
static FilterType getSobelKernelX(FilterType *filter, unsigned int size)
@ 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 filter(const vpImage< ImageType > &I, vpImage< FilterType > &If, const vpArray2D< FilterType > &M, bool convolve=false, const vpImage< bool > *p_mask=nullptr)
static std::string vpCannyFilteringAndGradientTypeToString(const vpCannyFilteringAndGradientType &type)
Cast a vpImageFilter::vpCannyFilteringAndGradientType into a string, to know its name.
@ CANNY_VISP_BACKEND
Use ViSP.
static void getGaussianKernel(FilterType *filter, unsigned int size, FilterType sigma=0., bool normalize=true)
static FilterType getScharrKernelY(FilterType *filter, unsigned int size)
static FilterType getSobelKernelY(FilterType *filter, unsigned int size)
static void filterY(const vpImage< vpRGBa > &I, vpImage< vpRGBa > &dIx, const double *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
static FilterType getScharrKernelX(FilterType *filter, unsigned int size)
static void filterX(const vpImage< ImageType > &I, vpImage< FilterType > &dIx, const FilterType *filter, unsigned int size, const vpImage< bool > *p_mask=nullptr)
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)
unsigned int getWidth() const
unsigned int getCols() const
unsigned int getHeight() const
unsigned int getRows() const
static bool equal(double x, double y, double threshold=0.001)
Defines a rectangle in the plane.