原理介紹
若我們進行圖像處理的目的不是提取用於識別的特征點而是進行幾何測量,這通常需要更高的精度,而函數 goodFeaturesToTrack() 只能提供簡單的像素的坐標值,也就是說有時會需要實數坐標值而不是整數坐標值。
角點位置特征:角點與邊緣點的連線和邊緣點的梯度方向垂直。
如上圖所示,假設一個起始角點 q 在實際亞像素角點附近。
p 點在 q 點附近的鄰域中,若 p 點在均勻區域內部,則 p 點的梯度為0;若 p 點在邊緣上,則 p 點的梯度方向垂直邊緣方向。如果向量 q-p 方向與邊緣方向一致,那么 q-p 向量與 p 點的梯度向量點積運算結果為 0 。
在初始角點(初始角點可能不在邊緣上)附近我們可以收集很多組點的梯度以及相關向量 q-p ,此時的 q 就是我們所要求的更精確角點位置,那么每一組的向量點積設置為 0 ,正是基於這個思想,將點積為 0 的等式組合起來形成一個系統方程,該系統方程的解就是更精確的亞像素角點位置。 將新的 q 點作為區域的中心,可以繼續使用這個方法進行迭代,獲得很高的精度。
尋找亞像素角點:cornerSubPix 函數
void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);
- image,輸入圖像,即源圖像。
- corners,提供輸入角點的初始坐標和精確的輸出坐標。
- winSize,搜索窗口的一半尺寸。若 winSize = Size(5,5),那就表示用(5*2+1)×(5*2+1)= 11×11 大小的搜索窗口。
- zeroZone,死區的一半尺寸。真正搜索區域為 [zeroZone*2+1 , winSize*2+1]。值為(-1,-1)表示沒有死區。
- criteria,求角點的迭代過程的終止條件。要么是迭代數大於某個設定值,要么是精度達到某個設定值,甚至可以是它們的組合。
代碼示例:
#include<opencv.hpp> #include<iostream> #include<string> #include<vector>
using namespace std; using namespace cv; Mat src, cornerImg, grayImg; int mxCorners = 10; RNG rngs = { 12345 }; void Change(int, void*) { //計算整數角點
vector<Point>corners1; vector<Point2f>corners2;
//https://www.cnblogs.com/bjxqmy/p/12459005.html goodFeaturesToTrack(grayImg, corners1, mxCorners, 0.01, 10, Mat(), 3, false, 0.04); goodFeaturesToTrack(grayImg, corners2, mxCorners, 0.01, 10, Mat(), 3, false, 0.04); //計算精確角點
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 40, 0.001); cornerSubPix(grayImg, corners2, Size(5, 5), Size(-1, -1), criteria); Mat dst = src.clone(); for (int i = 0; i < corners1.size(); i++) { cout << "[" << corners1[i].x << "," << corners1[i].y << "]" << endl; cout << "[" << corners2[i].x << "," << corners2[i].y << "]" << endl << endl; Scalar colors = Scalar(rngs.uniform(0, 255), rngs.uniform(0, 255), rngs.uniform(0, 255)); circle(dst, corners2[i], 5, colors, -1); } imshow("dst", dst); } int main() { src = imread("C:/Users/齊明洋/Desktop/示例圖片/1.jpg"); imshow("src", src); //轉換為灰度圖像
cvtColor(src, grayImg, COLOR_BGR2GRAY); namedWindow("dst"); createTrackbar("maxCorners", "dst", &mxCorners, 100, Change); Change(0, 0); waitKey(0); }
效果演示:
借鑒博客:https://blog.csdn.net/weixin_41695564/article/details/79991733