【计算机视觉】 相机姿态估计之标记检测-检测ChArUco角点3

  • Post author:
  • Post category:其他


检测ChArUco角点


这张图片:

choriginal.png


图像与Charuco董事会


结果将是:

chcorners.png


Charuco板检测


在闭塞的存在。 像下面的图片,虽然有些角落清晰可见,并不是所有周围的标记已发现由于阻塞,因此,他们不是插值:

chocclusion.png


Charuco检测与闭塞

2 原理介绍


ArUco标记和



非常有用因为他们的快速检测和多功能性。 然而,ArUco标记的问题之一是,角落的位置的准确性不是太高,即使应用亚像素细分。


相反,棋盘模式的角落可以更准确地提炼因为每个角落周围是两个黑色方块。 然而,找到一个棋盘模式不像找到一个通用的ArUco板:它必须是完全可见,遮挡不允许。


ChArUco



试图结合这两种方式的好处:

charucodefinition.png


Charuco定义


ArUco部分用于插入棋盘角落的位置,这样它的多功能性标记板,因为它允许遮挡或部分视图。 此外,由于插值角落属于一个棋盘,他们非常准确的亚像素精度。


当精度高是必要的,比如在相机标定,Charuco



是一个更好的选择比标准Aruco





ChArUco



创造


aruco模块提供了




简历:aruco::CharucoBoard




类代表一个Charuco



和继承的









类。


ChArUco余下的功能,这个类中定义:


# include < opencv2 / aruco / charuco.hpp >


定义一个



CharucoBoard



,它是必要的:


  • 在X方向上棋盘方格的数量。

  • 棋盘方格的数量在Y方向上。

  • 广场边的长度。

  • 标记的长度。

  • 字典的标记。

  • id的所有标记。


至于



GridBoard



对象,aruco模块提供了一个函数来创建



CharucoBoard



很容易。 这个函数是静态函数





cv::aruco::CharucoBoard::create()



:



简历::aruco:CharucoBoard董事会=简历::aruco:CharucoBoard:创建(5、7、0.04、0.02、字典);


  • 第一和第二参数是方块的数量分别在X和Y方向。

  • 第三个和第四个参数分别广场和标记的长度。 他们可以在任何单位提供,记住,估计姿势这个委员会将以相同的单位(通常是使用米)。

  • 最后,提供了标记的字典。


每一个标记的id分配默认按升序,从0开始,喜欢



GridBoard:create()



。 这可以很容易地定制的访问id向量通过



board.ids



,就像在









父类。


一旦我们有



CharucoBoard



对象,我们可以创建一个图像打印它。 这可以完成了


{CharucoBoard::draw()```}
``` c++
    cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
    cv::Mat boardImage;
    board->draw( cv::Size(600, 500), boardImage, 10, 1 );



  • 第一个参数是输出图像的像素大小。 在这种情况下600 x500像素。 如果这不是



    规模成正比,它将以图像为中心。


  • boardImage



    :输出图像。

  • 第三个参数(可选)边缘的像素,所以所有的标记都触碰图像边界。 在这种情况下,保证是10。

  • 最后,标记边界的大小,类似




    drawMarker()




    函数。 默认值是1。


输出图像将是这样的:


一个完整的工作包含在示例



create_board_charuco.cpp



模块内的样本文件夹。


注意:样品现在通过命令行通过输入


OpenCV命令行解析器


。 这个文件的示例参数



“_ output path_/chboard.png” -w=5 -h=7 -sl=200 -ml=120 -d=10


ChArUco板检测


当你检测ChArUco



,你实际上是检测是棋盘上的每一个角落。


上的每一个角落ChArUco板都有一个唯一的标识符(id)。 这些id从0到角落的总数。


所以,发现ChArUco板包括:





    • std::vector<

      cv::Point2f

      > charucoCorners

      : list of image positions of the detected corners.

    • std::vector<int> charucoIds

      : ids for each of the detected corners in

      charucoCorners

      .


ChArUco角落的检测是基于前面的检测标记。 因此,首先检测到标记,然后ChArUco角落从标记插入。


的功能检测ChArUco角落




CV::aruco::interpolateCornersCharuco()




。 这个例子展示了整个过程。 首先,检测到标记,然后ChArUco角落从这些标记将被内插。


cv::Mat inputImage;
cv::Mat cameraMatrix, distCoeffs;
// camera parameters are read from somewhere
readCameraParameters(cameraMatrix, distCoeffs);
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
...
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners;
cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds);
// if at least one marker detected
if(markerIds.size() > 0) {
    std::vector<cv::Point2f> charucoCorners;
    std::vector<int> charucoIds;
    cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, inputImage, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);
}



的参数




interpolateCornersCharuco()




函数是:



  • markerCorners







    markerIds



    :检测到标记




    detectMarkers()




    函数。


  • inputImage



    :标记检测的原始图像。 执行所需的图像亚像素细分ChArUco角落。








  • :



    CharucoBoard



    对象


  • charucoCorners







    charucoIds



    :输出插入Charuco角落


  • cameraMatrix







    distCoeffs



    :可选相机标定参数

  • 插值函数返回的数量Charuco角落。


在这种情况下,我们有电话




interpolateCornersCharuco()




提供摄像机标定参数。 然而这些参数是可选的。 没有这些参数类似的例子是:


cv::Mat inputImage;
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
...
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners;
cv::Ptr<cv::aruco::DetectorParameters> params;
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE;
cv::aruco::detectMarkers(inputImage, board.dictionary, markerCorners, markerIds, params);
// if at least one marker detected
if(markerIds.size() > 0) {
    std::vector<cv::Point2f> charucoCorners;
    std::vector<int> charucoIds;
    cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, inputImage, board, charucoCorners, charucoIds);
}



