(三)OpenCV | 基于AruCo标记实现增强现实


AruCo(Augmented Reality University of Cordoba)标记是指一些放置在图像上的基准块,后续可以通过检测该标记进而理解图像。Aruco标记是一个二进制平方标记,由一个宽的黑边和一个内部二进制矩阵组成,内部矩阵的排列决定了它的id。如:

在这里插入图片描述
产生Aruco标记的代码如下:

#include<opencv2/aruco.hpp>
#include<opencv2/highgui.hpp>

using namespace cv;

int main(int argc, char** argv) {
	Mat markerImage;
	// 1. cv::Ptr<>模板和C++中的smart_ptr<>模板类似;
	// 2. 使用getPredefinedDictionary加载预定义的字典;
	// 3. DICT_6X6_250指定标记大小为6×6,共250个标记,
	//    存在组合DICT_4X4/5X5/6X6/7X7_50/100/250/1000等共25种。
	Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
	// 33表示取[0, 249]的第33个索引,200表示marker的大小,这里将生成图像放大
	// markerImage表示存储对象,1表示marker的边界宽度
	// 注意第三个参数必须满足:>= markerSize + 2 * borderSize,一般设置为远大于markerSize + 2 * borderSize的值
	aruco::drawMarker(dictionary, 33, 200, markerImage, 1);
	// 写入结果
	imwrite("marker33.png", markerImage);
	return 0;
}

由于Aruco标记的特殊性,我们可以预先在图像中放置多个该标记,然后通过检测算法找到其具体位置,进而理解图像。本文将介绍Aruco标记用于增强现实的例子,代码的具体含义见注释。该项目的整体流程如下:
在这里插入图片描述
首先是输入图像:

在这里插入图片描述

图1:test.jpg

上述图中的Arcuo标记由以下语句产生:

Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_250);
// aruco::drawMarker(dictionary, 30, 200, markerImage, 1);
// aruco::drawMarker(dictionary, 23, 200, markerImage, 1);
// aruco::drawMarker(dictionary, 25, 200, markerImage, 1);
aruco::drawMarker(dictionary, 33, 200, markerImage, 1);

从右下角开始顺时针的标记依次为:
在这里插入图片描述

程序实现功能:使用下图2替换上图1中Arcuo标记框定的矩形区域。

在这里插入图片描述

图2:new_scenery.jpg

#include<fstream>
#include<iostream>

#include<opencv2/aruco.hpp>
#include<opencv2/imgproc.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/calib3d.hpp>

using namespace cv;
using namespace std;
using namespace aruco;

