[OpenCV] Webcam을 이용해서 SIFT 매칭 예제

이전에 OpenCV를 이용해서 간단히 웹캠을 화면에 출력하는 코드 템플릿을 올렸다. 그런데, 포스팅이 심심하다는 생각이 들어서 얼굴 인식을 넣어서 포스팅을 한번 더 했었다.

2013/09/12 - [Research/Vision] - [OpenCV] OpenCV를 이용해 기본적인 Webcam 출력하기

2013/09/13 - [Research/Vision] - [OpenCV] Webcam을 이용해서 얼굴을 인식하는 간단한 예제


기왕 한 김에 이번엔 SIFT도 포스팅해야겠다고 생각했다. OpenCV에 라이브러리로 들어가 있으니 아주 쉽게 사용할 수 있다. 많이들 쓰는 lena를 DB로 써서 웹캠과 매칭되는 걸 테스트해보았다. 역시 명불허전이다. 대충 들고 있어도 매칭이 잘 된다.

/*
*   @file ocv_cam_sift.cc
*   @brief SIFT using OpenCV with Webcam
*   @author http://thinkpiece.tistory.com
*/

#include <opencv/cv.h>
#include <opencv/highgui.h>

#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/nonfree/features2d.hpp>

#include <iostream>
#include <cstdlib>

