Mac에서 OpenCV 설치 및 예제 구동하기

주말에 아티클을 보다가 관심이 생겨 OpenCV를 잠깐 살펴봤다. OpenCV는 Computer Vision 오픈소스 라이브러리로, 제공하는 예제를 통해 Face Tracking 등을 구현해볼 수 있다. 초보자를 위한 튜토리얼은 많은데 생각처럼 잘 안되는 부분들이 있어 이 글을 작성했다. 이 글에서는 Mac OSX, xcode를 사용했다.

OpenCV는 brew를 통해서도 설치할 수 있는데, 내가 놓치고 있는 부분이 있는지 잘 안되서 리포지터리에서 받아 컴파일했다.

먼저 컴파일을 위해 cmake를 brew로 설치한다.

$ brew install cmake

리포지터리를 복제한 후 build, install까지 진행한다.

$ git clone https://github.com/Itseez/opencv.git && cd opencv
$ mkdir build
$ cd build
$ cmake -G "Unix Makefiles" ..
$ make -j8
$ sudo make install

이렇게 설치는 완료된다. /usr/local/lib에 설치된 libopencv-*.dylib를 확인할 수 있다.

예제 구동 과정

예제 코드를 그대로 실행하면 바로 되어야 하는데, 리포지터리에 올라가 있는 예제 프로젝트는 xcode 버전 문제로 열리지 않았다. 다음과 같은 과정으로 예제 코드를 확인할 수 있다.

  1. XCode를 실행해 Command Line Tool 프로젝트 생성
  2. Project Navigator에서 우클릭, “Add File To…” 클릭
  3. 파일 선택창이 뜨면 “/”를 입력해 네비게이션 패널을 불러와 /usr/local/lib을 입력
  4. libopencv_<어쩌고>.dylib 을 모두 선택. (아래 예제에서는 core, highgui, imgproc, objdetect만 있어도 구동 가능.)
  5. 프로젝트 파일을 눌러 Build Settings에서 “Header Search Paths”에 /usr/local/include, “Library Search Paths”에 /usr/local/lib을 추가.
  6. 예전 버전의 cascade를 내려받음
  7. 프로젝트 파일을 눌러 Build Phases > Copy Files에 위에서 받은 xml 파일을 추가하고 “Destination”을 Products Directory로 설정

위 과정을 끝내고 나면 main.cpp를 열어 코드를 다음과 같이 작성한다. 이 코드는 opencv 리포지터리에 있는 MacOSX 예제 코드다.

#include <CoreFoundation/CoreFoundation.h>
#include <cassert>

// Example showing how to read and write images
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>

const char  * WINDOW_NAME  = "Face Tracker";
const CFIndex CASCADE_NAME_LEN = 2048;
char    CASCADE_NAME[CASCADE_NAME_LEN] = "~/haarcascade_frontalface_alt2.xml";

using namespace std;

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

    const int scale = 2;

    // locate haar cascade from inside application bundle
    // (this is the mac way to package application resources)
    CFBundleRef mainBundle  = CFBundleGetMainBundle ();
    assert (mainBundle);
    CFURLRef    cascade_url = CFBundleCopyResourceURL (mainBundle, CFSTR("haarcascade_frontalface_alt2"), CFSTR("xml"), NULL);
    assert (cascade_url);
    Boolean     got_it      = CFURLGetFileSystemRepresentation (cascade_url, true,
                                                                reinterpret_cast<UInt8 *>(CASCADE_NAME), CASCADE_NAME_LEN);

    if (! got_it)
        abort ();

    // create all necessary instances
    cvNamedWindow (WINDOW_NAME, CV_WINDOW_AUTOSIZE);
    CvCapture * camera = cvCreateCameraCapture (CV_CAP_ANY);
    CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*) cvLoad (CASCADE_NAME, NULL, NULL, NULL);
    CvMemStorage* storage = cvCreateMemStorage(0);
    assert (storage);

    // you do own an iSight, don't you ?!?
    if (! camera)
        abort ();

   // did we load the cascade?!?
    if (! cascade)
        abort ();

    // get an initial frame and duplicate it for later work
    IplImage *  current_frame = cvQueryFrame (camera);
    IplImage *  draw_image    = cvCreateImage(cvSize (current_frame->width, current_frame->height), IPL_DEPTH_8U, 3);
    IplImage *  gray_image    = cvCreateImage(cvSize (current_frame->width, current_frame->height), IPL_DEPTH_8U, 1);
    IplImage *  small_image   = cvCreateImage(cvSize (current_frame->width / scale, current_frame->height / scale), IPL_DEPTH_8U, 1);
    assert (current_frame && gray_image && draw_image);

    // as long as there are images ...
    while ((current_frame = cvQueryFrame (camera)))
    {
        // convert to gray and downsize
        cvCvtColor (current_frame, gray_image, CV_BGR2GRAY);
        cvResize (gray_image, small_image, CV_INTER_LINEAR);

        // detect faces
        CvSeq* faces = cvHaarDetectObjects (small_image, cascade, storage,
                                            1.1, 2, CV_HAAR_DO_CANNY_PRUNING,
                                            cvSize (30, 30));

        // draw faces
        cvFlip (current_frame, draw_image, 1);
        for (int i = 0; i < (faces ? faces->total : 0); i++)
        {
            CvRect* r = (CvRect*) cvGetSeqElem (faces, i);
            CvPoint center;
            int radius;
            center.x = cvRound((small_image->width - r->width*0.5 - r->x) *scale);
            center.y = cvRound((r->y + r->height*0.5)*scale);
            radius = cvRound((r->width + r->height)*0.25*scale);
            cvCircle (draw_image, center, radius, CV_RGB(0,255,0), 3, 8, 0 );
        }

        // just show the image
        cvShowImage (WINDOW_NAME, draw_image);

        // wait a tenth of a second for keypress and window drawing
        int key = cvWaitKey (100);
        if (key == 'q' || key == 'Q')
            break;
    }

    // be nice and return no error
    return 0;
}

코드를 빌드 및 실행하면 앱이 실행되어 얼굴의 위치를 트래킹하는 모습을 확인할 수 있다. (신기해!)

트러블 슈팅

  • CASCADE_NAME~로 시작하지만 컴파일된 파일을 기준으로 하는 상대 경로.
  • cvLoad()에 에러가 생길 땐 xml 경로, 파일명 문제, 디버깅용 라이브러리를 넣었을 때 등의 문제라고. 내 경우에는 최신 버전의 xml이라서 에러가 났다. 위 과정처럼 구버전의 xml을 쓰면 구동은 되지만 별로 내키지 않는 해결 방법. (코드는 개선되었는데 예제는 예전 방식으로 되어 있다거나 한 것 같다.)
김용균

안녕하세요, 김용균입니다. 문제를 해결하기 위해 작고 단단한 코드를 작성하는 일을 합니다. 웹의 자유로운 접근성을 좋아합니다. 프로그래밍 언어, 소프트웨어 아키텍처, 커뮤니티에 관심이 많습니다.

이 글 공유하기

이 글이 유익했다면 주변에도 알려주세요!

페이스북으로 공유하기트위터로 공유하기링크드인으로 공유하기Email 보내기

주제별 목록

같은 주제의 다른 글을 읽어보고 싶다면 아래 링크를 확인하세요.

May 28, 2014

기계식키보드 Leopold FC700RT 갈축 사용기

2013년 2월 한국 다녀올 당시 레오폴드 키보드를 구입해서 왔다. 당시 회사에서 사용했던 이상한 키배열의 삼성 노트북 이후 키보드에 관심이 생겨서 관련 글을 읽다보니 기계식 키보드에 대해 좋은 인상을 받아 구입했다. 구입할 때 직접 타건해보고 구입…

April 27, 2014

사람은 자신이 가장 많은 시간을 함께 보내는 다섯명의 평균치다

{{< youtube //www.youtube.com/embed/ZRXE7lX8wA8 >}} 위 영상은 Dropbox의 창업자인 Drew Houston의 MIT 졸업축사 로 페이스북에서 보게 되었는데 인상적인 부분을 적어둔다. ‘ 사람은 자신이 가…