Jacob的全景圖像融合算法系列
OpenCV 尺度不變特征檢測:SIFT、SURF、BRISK、ORB
OpenCV 匹配興趣點:SIFT庆聘、SURF 和二值描述子
OpenCV 估算圖像的投影關系:基礎矩陣和RANSAC
OpenCV 單應矩陣應用:全景圖像融合原理
圖像融合:拉普拉斯金字塔融合算法
之前的寫了好幾篇文,什么特征點檢測氛谜,匹配掏觉,RANSAC之類的亂七八糟的,就是為了做這個應用值漫。了解原理之后用NI Vision實現(xiàn)澳腹,數(shù)圖的課程設計算是交差了~~全景圖像融合使用到SIFT算子(特征點檢測和匹配)、單應矩陣(立體幾何)和RANSAC(隨機抽樣一致性)之類的內容杨何,了解其中的領域和原理還是需要花點時間的酱塔。
1.單應矩陣
單應(Homography)是射影幾何中的概念危虱,又稱為射影變換羊娃。它把一個射影平面上的點(三維齊次矢量)映射到另一個射影平面上。單應是關于三維齊次矢量的一種線性變換埃跷,可以用一個3×3的非奇異矩陣H表示蕊玷,這個矩陣H稱為單應矩陣。使用這個矩陣弥雹,就可以將射影平面上的一個點投影到另一個平面上(圖中的 m 投影到 m‘)垃帅。
平面上的點為三維齊次矢量,即
2.與基礎矩陣的區(qū)別
基礎矩陣體現(xiàn)的是兩個圖像間的對極約束(詳細見我之前的一篇文章)剪勿。兩個圖像之間的對極約束與場景的結構無關贸诚,也就是說你拍攝的物體可以是一個球,或者其他奇形怪狀的物體〗垂蹋基礎矩陣不能給出兩幅圖像的像點的一一對應的關系械念,只能給出像點到另一幅圖像的對極線的映射關系。
基礎矩陣F描述的實際是一種點和線的映射關系运悲,而不是點對點的關系龄减,不能給出另一個點的確切位置。
也就說扇苞,三維點如果不是在同一個平面上欺殿,可以使用基礎矩陣F來計算圖像上像點在另一幅圖像上對應的對極線寄纵,而不能使用單應矩陣H得到對應點的確切位置鳖敷。在實際應用中,當被拍攝物體深度Z比較大的時候程拭,可以視為一個平面來處理定踱,也可以使用單應矩陣來進行點對點的映射。
從公式推導中恃鞋,我們可以得到崖媚,當射影平面之間只有旋轉無平移時,也可以使用單應矩陣來進行映射恤浪,這里不進行推導畅哑。
如果拍攝物體不為平面(或不能視為平面來處理)并且射影平面之間不只有旋轉關系時時,強行估算單應矩陣水由,會產生巨大偏差荠呐。
假設強行使用單應矩陣
通過平面P上的匹配點得到了單應矩陣H之后砂客,再用來估計不在平面P上的點 p' 的位置泥张,就會出現(xiàn)這樣的情況。
3.通過匹配點來計算單應矩陣
關于特征點匹配的內容可以看我之前的文章(為了今天算是磨刀三月- -)鞠值,為了提高匹配準確率媚创,這里使用的是SIFT算子配合上RANSAC算法的方式進行估計。
兩圖像上的像點 p1(x1,y1) p2(x2,y2) 是一對匹配的點對彤恶,其單應矩陣為H钞钙,則有
展開得
那么就至少需要4對匹配點(4個方程組)進行計算(任意三點不共線)
4.代碼實現(xiàn)
/********************************************************************
* Created by 楊幫杰 on 10/12/18
* Right to use this code in any way you want without
* warranty, support or any guarantee of it working
* E-mail: yangbangjie1998@qq.com
* Association: SCAU 華南農業(yè)大學
********************************************************************/
#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/stitching.hpp>
#define PARLIAMENT01 "/home/jacob/圖片/images/parliament1.jpg"
#define PARLIAMENT02 "/home/jacob/圖片/images/parliament2.jpg"
using namespace cv;
using namespace std;
int main()
{
Mat image1= imread(PARLIAMENT01,0);
Mat image2= imread(PARLIAMENT02,0);
if (!image1.data || !image2.data)
return 0;
imshow("Image 1",image1);
imshow("Image 2",image2);
vector<KeyPoint> keypoints1;
vector<KeyPoint> keypoints2;
Mat descriptors1, descriptors2;
//創(chuàng)建SIFT檢測器
Ptr<Feature2D> ptrFeature2D = xfeatures2d::SIFT::create(74);
//檢測SIFT特征并生成描述子
ptrFeature2D->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);
cout << "Number of feature points (1): " << keypoints1.size() << endl;
cout << "Number of feature points (2): " << keypoints2.size() << endl;
//使用歐氏距離和交叉匹配策略進行圖像匹配
BFMatcher matcher(NORM_L2, true);
vector<DMatch> matches;
matcher.match(descriptors1,descriptors2,matches);
Mat imageMatches;
drawMatches(image1,keypoints1, // 1st image and its keypoints
image2,keypoints2, // 2nd image and its keypoints
matches, // the matches
imageMatches, // the image produced
Scalar(255,255,255), // color of the lines
Scalar(255,255,255), // color of the keypoints
vector<char>(),
2);
imshow("Matches (pure rotation case)",imageMatches);
//將keypoints類型轉換為Point2f
vector<Point2f> points1, points2;
for (vector<DMatch>::const_iterator it= matches.begin();
it!= matches.end(); ++it)
{
float x= keypoints1[it->queryIdx].pt.x;
float y= keypoints1[it->queryIdx].pt.y;
points1.push_back(Point2f(x,y));
x= keypoints2[it->trainIdx].pt.x;
y= keypoints2[it->trainIdx].pt.y;
points2.push_back(Point2f(x,y));
}
cout << "number of points: " << points1.size() << " & " << points2.size() << endl;
//使用RANSAC算法估算單應矩陣
vector<char> inliers;
Mat homography= findHomography(
points1,points2, // corresponding points
inliers, // outputed inliers matches
RANSAC, // RANSAC method
1.); // max distance to reprojection point
//畫出局內匹配項
drawMatches(image1, keypoints1, // 1st image and its keypoints
image2, keypoints2, // 2nd image and its keypoints
matches, // the matches
imageMatches, // the image produced
Scalar(255, 255, 255), // color of the lines
Scalar(255, 255, 255), // color of the keypoints
inliers,
2);
imshow("Homography inlier points", imageMatches);
//用單應矩陣對圖像進行變換
Mat result;
warpPerspective(image1, // input image
result, // output image
homography, // homography
Size(2*image1.cols,image1.rows)); // size of output image
//拼接
Mat half(result,Rect(0,0,image2.cols,image2.rows));
image2.copyTo(half);
imshow("Image mosaic",result);
waitKey();
return 0;
}
結果如下
可以看到通過變換視角,可以對圖像進行拼接声离。當然距離真正的全景圖像的合成還有點距離芒炼,比如說有明顯邊界,扭曲嚴重等問題抵恋。OpenCV3中提供了一個函數(shù)叫stitcher焕议,可以得到比較好的拼接效果。接下來的一段時間就需要我去研究一下里面的實現(xiàn)了,敬請期待吧 →_→!
References:
SLAM入門之視覺里程計(5):單應矩陣
Opencv Sift和Surf特征實現(xiàn)圖像無縫拼接生成全景圖像
opencv計算機視覺編程攻略(第三版) —— Robert Laganiere