学习opencv 角点检测
如果一个点在两个正交方向上都有明显的导数,则我们认为此点更倾向于是独一无二的,所以许多可跟踪的特征点都是角点。
一下为角点检测中用到的一些函数
cvGoodFeaturesToTrack先计算二阶导数,再计算特征值,它返回满足易于跟踪的定义的一系列点。
void cvGoodFeaturesToTrack(
const CvArr* image,
CvArr* eig_image,
CvArr* temp_image,
CvPoint2D32f* corners,
int* corner_count,
double quality_level,
double min_distance,
const CvArr* mask=NULL
int block_size =3,
int use_harris =0,
double k = 0.4
);
image
输入图像必须为8位或32位单通道图像
eig_image
当做临时变量使用,计算后eig_image 中的内容是有效的,其中每个元素包含了输入图像中对应点的最小特征值
temp_image
另外一个临时图像,格式与尺寸与 eig_image 一致
corners
是函数的输出,为检测到的32位的角点数组,在调用函数之前需要为它分配空间
corner_count
函数调用完成后,该值表示实际检测到的角点数目
quality_level
表示一点被认为是角点的可接受的最小特征值。
min_distance
检测完角点之后还要剔除一些距离比较近的角点,min_distance保证返回的角点之间的距离不小于min_distance.
mask
可选参数,一幅像素值为bool类型的图像,用于指定输入图像中参与角点计算的像素点。如果mask为NULL,则选择整个图像。
block_size
计算导数的自相关矩阵时指定的邻域。
use_harris
如果use_harris为0,则使用Shi-Tomasi定义,非零则使用Harris的角点定义
void cvCvtColor( const CvArr* src, CvArr* dst, int code );
该函数是opencv中的颜色空间转换函数
src
输入的 8-bit,16-bit或 32-bit单倍精度浮点数图像。
dst
输出的8-bit, 16-bit或 32-bit单倍精度浮点数图像。
code
色彩空间转换的模式,该code来实现不同类型的颜色空间转换。
void cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int lineType=8, int shift=0) img – 要画圆的图像
center – 圆心坐标
radius – 半径
color – 颜色
thickness – 线宽
lineType – Type of the circle boundary, see Line description shift –圆心坐标和和半径的小数点的数目
#include "StdAfx.h"
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include
#include
using namespace std;
using namespace cv;
const int max_corners=100;
const char* filename = "JINGLONGLONG.jpg";
int main()
{
int cornerCount =max_corners;//角点的最大数目
//用于保存角点的坐标
CvPoint2D32f corners[max_corners];
IplImage* sourceImage=0,*grayImage=0,*corners1=0,*corners2=0;
cvNamedWindow("IMAGE",CV_WINDOW_AUTOSIZE);
//读入图像,检测
sourceImage=cvLoadImage(filename,1);
grayImage=cvCreateImage(cvGetSize(sourceImage),IPL_DEPTH_8U,1);
cvCvtColor(sourceImage,grayImage,CV_RGB2GRAY);
corners1=cvCreateImage(cvGetSize(sourceImage),IPL_DEPTH_32F,1);
corners2=cvCreateImage(cvGetSize(sourceImage),IPL_DEPTH_32F,1);
cvGoodFeaturesToTrack(
grayImage,
corners1,
corners2,
corners,
&cornerCount,
0.05,
5,
0,
3,
0,
0.4
);
if(cornerCount)
{
int i=0;
CvScalar color=CV_RGB(0,0,255);
for(i;i
运行结果:
不知道是不是格式的问题,,,,这个程序还有点问题,,,待考试完了再处理一下..
Reference《学习opencv》
========
OpenCV入门教程之 OpenCV角点检测之Harris角点检测
http://blog.csdn.net/poem_qianmo/article/details/29356187
写作当前博文时配套使用的OpenCV版本: 2.4.9
本篇文章中,我们一起探讨了OpenCV中Harris角点检测相关的知识点,学习了OpenCV中实现Harris角点检测的cornerHarris函数的使用方法。此博文一共有两个配套的麻雀虽小但五脏俱全的示例程序,其经过浅墨详细注释过的代码都在文中贴出,且文章最后提供了综合示例程序的下载。
依然是先看看程序运行截图:
一、引言:关于兴趣点(interest points)
在图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints)、特征点(feature points) 被大量用于解决物体识别,图像识别、图像匹配、视觉跟踪、三维重建等一系列的问题。我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析。如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值。
图像特征类型可以被分为如下三种:
<1>边缘<2>角点 (感兴趣关键点)<3>斑点(Blobs)(感兴趣区域)
其中,角点是个很特殊的存在。他们在图像中可以轻易地定位,同时,他们在人造物体场景,比如门、窗、桌等出随处可见。因为角点位于两条边缘的交点处,代表了两个边缘变化的方向上的点,,所以他们是可以精确定位的二维特征,甚至可以达到亚像素的精度。且其图像梯度有很高的变化,这种变化是可以用来帮助检测角点的。需要注意的是,角点与位于相同强度区域上的点不同,与物体轮廓上的点也不同,因为轮廓点难以在相同的其他物体上精确定位。
二、角点检测算法的分类
在当前的图像处理领域,角点检测算法可归纳为三类:
<1>基于灰度图像的角点检测<2>基于二值图像的角点检测<3>基于轮廓曲线的角点检测
而基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合三类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
三、角点的定义
“如果某一点在任意方向的一个微小变动都会引起灰度很大的变化,那么我们就把它称之为角点”
角点检测(Corner Detection)是计算机视觉系统中用来获得图像特征的一种方法,广泛应用于运动检测、图像匹配、视频跟踪、三维建模和目标识别等领域中。也称为特征点检测。
角点通常被定义为两条边的交点,更严格的说,角点的局部邻域应该具有两个不同区域的不同方向的边界。而实际应用中,大多数所谓的角点检测方法检测的是拥有特定特征的图像点,而不仅仅是“角点”。这些特征点在图像中有具体的坐标,并具有某些数学特征,如局部最大或最小灰度、某些梯度特征等。
现有的角点检测算法并不是都十分的健壮。很多方法都要求有大量的训练集和冗余数据来防止或减少错误特征的出现。另外,角点检测方法的一个很重要的评价标准是其对多幅图像中相同或相似特征的检测能力,并且能够应对光照变化、图像旋转等图像变化。
在我们解决问题时,往往希望找到特征点,“特征”顾名思义,指能描述物体本质的东西,还有一种解释就是这个特征微小的变化都会对物体的某一属性产生重大的影响。而角点就是这样的特征。
观察日常生活中的“角落”就会发现,“角落”可以视为所有平面的交汇处,或者说是所有表面的发起处。假设我们要改变一个墙角的位置,那么由它而出发的平面势必都要有很大的变化。所以,这就引出了图像角点的定义。
我们知道,特征检测与匹配是计算机视觉应用中非常重要的一部分,这需要寻找图像之间的特征建立对应关系。图像中的点作为图像的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
另外,关于角点的具体描述可以有几种:
一阶导数(即灰度的梯度)的局部最大所对应的像素点;两条及两条以上边缘的交点;图像中梯度值和梯度方向的变化速率都很高的点;角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
四、cornerHarris函数详解
cornerHarris 函数用于在OpenCV中运行Harris角点检测算子处理图像。和cornerMinEigenVal( )以及cornerEigenValsAndVecs( )函数类似,cornerHarris 函数对于每一个像素(x,y)在邻域内,计算2x2梯度的协方差矩阵,接着它计算如下式子:
即可以找出输出图中的局部最大值,即找出了角点。
其函数原型和参数解析:
C++: void cornerHarris(InputArray src,OutputArray dst, int blockSize, int ksize, double k, intborderType=BORDER_DEFAULT )
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放Harris角点检测的输出结果,和源图片有一样的尺寸和类型。第三个参数,int类型的blockSize,表示邻域的大小,更多的详细信息在cornerEigenValsAndVecs()中有讲到。第四个参数,int类型的ksize,表示Sobel()算子的孔径大小。第五个参数,double类型的k,Harris参数。第六个参数,int类型的borderType,图像像素的边界模式,注意它有默认值BORDER_DEFAULT。更详细的解释,参考borderInterpolate( )函数。
接着我们一起过一遍稍后需要用到的Threshold函数的解析,然后看一个以cornerHarris为核心的示例程序。
五、Threshold函数详解
函数Threshold( ) 对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像。(另外,compare( )函数也可以达到此目的) 或者是去掉噪声,例如过滤很小或很大象素值的图像点。
C++: double threshold(InputArray src,OutputArray dst, double thresh, double maxval, int type)
第一个参数,InputArray类型的src,输入数组,填单通道 , 8或32位浮点类型的Mat即可。第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放输出结果,且和第一个参数中的Mat变量有一样的尺寸和类型。第三个参数,double类型的thresh,阈值的具体值。第四个参数,double类型的maxval,当第五个参数阈值类型type取 CV_THRESH_BINARY 或CV_THRESH_BINARY_INV 阈值类型时的最大值.第五个参数,int类型的type,阈值类型,。threshold( )函数支持的对图像取阈值的方法由其确定,具体用法如下图:
而图形化的阈值描述如下图:
讲解完这两个函数,让我们看一个调用示例程序:
#include
#include
using namespace cv;
int main()
{
//以灰度模式载入图像并显示
Mat srcImage = imread("1.jpg", 0);
imshow("原始图", srcImage);
//进行Harris角点检测找出角点
Mat cornerStrength;
cornerHarris(srcImage, cornerStrength, 2, 3, 0.01);
//对灰度图进行阈值操作,得到二值图并显示
Mat harrisCorner;
threshold(cornerStrength, harrisCorner, 0.00001, 255, THRESH_BINARY);
imshow("角点检测后的二值效果图", harrisCorner);
waitKey(0);
return 0;
}
运行截图:
六、本文相关核心函数在OpenCV中的实现源代码
这个部分贴出OpenCV中本文相关函数的源码实现细节,来给想了解实现细节的小伙伴们参考。
6.1 OpenCV2.X中cornerHarris函数源代码
源码路径: …opencvsourcesmodulesimgprocsrccorner.cpp
void cv::cornerHarris( InputArray _src,OutputArray _dst, int blockSize, int ksize, double k, int borderType )
{
Mat src = _src.getMat();
_dst.create( src.size(), CV_32F );
Mat dst = _dst.getMat();
cornerEigenValsVecs( src, dst, blockSize, ksize, HARRIS, k, borderType);
}
可见cornerHarris内部其实是调用了cornerEigenValsVecs函数,我们看看其实现源码:
static void
cornerEigenValsVecs( const Mat& src,Mat& eigenv, int block_size,
int aperture_size, intop_type, double k=0.,
intborderType=BORDER_DEFAULT )
{
#ifdef HAVE_TEGRA_OPTIMIZATION
if (tegra::cornerEigenValsVecs(src, eigenv, block_size, aperture_size,op_type, k, borderType))
return;
#endif
int depth = src.depth();
double scale = (double)(1 << ((aperture_size > 0 ?aperture_size : 3) - 1)) * block_size;
if( aperture_size < 0 )
scale *= 2.;
if( depth == CV_8U )
scale *= 255.;
scale = 1./scale;
CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );
Mat Dx, Dy;
if( aperture_size > 0 )
{
Sobel( src, Dx, CV_32F, 1, 0, aperture_size, scale, 0, borderType );
Sobel( src, Dy, CV_32F, 0, 1, aperture_size, scale, 0, borderType );
}
else
{
Scharr( src, Dx, CV_32F, 1, 0, scale, 0, borderType );
Scharr( src, Dy, CV_32F, 0, 1, scale, 0, borderType );
}
Size size = src.size();
Mat cov( size, CV_32FC3 );
int i, j;
for( i = 0; i < size.height; i++ )
{
float* cov_data = (float*)(cov.data + i*cov.step);
const float* dxdata = (const float*)(Dx.data + i*Dx.step);
const float* dydata = (const float*)(Dy.data + i*Dy.step);
for( j = 0; j < size.width; j++ )
{
float dx = dxdata[j];
float dy = dydata[j];
cov_data[j*3] = dx*dx;
cov_data[j*3+1] = dx*dy;
cov_data[j*3+2] = dy*dy;
}
}
boxFilter(cov, cov, cov.depth(), Size(block_size, block_size),
Point(-1,-1), false, borderType );
if( op_type == MINEIGENVAL )
calcMinEigenVal( cov, eigenv );
else if( op_type == HARRIS )
calcHarris( cov, eigenv, k );
else if( op_type == EIGENVALSVECS )
calcEigenValsVecs( cov, eigenv );
}
}
6.1 OpenCV2.X中Threshold函数源代码
路径:…opencvsourcesmodulesimgprocsrc hresh.cpp
double cv::threshold( InputArray _src,OutputArray _dst, double thresh, double maxval, int type )
02.
{
03.
Mat src = _src.getMat();
04.
bool use_otsu = (type & THRESH_OTSU) != 0;
05.
type &= THRESH_MASK;
06.
07.
if( use_otsu )
08.
{
09.
CV_Assert( src.type() == CV_8UC1 );
10.
thresh = getThreshVal_Otsu_8u(src);
11.
}
12.
13.
_dst.create( src.size(), src.type() );
14.
Mat dst = _dst.getMat();
15.
16.
if( src.depth() == CV_8U )
17.
{
18.
int ithresh = cvFloor(thresh);
19.
thresh = ithresh;
20.
int imaxval = cvRound(maxval);
21.
if( type == THRESH_TRUNC )
22.
imaxval = ithresh;
23.
imaxval = saturate_cast(imaxval);
24.
25.
if( ithresh < 0 || ithresh >= 255 )
26.
{
27.
if( type == THRESH_BINARY || type == THRESH_BINARY_INV ||
28.
((type == THRESH_TRUNC || type== THRESH_TOZERO_INV) && ithresh < 0) ||
29.
(type == THRESH_TOZERO&& ithresh >= 255) )
30.
{
31.
int v = type ==THRESH_BINARY ? (ithresh >= 255 ? 0 : imaxval) :
32.
type ==THRESH_BINARY_INV ? (ithresh >= 255 ? imaxval : 0) :
33.
/*type == THRESH_TRUNC? imaxval :*/ 0;
34.
dst.setTo(v);
35.
}
36.
else
37.
src.copyTo(dst);
38.
return thresh;
39.
}
40.
thresh = ithresh;
41.
maxval = imaxval;
42.
}
43.
else if( src.depth() == CV_16S )
44.
{
45.
int ithresh = cvFloor(thresh);
46.
thresh = ithresh;
47.
int imaxval = cvRound(maxval);
48.
if( type == THRESH_TRUNC )
49.
imaxval = ithresh;
50.
imaxval = saturate_cast(imaxval);
51.
52.
if( ithresh < SHRT_MIN || ithresh >= SHRT_MAX )
53.
{
54.
if( type == THRESH_BINARY || type == THRESH_BINARY_INV ||
55.
((type == THRESH_TRUNC || type== THRESH_TOZERO_INV) && ithresh < SHRT_MIN) ||
56.
(type == THRESH_TOZERO&& ithresh >= SHRT_MAX) )
57.
{
58.
int v = type == THRESH_BINARY ?(ithresh >= SHRT_MAX ? 0 : imaxval) :
59.
type == THRESH_BINARY_INV ?(ithresh >= SHRT_MAX ? imaxval : 0) :
60.
/*type == THRESH_TRUNC ?imaxval :*/ 0;
61.
dst.setTo(v);
62.
}
63.
else
64.
src.copyTo(dst);
65.
return thresh;
66.
}
67.
thresh = ithresh;
68.
maxval = imaxval;
69.
}
70.
else if( src.depth() == CV_32F )
71.
;
72.
else
73.
CV_Error( CV_StsUnsupportedFormat, "" );
74.
75.
parallel_for_(Range(0, dst.rows),
76.
ThresholdRunner(src, dst,thresh, maxval, type),
77.
dst.total()/(double)(1<<16));
78.
return thresh;
79.
}
另外在贴上与之相关的自适应阈值操作函数的源码adaptiveThreshold:
void cv::adaptiveThreshold( InputArray_src, OutputArray _dst, double maxValue,
02.
int method, inttype, int blockSize, double delta )
03.
{
04.
Mat src = _src.getMat();
05.
CV_Assert( src.type() == CV_8UC1 );
06.
CV_Assert( blockSize % 2 == 1 && blockSize > 1 );
07.
Size size = src.size();
08.
09.
_dst.create( size, src.type() );
10.
Mat dst = _dst.getMat();
11.
12.
if( maxValue < 0 )
13.
{
14.
dst = Scalar(0);
15.
return;
16.
}
17.
18.
Mat mean;
19.
20.
if( src.data != dst.data )
21.
mean = dst;
22.
23.
if( method == ADAPTIVE_THRESH_MEAN_C )
24.
boxFilter( src, mean, src.type(), Size(blockSize, blockSize),
25.
Point(-1,-1), true,BORDER_REPLICATE );
26.
else if( method == ADAPTIVE_THRESH_GAUSSIAN_C )
27.
GaussianBlur( src, mean, Size(blockSize, blockSize), 0, 0,BORDER_REPLICATE );
28.
else
29.
CV_Error( CV_StsBadFlag, "Unknown/unsupported adaptive thresholdmethod" );
30.
31.
int i, j;
32.
uchar imaxval = saturate_cast(maxValue);
33.
int idelta = type == THRESH_BINARY ? cvCeil(delta) : cvFloor(delta);
34.
uchar tab[768];
35.
36.
if( type == CV_THRESH_BINARY )
37.
for( i = 0; i < 768; i++ )
38.
tab[i] = (uchar)(i - 255 > -idelta ? imaxval : 0);
39.
else if( type == CV_THRESH_BINARY_INV )
40.
for( i = 0; i < 768; i++ )
41.
tab[i] = (uchar)(i - 255 <= -idelta ? imaxval : 0);
42.
else
43.
CV_Error( CV_StsBadFlag, "Unknown/unsupported threshold type");
44.
45.
if( src.isContinuous() && mean.isContinuous() &&dst.isContinuous() )
46.
{
47.
size.width *= size.height;
48.
size.height = 1;
49.
}
50.
51.
for( i = 0; i < size.height; i++ )
52.
{
53.
const uchar* sdata = src.data + src.step*i;
54.
const uchar* mdata = mean.data + mean.step*i;
55.
uchar* ddata = dst.data + dst.step*i;
56.
57.
for( j = 0; j < size.width; j++ )
58.
ddata[j] = tab[sdata[j] - mdata[j] + 255];
59.
}
60.
}
七、综合示例部分
本次综合示例为调节滚动条来控制阈值,以控制的harris检测角点的数量。一共有三个图片窗口,分别为显示原始图的窗口,包含滚动条的彩色效果图窗口,以及灰度图效果图窗口。
废话不多说,让我们一起来欣赏详细注释过后的完整源代码:
view sourceprint?
001.
//-----------------------------------【程序说明】----------------------------------------------
002.
// 程序名称::《【OpenCV入门教程之十六】OpenCV角点检测之Harris角点检测》 博文配套源码
003.
// 开发所用IDE版本:Visual Studio 2010
004.
// 开发所用OpenCV版本: 2.4.9
009.
//----------------------------------------------------------------------------------------------
010.
#include
015.
#include "opencv2/highgui/highgui.hpp"
016.
#include "opencv2/imgproc/imgproc.hpp"
017.
using namespace cv;
022.
using namespace std;
023.
// 描述:定义一些辅助宏
026.
//------------------------------------------------------------------------------------------------
027.
#define WINDOW_NAME1 "【程序窗口1】" //为窗口标题定义的宏
028.
#define WINDOW_NAME2 "【程序窗口2】" //为窗口标题定义的宏
029.
031.
// 描述:全局变量声明
032.
//-----------------------------------------------------------------------------------------------
033.
Mat g_srcImage, g_srcImage1,g_grayImage;
034.
int thresh = 30; //当前阈值
035.
int max_thresh = 175; //最大阈值
036.
037.
// 描述:全局函数声明
040.
//-----------------------------------------------------------------------------------------------
041.
void on_CornerHarris( int, void* );//回调函数
042.
static void ShowHelpText();
043.
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
046.
//-----------------------------------------------------------------------------------------------
047.
int main( int argc, char** argv )
048.
{
049.
//【0】改变console字体颜色
050.
system("color 3F");
051.
052.
//【0】显示帮助文字
053.
ShowHelpText();
054.
055.
//【1】载入原始图并进行克隆保存
056.
g_srcImage = imread( "1.jpg", 1 );
057.
if(!g_srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定的图片存在~!
058.
"); return false; }
059.
imshow("原始图",g_srcImage);
060.
g_srcImage1=g_srcImage.clone( );
061.
062.
//【2】存留一张灰度图
063.
cvtColor( g_srcImage1, g_grayImage, CV_BGR2GRAY );
064.
065.
//【3】创建窗口和滚动条
066.
namedWindow( WINDOW_NAME1, CV_WINDOW_AUTOSIZE );
067.
createTrackbar( "阈值: ", WINDOW_NAME1, &thresh, max_thresh, on_CornerHarris );
068.
069.
//【4】调用一次回调函数,进行初始化
070.
on_CornerHarris( 0, 0 );
071.
072.
waitKey(0);
073.
return(0);
074.
}
075.
076.
//-----------------------------------【on_HoughLines( )函数】--------------------------------
077.
// 描述:回调函数
078.
//----------------------------------------------------------------------------------------------
079.
080.
void on_CornerHarris( int, void* )
081.
{
082.
//---------------------------【1】定义一些局部变量-----------------------------
083.
Mat dstImage;//目标图
084.
Mat normImage;//归一化后的图
085.
Mat scaledImage;//线性变换后的八位无符号整型的图
086.
087.
//---------------------------【2】初始化---------------------------------------
088.
//置零当前需要显示的两幅图,即清除上一次调用此函数时他们的值
089.
dstImage = Mat::zeros( g_srcImage.size(), CV_32FC1 );
090.
g_srcImage1=g_srcImage.clone( );
091.
092.
//---------------------------【3】正式检测-------------------------------------
093.
//进行角点检测
094.
cornerHarris( g_grayImage, dstImage, 2, 3, 0.04, BORDER_DEFAULT );
095.
096.
// 归一化与转换
097.
normalize( dstImage, normImage, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
098.
convertScaleAbs( normImage, scaledImage );//将归一化后的图线性变换成8位无符号整型
099.
100.
//---------------------------【4】进行绘制-------------------------------------
101.
// 将检测到的,且符合阈值条件的角点绘制出来
102.
for( int j = 0; j < normImage.rows ; j++ )
103.
{ for( int i = 0; i < normImage.cols; i++ )
104.
{
105.
if( (int) normImage.at(j,i) > thresh+80 )
106.
{
107.
circle( g_srcImage1, Point( i, j ), 5, Scalar(10,10,255), 2, 8, 0 );
108.
circle( scaledImage, Point( i, j ), 5, Scalar(0,10,255), 2, 8, 0 );
109.
}
110.
}
111.
}
112.
//---------------------------【4】显示最终效果---------------------------------
113.
imshow( WINDOW_NAME1, g_srcImage1 );
114.
imshow( WINDOW_NAME2, scaledImage );
115.
116.
}
117.
118.
//-----------------------------------【ShowHelpText( )函数】----------------------------------
119.
// 描述:输出一些帮助信息
120.
//----------------------------------------------------------------------------------------------
121.
static void ShowHelpText()
122.
{
123.
//输出一些帮助信息
124.
printf("
125.
126.
127.
【欢迎来到Harris角点检测示例程序~】
128.
129.
");
130.
printf("
131.
132.
133.
请调整滚动条观察图像效果~
134.
135.
");
136.
printf("
137.
138.
by浅墨"
139.
);
140.
}
放出一些运行效果图。
首先是原始图,非常美丽的异域建筑群:
第一组阈值效果图:
第二组阈值效果图:
第三组阈值效果图:
第四组阈值效果图:
========
OpenCV角点检测:Harris角点及Shi-Tomasi角点检测
目录(?)[-]
角点
Harris角点检测
相关代码
Shi-Tomasi 算法
相关代码
实践
转载请注明出处httpblogcsdnnetxiaowei_cquarticledetails7805206
源码及资料下载 httpdownloadcsdnnetdetailxiaowei_cqu4466627
参考资料
角点
特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系。点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
关于角点的具体描述可以有几种:
一阶导数(即灰度的梯度)的局部最大所对应的像素点;
两条及两条以上边缘的交点;
图像中梯度值和梯度方向的变化速率都很高的点;
角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
Harris角点检测
当一个窗口在图像上移动,在平滑区域如图(a),窗口在各个方向上没有变化。在边缘上如图(b),窗口在边缘的方向上没有变化。在角点处如图(c),窗口在各个方向上具有变化。Harris角点检测正是利用了这个直观的物理现象,通过窗口在各个方向上的变化程度,决定是否为角点。
将图像窗口平移[u,v]产生灰度变化E(u,v)
由:, 得到:
对于局部微小的移动量 [u,v],近似表达为:
其中M是 2*2 矩阵,可由图像的导数求得:
E(u,v)的椭圆形式如下图:
定义角点响应函数 R 为:
Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold,即提取R的局部极大值。
OpenCV中定义了 cornerHarris 函数:
[cpp] view plain copy
void cornerHarris( InputArray src, OutputArray dst, int blockSize,
int ksize, double k,
int borderType=BORDER_DEFAULT );
可以结合 convertScaleAbs 函数,通过阈值取角点:
[cpp] view plain copy
void cornerHarris_demo( int, void* )
{
Mat dst, dst_norm;
dst = Mat::zeros( src.size(), CV_32FC1 );
/// Detector parameters
int blockSize = 2;
int apertureSize = 3;
double k = 0.04;
/// Detecting corners
cornerHarris( src_gray, dst, blockSize, apertureSize, k, BORDER_DEFAULT );
/// Normalizing
normalize( dst, dst_norm, 0, 255, NORM_MINMAX, CV_32FC1, Mat() );
convertScaleAbs( dst_norm, dst_norm_scaled );
/// Drawing a circle around corners
for( int j = 0; j < dst_norm.rows ; j++ )
{ for( int i = 0; i < dst_norm.cols; i++ )
{
if( (int) dst_norm.at(j,i) > thresh )
{
circle( dst_norm_scaled, Point( i, j ), 5, Scalar(0), 2, 8, 0 );
circle(src,Point( i, j ), 5, Scalar(255,0,0), -1, 8, 0 );
}
}
}
/// Showing the result
imshow( corners_window, dst_norm_scaled );
imshow( source_window, src );
}
Shi-Tomasi 算法
Shi-Tomasi 算法是Harris 算法的改进。Harris 算法最原始的定义是将矩阵 M 的行列式值与 M 的迹相减,再将差值同预先给定的阈值进行比较。后来Shi 和Tomasi 提出改进的方法,若两个特征值中较小的一个大于最小阈值,则会得到强角点。
如上面第二幅图中,对自相关矩阵 M 进行特征值分析,产生两个特征值和两个特征方向向量。因为较大的不确定度取决于较小的特征值,也就是,所以通过寻找最小特征值的最大值来寻找好的特征点也就解释的通了。
Shi 和Tomasi 的方法比较充分,并且在很多情况下可以得到比使用Harris 算法更好的结果。
由于这种Shi-Tomasi算子与1994年在文章 Good Features to Track [1]中提出,OpenCV 实现的算法的函数名定义为 goodFeaturesToTrack:
[cpp] view plain copy
void goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask=noArray(), int blockSize=3,
bool useHarrisDetector=false, double k=0.04 );
自定义使用函数(以方便createTrackbar的响应)如下:
[cpp] view plain copy
void cornerShiTomasi_demo( int, void* )
{
if( maxCorners < 1 ) { maxCorners = 1; }
/// Parameters for Shi-Tomasi algorithm
vector corners;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
bool useHarrisDetector = false;
double k = 0.04;
/// Copy the source image
Mat cormat;
/// Apply corner detection :Determines strong corners on an image.
goodFeaturesToTrack( src_gray,
corners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
blockSize,
useHarrisDetector,
k );
/// Draw corners detected
for( int i = 0; i < corners.size(); i++ ){
circle( dst_norm_scaled, corners[i], 5, Scalar(255), 2, 8, 0 );
circle( src, corners[i], 4, Scalar(0,255,0), 2, 8, 0 );
}
/// Show what you got
imshow( corners_window, dst_norm_scaled );
imshow( source_window, src );
}
实践
在主函数中定义两个进度条方便调整阈值:
[cpp] view plain copy
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
createTrackbar( "Threshold: ", source_window, &thresh, max_thresh, cornerHarris_demo );
createTrackbar( "Max corners:", source_window, &maxCorners, maxTrackbar, cornerShiTomasi_demo );
namedWindow( corners_window, CV_WINDOW_AUTOSIZE );
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
imshow( source_window, src );
cornerHarris_demo( 0, 0 );
cornerShiTomasi_demo( 0, 0 );
这里还需要说的是OpenCV 2.4.2中给的角点检测跟踪的示例代码有些问题,是应为SURF等不再定义在 feature2d模块中,而是legacy和nonfree,所以需要加入引用:
[cpp] view plain copy
#include "opencv2/legacy/legacy.hpp"
#include "opencv2/nonfree/nonfree.hpp"
角点检测结果:
蓝色实心点为Harris检测结果,绿色空心圈为goodFeaturetoTrack检测结果。
M特征值分解后每个像素点相减的图(也就是Harris阈值判断的图)如下:
黑色实心点为Harris阈值检测结果,白色空心圈为阈值为27时Shi-Tomasi检测结果。
转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7805206
源码及资料下载: http://download.csdn.net/detail/xiaowei_cqu/4466627
========
学习OpenCV范例(二十)——角点检测算法
原文 http://blog.csdn.net/chenjiazhou12/article/details/22683049
本次范例讲的都是检测角点的算法,在这里我们会讲到,harris角点检测,Shi-Tomasi角点检测,FAST角点检测,尺度不变surf检测,尺度不变sift检测,特征点的描述。由于是算法问题,相对来说会比较复杂,而且都是一些比较经典的算法,如果只是纯粹的想要用算法来实现一些功能,那么只要调用OpenCV几个简单的函数就可以了,但是如果想学习一下理论知识,为以后自己的研究有所帮助,而且想理解函数的参数如何改变,那么还是得硬着头皮去看看原理吧,本人也是看了挺久的算法原理,但是还是没有完全理解透,所以在这里只是贴出我看过的比较有用的博客,还有一些自己编译的代码和实现结果,记录一下这个过程,方便以后可以进一步的研究。
1、原理
Harris: Opencv学习笔记(五)Harris角点检测
Shi-Tomasi: 【OpenCV】角点检测:Harris角点及Shi-Tomasi角点检测
FAST: OpenCV学习笔记(四十六)——FAST特征点检测features2D
SIFT: 【OpenCV】SIFT原理与源码分析
特征点检测学习_1(sift算法)
SURF: 特征点检测学习_2(surf算法)
2、代码实现
由于代码量较大,所以只是贴出代码的一部分,如果想要整体代码,可以从下面的链接中找到
http://download.csdn.net/detail/chenjiazhou12/7129327
①、harris角点检测
//计算角点响应函数以及非最大值抑制
void detect(const Mat &image){
//opencv自带的角点响应函数计算函数
cornerHarris (image,cornerStrength,neighbourhood,aperture,k);
double minStrength;
//计算最大最小响应值
minMaxLoc (cornerStrength,&minStrength,&maxStrength);
Mat dilated;
//默认3*3核膨胀,膨胀之后,除了局部最大值点和原来相同,其它非局部最大值点被
//3*3邻域内的最大值点取代
dilate (cornerStrength,dilated,cv::Mat());
//与原图相比,只剩下和原图值相同的点,这些点都是局部最大值点,保存到localMax
compare(cornerStrength,dilated,localMax,cv::CMP_EQ);
}
cornerHarris
功能:Harris角点检测
结构:
void cornerHarris(InputArray src, OutputArray dst, int blockSize, int apertureSize, double k, int borderType=BORDER_DEFAULT )
src :8位或者32位浮点数单通道图像
dst:保存Harris检测结果的图像,32位单通道,和src有同样的size
blockSize :邻域大小,相邻像素的尺寸(见关于 cornerEigenValsAndVecs() 的讨论)
apertureSize :滤波器的孔径大小
k :harris 检测器的自由参数
boderType :插值类型
compare
功能:两个数组之间或者一个数组和一个常数之间的比较
结构:
void compare(InputArray src1, InputArray src2, OutputArray dst, int cmpop)
src1 :第一个数组或者标量,如果是数组,必须是单通道数组。
src2 :第二个数组或者标量,如果是数组,必须是单通道数组。
dst :输出数组,和输入数组有同样的size和type=CV_8UC1
cmpop :
标志指明了元素之间的对比关系
CMP_EQ src1 相等 src2.
CMP_GT src1 大于 src2.
CMP_GE src1 大于或等于 src2.
CMP_LT src1 小于 src2.
CMP_LE src1 小于或等于 src2.
CMP_NE src1 不等于 src2.
如果对比结果为true,那么输出数组对应元素的值为255,否则为0
//获取角点图
Mat getCornerMap(double qualityLevel) {
Mat cornerMap;
// 根据角点响应最大值计算阈值
thresholdvalue= qualityLevel*maxStrength;
threshold(cornerStrength,cornerTh,
thresholdvalue,255,cv::THRESH_BINARY);
// 转为8-bit图
cornerTh.convertTo(cornerMap,CV_8U);
// 和局部最大值图与,剩下角点局部最大值图,即:完成非最大值抑制
bitwise_and(cornerMap,localMax,cornerMap);
return cornerMap;
}
bitwise_and
功能:计算两个数组或数组和常量之间与的关系
结构:
void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray())
src1 :第一个输入的数组或常量
src2 :第二个输入的数组或常量
dst :输出数组,和输入数组有同样的size和type
mask :可选择的操作掩码,为8位单通道数组,指定了输出数组哪些元素可以被改变,哪些不可以
操作过程为:
\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0
\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} \quad \texttt{if mask} (I) \ne0
\texttt{dst} (I) = \texttt{src1} \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0
如果为多通道数组,每个通道单独处理
②、Shi-Tomasi检测
void goodFeaturesDetect()
{
// 改进的harris角点检测方法
vector corners;
goodFeaturesToTrack(image,corners,
200,
//角点最大数目
0.01,
// 质量等级,这里是0.01*max(min(e1,e2)),e1,e2是harris矩阵的特征值
10);
// 两个角点之间的距离容忍度
harris().drawOnImage(image,corners);//标记角点
imshow (winname,image);
}
goodFeaturesToTrack
功能:确定图像的强角点
结构:
void goodFeaturesToTrack(InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask=noArray(), int blockSize=3, bool useHarrisDetector=false, double k=0.04 )
image :输入8位或32为单通道图像
corners :输出检测到的角点
maxCorners :返回的角点的最大值,如果设置的值比检测到的值大,那就全部返回
qualityLevel :最大最小特征值的乘法因子。定义可接受图像角点的最小质量因子
minDistance :限制因子,两个角点之间的最小距离,使用 Euclidian 距离
mask :ROI:感兴趣区域。函数在ROI中计算角点,如果 mask 为 NULL,则选择整个图像。 必须为单通道的灰度图,大小与输入图像相同。mask对应的点不为0,表示计算该点。
blockSize :邻域大小,相邻像素的尺寸(见关于 cornerEigenValsAndVecs() 的讨论)
useHarrisDetector :是否使用Harris检测器 (见关于 cornerHarris() 或 cornerMinEigenVal()的讨论).
k :Harris检测器的自由参数
1、该函数在原图像的每一个像素点使用cornerMinEigenVal()或者cornerHarris()来计算角点
2、对检测到的角点进行非极大值抑制(在3*3的领域内极大值被保留)
3、对检测到的角点进行阈值处理,小于阈值,则被删除
4、对最终得到的角点进行降序排序
5、删除离强角点距离比minDistance近的角点
③、FAST检测
void fastDetect()
{
//快速角点检测
vector keypoints;
FastFeatureDetector fast(40,true);
fast.detect (image,keypoints);
drawKeypoints (image,keypoints,image,Scalar::all(255),DrawMatchesFlags::DRAW_OVER_OUTIMG);
imshow (winname,image);
}
FastFeatureDetector 类
这个类是FeatureDetector类继承过来的
构造函数
FastFeatureDetector( int threshold=1, bool nonmaxSuppression=true );
threshold:检测阈值
nonmaxSuppression:非极大值抑制
void FeatureDetector::detect(const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const
void FeatureDetector::detect(const vector& images, vector>& keypoints, const vector& masks=vector() ) const
image :输入图片
images :输入图片组
keypoints :第一个为检测到的keypoints,第二个为检测到的keypoints组
mask :可选的操作掩码,指定哪些keypoints,必须是8位二值化有非零元素的感兴趣区域
masks :多个操作掩码,masks[i]对应images[i]
drawKeypoints
功能:绘制特征关键点.
结构:
void drawKeypoints(const Mat& image, const vector& keypoints, Mat& outImg, const Scalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT )
image :原图片
keypoints :得到的keypoints
outImg :输出图片,它的内容依赖于flags的值
color :keypoints的颜色
flags :标志画在输出图像的特征,flags是由DrawMatchesFlags定义的
struct DrawMatchesFlags
{
enum
{
DEFAULT = 0, // 会创建一个输出矩阵,两张源文件,匹配结果,
// 和keypoints将会被绘制在输出图像中
// 对于每一个keypoints点,只有中心被绘制,
// 不绘制半径和方向
DRAW_OVER_OUTIMG = 1, // 不创建输出图像,匹配结构绘制在已经存在的输出图像中
NOT_DRAW_SINGLE_POINTS = 2, // 单独的keypoints点不被绘制
DRAW_RICH_KEYPOINTS = 4 // 对于每一个keypoints点,半径和方向都被绘制
};
};
④、SIFT检测
void siftDetect()
{
vector keypoints;
SiftFeatureDetector sift(0.03,10);
sift.detect(image,keypoints);
drawKeypoints(image,keypoints,image,Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow (winname,image);
}
SiftFeatureDetector
结构:
SiftFeatureDetector( double threshold, double edgeThreshold,
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
threshold:过滤掉较差的特征点的对阈值。threshold越大,返回的特征点越少。
edgeThreshold:过滤掉边缘效应的阈值。edgeThreshold越大,特征点越多
⑤、SURF检测
void surfDetect()
{
vector keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
//-- Step 1: Detect the keypoints using SURF Detector
SurfFeatureDetector surf(2500);
surf.detect(image,keypoints_1);
surf.detect(image2,keypoints_2);
//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
extractor.compute( image, keypoints_1, descriptors_1 );
extractor.compute( image2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
BruteForceMatcher< L2 > matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
nth_element(matches.begin(),matches.begin()+24,matches.end());
matches.erase(matches.begin()+25,matches.end());
//-- Draw matches
Mat img_matches;
drawMatches( image, keypoints_1, image2, keypoints_2, matches, img_matches,Scalar(255,255,255) );
drawKeypoints(image,keypoints_1,image,Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//-- Show detected matches
imshow("Matches", img_matches );
imshow (winname,image);
}
SurfFeatureDetector
构造函数
SurfFeatureDetector( double hessianThreshold = 400., int octaves = 3,
int octaveLayers = 4 );
hessianThreshold:阈值
octaves:金字塔组数
octaveLayers:金字塔中每组的层数
SurfDescriptorExtractor
功能:来封装的用于计算特征描述子的类,构造SURE描述子提取器
compute
功能:根据检测到的图像(第一种情况)或者图像集合(第二种情况)中的关键点(检测子)计算描述子.
void DescriptorExtractor::compute(const Mat& image, vector& keypoints, Mat& descriptors) const
void DescriptorExtractor::compute(const vector& images, vector>& keypoints, vector& descriptors) const
image :输入图像.
images :输入图像集.
keypoints:输入的特征关键点.
descriptors:计算特征描述子
BruteForceMatcher< L2 >
功能:暴力搜索特征点匹配. 对于第一集合中的特征描述子, 这个匹配寻找了在第二个集合中最近的特征描述子. 这种特征描述子匹配支持 masking permissible特征描述子集合匹配.
它是一个模板类,<>中的参数表示匹配的方式
DMatch
功能:用于匹配特征关键点的特征描述子的类:查询特征描述子索引, 特征描述子索引, 训练图像索引, 以及不同特征描述子之间的距离.
match
功能:给定查询集合中的每个特征描述子,寻找最佳匹配.
结构:
void DescriptorMatcher::match(const Mat& queryDescriptors, const Mat& trainDescriptors, vector& matches, const Mat& mask=Mat() ) const
void DescriptorMatcher::match(const Mat& queryDescriptors, vector& matches, const vector& masks=vector() )
queryDescriptors :特征描述子查询集.
trainDescriptors :待训练的特征描述子集.
matches :匹配特征描述子类
mask – 特定的在输入查询和训练特征描述子集之间的可允许掩码匹配,指定哪些可以被匹配
masks – masks集. 每个 masks[i] 特定标记出了在输入查询特征描述子和存储的从第i个图像中提取的特征描述子集
第二个方法的trainDesctiptors由DescriptorMatcher::add给出。
nth_element
功能:nth_element作用为求第n小的元素,并把它放在第n位置上,下标是从0开始计数的,也就是说求第0小的元素就是最小的数。
erase
功能:移除参数1和参数2之间的元素,返回下一个元素
drawMatches
功能:给定两幅图像,绘制寻找到的特征关键点及其匹配
结构:
void drawMatches(const Mat& img1, const vector& keypoints1, const Mat& img2, const vector& keypoints2, const vector& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector& matchesMask=vector(), int flags=DrawMatchesFlags::DEFAULT )
img1 :第一张原图片
keypoints1 :第一张得到的关键点
img2 :第二张图片
keypoints2 :第二张得到的关键点
matches :匹配点
outImg :输出图片,它的内容依赖于flags的值
matchColor :匹配线的颜色,如果为-1,则颜色随机分配
singlePointColor :单独点,没有匹配到的点的颜色,如果为-1,则颜色随机分配
matchesMask :掩码,表示哪些匹配值被绘制出来,如果为空,表示所有匹配点都绘制出来
flags :和上面drawkeypoints中的flags一样
3、运行结果
图1、Harris 图2、Shi-Tomasi
图3、FAST 图4、SIFT
图5、SURF
图6、匹配结果
源代码下载地址:
http://download.csdn.net/detail/chenjiazhou12/7129327
本次范例讲的都是检测角点的算法,在这里我们会讲到,harris角点检测,Shi-Tomasi角点检测,FAST角点检测,尺度不变surf检测,尺度不变sift检测,特征点的描述。由于是算法问题,相对来说会比较复杂,而且都是一些比较经典的算法,如果只是纯粹的想要用算法来实现一些功能,那么只要调用OpenCV几个简单的函数就可以了,但是如果想学习一下理论知识,为以后自己的研究有所帮助,而且想理解函数的参数如何改变,那么还是得硬着头皮去看看原理吧,本人也是看了挺久的算法原理,但是还是没有完全理解透,所以在这里只是贴出我看过的比较有用的博客,还有一些自己编译的代码和实现结果,记录一下这个过程,方便以后可以进一步的研究。
1、原理
Harris: Opencv学习笔记(五)Harris角点检测
Shi-Tomasi: 【OpenCV】角点检测:Harris角点及Shi-Tomasi角点检测
FAST: OpenCV学习笔记(四十六)——FAST特征点检测features2D
SIFT: 【OpenCV】SIFT原理与源码分析
特征点检测学习_1(sift算法)
SURF: 特征点检测学习_2(surf算法)
2、代码实现
由于代码量较大,所以只是贴出代码的一部分,如果想要整体代码,可以从下面的链接中找到
http://download.csdn.net/detail/chenjiazhou12/7129327
①、harris角点检测
//计算角点响应函数以及非最大值抑制
void detect(const Mat &image){
//opencv自带的角点响应函数计算函数
cornerHarris (image,cornerStrength,neighbourhood,aperture,k);
double minStrength;
//计算最大最小响应值
minMaxLoc (cornerStrength,&minStrength,&maxStrength);
Mat dilated;
//默认3*3核膨胀,膨胀之后,除了局部最大值点和原来相同,其它非局部最大值点被
//3*3邻域内的最大值点取代
dilate (cornerStrength,dilated,cv::Mat());
//与原图相比,只剩下和原图值相同的点,这些点都是局部最大值点,保存到localMax
compare(cornerStrength,dilated,localMax,cv::CMP_EQ);
}
cornerHarris
功能:Harris角点检测
结构:
void cornerHarris(InputArray src, OutputArray dst, int blockSize, int apertureSize, double k, int borderType=BORDER_DEFAULT )
src :8位或者32位浮点数单通道图像
dst:保存Harris检测结果的图像,32位单通道,和src有同样的size
blockSize :邻域大小,相邻像素的尺寸(见关于 cornerEigenValsAndVecs() 的讨论)
apertureSize :滤波器的孔径大小
k :harris 检测器的自由参数
boderType :插值类型
compare
功能:两个数组之间或者一个数组和一个常数之间的比较
结构:
void compare(InputArray src1, InputArray src2, OutputArray dst, int cmpop)
src1 :第一个数组或者标量,如果是数组,必须是单通道数组。
src2 :第二个数组或者标量,如果是数组,必须是单通道数组。
dst :输出数组,和输入数组有同样的size和type=CV_8UC1
cmpop :
标志指明了元素之间的对比关系
CMP_EQ src1 相等 src2.
CMP_GT src1 大于 src2.
CMP_GE src1 大于或等于 src2.
CMP_LT src1 小于 src2.
CMP_LE src1 小于或等于 src2.
CMP_NE src1 不等于 src2.
如果对比结果为true,那么输出数组对应元素的值为255,否则为0
//获取角点图
Mat getCornerMap(double qualityLevel) {
Mat cornerMap;
// 根据角点响应最大值计算阈值
thresholdvalue= qualityLevel*maxStrength;
threshold(cornerStrength,cornerTh,
thresholdvalue,255,cv::THRESH_BINARY);
// 转为8-bit图
cornerTh.convertTo(cornerMap,CV_8U);
// 和局部最大值图与,剩下角点局部最大值图,即:完成非最大值抑制
bitwise_and(cornerMap,localMax,cornerMap);
return cornerMap;
}
bitwise_and
功能:计算两个数组或数组和常量之间与的关系
结构:
void bitwise_and(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray())
src1 :第一个输入的数组或常量
src2 :第二个输入的数组或常量
dst :输出数组,和输入数组有同样的size和type
mask :可选择的操作掩码,为8位单通道数组,指定了输出数组哪些元素可以被改变,哪些不可以
操作过程为:
\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0
\texttt{dst} (I) = \texttt{src1} (I) \wedge \texttt{src2} \quad \texttt{if mask} (I) \ne0
\texttt{dst} (I) = \texttt{src1} \wedge \texttt{src2} (I) \quad \texttt{if mask} (I) \ne0
如果为多通道数组,每个通道单独处理
②、Shi-Tomasi检测
void goodFeaturesDetect()
{
// 改进的harris角点检测方法
vector corners;
goodFeaturesToTrack(image,corners,
200,
//角点最大数目
0.01,
// 质量等级,这里是0.01*max(min(e1,e2)),e1,e2是harris矩阵的特征值
10);
// 两个角点之间的距离容忍度
harris().drawOnImage(image,corners);//标记角点
imshow (winname,image);
}
goodFeaturesToTrack
功能:确定图像的强角点
结构:
void goodFeaturesToTrack(InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask=noArray(), int blockSize=3, bool useHarrisDetector=false, double k=0.04 )
image :输入8位或32为单通道图像
corners :输出检测到的角点
maxCorners :返回的角点的最大值,如果设置的值比检测到的值大,那就全部返回
qualityLevel :最大最小特征值的乘法因子。定义可接受图像角点的最小质量因子
minDistance :限制因子,两个角点之间的最小距离,使用 Euclidian 距离
mask :ROI:感兴趣区域。函数在ROI中计算角点,如果 mask 为 NULL,则选择整个图像。 必须为单通道的灰度图,大小与输入图像相同。mask对应的点不为0,表示计算该点。
blockSize :邻域大小,相邻像素的尺寸(见关于 cornerEigenValsAndVecs() 的讨论)
useHarrisDetector :是否使用Harris检测器 (见关于 cornerHarris() 或 cornerMinEigenVal()的讨论).
k :Harris检测器的自由参数
1、该函数在原图像的每一个像素点使用cornerMinEigenVal()或者cornerHarris()来计算角点
2、对检测到的角点进行非极大值抑制(在3*3的领域内极大值被保留)
3、对检测到的角点进行阈值处理,小于阈值,则被删除
4、对最终得到的角点进行降序排序
5、删除离强角点距离比minDistance近的角点
③、FAST检测
void fastDetect()
{
//快速角点检测
vector keypoints;
FastFeatureDetector fast(40,true);
fast.detect (image,keypoints);
drawKeypoints (image,keypoints,image,Scalar::all(255),DrawMatchesFlags::DRAW_OVER_OUTIMG);
imshow (winname,image);
}
FastFeatureDetector 类
这个类是FeatureDetector类继承过来的
构造函数
FastFeatureDetector( int threshold=1, bool nonmaxSuppression=true );
threshold:检测阈值
nonmaxSuppression:非极大值抑制
void FeatureDetector::detect(const Mat& image, vector& keypoints, const Mat& mask=Mat() ) const
void FeatureDetector::detect(const vector& images, vector>& keypoints, const vector& masks=vector() ) const
image :输入图片
images :输入图片组
keypoints :第一个为检测到的keypoints,第二个为检测到的keypoints组
mask :可选的操作掩码,指定哪些keypoints,必须是8位二值化有非零元素的感兴趣区域
masks :多个操作掩码,masks[i]对应images[i]
drawKeypoints
功能:绘制特征关键点.
结构:
void drawKeypoints(const Mat& image, const vector& keypoints, Mat& outImg, const Scalar& color=Scalar::all(-1), int flags=DrawMatchesFlags::DEFAULT )
image :原图片
keypoints :得到的keypoints
outImg :输出图片,它的内容依赖于flags的值
color :keypoints的颜色
flags :标志画在输出图像的特征,flags是由DrawMatchesFlags定义的
struct DrawMatchesFlags
{
enum
{
DEFAULT = 0, // 会创建一个输出矩阵,两张源文件,匹配结果,
// 和keypoints将会被绘制在输出图像中
// 对于每一个keypoints点,只有中心被绘制,
// 不绘制半径和方向
DRAW_OVER_OUTIMG = 1, // 不创建输出图像,匹配结构绘制在已经存在的输出图像中
NOT_DRAW_SINGLE_POINTS = 2, // 单独的keypoints点不被绘制
DRAW_RICH_KEYPOINTS = 4 // 对于每一个keypoints点,半径和方向都被绘制
};
};
④、SIFT检测
void siftDetect()
{
vector keypoints;
SiftFeatureDetector sift(0.03,10);
sift.detect(image,keypoints);
drawKeypoints(image,keypoints,image,Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow (winname,image);
}
SiftFeatureDetector
结构:
SiftFeatureDetector( double threshold, double edgeThreshold,
int nOctaves=SIFT::CommonParams::DEFAULT_NOCTAVES,
int nOctaveLayers=SIFT::CommonParams::DEFAULT_NOCTAVE_LAYERS,
int firstOctave=SIFT::CommonParams::DEFAULT_FIRST_OCTAVE,
int angleMode=SIFT::CommonParams::FIRST_ANGLE );
threshold:过滤掉较差的特征点的对阈值。threshold越大,返回的特征点越少。
edgeThreshold:过滤掉边缘效应的阈值。edgeThreshold越大,特征点越多
⑤、SURF检测
void surfDetect()
{
vector keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
//-- Step 1: Detect the keypoints using SURF Detector
SurfFeatureDetector surf(2500);
surf.detect(image,keypoints_1);
surf.detect(image2,keypoints_2);
//-- Step 2: Calculate descriptors (feature vectors)
SurfDescriptorExtractor extractor;
extractor.compute( image, keypoints_1, descriptors_1 );
extractor.compute( image2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
BruteForceMatcher< L2 > matcher;
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
nth_element(matches.begin(),matches.begin()+24,matches.end());
matches.erase(matches.begin()+25,matches.end());
//-- Draw matches
Mat img_matches;
drawMatches( image, keypoints_1, image2, keypoints_2, matches, img_matches,Scalar(255,255,255) );
drawKeypoints(image,keypoints_1,image,Scalar(255,255,255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//-- Show detected matches
imshow("Matches", img_matches );
imshow (winname,image);
}
SurfFeatureDetector
构造函数
SurfFeatureDetector( double hessianThreshold = 400., int octaves = 3,
int octaveLayers = 4 );
hessianThreshold:阈值
octaves:金字塔组数
octaveLayers:金字塔中每组的层数
SurfDescriptorExtractor
功能:来封装的用于计算特征描述子的类,构造SURE描述子提取器
compute
功能:根据检测到的图像(第一种情况)或者图像集合(第二种情况)中的关键点(检测子)计算描述子.
void DescriptorExtractor::compute(const Mat& image, vector& keypoints, Mat& descriptors) const
void DescriptorExtractor::compute(const vector& images, vector>& keypoints, vector& descriptors) const
image :输入图像.
images :输入图像集.
keypoints:输入的特征关键点.
descriptors:计算特征描述子
BruteForceMatcher< L2 >
功能:暴力搜索特征点匹配. 对于第一集合中的特征描述子, 这个匹配寻找了在第二个集合中最近的特征描述子. 这种特征描述子匹配支持 masking permissible特征描述子集合匹配.
它是一个模板类,<>中的参数表示匹配的方式
DMatch
功能:用于匹配特征关键点的特征描述子的类:查询特征描述子索引, 特征描述子索引, 训练图像索引, 以及不同特征描述子之间的距离.
match
功能:给定查询集合中的每个特征描述子,寻找最佳匹配.
结构:
void DescriptorMatcher::match(const Mat& queryDescriptors, const Mat& trainDescriptors, vector& matches, const Mat& mask=Mat() ) const
void DescriptorMatcher::match(const Mat& queryDescriptors, vector& matches, const vector& masks=vector() )
queryDescriptors :特征描述子查询集.
trainDescriptors :待训练的特征描述子集.
matches :匹配特征描述子类
mask – 特定的在输入查询和训练特征描述子集之间的可允许掩码匹配,指定哪些可以被匹配
masks – masks集. 每个 masks[i] 特定标记出了在输入查询特征描述子和存储的从第i个图像中提取的特征描述子集
第二个方法的trainDesctiptors由DescriptorMatcher::add给出。
nth_element
功能:nth_element作用为求第n小的元素,并把它放在第n位置上,下标是从0开始计数的,也就是说求第0小的元素就是最小的数。
erase
功能:移除参数1和参数2之间的元素,返回下一个元素
drawMatches
功能:给定两幅图像,绘制寻找到的特征关键点及其匹配
结构:
void drawMatches(const Mat& img1, const vector& keypoints1, const Mat& img2, const vector& keypoints2, const vector& matches1to2, Mat& outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const vector& matchesMask=vector(), int flags=DrawMatchesFlags::DEFAULT )
img1 :第一张原图片
keypoints1 :第一张得到的关键点
img2 :第二张图片
keypoints2 :第二张得到的关键点
matches :匹配点
outImg :输出图片,它的内容依赖于flags的值
matchColor :匹配线的颜色,如果为-1,则颜色随机分配
singlePointColor :单独点,没有匹配到的点的颜色,如果为-1,则颜色随机分配
matchesMask :掩码,表示哪些匹配值被绘制出来,如果为空,表示所有匹配点都绘制出来
flags :和上面drawkeypoints中的flags一样
3、运行结果
图1、Harris 图2、Shi-Tomasi
图3、FAST 图4、SIFT
图5、SURF
图6、匹配结果
源代码下载地址:
http://download.csdn.net/detail/chenjiazhou12/7129327
========
什么是角点检测,角点检测能做什么?opencv的
目前的角点检测算法可归纳为3类:基于灰度图像的角点检测、基于二值图像的角点检测、基于轮廓曲线的角点检测。基于灰度图像的角点检测又可分为基于梯度、基于模板和基于模板梯度组合3类方法,其中基于模板的方法主要考虑像素领域点的灰度变化,即图像亮度的变化,将与邻点亮度对比足够大的点定义为角点。常见的基于模板的角点检测算法有Kitchen-Rosenfeld角点检测算法,Harris角点检测算法、KLT角点检测算法及SUSAN角点检测算法。和其他角点检测算法相比,SUSAN角点检测算法具有算法简单、位置准确、抗噪声能力强等特点。
========
opencv harris角点检测
#include "cv.h"
#include "highgui.h"
#include
int main(int argc,char** argv)
{
IplImage* pImg;
IplImage* pHarrisImg;
IplImage* grayImage;
IplImage* dst8;
double minVal=0.0, maxVal=0.0;
double scale, shift;
double min=0, max=255;
if((pImg=cvLoadImage("/home/dz/study/7class/a.jpg",1))!=NULL)
{
cvNamedWindow("source",1);
cvShowImage("source",pImg);
pHarrisImg=cvCreateImage(cvGetSize(pImg),IPL_DEPTH_32F,1);
//there we should define IPL_DEPTH_32F rather than IPL_DEPTH_8U
grayImage=cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U,1);
dst8=cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U,1);//this is for the result image
grayImage->origin=pImg->origin; //there make sure the same origin between grayImage and pImg
cvCvtColor(pImg,grayImage,CV_BGR2GRAY);//cause harris need gray scale image,we should convert RGB 2 gray
int block_size=7;
//do harris algorithm
cvCornerHarris(grayImage,pHarrisImg,block_size,3,0.04);
//convert scale so that we see the clear image
cvMinMaxLoc(pHarrisImg,&minVal,&maxVal,NULL,NULL,0);
std::cout< std::cout<
scale=(max-min)/(maxVal-minVal);
shift=-minVal*scale+min;
cvConvertScale(pHarrisImg,dst8,scale,shift);
cvNamedWindow("Harris",1);
cvShowImage("Harris",dst8);
cvWaitKey(0);
cvDestroyWindow("source");
cvDestroyWindow("Harris");
cvReleaseImage(&dst8);
cvReleaseImage(&pHarrisImg);
return 0;
}
return 1;
}
========
opencv学习 trajkovic 角点检测
目录(?)[-]
来源httpblogcsdnnetsongziteaarticledetails13614977
背景引言
基本理论
角点量的计算方法
多格算法Multigrid Algorithm
算法流程
来源:http://blog.csdn.net/songzitea/article/details/13614977
背景引言
本节主要内容来源于是由 Miroslav Trajkovic和Mark Hedley[1]在1998年提出Trajkovic算子,其论文为FastCorner Detection.和Trajkovic Operator (4-Neighbours)[3](注:本节图片主要是来源于此)。Trajkovic算子角点提取方法存在价值是在角点提取的效果上,它优于同时期的其他角点(如Moravec角点,Harris角点)提取方法,同时,从算法的运行速度角度而言,它比同是期的其他角点提取方法要快很多。
基本理论
作者对角点的定义,与Moravec角点、 Harris 角点的定义是一样的:图像灰度值在各个方向变化都比较大的点,即认为是角点。角点量的定义也类似于Moravec,即角点量的是在各个方向上灰度变化的最小值(当在某个方向上,灰度值的变化最小,并且这个最小值也大于某某个设定的阈值,那么认为这个点就是一个角点)。与Moravec 相比 ,Trajkovic 的性能更好,因为Trajkovic通过使用像素插值的方法比较每一个方向上的灰度值变化程度,而moravec只有限个方向的比较,与harris相比,两都效果差不多,但是Trajkovic的速度远快于haris.
Trajkovic在速度方面的表现于决定它能够应用对实时要求比较高的系统中。但是Trajkovic也有缺点是它不具备旋转不变性,对噪声比较敏感,对对角点的响应值比较大等。对于这些缺点也会有相应的方法,但不能完全的解决这些缺点。Trajkovic算子计算角点量是在一个小的圆形窗口内,并且考虑了所有通过圆心的直线,即计算了每一个可能的方向上的灰度值变化程度)。假设用C表示圆的圆心,那么通过圆心C的任意一条直线会与圆在两个交点,如图1所示:
Figure 1: Notation for Trajkovic Operator
那么,Trajkovic对角点量的定义是
C(x,y)表示的是图像I上,任意一点(x,y)处的角点量。这个公式能断出是否是角点。点(x,y)在图像目的位置有四种情况。如下图所示:
第一情况:点(x,y)在内部区域:此时经点(x,y)位中心的圆形窗中完全在一个比较平坦的区域,或者大部分的圆形窗口是在这个平坦区域的(如上图A所示)。这种情况下,至少存大一条直线过中心点C的直线使得Ic约等于Ip和Ic约等于Ip‘,根据角点量计算公式,计算出的C(x,y)一定是一个很小的值,那么就可以排除点(x,y)不是角点。并且,大多数的情况下,会有多个这样的点P存大,使得对噪声一定的承受能力。
第二情况:点(x,y)在边缘上:此时圆形窗口的中心点(x,y)刚好落在边缘上(如上图B所示)。仅存在一个点对P,P'使得Ic约等于Ip和Ic约等于Ip‘。在理想状况下,根据角点量的公式计算出来的角点量C(x,y)是比较小的。但是由于只存在一个条这样直线,所以容易受到噪声的干扰。
第三情况:点(x,y)在角点上:当圆窗口的中心(x, y)位于角点上时(如上图C 所示)。任意一条通过点(x,y)与圆相交的交点P、P', P、P' 中至少有一个点的灰度值与中心的点的灰度值差别比较大。所以计算出来的角点量也比较大(大于设定的阈值,所以会被认为是角点)。
第四情况:点(x,y)在一个孤立点上:如果在孤立点上(如上图D所示),对于每一个直线上的Ip, Ip’,都分与Ic的值相差比较大,此时计算出的角点量也比较大。此时这个点是噪声,但是由于角点量比较大,会被认为是角点。为了削除这种噪声的影响,可以先使用高斯平滑进行去噪。
根据上面的分析,Trajkovic 受噪声的影响比较大,所以,可以先实现高斯平滑去噪再使用Trajkovic算子进行角点检测。
角点量的计算方法
目前,关于Trajkovic计算角点量的并未具体介绍如何时计算。首先,如下图2所示,水平方向的灰度值变化量rA,垂直方向的灰度值变化量rB很容易计算。
如何计算任意一个方向上灰度值的变化程度呢?首先,我们角点量就取其最小值即可:
Figure 2: Interpixel positions Figure3: Interpixel approximation for a 3x3 window using 4-neighbours
现在,我们可将问题可以转换为,任意一条通过点C 的直线与圆的交点是P,P',Q,Q'即可:
把公式(2)代入公式(1) 中即可:
那么,最小值是即可结论是
多格算法(Multigrid Algorithm)
角点可以分为两类:几何角点和纹理角点。几何角点是由图像中物体边缘的相交。而纹理角点有由物体表面的纹理产生的角点(如:草地,衣服的纹理等)。通常情况下,一幅图像中的几何角点的数量要远少于纹理角点。多格算法的目的是希望能多检测到几何角点,少检测到纹理点。因Trajkovic 认为几何角点比纹理角点更加的稳定。所以,从实践使用来看,减少纹理角点是合理的。
经过观察发现,纹理角点一般都是非常的密集,并且是在一个很小区域内灰度值发生变化,所以,采用把原图缩小以后,再来提取角点,通过缩小图像,可以消除区域内灰度值的变换。缩小图像的方法不是使用插值法,而是使用平均法。如下图所示:
Trajkovic角点检测,首先使用多格法对原如图像进行缩小,这样不仅能够减少检测到纹理角点数目,而且加快了Trajkovic角点检测的速度,在Trajkovic角点检测缩小版的图像上,使用角为简单的角点量计算公式初步判断是不是侯选角点。如果是,再到原始图像上进一步判定。
Figure 4: Corner points detected at different image resolutions
如图4所示,原始的大小是256*256,图像中有一片草地,而草地上的角点,对于我们进行角点匹配时,是没有什么意义。当把图像缩小到128*128时,草地上的角点已经消除了。当然,图像也不能缩小太多,否则,几何角点也会消失,当缩小到64*64时,部分几何角点也消失了。
算法流程
#include
#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
using namespace std;
void getTrajkovic4NCorner(IplImage* src,IplImage* srcResize , float T1 , float T2 ,CvSeq* corners,int maximumSize)
{
int x,y,maxChar=255,scaleX = src->width/srcResize->width,scaleY = src->height/srcResize->height;
IplImage* srcResizeMap ,*srcMap;
srcResizeMap = cvCreateImage(cvGetSize(srcResize),8,1); // 用来保存缩小图像的角点量
srcMap = cvCreateImage(cvGetSize(src),32,1); // 用来保存源图像计算得到的角点量
cvZero(srcMap);
cvZero(srcResizeMap);
for( y=1;yheight-1;y++)
{
uchar* preRow = (uchar*)(srcResize->imageData + (y-1)*srcResize->widthStep);
uchar* curRow = (uchar*)(srcResize->imageData + y*srcResize->widthStep);
uchar* nextRow = (uchar*)(srcResize->imageData + (y+1)*srcResize->widthStep);
uchar* MapData = (uchar*)(srcResizeMap->imageData + y*srcResizeMap->widthStep);
for(x=1;xwidth-1;x++)
{
int IC,IA,IB,IAA,IBB;
int rA,rB,C_Simple;
// curRow[x]是uchar型,而IC是int型,如果直接赋值int类型的其它三个字节可能会产生随机值
IC = curRow[x]&maxChar;
IA = curRow[x+1]&maxChar;
IAA = curRow[x-1]&maxChar;
IB = preRow[x]&maxChar;
IBB = nextRow[x]&maxChar;
rA = (IA-IC)*(IA-IC) + (IAA-IC)*(IAA-IC);
rB = (IB-IC)*(IB-IC) + (IBB-IC)*(IBB-IC);
C_Simple = rA < rB ? rA : rB;
if(C_Simple > T1)
{
MapData[x]=1;
}
}
}
for( y=1;yheight-1;y++)
{
uchar* srcResizeMapData = (uchar*)(srcResizeMap->imageData + y*srcResizeMap->widthStep);
//uchar* srcMapData = (uchar*)(srcMap->imageData + y*srcMap->widthStep);
for(x=1;xwidth-1;x++)
{
if(srcResizeMapData[x]==0)
{
continue;
}
int originX,originY,IC,IA,IB,IAA,IBB;
int rA,rB,C_Simple;
originX = x*scaleX;
originY = y*scaleY;
uchar* srcPreRow = (uchar*)(src->imageData + (originY-1)*src->widthStep);
uchar* srcCurRow = (uchar*)(src->imageData + originY*src->widthStep);
uchar* srcNextRow = (uchar*)(src->imageData + (originY+1)*src->widthStep);
IC = srcCurRow[originX]&maxChar;
IA = srcCurRow[originX+1]&maxChar;
IAA = srcCurRow[originX-1]&maxChar;
IB = srcPreRow[x]&maxChar;
IBB = srcNextRow[x]&maxChar;
rA = (IA-IC)*(IA-IC) + (IAA-IC)*(IAA-IC);
rB = (IB-IC)*(IB-IC) + (IBB-IC)*(IBB-IC);
C_Simple = rA < rB ? rA : rB;
if(C_Simple>T2)
{
float B1,B2,C,A,B,C_InterPixel;
B1 = (IB-IA)*(IA-IC)+(IBB-IAA)*(IAA-IC);
B2 = (IB-IAA)*(IAA-IC)+(IBB-IA)*(IA-IC);
C = rA;
B = B1 A = rB-rA-2*B;
if(B<0 &&(A+B)>0)
{
C_InterPixel = C-(B*B)/A;
}
else
{
C_InterPixel=C_Simple;
}
if(C_InterPixel>T2)
{
float* srcMapData = (float*)(srcMap->imageData + originY*srcMap->widthStep);
srcMapData[originX]=C_InterPixel;
//cvSetReal2D(srcMap,originY,originX,C_InterPixel);
}
}
}
}
//计算局部极大值 及 极大值是否大于阈值
int beginY,endY,beginX,endX;
int halfWinSize = maximumSize/2;
beginY = halfWinSize;
endY = srcMap->height - halfWinSize;
beginX = halfWinSize;
endX = srcMap->width - halfWinSize;
for(y=beginY;y {
for(x=beginX;x {
//寻找局部极大值 及其位置信息
float maxValue=0;
int flag = 0 ;
CvPoint maxLoc;
maxLoc.x = -1;
maxLoc.y = -1;
//首先计算以点(x,y)位中心的maximumSize*maximumSize的窗口内部的局部极大值
for(int winy=-halfWinSize;winy<=halfWinSize;winy++)
{
for(int winx=-halfWinSize;winx<=halfWinSize;winx++)
{
float value ;
value = cvGetReal2D(srcMap,y+winy,x+winx);
//计算该窗口内 最大值 保存到max 并保存其坐标到maxLoc
if(value>maxValue)
{
maxValue = value;
maxLoc.x = x+winx;
maxLoc.y = y+winy;
flag = 1;
}
}
}
//如果找到局部极大值 并且该值大于预先设定的阈值 则认为是角点
if(flag==1 && maxValue>T2)
{
cvSeqPush(corners,&maxLoc);
}
x = x+halfWinSize;
}
y = y + halfWinSize;
}
cvReleaseImage(&srcResizeMap);
cvReleaseImage(&srcMap);
}
int main(int argc,char* argv[])
{
//相关变量
int scale = 2;
IplImage* src,*srcGray,*srcGrayResize;
CvMemStorage* mem = cvCreateMemStorage(0);
CvSeq* TrajkovicPoints;
src = cvLoadImage("E:\\study_opencv_video\\lesson17_2\\2.jpg");//源图像
srcGray = cvCreateImage(cvGetSize(src),8,1);
if(!src)
{
cout << "src is null" << endl;
return 0;
}
cvCvtColor(src,srcGray,CV_BGR2GRAY);
srcGrayResize = cvCreateImage(cvSize(srcGray->width/scale,srcGray->height/scale),8,1);
cvResize(srcGray,srcGrayResize);//请将resize修改为多格算法
//Trajkovic 4 邻域角点角点保存的空间 角点坐标保存在一个序列中
TrajkovicPoints = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvPoint),mem);
//进行Trajkovic 4 邻域角点检测
int T1=120,T2=150,localArea = 8; // localArea 是局部极大值抑制时窗口的大小
getTrajkovic4NCorner(srcGray,srcGrayResize,T1,T2,TrajkovicPoints,localArea);
//获取每一个角点的坐标
for(int x=0;xtotal;x++)
{
//获取第x个角点的坐标
CvPoint* pt = (CvPoint*)cvGetSeqElem(TrajkovicPoints,x);
//以角点坐标为中心 绘制一个半径为5的圆
cvCircle(src,*pt,2,cvScalar(255,0,255,0));
}
//显示图像
cvNamedWindow("dst");
cvShowImage("dst",src);
cvWaitKey(0);
//释放资源
cvReleaseImage(&src);
cvReleaseImage(&srcGray);
cvReleaseImage(&srcGrayResize);
cvReleaseMemStorage(&mem);
return 0;
}
========
OpenCV成长之路 特征点检测与图像匹配
特征点又称兴趣点、关键点,它是在图像中突出且具有代表意义的一些点,通过这些点我们可以用来识别图像、进行图像配准、进行3D重建等。本文主要介绍OpenCV中几种定位与表示关键点的函数。
一、Harris角点
角点是图像中最基本的一种关键点,它是由图像中一些几何结构的关节点构成,很多都是线条之间产生的交点。Harris角点是一类比较经典的角点类型,它的基本原理是计算图像中每点与周围点变化率的平均值。
(1)
(2)
其中I(x+u,y+u)代表了点(x,y)邻域点的灰度值。通过变换可以将上式变化为一个协方差矩阵求特征值的问题(2),具体数学原理本文不过多描述。
OpenCV的Hairrs角点检测的函数为cornerHairrs(),但是它的输出是一幅浮点值图像,浮点值越高,表明越可能是特征角点,我们需要对图像进行阈值化。我们使用一张建筑图像来显示:
int main()
{
Mat image=imread("../buliding.png");
Mat gray;
cvtColor(image,gray,CV_BGR2GRAY);
Mat cornerStrength;
cornerHarris(gray,cornerStrength,3,3,0.01);
threshold(cornerStrength,cornerStrength,0.001,255,THRESH_BINARY);
return 0;
}
首先我们来说明一下cornerHairrs()这个函数参数的意思:
前2参数是输入与输出,输入是一个灰度图像,输出是一个浮点图像,第三个参数指定角点分析的邻域,第4个参数实际上在角点求取过程中计算梯度图像的核窗口大小,第5个参数是它原理公式(2)中的一个系数。
从上面的例子的结果我们可以看到,有很多角点都是粘连在一起的,我们下面通过加入非极大值抑制来进一步去除一些粘在一起的角点。
非极大值抑制原理是,在一个窗口内,如果有多个角点则用值最大的那个角点,其他的角点都删除,窗口大小这里我们用3*3,程序中通过图像的膨胀运算来达到检测极大值的目的,因为默认参数的膨胀运算就是用窗口内的最大值替代当前的灰度值。程序的最后使用了一个画角点的函数将角点显示在图像中,这个函数与本系列第5篇中画角点的函数是一致的。
int main()
{
Mat image=imread("../buliding.png");
Mat gray;
cvtColor(image,gray,CV_BGR2GRAY);
Mat cornerStrength;
cornerHarris(gray,cornerStrength,3,3,0.01);
double maxStrength;
double minStrength;
// 找到图像中的最大、最小值
minMaxLoc(cornerStrength,&minStrength,&maxStrength);
Mat dilated;
Mat locaMax;
// 膨胀图像,最找出图像中全部的局部最大值点
dilate(cornerStrength,dilated,Mat());
// compare是一个逻辑比较函数,返回两幅图像中对应点相同的二值图像
compare(cornerStrength,dilated,locaMax,CMP_EQ);
Mat cornerMap;
double qualityLevel=0.01;
double th=qualityLevel*maxStrength;
// 阈值计算
threshold(cornerStrength,cornerMap,th,255,THRESH_BINARY);
cornerMap.convertTo(cornerMap,CV_8U);
// 逐点的位运算
bitwise_and(cornerMap,locaMax,cornerMap);
drawCornerOnImage(image,cornerMap);
namedWindow("result");
imshow("result",image);
waitKey();
return 0;
}
void drawCornerOnImage(Mat& image,const Mat&binary)
{
Mat_::const_iterator it=binary.begin();
Mat_::const_iterator itd=binary.end();
for(int i=0;it!=itd;it++,i++)
{
if(*it)
circle(image,Point(i%image.cols,i/image.cols),3,Scalar(0,255,0),1);
}
}
现在我们得到的效果就比默认的函数得到的结果有相当的改善。
由于cornerHarris的一些缺点,OpenCV提供了另一个相似的函数GoodFeaturesToTrack()它用角点间的距离限制来防止角点粘连在一起。
goodFeaturesToTrack(image,corner,
500,
// 最多检测到的角点数
0.01,
// 阈值系数
10);
// 角点间的最小距离
它可以得到与上面基本一致的结果。
二、FAST特征点
harris特征在算法复杂性上比较高,在大的复杂的目标识别或匹配应用上效率不能满足要求,OpenCV提供了一个快速检测角点的类FastFeatureDetector,而实际上FAST并不是快的意思,而是Features from Accelerated Segment Test,但这个算法效率确实比较高,下面我们来看看这个类的用法。
OpenCV里为角点检测提供了统一的接口,通过类下面的detect方法来检测对应的角点,而输出格式都是vector。
vector keypoints;
FastFeatureDetector fast(
// 定义检测类
40);
//40是检测的阈值
fast.detect(image,keypoints);
drawKeypoints(image,keypoints,image,Scalar(255,0,0),
DrawMatchesFlags::DRAW_OVER_OUTIMG);
其中drawKeypoints是OpenCV提供的在图像上画角点的函数。它的参数可以让我们选择用不同的方式标记出特征点。
三、尺度不变的SURF特征
surf特征是类似于SIFT特征的一种尺度不变的特征点,它的优点在于比SIFT效率要高,在实际运算中可以达到实时性的要求,关于SURF的原理这里就不过多的介绍,网络上这类的文章很多。
类似于FAST特征点的求法,SURF也可以使用通用接口求得,而SURF特征的类为SurfFeatureDetector,类似的SIFT特征点的检测类为SiftFeatureDetector。
#include
#include
#include
using namespace cv;
int main()
{
Mat image=imread("../buliding.png");
vector keypoints;
SurfFeatureDetector surf(2500.);
surf.detect(image,keypoints);
drawKeypoints(image,keypoints,image,Scalar(255,0,0),
DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
namedWindow("result");
imshow("result",image);
waitKey();
return 0;
}
这里有一个值得说明的问题是:OpenCV2.4版本后好像把SurfFeatureDetector这个类的定义移到了头文件nonfree/features2d.hpp
中,所以头文件中要加入该文件,并且要把opencv_nonfree24xd.lib加入属性表的链接器熟悉的输入中,其中x换成你当前opencv的版本号。
最终的显示效果如下:
四、SURF特征的描述
在图像配准中,特征点的描述往往不是位置这么简单,而是使用了一个N维向量来描述一个特征点,这些描述子之间可以通过定义距离公式来比较相近程度。
SurfDescriptorExtractor 是一个提取SURF特征点以及其描述的类。
下面是一个宽景图像的拼接配准的例子:
#include
#include
#include
#include
using namespace cv;
int main()
{
Mat image1=imread("../b1.png");
Mat image2=imread("../b2.png");
// 检测surf特征点
vector keypoints1,keypoints2;
SurfFeatureDetector detector(400);
detector.detect(image1, keypoints1);
detector.detect(image2, keypoints2);
// 描述surf特征点
SurfDescriptorExtractor surfDesc;
Mat descriptros1,descriptros2;
surfDesc.compute(image1,keypoints1,descriptros1);
surfDesc.compute(image2,keypoints2,descriptros2);
// 计算匹配点数
BruteForceMatcher>matcher;
vector matches;
matcher.match(descriptros1,descriptros2,matches);
std::nth_element(matches.begin(),matches.begin()+24,matches.end());
matches.erase(matches.begin()+25,matches.end());
// 画出匹配图
Mat imageMatches;
drawMatches(image1,keypoints1,image2,keypoints2,matches,
imageMatches,Scalar(255,0,0));
namedWindow("image2");
imshow("image2",image2);
waitKey();
return 0;
}
程序中我们选择了25个配准点,得到最后的匹配如下:
========