실행결과예 IplImage 구조체정보의보존 cvwrite, cvwritecomment IplImage 구조체의정보를파일에보존한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv

Size: px
Start display at page:

Download "실행결과예 IplImage 구조체정보의보존 cvwrite, cvwritecomment IplImage 구조체의정보를파일에보존한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv"

Transcription

1 // (2) 폰트구조체를초기화한다 for (i = 0; i < 16; i += 2) { cvinitfont (&font[i], font_face[i / 2], 1.0, 1.0); cvinitfont (&font[i + 1], font_face[i / 2] CV_FONT_ITALIC, 1.0, 1.0); // (3) 폰트를지정하고, 텍스트를그리기한다 for (i = 0; i < 16; i++) { irandom = cvrandint (&rng); rcolor = CV_RGB (irandom & 255, (irandom >> 8) & 255, (irandom >> 16) & 255); cvputtext (img, "OpenCV sample code", cvpoint (15, (i + 1) * 30), &font[i], rcolor); // (4) 이미지의표시, 키가밀렸을때에종료 cvnamedwindow ("Text", CV_WINDOW_AUTOSIZE); cvshowimage ("Text", img); cvwaitkey (0); cvdestroywindow ("Text"); cvreleaseimage (&img); return 0; // (1) 이미지를확보해초기화한다폭 400, 높이 500 픽셀의이미지영역을확보해, 초기화 ( 제로클리어 ) 한다. // (2) 폰트구조체를초기화한다이번은,16 종류의폰트와 2 종류의자체 ( 노멀, 이탤릭 ) 를지정하고, 폰트구조체를초기화한다. 사이즈의비율은, 폭, 높이모두 1.0. // (3) 폰트를지정하고, 텍스트를그리기한다함수 cvputtext()( 을 ) 를이용하고, 텍스트의그리기를실시한다. 이때에, 초기화한폰트구조체와색을지정한다. 또,OpenCV 시점에서이용할수있는폰트세트에서는, 일본어의그리기는할수없다. // (4) 이미지의표시, 키가밀렸을때에종료이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 201

2 실행결과예 IplImage 구조체정보의보존 cvwrite, cvwritecomment IplImage 구조체의정보를파일에보존한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *color_img, *gray_img; CvRect roi; CvFileStorage *fs; // (1) 이미지를읽어들인다 if (argc!= 2 (color_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; // (2)ROI 의설정과 2 치화처리 gray_img = cvcreateimage (cvgetsize (color_img), IPL_DEPTH_8U, 1); cvcvtcolor (color_img, gray_img, CV_BGR2GRAY); roi = cvrect (0, 0, color_img->width / 2, color_img->height / 2); cvsetimageroi (gray_img, roi); cvsetimageroi (color_img, roi); cvthreshold (gray_img, gray_img, 90, 255, CV_THRESH_BINARY); // (3)xml 파일에의써내 fs = cvopenfilestorage ("images.xml", 0, CV_STORAGE_WRITE); cvwritecomment (fs, "This is a comment line.", 0); cvwrite (fs, "color_img", color_img); cvstartnextstream (fs); cvwrite (fs, "gray_img", gray_img); cvreleasefilestorage (&fs); 202

3 cvreleaseimage (&color_img); cvreleaseimage (&gray_img); return 0; // (1) 이미지를읽어들인다함수 cvloadimage 에의해, 보존하기위한칼라이미지를읽어들인다. // (2)ROI 의설정과 2 치화처리함수 cvcvtcolor 에의해칼라이미지를그레이스케일이미지로변환해, 함수 cvsetimageroi 에의해 ROI( 을 ) 를설정한다. 또, 변환한이미지에대해서 2 치화처리를가한다. // (3)xml 파일에의써내함수 cvwrite 에의해, 입력된칼라이미지와변환 처리된그레이스케일이미지를파일에써낸다. 함수 cvsaveimage 과는달리, 오브젝트의정보를데이터로서보존한다. 또, 여기에서는, 둘의 IplImage 구조체의데이터를하나의파일에써내고있지만, 하나의데이터를하나의파일에써내는경우는, cvsave("image.xml", color_img); ( 와 ) 과같이해보존해도좋다. 함수 cvsave 의내부에서는, 함수 cvwrite 하지만불려간다. 함수 cvstartnextstream()( 은 ) 는, 파일기입중의스트림을새롭게하는함수이며, 이장소에서 <!-- next stream --> 그렇다고하는코멘트와개행이삽입되는것만으로있다 ( 이번경우, 표준타입의데이터를톱레벨에쓸뿐 ( 만큼 ) 이 므로 ). 물론, 복수의파일을쓸때에필수라고하는것은아니다. 실행결과예 images.xml.zip 하지만실제로써내진파일이다. 이하의 snapshot 로부터도알수있듯이,ROI 등의정보도보존되고있 다. 맵의순서를보존 cvstartwritestruct, cvendwritestruct 두개의엔트리를가지는맵의순서를파일에보존한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> 203

4 #include <time.h> int main (int argc, char *argv[]) { int size = 20; int i; CvFileStorage *fs; CvRNG rng = cvrng (time (NULL)); CvPoint *pt = (CvPoint *) cvalloc (sizeof (CvPoint) * size); // (1) 점렬의작성 for (i = 0; i < size; i++) { pt[i].x = cvrandint (&rng) % 100; pt[i].y = cvrandint (&rng) % 100; // (2) 맵의순서로서점렬을보존 fs = cvopenfilestorage ("sequence.yml", 0, CV_STORAGE_WRITE); cvstartwritestruct (fs, "points", CV_NODE_SEQ); for (i = 0; i < size; i++) { cvstartwritestruct (fs, NULL, CV_NODE_MAP CV_NODE_FLOW); cvwriteint (fs, "x", pt[i].x); cvwriteint (fs, "y", pt[i].y); cvendwritestruct (fs); cvendwritestruct (fs); cvreleasefilestorage (&fs); return 0; // (1) 점렬의작성랜덤인값을가지는,CvPoint 형태의배열 ( 요소수 20)( 을 ) 를작성한다. // (2) 맵의순서로서점렬을보존레퍼런스메뉴얼에있어서의함수 GetHashedKey() 의설명의항에있는것같은, 두개의엔트리를가지는맵의순서로서보존한다. 함수 cvstartwritestruct()( 을 ) 를이용하고,"x"( 와 ) 과 "y" 의엔트리를가지는맵의기입을실시한다. 한층더그러한맵을, 마지막인수에 CV_NODE_SEQ( 을 ) 를준함수 cvstartwritestruct() 그리고정리하는것으로, 순서로서쓴다. 이번은, 레퍼런스메뉴얼의예에배워,YAML 형식의파일로서보존한다. 204

5 실행결과예 IplImage 구조체정보의읽기 cvread, cvgetfilenodebyname IplImage 구조체의정보를파일로부터읽어들인다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *color_img, *gray_img; CvRect roi_color, roi_gray; CvFileStorage *fs; CvFileNode *node; // (1) 파일을읽어들인다 if (argc!= 2 (fs = cvopenfilestorage (argv[1], 0, CV_STORAGE_READ)) == 0) return -1; node = cvgetfilenodebyname (fs, NULL, "color_img"); color_img = (IplImage *) cvread (fs, node); node = cvgetfilenodebyname (fs, NULL, "gray_img"); gray_img = (IplImage *) cvread (fs, node); cvreleasefilestorage (&fs); // (2)ROI 정보를취득해구형을그린후, 해방 roi_color = cvgetimageroi (color_img); roi_gray = cvgetimageroi (gray_img); cvresetimageroi (color_img); cvresetimageroi (gray_img); cvrectangle (color_img, cvpoint (roi_color.x, roi_color.y), cvpoint (roi_color.x + roi_color.width, roi_color.y + roi_color.height), CV_RGB (255, 0, 0)); cvrectangle (gray_img, cvpoint (roi_gray.x, roi_gray.y), cvpoint (roi_gray.x + roi_gray.width, roi_gray.y + roi_gray.height), cvscalar (0)); 205

6 // (3) 이미지를그리기 cvnamedwindow ("Color Image", CV_WINDOW_AUTOSIZE); cvshowimage ("Color Image", color_img); cvnamedwindow ("Grayscale Image", CV_WINDOW_AUTOSIZE); cvshowimage ("Grayscale Image", gray_img); cvwaitkey (0); cvdestroywindow ("Color Image"); cvdestroywindow ("Grayscale Image"); cvreleaseimage (&color_img); cvreleaseimage (&gray_img); return 0; // (1) 파일을읽어들인다커멘드인수로지정된파일을오픈해, 함수 cvopenfilestorage() 에의해파일스토리지포인터를얻는다. 함수 cvgetfilenodebyname() 에의해,"color_img" 라고 "gray_img"( 이 ) 라는이름으로부터노드를취득해, 함수 cvread() 에의해데이터를읽어들인다. // (2)ROI 정보를취득해구형을그린후, 해방함수 cvgetimageroi() 에의해, 읽어들였다 IplImage 구조체로부터 ROI 의정보를추출해, 그구형을그린다. 또, 그때문에 ( 위해 ) 이미지의 ROI( 을 ) 를해방해둔다. // (3) 이미지를그리기실제로, 파일로부터읽힌이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 여기서읽히고있는파일은,IplImage 구조체정보의보존그리고보존되었다 xml 파일이다. 맵의순서를읽어들인다 cvgethashedkey, cvgetfilenode 두개의엔트리를가지는맵의순서를파일로부터읽어들인다. 이것은, 참조설명서내의샘플과거의동일하다. 표시의변환 샘플코드 206

7 #include <cv.h> #include <stdio.h> int main (int argc, char **argv) { // (1) 파일스토리지의오픈, 핫슈드키의계산, 순서노드의취득 CvFileStorage *fs = cvopenfilestorage ("sequence.yml", 0, CV_STORAGE_READ); CvStringHashNode *x_key = cvgethashedkey (fs, "x", -1, 1); CvStringHashNode *y_key = cvgethashedkey (fs, "y", -1, 1); CvFileNode *points = cvgetfilenodebyname (fs, 0, "points"); // (2) 순서리더를초기화, 각노드를차례차례취득 if (CV_NODE_IS_SEQ (points->tag)) { CvSeq *seq = points->data.seq; int i, total = seq->total; CvSeqReader reader; cvstartreadseq (seq, &reader, 0); for (i = 0; i < total; i++) { CvFileNode *pt = (CvFileNode *) reader.ptr; #if 1 // (3) 고속버젼 CvFileNode *xnode = cvgetfilenode (fs, pt, x_key, 0); CvFileNode *ynode = cvgetfilenode (fs, pt, y_key, 0); assert (xnode && CV_NODE_IS_INT (xnode->tag) && ynode && CV_NODE_IS_INT (ynode- >tag)); int x = xnode->data.i; /* 혹은 x = cvreadint( xnode, 0 ); */ int y = ynode->data.i; /* 혹은 y = cvreadint( ynode, 0 ); */ #elif 1 // (4) 저속버젼.x_key( 와 ) 과 y_key( 을 ) 를사용하지않는다 CvFileNode *xnode = cvgetfilenodebyname (fs, pt, "x"); CvFileNode *ynode = cvgetfilenodebyname (fs, pt, "y"); assert (xnode && CV_NODE_IS_INT (xnode->tag) && ynode && CV_NODE_IS_INT (ynode- >tag)); int x = xnode->data.i; /* 혹은 x = cvreadint( xnode, 0 ); */ int y = ynode->data.i; /* 혹은 y = cvreadint( ynode, 0 ); */ #else // (5) 한층더저속이지만, 사용하기쉬운버젼 int x = cvreadintbyname (fs, pt, "x", 0); int y = cvreadintbyname (fs, pt, "y", 0); 207

8 #endif // (6) 데이터를표시해, 다음의순서노드를취득 CV_NEXT_SEQ_ELEM (seq->elem_size, reader); printf ("%d: (%d, %d)\n", i, x, y); cvreleasefilestorage (&fs); return 0; // (1) 파일스토리지의오픈, 핫슈드키의계산, 순서노드의취득최초로, 파일명을지정해파일스토리지를오픈한다. 또, 각순서의두개의엔트리의키이다 "x","y" 에대한독특한값을함수 cvgethashedkey() 에의해계산하고,CvStringHashNode 형태의포인터를취득한다. 여기서, 함수 cvgethashedkey() 의제 3 인수는, 키의이름의길이이므로, 여기에서는,"1"( 을 ) 를지정해도좋지만,0 이하의값을지정하는것으로자동적으로문자열의길이가계산된다. 오픈된파일노드로부터,"points"( 이 ) 라는이름을가지는순서노드의포인터를취득한다. // (2) 순서리더를초기화, 각노드를차례차례취득함수 cvstartreadseq()( 을 ) 를이용하고, 순서리더에의한연속읽기처리를초기화해, 차례차례노드를읽어들인다. 노드의취득은, 초기화된순서리더가가리키는노드 (ptr 멤버 ) 를캐스트하는것으로행해진다. 또, 이러한처리전에, 미리취득한파일노드의종류가순서인것을확인하기위해서, 매크로 CV_NODE_IS_SEQ()( 을 ) 를이용하고있다. 노드의멤버 tag( 을 ) 를이용하고, 그노드의종류를아는매크로는,CV_NODE_IS_* 그렇다고하는형태로,cxtypes.h 안에서정의되고있다. // (3) 고속버젼미리계산되었다 CvStringHashNode 형태의포인터를이용하고,"x","y" 의이름을가지는노드를취득한다. 이것은, 키의문자열의내용을비교하는것이아니라, 독특한포인터를비교하는것으로써노드를요구하기위해, 후술의함수 cvgetfielenodebyname()( 와 ) 과비교하면약간고속으로동작한다. 또, 취득한노드의값을직접참조하는것이, 함수 cvreadint()( 을 ) 를이용하고값을읽어내는것보다도약간고속으로있다. // (4) 저속버젼.x_key( 와 ) 과 y_key( 을 ) 를사용하지않는다함수 cvgetfielenodebyname() 에의해노드를취득하는일이외는, 전술의예와같이. // (5) 한층더저속이지만, 사용하기쉬운버젼함수 cvreadintbyname()( 은 ) 는, 그내부에서함수 cvgetfilenodebyname 라고함수 cvreadint()( 을 ) 를호출하고있다. 함수 cvreadint() 의호출분의오바헷드가걸리기위해, 상술의예보다한층더약간저속이된다. // (6) 데이터를표시해, 다음의순서노드를취득상술의몇개의방법으로취득된값을표시한다. 또, 매크로 CV_NEXT_SEQ_ELEM() 에의해, 순서리더가가리키는노드를요소의사이즈분만큼진행하고 ( 혹은블록을변환이라고 ), 다음의노드를취득한다. 208

9 실행결과예 여기서읽히고있는파일은, 맵의순서를보존그리고보존되었다 YAML 파일이다. K-means 법에따르는클러스터링 cvkmeans2 K-means 법으로, 샘플의클러스터링을실시한다. 이것은,OpenCV 부속의샘플코드 ( 와거의동일 ) 이다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <time.h> #define MAX_CLUSTERS (5) int main (int argc, char **argv) { CvScalar color_tab[max_clusters] = { CV_RGB (255, 0, 0), CV_RGB (0, 255, 0), CV_RGB (100, 100, 255), CV_RGB (255, 0, 255), CV_RGB (255, 255, 0) ; IplImage *img = cvcreateimage (cvsize (500, 500), IPL_DEPTH_8U, 3); CvRNG rng = cvrng (time (NULL)); CvPoint ipt; while (1) { int c; int k, cluster_count = cvrandint (&rng) % MAX_CLUSTERS + 1; int i, sample_count = cvrandint (&rng) % MAX_CLUSTERS; CvMat *points = cvcreatemat (sample_count, 1, CV_32FC2); CvMat *clusters = cvcreatemat (sample_count, 1, CV_32SC1); // (1) 복수의가우시안으로부터완성되는랜덤샘플을생성한다 for (k = 0; k < cluster_count; k++) { CvPoint center; CvMat point_chunk; center.x = cvrandint (&rng) % img->width; center.y = cvrandint (&rng) % img->height; cvgetrows (points, &point_chunk, k * sample_count / cluster_count, k == cluster_count - 1? sample_count : (k + 1) * sample_count / 209

10 cluster_count, 1); cvrandarr (&rng, &point_chunk, CV_RAND_NORMAL, cvscalar (center.x, center.y, 0, 0), cvscalar (img->width * 0.1, img- >height * 0.1, 0, 0)); // (2) 랜덤샘플을상훌한다 for (i = 0; i < sample_count / 2; i++) { CvPoint2D32f *pt1 = (CvPoint2D32f *) points->data.fl + cvrandint (&rng) % sample_count; CvPoint2D32f *pt2 = (CvPoint2D32f *) points->data.fl + cvrandint (&rng) % sample_count; CvPoint2D32f temp; CV_SWAP (*pt1, *pt2, temp); // (3)K-menas 법에따르는클러스터링 cvkmeans2 (points, cluster_count, clusters, cvtermcriteria (CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0)); // (4) 클러스터마다색을바꾸어샘플을그리기한다 cvzero (img); for (i = 0; i < sample_count; i++) { int cluster_idx = clusters->data.i[i]; ipt.x = (int) points->data.fl[i * 2]; ipt.y = (int) points->data.fl[i * 2 + 1]; cvcircle (img, ipt, 2, color_tab[cluster_idx], CV_FILLED, CV_AA, 0); cvreleasemat (&points); cvreleasemat (&clusters); // (5) 이미지를표시,"Esc" 키가밀렸을때에종료 cvnamedwindow ("Clusters", CV_WINDOW_AUTOSIZE); cvshowimage ("Clusters", img); c = cvwaitkey (0); if (c == '\x1b') break; cvreleasemat (&clusters); cvreleasemat (&points); cvdestroywindow ("Clusters"); cvreleaseimage (&img); 210

11 return 0; // (1) 복수의가우시안으로부터완성되는랜덤샘플을생성한다우선, 클러스터링대상데이터가되는데이터집합을작성한다. 각데이터는 2 차원좌표를가지는점이며, 그전데이터는,1 sample_count 의행렬 ( 각요소는,CV_32FC2)points 그리고나타내진다. 함수 cvgetrows 에의해의사적으로 ( 클러스터몇분에 ) 분할된부분행렬을, 정규분포하는데이터로초기화한다. 이때의평균은, 최초로랜덤에생성된값, 분산은,0.1* 이미지의사이즈, 이다. 이행렬의초기화에는, 함수 cvrandarr()( 을 ) 를이용한다. // (2) 랜덤샘플을상훌한다또, 생성된샘플은, 이시점에서는각클러스터마다올바르게정렬하고있으므로, 이것을, 매크로 CV_SWAP() 에의해상훌한다. 매크로 CV_SWAP( 은 ) 는,cxtypes.h 안에서, 다음과같이정의된다. #define CV_SWAP(a,b,t) ((t) = (a), (a) = (b), (b) = (t)) // (3)K-menas 법에따르는클러스터링함수 cvkmeans2() 에의해,K-menas 법에따르는클러스터링을실시한다. 인수에는, 각각, 샘플데이터, 클러스터수, 출력클러스터, 종료조건, 을준다. 출력클러스터에는, 각샘플이어느클러스터에속하는지를나타내는클러스터인덱스가들어간다. 또, 종료조건은, 반복계산의최대수와센터의각스텝에있어서의이동거리의최소치가주어져이어느쪽인지가만족되면계산이종료한다. // (4) 클러스터마다색을바꾸어샘플을그리기한다 clusters->data.i[i] 에,i 차례째의샘플이속하는클러스터의인덱스 (int 형태 ) 가보존되고있으므로, 거기에따라, 모든샘플을분류해엔으로그리기한다. // (5) 이미지를표시,"Esc" 키가밀렸을때에종료실제로이미지를표시해, 유저의입력을기다린다. "Esc" 키가밀렸을경우는종료해, 그이외의입력이맞았을경우는, 다시랜덤샘플을생성한다. 실행결과예 각클러스터를대표하는최초의센터는,OpenCV 내부에서랜덤에초기화되기위해, 반드시적절한클러스터링결과가 되는것은아니다. 클러스터링에의한감소색처리 cvkmeans2 k-means 법에따르는클러스터링을이용하고, 매우단순한감소색을실시한다 211

12 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #define MAX_CLUSTERS (32) /* 클러스터수 */ int main (int argc, char **argv) { int i, size; IplImage *src_img = 0, *dst_img = 0; CvMat *clusters; CvMat *points; CvMat *color = cvcreatemat (MAX_CLUSTERS, 1, CV_32FC3); CvMat *count = cvcreatemat (MAX_CLUSTERS, 1, CV_32SC1); // (1) 이미지를읽어들인다 if (argc!= 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; size = src_img->width * src_img->height; dst_img = cvcloneimage (src_img); clusters = cvcreatemat (size, 1, CV_32SC1); points = cvcreatemat (size, 1, CV_32FC3); // (2) 픽셀의값을행렬에대입 for (i = 0; i < size; i++) { points->data.fl[i * 3 + 0] = (uchar) src_img->imagedata[i * 3 + 0]; points->data.fl[i * 3 + 1] = (uchar) src_img->imagedata[i * 3 + 1]; points->data.fl[i * 3 + 2] = (uchar) src_img->imagedata[i * 3 + 2]; // (3) 클러스터링 cvkmeans2 (points, MAX_CLUSTERS, clusters, cvtermcriteria (CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 10, 1.0)); // (4) 각클러스터의평균치를계산 cvsetzero (color); cvsetzero (count); for (i = 0; i < size; i++) { int idx = clusters->data.i[i]; int j = ++count->data.i[idx];; color->data.fl[idx * 3 + 0] = color->data.fl[idx * 3 + 0] * (j - 1) / j + points- 212

13 >data.fl[i * 3 + 0] / j; color->data.fl[idx * 3 + 1] = color->data.fl[idx * 3 + 1] * (j - 1) / j + points- >data.fl[i * 3 + 1] / j; color->data.fl[idx * 3 + 2] = color->data.fl[idx * 3 + 2] * (j - 1) / j + points- >data.fl[i * 3 + 2] / j; // (5) 클러스터마다색을그리기 for (i = 0; i < size; i++) { int idx = clusters->data.i[i]; dst_img->imagedata[i * 3 + 0] = (char) color->data.fl[idx * 3 + 0]; dst_img->imagedata[i * 3 + 1] = (char) color->data.fl[idx * 3 + 1]; dst_img->imagedata[i * 3 + 2] = (char) color->data.fl[idx * 3 + 2]; // (6) 이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvnamedwindow ("low-color", CV_WINDOW_AUTOSIZE); cvshowimage ("low-color", dst_img); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("low-color"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasemat (&clusters); cvreleasemat (&points); cvreleasemat (&color); cvreleasemat (&count); return 0; // (1) 이미지를읽어들인다함수 vcloadimage() 에의해, 입력상을칼라이미지로서읽어들인다. // (2) 픽셀의값을행렬에대입각픽셀을클러스터링하기위해서, 각픽셀의 RGB 채널의값을 CvMat 형태의변수에대입한다. // (3) 클러스터링전술의샘플과같게, 함수 cvkmeans2() 에의해,k-means 법을이용한클러스터링을실시한다. 클러스터수는미리정의되고있어이것이, 감소색후의색가지수가된다. // (4) 각클러스터의평균치를계산클러스터링된각픽셀군을, 그클러스터의센터에서대표할수있으면처리가간단하지만, OpenCV 그럼, 함수 cvkmeans2() 에의해클러스터링된각클러스터의센터에액세스할수 213

14 없다. 거기서, 이샘플에서는클러스터링후의각클러스터의 (K 개의대표치는아니다 ) 완전한평균치를요구해그것을각클러스터의대표치로한다. // (5) 클러스터마다색을그리기각클러스터의픽셀을, 구할수있던평균치로묻는다. // (6) 이미지를표시, 키가밀렸을때에종료실제로, 입력이미지와클러스터링 ( 감소색 ) 된이미지를표시해, 무엇인가키가밀릴때까지기다린다. 이와같이 K-means 법을이용해감소색을실시하는일은, 색에의한이미지의영역분할과동일하다. 그러한처리에대해서는, 이미지분할, 영역결합, 윤곽검출 ( 을 ) 를참조의일. 실행결과예 입력이미지 4 색 8 색 16 색 32 색 엣지의검출 cvsobel, cvlaplace, cvcanny Sobel,Laplacian,Canny 에의한엣지검출 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img, *dst_img1, *dst_img2, *dst_img3; IplImage *tmp_img; // (1) 이미지의읽기 214

15 if (argc!= 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; tmp_img = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_16S, 1); dst_img1 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); dst_img2 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); dst_img3 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); // (2)Sobel 필터에의한미분이미지의작성 cvsobel (src_img, tmp_img, 1, 0); cvconvertscaleabs (tmp_img, dst_img1); // (3) 이미지의 Laplacian 의작성 cvlaplace (src_img, tmp_img); cvconvertscaleabs (tmp_img, dst_img2); // (4)Canny 에의한엣지이미지의작성 cvcanny (src_img, dst_img3, 50.0, 200.0); // (5) 이미지의표시 cvnamedwindow ("Original", CV_WINDOW_AUTOSIZE); cvshowimage ("Original", src_img); cvnamedwindow ("Sobel", CV_WINDOW_AUTOSIZE); cvshowimage ("Sobel", dst_img1); cvnamedwindow ("Laplace", CV_WINDOW_AUTOSIZE); cvshowimage ("Laplace", dst_img2); cvnamedwindow ("Canny", CV_WINDOW_AUTOSIZE); cvshowimage ("Canny", dst_img3); cvwaitkey (0); cvdestroywindow ("Original"); cvdestroywindow ("Sobel"); cvdestroywindow ("Laplace"); cvdestroywindow ("Canny"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img1); cvreleaseimage (&dst_img2); cvreleaseimage (&dst_img3); cvreleaseimage (&tmp_img); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고 215

16 읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_GRAYSCALE ( 을 ) 를지정하는것으로, 원이미지를그레이스케일이미지로서읽어들인다. // (2)Sobel 필터에의한미분이미지의작성 x 방향으로 1 다음미분하기위한,3 3 사이즈의 Sobel 필터를이용해미분이미지를작성한다. 여기에서는,aperture_size( 을 ) 를지정하고있지않지만, 그경우는디폴트의사이즈 3 하지만이용된다. 또, 여기서 CV_SCHARR(=-1)( 을 ) 를지정하면,Sharr 필터를이용할수있다. 이함수에서는슬캘링은행해지지않기때문에, 출력이미지의각픽셀치는, 대부분의경우입력이미지의그것보다큰값이된다. 오버플로우를피차기위해서, 예를들면입력이미지가 8 비트의경우, 출력이미지로서 16 비트이미지를지정할필요가있다. // (3) 이미지의 Laplacian 의작성이하와같이,x-y 양방향의 2 차미분의화를계산해, 이미지의 Laplacian( 을 ) 를작성한다. dst(x,y) = d 2 src/dx 2 + d 2 src/dy 2 이함수에서도,cvSobel 의경우와같게출력이미지로서 16 비트이미지를지정한다. // (4)Canny 에의한엣지이미지의작성함수 cvcanny() 하지만출력하는결과는, 전술의두개의함수와는달라, 구배이미지는아니다. 여기서출력되는이미지는, 엣지인가, 그렇지않은가의 2 치이미지가된다. cvcanny()( 은 ) 는, 2 종류의반응을일으키는최소의물리량을인수 (3 번째,4 번째 ) 에있어, 작은편이엣지끼리를접속하기위해서이용되어큰편이강한엣지의초기검출에이용된다. 엣지의접속에서는, 주목픽셀의 8 근방 (4 방향 ) 에대한구배가요구되어가장강한구배를가질방향으로 ( 반응을일으키는최소의물리량을밑돌지않는한 ) 엣지가접속된다. // (5) 이미지의표시입력이미지와처리이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 입력이미지결과 (Sobel) 결과 (Laplacian) 결과 (Canny) 216

17 코너 샘플 코너의검출 cvgoodfeaturestotrack, cvfindcornersubpix 이미지중의코너 ( 특징점 ) 검출 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, corner_count = 150; IplImage *dst_img1, *dst_img2, *src_img_gray; IplImage *eig_img, *temp_img; CvPoint2D32f *corners; if (argc!= 2 (dst_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYCOLOR CV_LOAD_IMAGE_ANYDEPTH)) == 0) return -1; dst_img2 = cvcloneimage (dst_img1); src_img_gray = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE); eig_img = cvcreateimage (cvgetsize (src_img_gray), IPL_DEPTH_32F, 1); temp_img = cvcreateimage (cvgetsize (src_img_gray), IPL_DEPTH_32F, 1); corners = (CvPoint2D32f *) cvalloc (corner_count * sizeof (CvPoint2D32f)); // (1)cvCornerMinEigenVal( 을 ) 를이용한코너검출 cvgoodfeaturestotrack (src_img_gray, eig_img, temp_img, corners, &corner_count, 0.1, 15); cvfindcornersubpix (src_img_gray, corners, corner_count, cvsize (3, 3), cvsize (-1, -1), cvtermcriteria (CV_TERMCRIT_ITER CV_TERMCRIT_EPS, 20, 0.03)); // (2) 코너의그리기 for (i = 0; i < corner_count; i++) cvcircle (dst_img1, cvpointfrom32f (corners[i]), 3, CV_RGB (255, 0, 0), 2); // (3)cvCornerHarris( 을 ) 를이용한코너검출 corner_count = 150; cvgoodfeaturestotrack (src_img_gray, eig_img, temp_img, corners, &corner_count, 0.1, 15, NULL, 3, 1, 0.01); 217

18 cvfindcornersubpix (src_img_gray, corners, corner_count, cvsize (3, 3), cvsize (-1, -1), cvtermcriteria (CV_TERMCRIT_ITER CV_TERMCRIT_EPS, 20, 0.03)); // (4) 코너의그리기 for (i = 0; i < corner_count; i++) cvcircle (dst_img2, cvpointfrom32f (corners[i]), 3, CV_RGB (0, 0, 255), 2); // (5) 이미지의표시 cvnamedwindow ("EigenVal", CV_WINDOW_AUTOSIZE); cvshowimage ("EigenVal", dst_img1); cvnamedwindow ("Harris", CV_WINDOW_AUTOSIZE); cvshowimage ("Harris", dst_img2); cvwaitkey (0); cvdestroywindow ("EigenVal"); cvdestroywindow ("Harris"); cvreleaseimage (&dst_img1); cvreleaseimage (&dst_img2); cvreleaseimage (&eig_img); cvreleaseimage (&temp_img); cvreleaseimage (&src_img_gray); return 0; // (1)cvCornerMinEigenVal( 을 ) 를이용한코너검출함수 cvgoodfeaturestotrack() ( 은 ) 는, 이미지중으로부터큰고유치를가지는코너를검출한다. 이함수는최초로, 모든입력이미지의픽셀에대해서, cvcornermineigenval ( 을 ) 를이용해최소의고유치를계산해, 결과를 (2 번째의인수로지정되었다 ) eig_image 에보존한다. 다음에,"non-maxima suppression"( 을 ) 를실행한다 (3 3 의인접영역내의극대만이남는다 ). 다음의스텝에서는, (6 번째의인수로지정되었다 )quality_levelth ( 와 ) 과 max(eig_image(x,y)) 의적보다작은최소고유치를가지는코너를삭제한다. 마지막으로, 이함수는코너점에주목해 ( 가장강한코너가제일최초로대상이된다 ), 새롭게주목한특징점으로그이전에대상으로한특징점군과의거리가 (7 번째의인수로지정되었다 ) min_distance 보다큰것을체크하는것으로, 모든검출된코너각각의거리가충분히떨어져있는것을보증한다. 그때문에, 이함수는선명한특징점과의거리가가까운특징점을삭제한다. 게다가함수 cvfindcornersubpix()( 을 ) 를이용하고,4 번째의인수로지정한사이즈의배의탐색윈도우로, 코너 ( 특징점 ) 의것보다정확한좌표를탐색한다. // (2) 코너의그리기검출된코너를적색의엔으로그리기한다. // (3)cvCornerHarris( 을 ) 를이용한코너검출 10 번째의인수가 0( 이 ) 가아닌경우는, 디폴트의 cvcornermineigenval 대신에, Harris 오퍼레이터 (cvcornerharris)( 을 ) 를이용한다. 218

19 // (4) 코너의그리기검출된코너를청색의엔으로그리기한다. // (5) 이미지의표시실제로검출된코너의이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 최소고유치 Harris 병진이동을위한픽셀산프링 cvgetrectsubpix 지정한좌표가이미지중심이되도록 ( 듯이 ) 원이미지를병진시킨다. 비정수치좌표의픽셀치는, 바이리니아보간에의해얻을수있다, 또이미지경계의외측에존재하는픽셀의값을얻기위해서, 복제경계모드 ( 이미지의가장외측의픽셀치가외측무한원까지성장하고있으면가정 ) 하지만사용된다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img = 0; CvPoint2D32f center; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; dst_img = cvcloneimage (src_img); // (2)dst_img 의이미지중심이된다 src_img 안의위치 center( 을 ) 를지정한다 center.x = src_img->width - 1; center.y = src_img->height - 1; // (3)center 하지만이미지중심이되도록 ( 듯이 ),GetRectSubPix( 을 ) 를이용해이미지전체를시프트시킨다 219

20 cvgetrectsubpix (src_img, dst_img, center); // (4) 결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst", dst_img); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 1; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 출력용이미지의영역을, 입력이미지를카피하는것으로확보한다. // (2)dst_img 의이미지중심이된다 src_img 안의위치 center( 을 ) 를지정한다이샘플에서는, 입력이미지의우하구석을출력이미지의중심으로하고있다. // (3)center 하지만이미지중심이되도록 ( 듯이 ),GetRectSubPix( 을 ) 를이용해이미지전체를시프트시킨다이미지를시프트시키는것은, 출력이미지 (dst_img) 안의각픽셀치를이하의식을이용해입력이미지중의픽셀로부터샘플링하는일로결정물은것이라도있다. dst(x, y) = src(x + center.x - (width(dst)-1)*0.5, y + center.y - (height(dst)-1)*0.5) // (4) 결과를표시한다윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예입력이미지와출력이미지 [ 왼쪽에서 ] 입력이미지, 출력이미지출력이미지의중심이입력이미지의우하가되도록 ( 듯이 ) 화면이좌상방향으로시프트되고있다. 나머지의부분은복제경계모드가적용되고, 원이미지의경계부의픽셀치가옆 세로그리고우하영역에뻗어있다. 회전이동을위한픽셀산프링 cvgetquadranglesubpix 220

