OpenCV + CPP 系列(卅三)圖像特征提取(Harris角點檢測、Shi-Tomasi角點檢測、自定義角點檢測)

SongpingWang 2021-08-15 03:06:51 阅读数:910

本文一共[544]字,预计阅读时长:1分钟~
opencv cpp 系列 卅三 特征提取

一、常用圖像特征描述

  • SIFT、SURF、HOG、Haar、LBP、KAZE、AKAZE、BRISK
SIFT

關於詳情查看 OpenCV—python 角點特征檢測之二(SIFT、SURF、ORB)
SIFT 是用於描述圖像中的局部特征,在空間尺度(使用高斯卷積核實現多尺度空間)中尋找極值點(LoG近似DoG找到關鍵點),並且提取出其比特置、尺度、旋轉不變量,因此具有尺度和旋轉不變的性質。

SURF

與sift的算法相似,只是sift算法更加穩定,檢測得到的特征點更多。但是surf的運算簡單,運算時間更短一些。
surf最大的特點是采用 HARR 特征以及積分圖像

HOG

方向梯度直方圖。用來錶示圖像的物體特征,算法流程如下:
【圖像預處理:伽馬校正和灰度化】 -> 【計算每一個像素點的梯度值,得到梯度圖】 -> 【計算梯度直方圖】 -> 【對16*16大小的block歸一化】 -> 【得到HOG特征向量】

Haar

Haar特征是一種反映圖像的灰度變化的,像素分模塊求差值的一種特征。它分為三類:邊緣特征、線性特征、中心特征和對角線特征。用黑白兩種矩形框組合成特征模板,矩形特征只對一些簡單的圖形結構,如邊緣、線段較敏感,所以只能描述在特定方向(水平、垂直、對角)上有明顯像素模塊梯度變化的圖像結構。
在這裏插入圖片描述
詳情查看:https://blog.csdn.net/lanxuecc/article/details/52222369

LBP

LBP(Local Binary Pattern,局部二值模式)是一種用來描述圖像局部紋理特征的算子;它具有旋轉不變性灰度不變性等顯著的優點。它是首先由T. Ojala, M.Pietikäinen, 和D. Harwood 在1994年提出,用於紋理特征提取。而且,提取的特征是圖像的局部的紋理特征;
詳情查看:https://blog.csdn.net/xidianzhimeng/article/details/19634573

KAZE

KAZE Features 算法是由法國學者在在2012年的ECCV會議 (論文) 中提出的, 是一種比SIFT更穩定的特征檢測算法。KAZE特征檢測是在圖像域中進行非線性擴散處理的過程。

SITF、SURF算法是通過線性尺度空間,在線性尺度空間來檢測特征點的,容易造成邊界模糊和細節丟失;而KAZE算法是通過構造非線性尺度空間,並在非線性尺度空間來檢測特征點,保留了更多的圖像細節。
KAZE算法主要包括以下步驟:
(1) 非線性尺度空間的構建;
(2) 特征點的檢測與精確定比特;
(3) 特征點主方向的確定;
(4) 特征描述子的生成。
關於詳情查看:https://www.cnblogs.com/xinyuyuanm/archive/2013/03/25/2980817.html

AKAZE

AKAZE是KAZE的加速版本。KAZE在構建非線性空間的過程中很耗時。
在非線性擴散濾波方面:AKAZE中將Fast Explicit Diffusion(FED)加入到金字塔框架可以dramatically speed-up。
在描述子方面:AKAZE使用了更高效的Modified Local Difference Binary(M-LDB),可以從非線性空間中利用梯度信息gradient information。M-LDB是旋轉和尺度不變的,並且對內存的要求更低。
詳情鏈接:https://blog.csdn.net/zcg1942/article/details/84570068
論文及github地址:http://www.robesafe.com/personal/pablo.alcantarilla/kaze.html
在AKAZE算法中默認使用二進制描述符,故漢明距離進行比較

BRISK

Binary Robust Invariant Scalable Keypoints。它是一種二進制的特征描述算子。它具有較好的旋轉不變性、尺度不變性,較好的魯棒性等。在對有較大模糊的圖像配准時,BRISK算法在其中錶現最為出色。
在圖像配准應用中,速度比較:SIFT<SURF<BRISK<FREAK<ORB。
詳情查看:https://www.cnblogs.com/welen/articles/6088639.htmlhttps://blog.csdn.net/Daker_Huang/article/details/84960704
算法流程:

  1. 特征點檢測(建立尺度空間、特征點檢測、非極大值抑制、亞像素插值)
  2. 特征點描述(高斯濾波、局部梯度計算、特征描述符)
  3. 匹配方法(漢明距離進行比較)

圖像特征描述子用於: Detection、Description、Matching

