OpenCV中feature2D学习——自定义角点检测函数

概述

除了之前文章所说的利用Harris进行角点检测和利用Shi-Tomasi方法进行角点检测外,也可以自己制作角点检测的函数:使用cornerEigenValsAndVecs()函数和minMaxLoc()函数结合来模拟Harris角点检测,或者使用cornerMinEigenVal()函数和minMaxLoc()函数结合来模拟Shi-Tomasi角点检测,最后特征点选取的判断条件要根据实际情况进行选择。

cornerEigenValsAndVecs()函数

(1)函数原型

cornerEigenValsAndVecs()函数在角点检测中计算扫描图像块的特征向量与特征值,其函数原型如下:

C++: void cornerEigenValsAndVecs(InputArray src, OutputArray dst, int blockSize, int ksize, int borderType=BORDER_DEFAULT );
C: void cvCornerEigenValsAndVecs(const CvArr* image, CvArr* eigenvv, int block_size, int aperture_size=3 );

(2)函数参数

函数参数说明如下:

src:输入单通道8-bit或者浮点类型图像。

dst:用来存储结果的图像,大小与输入图像一致并且为CV_32FC(6)类型。

blockSize:邻域大小。

ksize:Sobel算子当中的核大小,只能取1、3、5、7(具体可参考这里:Sobel())。

borderType:像素扩展的方法,因为在滤波处理的过程中会扩展图像边缘,每扩张一个边界像素,都需要计算出该像素点在原图中的位置,这个功能被提炼出来就变成了borderInterpolate()函数。该函数输入一个点坐标,返回他在原图中的坐标,关于这个函数的详细解释,参考:

1、在OpenCV中圖像邊界擴展 copyMakeBorder 的實現_人人IT網

2、borderInterpolate解释_Halley_新浪博客

(3)函数功能

对于每个像素点p,该函数考虑一个blockSize*blockSize的邻域S(p),它在邻域上计算导数的协方差矩阵:



其中导数是使用Sobel()算子计算得到的。

随后,函数计算矩阵M的特征值和特征向量,并将它们以(λ1, λ2, x1, y1, x2,y2)的形式存储在目标图像dst中。其中λ1, λ2是M未经过排序的特征值;x1, y1是对应于λ1的特征向量;x2, y2是对应于λ2的特征向量。该函数的输出能够用于鲁棒的边缘或角点检测。

cornerMinEigenVal()函数

(1)函数原型

cornerMinEigenVal()函数在角点检测中计算梯度矩阵的最小特征值,其函数原型如下:

C++: void cornerMinEigenVal(InputArray src, OutputArray dst, int blockSize, int ksize=3, int borderType=BORDER_DEFAULT );
C: void cvCornerMinEigenVal(const CvArr* image, CvArr* eigenval, int block_size, int aperture_size=3 );

(2)函数参数

函数参数说明中除了dst必须为CV_32FC1类型以外,其它与cornerEigenValsAndVecs()函数的一致。

(3)函数功能

功能与cornerEigenValsAndVecs()函数相似,但是它只计算导数协方差矩阵的最小特征值,按照cornerEigenValsAndVecs()函数给定的特征值λ1, λ2来说就是min(λ1, λ2)。

代码示例

(1)采用cornerEigenValsAndVecs()函数和minMaxLoc()函数结合来模拟Harris角点检测的代码示例如下:

/**
 * @使用cornerEigenValsAndVecs()函数模拟Harris角点检测
 * @author holybin
 */

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;

/// 全局变量
Mat src;
Mat srcGray, srcCopy;	//srcCopy用于绘图,srcGray用于检测角点
Mat dstHarris;	//dstHarris用于存储角点检测的结果
Mat resHarris;	//resHarris用于存储特征点选择后的结果
/// 各类阈值
int HarrisQualityLevel = 50;
int maxQualityLevel = 100;
double HarrisMinVal = 0.0;
double HarrisMaxVal = 0.0;
char* HarrisWindow = "My Harris corner detector";

/// 角点检测函数声明
void HarrisFunction( int, void* );

int main( int argc, char** argv )
{
	/// 载入图像并灰度化
	src = imread("D:\\opencv_pic\\house_small.jpg", 1 );
	cvtColor( src, srcGray, CV_BGR2GRAY );

	/// 设置参数
	int blockSize = 3;
	int apertureSize = 3;

	/// 使用cornerEigenValsAndVecs()函数检测角点
	dstHarris = Mat::zeros( srcGray.size(), CV_32FC(6) );
	resHarris = Mat::zeros( srcGray.size(), CV_32FC1 );
	cornerEigenValsAndVecs( srcGray, dstHarris, blockSize, apertureSize, BORDER_DEFAULT );

	/// 特征点选择
	for( int j = 0; j < srcGray.rows; j++ )
	{
		for( int i = 0; i < srcGray.cols; i++ )
		{
			// 两个特征值
			float* lambda = dstHarris.ptr<float>( j, i);
			float lambda1 = lambda[0];
			float lambda2 = lambda[1];
			// 会报错!!!//
			//float lambda1 = dstHarris.at<float>( j, i, 0);
			//float lambda2 = dstHarris.at<float>( j, i, 1);
			resHarris.at<float>(j,i) = lambda1*lambda2 - 0.04*pow( ( lambda1 + lambda2 ), 2 );
		}
  }
  minMaxLoc( resHarris, &HarrisMinVal, &HarrisMaxVal, 0, 0, Mat() );
  
  /// 创建窗口和滑动条
  namedWindow( HarrisWindow, CV_WINDOW_AUTOSIZE );
  createTrackbar( "Quality:", HarrisWindow, &HarrisQualityLevel, maxQualityLevel, HarrisFunction );  
  HarrisFunction( 0, 0 );
  
  waitKey(0);
  return(0);
}

