亚像素级别角点检测
亚像素:在生成数字图像处理时(拍照等)我们是将物理世界中连续的图像进行了离散化处理。现实世界中颜色为连续的且有无数种类,成像到像素面上每一个像素点只代表其附近的颜色,我们常使用的3通道图像颜色种类(255*255*255)代表,至于“附近”到什么程度?就很困难解释。两个像素之间有5.2微米的距离,在宏观上可以看作是连在一起的。但是在微观上,它们之间还有无限的更小的东西存在。这个更小的东西我们称它为“亚像素”。实际上“亚像素”应该是存在的,只是硬件上没有个细微的传感器把它检测出来。于是软件上把它近似地计算出来。
亚像素级别角点检测,提高检测精准度;由于理论与现实总是不一致的,实际情况下几乎所有的角点不会是一个真正的准确像素点。例如(100, 5) 实际上(100.234, 5.789)。
特别是在:跟踪,三维重建,相机校正方面,为了获取更加精准的角点,这样一来就需要亚像素级别角点检测。
亚像素定位方法
- 插值方法
- 基于图像矩计算
- 曲线拟合方法 -(高斯曲面、多项式、椭圆曲面)
除了利用 Harris和 Shi-Tomasi方法进行角点检测 外, 还可以使用cornerEigenValsAndVecs() 函数和 cornerMinEigenVal() 函数自定义角点检测函数。 详情查看点击
如果对角点的精度有更高的要求,可以用 cornerSubPix() 函数将角点定位到子像素,从而取得亚像素级别的角点检测效果。
函数简介
函数goodFeaturesToTrack()函数只能提供简单的像素的整数坐标值,若需要实数坐标值则需要使用cornerSubPix()函数,用于寻找亚像素角点的位置:
-
void cornerSubPix(
-
InputArray image, 输入图像;
InputOutputArray corners, 初始化输入角点与精确的输出坐标
Size winSize, 搜索窗口半径。若Size(5,5),表示(5*2+1)*(5*2+1)=11*11大小的搜索窗口。
Size zeroZone, 表示死区的一半尺寸。为不对搜索区的中央位置做求和运算,用来避免自相关矩阵出现的某些可能的奇异性。值为(-1,-1)表示没有死区。
TermCriteria criteria 求角点的迭代过程的终止条件。
);- 其中:
cv::TerCriteria::MAX_ITER :迭代终止条件为达到最大迭代次数终止
cv::TerCriteria::EPS : 迭代到阈值终止
cv::TerCriteria::MAX_ITER+cv::TerCriteria::EPS :两者都作为迭代终止条件
- 其中:
头文件 image_feature_all.h
:声明类与公共函数
#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
class ImageFeature {
public:
void subpixel_corner_demo(Mat& image);
};
主函数main.cpp
调用该类的公共成员函数
#include "image_feature_all.h"
int main(int argc, char** argv) {
const char* img_path = "D:\\Desktop\\jianzhu.jpg";
Mat image = imread(img_path);
if (image.empty()) {
cout << "图像数据为空,读取文件失败!" << endl;
}
ImageFeature imgfeature;
imgfeature.subpixel_corner_demo(image);
imshow("image", image);
waitKey(0);
destroyAllWindows();
return 0;
}
演示像素坐标检测
检测出角点,再拟合亚像素级别角点位置
源文件 feature_extract.cpp
:实现类与公共函数
static void on_subpixel(int num_corner, void* userdata) {
Mat image = *((Mat*)userdata);
Mat gray_src;
cvtColor(image, gray_src, COLOR_BGR2GRAY);
if (num_corner < 5) { num_corner = 5; }
//角点检测Shi-Tomasi
vector<Point2f> corners;
double qualityLevel = 0.03;
double minDistance = 10;
int blockSize = 3;
bool userHarris = false;
double k = 0.04;
goodFeaturesToTrack(gray_src, corners, num_corner,
qualityLevel, minDistance, Mat(), blockSize, userHarris, k);
cout << "corners.size() = " << corners.size() << endl;
//可视化
RNG rng(12345);
Mat resultImg = gray_src.clone();
cvtColor(resultImg, resultImg, COLOR_GRAY2BGR);
for (size_t i = 0; i < corners.size(); i++){
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
circle(resultImg, corners[i], 2, color, 1, 8, 0);
}
imshow("subpixel", resultImg);
//拟合亚像素角点位置并计算亚像素角点位置
Size winSize = Size(5, 5);
Size zerozone = Size(-1, -1);
TermCriteria tc = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001);
cornerSubPix(gray_src, corners, winSize, zerozone, tc);
//打印精细坐标
for (size_t i = 0; i < corners.size(); i++){
cout << (i + 1) << ".point[x,y]\t" << corners[i].x << "," << corners[i].y << endl;
}
return;
}
void ImageFeature::subpixel_corner_demo(Mat& image) {
cv::namedWindow("subpixel", WINDOW_NORMAL);
int num_corner = 100;
int max_corner = 500;
createTrackbar("CorNum", "subpixel", &num_corner, max_corner, on_subpixel, (void*)&image);
on_subpixel(0, &image);
}