目錄
- 原理講解
- 【1】為何選取角點作為特征?
- 【2】角點的定義:
- 【3】判斷角點的方法:
- 【4】Harris角點檢測法
- 示例
- Opencv自帶函數:cornerHarris()函數
- 示例程式1
- 示例程式2
原理講解
【1】為何選取角點作為特征?
角點是一種局部特征。
角落上的可區分性特别強,邊緣次之,平滑區域則基本沒有區分性。
【2】角點的定義:
【3】判斷角點的方法:
這裡有個細節:将計算的所有方向上的變化值平方和的最小值作為像素點的灰階變化特征值。為何是最小值呢?
分别對平均區域、邊緣區域、角落區域進行計算,觀察結果:
取最小值,這樣邊緣的特征值為0了,将邊緣與角點區分開來。(邊緣的特點是一個方向變化值不明顯,與之垂直的方向變化值明顯)
這種方法的缺點:
滑動視窗缺點:視窗滑動隻有8個方向,當邊緣角落的角度不在這8個方向上則檢測不準。
【4】Harris角點檢測法
Harris角點檢測法使用特征值的方式,使得任何方向上的角點都可以被檢測出來。
數學定義:
權重函數形式:高斯分布形式、均值函數形式
公式繼續化簡:
泰勒公式回顧:
由于圖像是二進制函數,這裡隻取,x和y方向上的一階導數做近似。
海森矩陣:
平原地區,四周望去皆是平坦,最陡峭和最不陡峭的地方陡峭程度差不多。
懸崖地區,水準方向上望去很平坦,從其垂直方向看去,十分陡峭。
站立山尖,四周皆是十分陡峭。
R稱之為響應函數,k根據經驗取0.02~0.04左右
示例
Opencv自帶函數:cornerHarris()函數
void cornerHarris( InputArray src, OutputArray dst, int block Size, int ksize, double k, int borderType = BORDER_DEFAULT)
1.InputArray類型的src,輸入圖像,即原圖像,填Mat類型即可,且需要為單通道8位或者浮點型圖像;
2.OutputArray類型的dst,函數調用後的運算結果存在這裡,即這個參數用于存放Harris角點檢測的輸出結果,和原圖檔有一樣的尺寸和類型;
3.int類型的blockSize,表示鄰域的大小,更多詳細資訊在cornerEigenValsAndVecs()中講到;
4.int類型的ksize,表示Sobel()算子的孔徑的大小;
5.double類型的k,Harris參數;
6.int類型的borderType,圖像像素的邊界模式。注意它有預設值BORDER_DEFAULT;
示例程式1
int main()
{
//改變控制台字型顔色
system("color 02");
//讀取圖像
//Mat src_image = imread("D:\\opencv_picture_test\\霍夫變換\\霍夫變換.png", 0);
Mat src_image = imread("D:\\opencv_picture_test\\角點檢測\\五角星.jpg", 0);
//出錯判斷
if (!src_image.data)
{
cout << "src image load failed!" << endl;
return -1;
}
//進行角點檢測,找出角點
Mat cornerStrength;
cornerHarris(src_image, cornerStrength,2,3,0.03);
//對灰階圖進行門檻值操作,得到二值圖并顯示
Mat harrisCorner;
threshold(cornerStrength, harrisCorner,0.00001,255,THRESH_BINARY);
//顯示
namedWindow("角點圖", WINDOW_NORMAL);
imshow("角點圖", cornerStrength);
namedWindow("二值圖", WINDOW_NORMAL);
imshow("二值圖", harrisCorner);
waitKey(0);
return 0;
}
需要注意的是:角點計算後需要進行二值化才能較好地可視化角點。最好是歸一化一下。
原圖:
cornerStrength角點圖:
harrisCorner二值化後的角點圖:
示例程式2
#include <opencv2/opencv.hpp>
#include <iostream>
#include "windows.h"
#include <stdio.h>
#include <time.h>
#include <math.h>
//#include "My_ImageProssing_base.h"
#define WINDOW_NAME1 "【程式視窗1】"
#define WINDOW_NAME2 "【程式視窗2】"
using namespace cv;
using namespace std;
RNG g_rng(12345);
//*--------------------------動态角點檢測-------------------------------------*/
Mat g_srcImage, g_srcImage1, g_grayImage;
int thresh = 20;
int max_thresh = 205;
void on_CornerHarris(int ,void*);
int main()
{
//改變控制台字型顔色
system("color 02");
//讀取圖像
//Mat src_image = imread("D:\\opencv_picture_test\\霍夫變換\\霍夫變換.png", 0);
g_srcImage = imread("D:\\opencv_picture_test\\角點檢測\\五角星.jpg", 1);
//出錯判斷
if (!g_srcImage.data)
{
cout << "src image load failed!" << endl;
return -1;
}
//namedWindow("原始圖", WINDOW_NORMAL);
//imshow("原始圖", g_srcImage);
g_srcImage1 = g_srcImage.clone();
cvtColor(g_srcImage1, g_grayImage,COLOR_BGR2GRAY);
//建立視窗和滑動條
namedWindow(WINDOW_NAME1, WINDOW_NORMAL);
createTrackbar("門檻值",WINDOW_NAME1,&thresh,max_thresh, on_CornerHarris);
//初始化回調函數
on_CornerHarris(0,0);
waitKey(0);
return 0;
}
void on_CornerHarris(int, void*)
{
//定義局部變量
Mat dstImage;
Mat normImage; //歸一化
Mat scaleImage; //線性變換後的八位無符号整型的圖
//初始化,清除上一次調用次函數時他們的值
dstImage = Mat::zeros(g_srcImage.size(),CV_32FC1);
g_srcImage1 = g_srcImage.clone();
//進行角點檢測,找出角點
Mat cornerStrength;
cornerHarris(g_grayImage, dstImage, 2, 3, 0.03);
//歸一化與轉換
normalize(dstImage, normImage,0,255,NORM_MINMAX,CV_32FC1,Mat());
convertScaleAbs(normImage, scaleImage); //将歸一化後的圖線性變換成8位無符号整數
//進行繪制
for (int j = 0;j < normImage.rows;j++)
{
for (int i = 0;i < normImage.rows;i++)
{
Scalar color = Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255));//任意值
if ((int)normImage.at<float>(j, i) > thresh+60)
{
circle(g_srcImage1,Point(i,j),5, color,2,8,0);
circle(scaleImage, Point(i, j), 5, color, 2, 8, 0);
}
}
}
imshow(WINDOW_NAME1, g_srcImage1);
imshow(WINDOW_NAME2, scaleImage);
}
需要注意的是,當門檻值較小的時候,可能會“檢測”出若幹個角點,繪制時會卡住,是以需要限制一下,這裡我們去最低為60。
效果:
參考資料: