前言:
- 我们知道,在OpenCV中已经为我们提供了相关函数——cornerHarris() 函数和 goodFeaturesToTrack()函数,来实现Harris角点检测和Shi-Tomasi角点检测,
- 除此之外,其实我们也可以根据算法的原理和需求来制作角点检测的函数。例如:
使用cornerEigenValsAndVecs()函数和minMaxLoc()函数结合来模拟Harris角点检测,
或者使用cornerMinEigenVal()函数和minMaxLoc()函数结合来模拟Shi-Tomasi角点检测,
最后特征点选取的判断条件要根据实际情况进行选择。
自定义角点检测器
- 基于 Harris 与 Shi-Tomasi 角点检测
- 首先通过计算矩阵 M 得到 λ λ λ1 λ λ λ2 两个特征值根据他们得到角点响应值
- 然后自己设置阈值实现计算出阈值得到有效响应值的角点位置
相关API说明
1. cornerEigenValsAndVecs()函数
cornerEigenValsAndVecs()函数用来求解输入图像矩阵的特征向量与特征值,其函数原型如下:
void cornerEigenValsAndVecs(
InputArray src, --单通道输入8位或浮点图像
OutputArray dst, --输出图像,同源图像或CV_32FC(6)
int blockSize, --邻域大小值
int apertureSize, --Sobel算子当中的核大小
int borderType=BORDER_DEFAULT --像素外插方法
)//对应于Harris
其中:第二个参数 dst:输出矩阵,即用来存储结果的图像,大小与输入图像一致并且为CV_32FC(6)类型。计算自相关矩阵M的特征值和特征向量,并将它们以(λ1, λ2, x1, y1, x2,y2)的形式存储在目标图像dst中。其中λ1, λ2是M未经过排序的特征值;x1, y1是对应于λ1的特征向量;x2, y2是对应于λ2的特征向量。因此是6通道的矩阵。
2. cornerMinEigenVal()函数
功能与cornerEigenValsAndVecs()函数相似,但是它只计算导数协方差矩阵(即自相关矩阵)的最小特征值,其函数原型如下:
void cornerMinEigenVal(
InputArray src, --单通道输入8位或浮点图像
OutputArray dst, --图像存储的最小特征值。类型为CV_32FC1
int blockSize, --邻域大小值
int apertureSize=3, --Sobel算子的参数
int borderType=BORDER_DEFAULT --像素外插方法
}//对应Shi-Tomasi
3.minMaxLoc()函数介绍
- minMaxLoc(): minMaxLoc()函数寻找输入矩阵(在c++中就是Mat对象)中的最小值和最大值,同时可以其相应值的位置(Point对象)。其函数声明如下:
–minMaxLoc()的API介绍来自:https://blog.csdn.net/sinat_36264666/article/details/78754897
1. cornerEigenValsAndVecs()函数、自定义角点检测器_harris
程序代码:
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
const char* harris_win = "Custom Harris Corners Detector";
Mat src, gray_src;
Mat harris_dst, harrisRspImg;
double harris_min_rsp;// harris 角度响应最小值
double harris_max_rsp;// harris 角度响应最大值
// quality level
int qualityLevel = 30;
int max_count = 100;
void CustomHarris_Demo(int, void*);
int main(int argc, char** argv) {
src = imread("E:/Experiment/OpenCV/Pictures/cornerHarrisTest.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
namedWindow(harris_win, CV_WINDOW_AUTOSIZE);
imshow("input image", src);
cvtColor(src, gray_src, COLOR_BGR2GRAY);
// 计算特征值 eigon values λ1 λ2
int blockSize = 3;// blockSize取2或取3,会导致计算出来的角度响应的最大最小值不一样,下面的阈值需要适当调整
int ksize = 3;
double k = 0.04;
harris_dst = Mat::zeros(src.size(), CV_32FC(6));// 深度为 CV_32F 通道数为6 ,通道数不一定要6 大于等于2就行
harrisRspImg = Mat::zeros(src.size(), CV_32FC1);
cornerEigenValsAndVecs(gray_src, harris_dst, blockSize, ksize, 4);// 计算 λ1 λ2
// 计算角度响应
for (int row = 0; row < harris_dst.rows; row++) {
for (int col = 0; col < harris_dst.cols; col++) {
double lambda1 =harris_dst.at<Vec6f>(row, col)[0]; // λ1
double lambda2 = harris_dst.at<Vec6f>(row, col)[1]; // λ2
// harris角度响应计算,公式 R = det(M) - k*(trace(M))^2, det(M)=λ1*λ2, trace(M)=λ1+λ2
harrisRspImg.at<float>(row, col) = lambda1*lambda2 - k*pow((lambda1 + lambda2), 2);
}
}
//寻找harrisRspImg中角度响应最大值,最小值,及它们所在的位置,位置参数传0表示不管
minMaxLoc(harrisRspImg, &harris_min_rsp, &harris_max_rsp, 0, 0, Mat());
printf("harris_min_rsp=%f, harris_max_rsp=%f\n", harris_min_rsp, harris_max_rsp); //harris_min_rsp=-0.016178, harris_max_rsp=0.047225
createTrackbar("harris thresh:", harris_win, &qualityLevel, max_count, CustomHarris_Demo);
CustomHarris_Demo(0, 0);
waitKey(0);
return 0;
}
void CustomHarris_Demo(int, void*) {// 自定义harris角点检测,与harris函数计算的结果差不多
if (qualityLevel < 10) {
qualityLevel = 10;
}
Mat resultImg = src.clone(); // 完全拷贝
//定义阈值, 以 thresh_v_harris / thresh_max_harris 的比例显示角点
float t = harris_min_rsp + (((double)qualityLevel) / max_count)*(harris_max_rsp - harris_min_rsp);
for (int row = 0; row < src.rows; row++) {
for (int col = 0; col < src.cols; col++) {
float v = harrisRspImg.at<float>(row, col);
if (v > t) {
// 角度响应大于阈值,在当前位置显示出来,当角度响应阈值设的很低时,因为绘制的多,所以绘制速度会很慢
circle(resultImg, Point(col, row), 2, Scalar(0, 0, 255), 2, 8, 0);
}
}
}
imshow(harris_win, resultImg);
}
运行截图:
2. cornerMinEigenVal()函数、自定义角点检测器_ShiTomasi
程序代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
using namespace cv;
using namespace std;
const char* shitomasi_win = "Custom Shi-Tomasi Corners Detector";
Mat src, gray_src, shiTomasiRsp;
double shitomasi_min_rsp;// tomasi 角度响应最小值
double shitomasi_max_rsp;// tomasi 角度响应最大值
int sm_qualitylevel = 30;
int max_count = 100;
void CustomShiTomasi_Demo(int, void*);
int main(int argc, char** argv) {
src = imread("E:/Experiment/OpenCV/Pictures/ShiTomasiTest.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
namedWindow(shitomasi_win, CV_WINDOW_AUTOSIZE);
imshow("input image", src);
cvtColor(src, gray_src, COLOR_BGR2GRAY);
// 计算特征值
int blockSize = 3;
int ksize = 3;
double k = 0.04;
shiTomasiRsp = Mat::zeros(src.size(), CV_32FC1);
//计算最小特征值,即ShiTomasi角度响应值 R = min(λ1, λ2)
cornerMinEigenVal(gray_src, shiTomasiRsp, blockSize, ksize, BORDER_DEFAULT);
minMaxLoc(shiTomasiRsp, &shitomasi_min_rsp, &shitomasi_max_rsp, 0, 0, Mat());//寻找tomasiRspImg中角度响应最大值,最小值,及它们所在的位置,位置参数传0表示不管
printf("tomasi_min_rsp=%f, tomasi_max_rsp=%f\n", shitomasi_min_rsp, shitomasi_max_rsp);//tomasi_min_rsp=-0.000000, tomasi_max_rsp=0.192643
createTrackbar("Quality:", shitomasi_win, &sm_qualitylevel, max_count, CustomShiTomasi_Demo);
CustomShiTomasi_Demo(0, 0);
waitKey(0);
return 0;
}
void CustomShiTomasi_Demo(int, void*) {
if (sm_qualitylevel < 20) {
sm_qualitylevel = 20;
}
Mat resultImg = src.clone();
//定义阈值, 以 thresh_v_tomasi / thresh_max_tomasi 的比例显示角点
float t = shitomasi_min_rsp + (((double)sm_qualitylevel) / max_count)*(shitomasi_max_rsp - shitomasi_min_rsp);
for (int row = 0; row < src.rows; row++) {
for (int col = 0; col < src.cols; col++) {
float v = shiTomasiRsp.at<float>(row, col);// shiTomasi角点检测出的角度响应值有很多为0,所以减少了circle绘制,所以程序速度比harris要快
if (v > t) {
circle(resultImg, Point(col, row), 2, Scalar(0, 0, 255), 2, 8, 0);
}
}
}
imshow(shitomasi_win, resultImg);
}