投稿作者:小黃弟
來自:中國電科智慧城市模組化仿真與智能技術重點實驗室
文字編輯:gloomyfish
特征提取
基于特征的圖像配準,具有非常廣泛的應用,大緻流程可以如下:

經典的特征比對算法有SIFT、SURF、ORB等,這三種方法在OpenCV裡面都已實作。SURF基本就是SIFT的全面更新版,有 SURF基本就不用考慮SIFT,而ORB的強點在于計算時間,以下具體比較:
計算速度:ORB>>SURF>>SIFT(各差一個量級)
旋轉魯棒性:SURF>ORB~SIFT(~表示差不多)
模糊魯棒性:SURF>ORB~SIFT
尺度變換魯棒性:SURF>SIFT>ORB(ORB并不具備尺度變換性)
是以結論就是,如果對計算實時性要求非常高,可選用ORB算法,但基本要保證正對拍攝;如果對穩定性要求稍高,可以選擇SURF;基本不用SIFT。此外補充一點,自從OpenCV3.x開始,受到SIFT跟SURF專利授權的影響,OpenCV正式的釋出版本中已經移除了SIFT跟SURF算法。ORB特征提取算法是基于FAST跟BRIEF算法改進的組合算法,其中FAST實作關鍵點/特征點的檢測,在此基礎上基于幾何矩添加方向屬性,BRIEF實作描述子生成,添加旋轉不變性支援。
ORB特征比對速度快的一個原因之一就是使用字元串向量的描述子,避免了浮點數計算。字元串描述子比對上可以采用漢明距離或者LSH改進算法實作,相比浮點數計算L2距離進一步降低了計算量。是以在一般情況下建議使用ORB特征比對,如果效果不好再嘗試AKAZE/SURF/SIFT等其它特征比對算法。
特征對齊/配準
兩幅圖像之間的基于特征比對的透視變換矩陣求解通常被稱為圖像對齊或者配準。基于特征的比對可以很好實作圖像對齊或者配準,首先需要擷取兩張圖像的特征關鍵點與特征描述子,然後通過暴力比對或者FLANN比對尋找比對度高的相關特征點。最後基于這些相關特征點估算它們之間的單應性矩陣,通過單應性矩陣實作透視變換,完成圖像對齊與配準。OpenCV中有兩個函數可以獲得單映射變換矩陣,分别為:
- findHomography
- getPerspectiveTransform
複制
兩者之間的差別在于getPerspectiveTransform隻會拿4個點去計算,findHomography則會拿一堆點(>=4)去計算。
應用代碼示範
下面是一個簡單的代碼示範,基于特征對齊,實作基于分差的缺陷檢測。
用基于ORB特征的比對結果,如下圖所示,可以看到有一些錯誤的比對點
基于ORB特征實作圖像相關特征點比對的代碼實作如下:
constint MAX_FEATURES = 5000;
constfloat GOOD_MATCH_PERCENT = 0.45f;
//im1為待配準圖檔
//im2為模闆圖檔
//im1Reg為配準後的圖檔
//h為單應性矩陣
void alignImages(Mat&im1, Mat&im2, Mat&im1Reg, Mat&h)
{
// 将圖像轉為灰階圖
Mat im1Gray, im2Gray;
cvtColor(im1, im1Gray, COLOR_BGR2GRAY);
cvtColor(im2, im2Gray, COLOR_BGR2GRAY);
// 存儲特征與特征描述子的變量
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
// 檢測ORB特征計算特征描述子.
Ptr<Feature2D> orb = ORB::create(MAX_FEATURES);
orb->detectAndCompute(im1Gray, Mat(), keypoints1, descriptors1);
clock_t start, end;
start = clock();
orb->detectAndCompute(im2Gray, Mat(), keypoints2, descriptors2); //77ms
// 特征比對.
std::vector<DMatch> matches;
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
matcher->match(descriptors1, descriptors2, matches, Mat());
// Sort matches by score
std::sort(matches.begin(), matches.end());
//基于GMS的特征比對算法
//vector<DMatch> matchesAll, matchesGMS;
//BFMatcher matcher(NORM_HAMMING);
//std::vector<DMatch> matches;
//matcher.match(descriptors1, descriptors2, matchesAll);
//cout << "matchesAll: " << matchesAll.size() << endl;
//matchGMS(im1.size(), im2.size(), keypoints1, keypoints2, matchesAll, matches);
//std::sort(matches.begin(), matches.end());
end = clock();
cout << (float)(end - start) * 1000 / CLOCKS_PER_SEC<<"ms"<< endl;
// 移除不好的比對點
constint numGoodMatches = matches.size() * GOOD_MATCH_PERCENT;
matches.erase(matches.begin() + numGoodMatches, matches.end());
// 畫比對點
Mat imMatches;
drawMatches(im1, keypoints1, im2, keypoints2, matches, imMatches);
imwrite("matches.jpg", imMatches);
// 存儲好的比對點
std::vector<Point2f> points1, points2;
for (size_t i = 0; i < matches.size(); i++)
{
points1.push_back(keypoints1[matches[i].queryIdx].pt);
points2.push_back(keypoints2[matches[i].trainIdx].pt);
}
// 找出最優單映射變換矩陣h
h= findHomography(points1, points2, RANSAC);
// 利用h矩陣進行透視變換
warpPerspective(im1, im1Reg, h, im2.size());
}
複制
Grid-based Motion Statistics(GMS)通過網格劃分、運動統計特性的方法可以迅速剔除錯誤比對,以此來提高比對的穩定性。ORB+GMS的比對效果如下,可見錯誤的比對點少了很多。
配準後的圖如下圖所示:
将配準後的圖與基準模闆圖做差分,效果如下:
進行形态學操作,
找出缺陷,比較大的缺陷可以找出來,較小的缺陷還是不能找出來。
這部分的代碼實作如下:
int main(intargc, char **argv)
{
// Read reference image
string refFilename("8.jpg");
cout <<"Reading reference image : "<< refFilename << endl;
Mat imReference = imread(refFilename);
// Read image to be aligned
string imFilename("7.jpg");
cout <<"Reading image to align : "<< imFilename << endl;
Mat im = imread(imFilename);
// Registered image will be resotred in imReg.
// The estimated homography will be stored in h.
Mat imReg, h;
// Align images
cout <<"Aligning images ..."<< endl;
alignImages(im, imReference, imReg, h);
// Write aligned image to disk.
string outFilename("aligned.jpg");
cout <<"Saving aligned image : "<< outFilename << endl;
imwrite(outFilename, imReg);
// Print estimated homography
cout <<"Estimated homography : \n"<< h << endl;
Mat currentframe, previousframe;
cvtColor(imReference, previousframe, COLOR_BGR2GRAY);
cvtColor(imReg, currentframe, COLOR_BGR2GRAY); //轉化為單通道灰階圖
absdiff(currentframe, previousframe, currentframe);//做差求絕對值
imshow("1", currentframe);
imwrite("re.jpg", currentframe);
threshold(currentframe, currentframe, 120, 255.0, THRESH_BINARY);
imwrite("re11.jpg", currentframe);
erode(currentframe, currentframe, Mat());//腐蝕
dilate(currentframe, currentframe, Mat());//膨脹
dilate(currentframe, currentframe, Mat());//膨脹
imshow("moving area", currentframe); //顯示圖像
vector<vector<Point>> v;
vector<Vec4i> hierarchy;
Mat result;
Rect rect;
findContours(currentframe, v, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
for (int i = 0; i < hierarchy.size(); i++)
{
rect = boundingRect(v.at(i));
if (rect.area() > 1)
{
rectangle(imReg, rect, Scalar(0, 0, 255), 2);
}
}
imwrite("res1.jpg", imReg);
imshow("moving area1", imReg);
waitKey(0);
}
複制
關于特征檢測跟提取,基于特征的對齊、全景拼接、圖像配準等相關知識還可以閱讀下面的相關連結擷取更多知識。
志合者不以山海為遠
道乖者不以咫尺為近