21 지정한회전행렬을이용해원이미지를회전시킨다. 비정수치좌표의픽셀치는, 바이리니아보간에의해얻을수있다, 또이미지경계의외측에존재하는픽셀의값을얻기위해서, 복제경계모드 ( 이미지의가장외측의픽셀치가외측무한원까지성장하고있으면가정 ) 가사용된다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <math.h> int main (int argc, char **argv) { int angle = 45; float m[6]; IplImage *src_img = 0, *dst_img = 0; CvMat M; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; dst_img = cvcloneimage (src_img); // (2) 회전을위한행렬 ( 아핀행렬 ) 요소를설정해,CvMat 행렬 M( 을 ) 를초기화한다 m[0] = (float) (cos (angle * CV_PI / 180.)); m[1] = (float) (-sin (angle * CV_PI / 180.)); m[2] = src_img->width * 0.5; m[3] = -m[1]; m[4] = m[0]; m[5] = src_img->height * 0.5; cvinitmatheader (&M, 2, 3, CV_32FC1, m, CV_AUTOSTEP); // (3) 지정된회전행렬에의해,GetQuadrangleSubPix( 을 ) 를이용해이미지전체를회전시킨다 cvgetquadranglesubpix (src_img, dst_img, &M); // (4) 결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst", dst_img); cvwaitkey (0); 221

22 cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 1; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 출력용이미지의영역을, 입력이미지를카피하는것으로확보한다. // (2) 회전을위한행렬 ( 아핀행렬 ) 요소를설정해,CvMat 행렬 M( 을 ) 를초기화한다여기서지정하는아핀행렬은다음과같이설정한다. A 11 A 12 b 1 map_matrix = A 21 A 22 b 2 m[0] = A 11 = cos(angle*π/180) m[1] = A 12 = -sin(angle*π/180) m[2] = b 1 = 회전중심의 x 좌표 m[3] = A 21 = sin(angle*π/180) m[4] = A 22 = cos(angle*π/180) m[5] = b 2 = 회전중심의 y 좌표 angle 의단위는 degree. // (3) 지정된회전행렬에의해,GetQuadrangleSubPix( 을 ) 를이용해이미지전체를회전시킨다이미지를회전시키는것은, 출력이미지 (dst_img) 안의각픽셀치를입력이미지중의픽셀로부터이하의식에따라서샘플링하는일로, 결정하는것이기도하다. dst(x, y) = src( A 11 x' + A 12 y' + b 1, A 21 x' + A 22 y' + b 2 ) x' = x - ( dst_img->width - 1 ) * 0.5 y' = y - ( dst_img->height - 1 ) * 0.5 // (4) 결과를표시한다윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 222

23 실행결과예입력이미지와출력이미지 [ 왼쪽에서 ] 입력이미지, 출력이미지입력이미지의중심좌표를회전중심으로서 45[deg] 회전한결과가출력이되어있다. 출력이미지의픽셀로대응점이없는경우 ( 입력이미지의영역외 ) 는, 복제경계모드가적용되어원이미지의경계부의픽셀치가각우방향으로뻗어있는것이확인할수있다. 보간 여러가지보간방법을이용해이미지의사이즈변경을실시한다. 샘플 이미지의사이즈변경 cvresize 지정한출력이미지사이즈에맞도록 ( 듯이 ), 입력이미지의사이즈를변경해출력한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img1 = 0, *dst_img2 = 0; // (1) 이미지를읽어들인다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; // (2) 출력용이미지영역의확보를행한다 dst_img1 = cvcreateimage (cvsize (src_img->width / 2, src_img->height / 2), src_img- >depth, src_img->nchannels); dst_img2 = cvcreateimage (cvsize (src_img->width * 2, src_img->height * 2), src_img- >depth, src_img->nchannels); 223

24 // (3) 이미지의사이즈변경을실시한다 cvresize (src_img, dst_img1, CV_INTER_NN); cvresize (src_img, dst_img2, CV_INTER_NN); // (4) 결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst1", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst2", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst1", dst_img1); cvshowimage ("dst2", dst_img2); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dst1"); cvdestroywindow ("dst2"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img1); cvreleaseimage (&dst_img2); return 1; // (1) 이미지를읽어들인다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. // (2) 출력용이미지영역의확보를행한다입력이미지사이즈의각각 1/2 배,2 배의사이즈를지정한출력이미지영역을함수 cvcreateimage() 그리고확보한다. // (3) 이미지의사이즈변경을실시한다보간방법을지정하고, 사이즈변경을실시한다. // (4) 결과를표시한다윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 최근인접보간과바이큐빅크보간에서는이미지축소때에무아레가발생하고있는것을확인할수있다. 보간방법사이즈 1/2 배입력이미지사이즈 2 배 224

25 최근인접 바이리니아 225

26 에리어 바이큐빅크 기하변환 아핀변환 투시투영변환등의이미지의기하학적변환을실장하고있다 샘플 이미지의아핀변환 (1) cvgetaffinetransform + cvwarpaffine 이미지상의 3 점대응보다아핀변환행렬을계산해, 그행렬을이용해이미지전체의아핀변환을실시한다. 226

27 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img = 0; CvMat *map_matrix; CvPoint2D32f src_pnt[3], dst_pnt[3]; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; dst_img = cvcloneimage (src_img); // (2) 삼각형의회전전과회전후의대응하는정점을각각세트해 // cvgetaffinetransform( 을 ) 를이용해아핀행렬을요구한다 src_pnt[0] = cvpoint2d32f (200.0, 200.0); src_pnt[1] = cvpoint2d32f (250.0, 200.0); src_pnt[2] = cvpoint2d32f (200.0, 100.0); dst_pnt[0] = cvpoint2d32f (300.0, 100.0); dst_pnt[1] = cvpoint2d32f (300.0, 50.0); dst_pnt[2] = cvpoint2d32f (200.0, 100.0); map_matrix = cvcreatemat (2, 3, CV_32FC1); cvgetaffinetransform (src_pnt, dst_pnt, map_matrix); // (3) 지정된아핀행렬에의해,cvWarpAffine( 을 ) 를이용해이미지를회전시킨다 cvwarpaffine (src_img, dst_img, map_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvscalarall (0)); // (4) 결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst", dst_img); cvwaitkey (0); 227

28 cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasemat (&map_matrix); return 1; // (1) 이미지의읽기와출력용이미지의파티션커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 출력용이미지의영역을, 입력이미지를카피하는것으로확보한다. // (2) 삼각형의회전전과회전후의대응하는정점을각각세트해 cvgetaffinetransform()( 을 ) 를이용해아핀행렬을요구한다 윗이미지의물색삼각형이핑크의삼각형이되도록 ( 듯이 ) 회전했다고한다. 이샘플프로그램에서는, 삼각형의각각의대응점이기존으로서코드를기술하고있지만, 실제의프로그램에서는삼각형닮아대응하는특징점의트랙킹등에의해이러한점대응을요구할수가있다.src_pnt[] 에회전전의정점좌표를,dst_pnt[] 에회전후의대응점을세트한다.cvGetAffineTransform() 에의해,2 3 의 map_matrix 에요구된아핀행렬이격납된다. // (3) 지정된아핀행렬에의해,cvWarpAffine( 을 ) 를이용해이미지를회전시킨다변환후대응을취할수없는화소에대해서는 fillval( 으 ) 로서 cvscalarall(0) ( 을 ) 를지정해, 쿠로에서묻는다. 아핀변환을이용하면, 이미지의확대 축소, 병진 회전을행할수가있다. // (4) 결과를표시한다윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예입력이미지와출력이미지 [ 왼쪽에서 ] 입력이미지, 출력이미지 228

29 이미지의아핀변환 (2) cv2drotationmatrix + cvwarpaffine 회전각도, 스케일계수, 회전중심을주어변환행렬을계산해, 그행렬을이용해이미지의 ROI 부분의아핀변환을실시한다.cvWarpAffine() 하위에서나타내보였다 cvgetquadranglesubpix() 에유사한기능을가지고있지만, 입출력의이미지형식이같을이라고하는제약을가져오버헤드가크다. 그러나,ROI( 을 ) 를이용해이미지의일부를변환할수가있다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { double angle = -45.0, scale = 1.0; IplImage *src_img = 0, *dst_img = 0; CvMat *map_matrix; CvPoint2D32f center; CvPoint pt1, pt2; CvRect rect; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; rect.x = (int) (src_img->width * 0.25); rect.y = (int) (src_img->height * 0.25); rect.width = (int) (src_img->width * 0.5); rect.height = (int) (src_img->height * 0.5); cvsetimageroi (src_img, rect); dst_img = cvcloneimage (src_img); // (2) 각도, 스케일계수, 회전중심을지정해 // cv2drotationmatrix( 을 ) 를이용해아핀행렬을요구한다 map_matrix = cvcreatemat (2, 3, CV_32FC1); center = cvpoint2d32f (src_img->width * 0.25, src_img->height * 0.25); cv2drotationmatrix (center, angle, scale, map_matrix); // (3) 지정된아핀행렬에의해,cvWarpaffine( 을 ) 를이용해이미지의 ROI 부분을회전시킨다 229

30 cvwarpaffine (src_img, dst_img, map_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvscalarall (255)); // (4) 결과를표시한다 cvresetimageroi (src_img); cvresetimageroi (dst_img); pt1 = cvpoint (rect.x, rect.y); pt2 = cvpoint (rect.x + rect.width, rect.y + rect.height); cvrectangle (src_img, pt1, pt2, CV_RGB (255, 0, 255), 2, 8, 0); cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst", dst_img); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasemat (&map_matrix); return 1; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들여, 이미지의중앙에서, 폭 높이모두원이미지의반의영역을지정해 ROI( 을 ) 를세트한다. 그후, 출력용이미지의영역을입력이미지를카피하는것으로확보한다. // (2) 각도, 스케일계수, 회전중심을지정해 cv2drotationmatrix( 을 ) 를이용해아핀행렬을요구한다샘플프로그램에서는, 시계회전에 45 번, 배율 1.0 배를지정해,cv2DRotationMatrix() 그리고아핀변환행렬을요구한다. // (3) 지정된아핀행렬에의해,cvWarpaffine( 을 ) 를이용해이미지의 ROI 부분을회전시킨다변환후대응을취할수없는화소에대해서는 fillval( 으 ) 로서 cvscalarall(255) ( 을 ) 를지정해, 흰색으로묻는다.ROI 하지만지정되어있는경우, 지정영역만을변환하고있다. // (4) 결과를표시한다결과표시전에 ROI( 을 ) 를해제하는것으로, 이미지전체를표시할수있도록한다. 그후, 윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 230

31 실행결과예입력이미지와출력이미지 [ 왼쪽에서 ] 입력이미지, 출력이미지 ROI 그리고지정한영역만, 변환되어그외의부분으로변경이없는것을확인할수있다. 이미지의투시투영변환 cvgetperspectivetransform + cvwarpperspective 이미지상의 4 점대응보다투시투영변환행렬을계산해, 그행렬을이용해이미지전체의투시투영변환을실시한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img = 0; CvMat *map_matrix; CvPoint2D32f src_pnt[4], dst_pnt[4]; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; dst_img = cvcloneimage (src_img); // (2) 사각형의변환전과변환후의대응하는정점을각각세트해 // cvwarpperspective( 을 ) 를이용해투시투영변환행렬을요구한다 src_pnt[0] = cvpoint2d32f (150.0, 150.0); src_pnt[1] = cvpoint2d32f (150.0, 300.0); 231

32 src_pnt[2] = cvpoint2d32f (350.0, 300.0); src_pnt[3] = cvpoint2d32f (350.0, 150.0); dst_pnt[0] = cvpoint2d32f (200.0, 200.0); dst_pnt[1] = cvpoint2d32f (150.0, 300.0); dst_pnt[2] = cvpoint2d32f (350.0, 300.0); dst_pnt[3] = cvpoint2d32f (300.0, 200.0); map_matrix = cvcreatemat (3, 3, CV_32FC1); cvgetperspectivetransform (src_pnt, dst_pnt, map_matrix); // (3) 지정된투시투영변환행렬에의해,cvWarpPerspective( 을 ) 를이용해이미지를변환시킨다 cvwarpperspective (src_img, dst_img, map_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvscalarall (100)); // (4) 결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dst", dst_img); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasemat (&map_matrix); return 1; // (1) 이미지의읽어들여, 출력용이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 출력용이미지의영역을, 입력이미지를카피하는것으로확보한다. // (2) 사각형의변환전과변환후의대응하는정점을각각세트해 cvgetperspectivetransform()( 을 ) 를이용해투시투영변환행렬을요구한다 232

33 윗이미지의물색사각형이핑크의사각형이되도록 ( 듯이 )( 샘플코드에서는, 각각의사각형의저변은같다라고하고있다 ) 이미지를변환시킨다. 이샘플프로그램에서는, 사각형의각각의대응점이기존으로서코드를기술하고있지만, 실제의프로그램에서는특징점추출등에의해대응점을요구해행렬을작성하거나카메라 calibration matrices 등에서직접투시투영변환행렬을요구한다. src_pnt[] 에변환전의좌표를,dst_pnt[] 에대응하는반환후의좌표를세트해,cvGetPerspectiveTransform() ( 을 ) 를부르는일에의해서,3 3 의 map_matrix 에투시투영변환행렬이격납되어돌려주어진다. // (3) 지정된투시투영변환행렬에의해,cvWarpPerspective( 을 ) 를이용해이미지를변환시킨다변환후대응을취할수없는화소에대해서는 fillval( 으 ) 로서 cvscalarall(100) ( 을 ) 를지정해, 그레이로묻고있다. // (4) 결과를표시한다윈도우를생성해, 입력이미지, 결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예입력이미지와출력이미지 [ 왼쪽에서 ] 입력이미지, 출력이미지 전방위이미지의투시투영변환 cvgetperspectivetransform + cvwarpperspective 전방위이미지의일부에대해서투시투영변환을실시한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <stdio.h> #include <ctype.h> int main (int argc, char **argv) { IplImage *src = NULL, *dst = NULL; CvPoint2D32f src_pnt[4], dst_pnt[4]; CvMat *map_matrix; int i; 233

34 double t; double a = 29.0, b = 40.0, c = sqrt (a * a + b * b); /* 밀러파라미터 */ double f = 100; int xc = 343, yc = 258; /* 이미지중심 */ int x[4] = { -30, 30, 30, -30 ; int y[4] = { -50, -50, -50, -50 ; int z[4] = { 180, 180, 100, 100 ; int xx[4], yy[4]; // (1) 파일로부터이미지를읽어들인다 src = cvloadimage ("src.jpg", CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src == NULL) exit (-1); dst = cvcloneimage (src); // (2) 윈도우를작성한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dst", CV_WINDOW_AUTOSIZE); // (3) 대응점을계산한다 for (i = 0; i < 4; i++) { t = -a * a * f / ((b * b + c * c) * z[i] - 2 * b * c * sqrt (x[i] * x[i] + y[i] * y[i] + z[i] * z[i])); xx[i] = (int) (x[i] * t + xc + 0.5); yy[i] = (int) (y[i] * t + yc + 0.5); src_pnt[i] = cvpoint2d32f (xx[i], yy[i]); // (4) 출력이미지의대응점을지정 dst_pnt[0] = cvpoint2d32f (0.0, 0.0); dst_pnt[1] = cvpoint2d32f (dst->width, 0.0); dst_pnt[2] = cvpoint2d32f (dst->width, dst->height); dst_pnt[3] = cvpoint2d32f (0.0, dst->height); // (5) 투시투영변환을실행한다 map_matrix = cvcreatemat (3, 3, CV_32FC1); cvgetperspectivetransform (src_pnt, dst_pnt, map_matrix); cvwarpperspective (src, dst, map_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvscalarall (0)); // (6) 전개영역을그리기한다 for (i = 0; i < 4; i++) { cvline (src, cvpoint (xx[i], yy[i]), cvpoint (xx[(i + 1) % 4], yy[(i + 1) % 4]), CV_RGB (255, 255, 0), 1, 0, 0); 234

35 // (7) 입출력이미지를표시한다 cvshowimage ("src", src); cvshowimage ("dst", dst); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dst"); cvreleaseimage (&src); return 0; // (1) 파일로부터이미지를읽어들인다입력이미지 ( 전방위이미지 ) 을함수 cvloadimage() 그리고읽어들인다. 또출력이미지의영역을함수 cvcloneimage () 그리고확보한다. // (2) 윈도우를작성한다입력이미지와출력이미지를표시하기위한윈도우를작성한다. // (3) 대응점을계산한다삼차원좌표의 4 정점으로부터이미지좌표의 4 정점을이하의계산식에의해요구해배열 src_pnt 에격납한다. 여기서 ( 은 ) 는삼차원좌표의점, ( 은 ) 는이미지좌표계의점, ( 은 ) 는이미지의중심좌표, ( 은 ) 는쌍곡면밀러의파라미터로, ( 을 ) 를채운다. ( 은 ) 는촛점거리이지만여기에서는적당하게지정해있다. // (4) 출력이미지의대응점을지정 src_pnt 에대응하는출력이미지의좌표를배열 dst_pnt 에격납한다. // (5) 투시투영변환을실행한다투시투영변환을실행한다. // (6) 전개영역을그리기한다입력이미지에전개영역을그리기한다. 235

36 실행결과예입력이미지와출력이미지 모르포로지변환 구조요소를지정하고, 여러가지모르포로지연산을행한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> /* 메인프로그램 */ int main (int argc, char **argv) { IplImage *src_img = 0; IplImage *dst_img_dilate, *dst_img_erode; IplImage *dst_img_opening, *dst_img_closing; IplImage *dst_img_gradient, *dst_img_tophat, *dst_img_blackhat; IplImage *tmp_img; IplConvKernel *element; //(1) 이미지의읽어들여, 연산결과이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); dst_img_dilate = cvcloneimage (src_img); dst_img_erode = cvcloneimage (src_img); dst_img_opening = cvcloneimage (src_img); dst_img_closing = cvcloneimage (src_img); dst_img_gradient = cvcloneimage (src_img); dst_img_tophat = cvcloneimage (src_img); dst_img_blackhat = cvcloneimage (src_img); tmp_img = cvcloneimage (src_img); //(2) 구조요소를생성한다 236

37 element = cvcreatestructuringelementex (9, 9, 4, 4, CV_SHAPE_RECT, NULL); //(3) 각종의모르포로지연산을실행한다 cvdilate (src_img, dst_img_dilate, element, 1); cverode (src_img, dst_img_erode, element, 1); cvmorphologyex (src_img, dst_img_opening, tmp_img, element, CV_MOP_OPEN, 1); cvmorphologyex (src_img, dst_img_closing, tmp_img, element, CV_MOP_CLOSE, 1); cvmorphologyex (src_img, dst_img_gradient, tmp_img, element, CV_MOP_GRADIENT, 1); cvmorphologyex (src_img, dst_img_tophat, tmp_img, element, CV_MOP_TOPHAT, 1); cvmorphologyex (src_img, dst_img_blackhat, tmp_img, element, CV_MOP_BLACKHAT, 1); //(4) 모르포로지연산결과를표시한다 cvnamedwindow ("src", CV_WINDOW_AUTOSIZE); cvnamedwindow ("dilate", CV_WINDOW_AUTOSIZE); cvnamedwindow ("erode", CV_WINDOW_AUTOSIZE); cvnamedwindow ("opening", CV_WINDOW_AUTOSIZE); cvnamedwindow ("closing", CV_WINDOW_AUTOSIZE); cvnamedwindow ("gradient", CV_WINDOW_AUTOSIZE); cvnamedwindow ("tophat", CV_WINDOW_AUTOSIZE); cvnamedwindow ("blackhat", CV_WINDOW_AUTOSIZE); cvshowimage ("src", src_img); cvshowimage ("dilate", dst_img_dilate); cvshowimage ("erode", dst_img_erode); cvshowimage ("opening", dst_img_opening); cvshowimage ("closing", dst_img_closing); cvshowimage ("gradient", dst_img_gradient); cvshowimage ("tophat", dst_img_tophat); cvshowimage ("blackhat", dst_img_blackhat); cvwaitkey (0); cvdestroywindow ("src"); cvdestroywindow ("dilate"); cvdestroywindow ("erode"); cvdestroywindow ("opening"); cvdestroywindow ("closing"); cvdestroywindow ("gradient"); cvdestroywindow ("tophat"); cvdestroywindow ("blackhat"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img_dilate); cvreleaseimage (&dst_img_erode); cvreleaseimage (&dst_img_opening); cvreleaseimage (&dst_img_closing); cvreleaseimage (&dst_img_gradient); cvreleaseimage (&dst_img_tophat); 237

38 cvreleaseimage (&dst_img_blackhat); cvreleaseimage (&tmp_img); return 1; // (1) 이미지의읽어들여, 연산결과이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 출력용이미지의영역을, 입력이미지를카피하는것으로확보한다. 고도의모르포로지연산 cvmorphologyex()( 을 ) 를행하는경우는텐포라리의영역이필요하다 ( 모르포로지구배때와인프레이스모드로의톱하트변환 ( 와 ) 과블랙하트변환의경우 ) 의로,tmp_img( 으 ) 로서준비해있다. // (2) 구조요소를생성한다모르포로지연산에사용하는구조요소를생성한다. 샘플프로그램에서는,9 9 의구형으로, 정확히그구형의중심으로엥커포인트를가지는구조요소를지정해있다. // (3) 각종의모르포로지연산을실행한다 OpenCV 에준비떠날수있어모든모르포로지연산의실행을행해, 결과를각각의출력영역에보존한다. // (4) 모르포로지연산결과를표시한다윈도우를생성해, 각종모르포로지검출결과를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예입력이미지와연산결과 [ 상단왼쪽에서 ] 입력이미지 (2 값이미지 ),Dilation,Erosion,Opening [ 하단왼쪽에서 ]Closing, 경사, 톱하트변환, 블랙하트변환 238

39 [ 상단왼쪽에서 ] 입력이미지 ( 칼라이미지 ),Dilation,Erosion,Opening [ 하단왼쪽에서 ]Closing, 경사, 톱하트변환, 블랙하트변환 평활화 cvsmooth 브라 -, 가우시안, 미디언, 쌍방, 의각필터에의한평활화 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i; IplImage *src_img = 0, *dst_img[4]; // (1) 이미지를읽어들인다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); for (i = 0; i < 4; i++) dst_img[i] = cvcloneimage (src_img); // (2) 수법을지정해이미지를평활화 cvsmooth (src_img, dst_img[0], CV_BLUR, 5, 0, 0, 0); cvsmooth (src_img, dst_img[1], CV_GAUSSIAN, 11, 0, 0, 0); cvsmooth (src_img, dst_img[2], CV_MEDIAN, 5, 0, 0, 0); cvsmooth (src_img, dst_img[3], CV_BILATERAL, 80, 80, 0, 0); 239

40 // (3) 처리된이미지를실제로표시 cvnamedwindow ("Blur", CV_WINDOW_AUTOSIZE); cvshowimage ("Blur", dst_img[0]); cvnamedwindow ("Gaussian", CV_WINDOW_AUTOSIZE); cvshowimage ("Gaussian", dst_img[1]); cvnamedwindow ("Median", CV_WINDOW_AUTOSIZE); cvshowimage ("Median", dst_img[2]); cvnamedwindow ("Bilateral", CV_WINDOW_AUTOSIZE); cvshowimage ("Bilateral", dst_img[3]); cvwaitkey (0); cvdestroywindow ("Blur"); cvdestroywindow ("Gaussian"); cvdestroywindow ("Median"); cvdestroywindow ("Bilateral"); cvreleaseimage (&src_img); for (i = 0; i < 4; i++) { cvreleaseimage (&dst_img[i]); return 0; // (1) 이미지를읽어들인다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변갱하지않고읽어들인다. // (2) 수법을지정해이미지를평활화함수 cvsmooth() 의 3 번째의인수로, CV_BLUR( 브라피르타 ),CV_GAUSSIAN( 가우시안피르타 ),CV_MEDIAN( 미디언필터 ),CV_BILATERAL( 쌍방필터 ) 의각수법을지정하고평활화를실시한다. 4 번째이후의인수의의미는, 수법마다다르다. 자세한것은, 레퍼런스메뉴얼을참조하는것. // (3) 처리된이미지를실제로표시각수법으로평활화된이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예하단의이미지는, 처리이미지의일부를확대한것이다. 다만, 수법마다파라미터를바꾸어있으므로, 단순하게비교할수있는이미지는아니다. 입력이미지 Blur Gaussian Median Bilateral 240

41 유저정의필터 cvfilter2d 유저가정의한커넬에의한필터링 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <stdio.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img; float data[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ; CvMat kernel = cvmat (1, 21, CV_32F, data); // (1) 이미지의읽기 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); dst_img = cvcreateimage (cvgetsize (src_img), src_img->depth, src_img->nchannels); // (2) 커넬의정규화와필터처리 cvnormalize (&kernel, &kernel, 1.0, 0, CV_L1); cvfilter2d (src_img, dst_img, &kernel, cvpoint (0, 0)); // (3) 처리이미지의표시 cvnamedwindow ("Filter2D", CV_WINDOW_AUTOSIZE); cvshowimage ("Filter2D", dst_img); cvwaitkey (0); cvdestroywindow ("Filter2D"); 241

42 cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변갱하지않고읽어들인다. // (2) 커넬의정규화와필터처리부동소수점형배열 data 의값을각요소로하는,1 21; 의행렬을커넬로하는필터처리를실시한다. 함수 cvnormalize() 에의해서, 커넬을정규화한다. 즉, 여기에서는, 절대치의법칙이 "1.0"( 이 ) 가되도록 ( 듯이 ), 배열의값을정규화하고있다. 그리고, 그커넬을이용 ( 함수 cvfilter2d() 의 3 번째의인수로지정 ) 하고, 필터처리를실시한다. 함수 cvfilter2d() 의 4 번째의인수는, 필터대상픽셀의커넬내에서의상대위치를나타내고있어디폴트는 cvpoint(-1,-1)( 이어 ) 여커넬중심을가리키지만, 이번은,cvPoint(0,0)( 을 ) 를지정하는것으로, 커넬의좌단을가리키고있다. // (3) 처리이미지의표시처리된이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 경계선이미지의경계를어떻게취급할까는, 많은이미지처리에대해자주문제가된다. 예를들면, 필터처리에대하고, 필터가부분적으로이미지의밖에는보기시작하고해기다렸을경우, 어떠한화소치를가정해이용할까에의해서, 처리결과가다르다. OpenCV그럼, 이미지를카피해, 그주위에경계선을붙이는처리를실시하는함수가용뜻되고있어IPL_BORDER_CONSTANT, 및IPL_BORDER_REPLICAT( 을 ) 를서포트하고있다. OpenCV그리고이용되는함수는,IP L_BORDER_REPLICAT, 즉복제경계모드를이용하는것이많이있지만, 유저가그러한처리를바라지않은경우에는, 미리경계를정수치로묻고나서처리를실시해, 처리후의이미지를클리핑하는일로경계의취급을컨트롤할수있다. 샘플 경계선의작성 cvcopymakeborder 이미지의카피와경계의작성 242

43 표시의변환 샘플코드 #include <cxcore.h> #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, offset = 30; IplImage *src_img = 0, *dst_img[2]; // (1) 이미지의읽기 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); for (i = 0; i < 2; i++) dst_img[i] = cvcreateimage (cvsize (src_img->width + offset * 2, src_img->height + offset * 2), src_img->depth, src_img->nchannels); // (2) 경계선의작성 cvcopymakeborder (src_img, dst_img[0], cvpoint (offset, offset), IPL_BORDER_REPLICATE); cvcopymakeborder (src_img, dst_img[1], cvpoint (offset, offset), IPL_BORDER_CONSTANT, CV_RGB (255, 0, 0)); // (3) 이미지의표시 cvnamedwindow ("Border_replicate", CV_WINDOW_AUTOSIZE); cvshowimage ("Border_replicate", dst_img[0]); cvnamedwindow ("Border_constant", CV_WINDOW_AUTOSIZE); cvshowimage ("Border_constant", dst_img[1]); cvwaitkey (0); cvdestroywindow ("Border_replicate"); cvdestroywindow ("Border_constant"); cvreleaseimage (&src_img); for (i = 0; i < 2; i++) { cvreleaseimage (&dst_img[i]); 243

44 return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변갱하지않고읽어들인다. 또, 경계를작성하기위해서, 입력이미지보다큰출력영역을확보한다. // (2) 경계선의작성함수 cvcopymakeborder() 에의해서, 입력이미지를출력이미지에카피해, 한층더경계를작성한다. 3 번째의인수는, 출력이미지상에카피되는입력이미지의좌상좌표를나타내고있어이미지가카피되지않는부분 ( 이번은, 상하좌우의구석으로부터 offset 픽셀분 ) 이경계영역이된다. 4 번째의인수는, 경계의종류를나타내고있어 IPL_BORDER_REPLICATE 하지만지정되면, 이미지위 / 아래의구석과왼쪽 / 오른쪽의구석 ( 이미지영역의제일외측의값 ) 을이용해경계선을생성한다. 또,4 번째의인수에,IPL_BORDER_CONSTANT 하지만지정되면, 경계는이함수의최후 (5 번째 ) 의파라미터로서건네받은정수 ( 이번은적색 ) 로묻힌다. // (3) 이미지의표시처리된이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 반응을일으키는최소의물리량처리이미지에있는반응을일으키는최소의물리량을마련하고, 화소치가그값보다큰가작은것처럼따르고처리를바꾸고싶다고하는일은자주있다. 예를들면, 단순한배경차분에서는, 현이미지와배경이미지와의화소치의차이의절대치가있는반응을일으키는최소의물리량이하가되는지아닌지로, 배경인가전경인지를판단한다. OpenCV 그럼, 이러한반응을일으키는최소의물리량처리를행하기위한함수,cvThreshold, 및 cvadaptivethreshold ( 이 ) 가준비되어있다. 샘플 이미지의 2 치화 cvthreshold, cvadaptivethreshold cvthreshold, cvadaptivethreshold( 을 ) 를이용하고, 이미지의 2 치화를실시한다 표시의변환 샘플코드 #include <cv.h> 244

45 #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img; IplImage *src_img_gray = 0; IplImage *tmp_img1, *tmp_img2, *tmp_img3; // (1) 이미지를읽어들인다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR); if (src_img == 0) return -1; tmp_img1 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); tmp_img2 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); tmp_img3 = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); src_img_gray = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); cvcvtcolor (src_img, src_img_gray, CV_BGR2GRAY); dst_img = cvcloneimage (src_img); // (2) 가우시안피르타로평활화를실시한다 cvsmooth (src_img_gray, src_img_gray, CV_GAUSSIAN, 5); // (3)2 치화 :cvthreshold cvthreshold (src_img_gray, tmp_img1, 90, 255, CV_THRESH_BINARY); // (4)2 치화 :cvadaptivethreshold cvadaptivethreshold (src_img_gray, tmp_img2, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 11, 10); // (5) 두개의 2 치화이미지의논리적 cvand (tmp_img1, tmp_img2, tmp_img3); cvcvtcolor (tmp_img3, dst_img, CV_GRAY2BGR); // (6) 원이미지와 2 치이미지의논리적 cvsmooth (src_img, src_img, CV_GAUSSIAN, 11); cvand (dst_img, src_img, dst_img); // (7) 이미지를표시한다 cvnamedwindow ("Threshold", CV_WINDOW_AUTOSIZE); cvshowimage ("Threshold", tmp_img1); cvnamedwindow ("AdaptiveThreshold", CV_WINDOW_AUTOSIZE); cvshowimage ("AdaptiveThreshold", tmp_img2); cvnamedwindow ("Image", CV_WINDOW_AUTOSIZE); 245

46 cvshowimage ("Image", dst_img); cvwaitkey (0); cvdestroywindow ("Threshold"); cvdestroywindow ("AdaptiveThreshold"); cvdestroywindow ("Image"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleaseimage (&src_img_gray); cvreleaseimage (&tmp_img1); cvreleaseimage (&tmp_img2); cvreleaseimage (&tmp_img3); return 0; // (1) 이미지를읽어들인다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 또, 2 치화를실시하기위해서입력이미지를그레이스케일로변환한다. // (2) 가우시안피르타로평활화를실시한다 2 치화를실시하기전에, 가우시안피르타로평활화를실시한다. 평활화를실시하는것에의해서, 이미지의노이즈를감소시켜, 안정된 ( 노이즈가적다 ) 2 치화이미지를얻을수있다. // (3)2 치화 :cvthreshold 함수 cvthreshold() 에의해서, 이미지의 2 치화처리를실시한다. 이함수의마지막인수가, 반응을일으키는최소의물리량처리의종류를나타내고있다. CV_THREASH_BINARY 하지만지정되어있는경우는, src(x,y)>threshold 의경우는,dst(x,y) = max_value 그이외는, 0 되도록 ( 듯이 ) 처리를실시한다. 여기서,threshold( 반응을일으키는최소의물리량 )( 은 ) 는 3 번째의인수로, max_value( 최대 치 )( 은 ) 는 4 번째의인수로, 각각지정되어있다. 그외의수법을지정했을경우의처리에대해서는, 레퍼런스메뉴얼을 참조. // (4)2 치화 :cvadaptivethreshold 함수 cvadaptivethreshold() 에의해서, 이미지의 2 치화처리를실시한다. 함수 cvthreshold() 그럼, 모든픽셀에대해같은반응을일으키는최소의물리량을가지고처리를실시했지만, 함수 cvadaptivethreshold() 의경우는, 픽셀마다사용하는반응을일으키는최소의물리량이다르다. 이함수의 5 번째의인수가, 함수 cvthreshold( 와 ) 과같게반응을일으키는최소의물리량처리의종류를나타내고있다. CV_THRESH_BINARY 하지만지정되어있는경우는, src(x,y)>t(x,y) 의경우는,dst(x,y) = max_value 그이외는, 0 되도록 ( 듯이 ) 처리를실시한다. 여기서,max_value( 최대치 )( 은 ) 는 3 번째의인수로지정되어있다. 또,T(x,y)( 은 ) 는, 각픽셀 마다계산된반응을일으키는최소의물리량이며,4 번째의인수로, 의반응을일으키는최소의물리량의산출방법이 지정된다. CV_ADAPTIVE_THRESH_MEAN_C 하지만지정되었을경우는, 주목픽셀의 block_size block_size 인접영역 246

47 의평균으로부터,param1 ( 을 ) 를당긴값이되어, block_size 하 6 번째의인수로,param1 하 7 번째의인수로, 각각지정된 다. // (5) 두개의 2 치화이미지의논리적두개의함수에의해서 2 치화된이미지의논리적을계산한다. 즉, 2 종류의 2 치화이미지를합성해,3 채널이미지로변환한다. // (6) 원이미지와 2 치이미지의논리적 ( 평활화한 ) 입력이미지와 2 치화이미지의요소마다의논리적을계산한다. 이것에의해, 입력이미지상에 2 치화이미지가합성된이미지를얻는다. // (7) 이미지를표시한다각각의 2 치화이미지, 및입력이미지와거듭해맞춘이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 입력이미지 Threshold AdaptiveThreshold 합성이미지 이미지의 2 치화 ( 오츠의수법 ) cvthreshold 오츠의수법을이용하고반응을일으키는최소의물리량을결정해, 이미지의 2 치화를실시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img; if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE); if (src_img == 0) return -1; dst_img = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); 247

48 cvsmooth (src_img, src_img, CV_GAUSSIAN, 5); // (1)2 치화 ( 오츠의수법을이용 ) cvthreshold (src_img, dst_img, 0, 255, CV_THRESH_BINARY CV_THRESH_OTSU); cvnamedwindow ("Threshold", CV_WINDOW_AUTOSIZE); cvshowimage ("Threshold", dst_img); cvwaitkey (0); cvdestroywindow ("Threshold"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 0; // (1)2 치화 ( 오츠의수법을이용 ) 전술의 2 치화와다른것은, 마지막인수 (threshold type) 에,CV_THRESHOLD_OTSU 하지만더해지고있는점이다. OpencvCV 시점에서의레퍼런스메뉴얼에는기술이없다 (CVS 판에는있다 ) 가, 함수 cvthreshold()( 은 ) 는, 오츠의수법으로불리는반응을일으키는최소의물리량결정수법을실장하고있다. 여기서이용되는오츠의수법이란, 어느값의집합을두개클래스로분류하는경우의, 적절한반응을일으키는최소의물리량을결정하는수법이다. 두개의클래스내의분산과클래스간의분산을생각해이러한비가최소에 ( 즉, 클래스비밀산은가능한한작고, 클래스간분산은가능한한크고 ) 되는반응을일으키는최소의물리량을요구한다. 따라서,3 번째의인수 (threshold) 에게주는값은이용되지않기때문에, 적당한값을지정해좋다. 자세한것은, 이하의문헌을참고로하는것. 오츠, " 판별및최소 2 승기준에근거하는자동해귀의치선정법 ", 전자통신학회논문잡지, Vol.J63-D, No.4, pp , N. Otsu, "A threshold selection method from gray level histograms",ieee Trans. Systems, Man and Cybernetics, 1979, Vol.9, pp 실행결과예 이미지피라밋드의작성 cvpyrdown, cvpyrup 입력이미지로부터, 피라밋드의상하의층이미지를작성한다 248

49 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img, *dst_img1, *dst_img2; if (argc!= 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR)) == 0) return -1; dst_img1 = cvcreateimage (cvsize (src_img->width / 2, src_img->height / 2), src_img- >depth, src_img->nchannels); dst_img2 = cvcreateimage (cvsize (src_img->width * 2, src_img->height * 2), src_img- >depth, src_img->nchannels); // (1) 입력이미지에대한이미지피라밋드를구성 cvpyrdown (src_img, dst_img1, CV_GAUSSIAN_5x5); cvpyrup (src_img, dst_img2, CV_GAUSSIAN_5x5); cvnamedwindow ("Original", CV_WINDOW_AUTOSIZE); cvshowimage ("Original", src_img); cvnamedwindow ("PyrDown", CV_WINDOW_AUTOSIZE); cvshowimage ("PyrDown", dst_img1); cvnamedwindow ("PyrUp", CV_WINDOW_AUTOSIZE); cvshowimage ("PyrUp", dst_img2); cvwaitkey (0); cvdestroywindow ("Original"); cvdestroywindow ("PyrDown"); cvdestroywindow ("PyrUp"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img1); cvreleaseimage (&dst_img2); return 0; // (1) 입력이미지에대한이미지피라밋드를구성함수 cvpyrdown() 에의해서, 입력이미지의저해상 (1/4) 번이미지를, 함수 cvpryup() 에의해서, 고해상도이미지 (2)( 을 ) 를구성한다. 마지막인수,CV_GAUSSIAN_5x5( 현재서포트되고있는값은, 이것만 )( 을 ) 를지정하는것에의해서, 가우시안피르타에근거한리산프링을한다. 249

50 실행결과예 이미지피라밋드를이용한이미지의영역분할 cvpyrsegmentation 레벨을지정해이미지피라밋드를작성해, 그정보를이용해이미지의부분화를행한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int level = 4; double threshold1, threshold2; IplImage *src_img = 0, *dst_img; CvMemStorage *storage = 0; CvSeq *comp = 0; CvRect roi; // (1) 이미지의읽기 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); // (2) 영역분할을위해서 ROI( 을 ) 를세트한다 roi.x = roi.y = 0; roi.width = src_img->width & -(1 << level); roi.height = src_img->height & -(1 << level); cvsetimageroi (src_img, roi); 250

51 // (3) 분할결과이미지출력용의이미지영역을확보해, 영역분할을실행 dst_img = cvcloneimage (src_img); storage = cvcreatememstorage (0); threshold1 = 255.0; threshold2 = 50.0; cvpyrsegmentation (src_img, dst_img, storage, &comp, level, threshold1, threshold2); // (4) 입력이미지와분할결과이미지의표시 cvnamedwindow ("Source", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Segmentation", CV_WINDOW_AUTOSIZE); cvshowimage ("Source", src_img); cvshowimage ("Segmentation", dst_img); cvwaitkey (0); cvdestroywindow ("Source"); cvdestroywindow ("Segmentation"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasememstorage (&storage); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. // (2) 영역분할을위해서 ROI( 을 ) 를세트한다 cvpyrsegmentation( 은 ) 는,2 ( 피라밋드의레벨 ) 그리고나뉘어떨어지는이미지사이즈밖에처리할수없다 ( 에러가발생한다 ). 그때문에, 지정한레벨에대응한다 ROI 사이즈를입력이미지에세트할필요가있다. 샘플프로그램에서는, 지정한레벨수만큼 1 을왼쪽으로시프트해 (2 ( 지정한레벨 ) ( 을 ) 를계산 ), 그후 2 의보수를취하는일로비트마스크를작성해, 원이미지의 width, height( 와 ) 과의 AND( 을 ) 를취해, 나뉘어떨어지는이미지사이즈 ( 을 ) 를계산한다. // (3) 분할결과이미지출력용의이미지영역을확보해, 영역분할을실행입력이미지와같은사이즈, 채널수, 데프스의출력용이미지 dst_img( 을 ) 를,cvCloneImage() 그리고확보해, 2 개의반응을일으키는최소의물리량을세트하고, 영역분할을실행한다. // (4) 입력이미지와분할결과이미지의표시윈도우를생성해, 입력이미지, 분할결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 251

52 실행결과예입력이미지와영역분할결과 [ 왼쪽에서 ] 입력이미지, 영역분할결과 (threshold2=25), 영역분할결과 (threshold2=50), 영역분할결과 (threshold2=75), 영역분할결과 (threshold2=100) 평균치시프트법에따르는이미지의부분화 cvpyrmeanshiftfiltering 평균치시프트법에따르는이미지의부분화를실시한다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int level = 2; IplImage *src_img = 0, *dst_img; CvRect roi; // (1) 이미지의읽기 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); if (src_img->nchannels!= 3) exit (-1); if (src_img->depth!= IPL_DEPTH_8U) exit (-1); // (2) 영역분할을위해서 ROI( 을 ) 를세트한다 roi.x = roi.y = 0; roi.width = src_img->width & -(1 << level); 252

53 roi.height = src_img->height & -(1 << level); cvsetimageroi (src_img, roi); // (3) 분할결과이미지출력용의이미지영역을확보해, 영역분할을실행 dst_img = cvcloneimage (src_img); cvpyrmeanshiftfiltering (src_img, dst_img, 30.0, 30.0, level, cvtermcriteria (CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 5, 1)); // (4) 입력이미지와분할결과이미지의표시 cvnamedwindow ("Source", CV_WINDOW_AUTOSIZE); cvnamedwindow ("MeanShift", CV_WINDOW_AUTOSIZE); cvshowimage ("Source", src_img); cvshowimage ("MeanShift", dst_img); cvwaitkey (0); cvdestroywindow ("Source"); cvdestroywindow ("MeanShift"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. // (2) 영역분할을위해서 ROI( 을 ) 를세트한다 cvpyrmeanshiftfiltering 도위에서나타내보였다 cvpyrsegmentation( 와 ) 과같이, 2 ( 피라밋드의레벨 ) 그리고나뉘어떨어지는이미지사이즈밖에처리할수없다 ( 에러가발생한다 ). 그때문에, 지정한레벨에대응한다 ROI 사이즈를입력이미지에세트할필요가있다. 샘플프로그램에서는, 지정한레벨수만큼 1 을왼쪽으로시프트해 (2 ( 지정한레벨 ) ( 을 ) 를계산 ), 그후 2 의보수를취하는일로비트마스크를작성해, 원이미지의 width, height( 와 ) 과의 AND( 을 ) 를취해, 나뉘어떨어지는이미지사이즈를계산한다. // (3) 분할결과이미지출력용의이미지영역을확보해, 영역분할을실행입력이미지와같은사이즈, 채널수, 데프스의출력용이미지 dst_img( 을 ) 를,cvCloneImage() 그리고확보해, 2 개의반응을일으키는최소의물리량을세트하고, 영역분할을실행한다. // (4) 입력이미지와분할결과이미지의표시윈도우를생성해, 입력이미지, 분할결과이미지를표시해, 무엇인가키가밀릴때까지기다린다. 253

54 실행결과예입력이미지와부분화결과 [ 왼쪽에서 ] 입력이미지, 부분화결과 Watershed 알고리즘에의한이미지의영역분할 cvwatershed 마우스로원형의마커 ( 배정영역 ) 의중심을지정해, 복수의마커를설정한다. 이마커를이미지의 gradient 에따라서넓혀가,gradient 의높은부분에할수있는경계를바탕으로영역을분할한다. 영역은, 최초로지정한마커의수로분할된다. 이샘플코드는, 마우스이벤트를처리하기위한함수 (on_mouse)( 을 ) 를포함하고있다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> IplImage *markers = 0, *dsp_img = 0; /* 마우스이벤트용콜백함수 */ void on_mouse (int event, int x, int y, int flags, void *param) { int seed_rad = 20; static int seed_num = 0; CvPoint pt; // (1) 클릭에의해중심을지정해, 원형의배정영역을설정한다 if (event == CV_EVENT_LBUTTONDOWN) { seed_num++; pt = cvpoint (x, y); cvcircle (markers, pt, seed_rad, cvscalarall (seed_num), CV_FILLED, 8, 0); cvcircle (dsp_img, pt, seed_rad, cvscalarall (255), 3, 8, 0); cvshowimage ("image", dsp_img); /* 메인프로그램 */ 254

55 int main (int argc, char **argv) { int *idx, i, j; IplImage *src_img = 0, *dst_img = 0; // (2) 이미지의읽어들여, 마커이미지의초기화, 결과표시용이미지영역의확보를행한다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); dsp_img = cvcloneimage (src_img); dst_img = cvcloneimage (src_img); markers = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_32S, 1); cvzero (markers); // (3) 입력이미지를표시해배정컴퍼넌트지정을위한마우스이벤트를등록한다 cvnamedwindow ("image", CV_WINDOW_AUTOSIZE); cvshowimage ("image", src_img); cvsetmousecallback ("image", on_mouse, 0); cvwaitkey (0); // (4)watershed 분할을실행한다 cvwatershed (src_img, markers); // (5) 실행결과의이미지중의 watershed 경계 ( 픽셀치 =-1)( 을 ) 를결과표시용이미지상에표시한다 for (i = 0; i < markers->height; i++) { for (j = 0; j < markers->width; j++) { idx = (int *) cvptr2d (markers, i, j, NULL); if (*idx == -1) cvset2d (dst_img, i, j, cvscalarall (255)); cvnamedwindow ("watershed transform", CV_WINDOW_AUTOSIZE); cvshowimage ("watershed transform", dst_img); cvwaitkey (0); cvdestroywindow ("watershed transform"); cvreleaseimage (&markers); cvreleaseimage (&dsp_img); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); 255

56 return 1; // (1) 클릭에의해중심을지정해, 원형의배정영역을설정한다입력이미지를표시한윈도우상에서, 마우스클릭의이벤트가발생하면, 배정의수 (seed_num)( 을 ) 를하나늘려, 마커이미지 (markers) 위에, 기정반경에서색을 seed_num 인전부칠해엔을그리기한다. 이,marker 하지만 watershed 분할의배정이된다. 무엇인가키입력을행할때까지, 배정을추가할수가있다. // (2) 이미지의읽어들여, 마커이미지의초기화, 결과표시용이미지영역의확보를행한다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 동시에표시용이미지로서입력이미지를카피한다. 마커이미지로서 32 비트싱글채널의이미지영역 (markers)( 을 ) 를확보한다. 이마커이미지에서는,watershed 경계가 -1, 관계가미지의영역은 0, 그이외에서는속한다영역번호가들어가있다. // (3) 입력이미지를표시해배정컴퍼넌트지정을위한마우스이벤트를등록한다입력이미지를표시해, 이윈도우상에서의마우스이벤트에대한콜백함수 (on_mouse)( 을 ) 를지정한다. // (4)watershed 분할을실행한다마커이미지설정완료의키입력을기다려,cvWatershed()( 을 ) 를실행한다. // (5) 실행결과의이미지중의 watershed 경계 ( 픽셀치 =-1)( 을 ) 를결과표시용이미지상에표시한다분할결과를보관유지하고있는마커이미지를스캔 (cvptr2d 그리고지정한배열요소에의포인터가되돌아온다 ) 해, 값이 -1 의픽셀에대응하는결과표시용이미지 (dst_img) 의픽셀치를 255( 흰색 ) 에 cvset2d()( 을 ) 를이용해설정해, 결과를표시한다. 실행결과예입력이미지와영역분할결과 [ 왼쪽에서 ] 입력이미지, 입력이미지상에표시한마커영역, 영역분할결과 이미지의윤곽검출 OpenCV그럼, 윤곽은다양한데이터구조에의해관리된다. 즉, 모든윤곽이단순한리스트로표현되기도하면, 부모와자식관계를보관유지한트리구조로표현되기도한다. 윤곽을이용한처리 ( 포함구형, 근사, 비교 ) 의해설붙어다른장에양보해, 여기에서는, 윤곽의검출과주사에대해간단하게설명한다. 샘플 256

57 윤곽의검출과그리기 cvfindcontours 이미지중으로부터윤곽을검출해,-1~+1 까지의레벨에있는윤곽을그리기한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { int i; IplImage *src_img = 0, **dst_img; IplImage *src_img_gray = 0; IplImage *tmp_img; CvMemStorage *storage = cvcreatememstorage (0); CvSeq *contours = 0; int levels = 0; if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR); if (src_img == 0) return -1; src_img_gray = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); cvcvtcolor (src_img, src_img_gray, CV_BGR2GRAY); tmp_img = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); dst_img = (IplImage **) cvalloc (sizeof (IplImage *) * 3); for (i = 0; i < 3; i++) { dst_img[i] = cvcloneimage (src_img); // (1) 이미지의 2 치화 cvthreshold (src_img_gray, tmp_img, 120, 255, CV_THRESH_BINARY); // (2) 윤곽의검출 cvfindcontours (tmp_img, storage, &contours, sizeof (CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); // (3) 윤곽의그리기 cvdrawcontours (dst_img[0], contours, CV_RGB (255, 0, 0), CV_RGB (0, 255, 0), levels - 1, 2, CV_AA, cvpoint (0, 0)); 257

58 cvdrawcontours (dst_img[1], contours, CV_RGB (255, 0, 0), CV_RGB (0, 255, 0), levels, 2, CV_AA, cvpoint (0, 0)); cvdrawcontours (dst_img[2], contours, CV_RGB (255, 0, 0), CV_RGB (0, 255, 0), levels + 1, 2, CV_AA, cvpoint (0, 0)); // (4) 이미지의표시 cvnamedwindow ("Level:-1", CV_WINDOW_AUTOSIZE); cvshowimage ("Level:-1", dst_img[0]); cvnamedwindow ("Level:0", CV_WINDOW_AUTOSIZE); cvshowimage ("Level:0", dst_img[1]); cvnamedwindow ("Level:1", CV_WINDOW_AUTOSIZE); cvshowimage ("Level:1", dst_img[2]); cvwaitkey (0); cvdestroywindow ("Level:-1"); cvdestroywindow ("Level:0"); cvdestroywindow ("Level:1"); cvreleaseimage (&src_img); cvreleaseimage (&src_img_gray); cvreleaseimage (&tmp_img); for (i = 0; i < 3; i++) { cvreleaseimage (&dst_img[i]); cvfree (dst_img); cvreleasememstorage (&storage); return 0; // (1) 이미지의 2 치화읽어들인이미지를 2 치화한다. 함수 cvfindcontours()( 은 ) 는, 처리대상의이미지를 2 값이미지로서취급한다 ( 값이 0 이외의픽셀은 "1",0 의픽셀은 "0"( 으 ) 로한다 ) 유익이다. // (2) 윤곽의검출 2 치화된이미지로부터, 윤곽을검출한다.5 번째의인수에서는, 윤곽의추출모드를지정한다. 여기에서는,CV_RETR_TREE ( 을 ) 를지정해, 모든윤곽을추출해, 분기한윤곽을완전하게표현하는계층구조를구성한다. 또,6 번째의인수는, 윤곽의근사수법을나타내고있어 CV_CHAIN_APPROX_SIMPLE 하지만지정되었을경우는, 수평 수직 기울기의선분이압축되어각각의단점만이점렬로서남는다. 그외의모드, 근사수법에대해서는, 레퍼런스메뉴얼을참조하는것. // (3) 윤곽의그리기함수 cvdrawcontours()( 을 ) 를이용하고, 윤곽을그리기한다. 이번은, 이하와같이 -1,0,1 의세개의레벨을지정해윤곽을그리기하고있다. 258

59 -1:2 번째의인수로지정한윤곽과그아이의레벨 0 까지의윤곽 ( 즉, 하나하의계층의윤곽 ) 이그리기된다 0:2 번째의인수로지정한윤곽만이그리기된다 +1:2 번째의인수로지정한윤곽과동레벨의윤곽 ( 즉, 같은계층에있는윤곽 ) 이그리기된다 // (4) 이미지의표시윈도우를생성해,3 종류의윤곽추출이미지를표시하고, 무엇인가키가밀릴때까지기다린다. 실행결과예 Level:-1 Level:0 Level:+1 이미지와형상의모멘트 이미지나형상의특징을정량적으로표현하는방법의하나에모멘트가있다. 이미지의 (x_order+y_order) 다음의공간 모멘트는다음과같이정의된다. M x_order,y_order =sum x,y (I(x,y) x x_order y y_order ) 여기서,I(x,y)( 은 ) 는좌표 (x, y) 의휘도치이며,x_order, y_order( 은 ) 는각각x방향,y방향의차수를나타내고있다. 2값이미지의경우,0다음모멘트M 0,0 하지만면적을나타내,( M 1,0 /M 0,0, M 0,1 /M 0,0 ) 하지만중심좌표를나타내는일이된다. 샘플 이미지의모멘트 이미지의각종모멘트치를계산한다 표시의변환 샘플코드 #include <stdio.h> #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { char text[10][30]; int i; double spatial_moment, central_moment, norm_c_moment; 259

60 IplImage *src_img = 0; CvFont font; CvSize text_size; CvMoments moments; CvHuMoments hu_moments; // (1) 이미지를읽어들인다.3 채널이미지의경우는 COI 하지만세트되어있지않으면안된다 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) return -1; if (src_img->nchannels == 3 && cvgetimagecoi (src_img) == 0) cvsetimagecoi (src_img, 1); // (2) 입력이미지의 3 차까지의이미지모멘트를계산한다 cvmoments (src_img, &moments, 0); cvsetimagecoi (src_img, 0); // (3) 모멘트나 Hu 모멘트불변량을, 얻을수있었다 CvMoments 구조체의값을사용해계산한다. spatial_moment = cvgetspatialmoment (&moments, 0, 0); central_moment = cvgetcentralmoment (&moments, 0, 0); norm_c_moment = cvgetnormalizedcentralmoment (&moments, 0, 0); cvgethumoments (&moments, &hu_moments); // (4) 얻을수있던모멘트나 Hu 모멘트불변량을문자로서이미지에그리기 cvinitfont (&font, CV_FONT_HERSHEY_SIMPLEX, 1.0, 1.0, 0, 2, 8); snprintf (text[0], 30, "spatial=%.3f", spatial_moment); snprintf (text[1], 30, "central=%.3f", central_moment); snprintf (text[2], 30, "norm=%.3f", spatial_moment); snprintf (text[3], 30, "hu1=%f", hu_moments.hu1); snprintf (text[4], 30, "hu2=%f", hu_moments.hu2); snprintf (text[5], 30, "hu3=%f", hu_moments.hu3); snprintf (text[6], 30, "hu4=%f", hu_moments.hu4); snprintf (text[7], 30, "hu5=%f", hu_moments.hu5); snprintf (text[8], 30, "hu6=%f", hu_moments.hu6); snprintf (text[9], 30, "hu7=%f", hu_moments.hu7); cvgettextsize (text[0], &font, &text_size, 0); for (i = 0; i < 10; i++) cvputtext (src_img, text[i], cvpoint (10, (text_size.height + 3) * (i + 1)), &font, cvscalarall (0)); // (5) 입력이미지와모멘트계산결과를표시, 키가밀렸을때에종료 cvnamedwindow ("Image", CV_WINDOW_AUTOSIZE); cvshowimage ("Image", src_img); 260

61 cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&src_img); return 0; // (1) 이미지를읽어들인다.3 채널이미지의경우는 COI 하지만세트되어있지않으면안된다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 만약, 지정된이미지가 3 채널이미지이면,COI 하지만세트되어있지않으면, 모멘트의계산을할수없기때문에체크한후, 제일최초의채널을지정해둔다. // (2) 입력이미지의 3 차까지의이미지모멘트를계산한다이미지의 3 다음까지의공간모멘트와중심모멘트를함수 cvmoments() 그리고계산한다. CvMoments 구조체는, 이하와같이정의되고있어 typedef struct CvMoments { double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; /* spatial moments */ double mu20, mu11, mu02, mu30, mu21, mu12, mu03; /* central moments */ double inv_sqrt_m00; /* m00!= 0? 1/sqrt(m00) : 0 */ CvMoments; 샘플예의경우이면, 예를들면직접,moments.m00 ( 으 ) 로서 0 다음모멘트의계산결과를취득하는일도할수있다. 결과표시때문에,COI 의설정을해제해둔다. // (3) 모멘트나 Hu 모멘트불변량을얻을수있었다 CvMoments 구조체의값을사용해계산한다. 위와같게직접구조체의내용에액세스해도계산결과는취득할수있지만,OpenCV 에는함수 cvgetspatialmoment(), cvgetcentralmoment(), cvgetnormalizedcentralmoment() 등에서, 필요한공간모멘트나중심모멘트, 정규화된중심모멘트등을취득할수가있다. 샘플예에서는모든 0 차모멘트치의취득을지정했다. 또 Hu 모멘트불변량도계산한다.7 의 Hu 모멘트불변량의각각의의미는, 레퍼런스를참조의일. // (4) 얻을수있던각종모멘트치나 Hu 모멘트불변량을문자로서이미지에그리기윈도우를생성해, 요구한모멘트치를표시하기위해서, 함수 cvputtext()( 을 ) 를이용하고, 값을텍스트로서이미지에덧쓰기해, 무엇인가키가밀릴때까지기다린다. 실행결과예 261

62 하후변환 OpenCV그럼, 표준적하후변환, 확률적하후변환, 멀티스케일형의고전적하후변환의 3 종류의수법을실장하고있다. 표준적하후변환과고전적하후변환에서는, 검출된선은점 (0, 0)( 으 ) 로부터선까지의거리ρ라고선의법선이 x축과이루는모퉁이θ의2개의값으로나타내진다. 한편확률적하후변환에서는, 단점을가지는선분으로서선을검출한다. 그때문에, 검출된선분은, 시점과종점에서나타내진다. 또, 엔은중심과반경을나타내는 3개의파라미터로표현할수있기위해, 직선검출로투표에이용하는하후공간을, 삼차원에확장하는것으로엔을검출하는것이가능하게된다. 샘플 하후변환에의한직선검출 cvhoughlines2 표준적하후변환과확률적하후변환을지정해선 ( 선분 ) 의검출을행한다. 샘플코드내의각파라미터치는처리예의이미지에대해서튜닝되고있다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <math.h> int main (int argc, char **argv) { int i; float *line, rho, theta; double a, b, x0, y0; IplImage *src_img_std = 0, *src_img_prob = 0, *src_img_gray = 0; CvMemStorage *storage; CvSeq *lines = 0; CvPoint *point, pt1, pt2; // (1) 이미지의읽기 if (argc >= 2) src_img_gray = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE); if (src_img_gray == 0) return -1; src_img_std = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR); src_img_prob = cvcloneimage (src_img_std); // (2) 하후변환을위한사전처리 cvcanny (src_img_gray, src_img_gray, 50, 200, 3); storage = cvcreatememstorage (0); 262

63 // (3) 표준적하후변환에의한선의검출과검출한선의그리기 lines = cvhoughlines2 (src_img_gray, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 50, 0, 0); for (i = 0; i < MIN (lines->total, 100); i++) { line = (float *) cvgetseqelem (lines, i); rho = line[0]; theta = line[1]; a = cos (theta); b = sin (theta); x0 = a * rho; y0 = b * rho; pt1.x = cvround (x * (-b)); pt1.y = cvround (y * (a)); pt2.x = cvround (x * (-b)); pt2.y = cvround (y * (a)); cvline (src_img_std, pt1, pt2, CV_RGB (255, 0, 0), 3, 8, 0); // (4) 확률적하후변환에의한선분의검출과검출한선분의그리기 lines = 0; lines = cvhoughlines2 (src_img_gray, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 50, 50, 10); for (i = 0; i < lines->total; i++) { point = (CvPoint *) cvgetseqelem (lines, i); cvline (src_img_prob, point[0], point[1], CV_RGB (255, 0, 0), 3, 8, 0); // (5) 검출결과표시용의윈도우를확보해표시한다 cvnamedwindow ("Hough_line_standard", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Hough_line_probalistic", CV_WINDOW_AUTOSIZE); cvshowimage ("Hough_line_standard", src_img_std); cvshowimage ("Hough_line_probalistic", src_img_prob); cvwaitkey (0); cvdestroywindow ("Hough_line_standard"); cvdestroywindow ("Hough_line_standard"); cvreleaseimage (&src_img_std); cvreleaseimage (&src_img_prob); cvreleaseimage (&src_img_gray); cvreleasememstorage (&storage); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고 263

64 읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_GRAYSCALE ( 을 ) 를지정하는것으로, 처리용으로서 1 채널, 굿의자케일로변환한다. 또, 표시용 ( 검출한선을빨강으로나타내보인다 ) 을위해서,2 번째의인수에 CV_LOAD_IMAGE_COLOR ( 을 ) 를지정하고, 칼라이미지이라고해도읽어들여둔다. // (2) 하후변환을위한사전처리 cvhoughlines2()( 은 ) 는, 입력으로서 8 비트, 싱글채널, 2 치화이미지를취한다. 그때문에사전처리로서 cvcanny()( 을 ) 를이용하고, 입력처리용의그레이스케일이미지를엣지이미지로변환한다.cvCanny()( 은 ) 는, 샘플과같이인프레이스처리가가능하다. 또,cvHoughLines2() 그리고사용하는검출결과의선을보존하는메모리스트레이지를생성해둔다. // (3) 표준적하후변환에의한선의검출과검출한선의그리기표준적하후변환 CV_HOUGH_STANDARD( 을 ) 를지정하고, 엣지이미지로부터선을검출한다. 검출결과는순서 lines 의선두포인터로서돌려주어진다. 하나하나의선은, 순서 lines( 으 ) 로부터,cvGetSeqElem()( 을 ) 를사용하고, 인덱스의순서에꺼낸다. 순서 lines 의하나의요소 line( 은 ) 는,32 비트부동소수점형 2 채널배열 ( 배열요소가 2 개 ) CV_32FC2 ( 주 1) 이기위해,line[0] 에 ρ 하지만,line[1] 에 θ 하지만들어간다. 최초로,ρ( 와 ) 과 θ( 을 ) 를이용해선상의 1 점 (x0, y0)( 을 ) 를,x0=ρ*cos(θ), y0=ρ*sin(θ)( 으 ) 로서계산한다. 다음에 (x0, y0)( 을 ) 를중심으로서각각 1000pixel 멀어진선상의 2 점 pt1,pt2( 을 ) 를계산한다. 여기서 θ 하지만검출된선의법선방향을나타내고있기때문에, 검출된선과 x 축이이루는모퉁이는 θ+90[deg] 된다.sin(θ+90) = cos(θ),cos(θ+90) = -sin(θ) 이기위해,pt1.x = x * (-sin(θ)), pt1.y = y * cos(θ) 그리고얻을수있다. 선상의 2 점을이용하고, 결과표시용의이미지데이터 src_img_std 위에적색, 선폭 3 그리고직선을표시한다. 이미지영역외의선은클리핑된다. 검출직선수가많아져결과가보기나뻐지므로, 표시를 100 line 까지누르고있다. // (4) 확률적하후변환에의한선분의검출과검출한선분의그리기확률적하후변환 CV_HOUGH_PROBABILISTIC( 을 ) 를지정하고, 엣지이미지로부터선분을검출한다. 검출결과는순서 lines 의선두포인터로서돌려주어진다. 하나하나의선분은, 순서 lines( 으 ) 로부터,cvGetSeqElem()( 을 ) 를사용하고, 인덱스의순서에꺼낸다. 순서 lines 의하나의요소 point( 은 ) 는,32 비트정수형 4 채널배열 ( 배열요소가 4 개 ) CV_32SC4 ( 주 1) 이기위해,point[0] 에시점좌표가,point[1] 에종점좌표가들어간다. 시점, 종점을이용하고, 결과표시용의이미지데이터 src_img_prob 위에적색, 선폭 3 그리고선분을표시한다. // (5) 검출결과표시용의윈도우를확보해표시한다윈도우를생성해, 직선검출결과를표시해, 무엇인가키가밀릴때까지기다린다. ( 주 1) CV_< 비트수 ( 데프스 )>(S U F)C< 채널수 > 264

65 실행결과예입력이미지와직선검출결과 [ 좌 ] 입력이미지 [ 중 ] 표준적하후변환에의해, 검출된직선 (100[line] 마셔표시 ) [ 우 ] 확률적하후변환에의해, 검출된선분 하후변환에의한엔검출 cvhoughcircles 그레이스케일이미지중의엔을하후변환을이용해검출한다. 샘플코드내의각파라미터치는처리예의이미지에대해서튜닝되고있다. 표시의변환 샘플코드 #include <stdio.h> #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i; float *p; IplImage *src_img = 0, *src_img_gray = 0; CvMemStorage *storage; CvSeq *circles = 0; // (1) 이미지의읽기 if (argc >= 2) src_img_gray = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE); if (src_img_gray == 0) exit (-1); src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR); // (2) 하후변환을위한사전처리 ( 이미지의평활화를행하지않으면오류검출이발생하기쉽다 ) cvsmooth (src_img_gray, src_img_gray, CV_GAUSSIAN, 11, 11, 0, 0); storage = cvcreatememstorage (0); 265

66 // (3) 하후변환에의한엔의검출과검출한엔의그리기 circles = cvhoughcircles (src_img_gray, storage, CV_HOUGH_GRADIENT, 1, 100, 20, 50, 10, MAX (src_img_gray->width, src_img_gray- >height)); for (i = 0; i < circles->total; i++) { p = (float *) cvgetseqelem (circles, i); cvcircle (src_img, cvpoint (cvround (p[0]), cvround (p[1])), 3, CV_RGB (0, 255, 0), - 1, 8, 0); cvcircle (src_img, cvpoint (cvround (p[0]), cvround (p[1])), cvround (p[2]), CV_RGB (255, 0, 0), 3, 8, 0); // (4) 검출결과표시용의윈도우를확보해표시한다 cvnamedwindow ("circles", 1); cvshowimage ("circles", src_img); cvwaitkey (0); cvdestroywindow ("circles"); cvreleaseimage (&src_img); cvreleaseimage (&src_img_gray); cvreleasememstorage (&storage); return 0; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고 src_img_gray 에읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_GRAYSCALE ( 을 ) 를지정하는것으로, 처리용으로서 1 채널, 그레이스케일로변환한다. 또, 표시용 ( 검출한선을빨강으로나타내보인다 ) 을위해서,2 번째의인수에 CV_LOAD_IMAGE_COLOR ( 을 ) 를지정하고, 칼라이미지 src_img( 이 ) 라고해도읽어들여둔다. // (2) 하후변환을위한사전처리 cvhoughcircles()( 은 ) 는, 입력으로서 8 비트, 싱글채널, 그레이스케일이미지를이용한다. 오류검출을줄이기위해서, 사전처리로서 cvsmooth()( 을 ) 를이용한이미지의평활화를베푼다. cvsmooth()( 은 ) 는, 샘플과같이인프레이스처리가가능하다. 또,cvHoughCircles() 그리고사용하는검출결과의엔을보존하는메모리스트레이지를생성해둔다. // (3) 하후변환에의한엔의검출과검출한엔의그리기기본적인 2 단계하후변환 CV_HOUGH_GRADIENT( 을 ) 를지정하고 ( 현재는이것밖에실장되어있지않다 ), 그레이스케일이미지로부터엔을검출한다. 검출결과는순서 circles 의선두포인터로서돌려주어진다. 하나하나의엔은, 순서 circles( 으 ) 로부터,cvGetSeqElem()( 을 ) 를사용하고, 인덱스의순서에꺼낸다. 순서 circles 의하나의요소 p( 은 ) 는,32 비트부동소수점형 3 채널배열 ( 배열요소가 3 개 ) 266

67 CV_32FC3 ( 주 1) 이기위해,p[0] 에엔중심의 x 좌표치가,p[1] 에엔중심의 y 좌표가, p[2] 에엔의반경이각각들어간다. 검출된엔을결과표시용의이미지데이터 src_img 위에적색, 선폭 3 그리고표시해, 엔의중심을초록으로표시하는, // (4) 검출결과표시용의윈도우를확보해표시한다윈도우를생성해, 엔검출결과를표시해, 무엇인가키가밀릴때까지기다린다. ( 주 1) CV_< 비트수 ( 데프스 )>(S U F)C< 채널수 > 실행결과예입력이미지와엔검출결과 [ 좌 ] 입력이미지 [ 우 ] 하후변환에의해검출된엔 거리변환 2값이미지의각화소에대해서, 거기로부터값이0인화소에의최단거리를주는변환을거리변환이라고한다.Open CV그럼, 마스크를지정하지않고근사없음의정확한거리계산을행하는일도, 처리의고속화를위해서, 지정한마스크의시프트를이용한근사계산을행하는일도가능하다. 샘플 거리변환과그가시화 cvdisttransform 입력이미지에대해서거리변환을행해, 결과를 에정규화해가시화한다 표시의변환 샘플코드 267

68 #include <stdio.h> #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *dst_img, *dst_img_norm; // (1) 이미지를읽어들여 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); if (src_img->nchannels!= 1) exit (-1); if (src_img->depth!= IPL_DEPTH_8U) exit (-1); // (2) 처리결과의거리이미지출력용의이미지영역과표시윈도우를확보 dst_img = cvcreateimage (cvsize (src_img->width, src_img->height), IPL_DEPTH_32F, 1); dst_img_norm = cvcreateimage (cvsize (src_img->width, src_img->height), IPL_DEPTH_8U, 1); // (3) 거리이미지를계산해, 표시용으로결과를 에정규화한다 cvdisttransform (src_img, dst_img, CV_DIST_L2, 3, NULL, NULL); cvnormalize (dst_img, dst_img_norm, 0.0, 255.0, CV_MINMAX, NULL); // (4) 거리이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Source", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Distance Image", CV_WINDOW_AUTOSIZE); cvshowimage ("Source", src_img); cvshowimage ("Distance Image", dst_img_norm); cvwaitkey (0); cvdestroywindow ("Source"); cvdestroywindow ("Distance Image"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleaseimage (&dst_img_norm); return 0; 268

69 // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변갱하지않고읽어들인다. cvdisttransform()( 은 ) 는, 입력으로서 8 비트싱글채널의 2 치이미지밖에잡히지않는다의로, 여기서입력이미지의채널수와데프스의체크를행한다. // (2) 처리결과의거리이미지출력용의이미지영역과표시윈도우를확보 cvdisttransform() 의출력은,32 비트부동소수점형싱글채널이기위해, 채널, 데프스를지정해출력용이미지 dst_img( 을 ) 를준비한다. 또, 결과를보기쉽게표시하기위한결과표시용의 8 비트부호없음싱글채널의이미지영역 dst_img_norm( 을 ) 를준비해, 이이미지를표시하기위한윈도우를준비한다. // (3) 거리이미지를계산해, 표시용으로결과를 에정규화한다각픽셀로부터값 0 까지의최근거리거리를계산한다.cvDistTransform() 의제 1, 제 2 인수의형태는 CvArr* 이지만, 샘플로나타내보이도록 ( 듯이 ), 직접 IplImage* 형태의변수를지정할수있다. 또이샘플에서는, 고속으로근사거리를요구하기위해서 CV_DIST_L2(Euclid 거리 ), 3 3 마스크를지정해있다. 계산후의이미지의화소치의범위를,cvNormalized()( 을 ) 를이용해 에정규화한다 (8 비트부호없음싱글채널이미지로한다 ). 정규화의타입으로서 CV_MINMAX( 배열의값이지정의범위에들어가도록 ( 듯이 ) 슬캘링과시프트를실시한다 )( 을 ) 를지정해, 제 3, 제 4 인수로그범위를지정해두면간단하게정규화를할수있다. // (4) 거리이미지를표시, 키가밀렸을때에종료입력이미지를표시하기위한윈도우를 cvnamedwindow() 그리고확보해,cvShowImage() 에입력이미지의포인터를건네주는것으로, 실제의표시를행한다. 거리이미지도마찬가지. 정규화된거리이미지 dst_img_norm( 을 ) 를윈도우를실제로표시해, 무엇인가키가밀릴때까지기다린다. 마지막으로, 종료할경우에는확보한구조체의메모리를해방한다. 실행결과예입력이미지 (8 비트,1 채널,2 값이미지 ) 과 에정규화한거리이미지 [ 좌 ] 의이미지의주위에값 0 의폭 20 의보더를가지는입력이미지. [ 우 ] 입력이미지에대해서거리변환한결과, 이미지의중심이가장거리가멀고, 휘도가높아지고있다. 이미지수복 OpenCV1.0그리고새롭게추가된기능으로, 지정한영역경계근방의화소를이용해지정한영역내의화소치를재구성한다. PhotoShop(Ver.CS2 이후 ) 의스포트수복브러쉬기능과같이, 이미지노이즈를제거하거나이미지로부터불필요한오브젝트를제거하기위해서이용하는것이가능하다. 269

70 샘플 불요오브젝트의제거 cvinpaint 이미지의불필요한문자열부분에대한마스크이미지를지정해문자열을제거한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src_img = 0, *mask_img = 0, *dst_img; // (1) 이미지의읽기 if (argc >= 2) src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img == 0) exit (-1); if (src_img->depth!= IPL_DEPTH_8U) exit (-1); // (2) 마스크이미지의읽기 if (argc >= 3) mask_img = cvloadimage (argv[2], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (mask_img == 0) exit (-1); if (mask_img->nchannels!= 1) exit (-1); if (mask_img->depth!= IPL_DEPTH_8U) exit (-1); // (3) 수복이미지출력용의이미지영역을확보해, 이미지수복을실행 dst_img = cvcloneimage (src_img); cvinpaint (src_img, mask_img, dst_img, CV_INPAINT_NS, 10); // (4) 입력, 마스크, 수복결과이미지의표시 cvnamedwindow ("Source", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Mask", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Inpaint", CV_WINDOW_AUTOSIZE); cvshowimage ("Source", src_img); cvshowimage ("Mask", mask_img); 270

71 cvshowimage ("Inpaint", dst_img); cvwaitkey (0); cvdestroywindow ("Source"); cvdestroywindow ("Mask"); cvdestroywindow ("Inpaint"); cvreleaseimage (&src_img); cvreleaseimage (&mask_img); cvreleaseimage (&dst_img); return 1; // (1) 이미지의읽기커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변갱하지않고읽어들인다. cvinpaint()( 은 ) 는, 입력으로서 8 비트의이미지밖에잡히지않기때문에, 여기서입력이미지의데프스의체크를행한다. // (2) 마스크이미지의읽기커멘드인수로지정된파일명의이미지 ( 마스크이미지 ) 을읽어들인다. 마스크로서는 8 비트, 싱글채널의이미지밖에잡히지않는다의로, 여기서마스크이미지의채널수와데프스의체크를행한다. 마스크이미지의비 0 부분이, 수복대상영역이된다. // (3) 수복이미지출력용의이미지영역을확보해, 이미지수복을실행입력이미지와같은사이즈, 채널수, 데프스의출력용이미지 dst_img( 을 ) 를,cvCloneImage() 그리고확보해, 나비에 stokes 베이스의수법 CV_INPAINT_NS ( 을 ) 를지정하고, 이미지수복을실행한다. // (4) 입력, 마스크, 수복결과이미지의표시윈도우를생성해, 입력, 마스크, 결과를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 [ 좌 ] Inpaint Sample 라고하는불필요한문자가삽입된이미지 [ 중 ] 문자영역을화소치 255 의흰색픽셀로나타내보인마스크이미지 [ 우 ] 이미지수복결과, 예쁘게수복하려면마스크형상을세세하게지정하는편이좋다. 히스토그램의그리기 cvcalchist 271

72 입력이미지의히스토그램을계산해, 결과의그래프를그리기한다 샘플코드 표시의변환 Python 판으로변경 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, bin_w; int hist_size = 256; int sch = 0, ch_width = 260; float max_value = 0; float range_0[] = { 0, 256 ; float *ranges[] = { range_0 ; IplImage *src_img = 0, *dst_img[4] = { 0, 0, 0, 0, *hist_img; CvHistogram *hist; // (1) 이미지를읽어들인다 if (argc < 2 //(src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR))==0) (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYCOLOR)) == 0) return -1; // (2) 입력이미지의채널몇분의이미지영역을확보 sch = src_img->nchannels; for (i = 0; i < sch; i++) { dst_img[i] = cvcreateimage (cvsize (src_img->width, src_img->height), src_img->depth, 1); // (3) 히스토그램구조체, 히스토그램이미지영역을확보 hist = cvcreatehist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist_img = cvcreateimage (cvsize (ch_width * sch, 200), 8, 1); // (4) 입력이미지가멀티채널의경우, 이미지를채널마다분할 if (sch == 1) cvcopy (src_img, dst_img[0], NULL); else cvsplit (src_img, dst_img[0], dst_img[1], dst_img[2], dst_img[3]); // (5) 히스토그램이미지를클리어 272

73 cvset (hist_img, cvscalarall (255), 0); for (i = 0; i < sch; i++) { // (6) 히스토그램을계산하고, 슬캘링 cvcalchist (&dst_img[i], hist, 0, NULL); cvgetminmaxhistvalue (hist, 0, &max_value, 0, 0); cvscale (hist->bins, hist->bins, ((double) hist_img->height) / max_value, 0); // (7) 히스토그램의그리기 bin_w = cvround ((double) ch_width / hist_size); for (j = 0; j < hist_size; j++) cvrectangle (hist_img, cvpoint (j * bin_w + (i * ch_width), hist_img->height), cvpoint ((j + 1) * bin_w + (i * ch_width), hist_img->height - cvround (cvgetreal1d (hist->bins, j))), cvscalarall (0), -1, 8, 0); // (8) 히스토그램이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Image", CV_WINDOW_AUTOSIZE); cvshowimage ("Image", src_img); cvnamedwindow ("Histogram", CV_WINDOW_AUTOSIZE); cvshowimage ("Histogram", hist_img); cvwaitkey (0); cvdestroywindow ("Histogram"); cvreleaseimage (&src_img); cvreleaseimage (&hist_img); for (i = 0; i < sch; i++) { cvreleaseimage (&dst_img[i]); cvreleasehist (&hist); return 0; // (1) 이미지를읽어들인다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다. 2 번째의인수에 CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR( 을 ) 를지정하는것으로, 원이미지의데프스, 채널을변경하지않고읽어들인다. // (2) 입력이미지의채널몇분의이미지영역을확보입력이미지의채널수와같은수의이미지영역을확보해, 그포인터를배열 dst_img[] 에납입한다. 또, 이배열은 0 그리고초기화되고있으므로, 장네루수이상의인덱스를가지는요소의값은 0( 이 ) 가된다. 273

74 // (3) 히스토그램구조체, 히스토그램이미지영역을확보히스토그램구조체 CvHistogram, 히스토그램이미지를그리기한다 IplImage 의영역을확보한다. 이번계산되는것은, 휘도치만의히스토그램이므로, 히스토그램구조체하 1 차원, 빈의수는 256(=hist_size), 빈의반응을일으키는최소의물리량은 0~255(=ranges) 이므로, 입력치중,0~255 의범위에있는값 ( 하나의채널에대해 8bit) 만이, 256 개의빈에누적되어가게된다. 또, 마지막인수를 1( 비 0 의값 ) 으로하는것으로, 등간격인빈을가지는히스토그램이된다. 또,ranges[i][0] v<ranges[i][1] 의범위의값이입력으로서놓치는것에주의한다. // (4) 입력이미지가멀티채널의경우, 이미지를채널마다분할입력이미지가멀티채널의경우에는, 이미지를채널마다분할하고, 의히스토그램을요구한다. 여기서, 분할후의이미지배열 dst_img[]( 을 ) 를최초로 0 그리고초기화하고있으므로, 그대로함수 cvsplit() 에건네주어도좋은것에주의한다. // (5) 히스토그램이미지를클리어다음에, 우선, 히스토그램을그리기한다 IplImage( 을 ) 를, 휘도치 255( 백색 )( 으 ) 로쿠리어한다. 즉, 히스토그램은, 백색배경의이미지가된다. // (6) 히스토그램을계산하고, 슬캘링각채널마다히스토그램을계산한다. 계산후의히스토그램의최대치를꺼내, 그값을이용해히스토그램의높이가이미지내에들어가도록 ( 듯이 ) 슬캘링을실시한다. 여기에서는, 히스토그램의피크 ( 최대치 ) 가, 이미지 hist_img 의높이로동일하고되도록 ( 듯이 ) 조정하고있다. // (7) 히스토그램의그리기결과적으로얻을수있던히스토그램을, 차례차례, 이미지에그리기한다. // (8) 히스토그램이미지를표시, 키가밀렸을때에종료전채널의히스토그램이그리기된이미지포함한윈도우를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예입력이미지 (24 비트,3 채널 ) 과각채널에대한히스토그램 입력이미지 (24 비트,1 채널 ) 과그에대한히스토그램 [ 좌 ] 높은명도를가지도록 ( 듯이 ) 변경한이미지와그히스토그램. 그래프가높은휘도치측에모이고있다. [ 우 ] 낮은명도를가지도록 ( 듯이 ) 변경한이미지와그히스토그램. 그래프가낮은휘도치측에모이고있다. 274

75 [ 좌 ] 높은콘트라스트를가지도록 ( 듯이 ) 변경한이미지와그히스토그램. 최저휘도가 0, 최고휘도가 255 되는넓은분포를가지지만, 콘트라스트를너무강조해서, 특정의범위 ( 빈 ) 의정보가빠져있다. [ 우 ] 낮은콘트라스트를가지도록 ( 듯이 ) 변경한이미지와그히스토그램. 히스토그램이중앙부분에치우쳐, 최저휘도와최고명도와의차이가작아지고있다. 히스토그램간의거리 cvcomparehist 두개의입력이미지의히스토그램의거리를계산해, 그값을표시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <math.h> #include <stdio.h> int main (int argc, char **argv) { char text[16]; int i, hist_size = 256, sch = 0; float range_0[] = { 0, 256 ; float *ranges[] = { range_0 ; double tmp, dist = 0; IplImage *src_img1 = 0, *src_img2 = 0, *dst_img1[4] = { 0, 0, 0, 0, *dst_img2[4] = { 0, 0, 0, 0; CvHistogram *hist1, *hist2; CvFont font; CvSize text_size; // (1)2 매의이미지를읽어들인다. 채널수가동일하지않은경우는, 종료 if (argc >= 3) { 275

76 src_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); src_img2 = cvloadimage (argv[2], CV_LOAD_IMAGE_ANYDEPTH CV_LOAD_IMAGE_ANYCOLOR); if (src_img1 == 0 src_img2 == 0) return -1; if (src_img1->nchannels!= src_img2->nchannels) return -1; // (2) 입력이미지의채널몇분의이미지영역을확보 sch = src_img1->nchannels; for (i = 0; i < sch; i++) { dst_img1[i] = cvcreateimage (cvsize (src_img1->width, src_img1->height), src_img1- >depth, 1); dst_img2[i] = cvcreateimage (cvsize (src_img2->width, src_img2->height), src_img2- >depth, 1); // (3) 히스토그램구조체를확보 hist1 = cvcreatehist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist2 = cvcreatehist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); // (4) 입력이미지가멀티채널의경우, 이미지를채널마다분할 if (sch == 1) { cvcopy (src_img1, dst_img1[0], NULL); cvcopy (src_img2, dst_img2[0], NULL); else { cvsplit (src_img1, dst_img1[0], dst_img1[1], dst_img1[2], dst_img1[3]); cvsplit (src_img2, dst_img2[0], dst_img2[1], dst_img2[2], dst_img2[3]); // (5) 히스토그램을계산, 정규화하고, 거리를요구한다 for (i = 0; i < sch; i++) { cvcalchist (&dst_img1[i], hist1, 0, NULL); cvcalchist (&dst_img2[i], hist2, 0, NULL); cvnormalizehist (hist1, 10000); cvnormalizehist (hist2, 10000); tmp = cvcomparehist (hist1, hist2, CV_COMP_BHATTACHARYYA); dist += tmp * tmp; dist = sqrt (dist); // (6) 요구한거리를문자로서이미지에그리기 snprintf (text, 16, "Distance=%.3f", dist); cvinitfont (&font, CV_FONT_HERSHEY_SIMPLEX, 2.0, 2.0, 0, 4, 8); cvputtext (src_img2, text, cvpoint (10, src_img2->height - 10), &font, cvscalarall 276

77 (0)); cvgettextsize ("Input1", &font, &text_size, 0); cvputtext (src_img1, "Input1", cvpoint (10, text_size.height), &font, cvscalarall (0)); cvputtext (src_img2, "Input2", cvpoint (10, text_size.height), &font, cvscalarall (0)); // (7) 두개의이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Image1", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Image2", CV_WINDOW_AUTOSIZE); cvshowimage ("Image1", src_img1); cvshowimage ("Image2", src_img2); cvwaitkey (0); cvdestroywindow ("Image1"); cvdestroywindow ("Image2"); for (i = 0; i < src_img1->nchannels; i++) { cvreleaseimage (&dst_img1[i]); cvreleaseimage (&dst_img2[i]); cvreleaseimage (&src_img1); cvreleaseimage (&src_img2); cvreleasehist (&hist1); cvreleasehist (&hist2); return 0; // (1)2 매의이미지를읽어들인다 히스토그램이미지의그리기 의경우와같게, 커멘드인수로지정된이미지 ( 을 ) 를, 함수 cvloadimage() 에의해서읽어들인다. 이번은, 히스토그램간의거리 ( 을 ) 를계산하므로, 2 매의이미지를읽어들인다. // (2) 입력이미지의채널몇분의이미지영역을확보채널마다의히스토그램간거리를계산하기위해서, 히스토그램이미지의그리기 의경우와같게, 채널몇분의이미지구조체를확보한다. // (3) 히스토그램구조체를확보히스토그램구조체를확보. // (4) 입력이미지가멀티채널의경우, 이미지를채널마다분할이것도, 히스토그램이미지의그리기 의경우와같게, 각채널을분할한다. // (5) 히스토그램을계산, 정규화하고, 거리를요구한다함수 cvcalchist() 에의해서, 각이미지의히스토그램을계산한후에, 거리를비교하기위해서정규화를실시한다. 어느쪽의히스토그램도, 전빈의값의합계하지만일정 ( 여기에서는,10000)( 이 ) 가되도록 ( 듯이 ) 조정한다. 실제로거리를계산하는함수 cvcomparehist()( 은 ) 는, 이와같이정규화된히파업그램에대해서만올바르게실행되는 277

78 것에주의. 이번은, 히스토그램비교의수법으로서 CV_COMP_BHATTACHARYYA( 을 ) 를지정했다하지만, 그밖에도 CV_COMP_CORREL,CV_COMP_CHISQR,CV_COMP_INTERSECT 등이손가락정가능하다. 자세한것은레퍼런스를참조하는것. 또, 이프로그램에서는, 멀티채널이미지의경우의최종적인거리와하고, 채널간의거리의자승을의화의평방근을요구하도록 ( 듯이 ) 하고있다. 다만, 이것이유일한거리의정의라고하는것으로않는다. // (6) 요구한거리를문자로서이미지에그리기요구한거리를표시하기위해서, 함수 cvputtext()( 을 ) 를이용하고, 값을테키스트로서 2 매목의이미지에덧쓰기한다. 또,1 매목,2 매목의이미지에,"Input1","Input2" 의문자를그린다. // (7) 두개의이미지를표시, 키가밀렸을때에종료 2 매의이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 종료시에는, 메모리를해방한다. 실행결과예입력이미지 (Input1, Input2) 의히스토그램간의거리 Input 이차원의히스토그램 cvcreatehist 입력이미지의색공간 (color space) 를 HSV( 으 ) 로변환해,H-S 의히스토그램을그리기한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { IplImage *src, *hsv; IplImage *h_plane, *s_plane, *v_plane; IplImage *planes[2]; int h_bins = 30, s_bins = 32; int hist_size[] = { h_bins, s_bins ; float h_ranges[] = { 0, 181 ; /* 색상은 0(0 도, 빨강 ) 으로부터 180(360 도, 빨강 ) 까지변화한다 */ 278

79 float s_ranges[] = { 0, 256 ; /* 채도는 0( 흑-그레이-흰색 ) 으로부터 255( 순수한스펙트럼칼라 ) 까지변화한다 */ float *ranges[] = { h_ranges, s_ranges ; int scale = 10; IplImage *hist_img = cvcreateimage (cvsize (h_bins * scale, s_bins * scale), 8, 3); CvHistogram *hist; float max_value = 0; int h, s; if (argc!= 2 (src = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; hsv = cvcreateimage (cvgetsize (src), 8, 3); planes[0] = h_plane = cvcreateimage (cvgetsize (src), 8, 1); planes[1] = s_plane = cvcreateimage (cvgetsize (src), 8, 1); v_plane = cvcreateimage (cvgetsize (src), 8, 1); // (1) 입력이미지의색공간 (color space) 를 RGB( 으 ) 로부터 HSV( 으 ) 로변환한다 cvcvtcolor (src, hsv, CV_BGR2HSV); cvcvtpixtoplane (hsv, h_plane, s_plane, v_plane, 0); // (2) 히스토그램을계산 hist = cvcreatehist (2, hist_size, CV_HIST_ARRAY, ranges, 1); cvcalchist (planes, hist, 0, 0); // (3) 각빈에있어서의값을요구해그것을바탕으로휘도치를산출해그리기 cvgetminmaxhistvalue (hist, 0, &max_value, 0, 0); cvzero (hist_img); for (h = 0; h < h_bins; h++) { for (s = 0; s < s_bins; s++) { float bin_val = cvqueryhistvalue_2d (hist, h, s); int intensity = cvround (bin_val * 255 / max_value); cvrectangle (hist_img, cvpoint (h * scale, s * scale), cvpoint ((h + 1) * scale - 1, (s + 1) * scale - 1), CV_RGB (intensity, intensity, intensity), CV_FILLED); // (4) 입력이미지와이차원 (H-S) 히스토그램을표시하고, 무엇인가키가밀릴때까지기다린다 cvnamedwindow ("Source", 1); cvshowimage ("Source", src); cvnamedwindow ("H-S Histogram", 1); cvshowimage ("H-S Histogram", hist_img); cvwaitkey (0); cvdestroywindow ("Source"); cvdestroywindow ("H-S Histogram"); 279

80 cvreleaseimage (&src); cvreleaseimage (&hsv); cvreleaseimage (&h_plane); cvreleaseimage (&s_plane); cvreleaseimage (&v_plane); cvreleaseimage (&hist_img); cvreleasehist (&hist); return 0; // (1) 입력이미지의색공간 (color space) 를 RGB( 으 ) 로부터 HSV( 으 ) 로변환한다함수 cvcvtcolor()( 을 ) 를이용하고, 입력된칼라이미지의색공간 (color space) 를 RGB 공간으로부터 HSV 공간으로변환한다. 실제로는,cvLoadImage() 그리고읽힌이미지는,BGR 의차례로줄지어있으므로, CV_BGR2HSV( 을 ) 를지정해변환한다. 다음에,cvCvtPixToPlane()( 을 ) 를이용하고,H,S,V 의각채널 ( 색평면 ) 로분해한다. 이,cvCvtPixToPlane()( 은 ) 는,cvcompat.h 그리고 #define cvcvtpixtoplane cvsplit ( 이 ) 라고정의되고있어 cvsplit()( 와 ) 과등가이다. // (2) 히스토그램을계산이번은,2 차원의히스토그램구조체를작성해, 함수 cvcalchist() 그럼, 둘의색평면 (H,S) 에있어서의히스토그램을계산한다. // (3) 각빈에있어서의값을요구해그것을바탕으로휘도치를산출해그리기함수 cvqueryhistvalue_2d() 에의해서, 지정했다 H-S 에있어서의빈의값을요구해그값을이용하고, 최대치가 255, 최소치가 0 되어에휘도치를산출한다. 그리고, 그휘도치로전부칠해진구형을순서대로, 이미지 hist_img 에쓴다. // (4) 입력이미지와이차원 (H-S) 히스토그램을표시하고, 무엇인가키가밀릴때까지기다린다마지막으로, 입력이미지와그리기되었다 2 차원 (H-S) 히스토그램이미지를표시한다. 횡축이 H( 색상 ), 세로축이 S( 채도 )( 을 ) 를나타낸다. 실행결과예 280

81 백프로젝션패치 cvcalcbackprojectpatch 히스토그램간거리에의한, 이미지내의템플릿탐색 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, hist_size = 90; float h_ranges[] = { 0, 181 ; float *ranges[] = { h_ranges ; double min_val, max_val; CvSize dst_size; CvPoint min_loc, max_loc; IplImage *src_img, *tmp_img, *dst_img; IplImage *src_hsv, *tmp_hsv; IplImage **src_planes, *tmp_planes[3]; CvHistogram *hist = 0; if (argc!= 3 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0 (tmp_img = cvloadimage (argv[2], CV_LOAD_IMAGE_COLOR)) == 0) return -1; src_planes = (IplImage **) cvalloc (sizeof (IplImage *) * 3); for (i = 0; i < 3; i++) { src_planes[i] = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); tmp_planes[i] = cvcreateimage (cvgetsize (tmp_img), IPL_DEPTH_8U, 1); // (1)2 개입력이미지 ( 탐색이미지, 템플릿이미지 ) 의색공간 (color space) 를,RGB( 으 ) 로부터 HSV 에변환 281

82 src_hsv = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 3); tmp_hsv = cvcreateimage (cvgetsize (tmp_img), IPL_DEPTH_8U, 3); cvcvtcolor (src_img, src_hsv, CV_BGR2HSV); cvcvtcolor (tmp_img, tmp_hsv, CV_BGR2HSV); cvcvtpixtoplane (src_hsv, src_planes[0], src_planes[1], src_planes[2], 0); cvcvtpixtoplane (tmp_hsv, tmp_planes[0], tmp_planes[1], tmp_planes[2], 0); // (2) 템플릿이미지의히스토그램을계산 hist = cvcreatehist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); cvcalchist (&tmp_planes[0], hist, 0, 0); // (3) 탐색이미지전체에대해서, 템플릿의히스토그램과의거리 ( 수법으로의존 ) 를계산 dst_size = cvsize (src_img->width - tmp_img->width + 1, src_img->height - tmp_img- >height + 1); dst_img = cvcreateimage (dst_size, IPL_DEPTH_32F, 1); cvcalcbackprojectpatch (src_planes, dst_img, cvgetsize (tmp_img), hist, CV_COMP_CORREL, 1.0); cvminmaxloc (dst_img, &min_val, &max_val, &min_loc, &max_loc, NULL); // (4) 템플릿에대응하는위치에구형을그리기 cvrectangle (src_img, max_loc, cvpoint (max_loc.x + tmp_img->width, max_loc.y + tmp_img->height), CV_RGB (255, 0, 0), 3); cvnamedwindow ("Image", 1); cvshowimage ("Image", src_img); cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleaseimage (&tmp_img); cvreleaseimage (&src_hsv); cvreleaseimage (&tmp_hsv); for (i = 0; i < 3; i++) { cvreleaseimage (&src_planes[i]); cvreleaseimage (&tmp_planes[i]); cvfree (&src_planes); return 0; // (1)2 개입력이미지 ( 탐색이미지, 템플릿이미지 ) 의색공간 (color space) 를,RGB( 으 ) 로부터 HSV 에변환이번은,HSV 공간에있어서의 H( 색상 ) 의히스토그램을비교하므로, 입력이미지의색공간 (color space) 를 RGB( 으 ) 로부터 HSV( 으 ) 로변환한다. 비교하는히스토그램은, 하나의채널 ( 색평면 ) 에한정하지않고와도좋다. 282

83 // (2) 템플릿이미지의히스토그램을계산최초로, 템플릿이상의히스토그램을미리계산해둔다. 함수 cvcalchist() 에대해서는, 전술의샘플코드를참조하는것. // (3) 탐색이미지전체에대해서, 템플릿의히스토그램과의거리 ( 수법으로의존 ) 를계산함수 cvcalcbackprojectpatch() 에의해서출력되는이미지는,32bit 부동소수점, 1 채널의이미지며, 그사이즈는, 탐색이미지 ( 의폭, 높이 )- 템플릿이미지 ( 의폭높이 )+ 1 된다. 이이미지영역 dst_img( 을 ) 를확보하고, 함수 cvcalcbackprojectpatch()( 을 ) 를실행한다. 이번은, 히스토그램비교의수법으로서 CV_COMP_CORREL( 상관 )( 을 ) 를지정해있다. 결과이미지중에있고최대치를가지는위치, 즉템플릿의히스토그램과가장가까운히스토그램을가지는위치, 를템플릿에대응하는위치로한다. 이미지중의값의최대, 최소치, 및그위치는, 함수 cvminmaxloc() 에의해서꺼낸다. // (4) 템플릿에대응하는위치에구형을그리기전술의처리에의해서꺼내진위치 ( 좌표 ) 는, 템플릿의좌상의좌표 ( 을 ) 를나타내므로, 거기에텐프레이트사이즈의구형을그리기한다. 마지막으로, 그이미지를표시하고, 무엇인가키가밀릴때까지기다린다. 실행결과예이하의예에서는, 탐색이미지의일부를자른것을템플릿이미지로서급 ( 이 ) 라고있다 ( 탐색이미지에직접 ROI( 을 ) 를설정해도같은일이가능하다 ). 따라서, 매칭결과의피크치는 1.0( 이 ) 가되지만, 실제로는이러한완전하게템플릿과영역이입력이미지내에존재하는것은드물다. 매칭위치의피크는 1.0( 을 ) 를밑돌아, 또, 피크자체가복수존재하는일도생각할수있다. 입력이미지템플릿이미지결과이미지 히스토그램의균일화 cvequalizehist 그레이스케일이미지의히스토그램을균일화한다 표시의변환 샘플코드 283

84 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, bin_w, ch_width = 260; int hist_size = 256; float max_value = 0; float range_0[] = { 0, 256 ; float *ranges[] = { range_0 ; IplImage *src_img[2], *hist_img[2]; CvHistogram *hist; if (argc!= 2 (src_img[0] = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; hist = cvcreatehist (1, &hist_size, CV_HIST_ARRAY, ranges, 1); hist_img[0] = cvcreateimage (cvsize (ch_width, 200), 8, 1); hist_img[1] = cvcreateimage (cvsize (ch_width, 200), 8, 1); src_img[1] = cvcreateimage (cvgetsize (src_img[0]), 8, 1); // (1) 히스토그램을균일화한다 cvequalizehist (src_img[0], src_img[1]); for (i = 0; i < 2; i++) { cvset (hist_img[i], cvscalarall (255), 0); // (2) 히스토그램을계산해, 슬캘링 cvcalchist (&src_img[i], hist, 0, NULL); cvgetminmaxhistvalue (hist, 0, &max_value, 0, 0); cvscale (hist->bins, hist->bins, ((double) hist_img[i]->height) / max_value, 0); bin_w = cvround ((double) ch_width / hist_size); // (3) 히스토그램을그리기 for (j = 0; j < hist_size; j++) cvrectangle (hist_img[i], cvpoint (j * bin_w, hist_img[i]->height), cvpoint ((j + 1) * bin_w, hist_img[i]->height - cvround (cvgetreal1d (hist->bins, j))), cvscalarall (0), -1, 8, 0); // (4) 균일화전후의이미지와그에대한히스토그램을표시 cvnamedwindow ("Image1", CV_WINDOW_AUTOSIZE); cvshowimage ("Image1", src_img[0]); cvnamedwindow ("Histogram1", CV_WINDOW_AUTOSIZE); 284

85 cvshowimage ("Histogram1", hist_img[0]); cvnamedwindow ("Image2", CV_WINDOW_AUTOSIZE); cvshowimage ("Image2", src_img[1]); cvnamedwindow ("Histogram2", CV_WINDOW_AUTOSIZE); cvshowimage ("Histogram2", hist_img[1]); cvwaitkey (0); cvdestroywindow ("Image1"); cvdestroywindow ("Image2"); cvdestroywindow ("Histogram1"); cvdestroywindow ("Histogram2"); cvreleaseimage (&src_img[0]); cvreleaseimage (&src_img[1]); cvreleaseimage (&hist_img[0]); cvreleaseimage (&hist_img[1]); cvreleasehist (&hist); return 0; // (1) 히스토그램을균일화한다함수 cvequalizehist() 에의해, 입력이미지를, 그히스토그램이균일하게분포하는이미지로변환한다. 이변환에의해, 이미지의콘트라스트가오른다. 그러나, 변환후의이미지의휘도치는, 변환전의이미지의히스토그램을정규화한값의부분적인총화이므로, 변환후는히스토그램에누락이생긴다. // (2) 히스토그램을계산해, 슬캘링재차, 히스토그램균일화전후의이미지에대해히스토그램을계산해, 스케이링을베푼다. // (3) 히스토그램을그리기계산한히스토그램을그리기한다. // (4) 균일화전후의이미지와그에대한히스토그램을표시실제로, 균일화전후의이미지와그에대한히스토그램을표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 템플릿매칭 cvmatchtemplate 탐색이미지로부터템플릿의위치를찾는다 285

86 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { double min_val, max_val; CvPoint min_loc, max_loc; CvSize dst_size; IplImage *src_img, *tmp_img, *dst_img; if (argc!= 3 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0 (tmp_img = cvloadimage (argv[2], CV_LOAD_IMAGE_COLOR)) == 0) return -1; // (1) 탐색이미지전체에대해서, 템플릿의매칭치 ( 지정한수법으로의존 ) 를계산 dst_size = cvsize (src_img->width - tmp_img->width + 1, src_img->height - tmp_img- >height + 1); dst_img = cvcreateimage (dst_size, IPL_DEPTH_32F, 1); cvmatchtemplate (src_img, tmp_img, dst_img, CV_TM_CCOEFF_NORMED); cvminmaxloc (dst_img, &min_val, &max_val, &min_loc, &max_loc, NULL); // (2) 템플릿에대응하는위치에구형을그리기 cvrectangle (src_img, max_loc, cvpoint (max_loc.x + tmp_img->width, max_loc.y + tmp_img->height), CV_RGB (255, 0, 0), 3); cvnamedwindow ("Image", 1); cvshowimage ("Image", src_img); cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&src_img); cvreleaseimage (&tmp_img); cvreleaseimage (&dst_img); return 0; // (1) 탐색이미지전체에대해서, 템플릿의매칭치 ( 지정한수법으로의존 ) 를계산읽힌탐색이미지와템플릿이미지를이용하고, 템플릿매칭을실시한다. 매칭에이용하는상관함수에는,CV_TM_SQDIFF,CV_TM_SQDIFF_NORMED, CV_TM_CCORR,CV_TM_CCORR_NORMED,CV_TM_CCOEFF,CV_TM_CCOEFF_NORMED, 하지만 286

87 지정가능하다. 이번은,CV_TM_CCOEFF_NORMED( 을 ) 를지정해있다. 이것은, 각비교영역의휘도치의평균이동일하면가정했을경우의상관연산식의근사이다. 자세한것은, 레퍼런스를참조하는것. 매칭은, 함수 cvcalcbackprojectionpatch()( 와 ) 과닮아있지만, 미리히스토그램을계산하는사전처리는필요하지않다. 이미지중의값의최대, 최소치, 및그위치는, 함수 cvminmaxloc() 에의해서꺼낸다. CV_TM_CCOEFF_NORMED( 을 ) 를지정했을경우에는, 최대치의좌표가매칭위치가된다. 또,CV_TM_SQDIFF(SSD( 이 ) 라고도불린다 ),CV_TM_SQDIFF_NORMED( 을 ) 를지정한장소합에는, 최소치의좌표가매칭위치가된다. // (2) 템플릿에대응하는위치에구형을그리기탐색이미지중이구할수있던좌표위치에구형을그리기한다. 그이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예이하의예에서는, 탐색이미지의일부를자른것을템플릿이미지로서급 ( 이 ) 라고있다 ( 탐색이미지에직접 ROI( 을 ) 를설정해도같은일이가능하다 ) 의로, 완전하게올바른결과가얻어지고있다. 따라서, 탐색영역과템플릿이미지로이미지가변화하는경우는, 항상올바른위치에매칭한다고는할수없다. 입력이미지템플릿이미지결과이미지 형상매칭두개의형상 ( 윤곽데이터, 혹은그레이스케일이미지 ) 을입력으로한다. OpenCV의함수cvMatchShapes() 그럼, 형상의비교에Hu모멘트를이용하기위해, 회전, 슬캘링, 반전에대해서불변이다. 샘플 형상의매칭 cvmatchshapes 두개의입력이미지에대해서,3 종류의수법으로형상비교를실시한다 표시의변환 샘플코드 287

88 #include <cv.h> #include <highgui.h> #include <stdio.h> int main (int argc, char **argv) { char text[16]; int i; double result[3]; CvFont font; IplImage *src_img1, *src_img2; IplImage *dst_img[3]; if (argc!= 3 (src_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 (src_img2 = cvloadimage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; for (i = 0; i < 3; i++) { dst_img[i] = (IplImage *) cvclone (src_img2); // (1)3 종류의수법으로형상을비교 result[0] = cvmatchshapes (src_img1, src_img2, CV_CONTOURS_MATCH_I1, 0); result[1] = cvmatchshapes (src_img1, src_img2, CV_CONTOURS_MATCH_I2, 0); result[2] = cvmatchshapes (src_img1, src_img2, CV_CONTOURS_MATCH_I3, 0); // (2) 형상매칭의결과를이미지에그리기 for (i = 0; i < 3; i++) { snprintf (text, 16, "%.5f", result[i]); cvinitfont (&font, CV_FONT_HERSHEY_SIMPLEX, 1.0, 1.0, 0, 4, 8); cvputtext (dst_img[i], text, cvpoint (10, dst_img[i]->height - 10), &font, cvscalarall (0)); // (3) 입력이미지 1( 와 ) 과 3 종류의값이써진입력이미지 2( 을 ) 를표시해, 무엇인가키가밀릴때까지기다린다 cvnamedwindow ("Image1", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Image2a", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Image2b", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Image2c", CV_WINDOW_AUTOSIZE); cvshowimage ("Image1", src_img1); cvshowimage ("Image2a", dst_img[0]); cvshowimage ("Image2b", dst_img[1]); 288

89 cvshowimage ("Image2c", dst_img[2]); cvwaitkey (0); cvdestroywindow ("Image1"); cvdestroywindow ("Image2a"); cvdestroywindow ("Image2b"); cvdestroywindow ("Image2c"); cvreleaseimage (&src_img1); cvreleaseimage (&src_img2); cvreleaseimage (&dst_img[0]); cvreleaseimage (&dst_img[1]); cvreleaseimage (&dst_img[2]); return 0; // (1)3 종류의수법으로형상을비교함수 cvmatchshapes() 에의해서, 두개의입력이미지에대해서형상매칭을실시한다. 여기서,3 번째의인수가비교수법을나타낸다. 또, 4 번째의인수는, 현재로서는이용되어있지않다. // (2) 형상매칭의결과를이미지에그리기비교결과를, 이미지에문자열로서그리기한다. 작은값인만큼두개의이미지의형상이가깝다 (0 의경우는같은형상 ) 이라고하는것이된다. // (3) 입력이미지 1( 와 ) 과 3 종류의값이써진입력이미지 2( 을 ) 를표시결과의이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 입력이미지비교이미지 1 비교이미지 2 비교이미지 3 비교이미지 4 CV_CONTOURS_MATCH_I1 CV_CONTOURS_MATCH_I2 CV_CONTOURS_MATCH_I3 289

90 입력이미지비교이미지 1 비교이미지 2 비교이미지 3 비교이미지 4 CV_CONTOURS_MATCH_I1 CV_CONTOURS_MATCH_I2 CV_CONTOURS_MATCH_I3 점렬을포함하는구형 cvboundingrect 점렬을포함하는구형을요구한다 샘플코드 표시의변환 Python 판으로변경 #include <cv.h> #include <highgui.h> #include <time.h> int main (int argc, char **argv) { int i; IplImage *img = 0; CvMemStorage *storage = cvcreatememstorage (0); CvSeq *points; CvRNG rng = cvrng (time (NULL)); CvPoint pt; CvRect rect; // (1) 이미지를확보해초기화한다 img = cvcreateimage (cvsize (640, 480), IPL_DEPTH_8U, 3); cvzero (img); // (2) 점렬을생성한다 points = cvcreateseq (CV_SEQ_ELTYPE_POINT, sizeof (CvSeq), sizeof (CvPoint), storage); for (i = 0; i < 20; i++) { 290

91 pt.x = cvrandint (&rng) % (img->width / 2) + img->width / 4; pt.y = cvrandint (&rng) % (img->height / 2) + img->height / 4; cvseqpush (points, &pt); cvcircle (img, pt, 3, CV_RGB (0, 255, 0), CV_FILLED); // (3) 점렬을포함하는구형을요구해그리기한다 rect = cvboundingrect (points, 0); cvrectangle (img, cvpoint (rect.x, rect.y), cvpoint (rect.x + rect.width, rect.y + rect.height), CV_RGB (255, 0, 0), 2); // (4) 이미지의표시, 키가밀렸을때에종료 cvnamedwindow ("BoundingRect", CV_WINDOW_AUTOSIZE); cvshowimage ("BoundingRect", img); cvwaitkey (0); cvdestroywindow ("BoundingRect"); cvreleaseimage (&img); cvreleasememstorage (&storage); return 0; // (1) 이미지를확보해초기화한다이샘플에서는, 외부이미지를읽어들이지않고, 프로그램내부에서이미지를준비한다. 또, 작성된칼라이미지를, 흑 (0)( 으 ) 로전부칠해초기화한다. // (2) 점렬을생성한다함수 cvrandint()( 을 ) 를이용하고, 이미지내가있는범위에들어가는랜덤인점렬을생성한다. 또, 개개의점을중심으로하는작은엔을이미지에그리기해, 점의위치를나타낸다. 이샘플에서는, 랜덤인점렬을이용하고있지만, 물론사전에검출했다윤곽 ( 를구성하는점렬 ) 에대해서도, 처리를실시할수있다. // (3) 점렬을포함하는구형을요구해그리기한다함수 cvboundingrect()( 을 ) 를이용하고, 점렬을포함하는최소의구형 ( 다만기울지않았다 ) 을요구한다. 함수 cvminarearect2()( 와 ) 과의차이는, 이기울기를고려하는지아닌지이다. cvboundingrect() 하 cvrect( 을 ) 를돌려주지만,cvMinAreaRect2() 하 cvbox2d( 을 ) 를돌려준다. 구할수있었다 cvrect( 을 ) 를이미지에그리기한다. // (4) 이미지의표시, 키가밀렸을때에종료결과의이미지를표시해, 무엇인가키가밀릴때까지기다린다. 291

92 실행결과예 윤곽영역의면적과윤곽의길이 cvcontourarea, cvarclength 윤곽에의해서단락지어진영역의면적과윤곽의길이를요구한다 샘플코드 표시의변환 Python 판으로변경 #include <cv.h> #include <highgui.h> #include <time.h> #include <math.h> #include <stdio.h> int main (int argc, char **argv) { int i, size = 500; char text[2][64]; double scale, area, length; IplImage *img = 0; CvMemStorage *storage = cvcreatememstorage (0); CvSeq *points; CvRNG rng = cvrng (time (NULL)); CvPoint pt0, pt1; CvRect rect; CvFont font; // (1) 이미지를확보해초기화한다 img = cvcreateimage (cvsize (size, size), IPL_DEPTH_8U, 3); cvzero (img); // (2) 점렬을생성한다 points = cvcreateseq (CV_SEQ_POLYLINE, sizeof (CvSeq), sizeof (CvPoint), storage); scale = cvrandreal (&rng) + 0.5; pt0.x = int (cos (0) * size / 4 * scale + size / 2); pt0.y = int (sin (0) * size / 4 * scale + size / 2); cvcircle (img, pt0, 2, CV_RGB (0, 255, 0)); cvseqpush (points, &pt0); 292

93 for (i = 1; i < 20; i++) { scale = cvrandreal (&rng) + 0.5; pt1.x = int (cos (i * 2 * CV_PI / 20) * size / 4 * scale + size / 2); pt1.y = int (sin (i * 2 * CV_PI / 20) * size / 4 * scale + size / 2); cvline (img, pt0, pt1, CV_RGB (0, 255, 0), 2); pt0.x = pt1.x; pt0.y = pt1.y; cvcircle (img, pt0, 3, CV_RGB (0, 255, 0), CV_FILLED); cvseqpush (points, &pt0); cvline (img, pt0, *CV_GET_SEQ_ELEM (CvPoint, points, 0), CV_RGB (0, 255, 0), 2); // (3) 포함구형, 면적, 길이를요구한다 rect = cvboundingrect (points, 0); area = cvcontourarea (points); length = cvarclength (points, CV_WHOLE_SEQ, 1); // (4) 결과를이미지에쓴다 cvrectangle (img, cvpoint (rect.x, rect.y), cvpoint (rect.x + rect.width, rect.y + rect.height), CV_RGB (255, 0, 0), 2); snprintf (text[0], 64, "Area: wrect=%d, contour=%d", rect.width * rect.height, int (area)); snprintf (text[1], 64, "Length: rect=%d, contour=%d", 2 * (rect.width + rect.height), int (length)); cvinitfont (&font, CV_FONT_HERSHEY_SIMPLEX, 0.7, 0.7, 0, 1, CV_AA); cvputtext (img, text[0], cvpoint (10, img->height - 30), &font, cvscalarall (255)); cvputtext (img, text[1], cvpoint (10, img->height - 10), &font, cvscalarall (255)); // (5) 이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("BoundingRect", CV_WINDOW_AUTOSIZE); cvshowimage ("BoundingRect", img); cvwaitkey (0); cvdestroywindow ("BoundingRect"); cvreleaseimage (&img); cvreleasememstorage (&storage); return 0; // (1) 이미지를확보해초기화한다이샘플에서는, 외부이미지를읽어들이지않고, 프로그램내부에서이미지를준비한다. 또, 작성된칼라이미지를, 흑 (0)( 으 ) 로전부칠해초기화한다. 293

94 // (2) 점렬을생성한다함수 cvrandint()( 을 ) 를이용하고, 이미지내가있는범위에들어가는랜덤인점렬을생성한다. 다만, 면적을요구할때에알기쉽게자기교차하지않게한다. 또, 개개의점을중심으로하는작은엔을이미지에그리기해, 점의위치를나타낸다. 게다가닫은영역이되듯이더욱점렬끼리를묶는선분을그리기한다. 이샘플에서는, 랜덤인점렬을이용하고있지만, 물론사전에검출했다윤곽 ( 를구성하는점렬 ) 등에대해도, 처리를실시할수있다. // (3) 포함구형, 면적, 길이를요구한다전술의샘플과같게, 함수 cvboundingrect()( 을 ) 를이용해점렬을포함하는구형영역을요구한다. 게다가함수 cvcontourarea() 에의해서점렬이구성하는윤곽의면적을요구해함수 cvarclength() 에는그길이 ( 주위장 ) 를요구한다. cvcontourarea() 의 2 번째의인수는, 윤곽의어느부분의면적을계산하는지를나타내고있어이인수가지정되지않는경우에는디폴트인수이다 CV_WHOLE_SEQ 하지만이용된다. 이경우는, 윤곽이나타내는영역모든면적이요구된다. cvarclength() 의 2 번째의인수의의미도마찬가지여,3 번째의인수에 0 보다큰값을지정하는것으로, 윤곽을폐곡선으로서취급한다. 그외의값에대해서는, 레퍼런스를참조하는것. // (4) 결과를이미지에쓴다포함구형, 포함구형의면적과길이, 윤곽의면적과길이, 를이미지에쓴다. // (5) 이미지를표시, 키가밀렸을때에종료결과의이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 배경과주목물체의분리 OpenCV에는, 배경차분을계산할때에편리한배경통계량의누적에관한함수가실장되고있다. 이미지중으로부터, 변화가없는배경영역과그이외의영역을분리하는것은, 컴퓨터비전시스템에대해많이사용되는기술이며, 여러가지수법이제안되고있다. OpenCV냄새나도,cvaux그리고2종류의동적배경차분수법이실장되고있다. 여기에서는, 참고문헌 [1] 그리고이용되고있는배경이미지의시간적인변화를고려한동적배경갱신에의한로바스트인주목물체의검출수법을,OpenCV의함수를이용해실장했다. 이수법에서는, 배경영역화소의휘도 ( 을 ) 를이하와같이모델화하고있다. sin ( 은 ) 는휘도치의시간평균, ( 은 ) 는휘도의진폭, ( 은 ) 는휘도의주파수, ( 은 ) 는시간, ( 은 ) 는계수, ( 은 ) 는카메라에만의존하는노이즈의최대치를각각나타낸다. 이모델에대하고, 하지만, 의경우에, 그화 294

95 소는배경영역에존재하는화소이다고판단한다. 배경으로판정된영역에서는휘도평균치 ( 와 ) 과진폭 ( 을 ) 를이하의식을이용해갱신한다. 여기서, ( 은 ) 는갱신속도파라미터이다. 한편, 물체영역으로판정된영역에서는, 휘도평균치는원래의값을보관유지해, 진폭 해갱신한다. 만을이하의식을이용 여기서, ( 은 ) 는물체영역갱신속도파라미터이다. [1] 모리타신지, 산택일성, 테라사와마사히코, 요코야곧화 : " 전방위이미지센서를이용한네트워크대응형원격감시시스템 ", 전자정보통신학회논문잡지 (D-II), Vol. J88-D-II, No. 5, pp , (2005.5). 샘플 동적배경갱신에의한물체검출 cvacc, cvrunningavg 배경이미지의시간적인명도변화를고려한물체검출. 프로그램개시보다, 윈도우가표시될때까지는배경통계량을초기화하고있기때문에카메라를작동시키지않게주의한다. 샘플내의초기화프레임수나갱신파라미터는테스트로사용카메라에관해서튜닝되고있다. 또,OpenCV 함수의사용예제시를우선했기때문에, 처리효율은약간낮다. 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ctype.h> #include <stdio.h> int main (int argc, char **argv) { 295

96 int i, c, counter; int INIT_TIME = 100; int w = 0, h = 0; double B_PARAM = 1.0 / 50.0; double T_PARAM = 1.0 / 200.0; double Zeta = 10.0; CvCapture *capture = 0; IplImage *frame = 0; IplImage *av_img, *sgm_img; IplImage *lower_img, *upper_img, *tmp_img; IplImage *dst_img, *msk_img; CvFont font; char str[64]; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다 if (argc == 1 (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvcreatecameracapture (argc == 2? argv[1][0] - '0' : 0); // (2)1 프레임캡쳐해, 캐프체사이즈를취득한다. frame = cvqueryframe (capture); w = frame->width; h = frame->height; // (3) 작업용의영역을생성한다 av_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_32F, 3); sgm_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_32F, 3); tmp_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_32F, 3); lower_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_32F, 3); upper_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_32F, 3); dst_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_8U, 3); msk_img = cvcreateimage (cvsize (w, h), IPL_DEPTH_8U, 1); // (4) 배경의휘도평균의초기치를계산한다 printf ("Background statistics initialization start\n"); cvsetzero (av_img); for (i = 0; i < INIT_TIME; i++) { frame = cvqueryframe (capture); cvacc (frame, av_img); cvconvertscale (av_img, av_img, 1.0 / INIT_TIME); // (5) 배경의휘도진폭의초기치를계산한다 cvsetzero (sgm_img); 296

97 for (i = 0; i < INIT_TIME; i++) { frame = cvqueryframe (capture); cvconvert (frame, tmp_img); cvsub (tmp_img, av_img, tmp_img); cvpow (tmp_img, tmp_img, 2.0); cvconvertscale (tmp_img, tmp_img, 2.0); cvpow (tmp_img, tmp_img, 0.5); cvacc (tmp_img, sgm_img); cvconvertscale (sgm_img, sgm_img, 1.0 / INIT_TIME); printf ("Background statistics initialization finish\n"); // (6) 표시용윈도우를생성한다 cvinitfont (&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7); cvnamedwindow ("Input", CV_WINDOW_AUTOSIZE); cvnamedwindow ("Substraction", CV_WINDOW_AUTOSIZE); // (7) 취득이미지로부터배경을분리하는루프 counter = 0; while (1) { frame = cvqueryframe (capture); cvconvert (frame, tmp_img); // (8) 배경이될수있는화소의휘도치의범위를체크한다 cvsub (av_img, sgm_img, lower_img); cvsubs (lower_img, cvscalarall (Zeta), lower_img); cvadd (av_img, sgm_img, upper_img); cvadds (upper_img, cvscalarall (Zeta), upper_img); cvinrange (tmp_img, lower_img, upper_img, msk_img); // (9) 휘도진폭을재계산한다 cvsub (tmp_img, av_img, tmp_img); cvpow (tmp_img, tmp_img, 2.0); cvconvertscale (tmp_img, tmp_img, 2.0); cvpow (tmp_img, tmp_img, 0.5); // (10) 배경이라고판단된영역의배경의휘도평균과휘도진폭을갱신한다 cvrunningavg (frame, av_img, B_PARAM, msk_img); cvrunningavg (tmp_img, sgm_img, B_PARAM, msk_img); // (11) 물체영역이라고판단된영역에서는휘도진폭만을 ( 배경영역보다늦은속도로 ) 갱신한다 cvnot (msk_img, msk_img); cvrunningavg (tmp_img, sgm_img, T_PARAM, msk_img); 297

98 // (12) 물체영역만을출력이미지에카피한다 ( 배경영역은흑 ) cvsetzero (dst_img); cvcopy (frame, dst_img, msk_img); // (13) 처리결과를표시한다 snprintf (str, 64, "%03d[frame]", counter); cvputtext (dst_img, str, cvpoint (10, 20), &font, CV_RGB (0, 255, 100)); cvshowimage ("Input", frame); cvshowimage ("Substraction", dst_img); counter++; c = cvwaitkey (10); if (c == '\x1b') break; cvdestroywindow ("Input"); cvdestroywindow ("Substraction"); cvreleaseimage (&frame); cvreleaseimage (&dst_img); cvreleaseimage (&av_img); cvreleaseimage (&sgm_img); cvreleaseimage (&lower_img); cvreleaseimage (&upper_img); cvreleaseimage (&tmp_img); cvreleaseimage (&msk_img); return 0; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다함수 cvcreatecameracapture()( 을 ) 를이용하고, 커멘드인수로서주어진번호의카메라에대한캡쳐구조체 CvCapture( 을 ) 를작성한다. 인수가지정되지않는경우는, 디폴트의값이다 "0" 하지만이용된다. // (2)1 프레임캡쳐해, 캐프체사이즈를취득한다. 함수 cvqueryframe() ( 을 ) 를호출해, 실제로 1 프레임캡쳐를실시해, 취득한이미지로부터, 이미지의폭 w( 와 ) 과높이 h( 을 ) 를취득한다. // (3) 작업용의영역을생성한다휘도평균이나휘도진폭계산을위한작업영역을확보한다. 누적계산이나누승계산을행하기위해, 데프스는 32 비트를지정해둔다. 마스크이미지는 8 비트, 싱글채널을지정한다. // (4) 배경의휘도평균의초기치를계산한다배경의모델화를위해, 지정된프레임수 (INIT_TIME) 사이의각화소치의평균을요구한다. cvacc() 함수를이용하고, 이미지의누적치를계산한다. 298

99 // (5) 배경의휘도진폭의초기치를계산한다 (4) 그리고계산된배경이미지의평균치를이용하고, 휘도진폭의초기치를계산한다. 휘도진폭 하프레임수 (INIT_TIME) 사이의평균을요구한다. ( 으 ) 로서계산한다. 휘도진폭에대해서도지정된 // (6) 표시용윈도우를생성한다입력이미지, 출력이미지를표시하는윈도우를생성한다. // (7) 취득이미지로부터배경을분리하는루프배경이미지모델의초기치를계산한후, 취득이미지로부터배경분리를행하는루프에들어간다. // (8) 배경이될수있는화소의휘도치의범위를체크한다 이미지의각화소에대해 ( 을 ) 를계산해,cvInRange() 그리고값이범위내인지어떤지를비교해,msk_img 에결과를보존해돌려준다. 범위내의화소에관해서는 msk_img 의대응하는화소치가 0xFF( 으 ) 로서세트된다. // (9) 휘도진폭을재계산한다 (5)( 와 ) 과같게해각화소의휘도진폭 ( 을 ) 를재계산한다. // (10) 배경이라고판단된영역의배경의휘도평균과휘도진폭을갱신한다 (8) 그리고돌려주어지는마스크이미지를이용하고, 배경영역만 cvrunningavg() 함수를이용해휘도평균과휘도진폭을갱신한다. // (11) 물체영역이라고판단된영역에서는휘도진폭만을 ( 배경영역보다늦은속도로 ) 갱신한다물체영역의휘도진폭을갱신하기위해서, 마스크이미지를반전시킨다. 그후 (10)( 와 ) 과같게해휘도진폭을갱신한다. 여기서사용하는갱신속도파라미터는배경영역갱신에사용하는파라미터보다크지않으면안된다. // (12) 물체영역만을출력이미지에카피한다 ( 배경영역은흑 ) 처리결과를나타내기위해서, 이미지 dst_img 에대해서,frame( 으 ) 로부터마스크에따라서화소치를카피한다. // (13) 처리결과를표시한다입력이미지와처리결과를표시한다. 캡쳐안에 "Esc" 키가밀렸을경우는, 종료한다. 실행결과예 CLOSE( 왼쪽 ) 입력이미지, ( 오른쪽 ) 물체검출결과 299

100 동적윤곽추적동적윤곽추적이란, 변형하는윤곽 ( 물체형상그자체의윤곽이변형하지않는경우에서도, 이미지에투영된윤곽이변형하는경우가있다 ) ( 을 ) 를순서대로계산하는 ( 많은경우는반복계산 ) 수법이다. 추적대상이되는물체는, 그러한수법에의해, 삼차원형상물체, 변형물체, 관절구조체등여러가지이다. Opencv그리고실장되고있다snake( 은 ) 는, 동적윤곽추적수법의가장기본적인방법의하나이다. snake그럼, 내부에너지와외부에너지의화가최소가되는윤곽을요구한다. 적절한윤곽을얻기위해는각에너지의중량감이되는파라미터가중요한역할을완수하지만, 이러한파라미터를결정하기위해서는, 추적물체형상이나배경이미지의특징이어느정도기존일필요가있다. 또, 기본적인snake그럼, 탐색공간이이차원이미지그자체로나타내지는공간이므로, 윤곽의자유도가높은분, 이미지자등의외란의영향을받기쉽다. 샘플 snake 에의한윤곽추적 ( 정지화면 ) cvsnakeimage snake 에의해동적윤곽추적을실시한다 ( 이번은대상이정지화면이므로, 어느쪽인가하면윤곽검출 ) 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <math.h> #include <stdio.h> typedef struct parameter Parameter; struct parameter { float alpha; float beta; float gamma; ; int main (int argc, char **argv) { int i, j = 0, c; IplImage *src_img, *dst_img; CvPoint *contour; CvPoint center; int length = 60; /* 동적윤곽을구성하는점수 */ Parameter param = { 0.45, 0.35, 0.2 ; /* cvsnakeimage 의파라미터 */ CvFont font; char iter[8]; // (1) 이미지를읽어들인다 300

101 1); if (argc < 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 3); cvinitfont (&font, CV_FONT_HERSHEY_DUPLEX, 0.7, 0.7); center.x = src_img->width / 2; center.y = src_img->height / 2; // (2) 동적윤곽의초기화 contour = (CvPoint *) cvalloc (sizeof (CvPoint) * length); for (i = 0; i < length; i++) { contour[i].x = (int) (center.x * cos (2 * CV_PI * i / length) + center.x); contour[i].y = (int) (center.y * sin (2 * CV_PI * i / length) + center.y); // (3) 초기윤곽의그리기 cvcvtcolor (src_img, dst_img, CV_GRAY2RGB); for (i = 0; i < length - 1; i++) { cvline (dst_img, contour[i], contour[i + 1], CV_RGB (255, 0, 0), 2, 8, 0); cvline (dst_img, contour[length - 1], contour[0], CV_RGB (255, 0, 0), 2, 8, 0); cvnamedwindow ("Snakes", CV_WINDOW_AUTOSIZE); cvshowimage ("Snakes", dst_img); cvwaitkey (0); /* 동적윤곽의수습계산 ( 과정을표시한다 ) */ while (1) { // (4) 동적윤곽의윤곽계산 cvsnakeimage (src_img, contour, length, m.alpha, m.beta, m.gamma, CV_VALUE, cvsize (15, 15), cvtermcriteria (CV_TERMCRIT_ITER, 1, 0.0), // (5) 계산된동적윤곽의그리기 cvcvtcolor (src_img, dst_img, CV_GRAY2RGB); for (i = 0; i < length - 1; i++) { cvline (dst_img, contour[i], contour[i + 1], CV_RGB (255, 0, 0), 2); cvline (dst_img, contour[length - 1], contour[0], CV_RGB (255, 0, 0), 2); snprintf (iter, 8, "%03d", ++j); cvputtext (dst_img, iter, cvpoint (15, 30), &font, CV_RGB (0, 0, 255)); // (6) 결과의표시 cvshowimage ("Snakes", dst_img); c = cvwaitkey (0); if (c == '\x1b') break; 301

102 cvdestroywindow ("Snakes"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 0; // (1) 이미지를읽어들인다커멘드인수로지정된파일명의이미지 ( 입력이미지 ) 을오픈해, 함수 cvloadimage() 그리고읽어들인다.2 번째의인수에 CV_LOAD_IMAGE_GRAYSCALE ( 을 ) 를지정하는것으로, 원이미지를그레이스케일이미지로서읽어들인다. 또, 결과를표시하기위해서, 입력이미지와동일사이즈의칼라이미지를작성한다. // (2) 동적윤곽의초기화초기윤곽의위치를결정한다. 본래는, 추적되는물체의형상에응한적절한초기윤곽이설정되는것이당연하지만, 이번은단지, 주어진이미지구형에내접하는타원을초기윤곽으로서이용하고있다. // (3) 초기윤곽의그리기초기화된윤곽을그리기하고, 실제로표시한다. 무엇인가키가압하될때까지기다린다. // (4) 동적윤곽의윤곽계산동적윤곽의윤곽을계산한다. 이샘플에서는, 입력이미지에대한사전처리등 ( 은 ) 는아무것도가서않지만, 실제로는, 예를들면노이즈의제거나콘트라스트의강조, 혹은엣지의검출등이사전처리로서행해지는경우가있다. 여기에서는, 윤곽이수습하는과정을표시하기위해서, 함수 cvsnakeimage() 하지만 1 회의반복계산밖에실시하지않게파라미터를설정해있다 (CvTermCriteria). 예를들면, 이하와같이하면, 100 회반복계산을하든가, 혹은각반복계산에대해이동하는점의수하지만 0 이하 ( 모든점이이동하지않게된다 ) 까지, 함수 cvsnakeimage() 안에서반복계산을하고나서돌아간다. 즉, 통상은, 반복계산을위한루프는필요없다. cvsnakeimage( src_img, contour, length, m.alpha, m.beta, m.gamma, CV_VALUE, cvsize(15, 15), cvtermcriteria( CV_TERMCRIT_ITER, 100, 0.0), 1); 또, 구배플래그 ( 함수 cvsnakeimage() 의마지막인수 ) 가 1 그래서, 이미지구배가에너지장으로서이용된다. 그외, 각 인수의상세한것에대하여는, 참조설명서를참조. // (5) 계산된동적윤곽의그리기계산되어갱신된윤곽 ( 여기에서는,1 스텝분 ), 및, 현재의반복수를이미지상에그리기한다. // (6) 결과의표시처리결과를표시한다. "Esc" 키가밀렸을경우는종료해, 그이외가밀렸을경우는, 다음의반복계산에들어간다. 실행결과예 초기위치 iter=10 iter=30 iter=50 302

103 옵티컬플로우옵티컬플로우란, 시간연속인이미지열을이용하고, 이미지의속도장 ( 물체의속도+카메라의속도 ) 를요구해그것을벡터집합으로표현한것이다. 크게나누고, 구배법, 블록매칭법이존재한다. 구배법에서는, 옵티컬플로우구속방정식 이라고불리는, 휘도의시간 / 공간적미분 ( 휘도구배 ) 의구속방정식을이용하고, 이것에제약조건 ( 을 ) 를부가하는것으로플로우를요구한다. 비교적고속으로전화소에대한속도장을계산할수있지만, 전제조건에맞지않는개곳 ( 급격한휘도변화, 노이즈 ) 에서는, 현저한오차가발생하는것이있다. 블록매칭법에서는, 이미지중이있는블록을템플릿으로서다음시간의이미지중으로부터매치하는곳을탐색하는것으로플로우를요구한다. 적절한특징을가지는이미지에대해서는, 로바스트에플로우를계산할수있지만, 비교적계산시간이걸린다. 또, 회전, 슬캘링에대한로바스트성은, 템플릿이되는블록의사이즈나이미지의특징에의존한다. 샘플 옵티컬플로우 1 cvcalcopticalflowhs, cvcalcopticalflowlk 구배법에따르는옵티컬플로우의계산 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, dx, dy, rows, cols; IplImage *src_img1, *src_img2, *dst_img1, *dst_img2; CvMat *velx, *vely; CvTermCriteria criteria; if (argc!= 3 (src_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 (src_img2 = cvloadimage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img1 = cvloadimage (argv[2], CV_LOAD_IMAGE_COLOR); dst_img2 = (IplImage *) cvclone (dst_img1); 303

104 // (1) 속도벡터를격납하는구조체의확보, 등 cols = src_img1->width; rows = src_img1->height; velx = cvcreatemat (rows, cols, CV_32FC1); vely = cvcreatemat (rows, cols, CV_32FC1); cvsetzero (velx); cvsetzero (vely); criteria = cvtermcriteria (CV_TERMCRIT_ITER CV_TERMCRIT_EPS, 64, 0.01); // (2) 옵티컬플로우를계산 (HS) cvcalcopticalflowhs (src_img1, src_img2, 0, velx, vely, 100.0, criteria); // (3) 옵티컬플로우를그리기 (HS) for (i = 0; i < cols; i += 5) { for (j = 0; j < rows; j += 5) { dx = (int) cvgetreal2d (velx, j, i); dy = (int) cvgetreal2d (vely, j, i); cvline (dst_img1, cvpoint (i, j), cvpoint (i + dx, j + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); // (4) 옵티컬플로우를계산 (LK) cvcalcopticalflowlk (src_img1, src_img2, cvsize (15, 15), velx, vely); // (5) 계산된플로우를그리기 (LK) for (i = 0; i < cols; i += 5) { for (j = 0; j < rows; j += 5) { dx = (int) cvgetreal2d (velx, j, i); dy = (int) cvgetreal2d (vely, j, i); cvline (dst_img2, cvpoint (i, j), cvpoint (i + dx, j + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); // (6) 옵티컬플로우의표시 cvnamedwindow ("ImageHS", 1); cvshowimage ("ImageHS", dst_img1); cvnamedwindow ("ImageLK", 1); cvshowimage ("ImageLK", dst_img2); cvwaitkey (0); cvdestroywindow ("ImageHS"); cvdestroywindow ("ImageLK"); cvreleaseimage (&src_img1); cvreleaseimage (&src_img2); cvreleaseimage (&dst_img1); 304

105 cvreleaseimage (&dst_img2); cvreleasemat (&velx); cvreleasemat (&vely); return 0; // (1) 속도벡터를격납하는구조체의확보, 등입력이미지와같은사이즈의 CvArr( 을 ) 를확보한다. 이번은,cvMat( 을 ) 를이용하고있지만, IplImage 등에서도상관없다. 또, 함수 cvcalcopticalflowhs() 그리고이용되는종료조건구조체도확보한다. // (2) 옵티컬플로우를계산 (HS) Horn & Schunck 알고리즘을이용해옵티컬플로우를계산한다. // (3) 옵티컬플로우를그리기 (HS) X 방향,y 방향모두 5[pixel] 마다, 함수 cvgetreal2d()( 을 ) 를이용하고, 인덱스 ( 좌표 ) 를지정하고속도벡터를꺼내, 그것을입력이미지상에그리기한다. // (4) 옵티컬플로우를계산 (LK) Lucas & Kanade 알고리즘을이용해옵티컬플로우를계산한다. // (5) 계산된플로우를그리기 (LK) X 방향,y 방향모두 5[pixel] 마다, 함수 cvgetreal2d()( 을 ) 를이용하고, 인덱스 ( 좌표 ) 를지정하고속도벡터를꺼내, 그것을입력이미지상에그리기한다. // (6) 옵티컬플로우의표시상기의두개의수법에의해계산된옵티컬플로우를, 입력이미지상에그리기한것을실제로표시한다. 무엇인가키가밀릴때까지기다린다. 실행결과예 입력이미지 1 입력이미지 2 결과이미지 (HS) 결과이미지 (LK) 옵티컬플로우 2 cvcalcopticalflowbm 블록매칭에의한옵티컬플로우의계산 표시의변환 샘플코드 305

106 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i, j, dx, dy, rows, cols; int block_size = 10; int shift_size = 1; CvMat *velx, *vely; CvSize block = cvsize (block_size, block_size); CvSize shift = cvsize (shift_size, shift_size); CvSize max_range = cvsize (50, 50); IplImage *src_img1, *src_img2, *dst_img; if (argc!= 3 (src_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 (src_img2 = cvloadimage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img = cvloadimage (argv[2], CV_LOAD_IMAGE_COLOR); // (1) 속도벡터를격납하는구조체의확보 rows = int (ceil (double (src_img1->height) / block_size)); cols = int (ceil (double (src_img1->width) / block_size)); velx = cvcreatemat (rows, cols, CV_32FC1); vely = cvcreatemat (rows, cols, CV_32FC1); cvsetzero (velx); cvsetzero (vely); // (2) 옵티컬플로우의계산 cvcalcopticalflowbm (src_img1, src_img2, block, shift, max_range, 0, velx, vely); // (3) 계산된플로우를그리기 for (i = 0; i < velx->width; i++) { for (j = 0; j < vely->height; j++) { dx = (int) cvgetreal2d (velx, j, i); dy = (int) cvgetreal2d (vely, j, i); cvline (dst_img, cvpoint (i * block_size, j * block_size), cvpoint (i * block_size + dx, j * block_size + dy), CV_RGB (255, 0, 0), 1, CV_AA, 0); cvnamedwindow ("Image", 1); cvshowimage ("Image", dst_img); 306

107 cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&src_img1); cvreleaseimage (&src_img2); cvreleaseimage (&dst_img); cvreleasemat (&velx); cvreleasemat (&vely); return 0; // (1) 속도벡터를격납하는구조체의확보사이즈 ceil(src.width/block_size) ceil(src.height/block_size), 32 비트, 싱글채널의, 속도벡터를격납하는구조체를확보한다. // (2) 옵티컬플로우의계산블록매칭에의해, 옵티컬플로우를계산한다. 3 번째의인수는, 블록 ( 템플릿 ) 사이즈를나타낸다. 4 번째의인수는,5 번째의인수로나타내지는하나의블록에대한탐색범위내를, 얼마나늦추어져서면서매칭을실시하는지를나타낸다. // (3) 계산된플로우를그리기함수 cvgetreal2d()( 을 ) 를이용하고, 인덱스 ( 좌표 ) 를지정하고속도벡터를꺼내, 그것을입력이미지상에그리기한다. 실행결과예 입력이미지 1 입력이미지 2 결과이미지 307

108 옵티컬플로우 3 cvcalcopticalflowpyrlk 드문드문한특징에대한옵티컬플로우의계산 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { char *status; int i, corner_count = 150; CvPoint2D32f *corners1, *corners2; CvTermCriteria criteria; IplImage *src_img1, *src_img2, *dst_img; IplImage *eig_img, *temp_img; IplImage *prev_pyramid, *curr_pyramid; if (argc!= 3 (src_img1 = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 (src_img2 = cvloadimage (argv[2], CV_LOAD_IMAGE_GRAYSCALE)) == 0) return -1; dst_img = cvloadimage (argv[2], CV_LOAD_IMAGE_COLOR); // (1) 필요한구조체의확보 eig_img = cvcreateimage (cvgetsize (src_img1), IPL_DEPTH_32F, 1); temp_img = cvcreateimage (cvgetsize (src_img1), IPL_DEPTH_32F, 1); corners1 = (CvPoint2D32f *) cvalloc (corner_count * sizeof (CvPoint2D32f)); corners2 = (CvPoint2D32f *) cvalloc (corner_count * sizeof (CvPoint2D32f)); prev_pyramid = cvcreateimage (cvsize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1); curr_pyramid = cvcreateimage (cvsize (src_img1->width + 8, src_img1->height / 3), IPL_DEPTH_8U, 1); 308

109 status = (char *) cvalloc (corner_count); criteria = cvtermcriteria (CV_TERMCRIT_ITER CV_TERMCRIT_EPS, 64, 0.01); // (2) 드문드문한특징점을검출 cvgoodfeaturestotrack (src_img1, eig_img, temp_img, corners1, &corner_count, 0.001, 5, NULL); // (3) 옵티컬플로우를계산 cvcalcopticalflowpyrlk (src_img1, src_img2, prev_pyramid, curr_pyramid, corners1, corners2, corner_count, cvsize (10, 10), 4, status, NULL, criteria, 0); // (4) 계산된플로우를그리기 for (i = 0; i < corner_count; i++) { if (status[i]) cvline (dst_img, cvpointfrom32f (corners1[i]), cvpointfrom32f (corners2[i]), CV_RGB (255, 0, 0), 1, CV_AA, 0); cvnamedwindow ("Image", 1); cvshowimage ("Image", dst_img); cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&src_img1); cvreleaseimage (&src_img2); cvreleaseimage (&dst_img); cvreleaseimage (&eig_img); cvreleaseimage (&temp_img); cvreleaseimage (&prev_pyramid); cvreleaseimage (&curr_pyramid); return 0; // (1) 필요한구조체의확보함수 cvgoodfeaturestotrack() 및, 함수 cvcalcopticalflowpyrlk() 에필요구조체를확보한다. // (2) 드문드문한특징점을검출함수 cvgoodfeaturestotrack()( 을 ) 를이용하고, 이미지중에서큰고유치를가지는코너 ( 분명히한특징점 )( 을 ) 를찾아낸다. 검출된코너는,4 번째의인수로나타나는배열에격납되어그개수는, 5 번째의인수로나타나는변수에격납된다. 6 번째, 및 7 번째의인수는각각, 특징점의질과특징점끼리의최저거리를나타내고있어이러한값이작아지면검출되는특징점이증가한다. 상세한것에대하여는, 레퍼런스를참조하는것. 309

110 // (3) 옵티컬플로우를계산앞의함수에의해검출된드문드문한특징점에관해서, 옵티컬플로우를계산한다. // (4) 계산된플로우를그리기플로우가발견된점에대해서만, 그플로우를그리기한다. 실행결과예 입력이미지 1 입력이미지 2 결과이미지 Condensation OpenCV에는, 추정기의하나로서Condensation( 파티클필터 )( 이 ) 가실장되고있다. 레퍼런스메뉴얼에도있도록 ( 듯이 ), 알고리즘의자세한것은, ( 을 ) 를참조되고싶다. 여기에서는, 특히OpenCV에실장되고있다Condensation알고리즘의함수의사용방법에대해말한다. 샘플 Condensation Condensation 알고리즘을이용해이미지중의붉은영역을트랙킹한다 표시의변환 샘플코드 #include <cv.h> 310

111 #include <highgui.h> #include <ctype.h> #include <math.h> // (1) 우도의계산을행하는함수 float calc_likelihood (IplImage * img, int x, int y) { float b, g, r; float dist = 0.0, sigma = 50.0; b = img->imagedata[img->widthstep * y + x * 3]; //B g = img->imagedata[img->widthstep * y + x * 3 + 1]; //G r = img->imagedata[img->widthstep * y + x * 3 + 2]; //R dist = sqrt (b * b + g * g + ( r) * ( r)); return 1.0 / (sqrt (2.0 * CV_PI) * sigma) * expf (-dist * dist / (2.0 * sigma * sigma)); int main (int argc, char **argv) { int i, c; double w = 0.0, h = 0.0; CvCapture *capture = 0; IplImage *frame = 0; int n_stat = 4; int n_particle = 4000; CvConDensation *cond = 0; CvMat *lowerbound = 0; CvMat *upperbound = 0; int xx, yy; // (2) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다 if (argc == 1 (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvcreatecameracapture (argc == 2? argv[1][0] - '0' : 0); // (3)1 프레임캡쳐해, 캐프체사이즈를취득한다. frame = cvqueryframe (capture); w = frame->width; h = frame->height; 311

112 cvnamedwindow ("Condensation", CV_WINDOW_AUTOSIZE); // (4)Condensation 구조체를작성한다. cond = cvcreatecondensation (n_stat, 0, n_particle); // (5) 상태벡터각차원이취할수있는최소치 최대치를지정한다. lowerbound = cvcreatemat (4, 1, CV_32FC1); upperbound = cvcreatemat (4, 1, CV_32FC1); cvmset (lowerbound, 0, 0, 0.0); cvmset (lowerbound, 1, 0, 0.0); cvmset (lowerbound, 2, 0, -10.0); cvmset (lowerbound, 3, 0, -10.0); cvmset (upperbound, 0, 0, w); cvmset (upperbound, 1, 0, h); cvmset (upperbound, 2, 0, 10.0); cvmset (upperbound, 3, 0, 10.0); // (6)Condensation 구조체를초기화한다 cvcondensinitsampleset (cond, lowerbound, upperbound); // (7)ConDensation 알고리즘에있어서의상태벡터의다이내믹스를지정한다 cond->dynammatr[0] = 1.0; cond->dynammatr[1] = 0.0; cond->dynammatr[2] = 1.0; cond->dynammatr[3] = 0.0; cond->dynammatr[4] = 0.0; cond->dynammatr[5] = 1.0; cond->dynammatr[6] = 0.0; cond->dynammatr[7] = 1.0; cond->dynammatr[8] = 0.0; cond->dynammatr[9] = 0.0; cond->dynammatr[10] = 1.0; cond->dynammatr[11] = 0.0; cond->dynammatr[12] = 0.0; cond->dynammatr[13] = 0.0; cond->dynammatr[14] = 0.0; cond->dynammatr[15] = 1.0; // (8) 노이즈파라미터를재설정한다. cvrandinit (&(cond->rands[0]), -25, 25, 0); cvrandinit (&(cond->rands[1]), -25, 25, 1); cvrandinit (&(cond->rands[2]), -5, 5, 2); cvrandinit (&(cond->rands[3]), -5, 5, 3); while (1) { 312

113 frame = cvqueryframe (capture); // (9) 각파티클에대해우도를계산한다. for (i = 0; i < n_particle; i++) { xx = (int) (cond->flsamples[i][0]); yy = (int) (cond->flsamples[i][1]); if (xx < 0 xx >= w yy < 0 yy >= h) { cond->flconfidence[i] = 0.0; else { cond->flconfidence[i] = calc_likelihood (frame, xx, yy); cvcircle (frame, cvpoint (xx, yy), 2, CV_RGB (0, 0, 255), -1); cvshowimage ("Condensation", frame); c = cvwaitkey (10); if (c == '\x1b') break; // (10) 다음의모델상태를추정한다 cvcondensupdatebytime (cond); cvdestroywindow ("Condensation"); cvreleaseimage (&frame); cvreleasecapture (&capture); cvreleasecondensation (&cond); cvreleasemat (&lowerbound); cvreleasemat (&upperbound); return 0; // (1) 우도의계산을행하는함수 Condensation 알고리즘에서는, 각파티클의우도를줄필요가있다. 샘플코드에서는, 각파티클의위치의정보로부터, 그픽셀치의빨강인것같음을우도로서이용하는일로했다. 직감적인알기쉬움을위해서 RGB 겉 ( 표 ) 색계인채계산을행한다. 구체적으로는이하와 같이,R(255,0,0)( 으 ) 로부터의 Euclid 거리 에대해서,0( 을 ) 를평균, sigma( 을 ) 를분산 ( 으 ) 로서가지는정규분포를우도함수 ( 으 ) 로서정의하고있다. exp 313

114 샘플에서는분산 sigma=50.0( 으 ) 로하고있지만, 이파라미터를튜닝하는일에따라서트랙킹성능이바뀌므로, 각자 확인되고싶다. 적색인것같음평가에는, 샘플과같이 RGB 겉 ( 표 ) 색계는아니고 HSV 겉 ( 표 ) 색계로행하는것이밝기의변화에대해서로 바스트인결과를얻을수있다. 그러나, 샘플에서는위에서설명한바와같이알고리즘의직감적인이해를위해서 RGB 겉 ( 표 ) 색계로처리를행하고있다. // (2) 커멘드인수에의해서지정된번호의카메라에대한다캡쳐구조체를작성한다함수 cvcreatecameracapture()( 을 ) 를이용하고, 커멘드인수로서주어진번호의카메라에대한캡쳐구조체 CvCapture( 을 ) 를작성한다. 인수가지정되지않는경우는, 디폴트의값이다 "0" 하지만이용된다. // (3)1 프레임캡쳐해, 캐프체사이즈 ( 을 ) 를취득한다. 함수 cvqueryframe() ( 을 ) 를호출해, 실제로 1 프레이무캐프체를실시해, 취득한이미지로부터, 이미지의폭 w( 와 ) 과높이 h( 을 ) 를취득한다. // (4)Condensation 구조체를작성한다. 함수 cvcreatecondensation() ( 을 ) 를, 상태벡터의차원수 n_stat( 와 ) 과파티클의수 n_particle( 을 ) 를지정해호출해,Condensatopn 구조체를작성한다. 제 2 인수는, 관측벡터수를지정하는일이되어있지만,OpenCV 의현버젼에서는관측벡터는사용하고있지않기때문에, 값에는 0( 을 ) 를지정했으므로상관없다. 샘플코드에서는, 다이내믹스로서이하와같이등속직선운동을가정하고있으므로, 상태벡터는그위치 (x,y)( 와 ) 과각축방향의속도 (u,v) 의 4 차원이된다. // (5) 상태벡터각차원이취할수있는최소치 최대치를지정한다. 벡터 lowerbound, upperbound ( 을 ) 를각각함수 cvcreatemat() 그리고파티션해, 상태벡터의각차원의변수가취할수있는값의하한치 ( 최소치 ) 와상한치 ( 최대치 ) 를설정한다. 이값은, 초기치설정시에는파티클을일모양분포시키기위해서사용된다. 샘플코드에서는, 위치에대해서최소치 0, 최대치는이미지사이즈 (w,h)( 을 ) 를지정, 또속도에대해 -10~10[pixel]( 을 ) 를지정해있다. // (6)Condensation 구조체를초기화한다함수 cvcondensinitsampleset() 그리고, 구조체의초기화, 초기파티클상태의설정을행한다. // (7)ConDensation 알고리즘에있어서의상태벡터의다이내믹스를지정한다 CvConDensation 구조체의멤버 DynamMatr[] 에다이내믹스를표현하는행렬요소를순서로지정한다. 샘플코드에서는,(4) 그리고나타내보인등속직선운동을이하와같이행렬표현했을때의 4X4 의배열을주고있다. 314

115 모델의다이내믹스로서랜덤워크를가정하는경우는, 여기서단위행렬을지정한다. // (8) 노이즈파라미터를재설정한다. 초기화시에, 상태벡터의각차원의변수에대한노이즈파라미터는자동적으로설정된다 ( 상한치, 하한치각각의범위의 1/5 의값이된다 ) 의로, 그것이무례한경우는여기서재설정한다. 샘플코드에서는, 위치에관해서는 -25~25[pixel], 속도에관해서는 -5~5[pixel] 의범위의노이즈 ( 을 ) 를지정해있다. // (9) 각파티클에대해우도를계산한다. 루프블록중에서함수 cvqueryframe() ( 을 ) 를호출해, 이미지를취득한다. 그후, 각파티클의위치 ( 샘플코드에서는,CvConDensation 구조체의 flsamples[i][0]( 와 ) 과 [i][1]) ( 을 ) 를이용해그우도를 (1) 그리고정의한우도함수를이용해계산한다. 파티클의위치가이미지범위내이면, 푸른환을캡쳐한이미지상에추가해, 이미지를그리기한다. "Esc" 키가밀렸을경우는, 트랙킹을종료한다. // (10) 다음의모델상태를추정한다함수 cvcondensupdatebytime()( 을 ) 를이용하고, 다음의시각의모델상태를추정한다. condensation 알고리즘에있어서의, 리산풀 이동 확산의국면을행하고있다 ( 이 ) 라고생각해도좋다. 실행결과예 CLOSE 물체검출 OpenCV그리고준비되어있는물체검출은, (Haar-like특징을이용한 ) 부스트된약분류기의캐스케이드를이용하고있다. 세세한알고리즘에관해서는, 레퍼런스및그외의참고문헌에양보하지만, 분류기는대략적으로이하와같이구성되어있다. Haar-like 특징을입력으로취하는결정목을, 기본분류기로한다. 부스팅기법을이용하고, 몇개의기본분류기를복합시켜스테이지분류기가구성된다. 스테이지분류기가죽늘어서묶은것에연결되어최종적인캐스케이드가구성된다. 인식시에입력되는이미지는, 캐스케이드의각스테이지에있어평가되어도중에기각되면그부분이미지에는물체가없는, 모든스테이지를패스하면, 그부분이미지는물체를포함하고있다고여겨진다. Haar-like특징은, ( 형상이나국소적인특징은아니고 ) 전체적인아피아랑스를이용하고있으므로, 이특징을이용한만큼류기에도, 검출의특기, 서툼이존재한다. 예를들면, 다소흔들린이미지나희미해진이미지에서도, ( 대략의 texture) 의대국적인특징은변화하지않기때문에, 이것들을검출할수있다. 그러나, 같은종류에대해도전체의 texture의개체차이가크고, 그것이넓게분포하는오브젝트는, 그형상이나국소적인특징이닮고있었다고해도검출하는것은곤란하다. 315

116 샘플 얼굴의검출 cvhaardetectobjects 미리학습된만큼류기를이용해입력이미지중의얼굴을검출한다 샘플코드 표시의변환 Python 판으로변경 #include <cv.h> #include <highgui.h> int main (int argc, char **argv) { int i; IplImage *src_img = 0, *src_gray = 0; const char *cascade_name = "haarcascade_frontalface_default.xml"; CvHaarClassifierCascade *cascade = 0; CvMemStorage *storage = 0; CvSeq *faces; static CvScalar colors[] = { {{0, 0, 255, {{0, 128, 255, {{0, 255, 255, {{0, 255, 0, {{255, 128, 0, {{255, 255, 0, {{255, 0, 0, {{255, 0, 255 ; // (1) 이미지를읽어들인다 if (argc < 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; src_gray = cvcreateimage (cvgetsize (src_img), IPL_DEPTH_8U, 1); // (2) 부스트된만큼류기의캐스케이드를읽어들인다 cascade = (CvHaarClassifierCascade *) cvload (cascade_name, 0, 0, 0); // (3) 메모리를확보해, 읽어들인이미지의그레이스케일화, 히스토그램의균일화를실시한다 storage = cvcreatememstorage (0); cvclearmemstorage (storage); cvcvtcolor (src_img, src_gray, CV_BGR2GRAY); cvequalizehist (src_gray, src_gray); // (4) 물체 ( 얼굴 ) 검출 faces = cvhaardetectobjects (src_gray, cascade, storage, 1.11, 4, 0, cvsize (40, 40)); 316

117 // (5) 검출된모든얼굴위치에, 엔을그리기한다 for (i = 0; i < (faces? faces->total : 0); i++) { CvRect *r = (CvRect *) cvgetseqelem (faces, i); CvPoint center; int radius; center.x = cvround (r->x + r->width * 0.5); center.y = cvround (r->y + r->height * 0.5); radius = cvround ((r->width + r->height) * 0.25); cvcircle (src_img, center, radius, colors[i % 8], 3, 8, 0); // (6) 이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Face Detection", CV_WINDOW_AUTOSIZE); cvshowimage ("Face Detection", src_img); cvwaitkey (0); cvdestroywindow ("Face Detection"); cvreleaseimage (&src_img); cvreleaseimage (&src_gray); cvreleasememstorage (&storage); return 0; // (1) 이미지를읽어들인다함수 cvloadimage()( 을 ) 를이용하고, 얼굴을검출하는대상이되는 ( 커멘드인수로지정된 ) 이미지를읽어들인다. 2 번째의인수에 CV_LOAD_IMAGE_COLOR( 을 ) 를지정하는것으로, 칼라이미지로서읽어들인다. 검출처리자체는그레이스케일이미지를대상에행해지지만, 검출결과를그리기하기위해서, 칼라이미지로서읽어들여둔다. 또, 그처리를위해서, 입력이상과동사이즈의그레이스케일이미지 (IplImage)( 을 ) 를작성해둔다. // (2) 부스트된만큼류기의캐스케이드를읽어들인다학습에의해서미리획득된, 분류기의캐스케이드가기술되었다 xml 파일을읽어들인다. 여기에서는,OpenCV 의샘플에부속되는정면얼굴이미지학습결과의 1 개인,"haarcascade_frontalface_default.xml"( 을 ) 를이용한다. // (3) 메모리를확보해, 읽어들인이미지의그레이스케일화, 히스토그램의균일화를실시한다실제의얼굴검출시에이용하는메모리를확보, 초기화한다. 또, 읽힌칼라이미지를그레이스케일이미지로변경해, 게다가함수 cvequalizehist() 에의해, 그히스토그램을균일화한다. // (4) 물체 ( 얼굴 ) 검출함수 cvhaardetectobjects() 에의해, 이미지로부터얼굴을검출한다. 4 번째의인수 (1.11)( 은 ) 는, 탐색윈도우의스케일변화를나타내고있어이번경우는, 스케일마다의탐색에대하고, 윈드우사이즈가 11[%] 변화하는일을나타내고있다. 또,5 번째의인수는, 오브젝트를구성하는근방구형의최소수를나타내고있어이것보다적은구형으로부터구성되는오브젝트는, 노이즈로서무시된다. 즉, 실제의오브젝트가존재하는부근에서는, 다소어긋난장소에있어도 317

118 오브젝트를포함한영역으로서인식될것이므로, 1 개의오브젝트는, 복수의구형영역의집합으로서나타내진다. 그렇지않은개소 ( 예를들어, 그주변에단일의구형밖에존재하지않는오브젝트후보 ) 는, 우연히캐스케이드를패스한영역으로서무시된다. // (5) 검출된모든얼굴위치에, 엔을그리기한다검출된모든얼굴에대해서, 사이즈와중심을요구해그자리소에, 엔을그리기한다. 엔의그리기에는, 미리정했다 8 색을차례차례이용한다. // (6) 이미지를표시, 키가밀렸을때에종료검출결과적으로엔이그리기된이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 카메라 calibration 카메라 calibration란, ( 어떤시점에있어 ) 카메라고유의내부파라미터와월드좌표계에있어서의위치자세를의미하는외부파라미터를요구하는처리이다. 카메라의 calibration가되면, 있다3차원좌표를가진점이카메라이미지의어디에투영되는지, 혹은복수의카메라에투영된점이3차원공간안의어디에있는지, 등을계산할수있다. 또, 카메라특유의 ( 원주방향및반경방향 ) 일그러짐의보정을실시할수도성과 ( 가, 이것은 calibration 수법으로의존해, 일그러짐을가지지않는모델을이용하는단순한수법도존재한다 ). 적응할수있는 calibration 수법은, 카메라의대수나준비할수있는치구에의해서변화한다. OpenCV의카메라 calibration는,z.zhang의수법을기본으로실장되고있다 (Zhang의수법에대해서는,"A flexible new technique for camera calibration". IEEE Transactions on Patte rn Analysis and Machine Intelligence, 22(11): , 2000.( 을 ) 를참조되고싶다 ). Ver.2( 으 ) 로부터 (cvcalibratecam era2등 ) 은,Matlab의 calibration 엔진 ( 이것도Zhang의수법 ) 을이식한것이되었다. 샘플 카메라 calibration cvcalibratecamera2, cvfindextrinsiccameraparams2 Zhang 의수법을이용해카메라의 calibration 를실시해, 결과를파일에보존한다 표시의변환 샘플코드 #include <stdio.h> #include <cv.h> #include <highgui.h> #define IMAGE_NUM (25) /* 이미지수 */ #define PAT_ROW (7) /* 패턴의행수 */ #define PAT_COL (10) /* 패턴의렬수 */ 318

119 #define PAT_SIZE (PAT_ROW*PAT_COL) #define ALL_POINTS (IMAGE_NUM*PAT_SIZE) #define CHESS_SIZE (24.0) /* 패턴 1 매스의 1 옆사이즈 [mm] */ int main (int argc, char *argv[]) { int i, j, k; int corner_count, found; int p_count[image_num]; IplImage *src_img[image_num]; CvSize pattern_size = cvsize (PAT_COL, PAT_ROW); CvPoint3D32f objects[all_points]; CvPoint2D32f *corners = (CvPoint2D32f *) cvalloc (sizeof (CvPoint2D32f) * ALL_POINTS); CvMat object_points; CvMat image_points; CvMat point_counts; CvMat *intrinsic = cvcreatemat (3, 3, CV_32FC1); CvMat *rotation = cvcreatemat (1, 3, CV_32FC1); CvMat *translation = cvcreatemat (1, 3, CV_32FC1); CvMat *distortion = cvcreatemat (1, 4, CV_32FC1); // (1)calibration 이미지의읽기 for (i = 0; i < IMAGE_NUM; i++) { char buf[32]; sprintf (buf, "calib_img/%02d.png", i); src_img[i] = cvloadimage (buf, CV_LOAD_IMAGE_COLOR); // (2)3 차원공간좌표의설정 for (i = 0; i < IMAGE_NUM; i++) { for (j = 0; j < PAT_ROW; j++) { for (k = 0; k < PAT_COL; k++) { objects[i * PAT_SIZE + j * PAT_COL + k].x = j * CHESS_SIZE; objects[i * PAT_SIZE + j * PAT_COL + k].y = k * CHESS_SIZE; objects[i * PAT_SIZE + j * PAT_COL + k].z = 0.0; cvinitmatheader (&object_points, ALL_POINTS, 3, CV_32FC1, objects); // (3) 체스보드 (calibration 패턴 ) 의코너검출 int found_num = 0; cvnamedwindow ("Calibration", CV_WINDOW_AUTOSIZE); for (i = 0; i < IMAGE_NUM; i++) { found = cvfindchessboardcorners (src_img[i], pattern_size, &corners[i * PAT_SIZE], 319

120 &corner_count); fprintf (stderr, "%02d...", i); if (found) { fprintf (stderr, "ok\n"); found_num++; else { fprintf (stderr, "fail\n"); // (4) 코너위치를사브픽셀정도에수정, 그리기 IplImage *src_gray = cvcreateimage (cvgetsize (src_img[i]), IPL_DEPTH_8U, 1); cvcvtcolor (src_img[i], src_gray, CV_BGR2GRAY); cvfindcornersubpix (src_gray, &corners[i * PAT_SIZE], corner_count, cvsize (3, 3), cvsize (-1, -1), cvtermcriteria (CV_TERMCRIT_ITER CV_TERMCRIT_EPS, 20, 0.03)); cvdrawchessboardcorners (src_img[i], pattern_size, &corners[i * PAT_SIZE], corner_count, found); p_count[i] = corner_count; cvshowimage ("Calibration", src_img[i]); cvwaitkey (0); cvdestroywindow ("Calibration"); if (found_num!= IMAGE_NUM) return -1; cvinitmatheader (&image_points, ALL_POINTS, 1, CV_32FC2, corners); cvinitmatheader (&point_counts, IMAGE_NUM, 1, CV_32SC1, p_count); // (5) 내부파라미터, 일그러짐계수의추정 cvcalibratecamera2 (&object_points, &image_points, &point_counts, cvsize (640, 480), intrinsic, distortion); // (6) 외부파라미터의추정 CvMat sub_image_points, sub_object_points; int base = 0; cvgetrows (&image_points, &sub_image_points, base * PAT_SIZE, (base + 1) * PAT_SIZE); cvgetrows (&object_points, &sub_object_points, base * PAT_SIZE, (base + 1) * PAT_SIZE); cvfindextrinsiccameraparams2 (&sub_object_points, &sub_image_points, intrinsic, distortion, rotation, translation); // (7)XML 파일에의써내 CvFileStorage *fs; fs = cvopenfilestorage ("camera.xml", 0, CV_STORAGE_WRITE); cvwrite (fs, "intrinsic", intrinsic); cvwrite (fs, "rotation", rotation); cvwrite (fs, "translation", translation); 320

121 cvwrite (fs, "distortion", distortion); cvreleasefilestorage (&fs); for (i = 0; i < IMAGE_NUM; i++) { cvreleaseimage (&src_img[i]); return 0; // (1)calibration 이미지의읽기 calibration 에필요한이미지열을읽어들인다. 여기에서는,calib_img 이하에미리준비되어있는,"00.png"~"24.png"( 이 ) 라는이름의이미지파일을이용했다. zhang 의 calibration 에필요한이미지매수 ( 뷰의수 ) 는정해져있지않지만, 일반적으로는, 치우침등이없는적절한이미지를수십매정도준비하면충분한정도를얻을수있다. // (2)3 차원공간좌표의설정이번은,1 매스의길이가 24.0[mm], 안쪽의코너수가 7 10 의체스보드패턴을이용했다 ( 실제의체스보드패턴파일 ). 후술하는이유에의해, 코너수는, 홀수 짝수, 혹은짝수 홀수의편성을이용하는것이바람직하다. x 및 y 의값은, 패턴의매스의사이즈에맞추어값을대입한다. 여기서설정되는축방향은, 카메라의외부파라미터에영향을준다. 또,z( 은 ) 는모두 0 또는 1( 으 ) 로설정할필요가있다. // (3) 체스보드 (calibration 패턴 ) 의코너검출함수 cvfindchessboardcorners() 에의해, 체스보드의코너를검출한다. 코너는좌하로부터우상을향해서검출되어제 2 인수로설정된사이즈의코너가모두검출되었을경우에는,0 이외의값을돌려준다. 덧붙여서, 본프로그램과는너무관계없지만,2 매이상의 calibration 패턴을인식시키고싶은경우는, ( 종횡의구형수가 ) 다른타입의 calibration 패턴을준비하는, 혹은, 하나의패턴을검출할때마다거기를전부칠해재탐색하는, 등의수법을생각할수있다. OpenCV 의함수 cvfindchessboardcorners() 하지만검출하는코너의차례는, 패턴의행과열의정의에의존한다. 이하의이미지체스패턴을, 샘플프로그램과같이 7 10(7 행 10 열 ) 이라고정의하면, 검출코너의최초, 즉 calibration 결과의월드좌표원점은, 이미지의 (A) 의장소가된다. 또, 같은이미지를 10 7(10 행 7 열 ) 이라고정의하면, 원점은 (B) 된다. 함수 cvfindchessboardcorners()( 은 ) 는, 체스패턴의코너를검출하기위해서구형의검출을실시하지만, 구형 ( 흑색 ) 에비스듬하게인접하는구형의수가 1 개의것이있으면 ( 이미지중의좌상과우상 ), 그구형이포함한코너의최초의점으로한다. 그것이존재하지않는경우는, 기울기인접구형수가 2 개의것을이용한다. 코너수가, 홀수 짝수, 혹은짝수 홀수의편성의체스패턴이라면, 그최초의코너가일의로결정되지만, 그렇지않은경우는, 코너가일의로결정되지않고, 체스패턴의이미지에따라서는검출되는코너의차례가달라버릴가능성이있다. 321

122 // (4) 코너위치를사브픽셀정도에수정, 그리기함수 cvfindchessboardcorners() 냄새나도,0.5[pixel] 단위로코너가검출되지만, 한층더검출된코너를사브픽셀정도로수정한다. 또, 그러한코너를이미지상에그리기해, 실제로표시한다. 모든코너가올바르게검출되었을경우는내부에서정의되었다 7 색으로그리기되어그렇지않은경우는모든코너가적색으로그리기된다. 이샘플코드에서는, 모든이미지 (25 매 ) 로코너가올바르게검출되지않는경우는, 프로그램이종료한다. // (5) 내부파라미터, 일그러짐계수의추정함수 cvcalibratecamera2() 에의해, 카메라의내부파라미터, 및일그러짐계수를추정한다. 내부파라미터는, 이하의식의 A 에해당한다. // (6) 외부파라미터의추정카메라의외부파라미터를추정한다. 이것은, 상술의식의 [R t] 에해당한다. 즉, 삼차원이있는월드좌표계원점으로부터, 카메라좌표계에의변환을나타내는파라미터이다. 월드좌표계원점으로부터의회전및병진을나타내는외부파라미터를결정하기위해서는, 기준이되는월드좌표계원점이필요하다. 여기에서는,base=0( 으 ) 로서 0 번째의이미지 ("00.png")( 을 ) 를지정하고있어, 이이미지에비치는패턴의 3 차원좌표계 ((2) 그리고주어진 ) 에있어서의외부파라미터가추정된다. 이때의월드좌표계의원점은,(3) 의항으로설명한것처럼결정된다. 이샘플프로그램에서는, 실행결과예에있는이미지의적색의행의최초의코너가원점이된다. // (7)XML 파일에의써내구할수있던, 내부파라미터, 외부파라미터, 일그러짐계수를파일에써낸다. 여기에서는, 확장자 (extension) 에의해 XML 형식을지정해있지만,YAML 형식에서출력하는것도가능하다. 실행결과예 카메라 calibration 에이용한이미지의일부 ( 무순서 ) 의인식결과. 실제의 calibration 결과파일 (XML). 이 calibration 결과는,DFW-VL500(sony) 에광각렌즈 VCL-00637S(sony)( 을 ) 를단것으로갔다. 일그러짐보정 cvundistort2 calibration 데이터를이용하고, 일그러짐을보정한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> 322

123 int main (int argc, char *argv[]) { IplImage *src_img, *dst_img; CvMat *intrinsic, *distortion; CvFileStorage *fs; CvFileNode *param; // (1) 보정대상이되는이미지의읽기 if (argc < 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; dst_img = cvcloneimage (src_img); // (2) 파라미터파일의읽기 fs = cvopenfilestorage ("camera.xml", 0, CV_STORAGE_READ); param = cvgetfilenodebyname (fs, NULL, "intrinsic"); intrinsic = (CvMat *) cvread (fs, param); param = cvgetfilenodebyname (fs, NULL, "distortion"); distortion = (CvMat *) cvread (fs, param); cvreleasefilestorage (&fs); // (3) 일그러짐보정 cvundistort2 (src_img, dst_img, intrinsic, distortion); // (4) 이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Distortion", CV_WINDOW_AUTOSIZE); cvshowimage ("Distortion", src_img); cvnamedwindow ("UnDistortion", CV_WINDOW_AUTOSIZE); cvshowimage ("UnDistortion", dst_img); cvwaitkey (0); cvdestroywindow ("Distortion"); cvdestroywindow ("UnDistortion"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleasemat (&intrinsic); cvreleasemat (&distortion); return 0; // (1) 보정대상이되는이미지의읽기보정을실시하는이미지를읽어들인다. 물론, 보정대상이되는이미지를촬영한카메라는, calibration 가끝난상태이다고한다. 323

124 // (2) 파라미터파일의읽기 calibration 결과를격납한파라미터파일을읽어들인다. 일그러짐보정에필요한파라미터는, 내부파라미터, 및일그러짐계수뿐이므로, 이것을파일로부터읽어들인다. // (3) 일그러짐보정함수 cvundisotort2() 에의해, 입력이미지의일그러짐보정을행한다. // (4) 이미지를표시, 키가밀렸을때에종료일그러짐보정전이미지와비뚤어져보정후이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 [ 좌 ] 일그러짐보정전. [ 우 ] 일그러짐보정후. 맵을이용한일그러짐보정 cvinitundistortmap calibration 데이터를이용해맵을작성해, 일그러짐을보정한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> int main (int argc, char *argv[]) { IplImage *src_img, *dst_img; CvMat *intrinsic, *distortion; CvFileStorage *fs; CvFileNode *param; IplImage *mapx = cvcreateimage (cvsize (640, 480), IPL_DEPTH_32F, 1); IplImage *mapy = cvcreateimage (cvsize (640, 480), IPL_DEPTH_32F, 1); if (argc < 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) return -1; dst_img = cvcloneimage (src_img); fs = cvopenfilestorage ("camera.xml", 0, CV_STORAGE_READ); param = cvgetfilenodebyname (fs, NULL, "intrinsic"); 324

125 intrinsic = (CvMat *) cvread (fs, param); param = cvgetfilenodebyname (fs, NULL, "distortion"); distortion = (CvMat *) cvread (fs, param); cvreleasefilestorage (&fs); // (1) 일그러짐보정을위한맵초기화 cvinitundistortmap (intrinsic, distortion, mapx, mapy); // (2) 일그러짐보정 cvremap (src_img, dst_img, mapx, mapy); cvnamedwindow ("Distortion", CV_WINDOW_AUTOSIZE); cvshowimage ("Distortion", src_img); cvnamedwindow ("UnDistortion", CV_WINDOW_AUTOSIZE); cvshowimage ("UnDistortion", dst_img); cvwaitkey (0); cvdestroywindow ("Distortion"); cvdestroywindow ("UnDistortion"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); cvreleaseimage (&mapx); cvreleaseimage (&mapy); cvreleasemat (&intrinsic); cvreleasemat (&distortion); return 0; // (1) 일그러짐보정을위한맵초기화함수 cvinitundistortmap()( 을 ) 를이용하고, 일그러짐을보정하기위한맵 mapx,mapy 의초기화를실시한다. // (2) 일그러짐보정작성된맵을이용하고, 이미지의기하변환 (cvremap)( 을 ) 를실시한다. 함수 cvundistort2()( 을 ) 를이용하는경우와비교해서, 계산속도가향상한다. 그러나, 일반적인기하변환을실시하는함수를이용하고있기때문에인가, 이전의버젼으로실장되고있던일그러짐보정함수에비해퍼포먼스가뒤떨어진다. 물론, 이전의함수 cvundistortinit,cvundistort 도 cvcompat.h 안에서선언되고있지만, 장래적으로폐지될예정이므로이용은추천되지않는다 ( 또코멘트에의하면, 이러한함수는,quite hackerish implementations 이다 ). 단순하게일그러짐보정의속도만을요구한다면, Yahoo!Groups 의투고 (Speeding up undistort) 에도있도록 ( 듯이 ), 스스로 LUT( 을 ) 를작성하면좋다. 그경우는, 이미지의보완을별도실시할필요가있다. 실행결과예 [ 좌 ] 일그러짐보정전. [ 우 ] 일그러짐보정후. 325

126 서포트벡터머신 OpenCV-1.0.0그리고제공된다SVM에관한함수는,libsvm라이브러리(version 2.6) 의기능을실장한것이다. 또, 이버젼에서는, 학습후의파라미터를보존, 읽는함수 (save, load) 에버그가존재해,Yahoo!Groups그리고보고되고있는패치 ( ) 등을수신자명차면, 해당기능을이용할수없기때문에주의하는것. 샘플 서포트벡터머신 CvSVM SVM( 을 ) 를이용해 2 차원벡터의 3 클래스분류문제를푼다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ml.h> #include <time.h> int main (int argc, char **argv) { const int s = 1000; int size = 400; int i, j, sv_num; IplImage *img; CvSVM svm = CvSVM (); CvSVMParams param; CvTermCriteria criteria; CvRNG rng = cvrng (time (NULL)); CvPoint pts[s]; float data[s * 2]; int res[s]; CvMat data_mat, res_mat; CvScalar rcolor; const float *support; // (1) 이미지영역의확보와초기화 img = cvcreateimage (cvsize (size, size), IPL_DEPTH_8U, 3); 326

127 cvzero (img); // (2) 학습데이터의생성 for (i = 0; i < s; i++) { pts[i].x = cvrandint (&rng) % size; pts[i].y = cvrandint (&rng) % size; if (pts[i].y > 50 * cos (pts[i].x * CV_PI / 100) + 200) { cvline (img, cvpoint (pts[i].x - 2, pts[i].y - 2), cvpoint (pts[i].x + 2, pts[i].y + 2), CV_RGB (255, 0, 0)); cvline (img, cvpoint (pts[i].x + 2, pts[i].y - 2), cvpoint (pts[i].x - 2, pts[i].y + 2), CV_RGB (255, 0, 0)); res[i] = 1; else { if (pts[i].x > 200) { cvline (img, cvpoint (pts[i].x - 2, pts[i].y - 2), cvpoint (pts[i].x + 2, pts[i].y + 2), CV_RGB (0, 255, 0)); cvline (img, cvpoint (pts[i].x + 2, pts[i].y - 2), cvpoint (pts[i].x - 2, pts[i].y + 2), CV_RGB (0, 255, 0)); res[i] = 2; else { cvline (img, cvpoint (pts[i].x - 2, pts[i].y - 2), cvpoint (pts[i].x + 2, pts[i].y + 2), CV_RGB (0, 0, 255)); cvline (img, cvpoint (pts[i].x + 2, pts[i].y - 2), cvpoint (pts[i].x - 2, pts[i].y + 2), CV_RGB (0, 0, 255)); res[i] = 3; // (3) 학습데이터의표시 cvnamedwindow ("SVM", CV_WINDOW_AUTOSIZE); cvshowimage ("SVM", img); cvwaitkey (0); // (4) 학습파라미터의생성 for (i = 0; i < s; i++) { data[i * 2] = float (pts[i].x) / size; data[i * 2 + 1] = float (pts[i].y) / size; cvinitmatheader (&data_mat, s, 2, CV_32FC1, data); cvinitmatheader (&res_mat, s, 1, CV_32SC1, res); criteria = cvtermcriteria (CV_TERMCRIT_EPS, 1000, FLT_EPSILON); param = CvSVMParams (CvSVM::C_SVC, CvSVM::RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, NULL, criteria); 327

128 // (5)SVM 의학습 svm.train (&data_mat, &res_mat, NULL, NULL, param); // (6) 학습결과의그리기 for (i = 0; i < size; i++) { for (j = 0; j < size; j++) { CvMat m; float ret = 0.0; float a[] = { float (j) / size, float (i) / size ; cvinitmatheader (&m, 1, 2, CV_32FC1, a); ret = svm.predict (&m); switch ((int) ret) { case 1: rcolor = CV_RGB (100, 0, 0); break; case 2: rcolor = CV_RGB (0, 100, 0); break; case 3: rcolor = CV_RGB (0, 0, 100); break; cvset2d (img, i, j, rcolor); // (7) 트레이닝데이터의재그리기 for (i = 0; i < s; i++) { CvScalar rcolor; switch (res[i]) { case 1: rcolor = CV_RGB (255, 0, 0); break; case 2: rcolor = CV_RGB (0, 255, 0); break; case 3: rcolor = CV_RGB (0, 0, 255); break; cvline (img, cvpoint (pts[i].x - 2, pts[i].y - 2), cvpoint (pts[i].x + 2, pts[i].y + 2), rcolor); cvline (img, cvpoint (pts[i].x + 2, pts[i].y - 2), cvpoint (pts[i].x - 2, pts[i].y + 2), rcolor); 328

129 // (8) 서포트벡터의그리기 sv_num = svm.get_support_vector_count (); for (i = 0; i < sv_num; i++) { support = svm.get_support_vector (i); cvcircle (img, cvpoint ((int) (support[0] * size), (int) (support[1] * size)), 5, CV_RGB (200, 200, 200)); // (9) 이미지의표시 cvnamedwindow ("SVM", CV_WINDOW_AUTOSIZE); cvshowimage ("SVM", img); cvwaitkey (0); cvdestroywindow ("SVM"); cvreleaseimage (&img); return 0; // (1) 이미지영역의확보와초기화이미지영역을확보해, 제로클리어 ( 흑색으로초기화 ) 한다. // (2) 트레이닝데이터의생성 2 차원의트레이닝데이터를랜덤에생성해, 그값을 CvPoint 형태의배열 pts[] 에격납한다. 또, 각트레이닝데이터의클래스를반응을일으키는최소의물리량에의해서결정해, 배열 res[] 에격납한다. // (3) 트레이닝데이터의표시생성된트레이닝데이터를, 최초로확보한이미지상에그리기해, 표시한다. 클래스 1-3 까지가, 적, 록, 청의각색으로나타난다. 여기서, 무엇인가키가밀릴때까지기다린다. // (4) 학습파라미터의생성 SVM 의학습파라미터를이하와같이결정한다. svm 의종류 :CvSVM::C_SVC 커넬의종류 :CvSVM::RBF degree:10.0( 이번은, 이용되지않는다 ) gamma:8.0 coef0:1.0( 이번은, 이용되지않는다 ) C:10.0 nu:0.5( 이번은, 이용되지않는다 ) p:0.1( 이번은, 이용되지않는다 ) 또, 트레이닝데이터를정규화해,CvMat 형태의행렬에격납한다. // (5)SVM 의학습트레이닝데이터와결정할수있던학습파라미터를이용하고,SVM 의학습을행한다. 329

130 // (6) 학습결과의그리기학습결과를나타내기위해서, 이미지영역내의모든픽셀 ( 특징벡터 ) 을입력으로서클래스분류를실시한다. 또, 입력픽셀을, 그것이속하는클래스에대응한색으로그리기한다. // (7) 트레이닝데이터의재그리기트레이닝데이터를, 결과이미지우에에겹쳐그리기한다. // (8) 서포트벡터의그리기트레이닝데이터가운데, 서포트벡터를흰동그라미로둘러싸표시한다. // (9) 이미지의표시실제로, 처리결과의이미지를표시해, 무엇인가키가밀리면종료한다. 실행결과예 [ 좌 ] 학습샘플,[ 우 ] 분류결과 이미지의각픽셀치를특징벡터로했다 SVM 의학습 학습용의이미지를읽어들여, 그픽셀치를특징벡터로서 SVM 의학습을실시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ml.h> #include <stdio.h> int main (int argc, char **argv) { int i, j, ii, jj; int width = 28, height = 30; /* 샘플이미지사이즈 */ int image_dim = width * height; int pimage_num = 500; /* 포지티브샘플수 */ int nimage_num = 1000; /* 네가티브샘플수 */ int all_image_num = pimage_num + nimage_num; IplImage *img_org; 330

131 IplImage *sample_img; int res[all_image_num]; float data[all_image_num * image_dim]; CvMat data_mat, res_mat; CvTermCriteria criteria; CvSVM svm = CvSVM (); CvSVMParams param; char filename[64]; // (1) 포지티브샘플의읽기 for (i = 0; i < pimage_num; i++) { sprintf (filename, "positive/%03d.png", i); img_org = cvloadimage (filename, CV_LOAD_IMAGE_GRAYSCALE); sample_img = cvcreateimage (cvsize (width, height), IPL_DEPTH_8U, 1); cvresize (img_org, sample_img); cvsmooth (sample_img, sample_img, CV_GAUSSIAN, 3, 0, 0, 0); for (ii = 0; ii < height; ii++) { for (jj = 0; jj < width; jj++) { data[i * image_dim + (ii * width) + jj] = float ((int) ((uchar) (sample_img->imagedata[ii * sample_img->widthstep + jj])) / 255.0); res[i] = 1; // (2) 네가티브샘플의읽기 j = i; for (i = j; i < j + nimage_num; i++) { sprintf (filename, "negative/%03d.jpg", i - j); img_org = cvloadimage (filename, CV_LOAD_IMAGE_GRAYSCALE); sample_img = cvcreateimage (cvsize (width, height), IPL_DEPTH_8U, 1); cvresize (img_org, sample_img); cvsmooth (sample_img, sample_img, CV_GAUSSIAN, 3, 0, 0, 0); for (ii = 0; ii < height; ii++) { for (jj = 0; jj < width; jj++) { data[i * image_dim + (ii * width) + jj] = float ((int) ((uchar) (sample_img->imagedata[ii * sample_img->widthstep + jj])) / 255.0); res[i] = 0; // (3)SVM 학습데이터와파라미터의초기화 cvinitmatheader (&data_mat, all_image_num, image_dim, CV_32FC1, data); 331

132 cvinitmatheader (&res_mat, all_image_num, 1, CV_32SC1, res); criteria = cvtermcriteria (CV_TERMCRIT_EPS, 1000, FLT_EPSILON); param = CvSVMParams (CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria); // (4)SVM 의학습과데이터의보존 svm.train (&data_mat, &res_mat, NULL, NULL, param); svm.save ("svm_image.xml"); cvreleaseimage (&img_org); cvreleaseimage (&sample_img); return 0; // (1) 포지티브샘플의읽기포지티브샘플이되는이미지군을읽어들여, 각픽셀의값을 float 형태의배열로변환한다. 여기에서는간단을위해서, 포지티브샘플 ("positive/ 디렉토리이하에있다 ")( 은 ) 는, 3 자리수연번의파일명의이미지로서미리준비되어있다고한다. 우선, 읽어들인각이미지를동일한사이즈 ( 여기에서는,28 30) 에리사이즈해, 노이즈의영향을경감하기위해서스무딩을실시한다. 다음에, 그이미지의각픽셀의휘도치 ( 여기서, 이미지는그레이스케일이미지로서읽히고있다 ) 를특징벡터로서이용하기위해서, 배열로변환한다. 즉,1 개의이미지에대한특징벡터는, 이미지의높이 이미지의폭, 이되어, 그것이샘플이미지의매수분만큼준비된다. 판별치로서 "1"( 을 ) 를이용하고있다. 또, 포지티브샘플로서 500 매의얼굴이미지 ( 거의정면, 기울기없음 ) 을이용하고있다. OpenCV 에는,haar-like 특징을이용한물체검출수법이실장되고있어그쪽을이용한얼굴검출이정도도처리속도도좋기때문에, 얼굴이미지를검출하는의미는별로없지만, 샘플의입수의하기쉬움이라고알기쉬움으로부터이번은얼굴이미지를이용한학습을실시했다. // (2) 네가티브샘플의읽기네가티브샘플이되는이미지군을읽어들여, 포지티브샘플과같게배열로변환한다. 판별치로서 "0"( 을 ) 를이용하고있다. 또, 네가티브샘플로서 1000 매의임의이미지 ( 얼굴이외의이미지 ) 을이용하고있다. // (3)SVM 학습데이터와파라미터의초기화샘플이미지의픽셀치의배열과판별치의배열을, 행렬로변환한다. 또,SVM 의학습을위한파라미터를초기화한다. 여기에서는, 꽤적당하게파라미터를지정해있으므로, 필요에따라서적절한파라미터를설정할필요가있다. // (4)SVM 의학습과데이터의보존포지티브, 네가티브샘플의픽셀치, 및지정된파라미터를이용하고,svm.train() 메소드에의해 SVM 의학습을실시한다. 샘플수는, 포지티브 500, 네가티브 1000, 특징벡터는,28 30=840 차원이다. 또, 학습되었다 SVM 의파라미터를,svm.save() 메소드에의해 XML 형식의파일로서보존한다. 이페이지의최초로도말한것처럼,save 및 load 의기능을이용하기위해서는,OpenCV 의소스를수정할필요가있다. 332

133 실행결과예 svm_image_xml.zip 하지만, 실제로보존되는파라미터의일례이다. 여기에서는,XML 형식을이용했지만,YAML 형식에서 보존하는것도가능하다. 이미지의각픽셀치를특징벡터로했다 SVM 에의한물체검출 학습되었다 SVM 파라미터를읽어들여, 주어진이미지중으로부터물체를검출한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ml.h> #include <stdio.h> int main (int argc, char **argv) { int i, j; int width = 28, height = 30; /* 샘플이미지사이즈 */ int image_dim = width * height; CvMat m; float a[image_dim]; float ret = -1.0; float scale; IplImage *src, *src_color, *src_tmp; int sx, sy, tw, th; int stepx = 3, stepy = 3; double steps = 1.2; int iterate; CvSVM svm = CvSVM (); // (1) 이미지의읽기 if (argc < 2 (src = cvloadimage (argv[1], CV_LOAD_IMAGE_GRAYSCALE)) == 0 (src_color = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) { return -1; 333

134 // (2)SVM 데이터의읽기 svm.load ("svm_image.xml"); /* 읽어들인이미지를부분이미지마다처리 */ cvinitmatheader (&m, 1, image_dim, CV_32FC1, NULL); tw = src->width; th = src->height; for (iterate = 0; iterate < 1; iterate++) { // (3) 이미지를축소해, 현재의부분이미지를행렬에변경 src_tmp = cvcreateimage (cvsize ((int) (tw / steps), (int) (th / steps)), IPL_DEPTH_8U, 1); cvresize (src, src_tmp); tw = src_tmp->width; th = src_tmp->height; for (sy = 0; sy <= src_tmp->height - height; sy += stepy) { for (sx = 0; sx <= src_tmp->width - width; sx += stepx) { for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { a[i * width + j] = float ((int) ((uchar) (src_tmp->imagedata[(i + sy) * src_tmp->widthstep + (j + sx)])) / 255.0); cvsetdata (&m, a, sizeof (float) * image_dim); // (4)SVM 에의한판정과결과의그리기 ret = svm.predict (&m); if ((int) ret == 1) { scale = (float) src->width / tw; cvrectangle (src_color, cvpoint ((int) (sx * scale), (int) (sy * scale)), cvpoint ((int) ((sx + width) * scale), (int) ((sy + height) * scale)), CV_RGB (255, 0, 0), 2); cvreleaseimage (&src_tmp); // (5) 검출결과이미지의표시 cvnamedwindow ("svm_predict", CV_WINDOW_AUTOSIZE); cvshowimage ("svm_predict", src_color); cvwaitkey (0); cvdestroywindow ("svm_predict"); 334

135 cvreleaseimage (&src); cvreleaseimage (&src_color); cvreleaseimage (&src_tmp); return 0; // (1) 이미지의읽기 SVM 에의한판별의대상이되는이미지를, 커멘드인수로지정된파일로부터읽어들인다. 처리는그레이스케일이미지에대해서행해지지만, 마지막결과표시를위해서칼라이미지도별도준비해둔다. // (2)SVM 데이터의읽기미리학습되었다 SVM 의파라미터를,svm.load() 메소드에의해파일 ( 여기에서는,"xvm_image.xml")( 으 ) 로부터읽어들인다. 이페이지의최초로도말한것처럼,save 및 load 의기능을이용하기위해서는,OpenCV 의소스를수정할필요가있다. // (3) 이미지를축소해, 현재의부분이미지를행렬에변경읽힌이미지를부분이미지마다처리하기위해서,stepx=3, stepy=3 픽셀마다,width height=28 30 의사이즈의부분이미지의픽셀치를배열에대입한다. 또,SVM( 을 ) 를적용하기위해서, 함수 cvsetdata() 에의해, 그배열을 1 (28 30) 의행렬의데이터로서세트한다. // (4)SVM 에의한판정과결과의그리기 svm.predict() 메소드에의해, 행렬에변환된부분이미지가, 어느클래스에속하는지를판별한다. 여기에서는, 전술의학습파라미터파일을이용하고있으므로, 처리대상이되는부분이미지가그학습된물체 ( 얼굴 ) 인지아닌지를판별하고있다. 얼굴이라면판별된부분이미지는, 붉은구형으로표시된다. 파라미터의최적화나, 특별한처리상의궁리 ( 처리영역의선정이나특징벡터의추출등 ) 를하고있는것도없기때문에, 검출정도는높지않다. 게다가이미지전체에대해서처리를행하는경우는, 특징벡터가큰일도있어, 상당한처리시간이필요하다. 또, 이번은,iterate 변수에의한루프를 1 번밖에가서않지만, 이미지중으로부터다른크기의물체를검출하고싶은경우에는, 원이미지를수시축소 ( 여기에서는,1/steps=1/1.2) 해같은처리를실시한다. // (5) 검출결과이미지의표시검출결과적으로붉은구형이그리기된이미지를표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 위제트 ( 컨트롤 ) OpenCV 그리고이용할수있는위제트 ( 컨트롤 ) 는, 이미지를표시하기위한윈도우, 및트럭바뿐이다. 복잡한 G 335

136 UI디자인이나이벤트를적절히읽어날린다고하는처리가어렵고, 카메라캡쳐냄새나도해상도의변경이나 frame r ate의설정등에난점하지만있으므로, 간단한테스트프로그램이외에서는, 다른라이브러리를이용하는편이무난하다. 샘플 트럭바의이용 cvcreatetrackbar, cvgettrackbarpos, cvsettrackbarpos 트럭바의작성, 그위치의취득과설정 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <stdio.h> /* 글로벌변수 */ CvFont font; IplImage *img = 0; /* 프로토타입선언 */ void on_trackbar1 (int val); void on_trackbar2 (int val); int main (int argc, char *argv[]) { // (1) 이미지영역을확보해, 초기화한다 img = cvcreateimage (cvsize (400, 200), IPL_DEPTH_8U, 3); cvzero (img); cvinitfont (&font, CV_FONT_HERSHEY_DUPLEX, 1.0, 1.0); // (2) 윈도우, 및트럭바를작성한다 cvnamedwindow ("Image", CV_WINDOW_AUTOSIZE); cvcreatetrackbar ("Trackbar1", "Image", 0, 100, on_trackbar1); cvcreatetrackbar ("Trackbar2", "Image", 0, 100, on_trackbar2); cvshowimage ("Image", img); cvwaitkey (0); cvdestroywindow ("Image"); cvreleaseimage (&img); return 0; 336

137 /* 콜백함수 */ void on_trackbar1 (int val) { char str[64]; // (3) 트럭바 2( 을 ) 를, 트럭바 1 에동기시킨다 cvsettrackbarpos ("Trackbar2", "Image", val); // (4) 트럭바 1 의값을그리기한다 cvrectangle (img, cvpoint (0, 0), cvpoint (400, 50), cvscalar (0), CV_FILLED); snprintf (str, 64, "%d", val); cvputtext (img, str, cvpoint (15, 30), &font, CV_RGB (0, 200, 100)); cvshowimage ("Image", img); void on_trackbar2 (int val) { char str[64]; int pos1, pos2; // (5) 트럭바 2 의이동범위를, 트럭바 1 의값 ±20( 으 ) 로한정한다 pos1 = cvgettrackbarpos ("Trackbar1", "Image"); if (pos1 > val) pos2 = pos1-20 < val? val : pos1-20; else pos2 = pos > val? val : pos1 + 20; cvsettrackbarpos ("Trackbar2", "Image", pos2); // (6) 트럭바 2 의값을그리기한다 cvrectangle (img, cvpoint (0, 50), cvpoint (400, 100), cvscalar (0), CV_FILLED); snprintf (str, 64, "%d", val); cvputtext (img, str, cvpoint (15, 80), &font, CV_RGB (200, 200, 0)); cvshowimage ("Image", img); // (1) 이미지영역을확보해, 초기화한다이미지영역을확보해, 초기화한다. 또, 후에결과의문자열을그리기하기위한폰트구조체를초기화한다. // (2) 윈도우, 및트럭바를작성한다윈도우를작성후, 함수 cvcreatetrackbar() 에의해, 윈도우상에트럭바를추가한다. 트럭바작성시에, 트럭바이벤트에대한콜백함수 (on_trackbar1,on_trackbar2)( 을 ) 를설정한다. 337

138 // (3) 트럭바 2( 을 ) 를, 트럭바 1 에동기시킨다함수 cvsettrackbarpos()( 을 ) 를이용하고, 트럭바 2 의값을, 트럭바 1 의현재의값과동기시킨다. // (4) 트럭바 1 의값을그리기한다트럭바 1 의현재의값을, 이미지영역상부에그리기한다. // (5) 트럭바 2 의이동범위를, 트럭바 1 의값 ±20( 으 ) 로한정한다함수 cvgettrackbarpos()( 을 ) 를이용하고, 트럭바 1 의현재의값을취득해, 그값의 ±20 의범위내에서트럭바 2 하지만움직이도록 ( 듯이 ) 설정한다. // (6) 트럭바 2 의값을그리기한다트럭바 2 의현재의값을, 이미지영역하부에그리기한다. 실행결과예 이벤트 OpenCV 그럼, 윈도우상에서발생한마우스이벤트, 및윈도우가액티브한상태로밀린키코드를취득할수있다. 샘플 마우스이벤트의취득 cvsetmousecallback 마우스이벤트를취득하고, 윈도우에표시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <stdio.h> /* 글로벌변수 */ CvFont font; IplImage *img = 0; /* 프로토타입선언 */ void on_mouse (int event, int x, int y, int flags, void *param); int main (int argc, char *argv[]) { int c; 338

139 // (1) 이미지영역을확보해, 초기화한다 img = cvcreateimage (cvsize (640, 480), IPL_DEPTH_8U, 3); cvzero (img); cvinitfont (&font, CV_FONT_HERSHEY_DUPLEX, 0.4, 0.4); // (2) 윈도우를작성해, 마우스이벤트에대한콜백함수를등록한다 cvnamedwindow ("Image", CV_WINDOW_AUTOSIZE); cvsetmousecallback ("Image", on_mouse); cvshowimage ("Image", img); // (3)'Esc' 키가밀렸을경우에종료한다 while (1) { c = cvwaitkey (0); if (c == '\x1b') return 1; cvdestroywindow ("Image"); cvreleaseimage (&img); return 0; /* 콜백함수 */ void on_mouse (int event, int x, int y, int flags, void *param = NULL) { char str[64]; static int line = 0; const int max_line = 15, w = 15, h = 30; // (4) 마우스이벤트를취득 switch (event) { case CV_EVENT_MOUSEMOVE: snprintf (str, 64, "(%d,%d) %s", x, y, "MOUSE_MOVE"); break; case CV_EVENT_LBUTTONDOWN: snprintf (str, 64, "(%d,%d) %s", x, y, "LBUTTON_DOWN"); break; case CV_EVENT_RBUTTONDOWN: snprintf (str, 64, "(%d,%d) %s", x, y, "RBUTTON_DOWN"); break; case CV_EVENT_MBUTTONDOWN: snprintf (str, 64, "(%d,%d) %s", x, y, "MBUTTON_DOWN"); break; 339

140 case CV_EVENT_LBUTTONUP: snprintf (str, 64, "(%d,%d) %s", x, y, "LBUTTON_UP"); break; case CV_EVENT_RBUTTONUP: snprintf (str, 64, "(%d,%d) %s", x, y, "RBUTTON_UP"); break; case CV_EVENT_MBUTTONUP: snprintf (str, 64, "(%d,%d) %s", x, y, "MBUTTON_UP"); break; case CV_EVENT_LBUTTONDBLCLK: snprintf (str, 64, "(%d,%d) %s", x, y, "LBUTTON_DOUBLE_CLICK"); break; case CV_EVENT_RBUTTONDBLCLK: snprintf (str, 64, "(%d,%d) %s", x, y, "RBUTTON_DOUBLE_CLICK"); break; case CV_EVENT_MBUTTONDBLCLK: snprintf (str, 64, "(%d,%d) %s", x, y, "MBUTTON_DOUBLE_CLICK"); break; // (5)mouse button, 수식키를취득 if (flags & CV_EVENT_FLAG_LBUTTON) strncat (str, " + LBUTTON", 64); if (flags & CV_EVENT_FLAG_RBUTTON) strncat (str, " + RBUTTON", 64); if (flags & CV_EVENT_FLAG_MBUTTON) strncat (str, " + MBUTTON", 64); if (flags & CV_EVENT_FLAG_CTRLKEY) strncat (str, " + CTRL", 64); if (flags & CV_EVENT_FLAG_SHIFTKEY) strncat (str, " + SHIFT", 64); if (flags & CV_EVENT_FLAG_ALTKEY) strncat (str, " + ALT", 64); // (6) 마우스좌표, 이벤트, 수식키등을이미지에그리기, 표시 if (line > max_line) { cvgetrectsubpix (img, img, cvpoint2d32f ( , h)); cvputtext (img, str, cvpoint (w, 20 + h * max_line), &font, CV_RGB (0, 200, 100)); else { cvputtext (img, str, cvpoint (w, 20 + h * line), &font, CV_RGB (0, 200, 100)); line++; cvshowimage ("Image", img); 340

141 // (1) 이미지영역을확보해, 초기화한다이미지영역을확보해, 초기화한다. 또, 후에결과의문자열을그리기하기위한폰트구조체를초기화한다. // (2) 윈도우를작성해, 마우스이벤트에대한콜백함수를등록한다초기화한이미지를표시해, 함수 cvsetmousecallback()( 을 ) 를이용하고, 마우스이벤트에대한콜백함수 (on_mouse)( 을 ) 를등록한다. // (3)'Esc' 키가밀렸을경우에종료한다 "Esc" 키가밀렸을경우에종료한다.'\x1b'( 은 ) 는,"Esc" 키를나타내지만, 이문자코드는 "27" 그래서, if( c == 27 ) ( 이 ) 라고해도좋다. 또, 그외의키의경우는, 이하와같이지정하면좋다. if( c == 'q' ) // (4) 마우스이벤트를취득마우스이벤트를취득한다. 취득할수있는마우스이벤트는, 마우스의이동과좌, 오른쪽, 중앙버튼의 DOWN,UP, 더블클릭이다. 덧붙여서, 더블클릭을실시했을때의마우스이벤트는, 버튼 DOWN, 버튼 UP, 버튼 DOWN, 버튼더블클릭, 버튼 UP 의순서로발생한다. // (5)mouse button, 수식키를취득이벤트시에밀리고있는 mouse button, 수식키 (CTRL,ALT,SHIFT)( 을 ) 를취득한다. 이러한플래그는, 각이벤트플래그의논리합으로표현된다. 또, mouse button 플래그는, 버튼 DOWN 시간에는발생하지않지만, 버튼 UP 시간에는발생한다. // (6) 마우스좌표, 이벤트, 수식키등을이미지에그리기, 표시현재의마우스좌표, 마우스이벤트, 수식키모두를이미지에그리기해, 표시한다. 또, 함수 cvgetrectsubpix() ( 을 ) 를이용하고, 화면을스크롤시킨다. 실행결과예 카메라 OpenCV 그럼, 실제의카메라나동이미지파일로부터캡쳐를실시하거나반대로이미지열을동이미지파일로서써내 거나하는것이가능하다. 카메라로부터의이미지캡쳐 cvcapturefromcam, cvqueryframe 지정된번호의카메라로부터이미지를캡쳐해표시한다 341

142 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ctype.h> int main (int argc, char **argv) { CvCapture *capture = 0; IplImage *frame = 0; double w = 320, h = 240; int c; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다 if (argc == 1 (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvcreatecameracapture (argc == 2? argv[1][0] - '0' : 0); /* 이설정은, 이용하는카메라에의존한다 */ // (2) 캐프체사이즈를설정한다. cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_WIDTH, w); cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_HEIGHT, h); cvnamedwindow ("Capture", CV_WINDOW_AUTOSIZE); // (3) 카메라로부터이미지를캡쳐한다 while (1) { frame = cvqueryframe (capture); cvshowimage ("Capture", frame); c = cvwaitkey (10); if (c == '\x1b') break; cvreleasecapture (&capture); cvdestroywindow ("Capture"); return 0; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다커멘드인수로서주어진번호의카메라에대한, 캡쳐구조체 CvCapture( 을 ) 를작성한다. 인수가지정되지않는경우는, 디폴트의값이다 "0" 하지만이용된다. 342

143 // (2) 캐프체사이즈를설정한다함수 cvsetcaptureproperty() 에의해, 캡쳐를행할때의화면사이즈 ( 폭과높이 ) 를지정한다. 다만, 실제의카메라가서포트하고있지않는캐프체사이즈는지정할수없다. // (3) 카메라로부터이미지를캡쳐한다루프블록중에함수 cvqueryframe() ( 을 ) 를호출하는것으로, 실제로캡쳐를실시해, 그것을표시한다. 캡쳐안에 "Esc" 키가밀렸을경우는, 종료한다. 실행결과예 CLOSE 카메라, 비디오파일 OpenCV그럼, 실제의카메라나동이미지파일로부터캡쳐를실시하거나반대로이미지열을동이미지파일로서써내거나하는것이가능하다. 카메라로부터의이미지캡쳐 cvcreatecameracapture, cvqueryframe 지정된번호의카메라로부터이미지를캡쳐해표시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ctype.h> int main (int argc, char **argv) { CvCapture *capture = 0; IplImage *frame = 0; double w = 320, h = 240; int c; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다 if (argc == 1 (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvcreatecameracapture (argc == 2? argv[1][0] - '0' : 0); /* 이설정은, 이용하는카메라에의존한다 */ // (2) 캐프체사이즈를설정한다. 343

144 cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_WIDTH, w); cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_HEIGHT, h); cvnamedwindow ("Capture", CV_WINDOW_AUTOSIZE); // (3) 카메라로부터이미지를캡쳐한다 while (1) { frame = cvqueryframe (capture); cvshowimage ("Capture", frame); c = cvwaitkey (10); if (c == '\x1b') break; cvreleasecapture (&capture); cvdestroywindow ("Capture"); return 0; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다함수 cvcreatecameracapture()( 을 ) 를이용하고, 커멘드인수로서주어진번호의카메라에대한캡쳐구조체 CvCapture( 을 ) 를작성한다. 인수가지정되지않는경우는, 디폴트의값이다 "0" 하지만이용된다. OpenCV 의샘플프로그램에서는,cvCreateCameraCapture 대신에, cvcapturefromcam 하지만이용되고있지만, 실제는 highgui.h 안에서, #define cvcapturefromcam cvcreatecameracapture ( 와 ) 과같이정의되고있어이러한함수는등가이다. 여기에서는, 메뉴얼에따라함수 cvcreatecameracapture ( 을 ) 를 이용한다. // (2) 캐프체사이즈를설정한다함수 cvsetcaptureproperty() 에의해, 캡쳐를행할때의화면사이즈 ( 폭과높이 ) 를지정한다. 다만, 실제의카메라가서포트하고있지않는캐프체사이즈는지정할수없다. // (3) 카메라로부터이미지를캡쳐한다루프블록중에함수 cvqueryframe() ( 을 ) 를호출하는것으로, 실제로캡쳐를실시해, 그것을표시한다. 캡쳐안에 "Esc" 키가밀렸을경우는, 종료한다. 실행결과예 CLOSE 344

145 동영상으로서파일에써낸다 cvcreatevideowriter, cvwriteframe, cvreleasevideowriter 카메라로부터캡쳐한프레임을, 차례차례파일에써낸다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include <ctype.h> #include <stdio.h> int main (int argc, char **argv) { CvCapture *capture = 0; IplImage *frame = 0; CvVideoWriter *vw; double w = 320, h = 240; int c, num = 0; CvFont font; char str[64]; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다 if (argc == 1 (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvcapturefromcam (argc == 2? argv[1][0] - '0' : 0); // (2) 캐프체사이즈를설정한다 cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_WIDTH, w); cvsetcaptureproperty (capture, CV_CAP_PROP_FRAME_HEIGHT, h); // (3) 비디오라이터구조체를작성한다 vw = cvcreatevideowriter ("cap.avi", CV_FOURCC ('X', 'V', 'I', 'D'), 15, cvsize ((int) w, (int) h)); cvinitfont (&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7); cvnamedwindow ("Capture", CV_WINDOW_AUTOSIZE); // (4) 카메라로부터이미지를캡쳐해, 파일에써낸다 while (1) { frame = cvqueryframe (capture); snprintf (str, 64, "%03d[frame]", num); 345

146 cvputtext (frame, str, cvpoint (10, 20), &font, CV_RGB (0, 255, 100)); cvwriteframe (vw, frame); cvshowimage ("Capture", frame); num++; c = cvwaitkey (10); if (c == '\x1b') break; // (5) 기입을종료해, 구조체를해방한다 cvreleasevideowriter (&vw); cvreleasecapture (&capture); cvdestroywindow ("Capture"); return 0; // (1) 커멘드인수에의해서지정된번호의카메라에대한캡쳐구조체를작성한다전술의캡쳐샘플과같게, 구조체를작성한다. // (2) 캐프체사이즈를설정한다전술의캡쳐샘플과같게, 캐프체사이즈를설정한다. // (3) 비디오라이터구조체를작성한다파일에써내기위한비디오라이터구조체를작성한다. 인수에는, 순서에, ( 파일명, 코덱지정자, frame rate, 사이즈 )( 을 ) 를지정한다. 여기에서는, 코덱의지정에,CV_FOURCC('X','V','I','D')(XVID)( 을 ) 를이용하고있지만, 레퍼런스메뉴얼에있도록 ( 듯이 ),CV_FOURCC('M','J','P','G')( 모션 JPEG)( 이 ) 나 CV_FOURCC('P','I','M','1')(MPEG- 1)( 을 ) 를이용할수도있다. 비압축으로의보존을희망하는경우는,CV_FOURCC('D','I','B',' ')( 을 ) 를지정하면좋다. 물론, 이용되는코덱이인스톨되고있을필요가있다. 또,MacOSX 그럼, 파일명을풀패스로지정하든가, 혹은,touch 커멘드등에서미리파일작성해둘필요가있는것같다. // (4) 카메라로부터이미지를캡쳐해, 파일에써낸다카메라로부터이미지를캡쳐해, 그이미지에대해서문자열을쓴후에, 함수 cvwrtiteframe() 에의해서차례차례파일에써낸다. // (5) 기입을종료해, 구조체를해방한다 "Esc" 키로루프를빠진후에는, 함수 cvreleasevideowriter() 에의해서, 기입을종료시켜, 구조체를해방한다. 실행결과예 346

147 CLOSE 라벨링 OpenCV자체에는 (OpenCV 시점에있고 ) 라벨링을실시하는함수는존재하지않지만, OpenCV Library Wiki그리고소개되고있다 "Blob extraction library"( 을 ) 를이용하고, 이미지의라벨링을실시할수있다. 이라이브러리는,MIL (Matrox Imaging Library) 에있어서의,MIL blob detection 모듈의기능을대체하는것을,OpenCV( 을 ) 를이용해실장되어있다. Win32환경에서동작하는것과Linux환경에서동작하는것이, 각각배포되고있다. 디폴트에서는, 스태틱라이브러리 (.lib 혹은.a) 의형식에서제공되지만, 소스로부터다이나믹링크 ( 혹은, 공유 ) 라이브러리를작성할수도있다. blob( 이 ) 란, 데이타베이스로말할곳의형태가아니고," 작은덩어리 " 그렇다고한다의미로이용되고있다 ( 컴퓨터그래픽스나비전의세계에서는, 메타보르를표현할때에이용되기도한다 ). 즉, 이미지중으로부터개별의작은덩어리 (blob)( 을 ) 를추출하는작업 ( 라벨링) ( 을 ) 를행하기위한클래스이며, 이하의특징이있다 ( 자세한것은, 상술의홈페이지를참조하는것 ). 2 치이미지혹은그레이스케일이미지로부터,8 근방에있어서연결되어있는영역을추출한다. 이러한영역은 blob ( 으 ) 로서참조된다. 이미지중의주목오브젝트를얻기위해서, 취득했다 blob 집합에대해서필터링을실시한다. 이처리는,CBlobResult 의 Filter 메소드에의해서실현된다. 샘플 라벨링 CBlobResult 입력이미지에대해서라벨링을실시해, 면적과주위장을표시한다 표시의변환 샘플코드 #include <cv.h> #include <highgui.h> #include "BlobResult.h" int main (int argc, char **argv) { int i; IplImage *src_img = 0, *dst_img = 0; CBlobResult blobs; CBlob blob; CvPoint p1, p2; CvFont font; char text[64]; CBlob blobwithbiggestperimeter, blobwithlessarea; // (1) 이미지를읽어들인다 if (argc!= 2 (src_img = cvloadimage (argv[1], CV_LOAD_IMAGE_COLOR)) == 0) 347

148 return -1; dst_img = cvcreateimage (cvsize (src_img->width, src_img->height), IPL_DEPTH_8U, 1); cvcvtcolor (src_img, dst_img, CV_BGR2GRAY); cvinitfont (&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA); // (2) 라벨링을실시한다 blobs = CBlobResult (dst_img, NULL, 100, false); // (3) 면적으로필터링 blobs.filter (blobs, B_INCLUDE, CBlobGetArea (), B_INSIDE, 1000, 50000); for (i = 0; i < blobs.getnumblobs (); i++) { // (4) 각 blob 의최소포함구형 ( 기울기없음 ) 을요구한다 blob = blobs.getblob (i); p1.x = (int) blob.minx (); p1.y = (int) blob.miny (); p2.x = (int) blob.maxx (); p2.y = (int) blob.maxy (); // (5) 각 blob 의포함구형, 인덱스, 면적, 주장을그리기 cvrectangle (src_img, p1, p2, CV_RGB (255, 0, 0), 2, 8, 0); sprintf (text, "[%d] %d,%d", blob.label (), (int) blob.area, (int) blob.perimeter ()); cvputtext (src_img, text, cvpoint (p1.x, p1.y - 5), &font, cvscalarall (255)); // (6) 최대의주장을가진다 blob( 을 ) 를, 녹색으로전부칠한다 blobs.getnthblob (CBlobGetPerimeter (), 0, blobwithbiggestperimeter); blobwithbiggestperimeter.fillblob (src_img, CV_RGB (0, 255, 0)); // (7) 최소의면적을가진다 blob( 을 ) 를, 청색으로전부칠한다 blobs.getnthblob (CBlobGetArea (), blobs.getnumblobs () - 1, blobwithlessarea); blobwithlessarea.fillblob (src_img, CV_RGB (0, 0, 255)); // (8) 이미지를표시, 키가밀렸을때에종료 cvnamedwindow ("Labeling", CV_WINDOW_AUTOSIZE); cvshowimage ("Labeling", src_img); cvwaitkey (0); cvdestroywindow ("Labeling"); cvreleaseimage (&src_img); cvreleaseimage (&dst_img); return 0; 348

149 // (1) 이미지를읽어들인다입력이미지를, 칼라이미지로서읽어들인다. 또, 라벨링대상이되는이미지는, 그레이스케일혹은 2 치이미지일필요가있으므로, 이미지의변환을실시한다. // (2) 라벨링을실시한다 1 번째의인수에, 라벨링대상이되는이미지를준다. 2 번째의인수는, 마스크이미지,3 번째의인수는,blob 인가그이외인지를결정하는반응을일으키는최소의물리량 (0-255), 4 번째의인수는, 각 blob 모멘트의계산을실시하는지아닌지의플래그이다. 반환값으로서오브젝트 CBlobResult( 을 ) 를돌려준다. // (3) 면적으로필터링 Filter() 메소드는, 지정된프롭퍼티에의해,blob( 을 ) 를필터링한다. 1 번째의인수는, 전의라벨링처리로얻을수있던오브젝트. 2 번째의인수는, 필터링작업 ( 조건에들어맞았다 blob( 을 ) 를, 포함한다 "B_INCLUDE" 인가, 포함하지않는다 "B_EXCLUDE" 인가 ). 3 번째의인수는, 필터링에이용하는프롭퍼티를결정하는함수. 4 번째의인수는, 필터링조건 (B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL,B_LESS_OR_EQU AL,B_INSIDE,B_OUTSIDE) 5 번째와 6 번째의인수는, 하한및상한 ( 이것은, 지정되지않는것도있다 ) 이되는반응을일으키는최소의물리량. 이번은, 면적이, [pixel] 의범위에있다 blob 만을남기고있다. // (4) 각 blob 의최소포함구형 ( 기울기없음 ) 을요구한다각 blob 에두어최소, 최대의 x,y 좌표를가지는픽셀을참조하는것으로, 그 blob( 을 ) 를포함하는구형을요구한다. // (5) 각 blob 의포함구형, 인덱스, 면적, 주위장을그리기각 blob 의포함구형, 및 blob 인덱스, 면적, 주위장을그리기한다. 여기서,blob 의주위장은,blob 의경계가되는픽셀의개수이며, Edges() 메소드에의해참조 (CvSeq* 형태 ) 할수있다. 다만, 이러한픽셀은, 래스터스캔되었을때의주사순서에줄지어있을뿐 ( 만큼 ) 이므로, 그대로윤곽을그리는일은할수없다. // (6) 최대의주위장을가진다 blob( 을 ) 를, 녹색으로전부칠한다 CBlobGetPerimeter() 에의해서얻을수있었다 blob 의주위의길이가, 최대 (0 번째 ) 의것을, CBlob 오브젝트로서취득해,FillBlob() 메소드에의해서전부칠한다. // (7) 최소의면적을가진다 blob( 을 ) 를, 청색으로전부칠한다 (6)( 와 ) 과같게,CBlobGetArea() 에의해서얻을수있었다 blob 의면적이, 최소 (blobs.getnumblobs()-1 번째, 즉최후 ) 의것을, CBlob 오브젝트로서취득해,FillBlob() 메소드에의해서전부칠한다. // (8) 이미지를표시, 키가밀렸을때에종료이미지를실제로표시해, 무엇인가키가밀릴때까지기다린다. 실행결과예 [ 좌 ] 처리전 [ 우 ] 처리후 349

150 350

Microsoft PowerPoint - IP11.pptx

Microsoft PowerPoint - IP11.pptx 열한번째강의카메라 1/43 1/16 Review 2/43 2/16 평균값 중간값 Review 3/43 3/16 캐니에지추출 void cvcanny(const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size = 3); aperture_size = 3 aperture_size

More information

종합설계중간보고서 _1 - 네트워크기반수배차량인식프로그램 이태화 이재형

종합설계중간보고서 _1 - 네트워크기반수배차량인식프로그램 이태화 이재형 종합설계중간보고서 _1 - 네트워크기반수배차량인식프로그램 200511347 이태화 200811447 이재형 목차 1. 개발요구사항 2. 필요기술및사용라이브러리 3. 동작구조 4. 주요개발내용 5. 개발예정사항 1. 개발요구사항 1. 차량번호판이찍힌스크린샷을분석하여번호판인식 2. 인식된번호판을분석하여번호획득, 사용자로부터입력받은번호와비교 3. 두번호가일치하면현재영상전송모듈수행

More information

chap 5: Trees

chap 5: Trees 5. Threaded Binary Tree 기본개념 n 개의노드를갖는이진트리에는 2n 개의링크가존재 2n 개의링크중에 n + 1 개의링크값은 null Null 링크를다른노드에대한포인터로대체 Threads Thread 의이용 ptr left_child = NULL 일경우, ptr left_child 를 ptr 의 inorder predecessor 를가리키도록변경

More information

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션 System Software Experiment 1 Lecture 5 - Array Spring 2019 Hwansoo Han (hhan@skku.edu) Advanced Research on Compilers and Systems, ARCS LAB Sungkyunkwan University http://arcs.skku.edu/ 1 배열 (Array) 동일한타입의데이터가여러개저장되어있는저장장소

More information

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi

[ 마이크로프로세서 1] 2 주차 3 차시. 포인터와구조체 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Functi 2 주차 3 차시포인터와구조체 학습목표 1. C 언어에서가장어려운포인터와구조체를설명할수있다. 2. Call By Value 와 Call By Reference 를구분할수있다. 학습내용 1 : 함수 (Function) 1. 함수의개념 입력에대해적절한출력을발생시켜주는것 내가 ( 프로그래머 ) 작성한명령문을연산, 처리, 실행해주는부분 ( 모듈 ) 자체적으로실행되지않으며,

More information

Microsoft PowerPoint - ch07 - 포인터 pm0415

Microsoft PowerPoint - ch07 - 포인터 pm0415 2015-1 프로그래밍언어 7. 포인터 (Pointer), 동적메모리할당 2015 년 4 월 4 일 교수김영탁 영남대학교공과대학정보통신공학과 (Tel : +82-53-810-2497; Fax : +82-53-810-4742 http://antl.yu.ac.kr/; E-mail : ytkim@yu.ac.kr) Outline 포인터 (pointer) 란? 간접참조연산자

More information

Microsoft PowerPoint - ch10 - 이진트리, AVL 트리, 트리 응용 pm0600

Microsoft PowerPoint - ch10 - 이진트리, AVL 트리, 트리 응용 pm0600 균형이진탐색트리 -VL Tree delson, Velskii, Landis에의해 1962년에제안됨 VL trees are balanced n VL Tree is a binary search tree such that for every internal node v of T, the heights of the children of v can differ by at

More information

사용방법은 (cvwavelet GrayScaleImage method) 로 GrayScaleImage 는 cvcvtcolor 함수를이 용하여 Gray 영상으로변환한리턴값을이용한다. method 는웨이블릿에사용할알고리즘을말하는데 하웨이블릿만구현되어있으므로 0 을입력한다.

사용방법은 (cvwavelet GrayScaleImage method) 로 GrayScaleImage 는 cvcvtcolor 함수를이 용하여 Gray 영상으로변환한리턴값을이용한다. method 는웨이블릿에사용할알고리즘을말하는데 하웨이블릿만구현되어있으므로 0 을입력한다. 19) cvsmooth 저주파통과필터링은일반적으로처리할영상성분중에화소값의변화율이낮은저주파성분은통과시키고, 화소값의변화율이큰고주파성분을차단시키는것을가리킨다. 저주파통과필터링을수행한영상은윤곽선부분을이루는고주파성분이제거되어전체영상이부드러워지게되는데이현상을가리켜블러링 (blurring) 이라한다. 잡음 (noise) 를제거하는것이가능하지만, 영상이부드러워지고희미해지기때문에영상대비가악화된다는단점이있다.

More information

금오공대 컴퓨터공학전공 강의자료

금오공대 컴퓨터공학전공 강의자료 C 프로그래밍프로젝트 Chap 14. 포인터와함수에대한이해 2013.10.09. 오병우 컴퓨터공학과 14-1 함수의인자로배열전달 기본적인인자의전달방식 값의복사에의한전달 val 10 a 10 11 Department of Computer Engineering 2 14-1 함수의인자로배열전달 배열의함수인자전달방식 배열이름 ( 배열주소, 포인터 ) 에의한전달 #include

More information

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx

Microsoft PowerPoint - chap02-C프로그램시작하기.pptx #include int main(void) { int num; printf( Please enter an integer "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; } 1 학습목표 을 작성하면서 C 프로그램의

More information

OCW_C언어 기초

OCW_C언어 기초 초보프로그래머를위한 C 언어기초 4 장 : 연산자 2012 년 이은주 학습목표 수식의개념과연산자및피연산자에대한학습 C 의알아보기 연산자의우선순위와결합방향에대하여알아보기 2 목차 연산자의기본개념 수식 연산자와피연산자 산술연산자 / 증감연산자 관계연산자 / 논리연산자 비트연산자 / 대입연산자연산자의우선순위와결합방향 조건연산자 / 형변환연산자 연산자의우선순위 연산자의결합방향

More information

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각

JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 (   ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각 JAVA 프로그래밍실습 실습 1) 실습목표 - 메소드개념이해하기 - 매개변수이해하기 - 새메소드만들기 - Math 클래스의기존메소드이용하기 ( http://java.sun.com/javase/6/docs/api ) 문제 - 직사각형모양의땅이있다. 이땅의둘레, 면적과대각선의길이를계산하는메소드들을작성하라. 직사각형의가로와세로의길이는주어진다. 대각선의길이는 Math클래스의적절한메소드를이용하여구하라.

More information

BMP 파일 처리

BMP 파일 처리 BMP 파일처리 김성영교수 금오공과대학교 컴퓨터공학과 학습내용 영상반전프로그램제작 2 Inverting images out = 255 - in 3 /* 이프로그램은 8bit gray-scale 영상을입력으로사용하여반전한후동일포맷의영상으로저장한다. */ #include #include #define WIDTHBYTES(bytes)

More information

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2

목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2 제 8 장. 포인터 목차 포인터의개요 배열과포인터 포인터의구조 실무응용예제 C 2 포인터의개요 포인터란? 주소를변수로다루기위한주소변수 메모리의기억공간을변수로써사용하는것 포인터변수란데이터변수가저장되는주소의값을 변수로취급하기위한변수 C 3 포인터의개요 포인터변수및초기화 * 변수데이터의데이터형과같은데이터형을포인터 변수의데이터형으로선언 일반변수와포인터변수를구별하기위해

More information

쉽게 풀어쓴 C 프로그래밍

쉽게 풀어쓴 C 프로그래밍 제 5 장생성자와접근제어 1. 객체지향기법을이해한다. 2. 클래스를작성할수있다. 3. 클래스에서객체를생성할수있다. 4. 생성자를이용하여객체를초기화할수 있다. 5. 접근자와설정자를사용할수있다. 이번장에서만들어볼프로그램 생성자 생성자 (constructor) 는초기화를담당하는함수 생성자가필요한이유 #include using namespace

More information

untitled

untitled int i = 10; char c = 69; float f = 12.3; int i = 10; char c = 69; float f = 12.3; printf("i : %u\n", &i); // i printf("c : %u\n", &c); // c printf("f : %u\n", &f); // f return 0; i : 1245024 c : 1245015

More information

Chapter 4. LISTS

Chapter 4. LISTS 6. 동치관계 (Equivalence Relations) 동치관계 reflexive, symmetric, transitive 성질을만족 "equal to"(=) 관계는동치관계임. x = x x = y 이면 y = x x = y 이고 y = z 이면 x = z 동치관계를이용하여집합 S 를 동치클래스 로분할 동일한클래스내의원소 x, y 에대해서는 x y 관계성립

More information

윤성우의 열혈 TCP/IP 소켓 프로그래밍

윤성우의 열혈 TCP/IP 소켓 프로그래밍 C 프로그래밍프로젝트 Chap 22. 구조체와사용자정의자료형 1 2013.10.10. 오병우 컴퓨터공학과 구조체의정의 (Structure) 구조체 하나이상의기본자료형을기반으로사용자정의자료형 (User Defined Data Type) 을만들수있는문법요소 배열 vs. 구조체 배열 : 한가지자료형의집합 구조체 : 여러가지자료형의집합 사용자정의자료형 struct

More information

Microsoft PowerPoint - chap06-1Array.ppt

Microsoft PowerPoint - chap06-1Array.ppt 2010-1 학기프로그래밍입문 (1) chapter 06-1 참고자료 배열 박종혁 Tel: 970-6702 Email: jhpark1@snut.ac.kr 한빛미디어 출처 : 뇌를자극하는 C프로그래밍, 한빛미디어 -1- 배열의선언과사용 같은형태의자료형이많이필요할때배열을사용하면효과적이다. 배열의선언 배열의사용 배열과반복문 배열의초기화 유연성있게배열다루기 한빛미디어

More information

adfasdfasfdasfasfadf

adfasdfasfdasfasfadf C 4.5 Source code Pt.3 ISL / 강한솔 2019-04-10 Index Tree structure Build.h Tree.h St-thresh.h 2 Tree structure *Concpets : Node, Branch, Leaf, Subtree, Attribute, Attribute Value, Class Play, Don't Play.

More information

Microsoft PowerPoint - chap03-변수와데이터형.pptx

Microsoft PowerPoint - chap03-변수와데이터형.pptx #include int main(void) { int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num %d\n", num); return 0; } 1 학습목표 의 개념에 대해 알아본다.

More information

Chapter 4. LISTS

Chapter 4. LISTS C 언어에서리스트구현 리스트의생성 struct node { int data; struct node *link; ; struct node *ptr = NULL; ptr = (struct node *) malloc(sizeof(struct node)); Self-referential structure NULL: defined in stdio.h(k&r C) or

More information

비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2

비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2 비트연산자 1 1 비트와바이트 비트와바이트 비트 (Bit) : 2진수값하나 (0 또는 1) 를저장할수있는최소메모리공간 1비트 2비트 3비트... n비트 2^1 = 2개 2^2 = 4개 2^3 = 8개... 2^n 개 1 바이트는 8 비트 2 2 진수법! 2, 10, 16, 8! 2 : 0~1 ( )! 10 : 0~9 ( )! 16 : 0~9, 9 a, b,

More information

Microsoft PowerPoint - chap11-포인터의활용.pptx

Microsoft PowerPoint - chap11-포인터의활용.pptx #include int main(void) int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; 1 학습목표 포인터를 사용하는 다양한 방법에

More information

C 언어 프로그래밊 과제 풀이

C 언어 프로그래밊 과제 풀이 과제풀이 (1) 홀수 / 짝수판정 (1) /* 20094123 홍길동 20100324 */ /* even_or_odd.c */ /* 정수를입력받아홀수인지짝수인지판정하는프로그램 */ int number; printf(" 정수를입력하시오 => "); scanf("%d", &number); 확인 주석문 가필요한이유 printf 와 scanf 쌍

More information

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D>

<4D F736F F F696E74202D20B8AEB4AABDBA20BFC0B7F920C3B3B8AEC7CFB1E22E BC8A3C8AF20B8F0B5E55D> 리눅스 오류처리하기 2007. 11. 28 안효창 라이브러리함수의오류번호얻기 errno 변수기능오류번호를저장한다. 기본형 extern int errno; 헤더파일 라이브러리함수호출에실패했을때함수예 정수값을반환하는함수 -1 반환 open 함수 포인터를반환하는함수 NULL 반환 fopen 함수 2 유닉스 / 리눅스 라이브러리함수의오류번호얻기 19-1

More information

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100

Microsoft PowerPoint - ch09 - 연결형리스트, Stack, Queue와 응용 pm0100 2015-1 프로그래밍언어 9. 연결형리스트, Stack, Queue 2015 년 5 월 4 일 교수김영탁 영남대학교공과대학정보통신공학과 (Tel : +82-53-810-2497; Fax : +82-53-810-4742 http://antl.yu.ac.kr/; E-mail : ytkim@yu.ac.kr) 연결리스트 (Linked List) 연결리스트연산 Stack

More information

Microsoft PowerPoint - chap13-입출력라이브러리.pptx

Microsoft PowerPoint - chap13-입출력라이브러리.pptx #include int main(void) int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; 1 학습목표 스트림의 기본 개념을 알아보고,

More information

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures

A Hierarchical Approach to Interactive Motion Editing for Human-like Figures 단일연결리스트 (Singly Linked List) 신찬수 연결리스트 (linked list)? tail 서울부산수원용인 null item next 구조체복습 struct name_card { char name[20]; int date; } struct name_card a; // 구조체변수 a 선언 a.name 또는 a.date // 구조체 a의멤버접근 struct

More information

PowerPoint 프레젠테이션

PowerPoint 프레젠테이션 실습 1 배효철 th1g@nate.com 1 목차 조건문 반복문 System.out 구구단 모양만들기 Up & Down 2 조건문 조건문의종류 If, switch If 문 조건식결과따라중괄호 { 블록을실행할지여부결정할때사용 조건식 true 또는 false값을산출할수있는연산식 boolean 변수 조건식이 true이면블록실행하고 false 이면블록실행하지않음 3

More information

Microsoft PowerPoint - [2009] 02.pptx

Microsoft PowerPoint - [2009] 02.pptx 원시데이터유형과연산 원시데이터유형과연산 원시데이터유형과연산 숫자데이터유형 - 숫자데이터유형 원시데이터유형과연산 표준입출력함수 - printf 문 가장기본적인출력함수. (stdio.h) 문법 ) printf( Test printf. a = %d \n, a); printf( %d, %f, %c \n, a, b, c); #include #include

More information

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070>

<443A5C4C C4B48555C B3E25C32C7D0B1E25CBCB3B0E8C7C1B7CEC1A7C6AE425CBED0C3E0C7C1B7CEB1D7B7A55C D616E2E637070> #include "stdafx.h" #include "Huffman.h" 1 /* 비트의부분을뽑아내는함수 */ unsigned HF::bits(unsigned x, int k, int j) return (x >> k) & ~(~0

More information

(Microsoft PowerPoint - 11\300\345.ppt [\310\243\310\257 \270\360\265\345])

(Microsoft PowerPoint - 11\300\345.ppt [\310\243\310\257 \270\360\265\345]) 입출력 C++ 의효율적인입출력방법을배워보자. 이장에서다룰내용 1 cin 과 cout 을이용한입출력 2 입출력연산자중복 3 조작자생성 4 파일입출력 01_cin 과 cout 을이용한입출력 포맷입출력 C++ 의표준입출력은 cin, cout 을사용한다. C 의 printf 는함수이므로매번여러인자를입력해줘야하지만, cin/cout 에서는형식을한번만정의하면계속사용할수있다.

More information

Microsoft PowerPoint - chap06-2pointer.ppt

Microsoft PowerPoint - chap06-2pointer.ppt 2010-1 학기프로그래밍입문 (1) chapter 06-2 참고자료 포인터 박종혁 Tel: 970-6702 Email: jhpark1@snut.ac.kr 한빛미디어 출처 : 뇌를자극하는 C프로그래밍, 한빛미디어 -1- 포인터의정의와사용 변수를선언하는것은메모리에기억공간을할당하는것이며할당된이후에는변수명으로그기억공간을사용한다. 할당된기억공간을사용하는방법에는변수명외에메모리의실제주소값을사용하는것이다.

More information

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning

A Dynamic Grid Services Deployment Mechanism for On-Demand Resource Provisioning C Programming Practice (II) Contents 배열 문자와문자열 구조체 포인터와메모리관리 구조체 2/17 배열 (Array) (1/2) 배열 동일한자료형을가지고있으며같은이름으로참조되는변수들의집합 배열의크기는반드시상수이어야한다. type var_name[size]; 예 ) int myarray[5] 배열의원소는원소의번호를 0 부터시작하는색인을사용

More information

Lab 3. 실습문제 (Single linked list)_해답.hwp

Lab 3. 실습문제 (Single linked list)_해답.hwp Lab 3. Singly-linked list 의구현 실험실습일시 : 2009. 3. 30. 담당교수 : 정진우 담당조교 : 곽문상 보고서제출기한 : 2009. 4. 5. 학과 : 학번 : 성명 : 실습과제목적 : 이론시간에배운 Singly-linked list를실제로구현할수있다. 실습과제내용 : 주어진소스를이용해 Singly-linked list의각함수를구현한다.

More information

KNK_C_05_Pointers_Arrays_structures_summary_v02

KNK_C_05_Pointers_Arrays_structures_summary_v02 Pointers and Arrays Structures adopted from KNK C Programming : A Modern Approach 요약 2 Pointers and Arrays 3 배열의주소 #include int main(){ int c[] = {1, 2, 3, 4}; printf("c\t%p\n", c); printf("&c\t%p\n",

More information

슬라이드 1

슬라이드 1 한국산업기술대학교 제 5 강스케일링및회전 이대현교수 학습안내 학습목표 3D 오브젝트의확대, 축소및회전방법을이해한다. 학습내용 3D 오브젝트의확대및축소 (Scaling) 3D 오브젝트의회전 (Rotation) 변홖공갂 (Transform Space) SceneNode 의크기변홖 (Scale) void setscale ( Real x, Real y, Real z)

More information

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다. 제 14 장포인터활용 유준범 (JUNBEOM YOO) Ver. 2.0 jbyoo@konkuk.ac.kr http://dslab.konkuk.ac.kr 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다. 이번장에서학습할내용 이중포인터란무엇인가? 포인터배열 함수포인터 다차원배열과포인터 void 포인터 포인터는다양한용도로유용하게활용될수있습니다. 2 이중포인터

More information

컴파일러

컴파일러 YACC 응용예 Desktop Calculator 7/23 Lex 입력 수식문법을위한 lex 입력 : calc.l %{ #include calc.tab.h" %} %% [0-9]+ return(number) [ \t] \n return(0) \+ return('+') \* return('*'). { printf("'%c': illegal character\n",

More information

(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345])

(Microsoft PowerPoint - 07\300\345.ppt [\310\243\310\257 \270\360\265\345]) 클래스의응용 클래스를자유자재로사용하자. 이장에서다룰내용 1 객체의치환 2 함수와클래스의상관관계 01_ 객체의치환 객체도변수와마찬가지로치환이가능하다. 기본예제 [7-1] 객체도일반변수와마찬가지로대입이가능하다. 기본예제 [7-2] 객체의치환시에는조심해야할점이있다. 복사생성자의필요성에대하여알아보자. [ 기본예제 7-1] 클래스의치환 01 #include

More information

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770>

<322EBCF8C8AF28BFACBDC0B9AEC1A6292E687770> 연습문제해답 5 4 3 2 1 0 함수의반환값 =15 5 4 3 2 1 0 함수의반환값 =95 10 7 4 1-2 함수의반환값 =3 1 2 3 4 5 연습문제해답 1. C 언어에서의배열에대하여다음중맞는것은? (1) 3차원이상의배열은불가능하다. (2) 배열의이름은포인터와같은역할을한다. (3) 배열의인덱스는 1에서부터시작한다. (4) 선언한다음, 실행도중에배열의크기를변경하는것이가능하다.

More information

K&R2 Reference Manual 번역본

K&R2 Reference Manual 번역본 typewriter structunion struct union if-else if if else if if else if if if if else else ; auto register static extern typedef void char short int long float double signed unsigned const volatile { } struct

More information

Microsoft PowerPoint - chap04-연산자.pptx

Microsoft PowerPoint - chap04-연산자.pptx int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); } 1 학습목표 수식의 개념과 연산자, 피연산자에 대해서 알아본다. C의 를 알아본다. 연산자의 우선 순위와 결합 방향에

More information

11장 포인터

11장 포인터 Dynamic Memory and Linked List 1 동적할당메모리의개념 프로그램이메모리를할당받는방법 정적 (static) 동적 (dynamic) 정적메모리할당 프로그램이시작되기전에미리정해진크기의메모리를할당받는것 메모리의크기는프로그램이시작하기전에결정 int i, j; int buffer[80]; char name[] = data structure"; 처음에결정된크기보다더큰입력이들어온다면처리하지못함

More information

11장 포인터

11장 포인터 누구나즐기는 C 언어콘서트 제 9 장포인터 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다. 포인터란? 포인터 (pointer): 주소를가지고있는변수 메모리의구조 변수는메모리에저장된다. 메모리는바이트단위로액세스된다. 첫번째바이트의주소는 0, 두번째바이트는 1, 변수와메모리

More information

Microsoft PowerPoint - chap10-함수의활용.pptx

Microsoft PowerPoint - chap10-함수의활용.pptx #include int main(void) { int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; } 1 학습목표 중 값에 의한 전달 방법과

More information

06장.리스트

06장.리스트 ---------------- DATA STRUCTURES USING C ---------------- CHAPTER 리스트 1/28 리스트란? 리스트 (list), 선형리스트 (linear list) 순서를가진항목들의모임 집합 : 항목간의순서의개념이없음 리스트의예 요일 : ( 일요일, 월요일,, 토요일 ) 한글자음의모임 : ( ㄱ, ㄴ,, ㅎ ) 카드 :

More information

<4D F736F F F696E74202D B3E22032C7D0B1E220C0A9B5B5BFECB0D4C0D3C7C1B7CEB1D7B7A1B9D620C1A638B0AD202D20C7C1B7B9C0D320BCD3B5B5C0C720C1B6C0FD>

<4D F736F F F696E74202D B3E22032C7D0B1E220C0A9B5B5BFECB0D4C0D3C7C1B7CEB1D7B7A1B9D620C1A638B0AD202D20C7C1B7B9C0D320BCD3B5B5C0C720C1B6C0FD> 2006 년 2 학기윈도우게임프로그래밍 제 8 강프레임속도의조절 이대현 한국산업기술대학교 오늘의학습내용 프레임속도의조절 30fps 맞추기 스프라이트프레임속도의조절 프레임속도 (Frame Rate) 프레임속도란? 얼마나빨리프레임 ( 일반적으로하나의완성된화면 ) 을만들어낼수있는지를나타내는척도 일반적으로초당프레임출력횟수를많이사용한다. FPS(Frame Per Sec)

More information

Chap 6: Graphs

Chap 6: Graphs 그래프표현법 인접행렬 (Adjacency Matrix) 인접리스트 (Adjacency List) 인접다중리스트 (Adjacency Multilist) 6 장. 그래프 (Page ) 인접행렬 (Adjacency Matrix) n 개의 vertex 를갖는그래프 G 의인접행렬의구성 A[n][n] (u, v) E(G) 이면, A[u][v] = Otherwise, A[u][v]

More information

Microsoft PowerPoint - 제11장 포인터(강의)

Microsoft PowerPoint - 제11장 포인터(강의) 쉽게풀어쓴 C 언어 Express 제 11 장포인터 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다. 포인터란? 포인터 (pointer): 주소를가지고있는변수 1003 1004 1005 영화관 1002 1006 1001 포인터 (pointer) 1007 메모리의구조

More information

untitled

untitled while do-while for break continue while( ) ; #include 0 i int main(void) int meter; int i = 0; while(i < 3) meter = i * 1609; printf("%d %d \n", i, meter); i++; return 0; i i< 3 () 0 (1)

More information

LIDAR와 영상 Data Fusion에 의한 건물 자동추출

LIDAR와 영상 Data Fusion에 의한 건물 자동추출 i ii iii iv v vi vii 1 2 3 4 Image Processing Image Pyramid Edge Detection Epipolar Image Image Matching LIDAR + Photo Cross correlation Least Squares Epipolar Line Matching Low Level High Level Space

More information

슬라이드 1

슬라이드 1 CHAP 2: 순환 (Recursion) 순환 (recursion) 이란? 알고리즘이나함수가수행도중에자기자신을다시호출하여문제를해결하는기법 정의자체가순환적으로 되어있는경우에적합한방법 순환 (recursion) 의예 팩토리얼값구하기 피보나치수열 1 n! n*( n 1)! fib( n) 0 1 fib( n 2) n n 0 ` 1 fib( n 1) if n 0 if

More information

02장.배열과 클래스

02장.배열과 클래스 ---------------- DATA STRUCTURES USING C ---------------- CHAPTER 배열과구조체 1/20 많은자료의처리? 배열 (array), 구조체 (struct) 성적처리프로그램에서 45 명의성적을저장하는방법 주소록프로그램에서친구들의다양한정보 ( 이름, 전화번호, 주소, 이메일등 ) 를통합하여저장하는방법 홍길동 이름 :

More information

프로그래밍개론및실습 2015 년 2 학기프로그래밍개론및실습과목으로본내용은강의교재인생능출판사, 두근두근 C 언어수업, 천인국지음을발췌수정하였음

프로그래밍개론및실습 2015 년 2 학기프로그래밍개론및실습과목으로본내용은강의교재인생능출판사, 두근두근 C 언어수업, 천인국지음을발췌수정하였음 프로그래밍개론및실습 2015 년 2 학기프로그래밍개론및실습과목으로본내용은강의교재인생능출판사, 두근두근 C 언어수업, 천인국지음을발췌수정하였음 CHAPTER 9 둘중하나선택하기 관계연산자 두개의피연산자를비교하는연산자 결과값은참 (1) 아니면거짓 (0) x == y x 와 y 의값이같은지비교한다. 관계연산자 연산자 의미 x == y x와 y가같은가? x!= y

More information

0. 표지에이름과학번을적으시오. (6) 1. 변수 x, y 가 integer type 이라가정하고다음빈칸에 x 와 y 의계산결과값을적으시오. (5) x = (3 + 7) * 6; x = 60 x = (12 + 6) / 2 * 3; x = 27 x = 3 * (8 / 4

0. 표지에이름과학번을적으시오. (6) 1. 변수 x, y 가 integer type 이라가정하고다음빈칸에 x 와 y 의계산결과값을적으시오. (5) x = (3 + 7) * 6; x = 60 x = (12 + 6) / 2 * 3; x = 27 x = 3 * (8 / 4 Introduction to software design 2012-1 Final 2012.06.13 16:00-18:00 Student ID: Name: - 1 - 0. 표지에이름과학번을적으시오. (6) 1. 변수 x, y 가 integer type 이라가정하고다음빈칸에 x 와 y 의계산결과값을적으시오. (5) x = (3 + 7) * 6; x = 60 x

More information

슬라이드 1

슬라이드 1 -Part3- 제 4 장동적메모리할당과가변인 자 학습목차 4.1 동적메모리할당 4.1 동적메모리할당 4.1 동적메모리할당 배울내용 1 프로세스의메모리공간 2 동적메모리할당의필요성 4.1 동적메모리할당 (1/6) 프로세스의메모리구조 코드영역 : 프로그램실행코드, 함수들이저장되는영역 스택영역 : 매개변수, 지역변수, 중괄호 ( 블록 ) 내부에정의된변수들이저장되는영역

More information

Microsoft PowerPoint - additional01.ppt [호환 모드]

Microsoft PowerPoint - additional01.ppt [호환 모드] 1.C 기반의 C++ part 1 함수 오버로딩 (overloading) 디폴트매개변수 (default parameter) 인-라인함수 (in-line function) 이름공간 (namespace) Jong Hyuk Park 함수 Jong Hyuk Park 함수오버로딩 (overloading) 함수오버로딩 (function overloading) C++ 언어에서는같은이름을가진여러개의함수를정의가능

More information

; struct point p[10] = {{1, 2, {5, -3, {-3, 5, {-6, -2, {2, 2, {-3, -3, {-9, 2, {7, 8, {-6, 4, {8, -5; for (i = 0; i < 10; i++){ if (p[i].x > 0 && p[i

; struct point p[10] = {{1, 2, {5, -3, {-3, 5, {-6, -2, {2, 2, {-3, -3, {-9, 2, {7, 8, {-6, 4, {8, -5; for (i = 0; i < 10; i++){ if (p[i].x > 0 && p[i ; struct point p; printf("0이아닌점의좌표를입력하시오 : "); scanf("%d %d", &p.x, &p.y); if (p.x > 0 && p.y > 0) printf("1사분면에있다.\n"); if (p.x < 0 && p.y > 0) printf("2사분면에있다.\n"); if (p.x < 0 && p.y < 0) printf("3사분면에있다.\n");

More information

Microsoft PowerPoint - 제11장 포인터

Microsoft PowerPoint - 제11장 포인터 쉽게풀어쓴 C 언어 Express 제 11 장포인터 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다. 포인터란? 포인터 (pointer): 주소를가지고있는변수 1003 1004 1005 영화관 1002 1006 1001 포인터 (pointer) 1007 메모리의구조

More information

구조체정의 자료형 (data types) 기본자료형 (primitive data types) : char, int, float 등과같이 C 언어에서제공하는자료형. 사용자정의자료형 (user-defined data types) : 다양한자료형을묶어서목적에따라새로운자료형을

구조체정의 자료형 (data types) 기본자료형 (primitive data types) : char, int, float 등과같이 C 언어에서제공하는자료형. 사용자정의자료형 (user-defined data types) : 다양한자료형을묶어서목적에따라새로운자료형을 (structures) 구조체정의 구조체선언및초기화 구조체배열 구조체포인터 구조체배열과포인터 구조체와함수 중첩된구조체 구조체동적할당 공용체 (union) 1 구조체정의 자료형 (data types) 기본자료형 (primitive data types) : char, int, float 등과같이 C 언어에서제공하는자료형. 사용자정의자료형 (user-defined

More information

Chapter 4. LISTS

Chapter 4. LISTS 연결리스트의응용 류관희 충북대학교 1 체인연산 체인을역순으로만드는 (inverting) 연산 3 개의포인터를적절히이용하여제자리 (in place) 에서문제를해결 typedef struct listnode *listpointer; typedef struct listnode { char data; listpointer link; ; 2 체인연산 체인을역순으로만드는

More information

Frama-C/JESSIS 사용법 소개

Frama-C/JESSIS 사용법 소개 Frama-C 프로그램검증시스템소개 박종현 @ POSTECH PL Frama-C? C 프로그램대상정적분석도구 플러그인구조 JESSIE Wp Aorai Frama-C 커널 2 ROSAEC 2011 동계워크샵 @ 통영 JESSIE? Frama-C 연역검증플러그인 프로그램분석 검증조건추출 증명 Hoare 논리에기초한프로그램검증도구 사용법 $ frama-c jessie

More information

쉽게 풀어쓴 C 프로그래밍

쉽게 풀어쓴 C 프로그래밍 제 3 장함수와문자열 1. 함수의기본적인개념을이해한다. 2. 인수와매개변수의개념을이해한다. 3. 함수의인수전달방법 2가지를이해한다 4. 중복함수를이해한다. 5. 디폴트매개변수를이해한다. 6. 문자열의구성을이해한다. 7. string 클래스의사용법을익힌다. 이번장에서만들어볼프로그램 함수란? 함수선언 함수호출 예제 #include using

More information

Microsoft PowerPoint - chap05-제어문.pptx

Microsoft PowerPoint - chap05-제어문.pptx int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); 1 학습목표 제어문인,, 분기문에 대해 알아본다. 인 if와 switch의 사용 방법과 사용시 주의사항에 대해 알아본다.

More information

12 강. 문자출력 Direct3D 에서는문자를출력하기위해서 LPD3DXFONT 객체를사용한다 LPD3DXFONT 객체생성과초기화 LPD3DXFONT 객체를생성하고초기화하는함수로 D3DXCreateFont() 가있다. HRESULT D3DXCreateFont

12 강. 문자출력 Direct3D 에서는문자를출력하기위해서 LPD3DXFONT 객체를사용한다 LPD3DXFONT 객체생성과초기화 LPD3DXFONT 객체를생성하고초기화하는함수로 D3DXCreateFont() 가있다. HRESULT D3DXCreateFont 12 강. 문자출력 Direct3D 에서는문자를출력하기위해서 LPD3DXFONT 객체를사용한다. 12.1 LPD3DXFONT 객체생성과초기화 LPD3DXFONT 객체를생성하고초기화하는함수로 D3DXCreateFont() 가있다. HRESULT D3DXCreateFont( in LPDIRECT3DDEVICE9 pdevice, in INT Height, in UINT

More information

fprintf(fp, "clf; clear; clc; \n"); fprintf(fp, "x = linspace(0, %d, %d)\n ", L, N); fprintf(fp, "U = [ "); for (i = 0; i <= (N - 1) ; i++) for (j = 0

fprintf(fp, clf; clear; clc; \n); fprintf(fp, x = linspace(0, %d, %d)\n , L, N); fprintf(fp, U = [ ); for (i = 0; i <= (N - 1) ; i++) for (j = 0 병렬계산을이용한열방정식풀기. 1. 처음 병렬계산을하기전에 C 언어를이용하여명시적유한차분법으로하나의열방정식을풀어본 다. 먼저 C 로열방정식을이해한다음초기조건만다르게하여클러스터로여러개의열방 정식을풀어보자. 2. C 를이용한명시적유한차분법으로열방적식풀기 열방정식을풀기위한자세한이론은앞서다룬 Finite-Difference method 을보기로하고 바로식 (1.10)

More information

03_queue

03_queue Queue Data Structures and Algorithms 목차 큐의이해와 ADT 정의 큐의배열기반구현 큐의연결리스트기반구현 큐의활용 덱 (Deque) 의이해와구현 Data Structures and Algorithms 2 큐의이해와 ADT 정의 Data Structures and Algorithms 3 큐 (Stack) 의이해와 ADT 정의 큐는 LIFO(Last-in,

More information

제 11 장포인터 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다.

제 11 장포인터 유준범 (JUNBEOM YOO) Ver 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다. 제 11 장포인터 유준범 (JUNBEOM YOO) Ver. 2.0 jbyoo@konkuk.ac.kr http://dslab.konkuk.ac.kr 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다. 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습합니다.

More information

ISP and CodeVisionAVR C Compiler.hwp

ISP and CodeVisionAVR C Compiler.hwp USBISP V3.0 & P-AVRISP V1.0 with CodeVisionAVR C Compiler http://www.avrmall.com/ November 12, 2007 Copyright (c) 2003-2008 All Rights Reserved. USBISP V3.0 & P-AVRISP V1.0 with CodeVisionAVR C Compiler

More information

금오공대 컴퓨터공학전공 강의자료

금오공대 컴퓨터공학전공 강의자료 C 프로그래밍프로젝트 Chap 13. 포인터와배열! 함께이해하기 2013.10.02. 오병우 컴퓨터공학과 13-1 포인터와배열의관계 Programming in C, 정재은저, 사이텍미디어. 9 장참조 ( 교재의 13-1 은읽지말것 ) 배열이름의정체 배열이름은 Compile 시의 Symbol 로서첫번째요소의주소값을나타낸다. Symbol 로서컴파일시에만유효함 실행시에는메모리에잡히지않음

More information

<B1E2BCFAB9AEBCAD5FB9DABAB4B1D45F F F64746F72732E687770>

<B1E2BCFAB9AEBCAD5FB9DABAB4B1D45F F F64746F72732E687770> 기술문서 09. 11. 3. 작성 Format String Bug 에서 dtors 우회 작성자 : 영남대학교 @Xpert 박병규 preex@ynu.ac.kr 1. 요약... 2 2. d to r 이란... 3 3. 포맷스트링... 4 4. ro o t 권한획득... 7 5. 참고자료... 1 0-1 - 1. 요약 포맷스트링버그 (Format String bug)

More information

Infinity(∞) Strategy

Infinity(∞) Strategy 반복제어 표월성 passwd74@cherub.sungkyul.edu 개요 for() 문 break문과 continue문 while문 do-while문 for() 문 for() 문형식 for( 표현식1; 표현식2; 표현식3) 여러문장들 ; 표현식 1 : 초기화 (1 번만수행 ) 표현식 2 : 반복문수행조건 ( 없으면무한반복 ) 표현식 3 : 반복문수행횟수 for()

More information

이미지 워핑과 모핑

이미지 워핑과 모핑 제 6 장영상워핑과모핑 학습목표 다음기하학적처리의개념을설명할수있고프로그램을작성할수있다 영상워핑 영상모핑 2014-11-11 영상처리 2 영상워핑 (Warpng) 픽셀의위치를이동하는기하학적처리 회전, 이동, 확대 / 축소등의기하학적처리와의차이점 픽셀별로이동정도가다름 고무종이위에그려진영상을임의로구부리는효과를낼수있음 2014-11-11 영상처리 3 영상워핑 (Warpng)

More information

윈도우즈프로그래밍(1)

윈도우즈프로그래밍(1) 제어문 (2) For~Next 문 윈도우즈프로그래밍 (1) ( 신흥대학교컴퓨터정보계열 ) 2/17 Contents 학습목표 프로그램에서주어진특정문장을부분을일정횟수만큼반복해서실행하는문장으로 For~Next 문등의구조를이해하고활용할수있다. 내용 For~Next 문 다중 For 문 3/17 제어문 - FOR 문 반복문 : 프로그램에서주어진특정문장들을일정한횟수만큼반복해서실행하는문장

More information

ch15

ch15 쉽게풀어쓴 C 언어 Express 제 14 장포인터활용 C Express 이중포인터 이중포인터 (double pointer) : 포인터를가리키는포인터 int i = 10; int *p = &i; int **q = &p; // i 는 int 형변수 // p 는 i 를가리키는포인터 // q 는포인터 p 를가리키는이중포인터 이중포인터 이중포인터의해석 이중포인터 //

More information

untitled

untitled if( ) ; if( sales > 2000 ) bonus = 200; if( score >= 60 ) printf(".\n"); if( height >= 130 && age >= 10 ) printf(".\n"); if ( temperature < 0 ) printf(".\n"); // printf(" %.\n \n", temperature); // if(

More information

Visual Basic 반복문

Visual Basic 반복문 학습목표 반복문 For Next문, For Each Next문 Do Loop문, While End While문 구구단작성기로익히는반복문 2 5.1 반복문 5.2 구구단작성기로익히는반복문 3 반복문 주어진조건이만족하는동안또는주어진조건이만족할때까지일정구간의실행문을반복하기위해사용 For Next For Each Next Do Loop While Wend 4 For

More information

학습목차 2.1 다차원배열이란 차원배열의주소와값의참조

학습목차 2.1 다차원배열이란 차원배열의주소와값의참조 - Part2- 제 2 장다차원배열이란무엇인가 학습목차 2.1 다차원배열이란 2. 2 2 차원배열의주소와값의참조 2.1 다차원배열이란 2.1 다차원배열이란 (1/14) 다차원배열 : 2 차원이상의배열을의미 1 차원배열과다차원배열의비교 1 차원배열 int array [12] 행 2 차원배열 int array [4][3] 행 열 3 차원배열 int array [2][2][3]

More information

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3

Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3 Poison null byte Excuse the ads! We need some help to keep our site up. List 1 Conditions 2 Exploit plan 2.1 chunksize(p)!= prev_size (next_chunk(p) 3 Example 3.1 Files 3.2 Source code 3.3 Exploit flow

More information

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074>

< E20C6DFBFFEBEEE20C0DBBCBAC0BB20C0A7C7D12043BEF0BEEE20492E707074> Chap #2 펌웨어작성을위한 C 언어 I http://www.smartdisplay.co.kr 강의계획 Chap1. 강의계획및디지털논리이론 Chap2. 펌웨어작성을위한 C 언어 I Chap3. 펌웨어작성을위한 C 언어 II Chap4. AT89S52 메모리구조 Chap5. SD-52 보드구성과코드메모리프로그래밍방법 Chap6. 어드레스디코딩 ( 매핑 ) 과어셈블리어코딩방법

More information

Microsoft PowerPoint - chap-11.pptx

Microsoft PowerPoint - chap-11.pptx 쉽게풀어쓴 C 언어 Express 제 11 장포인터 컴퓨터프로그래밍기초 이번장에서학습할내용 포인터이란? 변수의주소 포인터의선언 간접참조연산자 포인터연산 포인터와배열 포인터와함수 이번장에서는포인터의기초적인지식을학습한다. 컴퓨터프로그래밍기초 2 포인터란? 포인터 (pointer): 주소를가지고있는변수 컴퓨터프로그래밍기초 3 메모리의구조 변수는메모리에저장된다. 메모리는바이트단위로액세스된다.

More information

이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다. 2

이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다. 2 제 17 장동적메모리와연결리스트 유준범 (JUNBEOM YOO) Ver. 2.0 jbyoo@konkuk.ac.kr http://dslab.konkuk.ac.kr 본강의자료는생능출판사의 PPT 강의자료 를기반으로제작되었습니다. 이번장에서학습할내용 동적메모리란? malloc() 와 calloc() 연결리스트 파일을이용하면보다많은데이터를유용하고지속적으로사용및관리할수있습니다.

More information

슬라이드 1

슬라이드 1 2007 년 2 학기윈도우게임프로그래밍 제 7 강프레임속도의조절 이대현 핚국산업기술대학교 학습내용 프레임속도의조절 30fps 맞추기 스프라이트프레임속도의조절 프레임속도 (Frame Rate) 프레임속도란? 얼마나빨리프레임 ( 일반적으로하나의완성된화면 ) 을만들어낼수있는지를나타내는척도 일반적으로초당프레임출력횟수를많이사용핚다. FPS(Frame Per Sec)

More information

PowerPoint Presentation

PowerPoint Presentation 객체지향프로그래밍 클래스, 객체, 메소드 ( 실습 ) 손시운 ssw5176@kangwon.ac.kr 예제 1. 필드만있는클래스 텔레비젼 2 예제 1. 필드만있는클래스 3 예제 2. 여러개의객체생성하기 4 5 예제 3. 메소드가추가된클래스 public class Television { int channel; // 채널번호 int volume; // 볼륨 boolean

More information

Microsoft PowerPoint - chap12-고급기능.pptx

Microsoft PowerPoint - chap12-고급기능.pptx #include int main(void) int num; printf( Please enter an integer: "); scanf("%d", &num); if ( num < 0 ) printf("is negative.\n"); printf("num = %d\n", num); return 0; 1 학습목표 가 제공하는 매크로 상수와 매크로

More information

Lab 4. 실습문제 (Circular singly linked list)_해답.hwp

Lab 4. 실습문제 (Circular singly linked list)_해답.hwp Lab 4. Circular singly-linked list 의구현 실험실습일시 : 2009. 4. 6. 담당교수 : 정진우 담당조교 : 곽문상 보고서제출기한 : 2009. 4. 12. 학과 : 학번 : 성명 : 실습과제목적 : 이론시간에배운 Circular Singly-linked list를실제로구현할수있다. 실습과제내용 : 주어진소스를이용해 Circular

More information

Vector Differential: 벡터 미분 Yonghee Lee October 17, 벡터미분의 표기 스칼라미분 벡터미분(Vector diffrential) 또는 행렬미분(Matrix differential)은 벡터와 행렬의 미분식에 대 한 표

Vector Differential: 벡터 미분 Yonghee Lee October 17, 벡터미분의 표기 스칼라미분 벡터미분(Vector diffrential) 또는 행렬미분(Matrix differential)은 벡터와 행렬의 미분식에 대 한 표 Vector Differential: 벡터 미분 Yonhee Lee October 7, 08 벡터미분의 표기 스칼라미분 벡터미분(Vector diffrential) 또는 행렬미분(Matrix differential)은 벡터와 행렬의 미분식에 대 한 표기법을 정의하는 방법이다 보통 스칼라(scalar)에 대한 미분은 일분수 함수 f : < < 또는 다변수 함수(function

More information

쉽게 풀어쓴 C 프로그래밍

쉽게 풀어쓴 C 프로그래밍 제 13 장파일처리 1. 스트림의개념을이해한다. 2. 객체지향적인방법을사용하여파일입출력을할수있다. 3. 텍스트파일과이진파일의차이점을이해한다. 4. 순차파일과임의접근파일의차이점을이해한다. 이번장에서만들어볼프로그램 스트림 (stream) 스트림 (stream) 은 순서가있는데이터의연속적인흐름 이다. 스트림은입출력을물의흐름처럼간주하는것이다. 입출력관련클래스들 파일쓰기

More information

설계란 무엇인가?

설계란 무엇인가? 금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 6 강. 함수와배열, 포인터, 참조목차 함수와포인터 주소값의매개변수전달 주소의반환 함수와배열 배열의매개변수전달 함수와참조 참조에의한매개변수전달 참조의반환 프로그래밍연습 1 /15 6 강. 함수와배열, 포인터, 참조함수와포인터 C++ 매개변수전달방법 값에의한전달 : 변수값,

More information

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202834C1D6C2F7207E2038C1D6C2F729>

<4D F736F F F696E74202D20B8B6C0CCC5A9B7CEC7C1B7CEBCBCBCAD202834C1D6C2F7207E2038C1D6C2F729> 8주차중간고사 ( 인터럽트및 A/D 변환기문제및풀이 ) Next-Generation Networks Lab. 외부입력인터럽트예제 문제 1 포트 A 의 7-segment 에초시계를구현한다. Tact 스위치 SW3 을 CPU 보드의 PE4 에연결한다. 그리고, SW3 을누르면하강 에지에서초시계가 00 으로초기화된다. 동시에 Tact 스위치 SW4 를 CPU 보드의

More information

슬라이드 1

슬라이드 1 BMP 파일구조 김성영교수 금오공과대학교 컴퓨터공학부 학습목표 BMP 파일의구조및그특징을설명할수있다. 파일헤더및비트맵정보헤더의주요필드를구분하고그역할을설명할수있다. C언어를사용하여 BMP 파일을처리할수있다. 2 BMP 파일구조 File Header (BITMAPFILEHEADER) Bitmap Info. Header (BITMAPINFOHEADER) Headers

More information

설계란 무엇인가?

설계란 무엇인가? 금오공과대학교 C++ 프로그래밍 jhhwang@kumoh.ac.kr 컴퓨터공학과 황준하 16 강. 파일입출력목차 파일입출력기초 파일입출력모드 텍스트파일과이진파일 이진파일입출력 임의접근 1 /18 16 강. 파일입출력파일입출력기초 파일입출력과정 파일스트림객체생성 파일열기 사용 : 기본적으로표준입출력객체 (cin, cout) 사용방법과동일 파일닫기 파일스트림클래스의종류

More information

PowerPoint Presentation

PowerPoint Presentation Class - Property Jo, Heeseung 목차 section 1 클래스의일반구조 section 2 클래스선언 section 3 객체의생성 section 4 멤버변수 4-1 객체변수 4-2 클래스변수 4-3 종단 (final) 변수 4-4 멤버변수접근방법 section 5 멤버변수접근한정자 5-1 public 5-2 private 5-3 한정자없음

More information

Microsoft Word - PLC제어응용-2차시.doc

Microsoft Word - PLC제어응용-2차시.doc 과정명 PLC 제어응용차시명 2 차시. 접점명령 학습목표 1. 연산개시명령 (LOAD, LOAD NOT) 에대하여설명할수있다. 2. 직렬접속명령 (AND, AND NOT) 에대하여설명할수있다. 3. 병렬접속명령 (OR, OR NOT) 에대하여설명할수있다. 4.PLC의접점명령을가지고간단한프로그램을작성할수있다. 학습내용 1. 연산개시명령 1) 연산개시명령 (LOAD,

More information

중간고사

중간고사 중간고사 예제 1 사용자로부터받은두개의숫자 x, y 중에서큰수를찾는알고리즘을의사코드로작성하시오. Step 1: Input x, y Step 2: if (x > y) then MAX

More information

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt

Microsoft PowerPoint - 3ÀÏ°_º¯¼ö¿Í »ó¼ö.ppt 변수와상수 1 변수란무엇인가? 변수 : 정보 (data) 를저장하는컴퓨터내의특정위치 ( 임시저장공간 ) 메모리, register 메모리주소 101 번지 102 번지 변수의크기에따라 주로 byte 단위 메모리 2 기본적인변수형및변수의크기 변수의크기 해당컴퓨터에서는항상일정 컴퓨터마다다를수있음 short

More information

UI TASK & KEY EVENT

UI TASK & KEY EVENT T9 & AUTOMATA 2007. 3. 23 PLATFORM TEAM 정용학 차례 T9 개요 새로운언어 (LDB) 추가 T9 주요구조체 / 주요함수 Automata 개요 Automata 주요함수 추후세미나계획 질의응답및토의 T9 ( 2 / 30 ) T9 개요 일반적으로 cat 이라는단어를쓸려면... 기존모드 (multitap) 2,2,2, 2,8 ( 총 6번의입력

More information

Microsoft PowerPoint 웹 연동 기술.pptx

Microsoft PowerPoint 웹 연동 기술.pptx 웹프로그래밍및실습 ( g & Practice) 문양세강원대학교 IT 대학컴퓨터과학전공 URL 분석 (1/2) URL (Uniform Resource Locator) 프로토콜, 호스트, 포트, 경로, 비밀번호, User 등의정보를포함 예. http://kim:3759@www.hostname.com:80/doc/index.html URL 을속성별로분리하고자할경우

More information