반응형




OpenCV의 SURF 예제를 테스트  해보았습니다. 장면 이미지에서 특정 물체를 찾아내는 이미지 매칭 테스트입니다.


업데이트

2019. 3.  4

2019. 5. 11



윈도우에서 진행하려면  아래 글에 나온대로 OpenCV을 다시 컴파일한 후 진행해야 합니다.


Visual Studio 2017용으로 OpenCV 4.0.1 빌드 하기 (opencv_contrib 포함)

http://webnautes.tistory.com/1036


Visual Studio 2019용으로 OpenCV 4.1.0 빌드 하기 (Extra 모듈 contrib 포함)

https://webnautes.tistory.com/1329



코드는 다음 링크에 있는 것을 수정해서 사용했습니다.

https://github.com/opencv/opencv_contrib/blob/master/modules/xfeatures2d/samples/surf_matcher.cpp



#include <iostream>
#include <stdio.h>
#include "opencv2/core.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/ocl.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/calib3d.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xfeatures2d.hpp"

using namespace cv;
using namespace cv::xfeatures2d;

const int LOOP_NUM = 10;
const int GOOD_PTS_MAX = 50;
const float GOOD_PORTION = 0.15f;

int64 work_begin = 0;
int64 work_end = 0;

static void workBegin()
{
work_begin = getTickCount();
}

static void workEnd()
{
work_end = getTickCount() - work_begin;
}

static double getTime()
{
return work_end / ((double)getTickFrequency()) * 1000.;
}

struct SURFDetector
{
Ptr<Feature2D> surf;
SURFDetector(double hessian = 800.0)
{
surf = SURF::create(hessian);
}
template<class T>
void operator()(const T& in, const T& mask, std::vector<cv::KeyPoint>& pts, T& descriptors, bool useProvided = false)
{
surf->detectAndCompute(in, mask, pts, descriptors, useProvided);
}
};

template<class KPMatcher>
struct SURFMatcher
{
KPMatcher matcher;
template<class T>
void match(const T& in1, const T& in2, std::vector<cv::DMatch>& matches)
{
matcher.match(in1, in2, matches);
}
};

static Mat drawGoodMatches(
const Mat& img1,
const Mat& img2,
const std::vector<KeyPoint>& keypoints1,
const std::vector<KeyPoint>& keypoints2,
std::vector<DMatch>& matches,
std::vector<Point2f>& scene_corners_
)
{
//-- Sort matches and preserve top 10% matches
std::sort(matches.begin(), matches.end());
std::vector< DMatch > good_matches;
double minDist = matches.front().distance;
double maxDist = matches.back().distance;

const int ptsPairs = std::min(GOOD_PTS_MAX, (int)(matches.size() * GOOD_PORTION));
for (int i = 0; i < ptsPairs; i++)
{
good_matches.push_back(matches[i]);
}
std::cout << "\nMax distance: " << maxDist << std::endl;
std::cout << "Min distance: " << minDist << std::endl;

std::cout << "Calculating homography using " << ptsPairs << " point pairs." << std::endl;

// drawing the results
Mat img_matches;


drawMatches(img1, keypoints1, img2, keypoints2,
good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);


//-- Localize the object
std::vector<Point2f> obj;
std::vector<Point2f> scene;

for (size_t i = 0; i < good_matches.size(); i++)
{
//-- Get the keypoints from the good matches
obj.push_back(keypoints1[good_matches[i].queryIdx].pt);
scene.push_back(keypoints2[good_matches[i].trainIdx].pt);
}
//-- Get the corners from the image_1 ( the object to be "detected" )
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point(0, 0);
obj_corners[1] = Point(img1.cols, 0);
obj_corners[2] = Point(img1.cols, img1.rows);
obj_corners[3] = Point(0, img1.rows);
std::vector<Point2f> scene_corners(4);

Mat H = findHomography(obj, scene, RANSAC);
perspectiveTransform(obj_corners, scene_corners, H);

scene_corners_ = scene_corners;

//-- Draw lines between the corners (the mapped object in the scene - image_2 )
line(img_matches,
scene_corners[0] + Point2f((float)img1.cols, 0), scene_corners[1] + Point2f((float)img1.cols, 0),
Scalar(0, 255, 0), 2, LINE_AA);
line(img_matches,
scene_corners[1] + Point2f((float)img1.cols, 0), scene_corners[2] + Point2f((float)img1.cols, 0),
Scalar(0, 255, 0), 2, LINE_AA);
line(img_matches,
scene_corners[2] + Point2f((float)img1.cols, 0), scene_corners[3] + Point2f((float)img1.cols, 0),
Scalar(0, 255, 0), 2, LINE_AA);
line(img_matches,
scene_corners[3] + Point2f((float)img1.cols, 0), scene_corners[0] + Point2f((float)img1.cols, 0),
Scalar(0, 255, 0), 2, LINE_AA);

return img_matches;
}

