OpenCV的图像配准融合(一)

OpenCV的图像配准融合(一)

由于实习的工作在做手机双摄的算法,其中光学变焦的算法涉及到了广角和长焦图片的配准融合。由于两个镜头的焦距和光圈大小不同,导致了拍摄出来的两幅图片无论在色彩还是内容上都会有所不同,而模拟的光学变焦则是通过想办法将即保留广角信息也能加入长焦信息,获得画质的提升,有兴趣的可以看这个:
广角+长焦双摄方案
看了Corephotonics、ArcSoft这些厂家的专利还有一些论文后,大致对手机双摄的光学变焦有了些理解。在自己动手写Demo的过程中在网上也学到了不少东西,自己收获了很多,所以整理并记录下来不仅方便自己以后复习,还希望可以给入门的人提供点踩坑经验…

特征点选取

如我们所知,一幅图像包含很多纹理,色彩等。正是这些东西使得我们人可以很容易的知道图像的内容是什么。然而对计算机而言,这些并不能让计算机很好的理解这幅图像,因为对计算机,最容易理解的就是数字。所以我们通过对图像的特征进行提取,并将这些特征描述成数字,向量等形式给计算机,这样才算让计算机真正的理解了这幅图像。基于特征的图像配准算法的核心步骤为:特征提取、特征匹配、图像变换。
那么,什么是图像的特征呢?有些是可以直观地感受到,如亮度、边缘、纹理和色彩等,有些则是需要通过一些数学变换才能得到的,如矩、直方图以及主成份等。通常特征点提取的方法有:SIFT、SURF、ORB等,三种特征检测的优缺点可以看这位兄弟的博客:
SIFT\SURF\ORB对比
我在项目中使用的SURF(Speeded Up Robust Features),这是一种稳健的特征点检测和描述算法。最初由Herbert Bay发表在2006年的欧洲计算机视觉国际会议(Europen Conference on Computer Vision,ECCV)上,并在2008年正式发表在Computer Vision and Image Understanding期刊上。SURF也可以称作是SIFT的“升级版”,SIFT采用的是DOG图像,而SURF采用的是Hessian矩阵行列式近似值图像。选择SURF的原因是它可以很好的解决空间尺度不一致的问题,因为长焦和广角图片得到的结果具有尺度的不一致性。这里详细可以看这位老哥的博客:
SURF特征
好,原理懂了,那么怎么使用OpenCV去实现图片的特征点检测呢?OpenCV中自带了SURF算法的接口,这里直接使用就可以。但是由于lz使用的是OpencCV3.1的版本,这个版本中已经删掉了这个算法,所以要想使用,就要先去下载一个OpenCV-contrib。具体可以参见:
基本步骤:
下载安装OpenCv-contrib
由于lz的电脑里安装了CUDA,所以导致安装包时出现了一些坑,当时查了好久才知道可以把WITH_CUDA 取消勾选(0.0)。最后用了大半天的时间才把这些坑给填上= =…这里lz贴上一些我遇到坑时找的一些解决方法:
踩坑1
踩坑2
最后终于可以正常使用了,下面开始看看代码要怎么写吧。

#include
#include
#include
#include
#include
using namespace std;