/// 角点检测函数实现
void HarrisFunction( int, void* )
{
	/// 深度拷贝原图像用于绘制角点
	srcCopy = src.clone();

	if( HarrisQualityLevel < 1 )
		HarrisQualityLevel = 1;

	/// 角点满足条件则绘制
	for( int j = 0; j < srcGray.rows; j++ )
	{ 
		for( int i = 0; i < srcGray.cols; i++ )
		{
			if( resHarris.at<float>(j,i) > HarrisMinVal + ( HarrisMaxVal - HarrisMinVal )*HarrisQualityLevel/maxQualityLevel )
				circle( srcCopy, Point(i,j), 2, Scalar( 0,0,255 ), -1, 8, 0 );
		}
	}
	imshow( HarrisWindow, srcCopy );
	cout<<"Harris Quality Level: "<<HarrisQualityLevel<<endl;
}

实验结果:


OpenCV中feature2D学习——自定义角点检测函数_第1张图片


这里需要注意这里floatlambda1 = dstHarris.at<float>( j, i, 0);使用at方式访问Mat的数据会报错:


所以我改成了采用指针的形式float* lambda = dstHarris.ptr<float>( j, i);。结果显示当Quality Level增大时,满足条件被保留的角点数目越来越少。


(2)采用cornerMinEigenVal()函数和minMaxLoc()函数结合来模拟Shi-Tomasi角点检测的代码示例如下:

/**
* @使用cornerMinEigenVal()函数模拟Shi-Tomasi角点检测
* @author holybin
*/

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;

/// 全局变量
Mat src;
Mat srcGray, srcCopy;	//srcCopy用于绘图,srcGray用于检测角点
Mat dstShiTomasi;	//dstShiTomasi用于存储角点检测的结果
/// 各类阈值
int ShiTomasiQualityLevel = 50;
int maxQualityLevel = 100;
double ShiTomasiMinVal;
double ShiTomasiMaxVal;
char* ShiTomasiWindow = "My Shi-Tomasi corner detector";

/// 角点检测函数声明
void ShiTomasiFunction( int, void* );

int main( int argc, char** argv )
{
	/// 载入图像并灰度化
	src = imread("D:\\opencv_pic\\house_small.jpg", 1 );
	cvtColor( src, srcGray, CV_BGR2GRAY );

	/// 设置参数
	int blockSize = 3;
	int apertureSize = 3;

	/// 使用cornerMinEigenVal()函数检测角点
	dstShiTomasi = Mat::zeros( srcGray.size(), CV_32FC1 );  
	cornerMinEigenVal( srcGray, dstShiTomasi, blockSize, apertureSize, BORDER_DEFAULT );
	minMaxLoc( dstShiTomasi, &ShiTomasiMinVal, &ShiTomasiMaxVal, 0, 0, Mat() );

	/// 创建窗口和滑动条
	namedWindow( ShiTomasiWindow, CV_WINDOW_AUTOSIZE );   
	createTrackbar( " Quality:", ShiTomasiWindow, &ShiTomasiQualityLevel, maxQualityLevel, ShiTomasiFunction );  
	ShiTomasiFunction( 0, 0 );

	waitKey(0);
	return(0);
}

/// 角点检测函数实现
void ShiTomasiFunction( int, void* )
{
	/// 深度拷贝原图像用于绘制角点
	srcCopy = src.clone();

	if( ShiTomasiQualityLevel < 1 )
		ShiTomasiQualityLevel = 1;

	/// 角点满足条件则绘制
	for( int j = 0; j < srcGray.rows; j++ )
	{ 
		for( int i = 0; i < srcGray.cols; i++ )
		{
			if( dstShiTomasi.at<float>(j,i) > ShiTomasiMinVal + ( ShiTomasiMaxVal - ShiTomasiMinVal )*ShiTomasiQualityLevel/maxQualityLevel )
				circle( srcCopy, Point(i,j), 2, Scalar( 255,0,0 ), -1, 8, 0 );
		}
	}
	imshow( ShiTomasiWindow, srcCopy );
	cout<<"Shi-Tomasi Quality Level: "<<ShiTomasiQualityLevel<<endl;
}

实验结果:



结果显示当QualityLevel增大时,满足条件被保留的角点数目越来越少。

你可能感兴趣的:(自定义,角点检测)