原理講解
【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。
效果:
參考資料:
《Opencv3編程入門.毛星雲版》
《數字圖像處理PPT.李竹版》