int main(int argc, char** argv) {
	// 输入路径与输出路径
	string image_str = "test.jpg";
	string im_src_str = "new_scenery.jpg";
	string image_output_str = "test_output.jpg";
	// 判断路径是否合法
	ifstream isfile(image_str), isfile_(im_src_str);
	if (!isfile || !isfile_) {
		cout << "Input image is not found!";
		return -1;
	}
	// 读取输入图像
	Mat image = imread("test.jpg");
	Mat im_src = imread("new_scenery.jpg");
	// 创建窗体并定义基本属性
	static const string kWinName = "Augmented Reality using Aruco markers in OpenCV";
	namedWindow(kWinName, WINDOW_NORMAL);
	// 存放markers索引
	vector<int> markerIds;
	// 加载字典
	Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250);
	// 存放检测和滤除的marker
	vector<vector<Point2f>> markerCorners, rejectedCandidates;
	// 使用默认参数初始化检测器
	Ptr<DetectorParameters> parameters = DetectorParameters::create ();
	/**
	 * @param image 输入图像
	 * @param dictionary 图像中待搜索的marker类型
	 * @param markerCorners 检测的marker坐标,顺序为顺时针,其大小为N×4,共4N个坐标
	 * @param markerIds 检测的marker索引,其大小为N
	 * @param parameters 参数
	 * @param rejectedImgPoints 滤除的marker,其大小为M×4,共4M个坐标
	 */
	detectMarkers(image, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
	// 顺时针,共4N(N=4)个坐标,每个正方形区域的坐标表示方法,见图3
	// 30(右下角):右下 -> 左下 -> 左上 -> 右上
	// 23(左下角):左下 -> 左上 -> 右上 -> 右下
	// 25(左上角):左下 -> 左上 -> 右上 -> 右下
	// 33(右上角):左下 -> 左上 -> 右上 -> 右下
	// cout << markerCorners << endl;
	// [30, 23, 25, 33],从右下角开始顺时针
	//cout << markerIds << endl;
	// 共4M(M=1)个坐标
	// cout << rejectedCandidates << endl;
	// 用于矫正后的坐标
	vector<Point> pts_dst;
	// 缩放系数
	float scalingFac = 0.02;
	// 各个角点的坐标
	Point refPt1, refPt2, refPt3, refPt4;
	// 左上角,寻找索引为25的marker,下同
	std::vector<int>::iterator it = std::find(markerIds.begin(), markerIds.end(), 25);
	// 取其在markerCorners中的索引,下同
	int index = std::distance(markerIds.begin(), it);
	// 根据索引取对应坐标,at(1)表示取该正方形区域的左下角坐标
	refPt1 = markerCorners.at(index).at(1);
	// 右上角
	it = std::find(markerIds.begin(), markerIds.end(), 33);
	index = std::distance(markerIds.begin(), it);
	refPt2 = markerCorners.at(index).at(2);
	// 左上角角点和右上角角点间的距离
	float distance = norm(refPt1 - refPt2);
	// 左上角角点和右上角角点的坐标矫正
	pts_dst.push_back(Point(refPt1.x - round(scalingFac * distance), refPt1.y - round(scalingFac * distance)));
	pts_dst.push_back(Point(refPt2.x + round(scalingFac * distance), refPt2.y - round(scalingFac * distance)));
	// 右下角
	it = std::find(markerIds.begin(), markerIds.end(), 30);
	index = std::distance(markerIds.begin(), it);
	refPt3 = markerCorners.at(index).at(0);
	// 右下角角点的坐标矫正
	pts_dst.push_back(Point(refPt3.x + round(scalingFac * distance), refPt3.y + round(scalingFac * distance)));
	// 左下角
	it = std::find(markerIds.begin(), markerIds.end(), 23);
	index = std::distance(markerIds.begin(), it);
	refPt4 = markerCorners.at(index).at(0);
	// 左下角角点的坐标矫正(矫正前后坐标对比见图4)
	pts_dst.push_back(Point(refPt4.x - round(scalingFac * distance), refPt4.y + round(scalingFac * distance)));
	// 源图像的各个坐标,从左上角开始顺时针
	vector<Point> pts_src;
	pts_src.push_back(Point(0, 0));
	pts_src.push_back(Point(im_src.cols, 0));
	pts_src.push_back(Point(im_src.cols, im_src.rows));
	pts_src.push_back(Point(0, im_src.rows));
	// 根据角点坐标和矫正坐标计算最优单映射变换矩阵(见图5)
	Mat h = cv::findHomography(pts_src, pts_dst);
	// 变换后的图像
	Mat warpedImage;
	// 仿射变换
	warpPerspective(im_src, warpedImage, h, image.size(), INTER_CUBIC);
	// 定义mask
	Mat mask = Mat::zeros(image.rows, image.cols, CV_8UC1);
	// 绘图,第一个参数表示图像,第二个参数表示坐标,第三个参数表示颜色,第四个参数表示线条类型
	fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255), LINE_AA);
	// 形态学运算
	Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
	erode(mask, mask, element);
	// 输出图像
	Mat imOut = image.clone();
	warpedImage.copyTo(imOut, mask);
	// 图像拼接,并将结果存入变量concatenatedOutput中
	Mat concatenatedOutput;
	hconcat(image, imOut, concatenatedOutput);
	// 保存输出图像
	imwrite(image_output_str, concatenatedOutput);
	// 显示输出图像(见图6)
	imshow(kWinName, concatenatedOutput);
	waitKey(0);

	return 0;
}

在这里插入图片描述

图3:各个Arcuo标记的坐标顺序

在这里插入图片描述

图4:矫正前后的坐标

在这里插入图片描述

图5:求映射变换矩阵

在这里插入图片描述
图6:test_output.jpg


参考

  1. https://github.com/spmallick/learnopencv/tree/master/AugmentedRealityWithArucoMarkers.


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值