OpenCV-特征提取与检测(03、亚像素级别角点检测)
亚像素
- 面阵摄像机的成像面以像素为最小单位。例如某CMOS摄像芯片,其像素间距为5.2微米。摄像机拍摄时,将物理世界中连续的图像进行了离散化处理。到成像面上每一个像素点只代表其附近的颜色。至于“附近”到什么程度?就很困难解释。两个像素之间有5.2微米的距离,在宏观上可以看作是连在一起的。但是在微观上,它们之间还有无限的更小的东西存在。这个更小的东西我们称它为“亚像素”。实际上“亚像素”应该是存在的,只是硬件上没有个细微的传感器把它检测出来。于是软件上把它近似地计算出来。
-
亚像素的精度:
亚像素精度是指相邻两像素之间细分情况。输入值通常为二分之一,三分之一或四分之一。这意味着每个像素将被分为更小的单元从而对这些更小的单元实施插值算法。例如,如果选择四分之一,就相当于每个像素在横向和纵向上都被当作四个像素来计算。因此,如果一张5x5像素的图像选择了四分之一的亚像素精度之后,就等于创建了一张20x20的离散点阵,进而对该点阵进行插值。
亚像素级别角点检测
- 提高检测精准度
理论与现实总是不一致的,实际情况下几乎所有的角点不会是一个真正的
准确像素点。(100, 5) 实际上(100.234, 5.789) - 亚像素定位
- 插值方法
- 基于图像矩计算
- 曲线拟合方法 -(高斯曲面、多项式、椭圆曲面)
- 除了利用 Harris进行角点检测 和利用 Shi-Tomasi方法进行角点检测 外, 还可以使用cornerEigenValsAndVecs()函数和cornerMinEigenVal()函数自定义角点检测函数。 如果对角点的精度有更高的要求,可以用cornerSubPix()函数将角点定位到子像素,从而取得亚像素级别的角点检测效果。
相关API介绍- cornerSubPix()函数
函数goodFeaturesToTrack()函数只能提供简单的像素的坐标值,也就是说,有时候会需要实数坐标值而不是整数坐标值。在OpenCV中,就提供了一个cornerSubPix()函数,用于寻找亚像素角点的位置,其函数声明如下:
void cornerSubPix(
InputArray image, --输入图像,即源图像;
InputOutputArray corners, --提供输入角点的初始坐标和精确的输出坐标
Size winSize, --Size类型,表示搜索窗口的半径。若winSize=Size(5,5),那么就表示使用(5*2+1)x(5*2+1)=11*11大小的搜索窗口。
Size zeroZone, -- Size类型,表示死区的一半尺寸。而死区为不对搜索区的中央位置做求和运算的区域,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
TermCriteria criteria --TermCriteria类型,求角点的迭代过程的终止条件。
);
- 还有一个API,可以了解一下
void cvFindCornerSubPix(
const CvArr* image,
CvPoint2D32f* corners,
int count,
CvSize win,
CvSize zero_zone,
CvTermCriteria criteria
);
程序代码:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 全局变量
int max_corners = 20;
int max_count = 50;// 亚像素定位,浮点数运算,比较耗时
Mat src, gray_src;
const char* output_title = "SubPixel Result";
void SubPixel_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);
imshow("input image", src);
// 载入图像并灰度化
cvtColor(src, gray_src, COLOR_BGR2GRAY);
// 创建显示窗口以及滑动条
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
createTrackbar("Corners:", output_title, &max_corners, max_count, SubPixel_Demo);
SubPixel_Demo(0, 0);
waitKey(0);
return 0;
}
// 使用Shi-Tomasi方法检测角点,再对角点位置进行精准化
void SubPixel_Demo(int, void*) {
if (max_corners < 5) {
max_corners = 5;
}
// Shi-Tomasi的参数设置
vector<Point2f> corners;
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
double k = 0.04;
// 应用Shi-Tomasi角点检测算法
goodFeaturesToTrack(gray_src, corners, max_corners, qualityLevel, minDistance, Mat(), blockSize, false, k);
cout << "number of corners: " << corners.size() << endl;
// 深度拷贝原图像用于绘制角点
Mat resultImg = src.clone();
for (size_t t = 0; t < corners.size(); t++) {
cout << t << ".point[x,y]=" << corners[t].x << "," << corners[t].y << endl; // 整数值
circle(resultImg, corners[t], 2, Scalar(0, 0, 255), 2, 8, 0);
}
imshow(output_title, resultImg);
// 角点位置精准化参数
Size winSize = Size(5, 5); // 窗口不要太大,不然亚像素可能会被干扰
Size zerozone = Size(-1, -1); // 拟合时使用 零区域
//TermCriteria类是用来作为迭代算法的终止条件的,参数:类型(EPS表示迭代到阈值终止),第二个参数为迭代的最大次数,最后一个是特定的阈值
TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
// 计算精准化后的角点位置
cornerSubPix(gray_src, corners, winSize, zerozone, tc);// 亚像素定位,为后续计算提供更高精确度的值
cout << "subpixel corners.size=" << corners.size() << endl;
for (size_t t = 0; t < corners.size(); t++) {
cout << (t + 1) << " .point[x, y] = " << corners[t].x << " , " << corners[t].y << endl; // 亚像素定位出来的,精确度较高,浮点值
}
return;
}
运行截图
参考博客
- https://blog.****.net/pengjc2001/article/details/55095023 (亚像素与halcon)
- https://blog.****.net/CHNguoshiwushuang/article/details/81155361 (亚像素)
- https://blog.****.net/weixin_41695564/article/details/79991733 (亚像素角点检测)
- https://blog.****.net/holybin/article/details/41122493 (亚像素级角点检测(cornerSubPix))
- https://blog.****.net/huanghuangjin/article/details/81268227 (亚像素级别角点检测)