//! computes Harris cornerness criteria at each image pixel
CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize,
int ksize, double k,
int borderType=BORDER_DEFAULT );
src —— 待检测图像,单通道灰度图
dst —— 角点响应值,类型CV_32FC1
blockSize —— 论文中窗口大小,下面细说
ksize —— sobel窗口大小
k —— 原始论文里的调节参数 0.04到0.05之间
borderType —— 边界处理方式
其实写这个我想说明的就是这个blockSize和ksize的意义,查看源码
static void
cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,
int aperture_size, int op_type, double k=0.,
int borderType=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 );//参数ksize
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 );//参数blockSize
if( op_type == MINEIGENVAL )
calcMinEigenVal( cov, eigenv );
else if( op_type == HARRIS )
calcHarris( cov, eigenv, k );
else if( op_type == EIGENVALSVECS )
calcEigenValsVecs( cov, eigenv );
}
}
标出了使用两个参数的位置,其中ksize是sobel算子窗体大小就不用多说了,越大抗噪声能力越强,但模糊也更加严重。opencv用sobel来计算dx和dy,进而求得dx*dy dx*dx dy*dy,也即求得原方法中的海森矩阵。在论文中二次型参数M,是一个窗口内的海森矩阵加权和。窗口可以为方形或者高斯,opencv使用了boxFilter函数(窗口大小由blocksize控制)来实现方形窗口。论文这么实现应该主要还是为了抗噪。不要让噪声成为角点被检测出来,另外也可以用这两个参数来控制角点对比度。所以值的选取要看具体的应用场景中噪声的特性了。
#include
#include
#include
using namespace std;
using namespace cv;
int thres;
int blockSize;
int kSize;
void harrisCorner(int,void *){
Mat srcImg;
Mat grayImg,dst_norm,dst_normScale;
Mat responseImg = Mat::zeros( srcImg.size(), CV_32FC1 );
vector vConners;
//1.读取图片
srcImg=imread("box.png");
cvtColor(srcImg,grayImg,CV_RGB2GRAY);
//2.检查参数
if(blockSize<2)
blockSize=2;
if(kSize<3)
kSize=3;
//3.计算角点可能性
cornerHarris(grayImg,responseImg,blockSize,kSize,0.04); // sobel size
cv::normalize(responseImg,dst_norm,0,255,NORM_MINMAX,CV_32FC1);
cv::convertScaleAbs(dst_norm,dst_normScale);//归一化到0~255
//4.根据阈值记录角点
for(int y=0;yunsigned char *ptr=dst_normScale.data+y*dst_normScale.cols;
for(int x=0;xif(*ptr>thres){
vConners.push_back(Point2d(x,y));
}
ptr++;
}
}
//5.显示
for(auto &p : vConners){
circle(srcImg,p,5,Scalar(0,0,0));
}
imshow("showConners",srcImg);
}
int main(){
namedWindow("showConners");
thres=100;
blockSize=2;
kSize=3;
createTrackbar("thres","showConners",&thres,255,harrisCorner);
createTrackbar("blockSize","showConners",&blockSize,10,harrisCorner);
createTrackbar("kSize","showConners",&kSize,10,harrisCorner);
waitKey(0);
return 0;
}
另外一些离的很近的角点可以用非极大值抑制来消除,窗体大小可参考blockSize的大小,一种实现如下
//4.根据阈值记录角点
for(int y=1;y1;y++){
unsigned char *ptrT=dst_normScale.data+(y-1)*dst_normScale.cols;
unsigned char *ptrC=dst_normScale.data+y*dst_normScale.cols;
unsigned char *ptrB=dst_normScale.data+(y+1)*dst_normScale.cols;
for(int x=1;x1;x++){
if(*ptrC>thres){
if(ptrC[0]1]||ptrC[0]1])
goto CON;
if(ptrC[0]1]||ptrC[0]1]||ptrC[0]0])
goto CON;
if(ptrC[0]1]||ptrC[0]1]||ptrC[0]0])
goto CON;
vConners.push_back(Point2d(x,y));
}
CON:
ptrC++;
ptrB++;
ptrT++;
}
}