opencv3/C++ Harris角点、Shi-Tomasi角点&亚像素角点
角点检测在图像匹配、目标识别、目标跟踪、运动估计与三维重建等CV领域起着非常重要的作用。
角点定义
关于角点的定义有以下几种:
1、角点是两条及两条以上的边缘的交点;
2、角点处的一阶导数最大,二阶导数为零;
3、角点是一阶导数(即灰度梯度)的局部最大对应的像素点;
4、角点指示了物体边缘变化不连续的方向;
5、角点指图像梯度值和梯度方向的变化速率都很高的点;
Harris角点
Harris角点检测原理:
当一个窗口在图像上移动,在平滑区域上,窗口在各个方向没有变化(如图a),在边缘上,窗口在边缘方向上没有变化(如图b),在角点处,窗口在各个方向都有变化。
用I(x,y)表示图像灰度,w(x,y)表示图像窗口,用E(u,v)表示将窗口平移[u,v]造成的图像灰度的平均变化(自相关函数),则:
E(u,v)=∑x,yw(x,y)[I(x+u,y+v)−I(x,y)]2 E(u,v)=∑x,yw(x,y)[I(x+u,y+v)−I(x,y)]2
M为自相关函数E(x,y)的近似Hessian矩阵(M为2*2矩阵)。
设 λ1、λ2 λ1、λ2
R值越大表明该点越是角点。当R大于零且较大时对应角点,若R绝对值较小时对应平坦区域,若R较小但小于零则对应边缘。
R值与点类型的关系:
Harris角点检测算法就是对角点响应函数R进行阈值处理:R > threshold。
OpenCV3角点检测
自定义角点检测
cornerEigenValsAndVecs()计算特征值和特征向量
函数cornerEigenValsAndVecs()计算角点检测的图像块的特征值和特征向量。
对于每个像素p,函数cornerEigenValsAndVecs考虑一个 blockSize×blockSize blockSize×blockSize的特征向量
函数cornerEigenValsAndVecs()的输出可用于鲁棒的边缘或角点检测。
cornerEigenValsAndVecs()参数说明:
void cornerEigenValsAndVecs(
InputArray src, //输入单通道8位或浮点图像。
OutputArray dst,//与src具有相同的大小(CV_32FC(6)类型)
int blockSize, //邻域大小
int ksize, //Sobel算子的孔径参数
int borderType = BORDER_DEFAULT //像素外插方法
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
利用cornerEigenValsAndVecs()自定义角点检测
利用cornerEigenValsAndVecs()自定义角点检测示例:
#include<opencv2/opencv.hpp>
#include<math.h>
using namespace cv;
Mat src, gray, dst, harrisRspImg;
double harrisMinRsp;
double harrisMaxRsp;
int qualityLevel = 30;
int maxCount = 100;
void cornerTrack(int, void*);
int main()
{
src = imread("E:/image/image/bdb.jpg");
if (src.empty())
{
printf("can not load image \n");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", src);
cvtColor(src, gray, CV_BGR2GRAY);
float k = 0.04;
dst = Mat::zeros(src.size(), CV_32FC(6));
harrisRspImg = Mat::zeros(src.size(), CV_32FC1);
//计算角点检测的图像块的特征值和特征向量
cornerEigenValsAndVecs(gray, dst, 3, 3, 4);
for (int r = 0; r < dst.rows; r++)
{
for (int c = 0; c < dst.cols; c++)
{
double lambda1 = dst.at<Vec6f>(r,c)[0];
double lambda2 = dst.at<Vec6f>(r,c)[1];
harrisRspImg.at<float>(r, c) = lambda1*lambda2 - k*pow((lambda1+lambda2),2);
}
}
minMaxLoc(harrisRspImg, &harrisMinRsp, &harrisMinRsp, 0, 0, Mat());
namedWindow("output", WINDOW_AUTOSIZE);
createTrackbar("QualityValue", "output", &qualityLevel, maxCount, cornerTrack);
cornerTrack(0, 0);
waitKey(0);
return 0;
}
void cornerTrack(int, void*)
{
if (qualityLevel < 10)
{
qualityLevel = 10;
}
Mat showImage = src.clone();
float t = harrisMinRsp + ((((double)qualityLevel)/maxCount)*(harrisMaxRsp - harrisMinRsp));
for (int r = 0; r < src.rows; r++)
{
for (int c = 0; c < src.cols; c++)
{
float value = harrisRspImg.at<float>(r, c);
if (value > t)
{
circle(showImage, Point(c, r), 2, Scalar(0,255,255), 2, 8, 0);
}
}
}
imshow("output", showImage);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
亚像素角点检测
函数cornerSubPix()通过迭代找到角点或径向鞍点精确的亚像素位置。
函数cornerSubPix()参数说明:
void cornerSubPix(
InputArray image, //输入图像
InputOutputArray corners,//输入角点的初始坐标和为输出的精确坐标
Size winSize, //搜索窗口边长的一半
Size zeroZone,//搜索区域中间的死区大小的一半,(-1,-1)表示没有这样的大小。
TermCriteria criteria //终止角点优化迭代的条件
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
winSize为搜索窗口边长的一半,如果winSize = Size(5,5),则使用 5∗2+1×5∗2+1=11×11 5∗2+1×5∗2+1=11×11大小的搜索窗口。
实例:
#include<opencv2/opencv.hpp>
using namespace cv;
Mat src, gray, dst;
int maxCorners = 10;
void SubPixels(int, void*);
int main()
{
src = imread("E:/image/image/bdb.jpg");
if (src.empty())
{
printf("can not load image \n");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", src);
cvtColor(src, gray, CV_BGR2GRAY);
namedWindow("output", WINDOW_AUTOSIZE);
createTrackbar("corners", "output", &maxCorners, 200, SubPixels);
waitKey(0);
return 0;
}
void SubPixels(int, void*)
{
if (maxCorners < 5)
{
maxCorners = 5;
}
std::vector<Point2f> corners;
double qualityLevel = 0.01;
//获取角点
goodFeaturesToTrack(gray, corners, maxCorners, qualityLevel, 10, Mat(), 3, false, 0.04);
//清除控制台显示
system("cls");
printf("number of corners: %d \n", corners.size());
Mat result = src.clone();
for (int i = 0; i < corners.size(); i++)
{
circle(result, corners[i], 2, Scalar(0,255,0),2,8,0);
}
TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
//计算并打印出亚像素角点
cornerSubPix(gray, corners, Size(5,5), Size(-1,-1), tc);
for (int i = 0; i < corners.size(); i++)
{
printf("%d point(x,y) = (%f, %f) \n", i+1,corners[i].x,corners[i].y);
}
imshow("output", result);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
Harris角点检测
R=det(M)−k(tr(M))2 R=det(M)−k(tr(M))2
参数k默认0.04~0.06
cornerHarris()函数参数说明
void cornerHarris(
InputArray src, //输入图像(8位单通道图像或浮点图像)
OutputArray dst, //用于存储harris探测器响应的图像(大小与src相同,类型为CV_32FC1)
int blockSize,//邻域大小
int ksize, //Sobel算子的孔径参数
double k,//计算角度响应时候的参数大小(默认0.04~0.06)
int borderType = BORDER_DEFAULT //像素外插方法
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
Harris角点检测示例:
#include<opencv2/opencv.hpp>
using namespace cv;
Mat src , gray;
int threSet = 130;
void harris(int, void*);
int main()
{
src = imread("E:/image/image/bdb.jpg");
if (src.empty())
{
printf("can not load image \n");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", src);
cvtColor(src, gray, CV_BGR2GRAY);
namedWindow("output", WINDOW_AUTOSIZE);
createTrackbar("threshold:", "output", &threSet, 255, harris);
harris(0,0);
waitKey(0);
return 0;
}
void harris(int, void*)
{
Mat dst, normdst;
dst = Mat::zeros(gray.size(), CV_32FC1);
//Haar角点检测,结果存放到dst
cornerHarris(gray, dst, 2, 3, 0.04, BORDER_DEFAULT);
//规范数组的值范围
normalize(dst, dst, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
//缩放,计算绝对值,并将结果转换为8位
convertScaleAbs(dst, dst);
Mat result = src.clone();
//画出角点检测结果
for (int r = 0; r < result.rows; r++)
{
uchar* currentRow = dst.ptr(r);
for (int c = 0; c < result.cols; c++)
{
int value = (int)*currentRow;
if (value > threSet)
{
circle(result, Point(c,r), 2, Scalar(0,255,0), 2, 8, 0);
}
currentRow++;
}
}
imshow("output", result);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
Shi-Tomasi角点检测
与Harris角点检测的不同在于在使用矩阵特征值 λ1λ2 λ1λ2
goodFeaturesToTrack()函数参数说明
函数功能:查找图像或指定图像区域中最突出的角点.。
goodFeaturesToTrack()角点检测参数说明:
void goodFeaturesToTrack(
InputArray image, //输入单通道8位或32位浮点型图像。
OutputArray corners,//输出检测到的角点
int maxCorners, //要返回的最大角点数
double qualityLevel, //图像角点的品质因子
double minDistance,//角点之间的最小距离(删除该范围内更强的角点)
InputArray mask = noArray(), //感兴趣区
int blockSize = 3,//计算协方差矩阵时的窗口大小
bool useHarrisDetector = false, //是否使用Harris角点检测(默认计算shi-tomasi角点)
double k = 0.04 //Harris角点检测需要的k值
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Shi-Tomasi角点检测示例:
#include<opencv2/opencv.hpp>
using namespace cv;
void trackBar(int, void*);
int thre = 0;
Mat src, dst;
int main()
{
src = imread("E:/image/bdb.jpg");
if (src.empty())
{
printf("can not load image \n");
return -1;
}
namedWindow("input");
namedWindow("output");
imshow("input", src);
cvtColor(src, dst, COLOR_BGR2GRAY);
destroyWindow("output");
namedWindow("output");
createTrackbar("threshold:","output",&thre,250,trackBar);
waitKey();
return 0;
}
void trackBar(int, void*)
{
std::vector<Point2f> corners;
goodFeaturesToTrack(dst,corners, thre, 0.01, 10, Mat());
for (int i = 0; i < corners.size(); i++)
{
circle(src, corners[i], 2, Scalar(0,255,255), 2);
}
imshow("output", src);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37