【opencv基于特征描述子的物体检验寻找】

关于特征子中经常用的的函数:findHomography,estimateRigidTransform

关于opencv的特征描述子,我们需要了解DMatch与KPoints的数据结构,如下:

DMatch结构体

/* 
 * Struct for matching: query descriptor index, train descriptor index, train image index and distance between descriptors. 
 */ 
 /* 
  * DMatch主要用来储存匹配信息的结构体,query是要匹配的描述子,train是被匹配的描述子,在Opencv中进行匹配时 
  * void DescriptorMatcher::match( const Mat& queryDescriptors, const Mat& trainDescriptors, vector<DMatch>& matches, const Mat& mask ) const 
  * match函数的参数中位置在前面的为query descriptor,后面的是 train descriptor 
  * 例如:query descriptor的数目为20,train descriptor数目为30,则DescriptorMatcher::match后的vector<DMatch>的size为20 
  * 若反过来,则vector<DMatch>的size为30 
  *  
 */ 
1
struct DMatch  
{         //三个构造函数  
          DMatch():  
queryIdx(-1),trainIdx(-1),imgIdx(-1),distance(std::numeric_limits<float>::max()) {}  
          DMatch(int  _queryIdx, int  _trainIdx, float  _distance ) :  
queryIdx( _queryIdx),trainIdx( _trainIdx), imgIdx(-1),distance( _distance) {}  
          DMatch(int  _queryIdx, int  _trainIdx, int  _imgIdx, float  _distance ) :                   queryIdx(_queryIdx), trainIdx( _trainIdx), imgIdx( _imgIdx),distance( _distance) {}  
          int queryIdx;  //此匹配对应的查询图像的特征描述子索引  
          int trainIdx;   //此匹配对应的训练(模板)图像的特征描述子索引  
          int imgIdx;    //训练图像的索引(若有多个)  
          float distance;  //两个特征向量之间的欧氏距离,越小表明匹配度越高。  
          bool operator < (const DMatch &m) const;  
};

2
struct CV_EXPORTS_W_SIMPLE DMatch  
{  
    //默认构造函数,FLT_MAX是无穷大  
    //#define FLT_MAX         3.402823466e+38F        /* max value */  
    CV_WRAP DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(FLT_MAX) {}  
    //DMatch构造函数  
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, float _distance ) :  
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {}  
    //DMatch构造函数  
    CV_WRAP DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) :  
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {}  
   
    //queryIdx为query描述子的索引,match函数中前面的那个描述子  
    CV_PROP_RW int queryIdx; // query descriptor index  
    //trainIdx为train描述子的索引,match函数中后面的那个描述子  
    CV_PROP_RW int trainIdx; // train descriptor index  
    //imgIdx为进行匹配图像的索引  
    //例如已知一幅图像的sift描述子,与其他十幅图像的描述子进行匹配,找最相似的图像,则imgIdx此时就有用了。  
    CV_PROP_RW int imgIdx;   // train image index  
    //distance为两个描述子之间的距离  
    CV_PROP_RW float distance;  
    //DMatch比较运算符重载,比较的是DMatch中的distance,小于为true,否则为false  
    // less is better  
    bool operator<( const DMatch &m ) const 
    {  
        return distance < m.distance;  
    }  
};  

KeyPoint特征点类

保存特征点各种信息的KeyPoint类在使用中是不透明的,我们来看看KeyPoint类的主要属性:

class KeyPoint
{
Point2f  pt;  //特征点坐标
float  size; //特征点邻域直径
float  angle; //特征点的方向,值为0~360,负值表示不使用
float  response; //特征点的响应强度,代表了该点是特征点的程度,可以用于后续处理中特征点排序
int  octave; //特征点所在的图像金字塔的组
int  class_id; //用于聚类的id
}

主要包含的特征点信息有:位置、邻域直径、特征的方向、响应强度、多尺度信息和分类等。特征点匹配的实现就是通过逐个匹配特征点的这些信息。