如果校准参数提供,ChArUco角落内插,首先,估计ArUco标记和一个粗略的姿势,然后,reprojecting ChArUco角落回到形象。


另一方面,如果不提供校准参数,ChArUco角落被计算相应的插值ChArUco平面和ChArUco图像之间的单应性投影。


使用单应性的主要问题是,插值更明智的图像失真。 实际上,单应性只使用最接近的执行标记每个ChArUco角落减少失真的影响。


当检测标记ChArUco



特别是当使用单应性,建议禁用标志的角落细化。 这个的原因是,由于接近棋盘的广场,构造过程可以产生重要的在角落位置偏差,这些偏差传播到插值ChArUco角落,产生不良的结果。


此外,只有那些角落里的两个周围标记返回。 如果任何周围的两个标记没有被检测到,这通常意味着有阻塞或区域的图像质量不好。 在任何情况下,最好不要考虑到角落里,因为我们想要确保插值ChArUco角落非常准确。


ChArUco角落已经插入后,进行亚像素细分。


一旦我们有插值ChArUco角落,我们可能会想画,看看他们的检测是正确的。 这可以很容易地使用完成




drawDetectedCornersCharuco()




功能:



cv::aruco::drawDetectedCornersCharuco(image, charucoCorners, charucoIds, color)



  • 图像



    是将图像的角落(它将通常是相同的图像的角落发现了)。





  • outputImage



    将是一个克隆的



    inputImage



    的角落。


  • charucoCorners







    charucoIds



    的发现Charuco角落吗




    interpolateCornersCharuco()




    函数。

  • 最后,最后一个参数(可选)的颜色我们要画的角落,类型的




    简历:标量






这张图片:

choriginal.png


图像与Charuco




结果将是:

chcorners.png


Charuco板检测


在闭塞的存在。 像下面的图片,虽然有些角落清晰可见,并不是所有周围的标记已发现由于阻塞,因此,他们不是插值:

chocclusion.png


Charuco检测与闭塞


最后,这是一个完整的示例ChArUco检测(不使用校准参数):


cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params;
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE;
while (inputVideo.grab()) {
    cv::Mat image, imageCopy;
    inputVideo.retrieve(image);
    image.copyTo(imageCopy);
    std::vector<int> ids;
    std::vector<std::vector<cv::Point2f>> corners;
    cv::aruco::detectMarkers(image, dictionary, corners, ids, params);
    // if at least one marker detected
    if (ids.size() > 0) {
        cv::aruco::drawDetectedMarkers(imageCopy, corners, ids);
        std::vector<cv::Point2f> charucoCorners;
        std::vector<int> charucoIds;
        cv::aruco::interpolateCornersCharuco(corners, ids, image, board, charucoCorners, charucoIds);
        // if at least one charuco corner detected
        if(charucoIds.size() > 0)
            cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, cv::Scalar(255, 0, 0));
    }
    cv::imshow("out", imageCopy);
    char key = (char) cv::waitKey(waitTime);
    if (key == 27)
        break;
}



一个完整的工作包含在示例



detect_board_charuco.cpp



模块内的样本文件夹。


注意:样品现在通过命令行通过输入


OpenCV命令行解析器


。 这个文件的示例参数



-c=”_path_/calib.txt” -dp=”_path_/detector_params.yml” -w=5 -h=7 -sl=0.04 -ml=0.02 -d=10


ChArUco姿势估计


ChArUco



的最终目标是找到角落非常准确地高精度校准或估计。


aruco模块提供了一个函数来执行ChArUco姿势容易估计。 就像在



GridBoard



的坐标系统



CharucoBoard



被放置在板平面与Z轴指出,集中在



的左下角。


造成估计的函数




estimatePoseCharucoBoard()




:



cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec)






  • charucoCorners







    charucoIds



    参数的检测从charuco角落




    interpolateCornersCharuco()




    函数。

  • 第三个参数是



    CharucoBoard



    对象。





  • cameraMatrix







    distCoeffs



    摄像机标定参数为姿势估计是必要的。

  • 最后,



    rvec







    tvec



    参数输出Charuco



    的构成。

  • 函数返回true,如果姿势是正确估计和错误的。 失败的主要原因是没有足够的角落姿势估计或他们在同一条线上。


轴可以用




drawAxis()




检查姿势正确估计。 结果是:(X:红Y:绿色,Z:蓝色)

chaxis.png


Charuco板轴


一个完整的例子与姿势估计ChArUco检测:


cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Mat cameraMatrix, distCoeffs;
// camera parameters are read from somewhere
readCameraParameters(cameraMatrix, distCoeffs);
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary);
while (inputVideo.grab()) {
cv::Mat image, imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> ids;
std::vector<std::vector<cv::Point2f>> corners;
cv::aruco::detectMarkers(image, dictionary, corners, ids);
// if at least one marker detected
if (ids.size() > 0) {
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(corners, ids, image, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);
// if at least one charuco corner detected
if(charucoIds.size() > 0) {
cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, cv::Scalar(255, 0, 0));
cv::Vec3d rvec, tvec;
bool valid = cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec);
// if charuco pose is valid
if(valid)
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_charuco.cpp



模块内的样本文件夹。


注意:样品现在通过命令行通过输入


OpenCV命令行解析器


。 这个文件的示例参数



“_path_/calib.txt” -dp=”_path_/detector_params.yml” -w=5 -h=7 -sl=0.04 -ml=0.02 -d=10