检测ArUco板
来源OpenCV http://docs.opencv.org/master/db/da9/tutorial_aruco_board_detection.html
1 效果图
板与轴
这是另一个例子与板部分堵塞:
板与遮挡
因为它可以观察到,尽管一些标记没有被发现,板构成仍然可以估计的标记。
ArUco板是一组标记,就像一个标志,它提供了一个单一的姿势相机。
最受欢迎的板是一个标记在同一平面上,因为它可以很容易地印刷:
然而,板并不局限于这种安排,可以代表任何2 d或3 d布局。
板的区别和一组独立的标记,标记之间的相对位置板是已知的先验。这允许的所有标记可用于估计相机的姿势对整个板。
当你使用一组独立的标志,你可以估计每个单独标记的姿势,因为你不知道在环境中相对位置的标记。
使用板的主要好处是:
· 姿势估计更多功能。 只有一些标记进行姿态估计是必要的。 因此,构成可以计算出即使在遮挡或部分观点的存在。
· 以来获得的姿势通常是更准确的点对应(标记角落)。
aruco模块允许使用。 主要的类 cv::aruco::Board 类定义板布局:
class Board {
public:
std::vector > objPoints;
cv::Ptr dictionary;
std::vector ids;
};
一个类型的对象 板 有三个参数:
· 的 objPoints结构的3 d板的角落位置参考系统,即它的布局。 对于每一个标记,它的四个角落都存储在标准的顺序,即按顺时针方向从左上角开始。
· 的 字典参数表示的标记字典属于板标记。
· 最后, id结构表明每个标记的标识符 objPoints对指定的 字典。
板检测类似于标准标志检测。唯一的区别是在姿态估计的步骤。 事实上,使用标志板,一个标准的标记检测应该做过评估板构成。
aruco模块提供了一个特定的函数, estimatePoseBoard()板,执行评估:
cv::Mat inputImage;
// camera parameters are read from somewhere
cv::Mat cameraMatrix, distCoeffs;
readCameraParameters(cameraMatrix, distCoeffs);
// assume we have a function to create the board object
cv::Ptr board = cv::aruco::Board::create();
...
std::vector markerIds;
std::vector> markerCorners;
cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds);
// if at least one marker detected
if(markerIds.size() > 0) {
cv::Vec3d rvec, tvec;
int valid = cv::aruco::estimatePoseBoard(markerCorners, markerIds, board, cameraMatrix, distCoeffs, rvec, tvec);
}
estimatePoseBoard的参数是:
· markerCorners和 markerIds:检测到标记的结构 detectMarkers()函数。
· 板 : 板 对象定义了板布局及其id
· cameraMatrix和 distCoeffs:相机标定参数姿态估计的必要条件。
· rvec和 tvec预测:板的构成。如果非空,那么作为初始猜测。
· 函数返回的标记用于估计总数板构成。注意,并不是所有的提供的标记 markerCorners和 markerIds应该使用,因为只有中列出的标记的id吗 板:id结构被认为是。
的 drawAxis()函数可以用来检查获得的姿势。 例如:
And this is another example with the board partially occluded:
因为它可以观察到,尽管一些标记没有被发现,板构成仍然可以估计的标记。
创建 板 对象需要指定环境中的每个标记的角落位置。然而,在许多情况下,板将只是一组标记在同一个平面上,在网格布局,所以它可以很容易地印刷和使用。
幸运的是,aruco模块提供的基本功能来创建和打印这些类型的标记。
的 GridBoard类是一个专业类的继承 板 类代表板和所有的标记在同一个平面上,在网格布局,如以下图片:
图像与aruco板
具体的坐标系统在网格板放置在板平面,集中在左下角的板和Z指出,像下图(X:红Y:绿色,Z:蓝色):
板与轴
一个 GridBoard对象可以定义使用以下参数:
可以很容易地创建这个对象从这些参数使用 cv::aruco::GridBoard::create()静态函数:
一个 GridBoard对象可以定义使用以下参数:
· 标记在X方向上。
· 标记在Y方向上。
· 长度的标记。
· 的长度标记分离。
· 字典的标记。
· 所有标记的id(X * Y标记)。
可以很容易地创建这个对象从这些参数使用 cv::aruco::GridBoard::create()
静态函数:
cv::aruco::GridBoard board = cv::aruco::GridBoard::create(5, 7, 0.04, 0.01, dictionary);
·
所以,这个委员会将由5 x7 = 35标记。 每个标记的id分配,默认情况下,按升序开始0,所以他们将0,1,2,… 34岁。 这可以很容易地定制的访问id向量通过 board.ids
,就像在 板
父类。
。
创建一个网格板后,我们可能需要打印它并使用它。 一个函数生成的形象 GridBoard中提供
了 cv::aruco::GridBoard::draw().。例如:
cv::Ptr board = cv::aruco::GridBoard::create(5, 7, 0.04, 0.01, dictionary);
cv::Mat boardImage;
board->draw( cv::Size(600, 500), boardImage, 10, 1 );
boardImage
:输出图像。drawMarker()
函数。 默认值是1。输出图像将是这样的:
一个完整的工作示例板创建包含在 create_board.cpp
模块内的样本文件夹。
注意:样品现在通过命令行通过输入 OpenCV命令行解析器 。 这个文件的示例参数
"_output path_/aboard.png" -w=5 -h=7 -l=100 -s=10 -d=10最后,一个完整的板检测的例子:
cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Mat cameraMatrix, distCoeffs;
// camera parameters are read from somewhere
readCameraParameters(cameraMatrix, distCoeffs);
cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr board = cv::aruco::GridBoard::create(5, 7, 0.04, 0.01, dictionary);
while (inputVideo.grab()) {
cv::Mat image, imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector ids;
std::vector > corners;
cv::aruco::detectMarkers(image, dictionary, corners, ids);
// if at least one marker detected
if (ids.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);
cv::Vec3d rvec, tvec;
int valid = estimatePoseBoard(corners, ids, board, cameraMatrix, distCoeffs, rvec, tvec);
// if at least one board marker detected
if(valid > 0)
cv::aruco::drawAxis(imageCopy, cameraMatrix, distCoeffs, rvec, tvec, 0.1);
}
cv::imshow("out", imageCopy);
char key = (char) cv::waitKey(waitTime);
if (key == 27)
break;
}
一个完整的工作包含在示例 detect_board.cpp
模块内的样本文件夹。
注意:样品现在通过命令行通过输入 OpenCV命令行解析器 。 这个文件的示例参数
ArUco板也可以用来提高标记的检测。 如果我们有检测到标记的一个子集,属于板,我们可以使用这些标记和板会布局信息,试图找到先前没有被检测到的标记。
这可以通过使用 refineDetectedMarkers()
之后不应调用函数,该函数调用 detectMarkers()
。
这个函数的主要参数是原始图像标记检测板对象,检测到标记,检测到标记id和被拒绝的标志。
被拒绝的角落可以获得的 detectMarkers()
函数,也称为标记的候选人。 这个候选人是方形的形状已经发现在原始图像,但是未能通过识别步骤(即内心编纂了太多错误),因此他们没有被认为是标记。
然而,这些候选人有时实际标记没有被正确地识别图像中由于高噪音,非常低的分辨率或其他相关问题影响到二进制代码提取。 的 refineDetectedMarkers()
函数发现这些候选人之间的通讯和失踪的标记。 这个搜索是基于两个参数:
minRepDistance
参数 refineDetectedMarkers()
确定候选人之间的最小欧氏距离角落和预测标记(默认值10)。errorCorrectionRate
3.0参数(默认值)。 如果提供了负值,内部位根本不分析,只有角落距离评估。 这是一个使用的例子 refineDetectedMarkers()
功能:
cv::Ptr dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr board = cv::aruco::GridBoard::create(5, 7, 0.04, 0.01, dictionary);
std::vector markerIds;
std::vector> markerCorners, rejectedCandidates;
cv::aruco::detectMarkers(inputImage, dictionary, markerCorners, markerIds, cv::aruco::DetectorParameters(), rejectedCandidates);
cv::aruco::refineDetectedMarkersinputImage, board, markerCorners, markerIds, rejectedCandidates);
// After calling this function, if any new marker has been detected it will be removed from rejectedCandidates and included
// at the end of markerCorners and markerIds
它还必须指出,在某些情况下,如果检测到标记的数量在第一时间太低(例如只有1或2标记),失踪的预测标记可以是糟糕的质量,产生错误的通讯。
查看更详细的实现模块样品。