鱼眼拼接之SIFT之进行特征点匹配

来源:互联网 发布:笨办法学python第四版 编辑:程序博客网 时间:2024/05/03 17:54

这是SIFT检测特征点的最后一步,也是鱼眼拼接的开始第一步,比如有三幅鱼眼图像s1,s2,s3要将它们拼接成一幅完整的图,之前用sift就是分别得到s1,s2,s3的特征点描述子,现在拼接第一步就是找到两幅图匹配的特征点,比如我的三幅图顺序是s3s1s2这是拼接好以后的顺序,那么我现在就是要找到s3和s1之间匹配的特征点;s1和s2的匹配的特征点。说白了 就是这两幅图有一部分内容是重合的,,,看我发上来的图就知道了。

这是S1

这是s2

这是s3  额又把黄小鸭不小心弄上来了  忽略它吧。。。

可以看到s1和s3这两幅重合部分是那几棵树和被树挡到的房子  即 是我们应该生成的特征点匹配对 我装在matchpoint1里

而s1和s2这两幅的重合部分是这栋大房子的一部分和几棵树  即 是我们应该生成的特征点匹配对  我装在matchpoint2里

当两幅图像的SIFT特征向量生成后采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量 s3中的某个关键点,并找出其与s1中欧式距离最近的前两个关键点,在这两个关键点中,如果最近的距离除以次近的距离少于某个比例阈值(0.4到0.6),则接受这一对匹配点。降低这个比例阈值,SIFT匹配点数目会减少,但更加稳定。我取的0.5。
找到的匹配的特征点对放在matchpoint1和matchpoint2矩阵里  最近距离是firstd 次近距离是secondd

找到s3s1之间的特征点匹配点:

d=0;

firstd=100;

secondd=100;

matchpoint1=[];

for t3=1:m3

   for t1=1:m1

       for k=1:32

         d=d+(allvector3(t3,k)-allvector1(t1,k))^2;

       end    

       d=sqrt(d);

       if(d<firstd)

          firstd=d;

          mm=t1;

       end

       d=0;

   end

   for t1=1:m1

       for k=1:32

          d=d+(allvector3(t3,k)-allvector1(t1,k))^2;

       end

       d=sqrt(d);

       if(d<secondd && d>firstd)

          secondd=d;

       end

       d=0;

   end

   if(firstd/secondd<0.5)

       matchpoint1=[matchpoint1;t3,mm];

   end

end

找到s2和s1之间的匹配特征点

d=0;

firstd=100;

secondd=100;

matchpoint2=[];

for t2=1:m2

   for t1=1:m1

       for k=1:32

         d=d+(allvector2(t2,k)-allvector1(t1,k))^2;

       end

       d=sqrt(d);

       if(d<firstd)

          firstd=d;

          mm=t1;

       end

       d=0;

   end

   for t1=1:m1

       for k=1:32

         d=d+(allvector2(t2,k)-allvector1(t1,k))^2;

       end

       d=sqrt(d);

       if(d<secondd&& d>firstd)

         secondd=d;

       end

       d=0;

   end

  if(firstd/secondd<0.5)

      matchpoint2=[matchpoint2;t2,mm];

   end

end

为了后面方便  我把生成匹配特征点对编成M文件 以后直接调用:

function matchpoint1=pipeipoints(allvector1,allvector3)
%The function is meant to get the pipei character points of two miaoshuzi
% of two pictures.
[m1,n1]=size(allvector1);
[m3,n3]=size(allvector3);
d=0;
firstd=100;
secondd=100;
matchpoint1=[];
for t3=1:m3
   for t1=1:m1
       for k=1:32
          d=d+(allvector3(t3,k)-allvector1(t1,k))^2;
       end
       d=sqrt(d);
       if(d<firstd)
          firstd=d;
          mm=t1;
       end
       d=0;
   end
   for t1=1:m1
       for k=1:32
          d=d+(allvector3(t3,k)-allvector1(t1,k))^2;
       end
       d=sqrt(d);
       if(d<secondd && d>firstd)
          secondd=d;
       end
       d=0;
   end
   if(firstd/secondd<0.5)
       matchpoint1=[matchpoint1;t3,mm];
   end
end


我本来想把特征点匹配对显示出来 也就是对s3与s1这两幅图 那他们显示出来的部分 应该是那几棵树和被树挡到的一栋小房子  可是我发现无论是s3还是s1显示出来都不是这样子 即使是误匹配 那也不至于这样严重  所以后来我又重看了几遍SIFT完整的理论  发现那些牛人除了生成描述子时用的是32维  并没有严格遵守用128维   所以我用的是32维  还有一点就是生成梯度直方图时  我前几篇都是用的36个方向  因为SIFT理论上是对每个特征点要36个方向  然后找出主方向  所以我也是这样做的  但这次重看时 我发现他们并没有36个方向  他们对每个特征点只用了8个方向   他们说理论上用36个方向   所以我现在重新改了生成梯度直方图那里  改成了8个方向 :

function allbin=tiduzhifangtu(D12,XY1,H1)
%The function is meant to get zhifangtu of every character point of XY1.
X=XY1(:,1);
Y=XY1(:,2);
rad=1.5*0.4;
[m,n]=size(X);
bin=zeros(1,8);
max_mag=zeros(1,m);
allbin=zeros(1,m);
[m1,n1]=size(H1);
for t=1:m
    for i=-rad:rad
        for j=-rad:rad
            if(ceil(X(t)+i+1)>m1 || ceil(Y(t)+j+1)>n1)
                continue;
            end
            mag=sqrt((D12(ceil(X(t)+i+1),ceil(Y(t)+j))-D12(ceil(X(t)+i-1),ceil(Y(t)+j)))^2+(D12(ceil(X(t)+i),ceil(Y(t)+j+1))-D12(ceil(X(t)+i),ceil(Y(t)+j-1)))^2);
            ori=atan2(D12(ceil(X(t)+i),ceil(Y(t)+j+1))-D12(ceil(X(t)+i),ceil(Y(t)+j-1)),D12(ceil(X(t)+i+1),ceil(Y(t)+j))-D12(ceil(X(t)+i-1),ceil(Y(t)+j)));
            w=exp(-(i^2+j^2)/(2*0.4^2));
            n=ceil(8*(ori+pi)/(2*pi));
            if(n>8);
               n=n-8;
            end
            bin(n)=bin(n)+w*mag;
        end
end
    [max_mag(t),allbin(t)]=max(bin);
    bin=zeros(1,8);
end

  然后重新试了下  看生成的匹配特征点对是不是正确:

matchpoint1=pipeipoints(allvector1,allvector3);
matchpoint2=pipeipoints(allvector1,allvector2);
>> [mn,ti]=size(matchpoint1);
X=[];
Y=[];
for t=1:mn
     X=[X XY3(matchpoint1(t,1),1)];
     Y=[Y XY3(matchpoint1(t,1),2)];
end
imshow(H3)
hold on
plot(X,Y,'g.')

这是结果  可以看到s3这幅图除了左边有大概4个特征点是误匹配点  右边这一些还是很准确的显示出来了   很漂亮  我自己挺满意的  毕竟我编的时候没有编辅方向 还有一点  对当有两个峰值时 应该把一个特征点另外再生成一个特征点 就是拆成两个  这两步细节我都没有编进程序里来  所以有这样的效果 虽然这4个点是多的  我还是挺满意  s3显示得很准确

下面看s1生成的匹配对:

[mn,ti]=size(matchpoint1);
X=[];
Y=[];
for t=1:mn
     X=[X XY1(matchpoint1(t,2),1)];
     Y=[Y XY1(matchpoint1(t,2),2)];
end
figure
imshow(H1)
hold on
plot(X,Y,'g.')

看得见这两个点吗  为什么s1出来是这样子呢   说好的树呢?说好的小房子呢?怎么都没显示出来?按道理来说 这幅图的左边部分的特征点应该显示出来  可是为什么没有?我再想想。。。

接下来看matchpoint2里的匹配对


这两幅图本来应该是这个大房子和前面这两棵树都显示出来的。。。可是结果却是这样。。。为什么呢?

我看了下  s3和s1的特征点匹配对本来应该有253对   s2和s1的特征点匹配对应该有25对  可是关键是s1中的点都是相同的一两个点?为什么s1会这样子?我再想想。。。匹配对这里为什么有的显示正确有的错得离谱


我想起来了,后面旋转到主方向时,算角度时我之前是36个方向 所以用的下标乘以10度再转化为弧度  我刚刚改成了8个方向 可是这里还没改  应该改成乘以45度再转化为弧度

也就是在这个函数里function allvector1=miaoshuzi(allbin,XY1,D12,H1)生成描述子这里有一条语句是计算角度:arfa=2*pi*allbin(t)*45/360; 改成这样子就好了  