drawKeypoints特征点绘制

opencv提供了一个快速绘制特征点的函数drawKeypoints,函数原型:

void drawKeypoints( 
                    const Mat& image, 
                    const vector<KeyPoint>& keypoints,
                    CV_OUT Mat& outImage,
                    const Scalar& color=Scalar::all(-1), 
                    int flags=DrawMatchesFlags::DEFAULT );

第一个参数image:原始图像,可以使三通道或单通道图像;

第二个参数keypoints:特征点向量,向量内每一个元素是一个KeyPoint对象,包含了特征点的各种属性信息;

第三个参数outImage:特征点绘制的画布图像,可以是原图像;

扫描二维码关注公众号,回复: 3625764 查看本文章

第四个参数color:绘制的特征点的颜色信息,默认绘制的是随机彩色;

第五个参数flags:特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制,有以下几种模式可选:

  DEFAULT:只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
  DRAW_OVER_OUTIMG:函数不创建输出的图像,而是直接在输出图像变量空间绘制,要求本身输出图像变量就                                                     是一个初始化好了的,size与type都是已经初始化好的变量
  NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
  DRAW_RICH_KEYPOINTS:绘制特征点的时候绘制的是一个个带有方向的圆,这种方法同时显示图像的坐                                                              标,size,和方向,是最能显示特征的一种绘制方式。

#include<opencv2/opencv.hpp>
#include<opencv2/xfeatures2d/nonfree.hpp>
//在使用SurfFeatureDetector类时候,opencv3.x新版本需要加相关头文件与命名空间
#include <iostream>

using namespace cv;
using namespace std;
using namespace xfeatures2d;


int main(int argc, char *argv[]){

	Ptr<SurfFeatureDetector> detector = SurfFeatureDetector::create(800);
	Mat image01 = imread("1.png");
		Mat image02 = imread("2.png");
	imshow("原始测试图像", image01);
	imshow("基准图像", image02);

	//灰度图转换

	Mat srcImage1, srcImage2;
	cvtColor(image01, srcImage1, CV_RGB2GRAY);
	cvtColor(image02, srcImage2, CV_RGB2GRAY);
	vector<cv::KeyPoint> key_points_1, key_points_2;
	

	Mat dstImage1, dstImage2;
	detector->detectAndCompute(srcImage1, Mat(), key_points_1, dstImage1);
	detector->detectAndCompute(srcImage2, Mat(), key_points_2, dstImage2);//可以分成detect和compute
	

	Mat img_keypoints_1, img_keypoints_2;
	drawKeypoints(srcImage1, key_points_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	drawKeypoints(srcImage2, key_points_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT);

	Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("FlannBased");

	vector<DMatch>mach;
    matcher->match(dstImage1, dstImage2, mach);

	sort(mach.begin(), mach.end()); //特征点排序	

	double Max_dist = 0;
	double Min_dist = 100;

	for (int i = 0; i < dstImage1.rows; i++){
		double dist = mach[i].distance;
		if (dist < Min_dist)Min_dist = dist;
		if (dist > Max_dist)Max_dist = dist;
	}
	cout << "最短距离" << Min_dist << endl;
	cout << "最长距离" << Max_dist << endl;
	vector<DMatch>goodmaches;

	for (int i = 0; i < dstImage1.rows; i++){
		if (mach[i].distance < 2 * Min_dist)
			goodmaches.push_back(mach[i]);
	}

	Mat img_maches;

	drawMatches(srcImage1, key_points_1, srcImage2, key_points_2, goodmaches, img_maches);
	vector<Point2f> imagePoints1, imagePoints2;
	for (int i = 0; i<10; i++){
		imagePoints1.push_back(key_points_1[mach[i].queryIdx].pt);
		imagePoints2.push_back(key_points_2[mach[i].trainIdx].pt);
	}
	
	Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);
	
	////也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差

	//Mat	homo=getPerspectiveTransform(imagePoints1,imagePoints2);   

	cout << "变换矩阵为:\n" << homo << endl << endl; //输出映射矩阵

												//图像配准
	Mat imageTransform1, imageTransform2;
	warpPerspective(image01, imageTransform1, homo, Size(image02.cols, image02.rows));

	imshow("经过透视矩阵变换后", imageTransform1);
	waitKey();
	return 0;

}