////////////////////////////////////////////////////
// This program demonstrates the usage of SURF_OCL.
// use cpu findHomography interface to calculate the transformation matrix
int main(int argc, char* argv[])
{

UMat img1, img2;

std::string outpath = "output.jpg";

std::string leftName = "object.jpg";
imread(leftName, IMREAD_GRAYSCALE).copyTo(img1);
if (img1.empty())
{
std::cout << "Couldn't load " << leftName << std::endl;
return EXIT_FAILURE;
}

std::string rightName = "scene.jpg";
imread(rightName, IMREAD_GRAYSCALE).copyTo(img2);
if (img2.empty())
{
std::cout << "Couldn't load " << rightName << std::endl;
return EXIT_FAILURE;
}

double surf_time = 0.;

//declare input/output
std::vector<KeyPoint> keypoints1, keypoints2;
std::vector<DMatch> matches;

UMat _descriptors1, _descriptors2;
Mat descriptors1 = _descriptors1.getMat(ACCESS_RW),
descriptors2 = _descriptors2.getMat(ACCESS_RW);

//instantiate detectors/matchers
SURFDetector surf;

SURFMatcher<BFMatcher> matcher;

//-- start of timing section

for (int i = 0; i <= LOOP_NUM; i++)
{
if (i == 1) workBegin();
surf(img1.getMat(ACCESS_READ), Mat(), keypoints1, descriptors1);
surf(img2.getMat(ACCESS_READ), Mat(), keypoints2, descriptors2);
matcher.match(descriptors1, descriptors2, matches);
}
workEnd();
std::cout << "FOUND " << keypoints1.size() << " keypoints on first image" << std::endl;
std::cout << "FOUND " << keypoints2.size() << " keypoints on second image" << std::endl;

surf_time = getTime();
std::cout << "SURF run time: " << surf_time / LOOP_NUM << " ms" << std::endl << "\n";


std::vector<Point2f> corner;
Mat img_matches = drawGoodMatches(img1.getMat(ACCESS_READ), img2.getMat(ACCESS_READ), keypoints1, keypoints2, matches, corner);

//-- Show detected matches
namedWindow("surf matches", 0);
imshow("surf matches", img_matches);
imwrite(outpath, img_matches);


// 일치하는 오브젝트 위치 표시하는 사각형 그리기
cvtColor(img2, img2, COLOR_GRAY2BGR);
line(img2, corner[0], corner[1], Scalar(0, 255, 0), 2, LINE_AA);
line(img2, corner[1], corner[2], Scalar(0, 255, 0), 2, LINE_AA);
line(img2, corner[2], corner[3], Scalar(0, 255, 0), 2, LINE_AA);
line(img2, corner[3], corner[0], Scalar(0, 255, 0), 2, LINE_AA);
imshow("draw square", img2);


waitKey(0);
return EXIT_SUCCESS;
}




장면이 되는 사진(scene.jpg)을 찍고, 그 중 물체(object.jpg) 하나만 따로 찍어서 테스트했습니다.

입력 이미지 파일은 cpp 파일이 있는 위치에 저장했습니다.


예제 실행 결과는 object.jpg에 저장됩니다. 확인해보면 실제 입력 이미지 2장을 붙여놓고 매칭되는 포인트를 선으로 연결한 것을 볼 수 있습니다.  





프로그램을 실행하면 두개의 창이 보입니다.


surf matches 창은 오브젝트 이미지와 장면 이미지를 붙여서 보여주며 매칭되는 포인트를 선으로 연결후..

찾은 오브젝트를 초록색 사각형으로 그려줍니다.





draw square 창은 장면 이미지만 보여주며.. 찾은 오브젝트 위치에 초록색 사각형으로 표시해줍니다.





두 장의 이미지에서 각각 키포인트를 뽑아내고 SURF를 수행한 것까지해서 2초 정도 걸렸습니다.

입력 이미지의 크기나 파라미터 변경에 따라 실행시간은 달라질 수 있습니다.


FOUND 400 keypoints on first image

FOUND 1046 keypoints on second image

SURF run time: 1717.12 ms



Max distance: 0.530129

Min distance: 0.0675935

Calculating homography using 50 point pairs.




반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts