https://he-kate1130.tistory.com/151
[Computer Vision/OpenCV] 21. Feature Descriptor(2) - SIFT
https://he-kate1130.tistory.com/150 [Computer Vision/OpenCV] 20. Feature Descriptor(1)์ค๋์ Feature Descriptor๊ฐ ๋ฌด์์ธ์ง, ์ด๋ค ์๊ณ ๋ฆฌ์ฆ์ด ์๋์ง ํฐ ํ์์ ์์๋ณด๋๋กํ๋ค.Feature Descriptor๋ ์ด๋ฏธ์ง์ ํน์ง์ ์ ์์น
he-kate1130.tistory.com
SIFT์๊ณ ๋ฆฌ์ฆ์ ์ง์ ์ ์ฉํด๋ณด๋ฉฐ, ํคํฌ์ธํธ๋ฅผ ๊ฒ์ถํ๊ณ ๋ ์ด๋ฏธ์ง ๊ฐ์ ๋งค์นญ์ ์ํํด๋ณด์.
ํคํฌ์ธํธ ์ถ์ถ
๋จผ์ ๋ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์จ ํ, SIFT ์๊ณ ๋ฆฌ์ฆ์ ์ด์ฉํด ๊ฐ๊ฐ์ ์ด๋ฏธ์ง์์ ํคํฌ์ธํธ๋ฅผ ์ถ์ถํ๋ค.
์๋๋ drawKeypoints() ํจ์๋ฅผ ์ด์ฉํด ์๊ฐํํ ๊ฒฐ๊ณผ์ด๋ค




Brute-Force ๋งค์นญ
๊ฐ์ฅ ๋จ์ํ ๋ฐฉ์์ ๋งค์นญ์ธ Brute-Force Matcher๋ฅผ ์ด์ฉํด ๋ ์ด๋ฏธ์ง์ ํน์ง์ ๋ค์ ๋งค์นญํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค

๋๋ฌด ๋ง์ ๋งค์นญ์ด ์๊ฒจ์ ์ด๋ฏธ์ง๊ฐ ์ ์๋ณด์ธ๋ค.. ๋ฌด์๋ณด๋ค ์ ํํ์ง ์์ ๋งค์นญ๊ฒฐ๊ณผ๊ฐ ๋๋ฌด ๋ง์๋ณด์ธ๋ค.
FLANN + Lowe’s Ratio Test
์์ ์๊ฐํ SIFT๋ ์ด๋ฏธ์ง ๋ด์์ ๊ฐ๊ฑดํ ํคํฌ์ธํธ๋ฅผ ๊ฒ์ถํ๊ณ , ๊ฐ ํคํฌ์ธํธ์ ์ง์ญ ํน์ฑ์ ๋ฒกํฐ๋ก ๊ธฐ์ (descriptor)ํจ์ผ๋ก์จ ์๋ก ๋ค๋ฅธ ์ด๋ฏธ์ง ๊ฐ ์ ์ฌํ ์ง์ ์ ๋์(matching)์ํฌ ์ ์๋ค. ๊ทธ๋ฌ๋ ๊ธฐ์ ์ ๊ฐ์ ์ง์ ์ ์ธ ๊ฑฐ๋ฆฌ ๋น๊ต๋ ๊ณ์ฐ๋์ด ๋งค์ฐ ํฌ๋ฉฐ, ์ค์ฐจ๊ฐ ํฌํจ๋ ์๋ชป๋ ๋งค์นญ ์ญ์ ๋ค์ ํฌํจ๋ ์ ์๋ค๋ ๋จ์ ์ด ์๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ํ์ ์ธ ๊ธฐ๋ฒ์ด FLANN๊ณผ Lowe์ Ratio Test์ด๋ค.
FLANN (Fast Library for Approximate Nearest Neighbors)
FLANN์ ๊ณ ์ฐจ์ ๊ณต๊ฐ์์์ ํจ์จ์ ์ธ ์ต๊ทผ์ ์ด์ ํ์(Nearest Neighbor Search)์ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์ ํํ ๋งค์นญ๋ณด๋ค๋ ๊ทผ์ฌ์ (approximate) ์ต๊ทผ์ ์ด์์ ๋น ๋ฅด๊ฒ ์ฐพ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ค. ์ด๋ ์ด๋ฏธ์ง ๋ด ๋ค์์ ํคํฌ์ธํธ ๊ฐ ํน์ง ๋ฒกํฐ๋ฅผ ๋น๊ตํ ๋ ๊ณ์ฐ ๋ณต์ก๋๋ฅผ ์ค์ด๋ ๋ฐ ์ ๋ฆฌํ๋ค.
OpenCV์์๋ DescriptorMatcher::create(DescriptorMatcher::FLANNBASED)๋ฅผ ํตํด FLANN ๊ธฐ๋ฐ ๋งค์นญ๊ธฐ๋ฅผ ์์ฑํ ์ ์๊ณ , ๋ด๋ถ์ ์ผ๋ก๋ KD-Tree ํน์ KMeans Tree ๋ฑ์ ์๊ณ ๋ฆฌ์ฆ์ด ์๋์ผ๋ก ์ ํ๋์ด ์ฌ์ฉ๋๋ค.
Lowe’s Ratio Test
FLANN๊ณผ ๊ฐ์ ๊ทผ์ฌ ๋งค์นญ ๊ธฐ๋ฒ์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์๋ชป๋ ๋งค์นญ์ ํํฐ๋งํ๋ ํ์ฒ๋ฆฌ ๋จ๊ณ๊ฐ ํ์์ ์ด๋ค.
์ด์ ๋ํด David Lowe๋ ๊ทธ์ SIFT ๋ ผ๋ฌธ์์ ๋ค์๊ณผ ๊ฐ์ Ratio Test๋ฅผ ์ ์ํ์๋ค.
Ratio Test๋ ๊ฐ ํคํฌ์ธํธ์ ๋ํด ๊ฐ์ฅ ๊ฐ๊น์ด ๋งค์นญ ๋ฒกํฐ(nearest neighbor)์ ๋ ๋ฒ์งธ๋ก ๊ฐ๊น์ด ๋งค์นญ ๋ฒกํฐ(second nearest neighbor) ๊ฐ์ ๊ฑฐ๋ฆฌ ๋น์จ์ ์ด์ฉํ์ฌ ๋ชจํธํ ๋งค์นญ์ ์ ๊ฑฐํ๋ ๋ฐฉ๋ฒ์ด๋ค. ๋ ๋ฒกํฐ ๊ฐ์ ๊ฑฐ๋ฆฌ๊ฐ ์ถฉ๋ถํ ์ฐจ์ด๊ฐ ๋ ๊ฒฝ์ฐ, ์ฒซ ๋ฒ์งธ ๋งค์นญ์ด ์ ๋ขฐํ ์ ์๋ ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.

