2D 이미지에서 Keypoints과 Discriptor를 검출하고, 이를 통해 이미지 스티칭하는 방법의 파이프라인을 공부해보았다.
확실한 이해를 위해 OpenCV에서 제공하는 Stitching API를 사용하지 않고 직접 구현해 보았다.
사용 라이브러리
Opencv
ORB와 SIFT의 소요 시간과 정확성을 비교하기 위해 두 번의 작업을 진행해보았다.
cv::Ptr<cv::Feature2D> detector = cv::SIFT::create();
// cv::Ptr<cv::Feature2D> detector = cv::ORB::create();
cv::Mat desc1, desc2;
std::vector<cv::KeyPoint> keypoints1, keypoints2;
detector->detectAndCompute(src1, cv::Mat(), keypoints1, desc1);
detector->detectAndCompute(src2, cv::Mat(), keypoints2, desc2);
FlannBasedMatcher를 사용하기 위해선 Discriptor를 CV_32F
데이터 타입으로 무조건 바꿔 주어야 한다.
desc1.convertTo(desc1, CV_32F);
desc2.convertTo(desc2, CV_32F);
cv::Ptr<cv::DescriptorMatcher> matcher = cv::FlannBasedMatcher::create();
std::vector<cv::DMatch> matches;
matcher->match(desc1, desc2, matches);
Distance가 작을 수록 좋은 값이라고 배웠던 것 같은데 나중 결과를 보면 꼭 그렇지만은 않은 것 같다.
std::sort(matches.begin(), matches.end());
std::vector<cv::DMatch> good_matches(matches.begin(), matches.begin() + 20);
std::vector<cv::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);
}
cv::Mat H = cv::findHomography(pts2, pts1, cv::RANSAC);
20개의 필터링된 포인트들 만으로도 RANSAC을 통해 Homography가 잘 추출되어 스티칭이 잘 된다. 하지만 시간이 오래 걸린다.
시간은 3배 감소 하였지만 필터링된 20개의 포인트로는 Homography가 잘 추출되지 않으며, 필터링 되지 않은 포인트들로는 스티칭이 잘 이루어졌다.
유익한 정보 감사합니다!
그런데 혹시 5번째에서 이미지를 붙이는 건 어떻게 하셨는지 알 수 있을까요? warpPerspective을 써서 두 번째 이미지를 변환하는 것까지는 했는데 그 다음은 도저히 모르겠네요...