将OpenCV里SIFT的keypoint坐标以及描述子向量输出给MATLAB

来源:互联网 发布:linux复制并重命名 编辑:程序博客网 时间:2024/05/16 03:49

接着《鱼眼拼接之SIFT之进行特征点匹配》后面一部分,那里我将两幅图的匹配对坐标输出,总共有24对,然后我刚刚想检验下我输出的是否是实实在在成功的匹配对,然后我这样编来检验:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/core/core.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage1 = imread("11.jpg",1);  
       Mat srcImage2 = imread("22.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.628 * matches[i][1].distance)
goodMatches.push_back(matches[i][0]);
}

       std::vector<KeyPoint> kpt1,kpt2;
Mat kpt1_img,kpt2_img;
int num=0;
for( size_t k = 0; k< goodMatches.size(); k++ )
        {
            int fir1 = goodMatches[k].queryIdx;
            int sec2 = goodMatches[k].trainIdx;
KeyPoint kpp1 =train_keyPoint[fir1], kpp2=train_keyPoint2[sec2];
kpt1.push_back(kpp1);
kpt2.push_back(kpp2);
Point2f ptt1=kpp1.pt,ptt2=kpp2.pt;
            Point myptt1(cvRound(ptt1.x),cvRound(ptt1.y)),myptt2(cvRound(ptt2.x),cvRound(ptt2.y));
cout<<myptt1<<"   "<<myptt2<<endl;
++num;
}
cout<<"There are "<<num<<" points that match to each other very well!"<<endl;
//上面输出的是经过去噪精确后的匹配对坐标
drawKeypoints(srcImage1,kpt1,kpt1_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2,kpt2,kpt2_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("keypoints1",kpt1_img);
imshow("keypoints2",kpt2_img);
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;
}

结果:

这个是goodMatches里的匹配对坐标

这个是绘制的匹配对

这个为什么是这样 明明程序中我是把输出坐标的第二列给了这幅图 数了数 的确有24个点 但是为什么和上面那副绘制匹配对的图里的左边的图里的特征点不一样??

这个也是 为什么和“匹配窗口”里那幅图的右图的匹配点不一样位置?明明我是把输出匹配对的第一列坐标全部绘制在了这幅图中  难道是这两幅图的确是按照24对坐标绘制的 但“匹配窗口”里已经配对的特征点却不是按照那个坐标来的  不对啊 drawMatches(srcImage2, train_keyPoint2, srcImage1, train_keyPoint, goodMatches, dstImage,Scalar::all(-1),Scalar::all(-1),vector<char>(),DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);这句就是绘制的goodMatches里的匹配对  按坐标画的  可是为什么??
还有一个奇怪之处就是 我想输出matches里也就是初次匹配的匹配对坐标 结果像在《鱼眼拼接之SIFT之进行特征点匹配》说的一样 报错  而且我今天发现 matches里的初次匹配的匹配对坐标是“多对一”、“一对多”、甚至是“多对多的”,也就是说一幅图的特征点不是唯一和另一幅图的特征点相匹配的   细看这个结果就知道:

细看这些matches里初次匹配对的坐标  会发现比如这个:就不是一一对应的  我猜想matches匹配的时候 就是以一幅图像的每个像素为基准 然后去另一幅图像里找和它匹配的点  可能不只一个  也就是有误匹配的情况  所以才出现这种多对一的情况   

可是这个错误我还是不知道该怎么办:


感谢一个美女的导师  第一个问题终于知道为什么了 原来是写反了 真想戳瞎自己的铝合金狗眼

原来goodMatches是把第一幅图作为训练集  第二幅作为训练集  所以trainIdx其实是第一幅图的  而我之前想的是相反的  所以我之前把它传给第二幅图了  这样出来结果就正确了:

和下面这幅图的一样!!


也就证明这里输出的坐标是正确的!!

不过上面的是精确匹配对的坐标  那之前的第一个问题还是没解决 也就是matches里的坐标输出问题?

刚刚吃饭回来 我把matches里的重新弄了一下  现在可以了 原来matches里放的不是匹配对  而是所有检测到的keypoints两幅图的 :

764个keypoints!!! 但是我有个小疑惑 两幅图里的keypoints一定相同数目吗 不一定  因为两幅图的内容不完全相同 所以肯定不同  等下再想想

咦刚刚又试了下 先输出matches里第一幅图的keypoints  输出来是764个;然后又输出第二幅图的keypoints 输出来还是764  我想应该是matches是筛选了的 所以相同数目。

