OpenCV2.4.9+X64+VS2015測試通過
需要的小夥伴配置好環境就可以用了,只是個最簡單功能,初學者可以試試。
#include <stdio.h>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/opencv.hpp>
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
initModule_nonfree();//初始化模塊,使用SIFT或SURF時用到
Ptr<FeatureDetector> detector = FeatureDetector::create( "SIFT" );//創建SIFT特徵檢測器,可改成SURF/ORB
Ptr<DescriptorExtractor> descriptor_extractor = DescriptorExtractor::create( "SIFT" );//創建特徵向量生成器,可改成SURF/ORB
Ptr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create( "BruteForce" );//創建特徵匹配器
if( detector.empty() || descriptor_extractor.empty() )
cout<<"fail to create detector!";
//讀入圖像
Mat img1 = imread("imLL.png");
Mat img2 = imread("imRR.png");
//特徵點檢測
double t = getTickCount();//當前滴答數
vector<KeyPoint> m_LeftKey,m_RightKey;
detector->detect( img1, m_LeftKey );//檢測img1中的SIFT特徵點,存儲到m_LeftKey中
detector->detect( img2, m_RightKey );
cout<<"圖像1特徵點個數:"<<m_LeftKey.size()<<endl;
cout<<"圖像2特徵點個數:"<<m_RightKey.size()<<endl;
//根據特徵點計算特徵描述子矩陣,即特徵向量矩陣
Mat descriptors1,descriptors2;
descriptor_extractor->compute( img1, m_LeftKey, descriptors1 );
descriptor_extractor->compute( img2, m_RightKey, descriptors2 );
t = ((double)getTickCount() - t)/getTickFrequency();
cout<<"SIFT算法用時:"<<t<<"秒"<<endl;
cout<<"圖像1特徵描述矩陣大小:"<<descriptors1.size()
<<",特徵向量個數:"<<descriptors1.rows<<",維數:"<<descriptors1.cols<<endl;
cout<<"圖像2特徵描述矩陣大小:"<<descriptors2.size()
<<",特徵向量個數:"<<descriptors2.rows<<",維數:"<<descriptors2.cols<<endl;
//畫出特徵點
Mat img_m_LeftKey,img_m_RightKey;
drawKeypoints(img1,m_LeftKey,img_m_LeftKey,Scalar::all(-1),0);
drawKeypoints(img2,m_RightKey,img_m_RightKey,Scalar::all(-1),0);
//imshow("Src1",img_m_LeftKey);
//imshow("Src2",img_m_RightKey);
//特徵匹配
vector<DMatch> matches;//匹配結果
descriptor_matcher->match( descriptors1, descriptors2, matches );//匹配兩個圖像的特徵矩陣
cout<<"Match個數:"<<matches.size()<<endl;
//計算匹配結果中距離的最大和最小值
//距離是指兩個特徵向量間的歐式距離,表明兩個特徵的差異,值越小表明兩個特徵點越接近
double max_dist = 0;
double min_dist = 100;
for(int i=0; i<matches.size(); i++)
{
double dist = matches[i].distance;
if(dist < min_dist) min_dist = dist;
if(dist > max_dist) max_dist = dist;
}
cout<<"最大距離:"<<max_dist<<endl;
cout<<"最小距離:"<<min_dist<<endl;
//篩選出較好的匹配點
vector<DMatch> goodMatches;
for(int i=0; i<matches.size(); i++)
{
if(matches[i].distance < 0.2 * max_dist)
{
goodMatches.push_back(matches[i]);
}
}
cout<<"goodMatch個數:"<<goodMatches.size()<<endl;
//畫出匹配結果
Mat img_matches;
//紅色連接的是匹配的特徵點對,綠色是未匹配的特徵點
drawMatches(img1,m_LeftKey,img2,m_RightKey,goodMatches,img_matches,
Scalar::all(-1)/*CV_RGB(255,0,0)*/,CV_RGB(0,255,0),Mat(),2);
imshow("MatchSIFT",img_matches);
IplImage result=img_matches;
waitKey(0);
//RANSAC匹配過程
vector<DMatch> m_Matches=goodMatches;
// 分配空間
int ptCount = (int)m_Matches.size();
Mat p1(ptCount, 2, CV_32F);
Mat p2(ptCount, 2, CV_32F);
// 把Keypoint轉換爲Mat
Point2f pt;
for (int i=0; i<ptCount; i++)
{
pt = m_LeftKey[m_Matches[i].queryIdx].pt;
p1.at<float>(i, 0) = pt.x;
p1.at<float>(i, 1) = pt.y;
pt = m_RightKey[m_Matches[i].trainIdx].pt;
p2.at<float>(i, 0) = pt.x;
p2.at<float>(i, 1) = pt.y;
}
// 用RANSAC方法計算F
Mat m_Fundamental;
vector<uchar> m_RANSACStatus; // 這個變量用於存儲RANSAC後每個點的狀態
findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
// 計算野點個數
int OutlinerCount = 0;
for (int i=0; i<ptCount; i++)
{
if (m_RANSACStatus[i] == 0) // 狀態爲0表示野點
{
OutlinerCount++;
}
}
int InlinerCount = ptCount - OutlinerCount; // 計算內點
cout<<"內點數爲:"<<InlinerCount<<endl;
// 這三個變量用於保存內點和匹配關係
vector<Point2f> m_LeftInlier;
vector<Point2f> m_RightInlier;
vector<DMatch> m_InlierMatches;
m_InlierMatches.resize(InlinerCount);
m_LeftInlier.resize(InlinerCount);
m_RightInlier.resize(InlinerCount);
InlinerCount=0;
float inlier_minRx=img1.cols; //用於存儲內點中右圖最小橫座標,以便後續融合
for (int i=0; i<ptCount; i++)
{
if (m_RANSACStatus[i] != 0)
{
m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
if(m_RightInlier[InlinerCount].x<inlier_minRx) inlier_minRx=m_RightInlier[InlinerCount].x; //存儲內點中右圖最小橫座標
InlinerCount++;
}
}
// 把內點轉換爲drawMatches可以使用的格式
vector<KeyPoint> key1(InlinerCount);
vector<KeyPoint> key2(InlinerCount);
KeyPoint::convert(m_LeftInlier, key1);
KeyPoint::convert(m_RightInlier, key2);
// 顯示計算F過後的內點匹配
Mat OutImage;
drawMatches(img1, key1, img2, key2, m_InlierMatches, OutImage);
cvNamedWindow( "Match features", 1);
cvShowImage("Match features", &IplImage(OutImage));
waitKey(0);
cvDestroyAllWindows();
//矩陣H用以存儲RANSAC得到的單應矩陣
Mat H = findHomography( m_LeftInlier, m_RightInlier, RANSAC );
//存儲左圖四角,及其變換到右圖位置
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point(0,0); obj_corners[1] = Point( img1.cols, 0 );
obj_corners[2] = Point( img1.cols, img1.rows ); obj_corners[3] = Point( 0, img1.rows );
std::vector<Point2f> scene_corners(4);
perspectiveTransform( obj_corners, scene_corners, H);
//畫出變換後圖像位置
Point2f offset( (float)img1.cols, 0);
line( OutImage, scene_corners[0]+offset, scene_corners[1]+offset, Scalar( 0, 255, 0), 4 );
line( OutImage, scene_corners[1]+offset, scene_corners[2]+offset, Scalar( 0, 255, 0), 4 );
line( OutImage, scene_corners[2]+offset, scene_corners[3]+offset, Scalar( 0, 255, 0), 4 );
line( OutImage, scene_corners[3]+offset, scene_corners[0]+offset, Scalar( 0, 255, 0), 4 );
imshow( "Good Matches & Object detection", OutImage );
while(1)
{
if(waitKey(100)==19) cvSaveImage("E:\\warp_position.jpg", &IplImage(OutImage));
if(waitKey(100)==27) break;
} //按esc繼續,ctl+s保存圖像
int drift = scene_corners[1].x; //儲存偏移量
//新建一個矩陣存儲配準後四角的位置
int width = int(max(abs(scene_corners[1].x), abs(scene_corners[2].x)));
int height= img1.rows; //或者:int height = int(max(abs(scene_corners[2].y), abs(scene_corners[3].y)));
float origin_x=0,origin_y=0;
if(scene_corners[0].x<0) {
if (scene_corners[3].x<0) origin_x+=min(scene_corners[0].x,scene_corners[3].x);
else origin_x+=scene_corners[0].x;}
width-=int(origin_x);
if(scene_corners[0].y<0) {
if (scene_corners[1].y) origin_y+=min(scene_corners[0].y,scene_corners[1].y);
else origin_y+=scene_corners[0].y;}
//可選:height-=int(origin_y);
Mat imageturn=Mat::zeros(width,height,img1.type());
//獲取新的變換矩陣,使圖像完整顯示
for (int i=0;i<4;i++) {scene_corners[i].x -= origin_x; } //可選:scene_corners[i].y -= (float)origin_y; }
Mat H1=getPerspectiveTransform(obj_corners, scene_corners);
//進行圖像變換,顯示效果
warpPerspective(img1,imageturn,H1,Size(width,height));
imshow("image_Perspective", imageturn);
waitKey(0);
//圖像融合
int width_ol=width-int(inlier_minRx-origin_x);
int start_x=int(inlier_minRx-origin_x);
cout<<"width: "<<width<<endl;
cout<<"img1.width: "<<img1.cols<<endl;
cout<<"start_x: "<<start_x<<endl;
cout<<"width_ol: "<<width_ol<<endl;
uchar* ptr=imageturn.data;
double alpha=0, beta=1;
for (int row=0;row<height;row++) {
ptr=imageturn.data+row*imageturn.step+(start_x)*imageturn.elemSize();
for(int col=0;col<width_ol;col++)
{
uchar* ptr_c1=ptr+imageturn.elemSize1(); uchar* ptr_c2=ptr_c1+imageturn.elemSize1();
uchar* ptr2=img2.data+row*img2.step+(col+int(inlier_minRx))*img2.elemSize();
uchar* ptr2_c1=ptr2+img2.elemSize1(); uchar* ptr2_c2=ptr2_c1+img2.elemSize1();
alpha=double(col)/double(width_ol); beta=1-alpha;
if (*ptr==0&&*ptr_c1==0&&*ptr_c2==0) {
*ptr=(*ptr2);
*ptr_c1=(*ptr2_c1);
*ptr_c2=(*ptr2_c2);
}
*ptr=(*ptr)*beta+(*ptr2)*alpha;
*ptr_c1=(*ptr_c1)*beta+(*ptr2_c1)*alpha;
*ptr_c2=(*ptr_c2)*beta+(*ptr2_c2)*alpha;
ptr+=imageturn.elemSize();
} }
//imshow("image_overlap", imageturn);
//waitKey(0);
Mat img_result=Mat::zeros(height,width+img2.cols-drift,img1.type());
uchar* ptr_r=imageturn.data;
for (int row=0;row<height;row++) {
ptr_r=img_result.data+row*img_result.step;
for(int col=0;col<imageturn.cols;col++)
{
uchar* ptr_rc1=ptr_r+imageturn.elemSize1(); uchar* ptr_rc2=ptr_rc1+imageturn.elemSize1();
uchar* ptr=imageturn.data+row*imageturn.step+col*imageturn.elemSize();
uchar* ptr_c1=ptr+imageturn.elemSize1(); uchar* ptr_c2=ptr_c1+imageturn.elemSize1();
*ptr_r=*ptr;
*ptr_rc1=*ptr_c1;
*ptr_rc2=*ptr_c2;
ptr_r+=img_result.elemSize();
}
ptr_r=img_result.data+row*img_result.step+imageturn.cols*img_result.elemSize();
for(int col=imageturn.cols;col<img_result.cols;col++)
{
uchar* ptr_rc1=ptr_r+imageturn.elemSize1(); uchar* ptr_rc2=ptr_rc1+imageturn.elemSize1();
uchar* ptr2=img2.data+row*img2.step+(col-imageturn.cols+drift)*img2.elemSize();
uchar* ptr2_c1=ptr2+img2.elemSize1(); uchar* ptr2_c2=ptr2_c1+img2.elemSize1();
*ptr_r=*ptr2;
*ptr_rc1=*ptr2_c1;
*ptr_rc2=*ptr2_c2;
ptr_r+=img_result.elemSize();
}
}
imshow("image_result", img_result);
while(1)
{
if(waitKey(100)==19) cvSaveImage("E:\\final_result.jpg", &IplImage(img_result));
if(waitKey(100)==27) break; //按esc退出,ctl+s保存圖像
}
return 0;
}
示例