二、Harris角點檢測

1988 年的文章《A CombinedCorner and Edge Detector》中就已經提出了焦點檢測的方法,被稱為Harris 角點檢測。
關於理論詳情請查看:OpenCV—python 角點特征檢測之一(cornerHarris、Shi-Tomasi、FAST)

角度響應:
R = det ⁡ ( M ) − k ( t r a c e ( M ) ) 2   det ⁡ ( M ) = λ 1 ∗ λ 2 t r a c e ( M ) = λ 1 + λ 2 R = \det(M)-k(trace(M))^2\\ ~\\ \det(M) = \lambda_1 * \lambda_2\\ trace(M) = \lambda_1 + \lambda_2 R=det(M)k(trace(M))2 det(M)=λ1λ2trace(M)=λ1+λ2

void cornerHarris(
InputArray src, 輸入圖像
OutputArray dst, 輸出圖像
int blockSize, 計算時候的矩陣大小
int ksize, 窗口大小
double k, 閾值:計算角度響應時候的參數大小,默認在0.04~0.06
int borderType = BORDER_DEFAULT 邊界處理
)

頭文件 image_feature_all.h:聲明類與公共函數

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
class ImageFeature {

public:
void harris_demo(Mat& image);
void shi_tomasi_demo(Mat& image);
void custome_corner_demo(Mat& image);
};

主函數main.cpp調用該類的公共成員函數

#include "image_feature_all.h"
int main(int argc, char** argv) {

const char* img_path = "D:\\Desktop\\xiaomuren.jpg";
Mat image = imread(img_path);
if (image.empty()) {

cout << "圖像數據為空,讀取文件失敗!" << endl;
}
ImageFeature imgfeature;
imgfeature.harris_demo(image);
imgfeature.shi_tomasi_demo(image);
imgfeature.custome_corner_demo(image);
imshow("image", image);
waitKey(0);
destroyAllWindows();
return 0;
}

演示Harris角點檢測

源文件 feature_extract.cpp:實現類與公共函數

#include "image_feature_all.h"
void ImageFeature::harris_demo(Mat& image) {

Mat gray_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
int thresh_ = 80;
Mat dst = Mat::zeros(gray_img.size(), CV_32FC1);
Mat norm_dst, norm_scale_dst;
int blocksize = 2;
int ksize = 3;
double k = 0.04;
cornerHarris(gray_img, dst, blocksize, ksize, k, BORDER_DEFAULT);
normalize(dst, norm_dst, 0, 255, NORM_MINMAX, CV_32FC1, Mat());
convertScaleAbs(norm_dst, norm_scale_dst);
cout << "norm_scale_dst.type()=" << norm_scale_dst.type() << endl;
Mat result_img = image.clone();
for (int row = 0; row < result_img.rows; row++) {

uchar* row_ptr = norm_scale_dst.ptr(row);
for (int col = 0; col < result_img.cols; col++) {

int value = (int)*row_ptr++;
if (value > thresh_) {

circle(result_img, Point(col, row), 2, Scalar(0, 0, 255), 1, 8, 0);
}
}
}
imwrite("D:\\Desktop\\harris_"+ to_string(thresh_)+".jpg", result_img);
}

在這裏插入圖片描述

三、Shi-Tomasi角點檢測

跟Harris角點檢測的理論幾乎完全一致,唯一不同的是在使用矩陣特征值 λ 1 , λ 2 \lambda_1, \lambda_2 λ1,λ2 計算角度響應的時候

角度響應: R = min ⁡ ( λ 1 , λ 2 ) R = \min(\lambda_1 , \lambda_2) R=min(λ1,λ2)

void goodFeaturesToTrack(
InputArray image, 輸入圖像
OutputArray corners, 輸出角點vector<point2d>
int maxCorners, 返回角點的數目,若檢測角點數目大於maxCorners,則返回前maxCorners數目。
double qualityLevel, 最小可接受的向量值 0.01,
double minDistance, 兩個角點之間的最小距離(歐幾裏得距離)
InputArray mask = noArray(),
int blockSize = 3, 導數微分不同的窗口大小
bool useHarrisDetector = false, 是否使用Harris角點檢測
double k = 0.04
);
void ImageFeature::shi_tomasi_demo(Mat& image) {

Mat gray_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
int num_colors[] = {
 10,20,40,80 };
double qualityLevel = 0.01;
double minDistance = 10;
int blockSize = 3;
bool useHarris = false;
double k = 0.04;
RNG rng(1125);
for (size_t i = 0; i < 4; i++)
{

Mat result_img = image.clone();
vector<Point2f> corners;
corners.reserve(num_colors[i]);
goodFeaturesToTrack(gray_img, corners, num_colors[i], qualityLevel, minDistance, Mat(), blockSize, useHarris, k);
// 可視化角點
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (size_t i = 0; i < corners.size(); i++) {

circle(result_img, corners[i], 2, color, 2, 8);
}
imshow("result_img"+to_string(num_colors[i]), result_img);
}
}

