1. 程式人生 > >opencv中的harris角點檢測函式

opencv中的harris角點檢測函式

函式介面

//! 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<iostream>
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
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<Point2d> 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;y<dst_normScale.rows;y++){
       unsigned char *ptr=dst_normScale.data+y*dst_normScale.cols;
       for(int x=0;x<dst_normScale.cols;x++){
           if(*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;y<dst_normScale.rows-1;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;x<dst_normScale.cols-1;x++){
           if(*ptrC>thres){
               if(ptrC[0]<ptrC[-1]||ptrC[0]<ptrC[1])
                   goto CON;
               if(ptrC[0]<ptrT[-1]||ptrC[0]<ptrT[1]||ptrC[0]<ptrT[0])
                   goto CON;
               if(ptrC[0]<ptrB[-1]||ptrC[0]<ptrB[1]||ptrC[0]<ptrB[0])
                   goto CON;
               vConners.push_back(Point2d(x,y));
           }
           CON:
           ptrC++;
           ptrB++;
           ptrT++;
       }
   }

這裡寫圖片描述