특징점 추출이나 패턴인식을 위해 많이 쓰이는 전처리 작업인 Thinning이다.
'Skeletonization'이라는 이름으로도 불리기도 한다.
이진화된 사진이나 영상의 골격을 추출하는 기법이다.
OpenCV에서 라이브러리로 제시된 것이 없어 구현하였다.
함수 호출은
Thinning(cv::Mat, cv::Mat.rows, cv::Mat.cols);
위와 같이 하면된다.
cv::Mat → Mat 변수의 이름
cv::Mat.rows → Mat 행의 수
cv::Mat.cols → Mat 열의 수
[입력 이미지]
위와 같이 이진화된 회로사진이 있을 때 세선화한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | void Thinning(Mat input, int row, int col) { int x, y, p, q, xp, yp, xm, ym, cc, cd; int np1, sp1, hv; int cnt=0, chk=0, flag=0; unsigned char *m_BufImage; unsigned char *m_ResultImg; m_BufImage = (unsigned char*)malloc(sizeof(unsigned char)*row*col); m_ResultImg = (unsigned char*)malloc(sizeof(unsigned char)*row*col); // Result image에 Mat format의 input image Copy for( y = 0 ; y < row ; y ++ ){ for( x = 0 ; x < col ; x++ ){ *(m_ResultImg+(col*y)+x) = input.at<uchar>(y,x); } } do{ // Image Buffer를 0으로 셋팅 for( y = 1 ; y < row-1 ; y ++ ){ for( x = 1 ; x < col-1 ; x++ ){ *(m_BufImage+(col*y)+x) = 0; } } // 천이 추출 if(chk == 0) flag = 0; chk = cnt % 2; cnt ++; for( y = 1 ; y < row-1 ; y ++ ){ ym = y - 1; yp = y + 1; for( x = 1 ; x < col-1 ; x ++ ){ if(*(m_ResultImg+(col*y)+x) == 0) continue; np1 = 0; for(q = y-1 ; q <= y+1; q ++ ){ for(p = x-1 ; p <= x+1; p ++ ){ if(*(m_ResultImg+(col*q)+p) != 0) np1++; } } if(np1 < 3 || np1 > 7){ *(m_BufImage+(col*y)+x) = 255; continue; } xm = x - 1; xp = x + 1; sp1 = 0; if(*(m_ResultImg+(col*ym)+x) == 0 && *(m_ResultImg+(col*ym)+xp) != 0) sp1++; if(*(m_ResultImg+(col*ym)+xp) == 0 && *(m_ResultImg+(col*y)+xp) != 0) sp1++; if(*(m_ResultImg+(col*y)+xp) == 0 && *(m_ResultImg+(col*yp)+xp) != 0) sp1++; if(*(m_ResultImg+(col*yp)+xp) == 0 && *(m_ResultImg+(col*yp)+x) != 0) sp1++; if(*(m_ResultImg+(col*yp)+x) == 0 && *(m_ResultImg+(col*yp)+xm) != 0) sp1++; if(*(m_ResultImg+(col*yp)+xm) == 0 && *(m_ResultImg+(col*y)+xm) != 0) sp1++; if(*(m_ResultImg+(col*y)+xm) == 0 && *(m_ResultImg+(col*ym)+xm) != 0) sp1++; if(*(m_ResultImg+(col*ym)+xm) == 0 && *(m_ResultImg+(col*ym)+x) != 0) sp1++; if(sp1 != 1){ *(m_BufImage+(col*y)+x) = 255; continue; } if(chk == 0){ cc = *(m_ResultImg+(col*ym)+x) * *(m_ResultImg+(col*y)+xp); cc = cc * *(m_ResultImg+(col*yp)+x); cd = *(m_ResultImg+(col*y)+xp) * *(m_ResultImg+(col*yp)+x); cd = cd * *(m_ResultImg+(col*y)+xm); } else{ cc = *(m_ResultImg+(col*ym)+x) * *(m_ResultImg+(col*y)+xp); cc = cc * *(m_ResultImg+(col*y)+xm); cd = *(m_ResultImg+(col*ym)+x) * *(m_ResultImg+(col*yp)+x); cd = cd * *(m_ResultImg+(col*y)+xm); } if(cc != 0 || cd != 0){ *(m_BufImage+(col*y)+x) = 255; continue; } flag = 1; } } for( y = 1 ; y < row-1 ; y ++ ){ for( x = 1 ; x < col-1 ; x ++ ){ *(m_ResultImg+(col*y)+x) = *(m_BufImage+(col*y)+x); } } }while(!(chk == 1 && flag == 0)); // 4연결점 처리 for( y = 1 ; y < row-1 ; y ++ ){ yp = y + 1; ym = y - 1; for( x = 1 ; x < col-1 ; x ++ ){ if(*(m_ResultImg+(col*y)+x) == 0) continue; xm = x - 1; xp = x + 1; sp1 = 0; if(*(m_ResultImg+(col*ym)+x) == 0 && *(m_ResultImg+(col*ym)+xp) != 0) sp1++; if(*(m_ResultImg+(col*ym)+xp) == 0 && *(m_ResultImg+(col*y)+xp) != 0) sp1++; if(*(m_ResultImg+(col*y)+xp) == 0 && *(m_ResultImg+(col*yp)+xp) != 0) sp1++; if(*(m_ResultImg+(col*yp)+xp) == 0 && *(m_ResultImg+(col*yp)+x) != 0) sp1++; if(*(m_ResultImg+(col*yp)+x) == 0 && *(m_ResultImg+(col*yp)+xm) != 0) sp1++; if(*(m_ResultImg+(col*yp)+xm) == 0 && *(m_ResultImg+(col*y)+xm) != 0) sp1++; if(*(m_ResultImg+(col*y)+xm) == 0 && *(m_ResultImg+(col*ym)+xm) != 0) sp1++; if(*(m_ResultImg+(col*ym)+xm) == 0 && *(m_ResultImg+(col*ym)+x) != 0) sp1++; hv = 0; if(sp1 == 2){ if ((*(m_ResultImg+(col*ym)+x) & *(m_ResultImg+(col*y)+xp)) != 0) hv++; else if ((*(m_ResultImg+(col*y)+xp) & *(m_ResultImg+(col*yp)+x)) != 0) hv++; else if ((*(m_ResultImg+(col*yp)+x) & *(m_ResultImg+(col*y)+xm)) != 0) hv++; else if ((*(m_ResultImg+(col*y)+xm) & *(m_ResultImg+(col*ym)+x)) != 0) hv++; if(hv == 1) *(m_ResultImg+(col*y)+x) = 0; } else if(sp1 == 3){ if ((*(m_ResultImg+(col*ym)+x) & *(m_ResultImg+(col*y)+xm) & *(m_ResultImg+(col*y)+xp)) != 0) hv++; else if ((*(m_ResultImg+(col*yp)+x) & *(m_ResultImg+(col*y)+xm) & *(m_ResultImg+(col*y)+xp)) != 0) hv++; else if ((*(m_ResultImg+(col*ym)+x) & *(m_ResultImg+(col*yp)+x) & *(m_ResultImg+(col*y)+xm)) != 0) hv++; else if ((*(m_ResultImg+(col*ym)+x) & *(m_ResultImg+(col*yp)+x) & *(m_ResultImg+(col*y)+xp)) != 0) hv++; if(hv == 1) *(m_ResultImg+(col*y)+x) = 0; } } } // 들어온 배열에 재복사 for( y = 0 ; y < row ; y ++ ){ for( x = 0 ; x < col ; x++ ){ input.at<uchar>(y,x) = *(m_ResultImg+(col*y)+x); } } free(m_BufImage); free(m_ResultImg); } |
[결과 이미지]
728x90
반응형
'Programming > Image Processing' 카테고리의 다른 글
[OpenCV] Labeling(레이블링, 라벨링) 이론 및 소스코드 (3) | 2014.01.14 |
---|---|
배열접근방식 세선화(Thinning) 함수 소스코드 (0) | 2014.01.13 |
[OpenCV] Edge Detection(Canny Filter) 사용하기 예제 (0) | 2014.01.10 |
[OpenCV] Edge detection(Sobel Filter) 사용하기 예제 (1) | 2014.01.10 |
[OpenCV] IplImage* ↔ Mat Conversion (0) | 2014.01.10 |