|
|
|
@ -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++) { |
|
|
|
|
|
|
|
|
@ -244,39 +247,86 @@ 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){ |
|
|
|
|
|
|
|
|
|
biggerCandidates.push_back(candidatesIn[biggerIdx]); |
|
|
|
|
biggerContours.push_back(contoursIn[biggerIdx]); |
|
|
|
|
|
|
|
|
|
// 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++; |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -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); |
|
|
|
@ -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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -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) |
|
|
|
|
{ |
|
|
|
@ -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, |
|
|
|
@ -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 = |
|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -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: |
|
|
|
@ -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; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -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; |
|
|
|
|
|
|
|
|
@ -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]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -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 |
|
|
|
|
*/ |
|
|
|
@ -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); |
|
|
|
@ -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()){ |
|
|
|
|