14.3 특징점 매칭
14.3.1 OpenCV 특징점 매칭
특징점 매칭 : 두 영상에서 추출한 특징점 기술자를 비교하여 서로 비슷한 특징점을 찾는 작업 (크기 불변 특징점으로부터 구한 기술자를 매칭하면 크기와 회전에 강인한 영상 매칭을 수행가능)
DMatch 클래스 : 한 장의 영상에서 추출한 특징점과 다른 한 장의 영상, 또는 여러 영상에서 추출한 특징점 사이의 매칭 정보를 표현
BFMatcher 클래스 : 질의 기술자 집합에 있는 모든 기술자와 훈련 기술자 집합에 있는 모든 기술자 사이의 거리를 계산하고, 이 중 가장 거리가 작은 기술자를 찾아 매칭하는 방식 (매우 직관적이지만 특징점 개수가 늘어날수록 거리 계산 횟수가 급격하게 늘어날 수 있음)
FlannBaseMatcher 클래스 : 근사화된 거리 계산 방법을 사용하므로 가장 거리가 작은 특징점을 찾지 못할 수 있지만 매우 빠르게 동작함
drawMatches() 함수 : 두 매칭 입력 영상을 가로로 이어 붙이고, 각 영상에서 추출한 특징점과 매칭 결과를 다양한 색상으로 표시한 결과 영상을 생성
// 키포인트 매칭
void keypoint_matching()
{
Mat src1 = imread("box.png", IMREAD_GRAYSCALE);
Mat src2 = imread("box_in_scene.png", IMREAD_GRAYSCALE);
if (src1.empty() || src2.empty())
{
return;
}
Ptr<Feature2D> feature = ORB::create();
vector<KeyPoint> keypoints1, keypoints2;
Mat desc1, desc2;
feature->detectAndCompute(src1, Mat(), keypoints1, desc1); // 특징점 검출
feature->detectAndCompute(src2, Mat(), keypoints2, desc2);
Ptr<DescriptorMatcher> matcher = BFMatcher::create(NORM_HAMMING);
// 기술자 거리 계산 방식은 해밍 거리 사용
vector<DMatch> matches;
matcher->match(desc1, desc2, matches); // 기술자를 서로 매칭하여 matches에 저장
Mat dst;
drawMatches(src1, keypoints1, src2, keypoints2, matches, dst);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
// 키포인트 매칭 후 좋은 매칭 선별
void good_matching()
{
Mat src1 = imread("box.png", IMREAD_GRAYSCALE);
Mat src2 = imread("box_in_scene.png", IMREAD_GRAYSCALE);
if (src1.empty() || src2.empty())
{
return;
}
Ptr<Feature2D> feature = ORB::create();
vector<KeyPoint> keypoints1, keypoints2;
Mat desc1, desc2;
feature->detectAndCompute(src1, Mat(), keypoints1, desc1);
feature->detectAndCompute(src2, Mat(), keypoints2, desc2);
Ptr<DescriptorMatcher> matcher = BFMatcher::create(NORM_HAMMING);
vector<DMatch> matches;
matcher->match(desc1, desc2, matches);
std::sort(matches.begin(), matches.end());
vector<DMatch> good_matches(matches.begin(), matches.begin() + 50);
// 정렬된 매칭 결과에서 상위 50개 매칭 결과를 good_matches에 저장
Mat dst;
drawMatches(src1, keypoints1, src2, keypoints2, good_matches, dst, Scalar::all(-1),
Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
14.3.2 호모그래피와 영상 매칭
특징점 매칭 결과로부터 두 영상의 호모그래피를 계산하고, 이를 이용하여 크기가 다르고 회전이 되어 있는 객체를 정확하게 매칭하는 방법
호모그래피 : 3차원 공간상의 평면을 서로 다른 시점에서 바라봤을 때 획득되는 영상 사이의 관계를 나타내는 용어 (수학적으로 하나의 평면을 다른 평면으로 투시 변환하는 것과 관계가 있음)
findHomography() 함수 : 두 영상 평면에서 추출된 특징점 매칭 정보로부터 호모그래피를 계산할 때 사용할 수 있는 함수
method 인자에 기본값인 0을 지정하면 입력 점과 출력 점을 모두 사용하는 최소자승법으로 호모그래피 행렬을 계산. 그러나 일반적으로 특징점 매칭 결과로부터 호모그래피를 계산할 깨 최소자승법을 사용하면 호모그래피가 제대로 계산되지 않음. 오차가 큰 입력 정보를 이상치라고 부르며, 이상치가 많을 때에는 method 인자를 LMEDS, RANSAC, RHO 방법으로 설정.
LMEDS : 이상치가 50% 이하인 경우 올바르게 작동
RANSAC, RHO : 이상치가 50% 이상인 경우에도 가능하지만 srcPoints와 dstPoints 에 저장된 점이 이상치가 아니라고 판단하기 위한 임계값을 설정해야 함. 이 값은 ransacReprojThreshold 인자로 지정. 지정된 임계값보다 작으면 정상치로 간주
// 키포인트 매칭 및 호모그래피 계산
void find_homogrphy()
{
Mat src1 = imread("box.png", IMREAD_GRAYSCALE);
Mat src2 = imread("box_in_scene.png", IMREAD_GRAYSCALE);
if (src1.empty() || src2.empty())
{
return;
}
Ptr<Feature2D> feature = ORB::create();
vector<KeyPoint> keypoints1, keypoints2;
Mat desc1, desc2;
feature->detectAndCompute(src1, Mat(), keypoints1, desc1);
feature->detectAndCompute(src2, Mat(), keypoints2, desc2);
Ptr<DescriptorMatcher> matcher = BFMatcher::create(NORM_HAMMING);
vector<DMatch> matches;
matcher->match(desc1, desc2, matches);
std::sort(matches.begin(), matches.end());
vector<DMatch> good_matches(matches.begin(), matches.begin() + 50);
Mat dst;
drawMatches(src1, keypoints1, src2, keypoints2, good_matches, dst, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
vector<Point2f> pts1, pts2;
for (size_t i = 0; i < good_matches.size(); i++)
{
pts1.push_back(keypoints1[good_matches[i].queryIdx].pt);
pts2.push_back(keypoints2[good_matches[i].trainIdx].pt);
}
// good_matches 매칭 결과에 저장된 질의 영상과 훈련 영상의 특징점 좌표를 추출하여 pts1과 pts2에 저장
Mat H = findHomography(pts1, pts2, RANSAC);
// 호모그래피 행렬을 구하여 H에 저장. RANSAC 알고리즘 사용
vector<Point2f> corners1, corners2;
corners1.push_back(Point2f(0, 0));
corners1.push_back(Point2f(src1.cols - 1.f, 0));
corners1.push_back(Point2f(src1.cols - 1.f, src1.rows - 1.f));
corners1.push_back(Point2f(0, src1.rows - 1.f));
perspectiveTransform(corners1, corners2, H);
// src1 영상의 네 모서리 점을 corners1에 저장한 후, 호모그래피 행렬 H를 이용하여
// 이 점들이 이동하는 위치를 계산하여 corners2에 저장
vector<Point> corners_dst;
for (Point2f pt : corners2)
{
corners_dst.push_back(Point(cvRound(pt.x + src1.cols), cvRound(pt.y)));
}
polylines(dst, corners_dst, true, Scalar(0, 255, 0), 2, LINE_AA);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
14.4 영상 이어 붙이기
여러 장의 영상을 서로 이어 붙여서 하나의 큰 영상을 만드는 기법 (파노라마 영상)
입력으로 사용할 영상은 서로 일정 비율 이상으로 겹치는 영역이 존재해야 하며, 서로 같은 위치를 분간할 수 있도록 유효한 특징점이 많이 있어야 함
입력 영상에서 특징점을 검출하고, 서로 매칭을 수행하여 호모그래피를 구해야 함. 그리고 구해진 호모그래피 행렬을 기반으로 입력 영상을 변형하여 서로 이어 붙이는 작업을 수행. 저연스럽게 보이도록 이어 붙인 영상의 밝기를 적절하게 보정하는 블렌딩 처리도 해야 함. Stitcher 클래스 제공
// 영상 이어 붙이기
int main(int argc, char* argv[])
{
if (argc < 3)
{
cerr << "Usage : stitchong.exe img1.jpg img2.jpg img3.jpg ---" << endl;
return -1;
}
vector<Mat> imgs;
for (int i = 1; i < argc; i++)
{
Mat img = imread(argv[i]);
if (img.empty())
{
return -1;
}
imgs.push_back(img);
}
Ptr<Stitcher> stitcher = Stitcher::create();
Mat dst;
Stitcher::Status status = stitcher->stitch(imgs, dst);
if (status != Stitcher::Status::OK)
{
return -1;
}
imwrite("result.jpg", dst);
imshow("dst", dst);
waitKey();
return 0;
}
#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 22 (0) | 2022.09.02 |
---|---|
#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 21 (0) | 2022.08.19 |
#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 19 (0) | 2022.08.02 |
#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 18 (0) | 2022.07.27 |
#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 17 (0) | 2022.07.21 |