Goal:學習實作keypoint detection
學習用 OpenCV 實作關鍵點檢測算法(GFTT、SIFT、SURF、FAST、BRISK、ORB)。
比較這些算法multiscale detection的能力。
作業環境
Window 11
Visual Studio 2019
OpenCV 4.5.5
CMake 3.24.3
CPU:11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz 2.42 GHz
RAM:16.0 GB
InterestPoints
InterestPoints(也稱keypoints或特徵點)的概念已經得到了廣泛的應用,包括目標辨識、圖像配準、視覺追踪、三維重建等。這個概念的原理是,從圖像中選取某些特徵點並對圖像進行局部分析(即提取局部特徵),而非觀察整幅圖像(即提取全局特徵)。只要圖像中有足夠多可檢測的InterestPoints,並且這些InterestPoints各不相同且特徵穩定、能被精確地找出。不管圖像拍攝時採用了什麼視角、尺度和方位,理想情況下同一個場景或目標位置都要檢測到特徵點。接下來介紹幾個方法GFTT、SIFT、SURF、FAST、BRISK、ORB。
GFTT
GFTT(goodFeaturesToTrack),一種特徵檢測器,實際上是 Harris 算法的修改版本,由於Harris角點檢測算法容易出現聚簇現像以及角點信息丟失和位置偏移現象,所以提出一種名為goodFeatureToTrack的角點檢測算法,opencv的feature2D接口集成了這種算法,名稱為GFTTDetector。cv::GFTTDetector 與 cv::goodFeatrueToTrack 的功能基本相同。然而,cv::GFTTDetector 繼承自 cv::Feature2D,將corner視為通用keypoint,可以對角點做更多後續處理(如特徵點描述)。
static Ptr<GFTTDetector> cv::GFTTDetector::create()
maxCorners:最大角點數目
qualityLevel:角點可以接受的最小特徵值(一般0.1或者0.01)不超過1
minDistance:角點之間的最小距離
blockSize:倒數自相關矩陣的鄰域範圍
useHarrisDetector:是否使用角點檢測
khessian:自相關矩陣的相對權重係數(一般為0.04)
SIFT
SIFT (Scale-Invariant Feature Transform),2004 年,D.Lowe在他的論文Distinctive Image Features from Scale-Invariant Keypoints中提出了一種新算法 Scale Invariant Feature Transform (SIFT),它提取keypoint並計算descriptors。
SIFT算法主要涉及四個步驟:
1. Scale-space Extrema Detection:為了檢測更大的角,需要更大的window。使用scale-space filtering。其中,LoG(Laplacian of Gaussian)針對各種不同的圖像求σ值。LoG 充當blob detector,檢測由於σ變化而導致的各種大小的blob,σ作為縮放參數。但是 LoG 成本高,所以 SIFT 算法使用Difference of Gaussians(DoG),它是 LoG 的近似值。DoG是作為具有兩個不同的圖像的高斯模糊的差異而獲得的σ。 這個過程是針對Gaussian Pyramid中圖像的不同八度(octave)完成的(如右圖所示)。
找到 DoG,就會在圖像中搜索尺度和空間上的局部極值。例如,將圖像中的一個像素與其 8 個相鄰像素以及下一個尺度中的 9 個像素和前一個尺度中的 9 個像素進行比較。如果它是一個局部極值,它就是一個潛在的關鍵點。它基本上意味著keypoint在該scale中得到了最好的體現。(如右圖所示)
經驗數據:
octaves = 4, scale levels = 5, initial σ=1.6, k=2^(1/2)
2.Keypoint Localization:找到潛在的關鍵點位置,對其進行細化以獲得更準確的結果。使用尺度空間的泰勒展開獲得更準確的極值位置,如果該極值處的強度小於閾值(根據論文為 0.03),則將其拒絕。(在 OpenCV 中稱為contrastThreshold),DoG 對邊緣有更高的響應,邊緣也需要去除。為此,使用了類似於 Harris 角點檢測器的概念。他們使用 2x2 Hessian 矩陣 (H) 來計算主曲率。如果這個比率大於閾值,在 OpenCV 中稱為edgeThreshold(論文中為 10) ,則該關鍵點將被丟棄。因此消除了任何低對比度keypoints和edge keypoints ,剩下的是強的InterestPoints。
3.Orientation Assignment:為每個keypoint分配一個方向以實現圖像旋轉的不變性。根據scale在keypoint周圍取鄰域,計算該區域的梯度大小和方向。創建了一個包含 36 個 bin 的方向直方圖,360 度(由梯度幅度和高斯加權圓形窗口加權σ等於keypoint scale的 1.5 倍)。取直方圖中的最高峰,任何高於其 80% 的峰也被視為計算方向。創建具有相同位置和scale但方向不同的keypoint,助於匹配的穩定性。
4.Keypoint Descriptor:取keypoint 周圍的 16x16 鄰域。它被分成 16 個 4x4 大小的子塊。對於每個子塊,創建 8 bin 方向直方圖。因此總共有 128 個 bin 值可用。它被表示為一個向量以形成Keypoint Descriptor。除此之外,還採取了一些措施來實現對光照變化、旋轉等的robustness 。
nfeatures:要保留的最佳特徵的數量。特徵按分數排序(在SIFT算法中測量為局部對比度)
OctaveLayers:每個Octave中的層數。
contrastThreshold:用於過濾掉半均勻(低對比度)區域中的弱特徵的對比度閾值。閾值越大,檢測器產生的特徵就越少。
edgeThreshold:用於過濾掉類似邊緣的特徵的閾值。
注意:其含義與contrastThreshold不同,即edgeThreshold越大,過濾掉的特徵越少(保留的特徵越多)。
sigma:在octave #0 處應用於輸入圖像的高斯sigma。
SURF
SURF(Speeded Up Robust Features),不僅具有尺度不變,還具有較高的計算效率。2006 年,Bay, H.、Tuytelaars, T. 和 Van Gool, L 三人發表了另一篇論文“SURF: Speeded Up Robust Features”,介紹了名為 SURF 的新算法,是 SIFT 的加速版本。在 SIFT 中,Lowe 使用DoG近似LoG來尋找尺度空間。SURF 更進一步,使用 Box Filter 逼近 LoG。右上圖顯示了這種近似的演示。這種近似的一大優點是,在積分圖像(有可能造成不準確)的幫助下,可以很容易地計算出與Box Filter的捲積,並且可以針對不同的規模並行完成。SURF 還依賴於 Hessian 矩陣(右下)(衡量了一個函數的局部曲率)的行列式來確定比例和位置。
hessianThreshold:SURF 中使用的 hessian 關鍵點檢測器的閾值。
nOctaves:關鍵點檢測器將使用的金字塔八度數。
nOctaveLayers :每個八度內的八度層數。
extended:擴展描述符標誌(true - 使用擴展的 128 元素描述符;false - 使用 64 元素描述符)。
upright:Up-right 或 rotated 特徵標誌(true - 不計算特徵的方向;false - 計算方向)。
FAST
FAST(Features from Accelerated Segment Test)用來快速檢測InterestPoints,比其他現有的角檢測器快幾倍,但對高水平的噪聲不穩健,取決於閾值。Edward Rosten 和 Tom Drummond 在 2006 年的論文“Machine learning for high-speed corner detection”中提出了 FAST(Features from Accelerated Segment Test)算法(後來在 2010 年進行了修訂)。
使用 FAST 進行特徵檢測:
1.選擇一個像素p被辨識為InterestPoint,強度為Ip
2.選擇合適的閾值t.
3.考慮圍繞被測像素的 16 個像素的圓。(右圖)
4.現在像素p是一個角,如果存在一組n圓圈(共 16 個像素)中的連續像素都比Ip+t, 或者都比Ip-t (在上圖中顯示為白色虛線)。n被選為12。
5.提出了高速測試以排除大量非角點。該測試僅檢查 1、9、5 和 13 處的四個像素(首先測試 1 和 9 是否太亮或太暗。如果是,則檢查 5 和 13)。如果p是一個角,那麼其中至少三個都必須比Ip+t或比Ip-t.如果這兩種情況都不是,那麼p不能是角。然後可以通過檢查圓圈中的所有像素將完整段測試標準應用於通過的候選者。該檢測器本身表現出高性能,但存在幾個弱點:
對於 n < 12,不會拒絕候選角點。
像素的選擇不是最優的,因為它的效率取決於問題的排序和角出現的分佈。
高速測試的結果被丟棄。
檢測到多個彼此相鄰的特徵。
cv::FastFeatureDetector::create ()
type:cv::FastFeatureDetector::TYPE_5_8、cv::FastFeatureDetector::TYPE_7_12 、cv::FastFeatureDetector::TYPE_9_16
threshold:一個點與圓心強度值的差距必須達到一個指定的值,才能被認為是明顯更暗或更亮。
BRISK
BRISK(Binary Robust Invariant Scalable Kepoints),不僅是一個特徵點檢測器,它還包含了描述每個被檢測keypoint的鄰域的過程。BRISK使用二進制descriptor表示圖像中的keypoint,Descriptor具有較小的存儲空間和快速的匹配速度。在多尺度空間中檢測keypoint,實現了尺度不變性,能夠檢測和描述不同尺度的圖像特徵。利用圖像的方向來實現keypoint的方向不變性,提高匹配的準確性。對圖像的光照變化、旋轉和尺度變化具有較強的robustness,並且對噪聲和局部變形也有一定的容忍度。適用於圖像識別、物體追蹤和三維重建等應用場景。
為了在不同尺度下檢測InterestPoints,使用兩個下採樣過程構建圖像金字塔,其中每個層級都是原始圖像尺寸的一個比例。然後,在金字塔的所有層級上應用FAST特徵檢測器,以找到局部最大值的像素作為keypoint。BRISK算法在進行關鍵點定位時使用插值,包括空間和尺度兩個方面的插值。空間插值基於3x3鄰域,而尺度插值則基於與上下相鄰層級的局部關鍵點的拋物線擬合。這樣在不連續的尺度上進行keypoint檢測時,最終檢測到的每個keypoint都有連續的尺度值。
thresh:AGAST 檢測閾值分數。
Octaves:檢測Octave。 使用 0 做單比例。
patternScale:將此比例應用於用於對關鍵點鄰域進行採樣的模式。
ORB
ORB(Oriented FAST and Rotated BRIEF)是OpenCV中的一種特徵檢測和描述算法,由Ethan Rublee、Vincent Rabaud、Kurt Konolige和Gary R. Bradski於2011年提出"ORB: An efficient alternative to SIFT or SURF "。ORB是FAST keypoint檢測器和BRIEF descriptor的結合,通過多項修改來提高性能。
對於keypoint檢測,ORB首先使用FAST找到keypoint,然後應用Harris角點測量來選擇前N個得分最高的keypoint。它還使用金字塔來生成多尺度特徵。為了實現旋轉不變性,ORB計算keypoint周圍圓形區域的重心,並將從角點到重心的向量方向作為keypoint的方向。
對於descriptor,ORB使用BRIEF descriptor。由於BRIEF在旋轉下的表現較差,ORB根據keypoint的方向對BRIEF進行“導向”,即將BRIEF描述子根據keypoint的方向進行旋轉,從而獲得旋轉後的BRIEF descriptor。
為了實現descriptor的匹配,ORB使用改進的multi-probe LSH。研究表明,ORB比SURF和SIFT算法更快,而且ORB的descriptor在某些應用場景下表現更好。具有不需要支付專利費用的優勢,在計算成本和匹配性能方面是SIFT和SURF的一個良好替代品。ORB在低功耗設備上應用廣泛,如全景拼接等。
nfeatures:要保留的最大特徵數。
scaleFactor:金字塔抽取比率,大於 1。scaleFactor==2 表示經典金字塔,其中每個下一層的像素比上一層少 4 倍,但如此大的比例因子會顯著降低特徵匹配分數。 另一方面,太接近 1 比例因子意味著要覆蓋特定比例範圍,您將需要更多金字塔級別,因此速度會受到影響。
nlevels:金字塔層數。 最小級別的線性大小等於 input_image_linear_size/pow(scaleFactor, nlevels - firstLevel)。
edgeThreshold:這是未檢測到特徵的邊界大小。 它應該大致匹配 patchSize 參數。
firstLevel:將源圖像放入的金字塔級別。 前面的層填充了放大的源圖像。
WTA_K:生成定向 BRIEF descriptor的每個元素的點數。默認值 2 表示我們採用隨機點對並比較它們的亮度的 BRIEF,因此我們得到 0/1 響應。 其他可能的值是 3 和 4。例如,3 表示我們取 3 個隨機點(當然,這些點坐標是隨機的,但它們是從預定義的種子生成的,因此 BRIEF descriptor的每個元素都是從確定性地計算的像素矩形),找到最大亮度點和獲勝者的輸出索引(0、1 或 2)。 這樣的輸出將佔用 2 位,因此需要漢明距離的特殊變體,表示為 NORM_HAMMING2(每個 bin 2 位)。 當 WTA_K=4 時,我們取 4 個隨機點來計算每個 bin(這也將佔用 2 個位,可能的值為 0、1、2 或 3)。
scoreType:默認HARRIS_SCORE表示使用Harris算法對特徵進行排序(分數寫入KeyPoint::score,用於保留best nfeatures特徵);FAST_SCORE 是參數的替代值,它產生稍微不穩定的keypoint,但計算速度稍快一些。
patchSize:定向 BRIEF 描述符使用的補丁大小。 當然,在較小的金字塔層上,特徵覆蓋的感知圖像區域會更大。
fastThreshold:快速閾值
InterestPoints程式實做
InterestPoints.cpp
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/xfeatures2d.hpp>
#include "harrisDetector.h"
int main()
{
// Harris:
// Read input image
cv::Mat image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
if (!image.data)
return 0;
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
// Display the image
cv::namedWindow("Original");
cv::imshow("Original",image);
// Detect Harris corners
cv::Mat cornerStrength;
cv::cornerHarris(image, cornerStrength,
3, // neighborhood size
3, // aperture size
0.01); // Harris parameter
// threshold the corner strengths
cv::Mat harrisCorners;
double threshold= 0.0001;
cv::threshold(cornerStrength, harrisCorners,
threshold,255,cv::THRESH_BINARY_INV);
// Display the corners
cv::namedWindow("Harris");
cv::imshow("Harris", harrisCorners);
// Create Harris detector instance
HarrisDetector harris;
// Compute Harris values
harris.detect(image);
// Detect Harris corners
std::vector<cv::Point> pts;
harris.getCorners(pts,0.02);
// Draw Harris corners
harris.drawOnImage(image,pts);
// Display the corners
cv::namedWindow("Corners");
cv::imshow("Corners",image);
// GFTT:
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
// Compute good features to track
//計算適合追蹤的特徵
std::vector<cv::KeyPoint> keypoints;//keypoint的向量
// GFTT detector
cv::Ptr<cv::GFTTDetector> ptrGFTT = cv::GFTTDetector::create(
500, // keypoint的最大數量
0.05, // quality level
10); // 角點間允許最短的距離
// detect the GFTT
ptrGFTT->detect(image,keypoints);
// for all keypoints
std::vector<cv::KeyPoint>::const_iterator it= keypoints.begin();
while (it!=keypoints.end()) {
// draw a circle at each corner location
cv::circle(image,it->pt,3,cv::Scalar(255,255,255),1);
++it;
}
// Display the keypoints
cv::namedWindow("GFTT");
cv::imshow("GFTT",image);
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
cv::Mat corner;
cv::goodFeaturesToTrack(image, corner, 500, 0.01, 10);
// draw the keypoints
cv::drawKeypoints(image, // original image
keypoints, // vector of keypoints
image, // the resulting image
cv::Scalar(255,255,255), // color of the points
cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); //drawing flag
// Display the keypoints
cv::namedWindow("Good Features to Track Detector");
cv::imshow("Good Features to Track Detector",image);
// FAST feature:
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
keypoints.clear();
// FAST detector,閾值為40
cv::Ptr<cv::FastFeatureDetector> ptrFAST = cv::FastFeatureDetector::create(40);
// 檢測 keypoint
ptrFAST->detect(image,keypoints);
// draw the keypoints
cv::drawKeypoints(image,keypoints,image,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
std::cout << "Number of keypoints (FAST): " << keypoints.size() << std::endl;
// Display the keypoints
cv::namedWindow("FAST");
cv::imshow("FAST",image);
// FAST feature without non-max suppression
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
keypoints.clear();
// detect the keypoints
ptrFAST->setNonmaxSuppression(false);
ptrFAST->detect(image, keypoints);
// draw the keypoints
cv::drawKeypoints(image,keypoints,image,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
// Display the keypoints
cv::namedWindow("FAST Features (all)");
cv::imshow("FAST Features (all)",image);
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg",0);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
int total(100); // requested number of keypoints
int hstep(5), vstep(3); // a grid of 4 columns by 3 rows
// hstep= vstep= 1; // try without grid
int hsize(image.cols / hstep), vsize(image.rows / vstep);
int subtotal(total / (hstep*vstep)); // number of keypoints per grid
cv::Mat imageROI;
std::vector<cv::KeyPoint> gridpoints;
std::cout << "Grid of " << vstep << " by " << hstep << " each of size " << vsize << " by " << hsize << std::endl;
// detection with low threshold
ptrFAST->setThreshold(20);
// non-max suppression
ptrFAST->setNonmaxSuppression(true);
// The final vector of keypoints
keypoints.clear();
// detect on each grid
for (int i = 0; i < vstep; i++)
for (int j = 0; j < hstep; j++) {
// create ROI over current grid
imageROI = image(cv::Rect(j*hsize, i*vsize, hsize, vsize));
// detect the keypoints in grid
gridpoints.clear();
ptrFAST->detect(imageROI, gridpoints);
std::cout << "Number of FAST in grid " << i << "," << j << ": " << gridpoints.size() << std::endl;
if (gridpoints.size() > subtotal) {
for (auto it = gridpoints.begin(); it != gridpoints.begin() + subtotal; ++it) {
std::cout << " " << it->response << std::endl;
}
}
// get the strongest FAST features
auto itEnd(gridpoints.end());
if (gridpoints.size() > subtotal) { // select the strongest features
std::nth_element(gridpoints.begin(), gridpoints.begin() + subtotal, gridpoints.end(),
[](cv::KeyPoint& a, cv::KeyPoint& b) {return a.response > b.response; });
itEnd = gridpoints.begin() + subtotal;
}
// add them to the global keypoint vector
for (auto it = gridpoints.begin(); it != itEnd; ++it) {
it->pt += cv::Point2f(j*hsize, i*vsize); // convert to image coordinates
keypoints.push_back(*it);
std::cout << " " <<it->response << std::endl;
}
}
// draw the keypoints
cv::drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
// Display the keypoints
cv::namedWindow("FAST Features (grid)");
cv::imshow("FAST Features (grid)", image);
// SURF:
// Read input image
image = cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p51.jpg", 0);
cv::namedWindow("Origin_SURF");
cv::imshow("Origin_SURF", image);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
keypoints.clear();
// Construct the SURF feature detector object
cv::Ptr<cv::xfeatures2d::SURF> ptrSURF = cv::xfeatures2d::SURF::create(3000.0);
// detect the keypoints
ptrSURF->detect(image, keypoints);
// Detect the SURF features
ptrSURF->detect(image,keypoints);
cv::Mat featureImage;
cv::drawKeypoints(image,keypoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
// Display the keypoints
cv::namedWindow("SURF");
cv::imshow("SURF",featureImage);
std::cout << "Number of SURF keypoints: " << keypoints.size() << std::endl;
// Read a second input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/p52.jpg", cv::IMREAD_GRAYSCALE);
cv::namedWindow("Origin_SURF2");
cv::imshow("Origin_SURF2", image);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
// Detect the SURF features
ptrSURF->detect(image,keypoints);
cv::drawKeypoints(image,keypoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
// Display the keypoints
cv::namedWindow("SURF (2)");
cv::imshow("SURF (2)",featureImage);
// SIFT:
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg", cv::IMREAD_GRAYSCALE);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
//xfeatures2d是命名空间,SIFT是类名,SiftFeatureDetector和SiftDescriptorExtractor
keypoints.clear();
// Construct the SIFT feature detector object
cv::Ptr<cv::SIFT> ptrSIFT = cv::SIFT::create();
// detect the keypoints
ptrSIFT->detect(image, keypoints);
// Detect the SIFT features
ptrSIFT->detect(image,keypoints);
//cv::Mat featureImage;
cv::drawKeypoints(image,keypoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
// Display the keypoints
cv::namedWindow("SIFT");
cv::imshow("SIFT",featureImage);
std::cout << "Number of SIFT keypoints: " << keypoints.size() << std::endl;
// BRISK:
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg", cv::IMREAD_GRAYSCALE);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
// Display the keypoints
cv::namedWindow("BRISK");
cv::imshow("BRISK",featureImage);
keypoints.clear();
// Construct another BRISK feature detector object
cv::Ptr<cv::BRISK> ptrBRISK = cv::BRISK::create(
60, // threshold for BRISK points to be accepted
5); // number of octaves
// Detect the BRISK features
ptrBRISK->detect(image,keypoints);
cv::drawKeypoints(image,keypoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
// Display the keypoints
cv::namedWindow("BRISK");
cv::imshow("BRISK", featureImage);
std::cout << "Number of BRISK keypoints: " << keypoints.size() << std::endl;
// ORB:
// Read input image
image= cv::imread("C:/Users/User/Desktop/CV_testimage/P5/harris2.jpg", cv::IMREAD_GRAYSCALE);
// rotate the image (to produce a horizontal image)
//cv::transpose(image, image);
//cv::flip(image, image, 0);
keypoints.clear();
// Construct the ORB feature detector object
cv::Ptr<cv::ORB> ptrORB = cv::ORB::create(
75, // total number of keypoints
1.2, // scale factor between layers
8); // number of layers in pyramid
// detect the keypoints
ptrORB->detect(image, keypoints);
cv::drawKeypoints(image,keypoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
// Display the keypoints
cv::namedWindow("ORB");
cv::imshow("ORB",featureImage);
std::cout << "Number of ORB keypoints: " << keypoints.size() << std::endl;
cv::waitKey();
return 0;
}
ERROR
下載xfeatures2d module,將xfeatures2d加到最初下載opencv的檔案裡面(opencv\build\include\opencv2)
但我的還是無法開啟,所以我把cuda.hpp、nonfree.hpp、xfeatures2d.hpp直接放在include下,就沒有找不到xfeatures2d.hpp的問題。
接下來遇到的問題是E0135 namespace "cv::xfeatures2d" 沒有成員"SiftFeatureDetector"
看到OpenCV官網SIFT的用法是cv::SIFT::create,更改cv::xfeatures2d::SiftFeatureDetector -> cv::SIFT
接下來遇到的問題是無法解析cv::xfeatures2d::SurfFeatureDetector
看到OpenCV官網SURF的用法是cv::xfeatures2d::SURF ,同樣更改成官網的樣子
結果無法解析cv::xfeatures2d::SURF
先把SURF註解執行看看,
結果發生記憶體錯誤cv::Exception
在同學們分享自己是如何成功之後,我又再試一遍
下載opencv-4.5.5和opencv_contrib-4.5.5,建立一個新的資料夾opencv_n4.5.5,在裡面建立資料夾opencv_build,並將opencv_contrib-4.5.5和opencv-4.5.5放入。
打開CMake(3.24.3),source 選擇C:\opencv_n4.5.5\opencv-4.5.5 ,build 選擇C:\opencv_n4.5.5\opencv_build 再點選Configure選擇Visual Studio 16 2019
勾選OPENCV_EXTRA_NONFREE,能夠執行SIFT、SURF。
在OPENCV_EXTRA_MODULES_PATH 添加路徑C:/opencv_n4.5.5/opencv_contrib-4.5.5/modules
勾選BUILD_opencv_world,之後生成的opencv的lib和dll在相同文件opencv_world.lib和opencv_world.dll內。之後點選Configure,畫面會是紅色,顯示Configuring done後再次點選Configure,畫面變白之後,再點選Generation。
Generation done 之後選擇Open Project開啟visual studio。
選擇CMakeTargets ->ALL_BUILD並建置
ERROR 無法開啟'python310_d.lib'
首先要下載python的debug版。
下載完後,在opencv_python3添加屬性,選擇組態Debug(我因為選成Release一直error,才發現選錯),平台x64。
VC++目錄->參考目錄 添加C:\Users\User\AppData\Local\Programs\Python\Python310\libs
連結器->輸入 添加C:\Users\User\AppData\Local\Programs\Python\Python310\libs\python310_d.lib
添加完屬性後就建置成功,之後選擇 INSTALL -> 僅限專案 -> 僅建置 INSTALL
打開Visual Studio空白專案,按照Exercise 2的方式建立
發生錯誤:無法找到程序輸入點。把C:\opencv_n4.5.5\opencv_build\install\x64\vc16\bin裡所有.dll檔案複製到C:\Windows\System32。
解決完這個問題,程式就能執行了!
執行結果
原圖轉成灰階
GFTT
cv::GFTTDetector::create(500,0.01,10);
cv::goodFeaturesToTrack(image, corner, 500, 0.01, 10);
根據結果顯示cv::GFTTDetector跟cv::goodFeaturesToTrack的功能真的基本相同。
改變GFTT的qualityLevel
cv::GFTTDetector::create(500,0.01,10)
cv::GFTTDetector::create(500,0.05,10)
cv::GFTTDetector::create(500,0.1,10)
角點可以接受的最小特徵值為0.01時,偵測出來的角點最多,隨著qualityLevel的上升,角點減少,但留下來的更符合角點。
FAST
cv::FastFeatureDetector::create(40)
without non-max suppression
ptrFAST>setNonmaxSuppression(false);
non-max suppression
ptrFAST->setThreshold(20);
ptrFAST>setNonmaxSuppression(true);
改變threshold
cv::FastFeatureDetector::create(10)
cv::FastFeatureDetector::create(40)
cv::FastFeatureDetector::create(70)
threshold越大特徵點越少
SIFT
static Ptr<SIFT> create(int nfeatures = 0, int nOctaveLayers = 3,double contrastThreshold = 0.04, double edgeThreshold = 10,double sigma = 1.6);
static Ptr<SIFT> create(int nfeatures = 0, int nOctaveLayers = 4,double contrastThreshold = 0.04, double edgeThreshold = 10,double sigma = 1.6);
static Ptr<SIFT> create(int nfeatures = 0, int nOctaveLayers = 3,double contrastThreshold = 0.1, double edgeThreshold = 10,double sigma = 1.6);
static Ptr<SIFT> create(int nfeatures = 0, int nOctaveLayers = 3,double contrastThreshold = 0.04, double edgeThreshold = 30,double sigma = 1.6);
上圖1 是原本參數的結果;上圖2 是將nOctaveLayers改成4,根據SIFT的經驗數據設置,結果確實比上圖1好;上圖3 是我想試試看閾值越大特徵是否會減少,結果確實如此;上圖4 edgeThreshold越大過濾掉的特徵值越少(特徵值變多)。
SURF
灰階原圖
cv::xfeatures2d::SURF::create(3000.0);
cv::xfeatures2d::SURF::create(2000.0);
cv::xfeatures2d::SURF::create(200.0);
cv::xfeatures2d::SURF::create(3000.0);
cv::xfeatures2d::SURF::create(2000.0);
cv::xfeatures2d::SURF::create(200.0);
用兩張不同角度拍攝同一建築物的圖像,可以看出兩張圖像偵測出來的關鍵點差不多。跟SIFT一樣不同尺度檢測出不同大小的關鍵點,而且隨著閾值上升關鍵點越少。
BRISK
改變thresh
cv::BRISK::create(60,5);
cv::BRISK::create(70,5);
cv::BRISK::create(100,5);
thresh越大特徵點越少
改變octaves
cv::BRISK::create(60,3);
cv::BRISK::create(60,4);
cv::BRISK::create(60,5);
cv::BRISK::create(60,0);
octaves為3、4、5沒有感受到明顯的變化;octaves=0代表使用單scale,所以特徵點的尺度大小都一樣。
ORB
改變scaleFactor(大於 1)
cv::ORB::create(75, 1.05, 8);
cv::ORB::create(75,1.2, 8);
cv::ORB::create(75, 2.0, 8);
scaleFactor=2 ,表示經典金字塔,每下一層的像素比上一層少 4 倍,顯著降低特徵匹配分數。 scaleFacto太接近 1 意味著要覆蓋特定比例範圍,需要更多金字塔級別,因此速度會受到影響。
Reference
OpenCV 4 Computer Vision Application Programming Cookbook, by D. M. Escrivá, R. Laganiere, Fourth Edition, Packt Publishing, 2019
OpenCV计算机视觉编程攻略(第3版),2018年 5 月北京第 1 次印刷 ,R. Laganiere,翻譯 相银初
OpenCV: Feature Detection,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/dd/d1a/group__imgproc__feature.html
OpenCV: cv::FastFeatureDetector Class Reference,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/df/d74/classcv_1_1FastFeatureDetector.html#a4654f6fb0aa4b8e9123b223bfa0e2a08a7a1af523673c330d43ec47081bcc55e3
OpenCV: FAST Algorithm for Corner Detection,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/df/d0c/tutorial_py_fast.html
OpenCV: Introduction to SURF (Speeded-Up Robust Features),OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/df/dd2/tutorial_py_surf_intro.html
OpenCV: Introduction to SIFT (Scale-Invariant Feature Transform),OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/da/df5/tutorial_py_sift_intro.html
OpenCV: ORB (Oriented FAST and Rotated BRIEF),OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/d1/d89/tutorial_py_orb.html
OpenCV: Feature Detection and Description,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/db/d27/tutorial_py_table_of_contents_feature2d.html
OpenCV: cv::xfeatures2d::SURF Class Reference,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/d5/df7/classcv_1_1xfeatures2d_1_1SURF.html
OpenCV: cv::SIFT Class Reference,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/d7/d60/classcv_1_1SIFT.html
OpenCV: cv::BRISK Class Reference,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/de/dbf/classcv_1_1BRISK.html
OpenCV: cv::ORB Class Reference,OpenCV,Dec 25,2021,https://docs.opencv.org/4.5.5/db/d95/classcv_1_1ORB.html
OpenCV4学习笔记(48)——GFTT特征点检测算法,CSDN博客,邱小兵, Apr 23,2020,https://blog.csdn.net/weixin_45224869/article/details/105717490
cv::GFTTDetector特征点检测,CSDN博客,酸菜余,Aug 29,2019 ,https://blog.csdn.net/weixin_43821376/article/details/100135901
OpenCV 4 下 SIFT、SURF的使用_opencv4 surf,CSDN博客,魏Gordon,May 08,2023,https://blog.csdn.net/Gordon_Wei/article/details/88920411
OpenCV报错之 Microsoft C++ 异常: cv::Exception,CSDN博客,lee_swift13,Feb 26,2019,https://blog.csdn.net/weixin_43350361/article/details/87931095?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-87931095-blog-79727172.235%5Ev36%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-87931095-blog-79727172.235%5Ev36%5Epc_relevant_default_base3&utm_relevant_index=1
fatal error: opencv2/xfeatures2d.hpp: No such file or directory · Issue #3 · udacity/SFND_2D_Feature_Tracking , GitHub,suljaxm,Jul 15, 2019,https://github.com/udacity/SFND_2D_Feature_Tracking/issues/3
opencv_contrib/modules/xfeatures2d, GitHub,alalek,on Oct 8, 2021,https://github.com/opencv/opencv_contrib/tree/a26f71313009c93d105151094436eecd4a0990ed/modules/xfeatures2d
LNK1104 無法打開文件“python37_d.lib” 解決辦法,Leonwenbin,台部落,Dec 11,2018,https://www.twblogs.net/a/5c0e987abd9eee5e40bad117
【OpenCV】opencv4 + opencv_contrib 4 + VS2015-VS2019的编译_opencv4.0 vs2019 编译,魏Gordon,CSDN博客,Jan 4,2019,https://blog.csdn.net/Gordon_Wei/article/details/85775328