int main(int argc, char *argv[])
{

    // -------------------------------------------------------------------------
    // webcam routine
    cv::VideoCapture capture(0);

    if( !capture.isOpened() ) {
        std::cerr << "Could not open camera" << std::endl;
        return 0;
    }

    // create a window
    cv::namedWindow("webcam",1);

    // -------------------------------------------------------------------------
    // SIFT configuration
    if ( argc != 2 ) {
        std::cerr << "usage: ocv_cam_sift filename" << std::endl;
        return 0;
    }

    cv::Mat db_original = cv::imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
    cv::Mat db;

    cv::resize( db_original, db, cv::Size(db_original.cols/2,
        db_original.rows/2),0,0,CV_INTER_NN);

    // SIFT feature detector and feature extractor
    cv::SiftFeatureDetector detector( 0.05, 5.0 );
    cv::SiftDescriptorExtractor extractor( 3.0 );

    // Feature detection
    std::vector<cv::KeyPoint> kps_db;
    detector.detect( db, kps_db );

    // Feature description
    cv::Mat dscr_db;
    extractor.compute( db, kps_db, dscr_db );

    while (true) {
        bool frame_valid = true;

        cv::Mat frame_original;
        cv::Mat frame;

        try {
            capture >> frame_original; // get a new frame from webcam
            cv::resize(frame_original,frame,cv::Size(frame_original.cols/2,
                frame_original.rows/2),0,0,CV_INTER_NN); // downsample 1/2x
        } catch(cv::Exception& e) {
            std::cerr << "Exception occurred. Ignoring frame... " << e.err
                      << std::endl;
            frame_valid = false;
        }

        if (frame_valid) {
            try {
                // convert captured frame to gray scale & equalize
                cv::Mat grayframe;
                cv::cvtColor(frame, grayframe, CV_BGR2GRAY);
                cv::equalizeHist(grayframe,grayframe);

                // -------------------------------------------------------------
                // face detection routine

                // keypoint detection
                std::vector<cv::KeyPoint> kps_frame;
                detector.detect( grayframe, kps_frame);

                // keypoint description
                cv::Mat dscr_frame;
                extractor.compute( grayframe, kps_frame, dscr_frame);

                // matching using FLANN matcher
                cv::FlannBasedMatcher matcher;                
                std::vector<cv::DMatch> matches;
                matcher.match(dscr_db, dscr_frame, matches);

                double max_dist = 0.0, min_dist = 100.0;

                for(int i=0; i<matches.size(); i++) {
                    double dist = matches[i].distance;
                    if ( dist < min_dist ) min_dist = dist;
                    if ( dist > max_dist ) max_dist = dist;
                }

                // drawing only good matches (dist less than 2*min_dist)
                std::vector<cv::DMatch> good_matches;

                for (int i=0; i<matches.size(); i++) {
                    if (matches[i].distance <= 2*min_dist) {
                        good_matches.push_back( matches[i] );
                    }
                }

                cv::Mat img_matches;
                cv::drawMatches(db, kps_db, frame, kps_frame, good_matches,
                  img_matches, cv::Scalar::all(-1), cv::Scalar::all(-1), 
                  std::vector<char>(),
                  cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

                // print the output
                cv::imshow("webcam", img_matches);

            } catch(cv::Exception& e) {
                std::cerr << "Exception occurred. Ignoring frame... " << e.err
                          << std::endl;
            }
        }
        if (cv::waitKey(30) >= 0) break;
    }

    // VideoCapture automatically deallocate camera object
    return 0;
}
  • 게스트 썸네일
    새내기
    2014.04.08 18:28 신고

    다른 소스들은 다 되고 SIFT를 이용하여 웹캠에서 매칭은 자꾸 오류가 나네요.

    1>test.obj : error LNK2019: "public: __thiscall cv::SIFT::SIFT(int,int,double,double,double)" (??0SIFT@cv@@QAE@HHNNN@Z) 외부 기호(참조 위치: _main 함수)에서 확인하지 못했습니다.
    1>C:\Users\kwu\Desktop\realtime test\test\Debug\test.exe : fatal error LNK1120: 1개의 확인할 수 없는 외부 참조입니다.

    이렇게 뜨는데 오류를 못잡겠네요 ㅠㅠ

  • 게스트 썸네일
    새내기
    2014.04.09 16:46 신고

    아 해결됐습니다.
    라이브러리 경로가 꼬여있던게 문제 였네요 ㅠㅠ
    덕분에 이 사이트에서 많은 것을 배우고 갑니다.
    감사합니다~^^

    • 게스트 썸네일
      2014.04.09 17:43

      비밀댓글입니다

  • 게스트 썸네일
    방방
    2014.04.29 23:14 신고

    안녕하세요 게시물 잘 보고 있습니다.
    코드 내에 검출하고자하는 이미지 파일 부분이 몇번째줄인가요??
    lena로 찾아봐도 없고.. 잘 못찾겠습니다
    도움주시면 감사하겠습니다!

    • 게스트 썸네일
      2014.04.30 13:47 신고

      cv::Mat db_original = cv::imread(argv[1],CV_LOAD_IMAGE_GRAYSCALE);

      이 부분이구요. 정확히 말해서는 lena.jpg 라고 입력하는 부분은 없고, 프로그램을 실행할 때 첫번째 매개변수 (argv[1])로 넘겨받습니다.

      $ ./test lena.jpg

      이런 식으로 실행하면 되겠죠!

  • 게스트 썸네일
    이클립스
    2014.07.10 14:52 신고

    좋은 자료 감사합니다

    std::vector<cv::KeyPoint> kps_frame;
    detector.detect( grayframe, kps_frame);

    실습 중에 이 부분에서 자꾸 연산 속도가 아주 많이 저하 되는데...
    이유와 해결 방법을 알고 싶습니다..ㅠ

    • 게스트 썸네일
      2014.07.15 23:14 신고

      detector.detect() 함수에서는 입력받은 영상(grayframe)에서 특징점을 추출하게되는데, 이 부분이 원래 연산 시간이 오래 걸립니다.
      이를 빠르게 하기 위해서는 1) 직접 detect 함수를 구현하시는 방법(최적화해서 작성하신다면), 2) GPU기반으로 구현된 함수를 이용하는 방법, 3) 다른 종류의 특징점추출 알고리즘을 이용하는 방법, 예를 들어 FAST, SURF 등... 의 3가지 방법이 있겠네요.

  • 게스트 썸네일
    2014.07.23 13:26

    비밀댓글입니다

  • 게스트 썸네일
    희재
    2014.08.05 14:54 신고

    이전 얼굴인식 자료는 문제가 없었는데 SIFT는 실행하게 되면 창이 저절로 꺼지는데 왜 그러는건가요? 오류는 없고 워닝에서 타입에 관한거 몇개 뜨긴 하는데 그문제인가요??

    • 게스트 썸네일
      2014.08.10 07:42 신고

      워닝에 대해서 남겨주시면 더 많이 도와드릴 수 있을 것 같아요.

  • 게스트 썸네일
    희재
    2014.08.11 12:41 신고

    워닝에러는 3가지 거든요 유니코드 문제 C4819 & 데이터 손실 C4244 에러 & 부호에러 C4018 세가지요 이렇게 말씀드리면 이해 되실까요??
    실례가 안된다면 문자로좀 여쭙고 싶은데 010 2042 5767로 문자부탁드리거나 간단한거면 답글좀 부탁드릴게요

    • 게스트 썸네일
      2014.08.19 02:55 신고

      혹시 code에서 어느 부분이 문제가 되었다는 line number도 같이 뜨지 않나요? warning line number 알려주시면 아마 답해드릴 수 있을 것 같아요.

  • 게스트 썸네일
    정말궁금.
    2014.08.14 10:53 신고

    안녕하세요 계속 해결이 안되어 질문드립니다. 위의 코드는 잘 실행이 됩니다. 그런데 resize를 빼고 하니 에러가 나는 것을 확인했습니다. 혹시 webcam과 일반 이미지를 matching할 때 size에 따라 문제가 생기는 것인지 궁금하네요..
    calibration을 해야해서 size 변경은 피하고 싶은데 방법이 없을지 궁금합니다..
    drawmatches에서 지속적으로 error가 나옵니다..

    • 게스트 썸네일
      2014.08.19 02:57 신고

      위 코드에서 resize가 2번 나오는데 2개 모두 제거하셨는지요?

  • 게스트 썸네일
    MDCS
    2014.08.19 16:13 신고

    안녕하세요
    라인 넘버 95
    matcher.match(dscr_db, dscr_frame, matches);

    여기서 exception이 발생하면서 꺼저 버리는데, 이건 왜그런지 알수 있나요? 다른 코드에서 knnMatch를 사용해도 동일한 결과가 계속나오네요...

    opencv.exe의 0x5d31aa16에 처리되지 않은 예외가 있습니다. 0xC0000005: 0x00000006 위치를 읽는 동안 액세스 위반이 발생했습니다.

  • 게스트 썸네일
    비싼폰
    2014.11.25 12:55 신고

    위에 까지는 실행이 되는데
    이 밑 부분부터 진행이 안되는데 알려 주세요

    extractor.compute(db,kps_db,dscr_db);

    while (true) {
    bool frame_valid = true;

    cv::Mat frame_original;
    cv::Mat frame;

    try
    {
    capture >> frame_original; // get a new frame from webcam
    cv::resize(frame_original,frame,cv::Size(frame_original.cols/2,frame_original.rows/2),0,0,CV_INTER_NN); // downsample 1/2x
    }
    catch(cv::Exception& e)
    {
    std::cerr << "Exception occurred. Ignoring frame... " << e.err << std::endl;
    frame_valid = false;
    }

    if (frame_valid)
    {
    try
    {
    // convert captured frame to gray scale & equalize
    cv::Mat grayframe;
    cv::cvtColor(frame, grayframe, CV_BGR2GRAY);
    cv::equalizeHist(grayframe,grayframe);

    // -------------------------------------------------------------
    // face detection routine

    // keypoint detection
    std::vector<cv::KeyPoint> kps_frame;
    detector.detect( grayframe, kps_frame);

    // keypoint description
    cv::Mat dscr_frame;
    extractor.compute( grayframe, kps_frame, dscr_frame);

    // matching using FLANN matcher
    cv::FlannBasedMatcher matcher;
    std::vector<cv::DMatch> matches;
    matcher.match(dscr_db, dscr_frame, matches);

    double max_dist = 0.0, min_dist = 100.0;

    for(int i=0; i<matches.size(); i++) {
    double dist = matches[i].distance;
    if ( dist < min_dist ) min_dist = dist;
    if ( dist > max_dist ) max_dist = dist;
    }

    // drawing only good matches (dist less than 2*min_dist)
    std::vector<cv::DMatch> good_matches;

    for (int i=0; i<matches.size(); i++) {
    if (matches[i].distance <= 2*min_dist) {
    good_matches.push_back( matches[i] );
    }
    }

    cv::Mat img_matches;
    cv::drawMatches(db, kps_db, frame, kps_frame, good_matches,
    img_matches, cv::Scalar::all(-1), cv::Scalar::all(-1),
    std::vector<char>(),
    cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

    // print the output
    cv::imshow("webcam", img_matches);

    }
    catch(cv::Exception& e)
    {
    std::cerr << "Exception occurred. Ignoring frame... " << e.err
    << std::endl;
    }
    }
    if (cv::waitKey(30) >= 0) break;
    }

    // VideoCapture automatically deallocate camera object
    return 0;
    }

  • 게스트 썸네일
    2015.04.23 17:54

    비밀댓글입니다

  • 게스트 썸네일
    지나
    2016.07.12 17:15 신고

    에러는 안나는데 실행하면 웹캠도안켜지고 그냥 아무키나누르면 종료된다고 창만뜨네요
    에러가 없어서 더 힘듭니다 ㅜㅜ
    아 그리고 $./lena.jpg 이런식으로쓰고싶은데 어느자리어 어떻게 넣어야 할까요? 죄송해요 너무 잘몰라서 ㅜㅜ

  • 게스트 썸네일
    김민철
    2017.11.14 11:28 신고

    cv::SiftFeatureDetector detector(0.05, 5.0);
    cv::SiftDescriptorExtractor extractor(3.0);

    이부분에서 SIFT 파라미터 값들 설정해주는 것 같은데요.

    각각 저 값들이 어떤 파라미터인지 알려주실 수 있나요??