【机器视觉学习笔记】Harris 角点检测算法(C++)
目錄
- 原理
- 算法步驟
- 優缺點
- 源碼
- 效果
- 原圖
- 輸出
平臺:Windows 10 20H2
Visual Studio 2015
OpenCV 4.5.3
本文摘自2、Harris角點檢測算法 —— 諾亞方舟369
原理
Harris算子是對Moravec算子的改進,包括:
(1)Harris算子用高斯函數代替Moravec算子的二值窗口函數,如下圖所示,窗口函數應對離中心點越的像素賦予越大的權重,以減少噪聲影響;
?????????????????????????????????????????????????????????????????????????圖1
對于圖像I(x,y),當在點(x,y)處平移(u,v)后的自相似性,可以通過自相關函數給出:
其中,w(x,y)是以點(x,y)為中心的窗口加權函數,它既可是常數(圖1左),也可以是高斯加權函數(圖1右)。高斯函數的表達式如下:
(2)Moravec算子只考慮每隔45度方向,Harris用Taylor展開去近似任意方向;
利用泰勒級數將I(x+u,y+v)展開得:
則E(u,v)表達式可以更新為:
其中,Ix為x方向的差分(一階微分近似),Iy為y方向的差分,w(x,y)為高斯函數。通過推導,E(u,v)以矩陣的形式表示為:
矩陣M為實對稱矩陣,且
則E(u,v)化簡為二次項函數:
二次項函數本質上就是一個橢圓函數。橢圓的扁率和尺寸是由M的特征值\lambda _1,\lambda _2決定的,橢圓的方向是由M的特征向量決定的,如圖2所示。
????????????????????????????????????????????????圖2
橢圓函數特征值與圖像中的角點、直線(邊緣)和平面之間的關系如圖3所示。共可分為三種情況:
1)圖像上的直線。一個特征值比較大,另外一個特征值比較小;自相關函數在某一個方向上大,在另一個方向上小。
2)圖像中的平滑區域。兩個特征值都小,且近似相等;自相關函數值子各個方向上都小。
3)圖像中的角點。兩個特征值都比較大,且近似相等,自相關函數在所有方向上都增大。
??????????????????????????????????????????????????????????????????????????????????????????圖3
(3)角點響應函數
根據二次項函數特征值的計算公式,我們可以求M矩陣的特征值。但是Harris給出的角點差別方法并不需要計算具體的特征值,而是計算一個角點響應值R來判斷角點。R的計算公式為:
其中,detM為矩陣M的行列式,traceM為矩陣M的跡,k為常數,取值范圍為0.04~0.06。事實上,特征是隱含在detM和traceM中,因為
其實,角點量R的計算方式可以自由發揮,只要能反應角點的特征即可。例如,Nobel于1988年提出利用如下公式計算角點的響應值,無需設定參數k的值:
采用上述公式計算角點的CRF值,從而避免的參數k對角點選取的影響,在實際應用中,通常選用這個改進的Harris角點檢測算法進行檢測:當cim值大于預定的閾值,則該點為角點候選點,通過非極大值抑制挑選出最終的角點。
此外,或如式(1.12)計算R值,也無需考慮參數k。
4)最后設定R的閾值進行角點判斷,以及角點的極大值抑制等。
算法步驟
輸入:源單通道圖像,參數k
輸出:角點檢測圖
具體步驟:
(1)計算圖像I(x,y)在X和Y方向的梯度Ix和Iy;
(2)計算梯度方向的乘積;
(3)使用高斯核進行加權,計算矩陣M的元素A,B,C;
(4)計算角點響應函數R,并設定閾值,當R小于閾值時,不是候選角點;
(5)進行局部極大值抑制。
算法結束。
優缺點
Harris角點檢測算法有諸多優點,但也有不完善的地方。
(1)Harris角點檢測算子具有旋轉不變性
Harris角點檢測算子使用的是角點附近的區域灰度二階矩矩陣。而二階矩矩陣可以表示成一個橢圓,橢圓的長短軸正是二階矩矩陣特征值平方根的倒數。當特征橢圓轉動時,特征值并不發生變化,所以判斷角點響應值R也不發生變化,由此說明Harris角點檢測算子具有旋轉不變性。
(2)Harris角點檢測算子對灰度平移和灰度尺度變化不敏感
這是因為在進行Harris角點檢測時,使用了微分算子對圖像進行微分運算,而微分運算對圖像亮度的抬高或下降(I=I+a)、密度的拉升或收縮(I=bI)不敏感。換言之,對亮度和對比度的仿射變換并不改變Harris響應的極值點出現的位置,但是,由于閾值的選擇,可能會影響角點檢測的數量。
(3)Harris角點檢測算子不具有尺度不變性
如圖4所示,當圖像被縮小時,在檢測窗口尺寸不變的前提下,窗口內所包含圖像的內容可能是完全不同的。左側的圖像可能被檢測為邊緣或曲線,而右側的圖像則可能被檢測為一個角點。或者說如果圖像尺度發生變化,原來是角點的點在新的尺度可能就不是角點了。
注:尺度不變性問題可通過圖像金字塔解決,例如,在運算的開始先將圖像轉化到尺度空間表示,即將原圖像進行尺度變換,而尺度變換的方式就是源圖像與尺度核函數做卷積運算:
其中,sigma表示尺度。然后使用L代替原圖像去進行運算,尺度為運算的參數。
Harris角點本身就不受光照,旋轉的影響,現在又使其滿足尺度不變性,至此,Harris角點可以成為一個優秀的特征了。
源碼
/********************************************************************************** *函數 Mat detectHarrisCorners(const Mat& imgSrc, double alpha) *輸入: *imgSrc : 源單通道圖像 *alpha : Harris響應函數參數 *輸出 *imgDst : 提取到角點的圖像 ***************************************************************************************/#include <iostream> #include "opencv2/opencv.hpp"using namespace cv; using namespace std;Mat detectHarrisCorners(const Mat& imgSrc, double alpha) {Mat gray;gray = imgSrc.clone();gray.convertTo(gray, CV_64F);Mat xKernel = (Mat_<double>(1, 3) << -1, 0, 1);//水平方向模板計算Ix Mat yKernel = xKernel.t();Mat Ix, Iy;filter2D(gray, Ix, CV_64F, xKernel);filter2D(gray, Iy, CV_64F, yKernel);Mat Ix2, Iy2, Ixy;Ix2 = Ix.mul(Ix);Iy2 = Iy.mul(Iy);Ixy = Ix.mul(Iy);Mat gaussKernel = getGaussianKernel(5, 1);//獲得高斯核size=5,sigma=1filter2D(Ix2, Ix2, CV_64F, gaussKernel);filter2D(Iy2, Iy2, CV_64F, gaussKernel);filter2D(Ixy, Ixy, CV_64F, gaussKernel);Mat cornerStrength(gray.size(), gray.type());for (int i = 0; i < gray.rows; i++){for (int j = 0; j < gray.cols; j++){double det_m = Ix2.at<double>(i, j) * Iy2.at<double>(i, j) - Ixy.at<double>(i, j) * Ixy.at<double>(i, j);//行列式double trace_m = Ix2.at<double>(i, j) + Iy2.at<double>(i, j);//跡cornerStrength.at<double>(i, j) = det_m - alpha * trace_m *trace_m;//響應函數值R}}double maxStrength;minMaxLoc(cornerStrength, NULL, &maxStrength, NULL, NULL);double qualityLevel = 0.1;double thresh = qualityLevel * maxStrength;// 設置thresholdMat dilated, localMax;//默認3 * 3核膨脹,膨脹之后,除了局部最大值點和原來相同,其它非局部最大值點被3*3鄰域內的最大值點取代dilate(cornerStrength, dilated, Mat());//與原圖相比,只剩下和原圖值相同的點,這些點都是局部最大值點,保存到localMax compare(cornerStrength, dilated, localMax, CMP_EQ);//和局部最大值圖與,剩下角點局部最大值圖,即:完成非最大值抑制Mat cornerMap;cornerMap = cornerStrength > thresh;bitwise_and(cornerMap, localMax, cornerMap);vector<Point> points;for (int y = 0; y < cornerMap.rows; y++){const uchar* rowPtr = cornerMap.ptr <uchar>(y);for (int x = 0; x < cornerMap.cols; x++){//非零點就是角點 if (rowPtr[x])points.push_back(Point(x, y));}}//畫角點Mat imgRGB = imgSrc.clone();cvtColor(imgRGB, imgRGB, COLOR_GRAY2RGB);vector<Point>::const_iterator it = points.begin();while (it != points.end()){circle(imgRGB, *it, 3, Scalar(0, 0, 255), 1);++it;}return imgRGB; }int main() {Mat gray = imread("D:\\Work\\OpenCV\\Workplace\\Test_1\\3.jpg", 0);if (!gray.data)return -1;imshow("grayImage", gray);double time0 = static_cast<double>(getTickCount());Mat HarrisImage = detectHarrisCorners(gray, 0.05);time0 = ((double)getTickCount() - time0) / getTickFrequency();cout << "runtime :" << time0 << "s" << endl;imshow("HarrisImage", HarrisImage);waitKey(0);return 0; }效果
原圖
輸出
總結
以上是生活随笔為你收集整理的【机器视觉学习笔记】Harris 角点检测算法(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android UI设计系统-andro
- 下一篇: C++ 高级数据类型(五)—— 数据结构