detectMarkers 函数是 Aruco 标记检测的核心函数之一,其主要作用是从输入图像中检测出 Aruco 标记,并输出它们的角点、ID 以及其他相关信息。以下是对 detectMarkers 函数代码的主要功能和流程的详细解释:
void detectMarkers(InputArray _image, OutputArrayOfArrays _corners, OutputArray _ids,
OutputArrayOfArrays _rejectedImgPoints, OutputArray _dictIndices, DictionaryMode dictMode)
_image
: 输入图像(单通道或三通道)。_corners
: 输出检测到的标记角点数组。_ids
: 输出检测到的标记 ID 数组。_rejectedImgPoints
: 被拒绝的候选角点。_dictIndices
: 检测到的标记对应的字典索引(多字典模式下使用)。dictMode
: 字典模式,支持 Single
和 Multi
。Mat grey;
_convertToGrey(_image, grey);
将输入图像转换为灰度图 grey
,以便后续处理。
CV_Assert(!_image.empty());
CV_Assert(detectorParams.markerBorderBits > 0);
CV_Assert(!(detectorParams.useAruco3Detection == true && detectorParams.minSideLengthCanonicalImg == 0 && detectorParams.minMarkerLengthRatioOriginalImg == 0.0));
对输入图像和检测参数进行有效性检查:
markerBorderBits
大于 0。useAruco3Detection
),需要确保 minSideLengthCanonicalImg
或 minMarkerLengthRatioOriginalImg
有有效值。vector grey_pyramid;
int closest_pyr_image_idx = 0, num_levels = 0;
if (detectorParams.useAruco3Detection) {
const float scale_pyr = 2.f;
const float img_area = static_cast(grey.rows * grey.cols);
const float min_area_marker = static_cast(detectorParams.minSideLengthCanonicalImg *
detectorParams.minSideLengthCanonicalImg);
num_levels = static_cast(log2(img_area / min_area_marker)/scale_pyr);
const float scale_img_area = img_area * fxfy * fxfy;
closest_pyr_image_idx = cvRound(log2(img_area / scale_img_area)/scale_pyr);
}
buildPyramid(grey, grey_pyramid, num_levels);
// resize to segmentation image
if (fxfy != 1.f)
resize(grey, grey, Size(cvRound(fxfy * grey.cols), cvRound(fxfy * grey.rows)));
grey_pyramid
,用于多尺度检测。vector > candidates;
vector > contours;
if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){
_apriltag(grey, detectorParams, candidates, contours);
} else {
detectCandidates(grey, candidates, contours);
}
_apriltag
或 detectCandidates
方法检测候选标记。detectCandidates
调用 _detectInitialCandidates
和 _reorderCandidatesCorners
来检测和排序候选标记角点。vector ids;
vector> rejectedImgPoints;
vector selectedCandidates;
if (DictionaryMode::Single == dictMode) {
Dictionary& dictionary = dictionaries.at(0);
auto selectedCandidates = filterTooCloseCandidates(grey.size(), candidates, contours, dictionary.markerSize);
...
}
filterTooCloseCandidates
过滤过于接近的候选标记,并返回经过筛选的候选标记树 selectedCandidates
。identifyCandidates(grey, grey_pyramid, selectedCandidates, candidates, contours, ids, dictionary, rejectedImgPoints);
identifyCandidates
对候选标记进行识别,判断是否符合指定字典中的标记格式。if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) {
performCornerSubpixRefinement(grey, grey_pyramid, closest_pyr_image_idx, candidates, dictionary);
}
performCornerSubpixRefinement
对检测到的标记角点进行优化。if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_CONTOUR) {
parallel_for_(Range(0, (int)candidates.size()), [&](const Range& range) {
for (int i = range.start; i < range.end; i++) {
_refineCandidateLines(contours[i], candidates[i]);
}
});
}
CORNER_REFINE_CONTOUR
),则调用 _refineCandidateLines
对角点进行进一步优化。_copyVector2Output(candidates, _corners);
Mat(ids).copyTo(_ids);
if(_rejectedImgPoints.needed()) {
_copyVector2Output(rejectedImgPoints, _rejectedImgPoints);
}
if (_dictIndices.needed()) {
Mat(dictIndices).copyTo(_dictIndices);
}
detectMarkers
函数的主要流程如下:
该函数实现了从原始图像到 Aruco 标记检测的完整流程,涵盖了检测、识别和优化等多个关键步骤。