然后我重新试了写  看匹配对显示出来是什么样


可以看到 对于s1无论是与s3重合的部分还是与s2 它上面都没有显示出任何匹配对  所以s1这里是有问题的    

对于s3 它与s1重合的部分显示出来太多了 要是像没改之前那样就好了  可是这里显示了不是匹配对的匹配对  

对于s2   这里显示得还是比较好的  因为它与s1重合的基本上就是显示出来的这个大房子的一面墙和一点点草坪,树   所以这个匹配点对 255个还是比较可靠的

要是两个结合下就好了  matchpoint1像之前的253个 matchpoint2像现在的255个  。。。。所以这个还是有问题 在哪里 我想想。。。


十几天前看了浅墨大神的博客 决心学习OpenCV  然后买了他的《OpenCV3编程入门》 现在看完了 后面最后一章讲了一点SIFT  我稍微改动了下 用两幅图匹配 结果:


#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/core/core.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage1 = imread("s1.jpg",1);  
        Mat srcImage2 = imread("s2.jpg",1);  
        if( !srcImage1.data || !srcImage2.data )  
            { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~! \n"); return false; } 
vector<KeyPoint> train_keyPoint,train_keyPoint2;
Mat trainDescription,trainDescription2;
SiftFeatureDetector featureDetector;
featureDetector.detect(srcImage1, train_keyPoint);
featureDetector.detect(srcImage2, train_keyPoint2);
SiftDescriptorExtractor featureExtractor;
featureExtractor.compute(srcImage1, train_keyPoint, trainDescription);
featureExtractor.compute(srcImage2, train_keyPoint2, trainDescription2);


BFMatcher matcher;
vector<Mat> train_desc_collection(1, trainDescription);
matcher.add(train_desc_collection);
matcher.train();
vector<vector<DMatch> >  matches;
matcher.knnMatch(trainDescription2,matches,2); 
vector<DMatch> goodMatches;
for(unsigned int i = 0; i < matches.size(); i++)
{
if(matches[i][0].distance < 0.6 * matches[i][1].distance)
goodMatches.push_back(matches[i][0]);
}
Mat dstImage;
drawMatches(srcImage2, train_keyPoint2, srcImage1, train_keyPoint, goodMatches,              dstImage,Scalar::all(-1),Scalar::all(-1),vector<char(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow("匹配窗口", dstImage);
waitKey(0);
return 0;
}

效果还可以

至于我之前用MATLAB编SIFT时的问题  我想只有等我去研究完用OpenCV写的SIFT的源码 才能知道 

过几天要开学了 啊不想开学 开学了开的那几个课又不是我这个方向的 又必须去上凑学分  浪费时间浪费生命!!!

今天2015.10.7了  我前一段时间每天抽一点时间开始看拼接  希望这两个月能了解图像拼接融合   然后至于特征点提取、匹配那里我就不重新看了   我想重点放在后面的拼接、融合   所以我就把OpenCV特征点提取匹配SIFT的结果直接拿来用   我现在是把SIFT初次匹配的结果输出(方便以后用MATLAB编图像拼接融合时直接读取匹配对)  我在上述程序中加入下一段   :

int cnt=0;
for( size_t i = 0; i < matches.size(); i++ )
    {
        for( size_t j = 0; j < matches[i].size(); j++ )
        {
            int i1 = matches[i][j].queryIdx;
            int i2 = matches[i][j].trainIdx;
KeyPoint kp1 =train_keyPoint[i1], kp2=train_keyPoint2[i2];
Point2f pt1=kp1.pt,pt2=kp2.pt;
Point mypt1(cvRound(pt1.x),cvRound(pt1.y)),mypt2(cvRound(pt2.x),cvRound(pt2.y));
cout<<mypt1<<" "<<mypt2<<endl;
++cnt;
}
}

cout<<"There is "<<cnt<<" points that match to each other!"<<endl;

结果:

的确有两幅图的匹配对输出来  可是报错说vector下标超过范围???

刚刚我又试了下  不输出初次粗匹配的匹配对坐标  而输出经过RANSAC后的匹配对  结果没报错  统计出来有24对匹配对:

可是之前输出初次粗匹配的匹配对坐标为什么报错呢??

问题已解决  在《将OpenCV里SIFT的keypoint坐标和描述子向量输出给MATLAB》里

0 0