这样修改后 总的就是这样:

 #include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/core/core.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage1 = imread("11.jpg",1);  
    Mat srcImage2 = imread("22.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.628* matches[i][1].distance) //0.6时候只有20对
goodMatches.push_back(matches[i][0]);
}
//输出来的是两幅图的特征点,matches里放的不是匹配点 只是检测到的两幅图的特征点
Mat pt1_img,pt2_img;
std::vector<KeyPoint> pt1,pt2;
int cnt=0;
    for( size_t i = 0; i < matches.size(); i++ )
    {
        for( size_t j = 0; j < matches[i].size(); j++ )
        {
              int i2 = matches[i][j].queryIdx;
              int i1 = matches[i][j].trainIdx;
 KeyPoint kp1 =train_keyPoint[i1],kp2=train_keyPoint2[i2];
 pt1.push_back(kp1);
 pt2.push_back(kp2);
 Point2f kpp1,kpp2;
 kpp1=kp1.pt;
 kpp2=kp2.pt;
 Point mypt1(cvRound(kpp1.x),cvRound(kpp1.y));       
 Point mypt2(cvRound(kpp2.x),cvRound(kpp2.y));
 cout<<mypt1<<"    "<<mypt2<<endl;
 ++cnt;
  }
}
cout<<"There is "<<cnt<<" keypoints we detected in the first image!!"<<endl<<endl;
    drawKeypoints(srcImage1,pt1,pt1_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2,pt2,pt2_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("keypoints1_primer",pt1_img);
imshow("keypoints2_primer",pt2_img);
std::vector<KeyPoint> kpt1,kpt2;
Mat kpt1_img,kpt2_img;
int num=0;
for( size_t k = 0; k< goodMatches.size(); k++ )
        {
            int sec2 = goodMatches[k].queryIdx;
            int fir1 = goodMatches[k].trainIdx;
KeyPoint kpp1 =train_keyPoint[fir1], kpp2=train_keyPoint2[sec2];
kpt1.push_back(kpp1);
kpt2.push_back(kpp2);
Point2f ptt1=kpp1.pt,ptt2=kpp2.pt;
    Point myptt1(cvRound(ptt1.x),cvRound(ptt1.y)),myptt2(cvRound(ptt2.x),cvRound(ptt2.y));
cout<<myptt1<<"   "<<myptt2<<endl;
++num;
}
cout<<"There are "<<num<<" points that match to each other very well!"<<endl;
drawKeypoints(srcImage1,kpt1,kpt1_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2,kpt2,kpt2_img,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("keypoints1ofmatch",kpt1_img);
imshow("keypoints2ofmatch",kpt2_img);
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;

上面的结果 有一幅匹配图 还有两幅与匹配图中对应坐标的图(分别只有24个keypoints)还有两幅特征点图(764个)还有gui界面 显示764行坐标再显示24行坐标 如下:

 

然后我换了幅图试下,结果也是正确的 846个keypoints  匹配成功的有37对:


接下来就是输出matches里的keypoints的每个对应的128维描述子向量  以及输出goodMatches里匹配对对应keypoints的128维描述子向量   然后把这些描述子坐标以及128维向量生成xml文件给后面的MATLAB 

我今天又试了下 输出没有经过暴力匹配的SIFT检测到的特征点以及描述子,结果:

事实证明这时候检测到的两幅图的特征点是不同数目的 一个是405个  一幅是382幅  这才符合常识  不过我还是有个小疑惑 为什么输出来后还是有的特征点坐标相同 细看就会发现train_keyPoint里有时会有两个相同的  这个是为什么呢?

这是描述子(原来输出来时是这个鬼样子) 根据特征点个数不同 所以描述子明显看到第一个长

这是把第二幅的特征点的坐标输出

如果需要输出没有经过BFMatcher之前的特征点 和描述子 就这样写:

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <opencv2/features2d/features2d.hpp>
#include<opencv2/core/core.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
Mat srcImage1 = imread("11.jpg",1);  
    Mat srcImage2 = imread("22.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);
//show description!
imshow("1descri",trainDescription);
imshow("2descri",trainDescription2);
//show keypoints!
Mat shiyan1,shiyan2;
KeyPoint dian1,dian2;
Point2f pot1,pot2;
int shu1=0,shu2=0;
for(int k=0;k<train_keyPoint.size();k++)
{
dian1=train_keyPoint[k];
pot1=dian1.pt;
cout<<"["<<pot1.x<<" , "<<pot1.y<<"]"<<endl;
++shu1;
}
cout<<shu1<<" keypoints in the first image."<<endl;
   for(int k=0;k<train_keyPoint2.size();k++)
{
dian2=train_keyPoint2[k];
pot2=dian2.pt;
cout<<"["<<pot2.x<<" , "<<pot2.y<<"]"<<endl;
++shu2;
}
cout<<shu2<<" keypoints in the second image."<<endl;
drawKeypoints(srcImage1,train_keyPoint,shiyan1,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
drawKeypoints(srcImage2,train_keyPoint2,shiyan2,Scalar::all(-1),DrawMatchesFlags::DEFAULT);
imshow("keypoints1",shiyan1);
imshow("keypoints2",shiyan2);

        waitKey(0);
return 0;
}

但是SIFT得到的描述子向量是Mat类型 用图像输出就是那个黑白图 可是我想把它按照矩阵的样子输出 比如第一行128维就是第一个keypoint 第二行128维就是第二个keypoint的 我想看到具体的数据 不管是以xml文件还是txt格式  怎么用OpenCV把Mat转化成xml或者txt或者用一个矩阵输出?

我刚刚把SIFT检测到的keypoint坐标用txt文件输出 结果可以  如下所示:

就是在之前的程序里改动一点 就行

在工程目录下生成了两个txt文件 因为我是两幅图的keypoint

用记事本打开可以看到和之前的第二幅图gui界面输出的坐标一样  也就证明是对的

接下来就只用输出这些坐标的keypoint对应的描述子向量了!

刚刚把SIFT检测的特征点的描述子向量输出成txt文件了   如下:

这样改动就行

在目录下会生成两幅图的描述子的txt文件

用记事本打开 然后又傻傻的数了一下 真的有128维  每个特征点128维  就像SIFT理论里写的那样!    至此我要的数据已经全部输出!!!如果后续自己编了匹配的算法或者图像融合的算法(MATLAB写的) 就可以把这些数据拿给MATLAB读了   然后检测算法的有效性!

我是拿这几个txt给MATLAB的矩阵:

可以看到pt1和pt2是两幅图的keypoint坐标 descri1与descir2是描述子向量!

好了,现在总结下,以免以后我要用时候到处找,把OpenCV和MATLAB之前读啊写啊总结下:

1,OpenCV(或者是MATLAB里)生成的txt文件,文件里放的是矩阵,用MATLAB来读:

      直接desc1=load('des_new1.txt');   这样得到的就是一个矩阵

2,MATLAB生成的矩阵写进txt文件里

      fid1=fopen('des_new1.txt','w');
      [m1,n1]=size(des1);
      for i=1:m1
         for j=1:n1
            if(j==n1)
            fprintf(fid1,'%f\n',des1(i,j));   %把des1这个矩阵写进fid1这个txt文件  以浮点型写入
           else
            fprintf(fid1,'%f\t',des1(i,j));
           end
       end
    end
  fclose(fid1);

  如果想把这个txt继续在MATLAB里以矩阵形式读出来 ,那就像1所说的直接load就行了

3,还有一种好奇怪的,其实不单独算一种,但我用过好奇怪,也是用MATLAB把一个矩阵写进txt

fid=fopen('shiyan.txt','w');%写入文件路径 矩阵为a

[m,n]=size(a);

 for i=1:m

    for j=1:n

       if j==n

         fprintf(fid,'%f\r\n',a(i,j));    %用记事本打开txt可看到是矩阵形式!

      else

        fprintf(fid,'%f\r\t',a(i,j));

       end

    end

end

fclose(fid);

但在MATLAB里用C=load(‘shiyan.txt’);无法将它用矩阵形式读出来,能读出来 ,可是不是想要的矩阵形式!但奇怪的是OpenCV可以以矩阵形式将它读出来!

4,在OpenCV里将txt里矩阵数据以矩阵形式读出来

      ifstream filein1("data_new1.txt");
CvPoint2D32f *point1;
point1=(CvPoint2D32f*)malloc(N*sizeof(CvPoint2D32f));    //读出来给point1这个矩阵  得到点的横纵坐标NX2的矩阵  浮点型
for(int i=0;i<N;i++)
filein1>>point1[i].x>>point1[i].y;
filein1.close();

5, 在OpenCV里将矩阵写入一个txt文件

        ofstream outfile11("descridata1.txt",ios::out);
for(int r=0;r<trainDescription.rows;r++)
{
for(int c=0;c<trainDescription.cols;c++)
{
int data =trainDescription.at<float>(r,c);   //最终txt里写入的就是trainDescription的矩阵
outfile11<<data<< "\t" ;
}
outfile11<<endl;
}


1 0