Charuco生成和检测
#include <iostream>
#include <opencv2/highgui.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/aruco/charuco.hpp>
#include <opencv2/opencv.hpp>
// opencv Aruco官方示例
// https://github.com/opencv/opencv_contrib/tree/4.x/modules/aruco/samples
using namespace std;
using namespace cv;
auto aruco_t = cv::aruco::DICT_7X7_50; // maker为6*6,数量有250个
static const int squaresX = 5; // x方向棋盘格数量
static const int squaresY = 5; // y方向棋盘格数量
static const float squareLength = 0.18; // 棋盘格正方块边长(m为单位)
static const float markerLength = 0.12; // 棋盘格中maker的长度(m为单位)
void generateMarker(const std::string &charuco_path, cv::Mat& boardImage) {
// 声明字典,字典名称表示了该字典的aruco标记数量和尺寸
Ptr<aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(aruco_t);
// https://docs.opencv.org/4.5.2/d0/d3c/classcv_1_1aruco_1_1CharucoBoard.html#aa83b0a885d4dd137a41686991f85594c
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary);
// https://docs.opencv.org/4.5.2/d0/d3c/classcv_1_1aruco_1_1CharucoBoard.html#a5f1b7c91bd8bf8271931d6f06292f1bc
board->draw(cv::Size(600, 600), boardImage, 10, 1);
//API:drawMarker()来绘制出aruco标记,其参数含义如下:
// (1)参数dictionary : 之前创建的 Dictionary 对象指针
// (2)参数id:标记 id,表示绘制字典中的哪一个aruco标记。每个字典由不同数量的标记组成,id 有效范围是[0,字典包含的标记数 ),任何超出有效范围的特定 id 都会产生异常。
// (3)参数 sidepixel: 输出标记图像的尺寸,输出标记图像的尺寸为Size(sidepixel,sidepixel);此参数应足够大以存储特定字典的位数,至少需要满足(sidepixel - 标记的边长) >= 2;
// 并且为了避免输出标记图像变形,sidepixel应与位数 + 边界大小成比例,或者至少比标记尺寸大得多,以使变形不明显。
// (4)参数img:输出的标记图像;
// (5)参数borderBist:用于指定标记黑色边框的宽度,例如borderBist = 2 表示边框的宽度等于两个内部像素的大小,默认值 borderBist = 1。
imwrite(charuco_path, boardImage);
imshow("board", boardImage);
waitKey(0);
destroyAllWindows();
}
void detectMarker(const std::string &image_path, const std::string& detectedcharuco_path) {
cv::Mat cur_img, copyImage, imageCopy;
cur_img = cv::imread(image_path, 1);
//cvtColor(cur_img, cur_img, CV_GRAY2BGR);
std::cout << "cur_img:" << cur_img.channels() << std::endl;
// 声明字典,字典名称表示了该字典的aruco标记数量和尺寸
Ptr<aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(aruco_t);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(squaresX, squaresY, squareLength, markerLength, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
cur_img.copyTo(copyImage);
// ids: 检测到的 ArUco 序号。这是按检测先后存放,所以可能是乱序的
// corners: 第一层 vector 是检测到的 Marker,第二层是 1 个 ArUco 对应 4 个 Point2f 点坐标;
vector<int> markerIds;
vector<vector<Point2f>> markerCorners, rejectedMarkers;
// https://docs.opencv.org/4.5.2/d9/d6a/group__aruco.html#gab9159aa69250d8d3642593e508cb6baa
aruco::detectMarkers(copyImage, board->dictionary, markerCorners, markerIds, params, rejectedMarkers);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
// 根据检测的aruco角点优化插值棋盘格角点
int interpolatedCorners = 0;
if (markerIds.size() > 0) {
std::cout << "markeriDs:" << markerIds.size() << std::endl;
//cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, copyImage, board, charucoCorners, charucoIds);
interpolatedCorners =
aruco::interpolateCornersCharuco(markerCorners, markerIds, copyImage, board,
charucoCorners, charucoIds);
}
cur_img.copyTo(imageCopy);
// 画出maker角点和id
if (markerIds.size() > 0) {
aruco::drawDetectedMarkers(imageCopy, markerCorners, markerIds, Scalar(0, 255, 0)); // bgr,画出边框并显示出id,且标出来左上角第一个角点
// 下面与drawDetectedMarkers效果等同
int cnt = 0;
for (auto m : markerIds)
{
for (int i = 0; i < markerCorners[m].size(); ++i) {
if (markerCorners[m][i].x < 0) {
continue;
}
cout << "point2f:" << markerCorners[m][i].x << ", " << markerCorners[m][i].y << endl;
Point2i point = Point2i(int(markerCorners[m][i].x), int(markerCorners[m][i].y));
circle(imageCopy, point, 5,Scalar(255,0,0), 1);
/*putText(imageCopy, to_string(m)+" " + to_string(i), point, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(12, 23, 200), 1, 8);*/
putText(imageCopy, to_string(i), point, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(12, 23, 200), 1, 8);
}
++cnt;
}
}
// 画出charuco角点和id
if (interpolatedCorners > 0) {
aruco::drawDetectedCornersCharuco(imageCopy, charucoCorners, charucoIds, Scalar(0, 0, 255));
// 下面与drawDetectedCornersCharuco效果等同
int cnt(0);
for (auto m : charucoIds)
{
cout << "point2f:" << charucoCorners[m].x << ", " << charucoCorners[m].y << endl;
Point2i point = Point2i(int(charucoCorners[m].x), int(charucoCorners[m].y));
circle(imageCopy, point, 5, Scalar(255, 255, 0), 1);
putText(imageCopy, to_string(cnt), point, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(13, 230, 13), 1, 8);
++cnt;
}
}
imwrite(detectedcharuco_path, imageCopy);
namedWindow("out", WINDOW_NORMAL);
imshow("out", imageCopy);
waitKey(0);
destroyAllWindows();
}
// g++ ../charuco.cpp -o charuco `pkg-config --cflags --libs opencv4`
int main() {
Mat boardImage;
std::string charuco_path = "./charuco.png";
std::string detectedcharuco_path = "./detectedcharuco.png";
generateMarker(charuco_path, boardImage);
detectMarker(charuco_path, detectedcharuco_path);
}
生成的charuco
检测结果