해당 포스트는 "OpenCV로 배우는 영상 처리 및 응용", "C++ API OpenCV 프로그래밍" 책의 내용을 요약한 것이다.



※ 1차 미분 필터링

- void getDerivKernels(OutputArray kx, OutputArray ky, int dx, int dy, int ksize, bool normalize=false, int ktype= CV_32F)

: 영상에서 미분을 계산하기 위한 ksize*1 크기의 선형 마스크를 kx와 ky에 저장한다. dx와 dy는 행과 열의 미분계수인데 보통 1이 들어간다. ksize는 커널의 크기로 ksize=CV_SCHARR(-1)이면 Scharr 3*3 커널이 생성되고 ksize=1,3,5,7 이면 Sobel 커널이 생성된다. setFilter2D 함수에서 생성된 커널을 사용하여 필터링할 수 있고 2D 필터는 ky*kx.t() 로 얻을 수 있다.


- void Sobel(InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta =0, int borderType=BORDER_DEFAULT)

: src에 Sobel 마스크를 적용하여 dst에 저장한다. dx와 dy는 행과 열에 대한 미분 차수이고 ksize는 소벨 윈도우 마스크의 크기를 말한다. 만약 dx=1, dy=0 이면 x 방향 마스크가 되고 dx=0, dy=1이면 y방향 마스크가 된다. ksize가 -1 이면 Scharr 3*3 마스크가 적용된다. 


- void Scharr(InputArray src, OutputArray dst, int ddepth, int dx, int dy, double scale=1, double delta=0, int borderType=BORDER_DEFAULT)

: src에 Scharr 마스크를 적용하여 dst에 저장한다. 



※ 2차 미분 필터링

- void Laplacian(InputArray src, OutputArrya dst, int ddepth, int ksize=1, doule scale=1, double delta=0, int borderType=BORDER_DEFAULT)

: src에 대해서 라플라시안 마스크를 적용한 후 dst에 저장한다.


- LoG(Laplacian of Gaussian) 필터링

: 라플라시안 마스크는 잡음에 상당히 민감하다. 그래서 잡음을 줄이기 위해서 가우시안 마스크로 스무딩을 하여 잡음 제거 후 라플라시안을 적용할 수 있다. 이렇게 가우시안 마스크와 라플라시안을 같이 계산한 게 LoG 필터링이다. 에지는 LoG 필터링된 결과에서 0-교차하는 위치이다. 0-교차는 자신과 옆 픽셀의 곱이 마이너스 인 것을 말한다. 다음은 LoG(x,y)를 계산하는 수식이다.



#include "opencv2\opencv.hpp" using namespace cv; using namespace std; void ZeroCrossing(Mat &src, Mat &dst, int threshold); int main() { Mat srcImage = imread("lena.jpg", IMREAD_GRAYSCALE); if (srcImage.empty()) return -1; const int ksize = 9; float logArr[ksize*ksize]; int s, t, k = 0; float g; float sigma= 0.3f*(ksize/2-1.0f) + 0.8f; for (s = -ksize / 2; s <= ksize / 2; s++) //LoG 필터 마스크 생성 for (t = -ksize / 2; t <= ksize / 2; t++) { g = exp(-((float)s*s + (float)t*t) / (2 * sigma*sigma)); g *= (1 - ((float)s*s + (float)t*t) / (2 * sigma*sigma)); g /= (3.141592f*sigma*sigma*sigma*sigma); logArr[k++] = -g; } Mat logKernel(ksize, ksize, CV_32F, logArr); Mat logImage; filter2D(srcImage, logImage, CV_32F, logKernel); Mat dstImage; ZeroCrossing(logImage, dstImage, 1); //0-교차점 탐색 후 에지 검출 imshow("dstImage", dstImage); waitKey(); return 0; } void ZeroCrossing(Mat &src, Mat &dst, int th) { int x, y; double a, b; Mat zeroCrossH(src.size(), CV_32F, Scalar::all(0)); Mat_<float> _src(src); for (y = 1; y<src.rows - 1; y++) //가로 방향으로 0교차점 탐색 for (x = 1; x<src.cols - 1; x++) { a = _src(y, x); b = _src(y, x + 1); if (a == 0) a = _src(y, x - 1); if (a*b < 0) zeroCrossH.at<float>(y, x) = fabs(a) + fabs(b); else zeroCrossH.at<float>(y, x) = 0; } Mat zeroCrossV(src.size(), CV_32F, Scalar::all(0)); for (y = 1; y<src.rows - 1; y++) //세로 방향으로 0교차점 탐색 for (x = 1; x<src.cols - 1; x++) { a = _src(y, x); b = _src(y + 1, x); if (a == 0) a = _src(y - 1, x); if (a*b < 0) zeroCrossV.at<float>(y, x) = fabs(a) + fabs(b); else zeroCrossV.at<float>(y, x) = 0; } Mat zeroCross(src.size(), CV_32F, Scalar::all(0)); add(zeroCrossH, zeroCrossV, zeroCross); threshold(zeroCross, dst, th, 255, THRESH_BINARY); }


+ Recent posts