角点的特征检测与匹配是Computer Vision 应用总重要的一部分,这需要寻找图像之间的特征建立对应关系。点,也就是图像中的特殊位置,是很常用的一类特征,点的局部特征也可以叫做“关键特征点”(keypoint feature),或“兴趣点”(interest point),或“角点”(conrner)。
关于角点的具体描述可以有几种:
一阶导数(即灰度的梯度)的局部最大所对应的像素点;
两条及两条以上边缘的交点;
图像中梯度值和梯度方向的变化速率都很高的点;
角点处的一阶导数最大,二阶导数为零,指示物体边缘变化不连续的方向。
Harris角点检测推导:https://blog.csdn.net/lql0716/article/details/52628959
角点检测的Harris算子和ShiTomasi算子非常好的解释:https://blog.csdn.net/xiaowei_cqu/article/details/7805206
OpenCV中定义了 cornerHarris 函数:
void cornerHarris( InputArray src, OutputArray dst, int blockSize,
int ksize, double k,
int borderType=BORDER_DEFAULT );
可以结合 convertScaleAbs 函数,通过阈值取角点。
由于Shi-Tomasi算子与1994年在文章 Good Features to Track [1]中提出,OpenCV 实现的算法的函数名定义为 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 );
编译环境: Qt 5.5.1+opencv3.2.0+cmake3.5.1
#include
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
Mat image, image_gray;
int thresh = 200;
int max_thresh = 255;
char* source_window = "Source image";
char* corners_window = "Harris Courners detected";
int maxCorners = 23;
int maxTrackbar = 100;
RNG rng(12345);
Mat myHarris_dst;
Mat myHarris_copy;
Mat Mc;
Mat myShiTomasi_dst;
Mat myShiTomasi_copy;
int myShiTomasi_qualityLevel = 50;
int myHarris_qualityLevel = 50;
int max_qualityLevel = 100;
double myHarris_minVal;
double myHarris_maxVal;
double myShiTomasi_minVal;
double myShiTomasi_maxVal;
const char* myHarris_window = "my harris corner detector";
const char* myShiTomasi_window = "my Shi Tomasi corner detector";
// Function header
void cornerHarris_demo(int, void*);
void goodFeaturesToTrack_demo(int, void*);
void myShiTomasi_function(int, void*);
void myHarris_function(int, void*);
int main(int argc, char** argv)
{
image = imread(argv[1],1);
if(image.empty())
return 0;
cvtColor(image, image_gray, COLOR_BGR2GRAY);
int blockSize = 3;
int apertureSize = 3;
// my harris matrix -- usin cornerEigenValsAndVecs
myHarris_dst = Mat::zeros(image_gray.size(), CV_32FC(6));
Mc = Mat::zeros(image_gray.size(), CV_32FC1);
cornerEigenValsAndVecs(image_gray, myHarris_dst, blockSize, apertureSize, BORDER_DEFAULT);
/* calculate Mc */
for( int j=0; j<image_gray.rows; j++)
{
for(int i=0; i<image_gray.cols; i++)
{
float lambda_1 = myHarris_dst.at<Vec6f>(j,i)[0];
float lambda_2 = myHarris_dst.at<Vec6f>(j,i)[1];
Mc.at<float>(j,i) = lambda_1*lambda_2-0.04f*pow((lambda_1+lambda_2),2);
}
}
minMaxLoc(Mc, &myHarris_minVal, &myHarris_maxVal, 0, 0, Mat());
// create window and trackbar
namedWindow( myHarris_window, WINDOW_AUTOSIZE);
createTrackbar("Quality Level:", myHarris_window, &myHarris_qualityLevel, max_qualityLevel, myShiTomasi_function);
myHarris_function(0,0);
/// My Shi-Tomasi -- Using cornerMinEigenVal
myShiTomasi_dst = Mat::zeros( image_gray.size(), CV_32FC1 );
cornerMinEigenVal( image_gray, myShiTomasi_dst, blockSize, apertureSize, BORDER_DEFAULT );
minMaxLoc( myShiTomasi_dst, &myShiTomasi_minVal, &myShiTomasi_maxVal, 0, 0, Mat() );
/* Create Window and Trackbar */
namedWindow( myShiTomasi_window, WINDOW_AUTOSIZE );
createTrackbar( " Quality Level:", myShiTomasi_window, &myShiTomasi_qualityLevel, max_qualityLevel, myShiTomasi_function );
myShiTomasi_function( 0, 0 ) ;
// create window
namedWindow(source_window, WINDOW_AUTOSIZE);
createTrackbar("Threshold:", source_window, &thresh, max_thresh, cornerHarris_demo);
createTrackbar("Max corners:", source_window, &maxCorners, maxTrackbar, goodFeaturesToTrack_demo);
imshow(source_window, image);
cornerHarris_demo(0,0);
goodFeaturesToTrack_demo(0,0);
waitKey(10000);
return (0);
}
void cornerHarris_demo(int, void*)
{
Mat dst, dst_norm, dst_norm_scaled;
dst = Mat::zeros(image.size(), CV_32FC1);
int blockSize = 2;
int apertureSize = 3;
double k = 0.04;
//detecting corners
cornerHarris(image_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);
//draw a circle
for(int j=0; j<dst_norm.rows; j++)
{
for(int i = 0; i < dst_norm.cols; i++)
{
if((int)dst_norm.at<float>(j,i) > thresh)
{
circle(dst_norm_scaled, Point(i,j), 5, Scalar(0), 2, 8, 0);
}
}
}
namedWindow(corners_window, WINDOW_AUTOSIZE);
imshow(corners_window, dst_norm_scaled);
}
void goodFeaturesToTrack_demo(int, void*)
{
if (maxCorners < 1){maxCorners = 1;}
/// parameters for shi-tomasi algorithm
vector<Point2f> corners;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
bool useHarrisDetector = false;
double k = 0.04;
Mat copy;
copy = image.clone();
// apply corner detection
goodFeaturesToTrack(image_gray,
corners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
blockSize,
useHarrisDetector,
k);
// draw corners detected
cout<<"** Number of corners detected:"<<corners.size()<<endl;
int r=4;
for(int i = 0; i<corners.size(); i++)
{
circle(copy, corners[i], r, Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255)), -1, 8, 0);
}
// show
namedWindow(source_window, WINDOW_AUTOSIZE);
imshow(source_window, copy);
}
void myShiTomasi_function(int, void *)
{
myShiTomasi_copy = image.clone();
for( int j=0; j<image_gray.rows; j++ )
{
for( int i = 0; i<image_gray.cols; i++ )
{
if( myShiTomasi_dst.at<float>(j,i) > myShiTomasi_minVal+(myShiTomasi_maxVal-myShiTomasi_minVal)*myShiTomasi_qualityLevel/max_qualityLevel)
{
circle(myShiTomasi_copy, Point(i,j), 4, Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255)), -1, 8, 0);
}
}
}
imshow(myShiTomasi_window, myShiTomasi_copy);
}
void myHarris_function(int, void *)
{
myHarris_copy = image.clone();
if( myHarris_qualityLevel < 1){
myHarris_qualityLevel = 1;
}
for( int j=0; j<image_gray.rows; j++)
{
for(int i=0; i<image_gray.cols; i++)
{
if(Mc.at<float>(j,i) > myHarris_minVal+(myHarris_maxVal-myHarris_minVal)*myHarris_qualityLevel/max_qualityLevel)
{
circle( myHarris_copy, Point(i,j), 4, Scalar(rng.uniform(0,255), rng.uniform(0,255), rng.uniform(0,255)), -1, 8, 0);
}
}
}
imshow( myHarris_window, myHarris_copy);
}
openCV高斯模糊、边缘检测、灰度化、二值化、闭运算、绘制边缘
https://cloud.tencent.com/developer/article/1106887
角点检测的数学原理: https://www.cnblogs.com/riddick/p/7645904.html