opencv aruco码的生成和识别
·
新版opencv4.7.0开始在主库中就有了aruco码的生成和识别库
要再详细的识别位姿什么的,还是需要到opencv_contrib中
以下代码为自己的测试过程,包括有选定aruco码的字典、生成aruco码、图像仿射投影变换、识别aruco码的参数设置、所有用到的参数保存yaml等
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
// 分别创建两个标记图像
Mat markerImage1, markerImage2;
//创建aruco码的字典,必须要识别的类型保持一致
aruco::Dictionary dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
//Mat combinedImage = imread("C:\\Users\\Administrator\\Pictures\\combined_markers-1.png",0);
int size = 40;
////Mat bytelist = dictionary.getBitsFromByteList(dictionary.bytesList, dictionary.markerSize);
////Mat byte = aruco::Dictionary::getByteListFromBits(dictionary.bytesList);
// 生成两个不同的标记
dictionary.generateImageMarker(0, size, markerImage1, 1); // ID 0, 尺寸200
dictionary.generateImageMarker(1, size, markerImage2, 1); // ID 1, 尺寸200
// 创建一个大的画布来放置两个标记
Mat combinedImage = Mat(500, 600, CV_8UC1,Scalar(255));
// 将两个标记放在画布的不同位置
Rect roi1(50, 150, size, size); // 第一个标记位置
Rect roi2(350, 150, size, size); // 第二个标记位置
markerImage1.copyTo(combinedImage(roi1));
markerImage2.copyTo(combinedImage(roi2));
#pragma region 将图片进行仿射变换
//// 获取图像中心点
//Point2f center(combinedImage.cols / 2.0f, combinedImage.rows / 2.0f);
//// 定义旋转角度(45度)和缩放因子
//double angle = 45.0; // 旋转角度
//double scale_x = 1.5; // X轴缩放因子(拉伸)
//double scale_y = 0.8; // Y轴缩放因子(压缩)
//// 方法1:使用getRotationMatrix2D构建旋转矩阵,然后添加缩放
//Mat rotation_matrix = getRotationMatrix2D(center, angle, 1.5);
//rotation_matrix.at<double>(0, 0) *= scale_x; // a * scale_x
//rotation_matrix.at<double>(0, 1) *= scale_x; // b * scale_x
//rotation_matrix.at<double>(1, 0) *= scale_y; // c * scale_y
//rotation_matrix.at<double>(1, 1) *= scale_y; // d * scale_y
//warpAffine(combinedImage, combinedImage, rotation_matrix, Size(700, 600));
// 定义原始图像的四个角点(顺时针顺序)
vector<Point2f> src_points = {
Point2f(0, 0), // 左上角
Point2f(combinedImage.cols - 1, 0), // 右上角
Point2f(combinedImage.cols - 1, combinedImage.rows - 1), // 右下角
Point2f(0, combinedImage.rows - 1) // 左下角
};
// 定义变换后的四个点(创建透视效果)
vector<Point2f> dst_points = {
Point2f(50, 50), // 左上角向内移动
Point2f(combinedImage.cols - 100, 80), // 右上角向内移动
Point2f(combinedImage.cols - 50, combinedImage.rows - 50), // 右下角向内移动
Point2f(100, combinedImage.rows - 80) // 左下角向内移动
};
// 计算投影变换矩阵
Mat perspective_matrix = getPerspectiveTransform(src_points, dst_points);
warpPerspective(combinedImage, combinedImage, perspective_matrix, combinedImage.size(),
INTER_LINEAR, BORDER_CONSTANT, Scalar(0));
// 保存生成的图像
imwrite("combined_markers.png", combinedImage);
#pragma endregion
// 准备检测
Mat displayImage;
cvtColor(combinedImage, displayImage, COLOR_GRAY2BGR);
//Mat binary;
//adaptiveThreshold(combinedImage, binary, 255, AdaptiveThresholdTypes::ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, 0);
aruco::DetectorParameters detectorParams;
// 优化参数设置
detectorParams.adaptiveThreshWinSizeMin = 3; //参数代表选择的自适应阈值窗口大小(以像素为单位)间隔
detectorParams.adaptiveThreshWinSizeMax = 23;
detectorParams.adaptiveThreshWinSizeStep = 2;
detectorParams.adaptiveThreshConstant = 7; // 这一参数表达了阈值状态下的常量
detectorParams.minMarkerPerimeterRate = 0.03; // 检测轮廓,这些参数决定了marker的最小值和最大值,具体来说,是最大最小marker的周长
detectorParams.maxMarkerPerimeterRate = 4.0; // 例如,大小为640x480,最大相对marker周长为0.05的图像,将会产生一个最大周长640x4 = 2560(像素)的marker,因为640是图像的最大尺寸
detectorParams.cornerRefinementMethod = aruco::CORNER_REFINE_SUBPIX;
detectorParams.cornerRefinementWinSize = 3; // 这一参数决定了亚像素级细化过程的窗口大小。
detectorParams.polygonalApproxAccuracyRate = 0.01;//我们对所有的候选进行多边形近似,只有近似结果为方形的形状才能通过测试
detectorParams.minCornerDistanceRate = 0.05;//同一张marker中每一对角的最小距离。这是相对于marker周长的值。像素的最小距离为Perimeter * minCornerDistanceRate
detectorParams.minDistanceToBorder = 3;// marker角到图像边缘最小距离。
detectorParams.markerBorderBits = 1;// 这一参数指定了marker边界的宽度。
//detectorParams.useAruco3Detection = true;
detectorParams.errorCorrectionRate = 3;
detectorParams.minOtsuStdDev = 5;
detectorParams.perspectiveRemovePixelPerCell = 10;
detectorParams.maxErroneousBitsInBorderRate = 0.35;
detectorParams.perspectiveRemoveIgnoredMarginPerCell = 0.13;
aruco::RefineParameters refineParams;
refineParams.minRepDistance = 20;
aruco::ArucoDetector detector(dictionary, detectorParams, refineParams);
#pragma region 保存参数
FileStorage fs("111.txt", cv::FileStorage::WRITE);
//fs.write("H", detectorParams.adaptiveThreshConstant);
dictionary.writeDictionary(fs);
detectorParams.writeDetectorParameters(fs);
refineParams.writeRefineParameters(fs);
fs.release();
FileStorage fs2("111.txt", cv::FileStorage::READ);
FileNode fn2 = fs2.root();
//std::cout << fn2["nmarkers"].real() << std::endl;;
aruco::RefineParameters refineParams2; //refineParams2.errorCorrectionRate的值会取到跟dictionary中的值一样
refineParams2.readRefineParameters(fn2);
aruco::Dictionary dictionary2;
dictionary2.readDictionary(fn2);
aruco::DetectorParameters detectorParams2;
detectorParams2.readDetectorParameters(fn2);
fs2.release();
#pragma endregion
// 检测标记
vector<vector<Point2f>> markerCorners, rejectedCandidates;
vector<int> markerIds;
detector.detectMarkers(combinedImage, markerCorners, markerIds, rejectedCandidates);
if (markerIds.size() > 0) {
aruco::drawDetectedMarkers(displayImage, markerCorners, markerIds);
}
// 绘制检测结果
cout << "检测到的标记数量: " << markerIds.size() << endl;
for (size_t i = 0; i < markerIds.size(); i++) {
cout << "标记ID: " << markerIds[i] << endl;
// 绘制角点
for (size_t j = 0; j < markerCorners[i].size(); j++) {
circle(displayImage, markerCorners[i][j], 3, Scalar(0, 0, 255), -1);
}
// 绘制标记边框
for (size_t j = 0; j < markerCorners[i].size(); j++) {
line(displayImage, markerCorners[i][j],
markerCorners[i][(j + 1) % 4], Scalar(0, 255, 0), 2);
}
}
// 绘制被拒绝的候选标记
for (size_t i = 0; i < rejectedCandidates.size(); i++) {
for (size_t j = 0; j < rejectedCandidates[i].size(); j++) {
circle(displayImage, rejectedCandidates[i][j], 2, Scalar(255, 0, 0), -1);
}
}
// 显示结果
//imwrite("detection_result.png", displayImage);
cout << "结果已保存为 detection_result.png" << endl;
return 0;
}
aruco码的周围得是白色,这样才可以找轮廓是找到aruco码,所有手动生成的码外面需要有一个白色轮廓
参考:https://www.cnblogs.com/yilangUAV/p/14436171.html
更多推荐


所有评论(0)