在這裏插入圖片描述

四、自定義角點檢測器

基於Harris與Shi-Tomasi角點檢測

  • 首先通過計算矩陣 M M M 得到 λ 1 , λ 2 \lambda_1 , \lambda_2 λ1,λ2 兩個特征值根據他們得到角點響應值
  • 然後自己設置閾值實現計算出閾值得到有效響應值的角點比特置

使用 OpenCV 函數 cornerEigenValsAndVecs 來計算像素對應的本征值和本征向量來確定其是否是角點。
使用OpenCV 函數 cornerMinEigenVal 通過最小化本征值來進行角點檢測。
用上述兩個函數實現一個定制化的Harris detector,類似Shi-Tomasi檢測子。

函數簡介:

void cornerEigenValsAndVecs( 使用Harris檢測角點方法
InputArray src,
OutputArray dst,
int blockSize,
int ksize,
int borderType = BORDER_DEFAULT
)

void cornerMinEigenVal( 使用Shi-Tomas檢測角點方法
InputArray src,
OutputArray dst,
int blockSize,
int ksize = 3,
int borderType = BORDER_DEFAULT
)
void ImageFeature::custome_corner_demo(Mat& image) {

Mat gray_dst;
cvtColor(image, gray_dst, COLOR_BGR2GRAY);
// 自定義harris角點檢測:計算本征值本征向量,獲取其最大最小值。
int blockSize = 3;
int kSize = 3;
double k = 0.04;
Mat harris_dst = Mat::zeros(image.size(), CV_32FC1);
Mat harris_tmp = Mat::zeros(image.size(), CV_32FC(6));
cornerEigenValsAndVecs(gray_dst, harris_tmp, blockSize, kSize, 4);
for (size_t row = 0; row < gray_dst.rows; row++) {

Vec6f* harris_tmp_ptr = harris_tmp.ptr<Vec6f>(row);
float* harris_dst_ptr = harris_dst.ptr<float>(row);
for (size_t col = 0; col < gray_dst.cols; col++) {

double lambda1 = harris_tmp_ptr[col][0];
double lambda2 = harris_tmp_ptr[col][1];
*harris_dst_ptr++ = (float)(lambda1 * lambda2 - k * pow((lambda1 + lambda2), 2));
}
}
double harris_minVal = 0, harris_maxVal = 0;
minMaxLoc(harris_dst, &harris_minVal, &harris_maxVal, 0, 0, Mat());
// 自定義harris角點檢測:可視化
int qualityLevel = 40; //修改該參數,顯示不同效果。
int max_qualityLevel = 100;
Mat Harris_copy = image.clone();
float t = harris_minVal + (((double)qualityLevel / max_qualityLevel) * (harris_maxVal - harris_minVal));
RNG rng;
for (size_t row = 0; row < image.rows; row++) {

float* harris_dst_ptr = harris_dst.ptr<float>(row);
for (size_t col = 0; col < image.cols; col++) {

float v = *harris_dst_ptr++;
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
if (v > t) {

circle(Harris_copy, Point(col, row), 1, color, 2, 8, 0);
}
}
}
imshow("custom_Harris", Harris_copy);
// 自定義Shi-Tomas角點檢測: 最小化本征值來進行角點檢測
double ShiTomas_minVal = 0, ShiTomas_maxVal = 0;
Mat ShiTomas_dst = Mat::zeros(image.size(), CV_32FC1);
cornerMinEigenVal(gray_dst, ShiTomas_dst, blockSize, kSize, 4);
minMaxLoc(ShiTomas_dst, &ShiTomas_minVal, &ShiTomas_maxVal, 0, 0, Mat());
// 自定義Shi-Tomas角點檢測:可視化
float t2 = ShiTomas_minVal + (((double)qualityLevel / max_qualityLevel) * (ShiTomas_maxVal - ShiTomas_minVal));
Mat ShiTomasi_copy = image.clone();
for (int row = 0; row < gray_dst.rows; row++){

float* ShiTomas_dst_ptr = ShiTomas_dst.ptr<float>(row);
for (int col = 0; col < gray_dst.cols; col++){

float val = *ShiTomas_dst_ptr++;
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
if (val > t2) {

circle(ShiTomasi_copy, Point(col, row), 4, color, -1, 8, 0);
}
}
}
imshow("custom_ShiTomasi", ShiTomasi_copy);
}

在這裏插入圖片描述

版权声明:本文为[SongpingWang]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815030634223W.html