本文目标是通过使用SIFT和RANSAC
算法
,完成特征点的正确匹配,并求出变换矩阵,通过变换矩阵计算出要识别物体的边界(文章中有部分源码,整个工程我也上传了,请点击
这里
)。
SIFT算法是目前公认的效果最好的特征点检测算法,关于该算法的就不多说了,网上的资料有很多,在此提供两个链接,一个是SIFT原文的译文,一个是关于SIFT算法的详细解释:
SIFT算法译文
SIFT算法详解
整个实现过程可以复述如下:提供两张初始图片,一幅为模板图像,一幅为
测试
图片,目的就是根据模板图片中的物体,检测出测试图片中的物体,并表示出物体的具体位置和大小,测试图片中的物体位置和大小,已经事先用白色方框标记。
首先,对两幅图片,都使用SIFT算法提取特征点,提取结果如下:(SIFT特征提取方法就用的是上文链接“SIFT算法详解”中提供的代码)
然后对特征点进行匹配,按照SIFT算法原文作者的思路,每个特征点产生一个128维的向量,计算向量之间的欧式距离,采用最近比次近的方式完成匹配,如果最近距离比上次近距离小于0.8,则认为这是一个正确的匹配, 否则认为匹配不成功。结果这种匹配后的情况如下图:
可以发现,仍然存在着很多错误的匹配点,所以再尝试用RANSAC算法消除错误匹配,尝试使用
OpenCV
中的findFundamentalMat函数消除错误匹配:
通过使用findFundamentalMat函数,函数返回一个3*3的矩阵,一开始我认为这个矩阵就是变换矩阵,只要将左图中的点与这个变换矩阵相乘,就可以得到右图中的对应点。但是这其实是不对的。
在这里有一个误解,就是findFundamentalMat函数确实可以使用RANSAC方法消除错误匹配,从名字上可以发现,这个函数的作用是返回基础矩阵的,基础矩阵和变换矩阵是两个不同的概念。基础矩阵描述是三维场景中的像点之间的对应关系(其实到现在为止这个函数求出的基础矩阵有个毛用我也不知道)。所以说,如果使用这个函数,这个实验也就能做到这一步了,没法再往下做了。
所以,为了得到变换矩阵,后来我才发现openCV中还有函数findHomography,这个函数才是真正的计算变换矩阵的函数,它的函数返回值才是真正的变换矩阵。
其实这个问题困扰了我很久,关于消除错误匹配的方法,网上查出来的多数都是通过findFundamentalMat函数来进行,所以我就想当然的认为该函数的返回值是变换矩阵了。而网上关于findHomography的介绍比较少,所以才会让人们误解findFundamentalMat会计算出变换矩阵了。
尝试用findHomography函数返回的矩阵,在模板图像中,已经用绿色方框标示出物体轮廓,根据物体的四个边界点,与变换矩阵相乘,即可得到变换后的物体的轮廓的四个边界点,将此边界点连接即为物体轮廓,如下图所示(绿色方框为事先标注的模板物体中的轮廓,白色方框为事先标注的测试图片中的轮廓,红色方框为经过绿色方框经变换矩阵变换后计算出的轮廓):
从结果可以看出,这才是比较正确的结果。
实验过程中的主要代码如下(这是主要的代码,SIFT算法和一些其他的功能函数我都写在了其他的文件中):
-
#include<math.h>
-
#include<time.h>
-
-
#include <windows.h>
-
#include <iostream>
-
using
namespace
std;
-
#include <cv.h>
-
#include <highgui.h>
-
#include <cxcore.h>
-
using
namespace
cv;
-
#include “sift.h”
-
#include “my_function.h”
-
-
int
main()
-
{
-
-
Mat src1 = imread(
”F:\\ylab\\image database\\camera\\obj01_001.jpg”
);
-
Mat src2 = imread(
”F:\\ylab\\image database\\imagesTest2\\test01_.jpg”
);
-
-
-
Point2f m1(173.0,0.0),m2(168.0,464.0),m3(507.0,464.0),m4(499.0,0.0);
-
std::vector<Point2f> obj_corners(4);
-
obj_corners[0] = cvPoint(173.0,0.0);
-
obj_corners[1] = cvPoint(168.0,464.0);
-
obj_corners[2] = cvPoint(507.0,464.0);
-
obj_corners[3] = cvPoint(499.0,0.0);
-
-
-
Size certainsize=Size(640,480);
-
Mat src_1;
-
Mat src_2;
-
resize(src1,src_1,certainsize);
-
resize(src2,src_2,certainsize);
-
-
-
Vector<Keypoint> feature_1,feature_2;
-
-
-
Sift(src_1, feature_1, 1.6);
-
Sift(src_2, feature_2, 1.6);
-
-
-
Vector<Key_point> feature_dis_1;
-
Vector<Key_point> feature_dis_2;
-
Vector<Key_point> result;
-
-
-
-
Match_feature(feature_1,feature_2,feature_dis_1,feature_dis_2);
-
-
printf(
”The number of features is %d\n”
,feature_1.size());
-
printf(
”The number of the match features is %d\n”
,feature_dis_1.size());
-
-
-
-
Ptr<DescriptorMatcher> descriptor_matcher = DescriptorMatcher::create(
”BruteForce”
);
-
int
count=feature_dis_1.size();
-
-
-
vector<KeyPoint>keypoints1,keypoints2;
-
KeyPoint keyp;
-
for
(
int
i=0;i<count;i++)
-
{
-
keyp.pt.x=feature_dis_1[i].dx;
-
keyp.pt.y=feature_dis_1[i].dy;
-
keypoints1.push_back(keyp);
-
keyp.pt.x=feature_dis_2[i].dx;
-
keyp.pt.y=feature_dis_2[i].dy;
-
keypoints2.push_back(keyp);
-
}
-
-
Mat descriptors1(count,FEATURE_ELEMENT_LENGTH, CV_32F);
-
Mat descriptors2(count,FEATURE_ELEMENT_LENGTH, CV_32F);
-
-
for
(
int
i=0; i<count; i++)
-
{
-
for
(
int
j=0;j<FEATURE_ELEMENT_LENGTH;j++)
-
{
-
descriptors1.at<
float
>(i,j)=feature_dis_1[i].descriptor[j];
-
descriptors2.at<
float
>(i,j)=feature_dis_2[i].descriptor[j];
-
}
-
-
}
-
-
Mat img_match;
-
vector<DMatch> matches;
-
descriptor_matcher->match( descriptors1, descriptors2, matches );
-
Mat img_matches;
-
drawMatches(src_1,keypoints1,src_2,keypoints2,matches,img_matches);
-
-
imshow(
”SIFT”
,img_matches);
-
-
-
Mat p1(feature_dis_1.size(),2,CV_32F);
-
Mat p2(feature_dis_1.size(),2,CV_32F);
-
for
(
int
i=0;i<feature_dis_1.size();i++)
-
{
-
p1.at<
float
>(i,0)=feature_dis_1[i].dx;
-
p1.at<
float
>(i,1)=feature_dis_1[i].dy;
-
p2.at<
float
>(i,0)=feature_dis_2[i].dx;
-
p2.at<
float
>(i,1)=feature_dis_2[i].dy;
-
}
-
-
Mat m_Fundamental;
-
-
vector<uchar> m_RANSACStatus;
-
-
-
-
-
m_Fundamental = findFundamentalMat(p1,p2,m_RANSACStatus,CV_FM_RANSAC);
-
-
-
Mat m_homography;
-
vector<uchar> m;
-
m_homography=findHomography(p1,p2,CV_RANSAC,3,m);
-
-
-
std::vector<Point2f> scene_corners(4);
-
perspectiveTransform( obj_corners, scene_corners, m_homography);
-
line( src_2, scene_corners[0] , scene_corners[1] , Scalar(0, 0, 255), 2 );
-
line( src_2, scene_corners[1] , scene_corners[2] , Scalar(0, 0, 255), 2 );
-
line( src_2, scene_corners[2] , scene_corners[3] , Scalar(0, 0, 255), 2 );
-
line( src_2, scene_corners[3] , scene_corners[0] , Scalar(0, 0, 255), 2 );
-
-
-
-
int
nr=m_Fundamental.rows;
-
int
nc=m_Fundamental.cols *m_Fundamental.channels();
-
-
-
int
OutlinerCount = 0;
-
for
(
int
i=0; i<Count; i++)
-
{
-
if
(m_RANSACStatus[i] == 0)
-
{
-
OutlinerCount++;
-
}
-
}
-
-
-
vector<Point2f> m_LeftInlier;
-
vector<Point2f> m_RightInlier;
-
vector<DMatch> m_InlierMatches;
-
-
int
ptCount = (
int
)matches.size();
-
int
InlinerCount = ptCount - OutlinerCount;
-
m_InlierMatches.resize(InlinerCount);
-
m_LeftInlier.resize(InlinerCount);
-
m_RightInlier.resize(InlinerCount);
-
InlinerCount = 0;
-
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;
-
InlinerCount++;
-
}
-
}
-
-
-
vector<KeyPoint> key1(InlinerCount);
-
vector<KeyPoint> key2(InlinerCount);
-
KeyPoint::convert(m_LeftInlier, key1);
-
KeyPoint::convert(m_RightInlier, key2);
-
-
-
-
-
-
-
line(src_1,m1,m2,Scalar(0,255,0),2);
-
line(src_1,m2,m3,Scalar(0,255,0),2);
-
line(src_1,m3,m4,Scalar(0,255,0),2);
-
line(src_1,m4,m1,Scalar(0,255,0),2);
-
-
Mat OutImage;
-
drawMatches(src_1, key1, src_2, key2, m_InlierMatches, OutImage);
-
imshow(
”SIFT_RANSAC”
,OutImage);
-
-
cvWaitKey( 0 );
-
return
0;
-
}
3、cvShowImage:在一个已创建好的窗口中显示图像;
4、cvWaitKey:使程序暂停,等待用户触发一个按键操作;
5、cvReleaseImage:释放图像文件所分配的内存;
6、cvDestroyWindo
这频率是每半个月一课啊,每周一课要改名了八!哈哈哈
http://blog.sina.com.cn/s/blog_4298002e01013w9a.html
OpenCV在未知相机内参数情况下的立体图像矫正方法及注意事项
http://blog.sina.com.cn/s/blog_4298002e01013yb8.html
SIFT
算法
是目前公认的效果最好的特征点检测
算法
,关于该
算法
的就不多说了,网上的资料有很多,在此提供两个链接,一个是
SIFT
原文的译文,一个是关于
SIFT
算法
的详细解释:
SIFT
算法
译文
SIFT
算法
详解
整个实现过程可以复述如下:提供两张初始图片,一幅为模板图像,一幅为测试图片,目的就是根据模板图片中的物体,检测出测试图片中的物体,并表示出物体的具体位置和大小,测试图片中的物体位
http://hi.baidu.com/zhongdudu/blog/item/8f6b242424e53833c995597f.html
关于Opencv的一些内容, 可以访问该博客的其他内容.
OpenCV 图像处理常用函数
2007-02-25 12
RANSAC(RANdom SAmple Consensus随机抽样一致)通过反复选择数据中的一组随机子集来达成目标。被选取的子集被假设为局内点,并用下述方法进行验证:
1.有一个模型适应于假设的局内点,即所有的未知参数都能从假设的局内点计算得出。(求解仿射变换,至少需要三个点)
2.用1中得到的模型去测试所有的其它数据,如果某个点适用于估计的模型,认为它也是局内点。
3.如果有足够多的点被归...
首先需要把cmakelists补全:
运行报错:terminate called after throwing an instance of ‘std::logic_error’
what(): basic_string::_M_construct null not valid
解决方法:
打开终端创建build
需要输入图片路径,否则是空指针
之后发现报错:
Failed to load module “canberra-gtk-module”
解决方法:
在终端输入: sudo apt-get ins