void main()
{
    cv::Mat Wide = cv::imread("C:/Users/lxy/Desktop/wide.jpg");
    cv::Mat Tele = cv::imread("C:/Users/lxy/Desktop/tele.jpg");

    //特征点检测与匹配
    cv::Ptr detector = cv::xfeatures2d::SurfFeatureDetector::create(5000);//hession矩阵的阈值自己设置
    vector key_points_1, key_points_2;//将提取的特征点以keypoint形式存储
    cv::Mat dstImage1, dstImage2;
    cv::Ptr matcher = cv::DescriptorMatcher::create("BruteForce"); //描述匹配特征点    
    vector mach;
    detector->detectAndCompute(Tele, cv::Mat(), key_points_1, dstImage1);
    detector->detectAndCompute(Wide, cv::Mat(), key_points_2, dstImage2);
    matcher->match(dstImage1, dstImage2, mach);

    cv::Mat Img_maches;
    drawMatches(Tele, key_points_1, Wide, key_points_2, mach, Img_maches);
    cv::imwrite("C:/Users/lxy/Desktop/Fundamental.jpg", Img_maches);

OpenCV的图像配准融合(一)_第1张图片
OpenCV的图像配准融合(一)_第2张图片
OpenCV的图像配准融合(一)_第3张图片
可以看到,这里面是由很多错误匹配的特征点的,这回导致配准后的结果出现两幅图片对不齐的结果,所以这里要做的是删除这些错误的匹配点。RANSAC(Random Sample Consensus),它是根据一组包含异常数据的样本数据集,计算出数据的数学模型参数,得到有效样本数据的算法。具体可见:
RANSAC原理

//删除错误匹配的特征点
vector InlierMatches;//定义内点集合
vector p1, p2;//先把keypoint转换为Point格式
for (int i = 0; i < mach.size(); i++)
{
    p1.push_back(key_points_1[mach[i].queryIdx].pt);// pt是position
    p2.push_back(key_points_2[mach[i].trainIdx].pt);
}
//RANSAC FindFundamental剔除错误点
vector RANSACStatus;//用以标记每一个匹配点的状态,等于0则为外点,等于1则为内点。
cv::findFundamentalMat(p1, p2, RANSACStatus, CV_FM_RANSAC);//p1 p2必须为float型
for (int i = 0; i < mach.size(); i++)
{
    if (RANSACStatus[i] != 0)
    {
        InlierMatches.push_back(mach[i]); //不等于0的是内点
    }
}
drawMatches(Tele, key_points_1, Wide, key_points_2, InlierMatches, Img_maches);
cv::imwrite("C:/Users/lxy/Desktop/Fundamental.jpg", Img_maches);

OpenCV的图像配准融合(一)_第4张图片
很明显图中的大部分错误点被删除掉,接下来就可以对长焦进行矫正了,使用单映性矩阵计算,这里注意OpenCv有findFundamentalMat和findHomography两种方法,不要搞混,为了清楚区别基本矩阵和单映性矩阵的区别,请看:
单应矩阵、基本矩阵、本质矩阵

vector Tele_point, Wide_point;
for (int i = 0; i < InlierMatches.size(); i++)
{
    Tele_point.push_back(key_points_1[InlierMatches[i].queryIdx].pt);
    Wide_point.push_back(key_points_2[InlierMatches[i].trainIdx].pt);
}
cv::Mat Homography = cv::findHomography(Tele_point, Wide_point, CV_RANSAC); //计算将p2投影到p1上的单映性矩阵
cv::Mat Registration;
warpPerspective(Tele, Registration, Homography, cv::Size(Wide.cols, Wide.rows));
cv::imwrite("C:/Users/lxy/Desktop/re.jpg", Registration);

OpenCV的图像配准融合(一)_第5张图片
接下来将两幅图片融合在一起就好啦

cv::Mat Stitch(Wide.rows,Wide.cols,CV_8UC3);
Wide.copyTo(Stitch(cv::Rect(0, 0, Wide.cols, Wide.rows)));
cv::Rect Mask_center;
Mask_center.y = Wide.rows / 2 - Wide.rows / (4);//根据广角和长焦的焦距参数决定的,lz使用的27mm和52mm的两个镜头
Mask_center.x = Wide.cols / 2 - Wide.cols / (4);
Mask_center.width = Wide.cols / (2);
Mask_center.height = Wide.rows / (2);
Registration(Mask_center).copyTo(Stitch(Mask_center));
cv::imwrite("C:/Users/lxy/Desktop/Stitch.jpg", Stitch);

OpenCV的图像配准融合(一)_第6张图片
通过对比细节,发现确实画质会有很大的提升
OpenCV的图像配准融合(一)_第7张图片
OpenCV的图像配准融合(一)_第8张图片
可以发现两幅图片在色彩上还是存在很大的不一致,所以下一篇会讲一下色彩补偿的思路。

看了很多大佬的文章才写了这篇博客,如有引用不妥之处,欢迎指正,也欢迎大家可以在这里交流学习!:)

你可能感兴趣的:(OpenCV的图像配准融合(一))