이 글을 읽는 사람은 대부분 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는 조금만 알면 레퍼런스 보고도 혼자 코딩이 가능하니 너무 어렵게 생각치마시고,
열심히 하시기 바랍니다.
오늘의 명언 : "많은 삽질과 많은 에러를 만난 놈이 코딩을 더 잘한다." ㅋㅋ
'Programming > OpenCV' 카테고리의 다른 글
[OpenCV] cvNamedWindow - 최상위 윈도우로 변경 (0) | 2015.11.18 |
---|---|
OpenCV를 프로젝트 배포 시 동영상 열기 실패 원인 (0) | 2015.07.12 |
행렬 관련, 고유벡터 관련 함수 (0) | 2013.11.23 |
KLT 소스 코드 - OpenCV 사용 (0) | 2013.01.09 |
[OpenCV] cvFloodFill (2) | 2012.11.08 |