opencv3.4.15源文档链接: link
ChArUco标定板试图结合这两种方法的优点:
ArUco部分用于插值棋盘角点的位置,因此它具有标记板的多功能性,因为它允许遮挡或部分视图。此外,由于插值角点属于棋盘,它们在亚像素精度方面非常精确。
当需要高精度时,例如在相机校准中,Charuco板是比标准Aruco板更好的选择。
在本教程中,您将学习:
您可以在opencv_contrib/modules/aruco/samples/tutorial_charuco_create_detect.cpp中找到此代码
下面是如何实现目标列表中列出的所有内容的示例代码。
#include
#include
#include
#include
namespace {
const char* about = "A tutorial code on charuco board creation and detection of charuco board with and without camera caliberation";
const char* keys = "{c | | Put value of c=1 to create charuco board;\nc=2 to detect charuco board without camera calibration;\nc=3 to detect charuco board with camera calibration and Pose Estimation}";
}
void createBoard();
void detectCharucoBoardWithCalibrationPose();
void detectCharucoBoardWithoutCalibration();
static bool readCameraParameters(std::string filename, cv::Mat& camMatrix, cv::Mat& distCoeffs)
{
cv::FileStorage fs(filename, cv::FileStorage::READ);
if (!fs.isOpened())
return false;
fs["camera_matrix"] >> camMatrix;
fs["distortion_coefficients"] >> distCoeffs;
return (camMatrix.size() == cv::Size(3,3)) ;
}
void createBoard()
{
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.04f, 0.02f, dictionary);
cv::Mat boardImage;
board->draw(cv::Size(600, 500), boardImage, 10, 1);
cv::imwrite("BoardImage.jpg", boardImage);
}
void detectCharucoBoardWithCalibrationPose()
{
cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Mat cameraMatrix, distCoeffs;
std::string filename = "calib.txt";
bool readOk = readCameraParameters(filename, cameraMatrix, distCoeffs);
if (!readOk) {
std::cerr << "Invalid camera file" << std::endl;
} else {
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.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
while (inputVideo.grab()) {
cv::Mat image;
cv::Mat imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f> > markerCorners;
cv::aruco::detectMarkers(image, board->dictionary, markerCorners, markerIds, params);
// if at least one marker detected
if (markerIds.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, markerCorners, markerIds);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, image, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);
// if at least one charuco corner detected
if (charucoIds.size() > 0) {
cv::Scalar color = cv::Scalar(255, 0, 0);
cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, color);
cv::Vec3d rvec, tvec;
// cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, 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.1f);
}
}
cv::imshow("out", imageCopy);
char key = (char)cv::waitKey(30);
if (key == 27)
break;
}
}
}
void detectCharucoBoardWithoutCalibration()
{
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.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE;
while (inputVideo.grab()) {
cv::Mat image, imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f> > markerCorners;
cv::aruco::detectMarkers(image, board->dictionary, markerCorners, markerIds, params);
//or
//cv::aruco::detectMarkers(image, dictionary, markerCorners, markerIds, params);
// if at least one marker detected
if (markerIds.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, markerCorners, markerIds);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, 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(30);
if (key == 27)
break;
}
}
int main(int argc, char* argv[])
{
cv::CommandLineParser parser(argc, argv, keys);
parser.about(about);
if (argc < 2) {
parser.printMessage();
return 0;
}
int choose = parser.get<int>("c");
switch (choose) {
case 1:
createBoard();
std::cout << "An image named BoardImg.jpg is generated in folder containing this file" << std::endl;
break;
case 2:
detectCharucoBoardWithoutCalibration();
break;
case 3:
detectCharucoBoardWithCalibrationPose();
break;
default:
break;
}
return 0;
}
aruco模块提供了cv::aruco::CharucoBoard类,它表示一个Charuco标定板,继承自Board类。
这个类和ChArUco的其他函数一样,定义在:
#include
要定义CharucoBoard,需要:
GridBoard
对象,aruco模块提供了一个函数来轻松创建charucoboard。这个函数是静态函数cv::aruco::CharucoBoard::create()
:```cv::aruco::CharucoBoard board = cv::aruco::CharucoBoard::create(5, 7, 0.04, 0.02, dictionary); ```
默认情况下,每个标记的id按升序分配,并从0开始,就像GridBoard::create()
中那样。就像Board
的父类一样,可以通过board.ids
访问ids向量很容易地自定义。
有了CharucoBoard
对象之后,就可以创建一个图像来打印它。这可以通过CharucoBoard::draw()
方法来完成:
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(5, 7, 0.04f, 0.02f, dictionary);
cv::Mat boardImage;
board->draw(cv::Size(600, 500), boardImage, 10, 1); ```
boardImage
:带有板的输出图像。drawMarker()
函数。缺省值为1。输出的图像将是这样的:
完整的工作示例包含在模块示例文件夹中的create_board_charuco.cpp
中。
注意:create_board_charuco.cpp现在通过OpenCV命令行解析器通过命令行获取输入。对于这个文件,示例参数如下所示
"_ output path_/chboard.png" -w=5 -h=7 -sl=200 -ml=120 -d=10
当你检测ChArUco板子时,你实际上是在检测板子的每个棋盘角点。
ChArUco板上的每个角点都有一个分配的唯一标识符(id)。这些id从0到标定板上的角点的总数。
charuco标定板检测的步骤可以分解为以下步骤:
cv::Mat image;
要检测标记的原始图像。图像是必要的执行亚像素细化ChArUco角。
cv::Mat cameraMatrix, distCoeffs;
std::string filename = "calib.txt";
bool readOk = readCameraParameters(filename, cameraMatrix, distCoeffs);
readcameraparparameters的参数是:
这个函数将这些参数作为输入,并返回一个布尔值,表示摄像机标定参数是否有效。对于没有校准的角的检测,这一步是不需要的。
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.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f> > markerCorners;
cv::aruco::detectMarkers(image, board->dictionary, markerCorners, markerIds, params);
detectmarker的参数为:
用于标定检测
std::vector charucoCorners;
std::vector charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, image, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);
无需标定的检测
std::vector charucoCorners;
std::vector charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, image, board, charucoCorners, charucoIds);
检测ChArUco角点的函数是cv::aruco::interpolateCornersCharuco()
。这个函数返回插值的Charuco角点的数量。
std::vector charucoCorners
: list of image positions of the detected corners.std::vector charucoIds
: ids for each of the detected corners in charucoCorners
.如果提供了标定参数,则首先从ArUco标记估计一个粗略的姿势,然后将ChArUco角点重新投影回图像,从而插值ChArUco角点。
另一方面,在不提供标定参数的情况下,通过计算ChArUco平面与ChArUco图像投影之间的对应单应性来插值ChArUco角点。
单应性插值的主要问题是插值对图像失真更敏感。实际上,单应性只使用每个ChArUco角点的最近标记来执行,以减少失真的影响。
当检测ChArUco板的标记时,特别是当使用单应性时,建议禁用标记的角点细化。这是因为,由于棋盘方格的接近性,亚像素过程会在角点的位置上产生重要的偏差,这些偏差会传播到ChArUco角插值中,产生较差的结果。
此外,只返回那些已经找到两个周围标记的角点。如果周围的两个标记中有任何一个没有被检测到,这通常意味着该区域有一些遮挡或图像质量不好。在任何情况下,最好不要考虑那个角点,因为我们想要的是确保插值ChArUco角点是非常准确的。
在插值ChArUco角点之后,执行亚像素细化。
一旦我们插值了ChArUco角点,我们可能想要画出它们,看看它们的检测是否正确。这可以使用drawdetectedcornscharuco()
函数轻松完成:
cv::aruco::drawDetectedCornersCharuco(image, charucoCorners, charucoIds, color);
outputImage
将是inputImage
的克隆,并绘制了角。charucoCorners
和charucoIds
是interpolateCornersCharuco()
函数检测到的Charuco角。cv::Scalar
。在图片被遮盖的情况下。就像下图一样,虽然一些角点是清晰可见的,但由于遮挡,并不是所有它们周围的标记都被检测到,因此,它们没有被插值:
带有遮挡的Charuco检测
最后,这是ChArUco检测的完整示例(不使用标定参数):
void detectCharucoBoardWithoutCalibration()
{
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.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE;
while (inputVideo.grab()) {
cv::Mat image, imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f> > markerCorners;
cv::aruco::detectMarkers(image, board->dictionary, markerCorners, markerIds, params);
//or
//cv::aruco::detectMarkers(image, dictionary, markerCorners, markerIds, params);
// if at least one marker detected
if (markerIds.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, markerCorners, markerIds);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, 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(30);
if (key == 27)
break;
}
}
样本视频:
10月14日
完整的工作示例包含在模块示例文件夹中的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
这里的calibrb .txt是calibrate_camera_charuco.cpp生成的输出文件。
ChArUco板的最终目标是为高精度校准或位姿估计找到非常准确的角点。
aruco模块提供了一个函数来轻松地执行ChArUco位姿估计。与GridBoard
一样,CharucoBoard
的坐标系统被放置在标定板板的平面上,Z轴指向外,并在标定板的左下角居中。
姿态估计的函数是estimatePoseCharucoBoard()
:
cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, rvec, tvec);
charucoCorners
和charucoIds
参数是从interpolateCornersCharuco()
函数中检测到的charuco角点。CharucoBoard
对象。cameraMatrix
和distCoeffs
是相机定标参数,是位姿估计所必需的。rvec
和tvec
参数是Charuco板的输出位姿。可以使用drawAxis()
绘制轴,以检查姿势是否正确估计。结果是:(X:红色,Y:绿色,Z:蓝色)
Charuco板轴
ChArUco检测与姿态估计的完整示例:
void detectCharucoBoardWithCalibrationPose()
{
cv::VideoCapture inputVideo;
inputVideo.open(0);
cv::Mat cameraMatrix, distCoeffs;
std::string filename = "calib.txt";
bool readOk = readCameraParameters(filename, cameraMatrix, distCoeffs);
if (!readOk) {
std::cerr << "Invalid camera file" << std::endl;
} else {
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.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
while (inputVideo.grab()) {
cv::Mat image;
cv::Mat imageCopy;
inputVideo.retrieve(image);
image.copyTo(imageCopy);
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f> > markerCorners;
cv::aruco::detectMarkers(image, board->dictionary, markerCorners, markerIds, params);
// if at least one marker detected
if (markerIds.size() > 0) {
cv::aruco::drawDetectedMarkers(imageCopy, markerCorners, markerIds);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, image, board, charucoCorners, charucoIds, cameraMatrix, distCoeffs);
// if at least one charuco corner detected
if (charucoIds.size() > 0) {
cv::Scalar color = cv::Scalar(255, 0, 0);
cv::aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, color);
cv::Vec3d rvec, tvec;
// cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, cameraMatrix, distCoeffs, 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.1f);
}
}
cv::imshow("out", imageCopy);
char key = (char)cv::waitKey(30);
if (key == 27)
break;
}
}
}
完整的工作示例包含在modules/aruco/samples/detect_board_charuco.cpp中的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