Loading modules/aruco/src/aruco.cpp +135 −148 Original line number Diff line number Diff line Loading @@ -212,17 +212,20 @@ static void _reorderCandidatesCorners(vector< vector< Point2f > > &candidates) { /** * @brief Check candidates that are too close to each other and remove the smaller one * @brief Check candidates that are too close to each other, save the potential candidates * (i.e. biggest/smallest contour) and remove the rest */ static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candidatesIn, vector< vector< Point2f > > &candidatesOut, vector< vector< vector< Point2f > > > &candidatesSetOut, const vector< vector< Point > > &contoursIn, vector< vector< Point > > &contoursOut, double minMarkerDistanceRate) { vector< vector< vector< Point > > > &contoursSetOut, double minMarkerDistanceRate, bool detectInvertedMarker) { CV_Assert(minMarkerDistanceRate >= 0); vector< pair< int, int > > nearCandidates; vector<int> candGroup; candGroup.resize(candidatesIn.size(), -1); vector< vector<unsigned int> > groupedCandidates; for(unsigned int i = 0; i < candidatesIn.size(); i++) { for(unsigned int j = i + 1; j < candidatesIn.size(); j++) { Loading @@ -244,40 +247,87 @@ static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candida // if mean square distance is too low, remove the smaller one of the two markers double minMarkerDistancePixels = double(minimumPerimeter) * minMarkerDistanceRate; if(distSq < minMarkerDistancePixels * minMarkerDistancePixels) { nearCandidates.push_back(pair< int, int >(i, j)); break; // i and j are not related to a group if(candGroup[i]<0 && candGroup[j]<0){ // mark candidates with their corresponding group number candGroup[i] = candGroup[j] = (int)groupedCandidates.size(); // create group vector<unsigned int> grouped; grouped.push_back(i); grouped.push_back(j); groupedCandidates.push_back( grouped ); } // i is related to a group else if(candGroup[i] > -1 && candGroup[j] == -1){ int group = candGroup[i]; candGroup[j] = group; // add to group groupedCandidates[group].push_back( j ); } // j is related to a group else if(candGroup[j] > -1 && candGroup[i] == -1){ int group = candGroup[j]; candGroup[i] = group; // add to group groupedCandidates[group].push_back( i ); } } } } } // mark smaller one in pairs to remove vector< bool > toRemove(candidatesIn.size(), false); for(unsigned int i = 0; i < nearCandidates.size(); i++) { // if one of the marker has been already markerd to removed, dont need to do anything if(toRemove[nearCandidates[i].first] || toRemove[nearCandidates[i].second]) continue; size_t perimeter1 = contoursIn[nearCandidates[i].first].size(); size_t perimeter2 = contoursIn[nearCandidates[i].second].size(); if(perimeter1 > perimeter2) toRemove[nearCandidates[i].second] = true; else toRemove[nearCandidates[i].first] = true; // save possible candidates candidatesSetOut.clear(); contoursSetOut.clear(); vector< vector< Point2f > > biggerCandidates; vector< vector< Point > > biggerContours; vector< vector< Point2f > > smallerCandidates; vector< vector< Point > > smallerContours; // save possible candidates for( unsigned int i = 0; i < groupedCandidates.size(); i++ ) { int smallerIdx = groupedCandidates[i][0]; int biggerIdx = -1; // evaluate group elements for( unsigned int j = 1; j < groupedCandidates[i].size(); j++ ) { size_t currPerim = contoursIn[ groupedCandidates[i][j] ].size(); // check if current contour is bigger if ( biggerIdx < 0 ) biggerIdx = groupedCandidates[i][j]; else if(currPerim >= contoursIn[ biggerIdx ].size()) biggerIdx = groupedCandidates[i][j]; // check if current contour is smaller if(currPerim < contoursIn[ smallerIdx ].size() && detectInvertedMarker) smallerIdx = groupedCandidates[i][j]; } // add contours und candidates if(biggerIdx > -1){ // remove extra candidates candidatesOut.clear(); unsigned long totalRemaining = 0; for(unsigned int i = 0; i < toRemove.size(); i++) if(!toRemove[i]) totalRemaining++; candidatesOut.resize(totalRemaining); contoursOut.resize(totalRemaining); for(unsigned int i = 0, currIdx = 0; i < candidatesIn.size(); i++) { if(toRemove[i]) continue; candidatesOut[currIdx] = candidatesIn[i]; contoursOut[currIdx] = contoursIn[i]; currIdx++; biggerCandidates.push_back(candidatesIn[biggerIdx]); biggerContours.push_back(contoursIn[biggerIdx]); if( detectInvertedMarker ){ smallerCandidates.push_back(candidatesIn[smallerIdx]); smallerContours.push_back(contoursIn[smallerIdx]); } } } // to preserve the structure :: candidateSet< defaultCandidates, whiteCandidates > // default candidates candidatesSetOut.push_back(biggerCandidates); contoursSetOut.push_back(biggerContours); // white candidates candidatesSetOut.push_back(smallerCandidates); contoursSetOut.push_back(smallerContours); } /** Loading Loading @@ -370,8 +420,8 @@ static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > /** * @brief Detect square candidates in the input image */ static void _detectCandidates(InputArray _image, vector< vector< Point2f > >& candidatesOut, vector< vector< Point > >& contoursOut, const Ptr<DetectorParameters> &_params) { static void _detectCandidates(InputArray _image, vector< vector< vector< Point2f > > >& candidatesSetOut, vector< vector< vector< Point > > >& contoursSetOut, const Ptr<DetectorParameters> &_params) { Mat image = _image.getMat(); CV_Assert(image.total() != 0); Loading @@ -389,8 +439,9 @@ static void _detectCandidates(InputArray _image, vector< vector< Point2f > >& ca _reorderCandidatesCorners(candidates); /// 4. FILTER OUT NEAR CANDIDATE PAIRS _filterTooCloseCandidates(candidates, candidatesOut, contours, contoursOut, _params->minMarkerDistanceRate); // save the outter/inner border (i.e. potential candidates) _filterTooCloseCandidates(candidates, candidatesSetOut, contours, contoursSetOut, _params->minMarkerDistanceRate, _params->detectInvertedMarker); } Loading Loading @@ -493,8 +544,11 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { /** * @brief Tries to identify one candidate given the dictionary * @return candidate typ. zero if the candidate is not valid, * 1 if the candidate is a black candidate (default candidate) * 2 if the candidate is a white candidate */ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray _image, static uint8_t _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray _image, vector<Point2f>& _corners, int& idx, const Ptr<DetectorParameters>& params) { Loading @@ -502,6 +556,7 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray CV_Assert(_image.getMat().total() != 0); CV_Assert(params->markerBorderBits > 0); uint8_t typ=1; // get bits Mat candidateBits = _extractBits(_image, _corners, dictionary->markerSize, params->markerBorderBits, Loading @@ -523,9 +578,10 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray if(invBError<borderErrors){ borderErrors = invBError; invertedImg.copyTo(candidateBits); typ=2; } } if(borderErrors > maximumErrorsInBorder) return false; if(borderErrors > maximumErrorsInBorder) return 0; // border is wrong // take only inner bits Mat onlyBits = Loading @@ -536,13 +592,13 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray // try to indentify the marker int rotation; if(!dictionary->identify(onlyBits, idx, rotation, params->errorCorrectionRate)) return false; return 0; // shift corner positions to the correct rotation if(rotation != 0) { std::rotate(_corners.begin(), _corners.begin() + 4 - rotation, _corners.end()); } return true; return typ; } Loading @@ -554,22 +610,24 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { public: IdentifyCandidatesParallel(const Mat& _grey, vector< vector< Point2f > >& _candidates, const Ptr<Dictionary> &_dictionary, vector< int >& _idsTmp, vector< char >& _validCandidates, vector< int >& _idsTmp, vector< uint8_t >& _validCandidates, const Ptr<DetectorParameters> &_params) : grey(_grey), candidates(_candidates), dictionary(_dictionary), idsTmp(_idsTmp), validCandidates(_validCandidates), params(_params) {} void operator()(const Range &range) const CV_OVERRIDE { void operator()(const Range &range) const CV_OVERRIDE { const int begin = range.start; const int end = range.end; for(int i = begin; i < end; i++) { int currId; if(_identifyOneCandidate(dictionary, grey, candidates[i], currId, params)) { validCandidates[i] = 1; validCandidates[i] = _identifyOneCandidate(dictionary, grey, candidates[i], currId, params); if(validCandidates[i] > 0) idsTmp[i] = currId; } } } private: Loading @@ -579,7 +637,7 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { vector< vector< Point2f > >& candidates; const Ptr<Dictionary> &dictionary; vector< int > &idsTmp; vector< char > &validCandidates; vector< uint8_t > &validCandidates; const Ptr<DetectorParameters> ¶ms; }; Loading Loading @@ -623,14 +681,13 @@ static void _copyVector2Output(vector< vector< Point2f > > &vec, OutputArrayOfAr /** * @brief Identify square candidates according to a marker dictionary */ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& _candidates, vector< vector<Point> >& _contours, const Ptr<Dictionary> &_dictionary, vector< vector< Point2f > >& _accepted, vector< int >& ids, static void _identifyCandidates(InputArray _image, vector< vector< vector< Point2f > > >& _candidatesSet, vector< vector< vector<Point> > >& _contoursSet, const Ptr<Dictionary> &_dictionary, vector< vector< Point2f > >& _accepted, vector< vector<Point> >& _contours, vector< int >& ids, const Ptr<DetectorParameters> ¶ms, OutputArrayOfArrays _rejected = noArray()) { int ncandidates = (int)_candidates.size(); int ncandidates = (int)_candidatesSet[0].size(); vector< vector< Point2f > > accepted; vector< vector< Point2f > > rejected; Loading @@ -642,32 +699,33 @@ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& _convertToGrey(_image.getMat(), grey); vector< int > idsTmp(ncandidates, -1); vector< char > validCandidates(ncandidates, 0); vector< uint8_t > validCandidates(ncandidates, 0); //// Analyze each of the candidates // for (int i = 0; i < ncandidates; i++) { // int currId = i; // Mat currentCandidate = _candidates.getMat(i); // if (_identifyOneCandidate(dictionary, grey, currentCandidate, currId, params)) { // validCandidates[i] = 1; // idsTmp[i] = currId; // } //} // this is the parallel call for the previous commented loop (result is equivalent) parallel_for_(Range(0, ncandidates), IdentifyCandidatesParallel(grey, _candidates, _dictionary, idsTmp, IdentifyCandidatesParallel(grey, params->detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0], _dictionary, idsTmp, validCandidates, params)); for(int i = 0; i < ncandidates; i++) { if(validCandidates[i] == 1) { accepted.push_back(_candidates[i]); if(validCandidates[i] > 0) { // add the white valid candidate if( params->detectInvertedMarker && validCandidates[i] == 2 ){ accepted.push_back(_candidatesSet[1][i]); ids.push_back(idsTmp[i]); contours.push_back(_contoursSet[1][i]); continue; } // add the default (black) valid candidate accepted.push_back(_candidatesSet[0][i]); ids.push_back(idsTmp[i]); contours.push_back(_contours[i]); contours.push_back(_contoursSet[0][i]); } else { rejected.push_back(_candidates[i]); rejected.push_back(_candidatesSet[0][i]); } } Loading @@ -682,80 +740,6 @@ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& } /** * @brief Final filter of markers after its identification */ static void _filterDetectedMarkers(vector< vector< Point2f > >& _corners, vector< int >& _ids, vector< vector< Point> >& _contours) { CV_Assert(_corners.size() == _ids.size()); if(_corners.empty()) return; // mark markers that will be removed vector< bool > toRemove(_corners.size(), false); bool atLeastOneRemove = false; // remove repeated markers with same id, if one contains the other (doble border bug) for(unsigned int i = 0; i < _corners.size() - 1; i++) { for(unsigned int j = i + 1; j < _corners.size(); j++) { if(_ids[i] != _ids[j]) continue; // check if first marker is inside second bool inside = true; for(unsigned int p = 0; p < 4; p++) { Point2f point = _corners[j][p]; if(pointPolygonTest(_corners[i], point, false) < 0) { inside = false; break; } } if(inside) { toRemove[j] = true; atLeastOneRemove = true; continue; } // check the second marker inside = true; for(unsigned int p = 0; p < 4; p++) { Point2f point = _corners[i][p]; if(pointPolygonTest(_corners[j], point, false) < 0) { inside = false; break; } } if(inside) { toRemove[i] = true; atLeastOneRemove = true; continue; } } } // parse output if(atLeastOneRemove) { vector< vector< Point2f > >::iterator filteredCorners = _corners.begin(); vector< int >::iterator filteredIds = _ids.begin(); vector< vector< Point > >::iterator filteredContours = _contours.begin(); for(unsigned int i = 0; i < toRemove.size(); i++) { if(!toRemove[i]) { *filteredCorners++ = _corners[i]; *filteredIds++ = _ids[i]; *filteredContours++ = _contours[i]; } } _ids.erase(filteredIds, _ids.end()); _corners.erase(filteredCorners, _corners.end()); _contours.erase(filteredContours, _contours.end()); } } /** * @brief Return object points for the system centered in a single marker, given the marker length */ Loading Loading @@ -1127,26 +1111,29 @@ void detectMarkers(InputArray _image, const Ptr<Dictionary> &_dictionary, Output vector< vector< Point > > contours; vector< int > ids; vector< vector< vector< Point2f > > > candidatesSet; vector< vector< vector< Point > > > contoursSet; /// STEP 1.a Detect marker candidates :: using AprilTag if(_params->cornerRefinementMethod == CORNER_REFINE_APRILTAG) if(_params->cornerRefinementMethod == CORNER_REFINE_APRILTAG){ _apriltag(grey, _params, candidates, contours); candidatesSet.push_back(candidates); contoursSet.push_back(contours); } /// STEP 1.b Detect marker candidates :: traditional way else _detectCandidates(grey, candidates, contours, _params); _detectCandidates(grey, candidatesSet, contoursSet, _params); /// STEP 2: Check candidate codification (identify markers) _identifyCandidates(grey, candidates, contours, _dictionary, candidates, ids, _params, _identifyCandidates(grey, candidatesSet, contoursSet, _dictionary, candidates, contours, ids, _params, _rejectedImgPoints); /// STEP 3: Filter detected markers; _filterDetectedMarkers(candidates, ids, contours); // copy to output arrays _copyVector2Output(candidates, _corners); Mat(ids).copyTo(_ids); /// STEP 4: Corner refinement :: use corner subpix /// STEP 3: Corner refinement :: use corner subpix if( _params->cornerRefinementMethod == CORNER_REFINE_SUBPIX ) { CV_Assert(_params->cornerRefinementWinSize > 0 && _params->cornerRefinementMaxIterations > 0 && _params->cornerRefinementMinAccuracy > 0); Loading @@ -1165,7 +1152,7 @@ void detectMarkers(InputArray _image, const Ptr<Dictionary> &_dictionary, Output MarkerSubpixelParallel(&grey, _corners, _params)); } /// STEP 4, Optional : Corner refinement :: use contour container /// STEP 3, Optional : Corner refinement :: use contour container if( _params->cornerRefinementMethod == CORNER_REFINE_CONTOUR){ if(! _ids.empty()){ Loading modules/aruco/test/test_arucodetection.cpp +1 −15 Original line number Diff line number Diff line Loading @@ -322,19 +322,6 @@ void CV_ArucoDetectionPerspective::run(int tryWith) { } for(int c = 0; c < 4; c++) { double dist = cv::norm(groundTruthCorners[c] - corners[0][c]); // TODO cvtest if(CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith){ if(szEnclosed && dist > 3){ ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } if(!szEnclosed && dist >15){ ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } } else{ if(dist > 5) { ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); Loading @@ -343,7 +330,6 @@ void CV_ArucoDetectionPerspective::run(int tryWith) { } } } } // change the state :: to detect an enclosed inverted marker if( CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith && distance == 0.1 ){ distance -= 0.1; Loading Loading
modules/aruco/src/aruco.cpp +135 −148 Original line number Diff line number Diff line Loading @@ -212,17 +212,20 @@ static void _reorderCandidatesCorners(vector< vector< Point2f > > &candidates) { /** * @brief Check candidates that are too close to each other and remove the smaller one * @brief Check candidates that are too close to each other, save the potential candidates * (i.e. biggest/smallest contour) and remove the rest */ static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candidatesIn, vector< vector< Point2f > > &candidatesOut, vector< vector< vector< Point2f > > > &candidatesSetOut, const vector< vector< Point > > &contoursIn, vector< vector< Point > > &contoursOut, double minMarkerDistanceRate) { vector< vector< vector< Point > > > &contoursSetOut, double minMarkerDistanceRate, bool detectInvertedMarker) { CV_Assert(minMarkerDistanceRate >= 0); vector< pair< int, int > > nearCandidates; vector<int> candGroup; candGroup.resize(candidatesIn.size(), -1); vector< vector<unsigned int> > groupedCandidates; for(unsigned int i = 0; i < candidatesIn.size(); i++) { for(unsigned int j = i + 1; j < candidatesIn.size(); j++) { Loading @@ -244,40 +247,87 @@ static void _filterTooCloseCandidates(const vector< vector< Point2f > > &candida // if mean square distance is too low, remove the smaller one of the two markers double minMarkerDistancePixels = double(minimumPerimeter) * minMarkerDistanceRate; if(distSq < minMarkerDistancePixels * minMarkerDistancePixels) { nearCandidates.push_back(pair< int, int >(i, j)); break; // i and j are not related to a group if(candGroup[i]<0 && candGroup[j]<0){ // mark candidates with their corresponding group number candGroup[i] = candGroup[j] = (int)groupedCandidates.size(); // create group vector<unsigned int> grouped; grouped.push_back(i); grouped.push_back(j); groupedCandidates.push_back( grouped ); } // i is related to a group else if(candGroup[i] > -1 && candGroup[j] == -1){ int group = candGroup[i]; candGroup[j] = group; // add to group groupedCandidates[group].push_back( j ); } // j is related to a group else if(candGroup[j] > -1 && candGroup[i] == -1){ int group = candGroup[j]; candGroup[i] = group; // add to group groupedCandidates[group].push_back( i ); } } } } } // mark smaller one in pairs to remove vector< bool > toRemove(candidatesIn.size(), false); for(unsigned int i = 0; i < nearCandidates.size(); i++) { // if one of the marker has been already markerd to removed, dont need to do anything if(toRemove[nearCandidates[i].first] || toRemove[nearCandidates[i].second]) continue; size_t perimeter1 = contoursIn[nearCandidates[i].first].size(); size_t perimeter2 = contoursIn[nearCandidates[i].second].size(); if(perimeter1 > perimeter2) toRemove[nearCandidates[i].second] = true; else toRemove[nearCandidates[i].first] = true; // save possible candidates candidatesSetOut.clear(); contoursSetOut.clear(); vector< vector< Point2f > > biggerCandidates; vector< vector< Point > > biggerContours; vector< vector< Point2f > > smallerCandidates; vector< vector< Point > > smallerContours; // save possible candidates for( unsigned int i = 0; i < groupedCandidates.size(); i++ ) { int smallerIdx = groupedCandidates[i][0]; int biggerIdx = -1; // evaluate group elements for( unsigned int j = 1; j < groupedCandidates[i].size(); j++ ) { size_t currPerim = contoursIn[ groupedCandidates[i][j] ].size(); // check if current contour is bigger if ( biggerIdx < 0 ) biggerIdx = groupedCandidates[i][j]; else if(currPerim >= contoursIn[ biggerIdx ].size()) biggerIdx = groupedCandidates[i][j]; // check if current contour is smaller if(currPerim < contoursIn[ smallerIdx ].size() && detectInvertedMarker) smallerIdx = groupedCandidates[i][j]; } // add contours und candidates if(biggerIdx > -1){ // remove extra candidates candidatesOut.clear(); unsigned long totalRemaining = 0; for(unsigned int i = 0; i < toRemove.size(); i++) if(!toRemove[i]) totalRemaining++; candidatesOut.resize(totalRemaining); contoursOut.resize(totalRemaining); for(unsigned int i = 0, currIdx = 0; i < candidatesIn.size(); i++) { if(toRemove[i]) continue; candidatesOut[currIdx] = candidatesIn[i]; contoursOut[currIdx] = contoursIn[i]; currIdx++; biggerCandidates.push_back(candidatesIn[biggerIdx]); biggerContours.push_back(contoursIn[biggerIdx]); if( detectInvertedMarker ){ smallerCandidates.push_back(candidatesIn[smallerIdx]); smallerContours.push_back(contoursIn[smallerIdx]); } } } // to preserve the structure :: candidateSet< defaultCandidates, whiteCandidates > // default candidates candidatesSetOut.push_back(biggerCandidates); contoursSetOut.push_back(biggerContours); // white candidates candidatesSetOut.push_back(smallerCandidates); contoursSetOut.push_back(smallerContours); } /** Loading Loading @@ -370,8 +420,8 @@ static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > /** * @brief Detect square candidates in the input image */ static void _detectCandidates(InputArray _image, vector< vector< Point2f > >& candidatesOut, vector< vector< Point > >& contoursOut, const Ptr<DetectorParameters> &_params) { static void _detectCandidates(InputArray _image, vector< vector< vector< Point2f > > >& candidatesSetOut, vector< vector< vector< Point > > >& contoursSetOut, const Ptr<DetectorParameters> &_params) { Mat image = _image.getMat(); CV_Assert(image.total() != 0); Loading @@ -389,8 +439,9 @@ static void _detectCandidates(InputArray _image, vector< vector< Point2f > >& ca _reorderCandidatesCorners(candidates); /// 4. FILTER OUT NEAR CANDIDATE PAIRS _filterTooCloseCandidates(candidates, candidatesOut, contours, contoursOut, _params->minMarkerDistanceRate); // save the outter/inner border (i.e. potential candidates) _filterTooCloseCandidates(candidates, candidatesSetOut, contours, contoursSetOut, _params->minMarkerDistanceRate, _params->detectInvertedMarker); } Loading Loading @@ -493,8 +544,11 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { /** * @brief Tries to identify one candidate given the dictionary * @return candidate typ. zero if the candidate is not valid, * 1 if the candidate is a black candidate (default candidate) * 2 if the candidate is a white candidate */ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray _image, static uint8_t _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray _image, vector<Point2f>& _corners, int& idx, const Ptr<DetectorParameters>& params) { Loading @@ -502,6 +556,7 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray CV_Assert(_image.getMat().total() != 0); CV_Assert(params->markerBorderBits > 0); uint8_t typ=1; // get bits Mat candidateBits = _extractBits(_image, _corners, dictionary->markerSize, params->markerBorderBits, Loading @@ -523,9 +578,10 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray if(invBError<borderErrors){ borderErrors = invBError; invertedImg.copyTo(candidateBits); typ=2; } } if(borderErrors > maximumErrorsInBorder) return false; if(borderErrors > maximumErrorsInBorder) return 0; // border is wrong // take only inner bits Mat onlyBits = Loading @@ -536,13 +592,13 @@ static bool _identifyOneCandidate(const Ptr<Dictionary>& dictionary, InputArray // try to indentify the marker int rotation; if(!dictionary->identify(onlyBits, idx, rotation, params->errorCorrectionRate)) return false; return 0; // shift corner positions to the correct rotation if(rotation != 0) { std::rotate(_corners.begin(), _corners.begin() + 4 - rotation, _corners.end()); } return true; return typ; } Loading @@ -554,22 +610,24 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { public: IdentifyCandidatesParallel(const Mat& _grey, vector< vector< Point2f > >& _candidates, const Ptr<Dictionary> &_dictionary, vector< int >& _idsTmp, vector< char >& _validCandidates, vector< int >& _idsTmp, vector< uint8_t >& _validCandidates, const Ptr<DetectorParameters> &_params) : grey(_grey), candidates(_candidates), dictionary(_dictionary), idsTmp(_idsTmp), validCandidates(_validCandidates), params(_params) {} void operator()(const Range &range) const CV_OVERRIDE { void operator()(const Range &range) const CV_OVERRIDE { const int begin = range.start; const int end = range.end; for(int i = begin; i < end; i++) { int currId; if(_identifyOneCandidate(dictionary, grey, candidates[i], currId, params)) { validCandidates[i] = 1; validCandidates[i] = _identifyOneCandidate(dictionary, grey, candidates[i], currId, params); if(validCandidates[i] > 0) idsTmp[i] = currId; } } } private: Loading @@ -579,7 +637,7 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { vector< vector< Point2f > >& candidates; const Ptr<Dictionary> &dictionary; vector< int > &idsTmp; vector< char > &validCandidates; vector< uint8_t > &validCandidates; const Ptr<DetectorParameters> ¶ms; }; Loading Loading @@ -623,14 +681,13 @@ static void _copyVector2Output(vector< vector< Point2f > > &vec, OutputArrayOfAr /** * @brief Identify square candidates according to a marker dictionary */ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& _candidates, vector< vector<Point> >& _contours, const Ptr<Dictionary> &_dictionary, vector< vector< Point2f > >& _accepted, vector< int >& ids, static void _identifyCandidates(InputArray _image, vector< vector< vector< Point2f > > >& _candidatesSet, vector< vector< vector<Point> > >& _contoursSet, const Ptr<Dictionary> &_dictionary, vector< vector< Point2f > >& _accepted, vector< vector<Point> >& _contours, vector< int >& ids, const Ptr<DetectorParameters> ¶ms, OutputArrayOfArrays _rejected = noArray()) { int ncandidates = (int)_candidates.size(); int ncandidates = (int)_candidatesSet[0].size(); vector< vector< Point2f > > accepted; vector< vector< Point2f > > rejected; Loading @@ -642,32 +699,33 @@ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& _convertToGrey(_image.getMat(), grey); vector< int > idsTmp(ncandidates, -1); vector< char > validCandidates(ncandidates, 0); vector< uint8_t > validCandidates(ncandidates, 0); //// Analyze each of the candidates // for (int i = 0; i < ncandidates; i++) { // int currId = i; // Mat currentCandidate = _candidates.getMat(i); // if (_identifyOneCandidate(dictionary, grey, currentCandidate, currId, params)) { // validCandidates[i] = 1; // idsTmp[i] = currId; // } //} // this is the parallel call for the previous commented loop (result is equivalent) parallel_for_(Range(0, ncandidates), IdentifyCandidatesParallel(grey, _candidates, _dictionary, idsTmp, IdentifyCandidatesParallel(grey, params->detectInvertedMarker ? _candidatesSet[1] : _candidatesSet[0], _dictionary, idsTmp, validCandidates, params)); for(int i = 0; i < ncandidates; i++) { if(validCandidates[i] == 1) { accepted.push_back(_candidates[i]); if(validCandidates[i] > 0) { // add the white valid candidate if( params->detectInvertedMarker && validCandidates[i] == 2 ){ accepted.push_back(_candidatesSet[1][i]); ids.push_back(idsTmp[i]); contours.push_back(_contoursSet[1][i]); continue; } // add the default (black) valid candidate accepted.push_back(_candidatesSet[0][i]); ids.push_back(idsTmp[i]); contours.push_back(_contours[i]); contours.push_back(_contoursSet[0][i]); } else { rejected.push_back(_candidates[i]); rejected.push_back(_candidatesSet[0][i]); } } Loading @@ -682,80 +740,6 @@ static void _identifyCandidates(InputArray _image, vector< vector< Point2f > >& } /** * @brief Final filter of markers after its identification */ static void _filterDetectedMarkers(vector< vector< Point2f > >& _corners, vector< int >& _ids, vector< vector< Point> >& _contours) { CV_Assert(_corners.size() == _ids.size()); if(_corners.empty()) return; // mark markers that will be removed vector< bool > toRemove(_corners.size(), false); bool atLeastOneRemove = false; // remove repeated markers with same id, if one contains the other (doble border bug) for(unsigned int i = 0; i < _corners.size() - 1; i++) { for(unsigned int j = i + 1; j < _corners.size(); j++) { if(_ids[i] != _ids[j]) continue; // check if first marker is inside second bool inside = true; for(unsigned int p = 0; p < 4; p++) { Point2f point = _corners[j][p]; if(pointPolygonTest(_corners[i], point, false) < 0) { inside = false; break; } } if(inside) { toRemove[j] = true; atLeastOneRemove = true; continue; } // check the second marker inside = true; for(unsigned int p = 0; p < 4; p++) { Point2f point = _corners[i][p]; if(pointPolygonTest(_corners[j], point, false) < 0) { inside = false; break; } } if(inside) { toRemove[i] = true; atLeastOneRemove = true; continue; } } } // parse output if(atLeastOneRemove) { vector< vector< Point2f > >::iterator filteredCorners = _corners.begin(); vector< int >::iterator filteredIds = _ids.begin(); vector< vector< Point > >::iterator filteredContours = _contours.begin(); for(unsigned int i = 0; i < toRemove.size(); i++) { if(!toRemove[i]) { *filteredCorners++ = _corners[i]; *filteredIds++ = _ids[i]; *filteredContours++ = _contours[i]; } } _ids.erase(filteredIds, _ids.end()); _corners.erase(filteredCorners, _corners.end()); _contours.erase(filteredContours, _contours.end()); } } /** * @brief Return object points for the system centered in a single marker, given the marker length */ Loading Loading @@ -1127,26 +1111,29 @@ void detectMarkers(InputArray _image, const Ptr<Dictionary> &_dictionary, Output vector< vector< Point > > contours; vector< int > ids; vector< vector< vector< Point2f > > > candidatesSet; vector< vector< vector< Point > > > contoursSet; /// STEP 1.a Detect marker candidates :: using AprilTag if(_params->cornerRefinementMethod == CORNER_REFINE_APRILTAG) if(_params->cornerRefinementMethod == CORNER_REFINE_APRILTAG){ _apriltag(grey, _params, candidates, contours); candidatesSet.push_back(candidates); contoursSet.push_back(contours); } /// STEP 1.b Detect marker candidates :: traditional way else _detectCandidates(grey, candidates, contours, _params); _detectCandidates(grey, candidatesSet, contoursSet, _params); /// STEP 2: Check candidate codification (identify markers) _identifyCandidates(grey, candidates, contours, _dictionary, candidates, ids, _params, _identifyCandidates(grey, candidatesSet, contoursSet, _dictionary, candidates, contours, ids, _params, _rejectedImgPoints); /// STEP 3: Filter detected markers; _filterDetectedMarkers(candidates, ids, contours); // copy to output arrays _copyVector2Output(candidates, _corners); Mat(ids).copyTo(_ids); /// STEP 4: Corner refinement :: use corner subpix /// STEP 3: Corner refinement :: use corner subpix if( _params->cornerRefinementMethod == CORNER_REFINE_SUBPIX ) { CV_Assert(_params->cornerRefinementWinSize > 0 && _params->cornerRefinementMaxIterations > 0 && _params->cornerRefinementMinAccuracy > 0); Loading @@ -1165,7 +1152,7 @@ void detectMarkers(InputArray _image, const Ptr<Dictionary> &_dictionary, Output MarkerSubpixelParallel(&grey, _corners, _params)); } /// STEP 4, Optional : Corner refinement :: use contour container /// STEP 3, Optional : Corner refinement :: use contour container if( _params->cornerRefinementMethod == CORNER_REFINE_CONTOUR){ if(! _ids.empty()){ Loading
modules/aruco/test/test_arucodetection.cpp +1 −15 Original line number Diff line number Diff line Loading @@ -322,19 +322,6 @@ void CV_ArucoDetectionPerspective::run(int tryWith) { } for(int c = 0; c < 4; c++) { double dist = cv::norm(groundTruthCorners[c] - corners[0][c]); // TODO cvtest if(CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith){ if(szEnclosed && dist > 3){ ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } if(!szEnclosed && dist >15){ ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); return; } } else{ if(dist > 5) { ts->printf(cvtest::TS::LOG, "Incorrect marker corners position"); ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); Loading @@ -343,7 +330,6 @@ void CV_ArucoDetectionPerspective::run(int tryWith) { } } } } // change the state :: to detect an enclosed inverted marker if( CV_ArucoDetectionPerspective::DETECT_INVERTED_MARKER == tryWith && distance == 0.1 ){ distance -= 0.1; Loading