31 #include <visp3/core/vpImageConvert.h>
32 #include <visp3/core/vpImageMorphology.h>
34 #include <visp3/imgproc/vpCircleHoughTransform.h>
38 #ifndef DOXYGEN_SHOULD_SKIP_THIS
41 #if (VISP_CXX_STANDARD == VISP_CXX_STANDARD_98)
42 float computeEffectiveRadius(
const float &votes,
const float &weigthedSumRadius)
44 float r_effective = -1.f;
45 if (votes > std::numeric_limits<float>::epsilon()) {
46 r_effective = weigthedSumRadius / votes;
52 typedef struct vpDataUpdateRadAccum
55 std::pair<float, float> m_centerCandidate;
56 std::pair<unsigned int, unsigned int> m_edgePoint;
61 float m_circlePerfectness2;
64 float m_mergingRadiusDiffThresh;
65 bool m_recordVotingPoints;
71 } vpDataUpdateRadAccum;
74 updateRadiusAccumulator(
const vpDataUpdateRadAccum &data, std::vector<float> &radiusAccumList,
75 std::vector<float> &radiusActualValueList,
76 std::vector<std::vector<std::pair<unsigned int, unsigned int> > > &votingPoints)
78 float gx = data.m_dIx[data.m_edgePoint.first][data.m_edgePoint.second];
79 float gy = data.m_dIy[data.m_edgePoint.first][data.m_edgePoint.second];
80 float grad2 = (gx * gx) + (gy * gy);
81 float scalProd = (data.m_rx * gx) + (data.m_ry * gy);
82 float scalProd2 = scalProd * scalProd;
83 if (scalProd2 >= (data.m_circlePerfectness2 * data.m_r2 * grad2)) {
85 float r =
static_cast<float>(std::sqrt(data.m_r2));
86 int r_bin =
static_cast<int>(std::floor((r - data.m_minRadius) / data.m_mergingRadiusDiffThresh));
87 r_bin = std::min<int>(r_bin, data.m_nbBins - 1);
88 if ((r < (data.m_minRadius + (data.m_mergingRadiusDiffThresh * 0.5f)))
89 || (r >(data.m_minRadius + (data.m_mergingRadiusDiffThresh * (
static_cast<float>(data.m_nbBins - 1) + 0.5f))))) {
91 radiusAccumList[r_bin] += 1.f;
92 radiusActualValueList[r_bin] += r;
93 if (data.m_recordVotingPoints) {
94 votingPoints[r_bin].push_back(data.m_edgePoint);
98 float midRadiusPrevBin = data.m_minRadius + (data.m_mergingRadiusDiffThresh * ((r_bin - 1.f) + 0.5f));
99 float midRadiusCurBin = data.m_minRadius + (data.m_mergingRadiusDiffThresh * (r_bin + 0.5f));
100 float midRadiusNextBin = data.m_minRadius + (data.m_mergingRadiusDiffThresh * (r_bin + 1.f + 0.5f));
102 if ((r >= midRadiusCurBin) && (r <= midRadiusNextBin)) {
104 float voteCurBin = (midRadiusNextBin - r) / data.m_mergingRadiusDiffThresh;
105 float voteNextBin = 1.f - voteCurBin;
106 radiusAccumList[r_bin] += voteCurBin;
107 radiusActualValueList[r_bin] += r * voteCurBin;
108 radiusAccumList[r_bin + 1] += voteNextBin;
109 radiusActualValueList[r_bin + 1] += r * voteNextBin;
110 if (data.m_recordVotingPoints) {
111 votingPoints[r_bin].push_back(data.m_edgePoint);
112 votingPoints[r_bin + 1].push_back(data.m_edgePoint);
117 float votePrevBin = (r - midRadiusPrevBin) / data.m_mergingRadiusDiffThresh;
118 float voteCurBin = 1.f - votePrevBin;
119 radiusAccumList[r_bin] += voteCurBin;
120 radiusActualValueList[r_bin] += r * voteCurBin;
121 radiusAccumList[r_bin - 1] += votePrevBin;
122 radiusActualValueList[r_bin - 1] += r * votePrevBin;
123 if (data.m_recordVotingPoints) {
124 votingPoints[r_bin].push_back(data.m_edgePoint);
125 votingPoints[r_bin - 1].push_back(data.m_edgePoint);
139 nbBins = std::max<int>(
static_cast<int>(1), nbBins);
140 std::vector<float> radiusAccumList;
141 std::vector<float> radiusActualValueList;
148 data.m_nbBins = nbBins;
149 data.m_circlePerfectness2 = circlePerfectness2;
151 data.m_mergingRadiusDiffThresh =
m_algoParams.m_mergingRadiusDiffThresh;
152 data.m_recordVotingPoints =
m_algoParams.m_recordVotingPoints;
154 for (
size_t i = 0; i < nbCenterCandidates; ++i) {
155 std::vector<std::vector<std::pair<unsigned int, unsigned int> > > votingPoints(nbBins);
158 radiusAccumList.clear();
159 radiusAccumList.resize(nbBins, 0);
160 radiusActualValueList.clear();
161 radiusActualValueList.resize(nbBins, 0.);
163 const unsigned int nbEdgePoints =
static_cast<unsigned int>(
m_edgePointsList.size());
164 for (
unsigned int e = 0; e < nbEdgePoints; ++e) {
165 const std::pair<unsigned int, unsigned int> &edgePoint =
m_edgePointsList[e];
168 float rx = edgePoint.second - centerCandidate.second;
169 float ry = edgePoint.first - centerCandidate.first;
170 float r2 = (rx * rx) + (ry * ry);
171 if ((r2 > rmin2) && (r2 < rmax2)) {
172 data.m_centerCandidate = centerCandidate;
173 data.m_edgePoint = edgePoint;
177 updateRadiusAccumulator(data, radiusAccumList, radiusActualValueList, votingPoints);
181 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
183 auto computeEffectiveRadius = [](
const float &votes,
const float &weigthedSumRadius) {
184 float r_effective = -1.f;
185 if (votes > std::numeric_limits<float>::epsilon()) {
186 r_effective = weigthedSumRadius / votes;
193 std::vector<float> v_r_effective;
194 std::vector<float> v_votes_effective;
195 std::vector<std::vector<std::pair<unsigned int, unsigned int> > > v_votingPoints_effective;
196 std::vector<bool> v_hasMerged_effective;
197 for (
int idBin = 0; idBin < nbBins; ++idBin) {
198 float r_effective = computeEffectiveRadius(radiusAccumList[idBin], radiusActualValueList[idBin]);
199 float votes_effective = radiusAccumList[idBin];
200 std::vector<std::pair<unsigned int, unsigned int> > votingPoints_effective = votingPoints[idBin];
201 bool is_r_effective_similar = (r_effective > 0.f);
204 int idCandidate = idBin + 1;
205 bool hasMerged =
false;
206 while ((idCandidate < nbBins) && is_r_effective_similar) {
207 float r_effective_candidate = computeEffectiveRadius(radiusAccumList[idCandidate], radiusActualValueList[idCandidate]);
208 if (std::abs(r_effective_candidate - r_effective) <
m_algoParams.m_mergingRadiusDiffThresh) {
209 r_effective = ((r_effective * votes_effective) + (r_effective_candidate * radiusAccumList[idCandidate])) / (votes_effective + radiusAccumList[idCandidate]);
210 votes_effective += radiusAccumList[idCandidate];
211 radiusAccumList[idCandidate] = -.1f;
212 radiusActualValueList[idCandidate] = -1.f;
213 is_r_effective_similar =
true;
217 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
218 votingPoints_effective.insert(
219 votingPoints_effective.end(),
220 std::make_move_iterator(votingPoints[idCandidate].begin()),
221 std::make_move_iterator(votingPoints[idCandidate].end())
224 votingPoints_effective.insert(
225 votingPoints_effective.end(),
226 votingPoints[idCandidate].begin(),
227 votingPoints[idCandidate].end()
234 is_r_effective_similar =
false;
239 if ((votes_effective >
m_algoParams.m_centerMinThresh) && (votes_effective >= (
m_algoParams.m_circleVisibilityRatioThresh * 2.f * M_PI_FLOAT * r_effective))) {
241 v_r_effective.push_back(r_effective);
242 v_votes_effective.push_back(votes_effective);
244 v_votingPoints_effective.push_back(votingPoints_effective);
245 v_hasMerged_effective.push_back(hasMerged);
250 unsigned int nbCandidates =
static_cast<unsigned int>(v_r_effective.size());
251 for (
unsigned int idBin = 0; idBin < nbCandidates; ++idBin) {
254 float r_effective = v_r_effective[idBin];
262 if (v_hasMerged_effective[idBin]) {
264 std::sort(v_votingPoints_effective[idBin].begin(), v_votingPoints_effective[idBin].end());
265 v_votingPoints_effective[idBin].erase(std::unique(v_votingPoints_effective[idBin].begin(), v_votingPoints_effective[idBin].end()), v_votingPoints_effective[idBin].end());
279 float visibleArc(
static_cast<float>(nbVotes));
280 float theoreticalLenght;
287 if (theoreticalLenght < std::numeric_limits<float>::epsilon()) {
291 proba = std::min(visibleArc / theoreticalLenght, 1.f);
304 mergeCandidates(circleCandidates, circleCandidatesVotes, circleCandidatesProba, circleCandidatesVotingPoints);
307 mergeCandidates(circleCandidates, circleCandidatesVotes, circleCandidatesProba, circleCandidatesVotingPoints);
318 std::vector<float> &circleCandidatesProba, std::vector<std::vector<std::pair<unsigned int, unsigned int> > > &votingPoints)
320 size_t nbCandidates = circleCandidates.size();
322 while (i < nbCandidates) {
324 bool hasPerformedMerge =
false;
327 while (j < nbCandidates) {
332 bool areCirclesSimilar = ((distanceBetweenCenters <
m_algoParams.m_centerMinDist)
333 && (radiusDifference <
m_algoParams.m_mergingRadiusDiffThresh)
336 if (areCirclesSimilar) {
337 hasPerformedMerge =
true;
339 unsigned int totalVotes = circleCandidatesVotes[i] + circleCandidatesVotes[j];
340 float totalProba = circleCandidatesProba[i] + circleCandidatesProba[j];
341 float newProba = 0.5f * totalProba;
342 float newRadius = ((cic_i.
getRadius() * circleCandidatesProba[i]) + (cic_j.
getRadius() * circleCandidatesProba[j])) / totalProba;
345 circleCandidates[j] = circleCandidates[nbCandidates - 1];
346 const unsigned int var2 = 2;
347 circleCandidatesVotes[i] = totalVotes / var2;
348 circleCandidatesVotes[j] = circleCandidatesVotes[nbCandidates - 1];
349 circleCandidatesProba[i] = newProba;
350 circleCandidatesProba[j] = circleCandidatesProba[nbCandidates - 1];
352 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
353 votingPoints[i].insert(
354 votingPoints[i].end(),
355 std::make_move_iterator(votingPoints[j].begin()),
356 std::make_move_iterator(votingPoints[j].end())
359 votingPoints[i].insert(
360 votingPoints[i].end(),
361 votingPoints[j].begin(),
362 votingPoints[j].end()
365 votingPoints.pop_back();
367 circleCandidates.pop_back();
368 circleCandidatesVotes.pop_back();
369 circleCandidatesProba.pop_back();
379 circleCandidates[i] = cic_i;
380 if (hasPerformedMerge &&
m_algoParams.m_recordVotingPoints) {
382 std::sort(votingPoints[i].begin(), votingPoints[i].end());
383 votingPoints[i].erase(std::unique(votingPoints[i].begin(), votingPoints[i].end()), votingPoints[i].end());
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.
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 getHeight() const
Defines a rectangle in the plane.