OpenCV 편하게 사용하기 (팁)

프로그래밍/opencv 2013.02.13 12:18

1. OpenCV 링크 편하게 하기

2. OpenCV 행렬 원소접근 편하게 하기

3. TypedMat 사용법




1. OpenCV 링크 편하게 하기


OpenCV를 이용한 프로그램을 작성하려면 필요한 헤더화일 및 라이브러리 파일들을 링크시키기 위해 이런 저런 세팅을 해 주어야 합니다.


저는 OpenCV 헤더파일 및 라이브러리 파일들을 다음과 같이 하나의 파일에 모두 넣어놓고 사용합니다 (물론 프로젝트 설정에서 opencv include directory, opencv library directory 는 별도로 설정해 주어야 합니다).


이제 opencv를 사용하고 싶으면 아래 파일만 include하면 ok입니다.


<use_opencv.h>

#ifndef __USE_OPENCV_H__

#define __USE_OPENCV_H__


#include "opencv2/highgui/highgui.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/core/core.hpp"

#include "opencv2/core/core_c.h"

#include "opencv2/highgui/highgui_c.h"

#include "opencv2/imgproc/imgproc_c.h"

#include "opencv2/video/video.hpp"

#include "opencv2/nonfree/nonfree.hpp"

#include "opencv2/videostab/videostab.hpp"

#include "opencv2/features2d/features2d.hpp"

#include "opencv2/objdetect/objdetect.hpp"

#include "opencv2/flann/miniflann.hpp"

#include "opencv2/photo/photo.hpp"

#include "opencv2/calib3d/calib3d.hpp"

#include "opencv2/ml/ml.hpp"

#include "opencv2/contrib/contrib.hpp"

#include "opencv2/ts/ts.hpp"

#include "opencv2/stitching/stitcher.hpp"

#include "opencv2/legacy/legacy.hpp"


#ifdef _DEBUG

      #pragma comment(lib,"opencv_core241d.lib")

      #pragma comment(lib,"opencv_highgui241d.lib")

      #pragma comment(lib,"opencv_imgproc241d.lib")

      #pragma comment(lib,"opencv_video241d.lib")

      #pragma comment(lib,"opencv_nonfree241d.lib")

      #pragma comment(lib,"opencv_videostab241d.lib")

      #pragma comment(lib,"opencv_features2d241d.lib")

      #pragma comment(lib,"opencv_objdetect241d.lib")

      #pragma comment(lib,"opencv_flann241d.lib")

      #pragma comment(lib,"opencv_photo241d.lib")

      #pragma comment(lib,"opencv_calib3d241d.lib")

      #pragma comment(lib,"opencv_ml241d.lib")

      #pragma comment(lib,"opencv_contrib241d.lib")

      #pragma comment(lib,"opencv_ts241d.lib")

      #pragma comment(lib,"opencv_stitching241d.lib")

      #pragma comment(lib,"opencv_legacy241d.lib")

#else

      #pragma comment(lib,"opencv_core241.lib")

      #pragma comment(lib,"opencv_highgui241.lib")

      #pragma comment(lib,"opencv_imgproc241.lib")

      #pragma comment(lib,"opencv_video241.lib")

      #pragma comment(lib,"opencv_nonfree241.lib")

      #pragma comment(lib,"opencv_videostab241.lib")

      #pragma comment(lib,"opencv_features2d241.lib")

      #pragma comment(lib,"opencv_objdetect241.lib")

      #pragma comment(lib,"opencv_flann241.lib")

      #pragma comment(lib,"opencv_photo241.lib")

      #pragma comment(lib,"opencv_calib3d241.lib")

      #pragma comment(lib,"opencv_ml241.lib")

      #pragma comment(lib,"opencv_contrib241.lib")

      #pragma comment(lib,"opencv_ts241.lib")

      #pragma comment(lib,"opencv_stitching241.lib")

      #pragma comment(lib,"opencv_legacy241.lib")

#endif  // _DEBUG


#endif  // __USE_OPENCV_H__



2. OpenCV 행렬 원소접근 편하게 하기


저는 opencv를 사용하면서 가장 불편한 점 중의 하나가 행렬(Mat)의 원소를 접근하는 것입니다. 특히나 영상처리를 하다보면 이미지 픽셀 단위로 처리를 할 필요가 있는데 OpenCV가 제공하는 원소 접근 방법(at)은 상당히 불편합니다.


그래서 아래와 같이 Wrapper class를 하나 작성해 보았습니다.


template<class T>

class TypedMat

{

        T** m_pData;

        int m_nChannels;

        int m_nRows, m_nCols;


public:

        TypedMat():m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0){}

        ~TypedMat(){if(m_pData) delete [] m_pData;}


        // OpenCV Mat 연동 (메모리 공유)

        void Attach(const cv::Mat& m);

        void Attach(const IplImage& m);

        TypedMat(const cv::Mat& m):m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0) { Attach(m);}

        TypedMat(const IplImage& m):m_pData(NULL),m_nChannels(1),m_nRows(0),m_nCols(0) { Attach(m);}

        const TypedMat & operator =(const cv::Mat& m){ Attach(m); return *this;}

        const TypedMat & operator =(const IplImage& m){ Attach(m); return *this;}


        // 행(row) 반환

        T* GetPtr(int r)

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        // 연산자 중첩 (원소접근) -- 2D

        T * operator [](int r)

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        const T * operator [](int r) const

        { assert(r>=0 && r<m_nRows); return m_pData[r];}


        // 연산자 중첩 (원소접근) -- 3D

        T & operator ()(int r, int c, int k)

        { assert(r>=0 && r<m_nRows && c>=0 && c<m_nCols && k>=0 && k<m_nChannels); return m_pData[r][c*m_nChannels+k];}


        const T operator ()(int r, int c, int k) const

        { assert(r>=0 && r<m_nRows && c>=0 && c<m_nCols && k>=0 && k<m_nChannels); return m_pData[r][c*m_nChannels+k];}

};


template<class T>

void TypedMat<T>::Attach(const cv::Mat& m)

{

        assert(sizeof(T)==m.elemSize1());


        m_nChannels = m.channels();

        m_nRows = m.rows;

        m_nCols = m.cols;

        

        if(m_pData) delete [] m_pData;

        m_pData = new T * [m_nRows];

        for(int r=0; r<m_nRows; r++)

        {

                m_pData[r] = (T *)(m.data + r*m.step);

        }

}


template<class T>

void TypedMat<T>::Attach(const IplImage& m)

{

        assert(sizeof(T) == m.widthStep/(m.width*m.nChannels));


        m_nChannels = m.nChannels;

        m_nRows = m.height;

        m_nCols = m.width;

        

        if(m_pData) delete [] m_pData;

        m_pData = new T * [m_nRows];

        for(int r=0; r<m_nRows; r++)

        {

                m_pData[r] = (T *)(m.imageData + r*m.widthStep);

        }

}



use_opencv.h



3. TypedMat 사용법


메모리를 공유하기 때문에 추가적인 연산 로드는 거의 없습니다. IplImage, cv::Mat을 지원합니다. 사용법은 다음과 같습니다.


* 이미지의 경우

#include "use_opencv.h"


Mat image;


TypedMat<unsigned char> tm = image;    // 연결방법 1

TypedMat<unsigned char> tm; tm = image;    // 연결방법 2

TypedMat<unsigned char> tm; tm.Attach(image);    // 연결방법 3


// image가 1채널 grayscale 이미지일 경우

tm[y][x] = 100;    // (x,y)의 픽셀값을 100으로 설정


// image가 3채널 color 이미지일 경우

tm(y,x,0) = 100;    // (x,y)의 픽셀의 blue값을 100으로 설정

tm(y,x,1) = 200;    // (x,y)의 픽셀의 green값을 200으로 설정

tm(y,x,2) = 50;    // (x,y)의 픽셀의 red값을 50으로 설정


* 2D float 타입 행렬의 경우

#include "use_opencv.h"

Mat f_image;

TypedMat<float> fm = f_image;

fm[y][x] = 3.12f;


by 다크 프로그래머

  • dd 2013.04.20 08:45 신고 ADDR 수정/삭제 답글

    행렬 접근하는거 정말 토나왔었는데 다크님덕분에 아주 좋은 자료 얻어갑니다.
    복받으실거에요!

  • 문일현 2013.08.13 20:26 신고 ADDR 수정/삭제 답글

    이렇게 Wrapper Class를 하나 만들면 될텐데 제가 생각과 실력이 부족하다보니...

    프로그래밍을 할 때 두려워하면 안되는데 제게 가장 부족한 부분인 것 같습니다.

    잘 사용하겠습니다.

    • BlogIcon 다크pgmr 2013.08.14 07:22 신고 수정/삭제

      저의 경우엔 프로그래밍을 할 때 가끔 망설임을 느낍니다.
      일단은 부딪쳐 봐야 하는데 머리속에서만 빙빙 도는 경우가 그런 경우입니다. 이게 정말 될까.. 이렇게 하는게 맞는 걸까.. 아닐까.. 하다가 시간만 보내고 맙니다. 정작 해봐야만 알 수 있는 것인데도 말입니다.
      그런 의미에서는 프로그래밍도 용기가 필요한것 같습니다 ^^

    • 수제자 2015.04.15 14:52 신고 수정/삭제

      허걱.. 다크님...

      이 글 제가 쓴 글인줄 알았어요.
      제가 딱 그렇거든요.
      저는 추가로 프로그래머는 배짱이 있어야 한다고도 생각합니다.
      부딪힐 수록 많이 배운다고 생각하거든요.
      많이 부딪히고 깨져 볼수록 좋은 소스와 제품이 나올수 없는 것 같기도 하구요.

    • BlogIcon 다크pgmr 2015.04.15 18:17 신고 수정/삭제

      네 감사합니다 ^^
      하다보면 저도 조금씩 조금씩 깨우쳐 가는게 참 많습니다.

  • 2014.07.23 14:41 ADDR 수정/삭제 답글

    비밀댓글입니다

  • 폰카만쓰는데... 2015.08.01 09:01 신고 ADDR 수정/삭제 답글

    늘 감사드립니다.
    전공분야가 아니다보니 아둔한 질문일지도 모르겠습니다.
    공개해주신 자료를 이용해서 프로그램을 작성중인데요,
    void TypedMat<T>::Attach(const IplImage& m)의 정의 하단에 있는 assert(sizeof(T)==m.elemSize1());에서 elemSize1이라는 멤버가 없다고 에러가 발생합니다.해당 맴버는 Mat에는 있지만 IplImage 구조를 보니 해당 멤버는 없어서 에러가 발생하는것 같은데 어떻게 해결해야 할까요?

    • BlogIcon 다크pgmr 2015.08.01 11:02 신고 수정/삭제

      그런 문제가 있는 줄 몰랐는데 감사합니다. 적절히 수정하여 다시 올렸습니다.

  • tigger 2015.11.09 12:29 신고 ADDR 수정/삭제 답글

    안녕하세요.
    혹시 wrapper class의 개념과 사용이유에 대해 간단히 설명해주실수 있나요?? 제가 쓰는 MRPT라는 라이브러리 소개 글에 "This library includes some wrappers to OpenCV methods and other original functionality" 라고 나오는데 저기서 말하는 wrapper의 개념이 잘 이해가 안가서요. 다크님의 작성하신 wrapper 클래스랑 같은 개념인지 궁금합니다.

    • BlogIcon 다크pgmr 2015.11.09 14:55 신고 수정/삭제

      네 비슷한 의미입니다. 핵심 내용은 그대로인데 단지 포장만 한번 더해서 좀더 사용하기 쉽게 한 것을 말합니다. 여기서 포장이라 함은 보통 UI(user interface)를 말합니다.

  • opencver 2016.04.14 07:58 신고 ADDR 수정/삭제 답글

    안녕하세요. 초보인데 님 블로그보면서 공부 많이 하고 있습니다.
    질문이 하나 있는데요..
    void TypedMat<T>::Attach(const IplImage& m)를 이용해서
    화소 접근/변경 후에 cvShowImage으로 &m을 찍으니
    Bad flag <parameter or structure field> <Unrecognized or unsupported array type) in cgGetMat 이라는 오류가 납니다.. 혹시 attach와 관련 있는 에러인지 궁금합니다!
    답변해주시면 감사하겠습니다.

    • BlogIcon 다크pgmr 2016.04.14 10:30 신고 수정/삭제

      그건 확인해 해 봐야만 알 수 있는 일입니다. 먼저, attach를 하지 않고 즉 원래의 iplImage m을 그대로 cvShowImage 함수에 넣었을 때 동일한 에러가 발생하는지 여부를 확인하시면 됩니다. 만일 동일한 에러가 난다면 카메라를 통해 넘어오는 이미지 데이터 자체에 무언가 문제가 있다는 말일 것입니다. 그렇지 않고 attach를 통해 화소접근/변경을 한 경우에만 그러한 에러가 난다면 그 다음으로는 화소접근/변경 과정중에 문제가 없는지 즉, 아주 간단한 화소접근/변경을 한 후에 cvShowImage를 했을 때에도 동일 현상이 나타나는지 확인해 보시면 될 것입니다. 디버깅이란 이렇게 문제의 원인이 될수 있는 가능성을 하나씩 확인/제거해 나가는 과정입니다. 조금만 노력해 보시면 충분히 문제의 원인을 찾아내실 수 있을 것으로 생각합니다.

    • opencver 2016.04.14 16:28 신고 수정/삭제

      음 좀 헷갈리는데,
      void TypedMat<T>::Attach(const cv::Mat& m)의
      용도가 무엇인지 여쭤봐도 될지요..
      m_nRows = m.rows;
      m_nCols = m.cols;
      이렇게 해놓고, for문은 row로만 돌리고 계셔서요~

    • BlogIcon 다크pgmr 2016.04.14 16:53 신고 수정/삭제

      입력 이미지 데이터에 대한 메모리 공유를 위한 함수입니다. TypedMat은 블로그 글에 적었듯이 opencv의 Mat 구조체에 대한 원소접근을 좀더 편하게 하기 위해 임의로 만든 편의 클래스입니다. 따라서, 굳이 사용하실 필요는 없습니다.. 조금 불편하긴 해도 원래의 IplImage나 Mat을 통해서도 원소 접근이 가능합니다.

  • 질문 있습니다. 2017.03.26 14:09 신고 ADDR 수정/삭제 답글

    안녕하세요... 제가 헤더파일에 대한 깊은 이해도가 없어서 질문이 있는데요
    혹시 헤더파일을 사용할 때 주석을 모두다 풀어주면 안 좋아지나요?
    만약 다 사용하나 안 하나 똑같다면 굳이 주석을 지웠다 썻다 해야할 필요가 없을거 같아서요

    • BlogIcon 다크pgmr 2017.03.26 22:22 신고 수정/삭제

      네, 그렇네요.. 본문의 내용도 수정하였습니다 ^^

    • 그런건가요 ㅠ? 2017.03.27 02:33 신고 수정/삭제

      깃허브 같은데 가서 보면 사람들이 다 하나하나 헤더파일 풀어서 놔서...
      다크님이 그렇다고 하시니 맞는거겠죠 ㅎㅎ 감사합니다!

  • 에러납니다. 2019.01.17 13:30 신고 ADDR 수정/삭제 답글

    안녕하세요..제가 다크님꺼를 바탕으로 프로젝트를 진행 중에 있습니다.
    그냥 올려져있는데 코드들을 복사하여, 일단 제대로 돌아가는지 확인하는 차에 에러가 발생하였습니다.
    "형식 지정자가 없습니다. int로 가정합니다. 참고 c++에서는 기본 int를 지원하지 않습니다"
    "구문 오류:','이(가) '&' 앞에 없습니다" 라는 에러들이 9개 정도 발생하는데 C++를 잘 알지 못하다보니 해결을 못하고 있습니다.
    부탁드리겠습니다(__)

    • BlogIcon 다크pgmr 2019.01.17 15:05 신고 수정/삭제

      저도 잘 모르는 에러네요. 프로그래밍 오류는 아무래도 본인이 직접 해결하는 게 좋을 것 같습니다.

    • 에러납니다 2019.01.17 15:59 신고 수정/삭제

      void Attach(const IplImage& m);
      TypedMat(const IplImage& m):m_pData(NULL), m_nChannels(1), m_nRows(0), m_nCols(0) { Attach(m); }

      이 부분에서 에러가 발생합니다.....

    • 에러납니다 2019.01.17 16:42 신고 수정/삭제

      opencv 버전을 알 수 있을까요?

    • BlogIcon 다크pgmr 2019.01.17 16:53 신고 수정/삭제

      저도 오래전 일이라 버전은 잘 기억나지 않습니다.. IplImage이 opencv 초창기때 쓰던 구조체라서 지금은 지원하지 않아서 발생한 문제같습니다. IplImage가 나오는 함수들을 코드에서 모두 삭제하면 해결될 것 같습니다.

  • 감사합니다 2019.01.18 08:31 신고 ADDR 수정/삭제 답글

    Mat 형식을 TypedMat 형식으로 변환하는 것 말고,
    반대로 TypedMat 형식을 Mat 형식으로 변환하는 방법도 있나요?

    • BlogIcon 다크pgmr 2019.01.18 08:55 신고 수정/삭제

      현재는 없습니다(관련된 구현을 추가해 넣는다면 가능은 합니다). 하지만, TypedMat을 다시 Mat으로 변환할 필요없이 원래의 Mat 객체를 버리지 않고 기억했다가 사용하면 될 것 같습니다.