#include<opencv2/opencv.hpp>
#include<opencv2/xfeatures2d/nonfree.hpp>
//在使用SurfFeatureDetector类时候,opencv3.x新版本需要加相关头文件与命名空间
#include <iostream>




using namespace cv;
using namespace xfeatures2d;
using namespace std;



int main()

{

	Mat srcImage1 = imread("1.jpg");

	Mat srcImage2 = imread("2.png");

	imshow("【原图1】", srcImage1);

	imshow("【原图2】", srcImage2);



	Mat grayImage1, grayImage2;

	cvtColor(srcImage1, grayImage1, CV_BGR2GRAY);

	cvtColor(srcImage2, grayImage2, CV_BGR2GRAY);



	//首先对两幅图像进行特征点的检测

	//先准备参数

	vector<KeyPoint> g_vKeyPoint1;

	vector<KeyPoint> g_vKeyPoint2;

	//SURF surf(400);

	Ptr<SurfFeatureDetector> detector = SurfFeatureDetector::create(400);

	//利用得到的特征点计算特征描述子

	//目的:对得到的每个特征点进行特征描述,整合到Mat类型的矩阵中(计算结果是Mat类型的)

	//该得到的结果矩阵的行数就是特征点的个数,因为是对每个点进行描述,所以每行都会有一个描述的字子向量,共同构成Mat矩阵
	Mat descriImage1, descriImage2;
	detector->detectAndCompute(srcImage1, Mat(), g_vKeyPoint1, descriImage1);
	detector->detectAndCompute(srcImage2, Mat(), g_vKeyPoint2, descriImage2);

	//surf.cmpute(grayImage1, g_vKeyPoint1, descriImage1);

	//surf.compute(grayImage2, g_vKeyPoint2, descriImage2);



	//正式开始在两幅图像中进行匹配

	//先得到一个匹配向量
	Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("FlannBased");
	//FlannBasedMatcher FLMatcher;

	 vector<DMatch> g_vMatches;

	//g_vMatches就是得到的匹配向量
	 matcher->match(descriImage1, descriImage2, g_vMatches);



	//FLMatcher.match(descriImage1, descriImage2, g_vMatches);



	//用找最大最小值的方式找到 两幅图像中匹配的点的距离的最大值和最小值

	//这里的 keyPoint1.size() 和 descriImage1.rows是一样的值,因为descriImage1的行数就是检测到的特征点的个数

	double minDistance = g_vMatches[0].distance, maxDistance = g_vMatches[0].distance;

	for (size_t i = 0; i < g_vKeyPoint1.size(); i++)

	{

		double currDistance = g_vMatches[i].distance;

		if (currDistance < minDistance)

			minDistance = currDistance;

		if (currDistance > maxDistance)

			maxDistance = currDistance;

	}



	//定义一个新的变量,用来存储 通过距离检测后  通过阀值的点

	vector<DMatch> newMatches;

	for (size_t i = 0; i < g_vKeyPoint1.size(); i++)

	{

		if (g_vMatches[i].distance < 2 * minDistance)

			newMatches.push_back(g_vMatches[i]);

	}



	//用绘制函数对匹配向量进行绘制

	Mat dstImage;

	drawMatches(srcImage1, g_vKeyPoint1, srcImage2, g_vKeyPoint2, newMatches, dstImage

		, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255))

		, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), Mat(), 2);



	imshow("【特征提取后的图像】", dstImage);



	/*****************************************************正式开始寻找已知物体*************************************************/

	//为了调用 得到H矩阵findHomography函数,所以需要得到 匹配点所对应的特征点   然后作为参数传递给计算H矩阵的函数

	//所以首先是进行 匹配点和对应的特征点的转换步骤

	//将得到的点放入新的容器中,所以需要定义新的容器

	vector<Point2f> g_vSrcPoint2f1;

	vector<Point2f> g_vSrcPoint2f2;

	for (size_t i = 0; i < newMatches.size(); i++)

	{

		g_vSrcPoint2f1.push_back(g_vKeyPoint1[newMatches[i].queryIdx].pt);

		g_vSrcPoint2f2.push_back(g_vKeyPoint2[newMatches[i].trainIdx].pt);

	}



	//将得到的对应的特征点  计算H矩阵

	Mat H = findHomography(g_vSrcPoint2f1, g_vSrcPoint2f2, 0);



	////用得到的H矩阵  来进行透视矩阵变换  用到的是perspectiveTransform函数

	//vector<Point2f> g_vCorners1(4);

	//vector<Point2f> g_vCorners2(4);

	//g_vCorners1[0] = Point2f(0, 0);
	//g_vCorners1[1] = Point2f((float)srcImage1.cols, 0);
	//g_vCorners1[2] = Point2f((float)srcImage1.cols, (float)srcImage1.rows);
	//g_vCorners1[3] = Point2f(0, (float)srcImage1.rows);
	//perspectiveTransform(g_vCorners1, g_vCorners2, H);

	////在得到的两幅图像的合成图中绘制检测到的物体的直线

	//line(dstImage, (Point)g_vCorners2[0] + Point(srcImage1.cols, 0), (Point)g_vCorners2[1] + Point(srcImage1.cols, 0)

	//	, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);

	//line(dstImage, (Point)g_vCorners2[1] + Point(srcImage1.cols, 0), (Point)g_vCorners2[2] + Point(srcImage1.cols, 0)

	//	, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);

	//line(dstImage, (Point)g_vCorners2[2] + Point(srcImage1.cols, 0), (Point)g_vCorners2[3] + Point(srcImage1.cols, 0)

	//	, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);

	//line(dstImage, (Point)g_vCorners2[3] + Point(srcImage1.cols, 0), (Point)g_vCorners2[0] + Point(srcImage1.cols, 0)

	//	, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 2);

	//imshow("【检测物体后的图像】", dstImage);

	//进行角点检测

	////开始进行强角点检测

	////先配置需要的函数参数

	vector<Point2f> dstPoint2f1;

	goodFeaturesToTrack(grayImage1, dstPoint2f1, 200, 0.01, 10, Mat(), 3);

	vector<Point2f> dstPoint2f2(dstPoint2f1.size());

	//进行透视变换

	perspectiveTransform(dstPoint2f1, dstPoint2f2, H);



	//在计算得到的点中寻找最小包围矩形

	//rectPoint变量中得到了矩形的四个顶点坐标

	RotatedRect rectPoint = minAreaRect(dstPoint2f2);

	//定义一个存储以上四个点的坐标的变量

	Point2f fourPoint2f[4];

	//将rectPoint变量中存储的坐标值放到 fourPoint的数组中

	rectPoint.points(fourPoint2f);



	//根据得到的四个点的坐标  绘制矩形

	for (int i = 0; i < 3; i++)

	{

		line(srcImage2, fourPoint2f[i], fourPoint2f[i + 1]

			, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 3);

	}

	line(srcImage2, fourPoint2f[0], fourPoint2f[3]

		, Scalar(theRNG().uniform(0, 255), theRNG().uniform(0, 255), theRNG().uniform(0, 255)), 3);

	imshow("【检测到的物体】", srcImage2);
	waitKey(0);

	return 0;

}

https://blog.csdn.net/qq_23880193/article/details/49970685

猜你喜欢

转载自blog.csdn.net/qq_35054151/article/details/82664373