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

来源:互联网 发布:数据库开发书籍 编辑:程序博客网 时间:2024/06/10 03:01

概述

除了之前文章所说的利用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;}

实验结果:




这里需要注意这里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增大时,满足条件被保留的角点数目越来越少。

1 0
原创粉丝点击