|
|
|
@ -76,6 +76,15 @@ DetectorParameters::DetectorParameters() |
|
|
|
|
errorCorrectionRate(0.6) {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Create a new set of DetectorParameters with default values. |
|
|
|
|
*/ |
|
|
|
|
Ptr<DetectorParameters> DetectorParameters::create() { |
|
|
|
|
Ptr<DetectorParameters> params = makePtr<DetectorParameters>(); |
|
|
|
|
return params; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Convert input image to gray if it is a 3-channels image |
|
|
|
|
*/ |
|
|
|
@ -267,7 +276,7 @@ class DetectInitialCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
DetectInitialCandidatesParallel(const Mat *_grey, |
|
|
|
|
vector< vector< vector< Point2f > > > *_candidatesArrays, |
|
|
|
|
vector< vector< vector< Point > > > *_contoursArrays, |
|
|
|
|
DetectorParameters *_params) |
|
|
|
|
const Ptr<DetectorParameters> &_params) |
|
|
|
|
: grey(_grey), candidatesArrays(_candidatesArrays), contoursArrays(_contoursArrays), |
|
|
|
|
params(_params) {} |
|
|
|
|
|
|
|
|
@ -296,7 +305,7 @@ class DetectInitialCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
const Mat *grey; |
|
|
|
|
vector< vector< vector< Point2f > > > *candidatesArrays; |
|
|
|
|
vector< vector< vector< Point > > > *contoursArrays; |
|
|
|
|
DetectorParameters *params; |
|
|
|
|
const Ptr<DetectorParameters> ¶ms; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -305,15 +314,15 @@ class DetectInitialCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
*/ |
|
|
|
|
static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > > &candidates, |
|
|
|
|
vector< vector< Point > > &contours, |
|
|
|
|
DetectorParameters params) { |
|
|
|
|
const Ptr<DetectorParameters> ¶ms) { |
|
|
|
|
|
|
|
|
|
CV_Assert(params.adaptiveThreshWinSizeMin >= 3 && params.adaptiveThreshWinSizeMax >= 3); |
|
|
|
|
CV_Assert(params.adaptiveThreshWinSizeMax >= params.adaptiveThreshWinSizeMin); |
|
|
|
|
CV_Assert(params.adaptiveThreshWinSizeStep > 0); |
|
|
|
|
CV_Assert(params->adaptiveThreshWinSizeMin >= 3 && params->adaptiveThreshWinSizeMax >= 3); |
|
|
|
|
CV_Assert(params->adaptiveThreshWinSizeMax >= params->adaptiveThreshWinSizeMin); |
|
|
|
|
CV_Assert(params->adaptiveThreshWinSizeStep > 0); |
|
|
|
|
|
|
|
|
|
// number of window sizes (scales) to apply adaptive thresholding
|
|
|
|
|
int nScales = (params.adaptiveThreshWinSizeMax - params.adaptiveThreshWinSizeMin) / |
|
|
|
|
params.adaptiveThreshWinSizeStep + 1; |
|
|
|
|
int nScales = (params->adaptiveThreshWinSizeMax - params->adaptiveThreshWinSizeMin) / |
|
|
|
|
params->adaptiveThreshWinSizeStep + 1; |
|
|
|
|
|
|
|
|
|
vector< vector< vector< Point2f > > > candidatesArrays((size_t) nScales); |
|
|
|
|
vector< vector< vector< Point > > > contoursArrays((size_t) nScales); |
|
|
|
@ -333,7 +342,7 @@ static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > |
|
|
|
|
|
|
|
|
|
// this is the parallel call for the previous commented loop (result is equivalent)
|
|
|
|
|
parallel_for_(Range(0, nScales), DetectInitialCandidatesParallel(&grey, &candidatesArrays, |
|
|
|
|
&contoursArrays, ¶ms)); |
|
|
|
|
&contoursArrays, params)); |
|
|
|
|
|
|
|
|
|
// join candidates
|
|
|
|
|
for(int i = 0; i < nScales; i++) { |
|
|
|
@ -349,7 +358,7 @@ static void _detectInitialCandidates(const Mat &grey, vector< vector< Point2f > |
|
|
|
|
* @brief Detect square candidates in the input image |
|
|
|
|
*/ |
|
|
|
|
static void _detectCandidates(InputArray _image, OutputArrayOfArrays _candidates, |
|
|
|
|
OutputArrayOfArrays _contours, DetectorParameters params) { |
|
|
|
|
OutputArrayOfArrays _contours, const Ptr<DetectorParameters> &_params) { |
|
|
|
|
|
|
|
|
|
Mat image = _image.getMat(); |
|
|
|
|
CV_Assert(image.total() != 0); |
|
|
|
@ -361,7 +370,7 @@ static void _detectCandidates(InputArray _image, OutputArrayOfArrays _candidates |
|
|
|
|
vector< vector< Point2f > > candidates; |
|
|
|
|
vector< vector< Point > > contours; |
|
|
|
|
/// 2. DETECT FIRST SET OF CANDIDATES
|
|
|
|
|
_detectInitialCandidates(grey, candidates, contours, params); |
|
|
|
|
_detectInitialCandidates(grey, candidates, contours, _params); |
|
|
|
|
|
|
|
|
|
/// 3. SORT CORNERS
|
|
|
|
|
_reorderCandidatesCorners(candidates); |
|
|
|
@ -370,7 +379,7 @@ static void _detectCandidates(InputArray _image, OutputArrayOfArrays _candidates |
|
|
|
|
vector< vector< Point2f > > candidatesOut; |
|
|
|
|
vector< vector< Point > > contoursOut; |
|
|
|
|
_filterTooCloseCandidates(candidates, candidatesOut, contours, contoursOut, |
|
|
|
|
params.minMarkerDistanceRate); |
|
|
|
|
_params->minMarkerDistanceRate); |
|
|
|
|
|
|
|
|
|
// parse output
|
|
|
|
|
_candidates.create((int)candidatesOut.size(), 1, CV_32FC2); |
|
|
|
@ -489,35 +498,35 @@ static int _getBorderErrors(const Mat &bits, int markerSize, int borderSize) { |
|
|
|
|
/**
|
|
|
|
|
* @brief Tries to identify one candidate given the dictionary |
|
|
|
|
*/ |
|
|
|
|
static bool _identifyOneCandidate(const Dictionary &dictionary, InputArray _image, |
|
|
|
|
InputOutputArray _corners, int &idx, DetectorParameters params) { |
|
|
|
|
static bool _identifyOneCandidate(Ptr<Dictionary> &dictionary, InputArray _image, |
|
|
|
|
InputOutputArray _corners, int &idx, const Ptr<DetectorParameters> ¶ms) { |
|
|
|
|
|
|
|
|
|
CV_Assert(_corners.total() == 4); |
|
|
|
|
CV_Assert(_image.getMat().total() != 0); |
|
|
|
|
CV_Assert(params.markerBorderBits > 0); |
|
|
|
|
CV_Assert(params->markerBorderBits > 0); |
|
|
|
|
|
|
|
|
|
// get bits
|
|
|
|
|
Mat candidateBits = |
|
|
|
|
_extractBits(_image, _corners, dictionary.markerSize, params.markerBorderBits, |
|
|
|
|
params.perspectiveRemovePixelPerCell, |
|
|
|
|
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev); |
|
|
|
|
_extractBits(_image, _corners, dictionary->markerSize, params->markerBorderBits, |
|
|
|
|
params->perspectiveRemovePixelPerCell, |
|
|
|
|
params->perspectiveRemoveIgnoredMarginPerCell, params->minOtsuStdDev); |
|
|
|
|
|
|
|
|
|
// analyze border bits
|
|
|
|
|
int maximumErrorsInBorder = |
|
|
|
|
int(dictionary.markerSize * dictionary.markerSize * params.maxErroneousBitsInBorderRate); |
|
|
|
|
int(dictionary->markerSize * dictionary->markerSize * params->maxErroneousBitsInBorderRate); |
|
|
|
|
int borderErrors = |
|
|
|
|
_getBorderErrors(candidateBits, dictionary.markerSize, params.markerBorderBits); |
|
|
|
|
_getBorderErrors(candidateBits, dictionary->markerSize, params->markerBorderBits); |
|
|
|
|
if(borderErrors > maximumErrorsInBorder) return false; // border is wrong
|
|
|
|
|
|
|
|
|
|
// take only inner bits
|
|
|
|
|
Mat onlyBits = |
|
|
|
|
candidateBits.rowRange(params.markerBorderBits, |
|
|
|
|
candidateBits.rows - params.markerBorderBits) |
|
|
|
|
.colRange(params.markerBorderBits, candidateBits.rows - params.markerBorderBits); |
|
|
|
|
candidateBits.rowRange(params->markerBorderBits, |
|
|
|
|
candidateBits.rows - params->markerBorderBits) |
|
|
|
|
.colRange(params->markerBorderBits, candidateBits.rows - params->markerBorderBits); |
|
|
|
|
|
|
|
|
|
// try to indentify the marker
|
|
|
|
|
int rotation; |
|
|
|
|
if(!dictionary.identify(onlyBits, idx, rotation, params.errorCorrectionRate)) |
|
|
|
|
if(!dictionary->identify(onlyBits, idx, rotation, params->errorCorrectionRate)) |
|
|
|
|
return false; |
|
|
|
|
else { |
|
|
|
|
// shift corner positions to the correct rotation
|
|
|
|
@ -539,9 +548,9 @@ static bool _identifyOneCandidate(const Dictionary &dictionary, InputArray _imag |
|
|
|
|
class IdentifyCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
public: |
|
|
|
|
IdentifyCandidatesParallel(const Mat *_grey, InputArrayOfArrays _candidates, |
|
|
|
|
InputArrayOfArrays _contours, const Dictionary *_dictionary, |
|
|
|
|
InputArrayOfArrays _contours, Ptr<Dictionary> &_dictionary, |
|
|
|
|
vector< int > *_idsTmp, vector< char > *_validCandidates, |
|
|
|
|
DetectorParameters *_params) |
|
|
|
|
const Ptr<DetectorParameters> &_params) |
|
|
|
|
: grey(_grey), candidates(_candidates), contours(_contours), dictionary(_dictionary), |
|
|
|
|
idsTmp(_idsTmp), validCandidates(_validCandidates), params(_params) {} |
|
|
|
|
|
|
|
|
@ -552,7 +561,7 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
for(int i = begin; i < end; i++) { |
|
|
|
|
int currId; |
|
|
|
|
Mat currentCandidate = candidates.getMat(i); |
|
|
|
|
if(_identifyOneCandidate(*dictionary, *grey, currentCandidate, currId, *params)) { |
|
|
|
|
if(_identifyOneCandidate(dictionary, *grey, currentCandidate, currId, params)) { |
|
|
|
|
(*validCandidates)[i] = 1; |
|
|
|
|
(*idsTmp)[i] = currId; |
|
|
|
|
} |
|
|
|
@ -564,21 +573,65 @@ class IdentifyCandidatesParallel : public ParallelLoopBody { |
|
|
|
|
|
|
|
|
|
const Mat *grey; |
|
|
|
|
InputArrayOfArrays candidates, contours; |
|
|
|
|
const Dictionary *dictionary; |
|
|
|
|
Ptr<Dictionary> &dictionary; |
|
|
|
|
vector< int > *idsTmp; |
|
|
|
|
vector< char > *validCandidates; |
|
|
|
|
DetectorParameters *params; |
|
|
|
|
const Ptr<DetectorParameters> ¶ms; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Copy the contents of a Mat vector to an OutputArray, settings its size. |
|
|
|
|
*/ |
|
|
|
|
void _copyVector2Output(vector< Mat > &vec, OutputArrayOfArrays out); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
void _copyVector2Output(vector< Mat > &vec, OutputArrayOfArrays out) { |
|
|
|
|
|
|
|
|
|
out.release(); |
|
|
|
|
out.create((int)vec.size(), 1, CV_32FC2); |
|
|
|
|
|
|
|
|
|
if(out.isMatVector()) { |
|
|
|
|
for (unsigned int i = 0; i < vec.size(); i++) { |
|
|
|
|
out.create(4, 1, CV_32FC2, i, true); |
|
|
|
|
Mat &m = out.getMatRef(i); |
|
|
|
|
vec[i].copyTo(m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if(out.isUMatVector()) { |
|
|
|
|
for (unsigned int i = 0; i < vec.size(); i++) { |
|
|
|
|
out.create(4, 1, CV_32FC2, i, true); |
|
|
|
|
UMat &m = out.getUMatRef(i); |
|
|
|
|
vec[i].copyTo(m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else if(out.kind() == _OutputArray::STD_VECTOR_VECTOR){ |
|
|
|
|
for (unsigned int i = 0; i < vec.size(); i++) { |
|
|
|
|
out.create(4, 1, CV_32FC2, i, true); |
|
|
|
|
Mat m = out.getMat(i); |
|
|
|
|
vec[i].copyTo(m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
CV_Error(cv::Error::StsNotImplemented, |
|
|
|
|
"Only Mat vector, UMat vector, and vector<vector> OutputArrays are currently supported."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief Identify square candidates according to a marker dictionary |
|
|
|
|
*/ |
|
|
|
|
static void _identifyCandidates(InputArray _image, InputArrayOfArrays _candidates, |
|
|
|
|
InputArrayOfArrays _contours, const Dictionary &dictionary, |
|
|
|
|
InputArrayOfArrays _contours, Ptr<Dictionary> &_dictionary, |
|
|
|
|
OutputArrayOfArrays _accepted, OutputArray _ids, |
|
|
|
|
DetectorParameters params, |
|
|
|
|
const Ptr<DetectorParameters> ¶ms, |
|
|
|
|
OutputArrayOfArrays _rejected = noArray()) { |
|
|
|
|
|
|
|
|
|
int ncandidates = (int)_candidates.total(); |
|
|
|
@ -607,8 +660,8 @@ static void _identifyCandidates(InputArray _image, InputArrayOfArrays _candidate |
|
|
|
|
|
|
|
|
|
// this is the parallel call for the previous commented loop (result is equivalent)
|
|
|
|
|
parallel_for_(Range(0, ncandidates), |
|
|
|
|
IdentifyCandidatesParallel(&grey, _candidates, _contours, &dictionary, &idsTmp, |
|
|
|
|
&validCandidates, ¶ms)); |
|
|
|
|
IdentifyCandidatesParallel(&grey, _candidates, _contours, _dictionary, &idsTmp, |
|
|
|
|
&validCandidates, params)); |
|
|
|
|
|
|
|
|
|
for(int i = 0; i < ncandidates; i++) { |
|
|
|
|
if(validCandidates[i] == 1) { |
|
|
|
@ -620,24 +673,14 @@ static void _identifyCandidates(InputArray _image, InputArrayOfArrays _candidate |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// parse output
|
|
|
|
|
_accepted.create((int)accepted.size(), 1, CV_32FC2); |
|
|
|
|
for(unsigned int i = 0; i < accepted.size(); i++) { |
|
|
|
|
_accepted.create(4, 1, CV_32FC2, i, true); |
|
|
|
|
Mat m = _accepted.getMat(i); |
|
|
|
|
accepted[i].copyTo(m); |
|
|
|
|
} |
|
|
|
|
_copyVector2Output(accepted, _accepted); |
|
|
|
|
|
|
|
|
|
_ids.create((int)ids.size(), 1, CV_32SC1); |
|
|
|
|
for(unsigned int i = 0; i < ids.size(); i++) |
|
|
|
|
_ids.getMat().ptr< int >(0)[i] = ids[i]; |
|
|
|
|
|
|
|
|
|
if(_rejected.needed()) { |
|
|
|
|
_rejected.create((int)rejected.size(), 1, CV_32FC2); |
|
|
|
|
for(unsigned int i = 0; i < rejected.size(); i++) { |
|
|
|
|
_rejected.create(4, 1, CV_32FC2, i, true); |
|
|
|
|
Mat m = _rejected.getMat(i); |
|
|
|
|
rejected[i].copyTo(m); |
|
|
|
|
} |
|
|
|
|
_copyVector2Output(rejected, _rejected); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -744,7 +787,7 @@ static void _getSingleMarkerObjectPoints(float markerLength, OutputArray _objPoi |
|
|
|
|
class MarkerSubpixelParallel : public ParallelLoopBody { |
|
|
|
|
public: |
|
|
|
|
MarkerSubpixelParallel(const Mat *_grey, OutputArrayOfArrays _corners, |
|
|
|
|
DetectorParameters *_params) |
|
|
|
|
const Ptr<DetectorParameters> &_params) |
|
|
|
|
: grey(_grey), corners(_corners), params(_params) {} |
|
|
|
|
|
|
|
|
|
void operator()(const Range &range) const { |
|
|
|
@ -765,15 +808,15 @@ class MarkerSubpixelParallel : public ParallelLoopBody { |
|
|
|
|
|
|
|
|
|
const Mat *grey; |
|
|
|
|
OutputArrayOfArrays corners; |
|
|
|
|
DetectorParameters *params; |
|
|
|
|
const Ptr<DetectorParameters> ¶ms; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void detectMarkers(InputArray _image, Dictionary dictionary, OutputArrayOfArrays _corners, |
|
|
|
|
OutputArray _ids, DetectorParameters params, |
|
|
|
|
void detectMarkers(InputArray _image, Ptr<Dictionary> &_dictionary, OutputArrayOfArrays _corners, |
|
|
|
|
OutputArray _ids, const Ptr<DetectorParameters> &_params, |
|
|
|
|
OutputArrayOfArrays _rejectedImgPoints) { |
|
|
|
|
|
|
|
|
|
CV_Assert(_image.getMat().total() != 0); |
|
|
|
@ -784,19 +827,19 @@ void detectMarkers(InputArray _image, Dictionary dictionary, OutputArrayOfArrays |
|
|
|
|
/// STEP 1: Detect marker candidates
|
|
|
|
|
vector< vector< Point2f > > candidates; |
|
|
|
|
vector< vector< Point > > contours; |
|
|
|
|
_detectCandidates(grey, candidates, contours, params); |
|
|
|
|
_detectCandidates(grey, candidates, contours, _params); |
|
|
|
|
|
|
|
|
|
/// STEP 2: Check candidate codification (identify markers)
|
|
|
|
|
_identifyCandidates(grey, candidates, contours, dictionary, _corners, _ids, params, |
|
|
|
|
_identifyCandidates(grey, candidates, contours, _dictionary, _corners, _ids, _params, |
|
|
|
|
_rejectedImgPoints); |
|
|
|
|
|
|
|
|
|
/// STEP 3: Filter detected markers;
|
|
|
|
|
_filterDetectedMarkers(_corners, _ids, _corners, _ids); |
|
|
|
|
|
|
|
|
|
/// STEP 4: Corner refinement
|
|
|
|
|
if(params.doCornerRefinement) { |
|
|
|
|
CV_Assert(params.cornerRefinementWinSize > 0 && params.cornerRefinementMaxIterations > 0 && |
|
|
|
|
params.cornerRefinementMinAccuracy > 0); |
|
|
|
|
if(_params->doCornerRefinement) { |
|
|
|
|
CV_Assert(_params->cornerRefinementWinSize > 0 && _params->cornerRefinementMaxIterations > 0 && |
|
|
|
|
_params->cornerRefinementMinAccuracy > 0); |
|
|
|
|
|
|
|
|
|
//// do corner refinement for each of the detected markers
|
|
|
|
|
// for (unsigned int i = 0; i < _corners.total(); i++) {
|
|
|
|
@ -809,7 +852,7 @@ void detectMarkers(InputArray _image, Dictionary dictionary, OutputArrayOfArrays |
|
|
|
|
|
|
|
|
|
// this is the parallel call for the previous commented loop (result is equivalent)
|
|
|
|
|
parallel_for_(Range(0, (int)_corners.total()), |
|
|
|
|
MarkerSubpixelParallel(&grey, _corners, ¶ms)); |
|
|
|
|
MarkerSubpixelParallel(&grey, _corners, _params)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -883,11 +926,11 @@ void estimatePoseSingleMarkers(InputArrayOfArrays _corners, float markerLength, |
|
|
|
|
* @brief Given a board configuration and a set of detected markers, returns the corresponding |
|
|
|
|
* image points and object points to call solvePnP |
|
|
|
|
*/ |
|
|
|
|
static void _getBoardObjectAndImagePoints(const Board &board, InputArray _detectedIds, |
|
|
|
|
static void _getBoardObjectAndImagePoints(Ptr<Board> &_board, InputArray _detectedIds, |
|
|
|
|
InputArrayOfArrays _detectedCorners, |
|
|
|
|
OutputArray _imgPoints, OutputArray _objPoints) { |
|
|
|
|
|
|
|
|
|
CV_Assert(board.ids.size() == board.objPoints.size()); |
|
|
|
|
CV_Assert(_board->ids.size() == _board->objPoints.size()); |
|
|
|
|
CV_Assert(_detectedIds.total() == _detectedCorners.total()); |
|
|
|
|
|
|
|
|
|
size_t nDetectedMarkers = _detectedIds.total(); |
|
|
|
@ -901,10 +944,10 @@ static void _getBoardObjectAndImagePoints(const Board &board, InputArray _detect |
|
|
|
|
// look for detected markers that belong to the board and get their information
|
|
|
|
|
for(unsigned int i = 0; i < nDetectedMarkers; i++) { |
|
|
|
|
int currentId = _detectedIds.getMat().ptr< int >(0)[i]; |
|
|
|
|
for(unsigned int j = 0; j < board.ids.size(); j++) { |
|
|
|
|
if(currentId == board.ids[j]) { |
|
|
|
|
for(unsigned int j = 0; j < _board->ids.size(); j++) { |
|
|
|
|
if(currentId == _board->ids[j]) { |
|
|
|
|
for(int p = 0; p < 4; p++) { |
|
|
|
|
objPnts.push_back(board.objPoints[j][p]); |
|
|
|
|
objPnts.push_back(_board->objPoints[j][p]); |
|
|
|
|
imgPnts.push_back(_detectedCorners.getMat(i).ptr< Point2f >(0)[p]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -926,7 +969,7 @@ static void _getBoardObjectAndImagePoints(const Board &board, InputArray _detect |
|
|
|
|
/**
|
|
|
|
|
* Project board markers that are not included in the list of detected markers |
|
|
|
|
*/ |
|
|
|
|
static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArrays _detectedCorners, |
|
|
|
|
static void _projectUndetectedMarkers(Ptr<Board> &_board, InputOutputArrayOfArrays _detectedCorners, |
|
|
|
|
InputOutputArray _detectedIds, InputArray _cameraMatrix, |
|
|
|
|
InputArray _distCoeffs, |
|
|
|
|
OutputArrayOfArrays _undetectedMarkersProjectedCorners, |
|
|
|
@ -935,7 +978,7 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
// first estimate board pose with the current avaible markers
|
|
|
|
|
Mat rvec, tvec; |
|
|
|
|
int boardDetectedMarkers; |
|
|
|
|
boardDetectedMarkers = aruco::estimatePoseBoard(_detectedCorners, _detectedIds, board, |
|
|
|
|
boardDetectedMarkers = aruco::estimatePoseBoard(_detectedCorners, _detectedIds, _board, |
|
|
|
|
_cameraMatrix, _distCoeffs, rvec, tvec); |
|
|
|
|
|
|
|
|
|
// at least one marker from board so rvec and tvec are valid
|
|
|
|
@ -944,10 +987,10 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
// search undetected markers and project them using the previous pose
|
|
|
|
|
vector< vector< Point2f > > undetectedCorners; |
|
|
|
|
vector< int > undetectedIds; |
|
|
|
|
for(unsigned int i = 0; i < board.ids.size(); i++) { |
|
|
|
|
for(unsigned int i = 0; i < _board->ids.size(); i++) { |
|
|
|
|
int foundIdx = -1; |
|
|
|
|
for(unsigned int j = 0; j < _detectedIds.total(); j++) { |
|
|
|
|
if(board.ids[i] == _detectedIds.getMat().ptr< int >()[j]) { |
|
|
|
|
if(_board->ids[i] == _detectedIds.getMat().ptr< int >()[j]) { |
|
|
|
|
foundIdx = j; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -956,8 +999,8 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
// not detected
|
|
|
|
|
if(foundIdx == -1) { |
|
|
|
|
undetectedCorners.push_back(vector< Point2f >()); |
|
|
|
|
undetectedIds.push_back(board.ids[i]); |
|
|
|
|
projectPoints(board.objPoints[i], rvec, tvec, _cameraMatrix, _distCoeffs, |
|
|
|
|
undetectedIds.push_back(_board->ids[i]); |
|
|
|
|
projectPoints(_board->objPoints[i], rvec, tvec, _cameraMatrix, _distCoeffs, |
|
|
|
|
undetectedCorners.back()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -984,19 +1027,19 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
* Interpolate board markers that are not included in the list of detected markers using |
|
|
|
|
* global homography |
|
|
|
|
*/ |
|
|
|
|
static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArrays _detectedCorners, |
|
|
|
|
static void _projectUndetectedMarkers(Ptr<Board> &_board, InputOutputArrayOfArrays _detectedCorners, |
|
|
|
|
InputOutputArray _detectedIds, |
|
|
|
|
OutputArrayOfArrays _undetectedMarkersProjectedCorners, |
|
|
|
|
OutputArray _undetectedMarkersIds) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// check board points are in the same plane, if not, global homography cannot be applied
|
|
|
|
|
CV_Assert(board.objPoints.size() > 0); |
|
|
|
|
CV_Assert(board.objPoints[0].size() > 0); |
|
|
|
|
float boardZ = board.objPoints[0][0].z; |
|
|
|
|
for(unsigned int i = 0; i < board.objPoints.size(); i++) { |
|
|
|
|
for(unsigned int j = 0; j < board.objPoints[i].size(); j++) { |
|
|
|
|
CV_Assert(boardZ == board.objPoints[i][j].z); |
|
|
|
|
CV_Assert(_board->objPoints.size() > 0); |
|
|
|
|
CV_Assert(_board->objPoints[0].size() > 0); |
|
|
|
|
float boardZ = _board->objPoints[0][0].z; |
|
|
|
|
for(unsigned int i = 0; i < _board->objPoints.size(); i++) { |
|
|
|
|
for(unsigned int j = 0; j < _board->objPoints[i].size(); j++) { |
|
|
|
|
CV_Assert(boardZ == _board->objPoints[i][j].z); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1007,14 +1050,14 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
// missing markers in different vectors
|
|
|
|
|
vector< int > undetectedMarkersIds; // ids of missing markers
|
|
|
|
|
// find markers included in board, and missing markers from board. Fill the previous vectors
|
|
|
|
|
for(unsigned int j = 0; j < board.ids.size(); j++) { |
|
|
|
|
for(unsigned int j = 0; j < _board->ids.size(); j++) { |
|
|
|
|
bool found = false; |
|
|
|
|
for(unsigned int i = 0; i < _detectedIds.total(); i++) { |
|
|
|
|
if(_detectedIds.getMat().ptr< int >()[i] == board.ids[j]) { |
|
|
|
|
if(_detectedIds.getMat().ptr< int >()[i] == _board->ids[j]) { |
|
|
|
|
for(int c = 0; c < 4; c++) { |
|
|
|
|
imageCornersAll.push_back(_detectedCorners.getMat(i).ptr< Point2f >()[c]); |
|
|
|
|
detectedMarkersObj2DAll.push_back( |
|
|
|
|
Point2f(board.objPoints[j][c].x, board.objPoints[j][c].y)); |
|
|
|
|
Point2f(_board->objPoints[j][c].x, _board->objPoints[j][c].y)); |
|
|
|
|
} |
|
|
|
|
found = true; |
|
|
|
|
break; |
|
|
|
@ -1024,9 +1067,9 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
undetectedMarkersObj2D.push_back(vector< Point2f >()); |
|
|
|
|
for(int c = 0; c < 4; c++) { |
|
|
|
|
undetectedMarkersObj2D.back().push_back( |
|
|
|
|
Point2f(board.objPoints[j][c].x, board.objPoints[j][c].y)); |
|
|
|
|
Point2f(_board->objPoints[j][c].x, _board->objPoints[j][c].y)); |
|
|
|
|
} |
|
|
|
|
undetectedMarkersIds.push_back(board.ids[j]); |
|
|
|
|
undetectedMarkersIds.push_back(_board->ids[j]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if(imageCornersAll.size() == 0) return; |
|
|
|
@ -1054,28 +1097,30 @@ static void _projectUndetectedMarkers(const Board &board, InputOutputArrayOfArra |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void refineDetectedMarkers(InputArray _image, const Board &board, |
|
|
|
|
void refineDetectedMarkers(InputArray _image, Ptr<Board> &_board, |
|
|
|
|
InputOutputArrayOfArrays _detectedCorners, InputOutputArray _detectedIds, |
|
|
|
|
InputOutputArray _rejectedCorners, InputArray _cameraMatrix, |
|
|
|
|
InputArray _distCoeffs, float minRepDistance, float errorCorrectionRate, |
|
|
|
|
bool checkAllOrders, OutputArray _recoveredIdxs, |
|
|
|
|
DetectorParameters params) { |
|
|
|
|
const Ptr<DetectorParameters> &_params) { |
|
|
|
|
|
|
|
|
|
CV_Assert(minRepDistance > 0); |
|
|
|
|
|
|
|
|
|
if(_detectedIds.total() == 0 || _rejectedCorners.total() == 0) return; |
|
|
|
|
|
|
|
|
|
DetectorParameters ¶ms = *_params; |
|
|
|
|
|
|
|
|
|
// get projections of missing markers in the board
|
|
|
|
|
vector< vector< Point2f > > undetectedMarkersCorners; |
|
|
|
|
vector< int > undetectedMarkersIds; |
|
|
|
|
if(_cameraMatrix.total() != 0) { |
|
|
|
|
// reproject based on camera projection model
|
|
|
|
|
_projectUndetectedMarkers(board, _detectedCorners, _detectedIds, _cameraMatrix, _distCoeffs, |
|
|
|
|
_projectUndetectedMarkers(_board, _detectedCorners, _detectedIds, _cameraMatrix, _distCoeffs, |
|
|
|
|
undetectedMarkersCorners, undetectedMarkersIds); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
// reproject based on global homography
|
|
|
|
|
_projectUndetectedMarkers(board, _detectedCorners, _detectedIds, undetectedMarkersCorners, |
|
|
|
|
_projectUndetectedMarkers(_board, _detectedCorners, _detectedIds, undetectedMarkersCorners, |
|
|
|
|
undetectedMarkersIds); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1083,8 +1128,9 @@ void refineDetectedMarkers(InputArray _image, const Board &board, |
|
|
|
|
vector< bool > alreadyIdentified(_rejectedCorners.total(), false); |
|
|
|
|
|
|
|
|
|
// maximum bits that can be corrected
|
|
|
|
|
Dictionary &dictionary = *(_board->dictionary); |
|
|
|
|
int maxCorrectionRecalculated = |
|
|
|
|
int(double(board.dictionary.maxCorrectionBits) * errorCorrectionRate); |
|
|
|
|
int(double(dictionary.maxCorrectionBits) * errorCorrectionRate); |
|
|
|
|
|
|
|
|
|
Mat grey; |
|
|
|
|
_convertToGrey(_image, grey); |
|
|
|
@ -1152,7 +1198,7 @@ void refineDetectedMarkers(InputArray _image, const Board &board, |
|
|
|
|
|
|
|
|
|
// extract bits
|
|
|
|
|
Mat bits = _extractBits( |
|
|
|
|
grey, rotatedMarker, board.dictionary.markerSize, params.markerBorderBits, |
|
|
|
|
grey, rotatedMarker, dictionary.markerSize, params.markerBorderBits, |
|
|
|
|
params.perspectiveRemovePixelPerCell, |
|
|
|
|
params.perspectiveRemoveIgnoredMarginPerCell, params.minOtsuStdDev); |
|
|
|
|
|
|
|
|
@ -1161,7 +1207,7 @@ void refineDetectedMarkers(InputArray _image, const Board &board, |
|
|
|
|
.colRange(params.markerBorderBits, bits.rows - params.markerBorderBits); |
|
|
|
|
|
|
|
|
|
codeDistance = |
|
|
|
|
board.dictionary.getDistanceToId(onlyBits, undetectedMarkersIds[i], false); |
|
|
|
|
dictionary.getDistanceToId(onlyBits, undetectedMarkersIds[i], false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// if everythin is ok, assign values to current best match
|
|
|
|
@ -1250,7 +1296,7 @@ void refineDetectedMarkers(InputArray _image, const Board &board, |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, const Board &board, |
|
|
|
|
int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, Ptr<Board> &board, |
|
|
|
|
InputArray _cameraMatrix, InputArray _distCoeffs, OutputArray _rvec, |
|
|
|
|
OutputArray _tvec) { |
|
|
|
|
|
|
|
|
@ -1279,33 +1325,33 @@ int estimatePoseBoard(InputArrayOfArrays _corners, InputArray _ids, const Board |
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void GridBoard::draw(Size outSize, OutputArray _img, int marginSize, int borderBits) { |
|
|
|
|
aruco::drawPlanarBoard((*this), outSize, _img, marginSize, borderBits); |
|
|
|
|
_drawPlanarBoardImpl(this, outSize, _img, marginSize, borderBits); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
GridBoard GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, |
|
|
|
|
Dictionary _dictionary) { |
|
|
|
|
|
|
|
|
|
GridBoard res; |
|
|
|
|
Ptr<GridBoard> GridBoard::create(int markersX, int markersY, float markerLength, float markerSeparation, |
|
|
|
|
Ptr<Dictionary> &dictionary) { |
|
|
|
|
|
|
|
|
|
CV_Assert(markersX > 0 && markersY > 0 && markerLength > 0 && markerSeparation > 0); |
|
|
|
|
|
|
|
|
|
res._markersX = markersX; |
|
|
|
|
res._markersY = markersY; |
|
|
|
|
res._markerLength = markerLength; |
|
|
|
|
res._markerSeparation = markerSeparation; |
|
|
|
|
res.dictionary = _dictionary; |
|
|
|
|
Ptr<GridBoard> res = makePtr<GridBoard>(); |
|
|
|
|
|
|
|
|
|
res->_markersX = markersX; |
|
|
|
|
res->_markersY = markersY; |
|
|
|
|
res->_markerLength = markerLength; |
|
|
|
|
res->_markerSeparation = markerSeparation; |
|
|
|
|
res->dictionary = dictionary; |
|
|
|
|
|
|
|
|
|
size_t totalMarkers = (size_t) markersX * markersY; |
|
|
|
|
res.ids.resize(totalMarkers); |
|
|
|
|
res.objPoints.reserve(totalMarkers); |
|
|
|
|
res->ids.resize(totalMarkers); |
|
|
|
|
res->objPoints.reserve(totalMarkers); |
|
|
|
|
|
|
|
|
|
// fill ids with first identifiers
|
|
|
|
|
for(unsigned int i = 0; i < totalMarkers; i++) { |
|
|
|
|
res.ids[i] = i; |
|
|
|
|
res->ids[i] = i; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// calculate Board objPoints
|
|
|
|
@ -1319,7 +1365,7 @@ GridBoard GridBoard::create(int markersX, int markersY, float markerLength, floa |
|
|
|
|
corners[1] = corners[0] + Point3f(markerLength, 0, 0); |
|
|
|
|
corners[2] = corners[0] + Point3f(markerLength, -markerLength, 0); |
|
|
|
|
corners[3] = corners[0] + Point3f(0, -markerLength, 0); |
|
|
|
|
res.objPoints.push_back(corners); |
|
|
|
|
res->objPoints.push_back(corners); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1403,15 +1449,13 @@ void drawAxis(InputOutputArray _image, InputArray _cameraMatrix, InputArray _dis |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void drawMarker(Dictionary dictionary, int id, int sidePixels, OutputArray _img, int borderBits) { |
|
|
|
|
dictionary.drawMarker(id, sidePixels, _img, borderBits); |
|
|
|
|
void drawMarker(Ptr<Dictionary> &dictionary, int id, int sidePixels, OutputArray _img, int borderBits) { |
|
|
|
|
dictionary->drawMarker(id, sidePixels, _img, borderBits); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void drawPlanarBoard(const Board &board, Size outSize, OutputArray _img, int marginSize, |
|
|
|
|
void _drawPlanarBoardImpl(Board *_board, Size outSize, OutputArray _img, int marginSize, |
|
|
|
|
int borderBits) { |
|
|
|
|
|
|
|
|
|
CV_Assert(outSize.area() > 0); |
|
|
|
@ -1424,17 +1468,17 @@ void drawPlanarBoard(const Board &board, Size outSize, OutputArray _img, int mar |
|
|
|
|
out.colRange(marginSize, out.cols - marginSize).rowRange(marginSize, out.rows - marginSize); |
|
|
|
|
|
|
|
|
|
// calculate max and min values in XY plane
|
|
|
|
|
CV_Assert(board.objPoints.size() > 0); |
|
|
|
|
CV_Assert(_board->objPoints.size() > 0); |
|
|
|
|
float minX, maxX, minY, maxY; |
|
|
|
|
minX = maxX = board.objPoints[0][0].x; |
|
|
|
|
minY = maxY = board.objPoints[0][0].y; |
|
|
|
|
minX = maxX = _board->objPoints[0][0].x; |
|
|
|
|
minY = maxY = _board->objPoints[0][0].y; |
|
|
|
|
|
|
|
|
|
for(unsigned int i = 0; i < board.objPoints.size(); i++) { |
|
|
|
|
for(unsigned int i = 0; i < _board->objPoints.size(); i++) { |
|
|
|
|
for(int j = 0; j < 4; j++) { |
|
|
|
|
minX = min(minX, board.objPoints[i][j].x); |
|
|
|
|
maxX = max(maxX, board.objPoints[i][j].x); |
|
|
|
|
minY = min(minY, board.objPoints[i][j].y); |
|
|
|
|
maxY = max(maxY, board.objPoints[i][j].y); |
|
|
|
|
minX = min(minX, _board->objPoints[i][j].x); |
|
|
|
|
maxX = max(maxX, _board->objPoints[i][j].x); |
|
|
|
|
minY = min(minY, _board->objPoints[i][j].y); |
|
|
|
|
maxY = max(maxY, _board->objPoints[i][j].y); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1459,14 +1503,15 @@ void drawPlanarBoard(const Board &board, Size outSize, OutputArray _img, int mar |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// now paint each marker
|
|
|
|
|
for(unsigned int m = 0; m < board.objPoints.size(); m++) { |
|
|
|
|
Dictionary &dictionary = *(_board->dictionary); |
|
|
|
|
for(unsigned int m = 0; m < _board->objPoints.size(); m++) { |
|
|
|
|
|
|
|
|
|
// transform corners to markerZone coordinates
|
|
|
|
|
vector< Point2f > outCorners; |
|
|
|
|
outCorners.resize(4); |
|
|
|
|
for(int j = 0; j < 4; j++) { |
|
|
|
|
Point2f p0, p1, pf; |
|
|
|
|
p0 = Point2f(board.objPoints[m][j].x, board.objPoints[m][j].y); |
|
|
|
|
p0 = Point2f(_board->objPoints[m][j].x, _board->objPoints[m][j].y); |
|
|
|
|
// remove negativity
|
|
|
|
|
p1.x = p0.x - minX; |
|
|
|
|
p1.y = p0.y - minY; |
|
|
|
@ -1476,9 +1521,9 @@ void drawPlanarBoard(const Board &board, Size outSize, OutputArray _img, int mar |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// get tiny marker
|
|
|
|
|
int tinyMarkerSize = 10 * board.dictionary.markerSize + 2; |
|
|
|
|
int tinyMarkerSize = 10 * dictionary.markerSize + 2; |
|
|
|
|
Mat tinyMarker; |
|
|
|
|
board.dictionary.drawMarker(board.ids[m], tinyMarkerSize, tinyMarker, borderBits); |
|
|
|
|
dictionary.drawMarker(_board->ids[m], tinyMarkerSize, tinyMarker, borderBits); |
|
|
|
|
|
|
|
|
|
// interpolate tiny marker to marker position in markerZone
|
|
|
|
|
Mat inCorners(4, 1, CV_32FC2); |
|
|
|
@ -1506,10 +1551,19 @@ void drawPlanarBoard(const Board &board, Size outSize, OutputArray _img, int mar |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
void drawPlanarBoard(Ptr<Board> &_board, Size outSize, OutputArray _img, int marginSize, |
|
|
|
|
int borderBits) { |
|
|
|
|
_drawPlanarBoardImpl(_board, outSize, _img, marginSize, borderBits); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*/ |
|
|
|
|
double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputArray _counter, |
|
|
|
|
const Board &board, Size imageSize, InputOutputArray _cameraMatrix, |
|
|
|
|
Ptr<Board> &board, Size imageSize, InputOutputArray _cameraMatrix, |
|
|
|
|
InputOutputArray _distCoeffs, OutputArrayOfArrays _rvecs, |
|
|
|
|
OutputArrayOfArrays _tvecs, int flags, TermCriteria criteria) { |
|
|
|
|
|
|
|
|
@ -1544,5 +1598,8 @@ double calibrateCameraAruco(InputArrayOfArrays _corners, InputArray _ids, InputA |
|
|
|
|
return calibrateCamera(processedObjectPoints, processedImagePoints, imageSize, _cameraMatrix, |
|
|
|
|
_distCoeffs, _rvecs, _tvecs, flags, criteria); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|