Opencv特征提取与目标检测04:亚像素级角点检测

具体概念

无论是Harris角点检测,Shi-Tomasi角点检测都无法对像素点精准定位,进而无法满足一些高精度图像角点处理,追踪的问题。如跟踪。相机矫正,三维重建,几何测量等。正如图所描述的。

qt opencv目标检测 opencv目标识别与定位_opencv


因此,亚像素级别角点检测应运而生。

亚像素

面阵摄像机的成像面以像素为最小单位。例如某CMOS摄像芯片,其像素间距为5.2微米。摄像机拍摄时,将物理世界中连续的图像进行了离散化处理。到成像面上每一个像素点只代表其附近的颜色。至于“附近”到什么程度?就很困难解释。两个像素之间有5.2微米的距离,在宏观上我们可以看作是连在一起的。但是在微观上,它们之间还有无限的更小的东西存在。这个更小的东西我们称它为“亚像素”。实际上“亚像素”应该是存在的,只是硬件上没有个细微的传感器把它检测出来。于是软件上把它近似地计算出来。

亚像素的精度

亚像素精度是指相邻两像素之间细分情况。输入值通常为二分之一,三分之一或四分之一。这意味着每个像素将被分为更小的单元从而对这些更小的单元实施插值算法。例如,如果选择四分之一,就相当于每个像素在横向和纵向上都被当作四个像素来计算。因此,如果一张5x5像素的图像选择了四分之一的亚像素精度之后,就等于创建了一张20x20的离散点阵,进而对该点阵进行插值。

亚像素定位

qt opencv目标检测 opencv目标识别与定位_opencv_02


我们已经知道,除了利用 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类型,求角点的迭代过程的终止条件。
);

这里重点记录一下corners这个参数,必须是包含了角点检测后角点响应的初始坐标,如用ShiTomasi角点检测后输出的corners,或是经过保存所有大于阈值Thresh的Harris角点检测输出值后的resultImage

看其他博客,记录的一个API,用于寻找亚像素角点的坐标。

void cvFindCornerSubPix(
const CvArr* image, 
CvPoint2D32f* corners, 
int count, 
CvSize win, 
CvSize zero_zone, 
CvTermCriteria criteria
);

代码演示精度上的不同

// Opencv 文件.cpp :
#include "pch.h"
#include<opencv2/opencv.hpp>
#include<math.h>
#include <iostream>
using namespace cv;
using namespace std;
Mat result;//存储响应的R
int blocksize = 3;//矩阵M的大小
double k = 0.04;//响应系数
int ksize = 3;//窗口
double qualityLevel = 0.01;
int maxCorners = 20;
int max_count = 50;
double minDistance = 10;

int sm_qualityLevel=30;
void cornerShit_tomasi_demo(int, void*);

Mat gray;
Mat src1;
char OUTPUT_shitomasi_WIN[] = "corner_shitomasi_demo";
int main()
{
	src1 = imread("E:\\360downloads\\core.jpg");
	if (!src1.data) {//!src.data与src.empty()一样;
		printf("failure to load the image1 ");
		return -1;
	}
	

	namedWindow(OUTPUT_shitomasi_WIN, WINDOW_AUTOSIZE);
	imshow("src1", src1);

	cvtColor(src1, gray, COLOR_BGR2GRAY);

	createTrackbar("max_corners:", OUTPUT_shitomasi_WIN, &maxCorners, max_count, cornerShit_tomasi_demo);
	cornerShit_tomasi_demo(0, 0);

	waitKey(0);



	return 0;
}
//shitomasi
void cornerShit_tomasi_demo(int, void*) {
	if (maxCorners < 5) {
		maxCorners = 5;
	}
	vector<Point2f>shitomasi_corner1;
	goodFeaturesToTrack(gray, shitomasi_corner1, maxCorners, qualityLevel, minDistance, Mat(), blocksize, false, k);
	Mat resultImage = src1.clone();
	cout << "the corners' size = " << shitomasi_corner1.size() << endl;
	for (size_t t= 0; t < shitomasi_corner1.size(); t++) {

		circle(resultImage, shitomasi_corner1[t], 2, Scalar(0, 0, 255), 2, 8);

	}
	vector<Point2f>shitomasi_corner2 = shitomasi_corner1;

	//开始做亚像素角点检测
	Size winsize = Size(5, 5);
	Size zerozone = Size(-1,-1);
	TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.0001);
	cornerSubPix(gray, shitomasi_corner1, winsize, zerozone, tc);
	//cout << "the number of the cornerSubpixel is \n";
	for (size_t t = 0; t < shitomasi_corner1.size(); t++) {
		
		cout << "第"<<(t + 1)<<"个"<<"point[x,y] "<< "corner_Shi - Tomasi: " 
			<< "["<<shitomasi_corner2[t].x << "," << shitomasi_corner2[t].y<<"]" << "  cornerSubpix:  "
			<< "["<<shitomasi_corner1[t].x << "," << shitomasi_corner1[t].y << "]" << endl;
	}


	imshow(OUTPUT_shitomasi_WIN, resultImage);

}

qt opencv目标检测 opencv目标识别与定位_角点检测_03


我们可以看到,用Shi-Tomasi角点检测得到的基本是整数,而用亚像素级别检测,精度显然提高,经过我的测试,最多支持4位小数,精度设置再小,至多也是4位小数。