harris 是在moravec corner基础上提出来。moravec值计算了45°方向的角点,而harris则计算的是所有方向的;这是因为harris将moravec中的ssd函数进行了转换,将:
进行替换后,ssd方程可以变换为hessian矩阵:
通过对矩阵A特征值的分析,可以得到我们需要的信息;有三种情况:
1 两个特征值都很小,这意味着,图像f在当前点平坦,变化方向有极大的不确定性;
2 一个特征值很大,另一个特征值很小,局域邻域呈现屋脊状,可以认为是边缘;
3 两个特征值都很大,任何方向的微小移动,都会造成图像f的显著变化,此时认为检测到了角点;
harris建议的确定角点的函数:其中 det(A)为行列式的值,trace(A)为矩阵的迹;可以认为是特征值的乘积与特征值的和的比例因子的确定;
#pragma once #include "opencv2/opencv.hpp" #include <vector> class CHarrisCorner { private : std::vector<cv::Point> corners; int threshold; public: CHarrisCorner(void); ~CHarrisCorner(void); cv::Mat detect_corners(const cv::Mat &img,int winsize=3,float factor=0.05,float threshold=5) { cv::Mat src; cv::Mat dst(img.rows,img.cols,CV_8UC1); cv::cvtColor(img,src,cv::COLOR_RGB2GRAY); //转换为灰度图像 cv::GaussianBlur(src,src,cv::Size(winsize,winsize),0); //进行高斯滤波 float dxx=0;float dyy=0; float dxy=0;float dyx=0;float d=0; for(int x=1;x<(src.rows-1);x++) //求取每一个像素出的hessian矩阵 { for(int y=1;y<(src.cols-1);y++) { d=src.at<uchar>(x,y); dxx=(src.at<uchar>(x+1,y)+src.at<uchar>(x-1,y)-2*d); dyy=(src.at<uchar>(x,y+1)+src.at<uchar>(x,y-1)-2*d); dxy=dyx=(src.at<uchar>(x+1,y+1)+src.at<uchar>(x-1,y-1)-src.at<uchar>(x+1,y-1)-src.at<uchar>(x-1,y+1)); //对hessian矩阵进行判断 double num=abs(((dxx*dyy-pow(dxy,2))-factor*pow((dxx+dyy),2))); if(num>100) { dst.at<uchar>(x,y)=src.at<uchar>(x,y); } //一种简易方法 //if(dxx>threshold&&dyy>threshold) //直接对hessian主变化分量进行判断 //{ // corners.push_back(cv::Point(x,y)); //} //调和均值 if((dxx*dyy-pow(dxy,2))/(dxx+dyy)>100) { corners.push_back(cv::Point(y,x)); } } } cv::imshow("and",dst); //进行非极大值抑制 cv::Mat dilate; //图像膨胀 //for(int i=0;i<10;i++) { cv::dilate(src,dilate,cv::Mat()); } cv::Mat result; cv::compare(dst,dilate,result,cv::CMP_EQ); /*for(int height=0;height<result.rows;height++) { for(int width=0;width<result.cols;width++) { if(result.at<uchar>(width,height)!=0) corners.push_back(cv::Point(width,height)); } }*/ std::cout<<corners.size()<<std::endl; imshow("vs",result); return result; } void show_corners(cv::Mat &img) { std::vector<cv::Point>::iterator it=corners.begin (); for(;it!=corners.end();it++) { cv::circle(img,*it,3,cv::Scalar(0,255,0),1); } } };