์ฝ๋์์๋ ๋ค์์ ์กฐ๊ฑด๋ฌธ์ ํตํด ๊ตฌํ๋๋ค:
if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
์ฌ๊ธฐ์ ratio_thresh๋ ์ผ๋ฐ์ ์ผ๋ก 0.7~0.8 ์ฌ์ด์ ๊ฐ์ ๊ฐ์ง๋ฉฐ, ์ฌ๊ธฐ์์๋ 0.8์ ์ฌ์ฉํ์๋ค.
์ด ์๊ณ๊ฐ๋ณด๋ค ๊ฑฐ๋ฆฌ ์ฐจ์ด๊ฐ ํฌ๋ฉด, ํด๋น ๋งค์นญ์ ์ ๋ขฐํ ์ ์๋(good match) ๊ฒ์ผ๋ก ๊ฐ์ฃผํ๋ค.

C++ OpenCV Code
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/features2d.hpp"
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, const char* argv[])
{
// ์ด๋ฏธ์ง ์ฝ๊ธฐ
Mat img_1 = imread("peters_1.jpg", IMREAD_COLOR);
if (img_1.empty()) {
std::cerr << "Image load failed!" << std::endl;
return -1;
}
Mat img_2 = imread("peters_2.jpg", IMREAD_COLOR);
if (img_2.empty()) {
std::cerr << "Image load failed!" << std::endl;
return -1;
}
//-- Step 1: Detect the keypoints
Ptr<SIFT> detector = SIFT::create();
std::vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
detector->detectAndCompute(img_1, noArray(), keypoints1, descriptors1);
detector->detectAndCompute(img_2, noArray(), keypoints2, descriptors2);
//-- Step 2: Matching descriptor vectors with a brute force matcher
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(DescriptorMatcher::BRUTEFORCE);
std::vector< DMatch > matches;
matcher->match(descriptors1, descriptors2, matches);
//-- Step 2: Matching descriptor vectors with a FLAN
Ptr<DescriptorMatcher> matcher2 = DescriptorMatcher::create(DescriptorMatcher::FLANNBASED);
std::vector< std::vector<DMatch> > knn_matches;
matcher2->knnMatch(descriptors1, descriptors2, knn_matches, 2);
const float ratio_thresh = 0.7f;
std::vector<DMatch> good_matches;
for (size_t i = 0; i < knn_matches.size(); i++)
{
if (knn_matches[i][0].distance < ratio_thresh * knn_matches[i][1].distance)
{
good_matches.push_back(knn_matches[i][0]);
}
}
//-- Draw keypoints
Mat img_keypoints_1, img_keypoints_2, img_gray_1, img_gray_2;
cvtColor(img_1, img_gray_1, COLOR_BGR2GRAY);
cvtColor(img_2, img_gray_2, COLOR_BGR2GRAY);
drawKeypoints(img_gray_1, keypoints1, img_keypoints_1);
drawKeypoints(img_gray_2, keypoints2, img_keypoints_2);
//-- Draw matches
Mat img_matches, img_matches_2;
drawMatches(img_1, keypoints1, img_2, keypoints2, matches, img_matches);
drawMatches(img_1, keypoints1, img_2, keypoints2, good_matches, img_matches_2, Scalar::all(-1),
Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
//-- Show detected matches
imshow("Matches", img_matches);
imshow("Good Matches", img_matches_2);
// ๊ฒฐ๊ณผ ์ ์ฅ ํด๋
std::string output_dir = "./output/";
imwrite(output_dir + "sift_keypoint_1.png", img_keypoints_1);
imwrite(output_dir + "sift_keypoint_2.png", img_keypoints_2);
imwrite(output_dir + "sift_matching.png", img_matches);
imwrite(output_dir + "sift_matching_flan.png", img_matches_2);
waitKey();
return 0;
}