今天要整理记录的是利用OpenCV中ArUco模块的aruco标记实现一个增强现实的小应用,当然了本次笔记的内容也是需要建立在之前的《OpenCV4学习笔记(72)》基础上的。
所谓增强现实(Augmented Reality),其实是一种将虚拟信息与真实世界巧妙融合的技术,通过技术手段将计算机生成的文字、图像、三维模型、音乐、视频等虚拟信息模拟仿真后,应用到真实世界中,两种信息互为补充,从而实现对真实世界的“增强”。在这里,我们主要利用aruco标记实现将图像、视频这些虚拟信息应用到拍摄下来的真实世界中。
首先,我们需要创建并打印出四个aruco标记,这四个标记作为我们确定现实增强区域的基准,例如下面这几张图像:
可以看到这四个aruco标记包围形成了一个大的四边形区域,这个区域就是我们要进行操作的ROI区域了,当然了还可以把这四个aruco标记裁剪下来,然后贴在门上、窗上、相框中等等场景,这样更有利于增强现实的体现。这里我们就不做裁剪了,就在这张纸上进行实验。下面将这张包含aruco标记的纸图像表述为场景图像。
首先,我们需要对场景图像进行aruco标记的检测,那么就需要先加载拍摄时所用相机的内参矩阵和畸变系数,这里默认已经对相机标定好了,以后有机会再整理一下相机标定的内容。
//加载图像
Mat inputImage = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\FourMarkerVideo.jpg");
resize(inputImage, inputImage, Size(600, 800));
Mat test_image = inputImage.clone();
//加载相机内参和畸变系数
cv::Mat cameraMatrix, distCoeffs;
vector<double> camera = { 657.1548323619423, 0, 291.8582472145741,0, 647.384819351103, 391.254810476919,0, 0, 1 };
cameraMatrix = Mat(camera);
cameraMatrix = cameraMatrix.reshape(1, 3);
vector<double> dist = { 0.1961793476399528, -1.38146317350581, -0.002301820186177369, -0.001054637905895881, 2.458286937422959 };
distCoeffs = Mat(dist);
distCoeffs = distCoeffs.reshape(1, 1);
然后对场景图像中的aruco标记进行检测
//进行标记检测
auto dictionary = aruco::getPredefinedDictionary(DICT_6X6_250);
vector<vector<Point2f>>corners, rejectedImaPoints;
vector<int>ids;
auto parameters = aruco::DetectorParameters::create();
aruco::detectMarkers(test_image, dictionary, corners, ids, parameters, rejectedImaPoints, cameraMatrix, distCoeffs);
//aruco::drawDetectedMarkers(test_image, corners, ids, Scalar(0, 255, 0));
接着我们要寻找所需的ROI区域,也就是四个aruco标记包围而成的一个四边形区域,那么我们需要确定这个四边形的四个顶点。我暂时考虑了两个方法,一是对四个标记的所有角点中的x、y坐标进行排序,找到正数和倒数的前两个值,再进行配对,从而找到四个顶点,这个方法在对于场景图像发生变化时、例如是场景视频的情况下有比较好的效果。二是直接从检测到的四个标记的ID值来索引标记,并获取四边形ROI区域中的对于角点,这个方法相对来说更简单粗暴,但是适用性会比较差,尤其是场景图像发生旋转的情况下比较难达到好的效果。
这里我使用的场景图像是一张固定的图像,也就是上面拍摄的场景图像,所以这里我使用第二个方法
//提取四个标记形成的ROI区域
vector<Point2f> roi_box_pt(4);
//寻找方框的四个顶点,从检测到标记的顺序决定
roi_box_pt[0] = corners[3][0];
roi_box_pt[1] = corners[2][1];
roi_box_pt[2] = corners[0][2];
roi_box_pt[3] = corners[1][3];
接着我们需要读取要替换ROI区域的目标图像,并提取四个顶点
//读取替换图像
Mat new_image = imread("D:\\opencv_c++\\opencv_tutorial\\data\\images\\pig.jpg");
int width = new_image.cols;
int height = new_image.rows;
//提取替换图像的四个顶点
vector<Point2f> new_image_box(4);
new_image_box[0] = Point(0, 0);
new_image_box[1] = Point(width, 0);
new_image_box[2] = Point(width, height);
new_image_box[3] = Point(0, height);
然后将目标图像从原视平面到场景图像的视平面做透视变换
//计算从替换图像到目标ROI图像的3x3单应性矩阵
Mat H = findHomography(new_image_box, roi_box_pt);
Mat roi_new_image;
//进行透视变换
warpPerspective(new_image, roi_new_image, H, test_image.size(), INTER_CUBIC);
然后为了将场景图像中的ROI区域挖空,我们需要设置一个掩膜,然后和场景图像进行按位与操作,最后将场景图像和目标图像通过加权相加融合,实现对场景图像的增强操作
//制作掩膜
Mat mask = Mat(test_image.size(), CV_8UC3,Scalar(255,255,255));
for (int i = 0; i < roi_new_image.rows; i++)
{
for (int j = 0; j < roi_new_image.cols; j++)
{
if (roi_new_image.at<Vec3b>(i, j) != Vec3b(0,0,0))
{
mask.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
}
}
}
Mat kernel = getStructuringElement(MorphShapes::MORPH_RECT, Size(3, 3));
dilate(mask, mask, kernel);
//用透视变换后的替换图像,替换原图像中的ROI区域
Mat result;
bitwise_and(test_image, mask, test_image);
addWeighted(test_image, 1, roi_new_image, 0.7, 1.0, result);
namedWindow("result", WINDOW_FREERATIO);
imshow("result", result);
得到的效果如下:
由于这里的aruco标记是打印在一张纸上,所以看起来效果没有特别的好,但是试想下如果将它们裁剪下来,并且贴在相框上或者是窗上,再对拍摄下的图像进行增强现实操作,那是不是感觉就不一样了(╹ڡ╹ )
而且,假如我们使用的场景图像是实时视频流中的每一帧图像,那么还可以实现实时拍摄的增强现实效果,这样的话能得到更好的AR体验,当然了这样的话对电脑的要求就会比较高了。。。我也尝试过一下在实时视频流中进行处理,但是卡帧劝退了我。。。
今天尝试着将虚拟图像信息应用到了真实的场景图像中,实现一个比较有趣的AR效果,下次笔记再来尝试一下将视频信息应用到真实场景当中去,那本次笔记到此结束啦。
PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!