注释:
1、书名:Mastering OpenCV with Practical Computer Vision Projects
2、章节:Chapter 3:Marker-less Augmented Reality
3、书中源代码的最新更新可以参考网址:https://github.com/MasteringOpenCV/code
我想整理一下思路,很想,很想,很想。所以,来排坑吧。233~
增强现实是一个什么样的过程?
两步走,第一步图像匹配,第二步图像上叠加三维物体。嗯,就是这样。读取一张标记图的关键信息(特征点),通过对摄像头采集的图片进行分析提取,得到实时图片的关键信息(特征点),关键信息进行匹配检测。经过优化处理排除两幅图片之间大小,方向,模糊程度,倾斜角度等的差别,我们就知道了标记图在现实世界中的位置,同时也知道了实时的图片在虚拟世界的位置,于是把虚拟的三维模型数据放在正确的位置,即我们看到了屏幕上目标位置上的虚拟物体。
增强现实实现是怎么做到的?
我们来结合代码一步一步看吧。
读入一张标记图;
Mat patternImage = imread("33.jpg"); //读入标记图
标记图传入processVideo中;
processVideo(patternImage, calibration, VideoCapture(0));
摄像头处理函数,把标记图patternImage传入ARPipeline中;
//摄像头处理的函数声明 void processVideo(const Mat &patternImage, CameraCalibration &calibration, VideoCapture &capture) { ARPipeline pipeline(patternImage, calibration); //构造对象其实就是把ARPipline中的m_pattern中的图片用他的特征向量把匹配器训练好 }
在ARPipeline类中,把标记图patternImage建立为模式对象m_pattern;
ARPipeline::ARPipeline(const Mat&patternImage, const CameraCalibration &calibration) :m_calibration(calibration) { m_patternDetector.buildPatternFromImage(patternImage, m_pattern); //用patternImage的数据填充m_pattern的图像对象 m_patternDetector.train(m_pattern); //开始训练图像 }
这里可以查看一下模式Pattern的类,Pattern m_pattern,在pattern模式对象中构造了图像的基本信息;
struct Pattern //模式结构体包含图像的大小关键点,特征描述2d3d点 { Size size; //大小 Mat frame; //图像 Mat grayImg; //灰度图 vector<KeyPoint> keypoints; //关键点 Mat descriptors; //特征向量描述 vector<Point2f> points2d; //二维点,即图片的长宽坐标 vector<Point3f> points3d; //三维点,即图片的三维坐标 };
接下来就是通过一些方法对模式对象进行计算;
//从标记图片中填充ARPipline对象中m_pattern的大小图像数据 void PatternDetector::buildPatternFromImage(const Mat &image, Pattern &pattern)const { pattern.size = Size(image.cols, image.rows); //模式的大小 pattern.frame = image.clone(); getGray(image, pattern.grayImg); pattern.points2d.resize(4); //模式点 pattern.points3d.resize(4); const float w = image.cols; const float h = image.rows; const float maxSize = max(w, h); //计算归一化的图像宽高 const float unitW = w / maxSize; const float unitH = h / maxSize; pattern.points2d[0] = Point2f(0, 0); //填充m_pattern对象的2d坐标 pattern.points2d[1] = Point2f(w, 0); pattern.points2d[2] = Point2f(w, h); pattern.points2d[3] = Point2f(0, h); pattern.points3d[0] = Point3f(-unitW, -unitH, 0); //归一化后的3d坐标 pattern.points3d[0] = Point3f(unitW, -unitH, 0); pattern.points3d[0] = Point3f(unitW, unitH, 0); pattern.points3d[0] = Point3f(-unitW, unitH, 0); //提取图片的关键点和特征描述 extractFeatures(pattern.grayImg, pattern.keypoints, pattern.descriptors); }
上边引出我们图像处理中的关键步骤,对图像的ORB特征提取和FREAK特征描述,这里没有使用ORB描述算子,为什么;
bool PatternDetector::extractFeatures(const Mat& image, vector<KeyPoint>&keypoints, Mat &descriptors)const { assert(!image.empty()); //如果不为空继续执行 assert(image.channels() == 1); //如果通道为1继续执行,通道等于1表示灰度图,减少计算量 m_detector->detect(image, keypoints); //提取关键点 if (keypoints.empty()) return false; m_extractor->compute(image, keypoints, descriptors); //计算特征向量 if (descriptors.empty()) return false; }
到这里我们已经可以把标记图即模式对象填充出来了,后面介绍的是面对这些对象我们又需要做什么样的处理。那么,临时定义为第二部分:主要是在PatternDetector类里面设计了特征提取器,特征描述器,特征匹配器。
匹配优化和单应性计算坐标提取为第三部分吧。分开阐述一下: