Programming/OpenCV

OpenCV 강좌 #1

빠릿베짱이 2013. 10. 29. 11:01
반응형

이 글을 읽는 사람은 대부분 OpenCV를 처음 접했거나,

이제 막 Opencv을 시작하시는 사람이라 생각하고 글을 쓰겠습니다.

어느정도 아시는 분은 패스~~

저는 비전 연구를 하는 사람으로써 많은 작업을 c++을 이용하여 수행합니다.

따라서 C++에 초점을 두어 설명하겠으니, 참조해주시기 바랍니다.

Opencv에서 가장 중요한 것은 이미지 버퍼입니다.

즉 이미지가 어떻게 메모리에 저장되고, 이를 어떻게 관리하는지가 가장 중요합니다.

영상처리를 한번이라도 해보셨다면, 결국 영상이란 것은 숫자로 된 2차원 배열이라고 아실 겁니다.

 

typedef struct _IplImage
{
    int  nSize;         /* sizeof(IplImage) */
    int  ID;            /* version (=0)*/
    int  nChannels;     /* Most of OpenCV functions support 1,2,3 or 4 channels */
    int  alphaChannel;  /* ignored by OpenCV */
    int  depth;         /* pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,
                           IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported */
    char colorModel[4]; /* ignored by OpenCV */
    char channelSeq[4]; /* ditto */
    int  dataOrder;     /* 0 - interleaved color channels, 1 - separate color channels.
                           cvCreateImage can only create interleaved images */
    int  origin;        /* 0 - top-left origin,
                           1 - bottom-left origin (Windows bitmaps style) */
    int  align;         /* Alignment of image rows (4 or 8).
                           OpenCV ignores it and uses widthStep instead */
    int  width;         /* image width in pixels */
    int  height;        /* image height in pixels */
    struct _IplROI *roi;/* image ROI. if NULL, the whole image is selected */
    struct _IplImage *maskROI; /* must be NULL */
    void  *imageId;     /* ditto */
    struct _IplTileInfo *tileInfo; /* ditto */
    int  imageSize;     /* image data size in bytes
                           (==image->height*image->widthStep
                           in case of interleaved data)*/
    char *imageData;  /* pointer to aligned image data */
    int  widthStep;   /* size of aligned image row in bytes */
    int  BorderMode[4]; /* ignored by OpenCV */
    int  BorderConst[4]; /* ditto */
    char *imageDataOrigin; /* pointer to very origin of image data
                              (not necessarily aligned) -
                              needed for correct deallocation */
}
IplImage;

 

 

위의 구조체는 영상을 관리하는 구조체입니다. 저도 다 사용하지는 않아서, 제가 자주 사용하는 것 위주로, 설명드리겠습니다.

 

먼저 nChannels은 컬러 영상이냐, 흑백영상이냐를 나타냅니다. 고로, 3 or 1 이겠죠.

origin은 캠으로 영상을 입력 받을 때 사용하는데, 가끔 캠으로 입력받으면, 영상이 뒤졉혀 보이는 경우가 있는데 그때 사용하시면 됩니다.

그리고 width는 영상의 가로, height는 영상의 세로, imageSize는 영상이 저장된 버퍼의 총 크기

마지막으로 우리가 가장 많이 사용할 변수인, imageData  <--- 여기에 실제 이미지가 들어가 있는 것입니다.

여기서 주의할 점은 타입이 char 라는 것..

영상 처리 또는 비전을 하면 보통 픽셀은 0~255의 값을 갖습니다.

하지만 char은 음수도 표현하기 때문에 실제로 imageData의 값을 그대로 가져와서 영상처리를 수행하면,

아마 이상한 결과가 나올 것입니다.

따라서 실제로 사용할때는

(unsigned char)변수명->imageData[y*변수명->widthstep*채널 + x*채널]

(BYTE)변수명->imageData[y*변수명->widthstep*채널 + x*채널]

이렇게해서 사용합니다. 이점은 초보자에게 매우 중요한 점이며, 자주 실수하는 부분입니다.

또 한가지 주의할 점은 widthstep인데,

opencv 내부적으로 이미지를 버퍼로 저장할때 4바이트 단위로 끊어서 하나봅니다.

예를 들어 width가 3이고 height가 3, 채널이 1이라면, 실제로 버퍼를 위해 필요한 공간은

3*3 = 9바이트 를 필요로 합니다.

하지만 Opencv는 가로로 4바이트 단위로 버퍼를 생성합니다.

결국 4*3= 12바이트를 필요로 하게 됩니다.

따라서 0,1,2 번째 바이트에는 픽셀 값이 들어있지만 3번째 바이트는 사용하지 않습니다.

그리고 다시 4,5,6 번째 바이트에 값이 있고, 7 번째 바이트에는 쓰레기값..

이런식인데,

그래서 위의 예제에서 width를 사용하지 않고 widthstep을 사용한 것입니다.

그리고 width는 가로 크기만 가지고 있지만 widthstep은 가로와 채널까지 고려하여 한 행의 버퍼 길이를 나타냅니다.

(unsigned char)변수명->imageData[y*변수명->widthstep + x*채널]

(BYTE)변수명->imageData[y*변수명->widthstep + x*채널]

 

IplImage만 제대로 알고 있으면 Opencv는 별거 아닙니다.

왜냐하면 대부분 영상처리 관련 함수는 IplImage를 가지고 서로 주고 받으며 연산을 수행하기 때문이죠.

물론, 메트릭스 연산이나, 머신 러닝 등 다른 형태의 입력을 받는 함수들도 존재합니다만,

가장 많이 사용하게 되는 파라미터가 IplImage이기 때문에,

이것을 먼저 설명했습니다.

일단 강좌를 시작하긴 했는데, 제가 얼마나, 언제까지 쓸지는 모르겠군요.

여튼 C++를 안다는 가정하에 Opencv는 조금만 알면 레퍼런스 보고도 혼자 코딩이 가능하니 너무 어렵게 생각치마시고,

열심히 하시기 바랍니다.

오늘의 명언 : "많은 삽질과 많은 에러를 만난 놈이 코딩을 더 잘한다." ㅋㅋ

 

 

 

반응형