[OpenCV]엣지에 관한 고찰

유경박·2023년 12월 6일
0

이미지 처리에서 엣지란 주로 "가장자리, 모서리" 라는 뜻에서 확장되어 윤곽선, 경계선정도로 표현할수 있다. 주로 엣지 검출을 통해 객체의 크기, 위치, 모양 그리고 검출영역의 방향성 등을 알수 있으며 기본적이면서도 중요한 이론이다.

가장 기초적인 이론으로는 유사/차연산자 에지 검출이 있으며 방향성까지 고려를한다면 1차, 2차 미분 마스크를 이용한 엣지 검출등 점점 어려워지는것 같다.

이 포스팅에서는 간단하게 유사/차연산 및 1차 미분 마스크(로버츠 마스크)까지 정도만 살펴보도록 하자.

유사 연산 & 차 연산

유사 연산 & 차 연산의 에지 검출의 방법은 심플하면서도 학습의 이론에는 가장 이상적인 방법이 아닐까 한다. 먼저 처리 방식은 다음과 같다.

ex. 3 * 3 행렬이 있다고 가정해보자.

m0 | m1 |m2
m3 | C |m5
m6 | m7 |m8

-유사 연산 출력화소 = max(|C-m0|, |c-m1|, |c-m2| ... |C-m8|)
-차 연산 출력화소 = max(|m0-m8|, |m1-m7|, |m2-m6|,|m3-m5|)

출력화소의 식은 위와같다. 즉 차분을 이용하여 임계값을 넘어서면 에지로 지정한다는 의미인데 두개 다 구현해본결과 결과만으로 봤을때는 큰 차이가 없고 차후 머신러닝 & 딥러닝에서도 쓰이는 목적에 따라 나뉘어지는것 같다.
(아직 머신러닝&딥러닝쪽은 공부를 안해서 잘 모르겠다.)

두 연산의 목적은 다음과 같다.

  • 유사 연산 : 이미지 분류, 객체 인식, 이미지 검색
  • 차 연산 : 이미지 처리에서 이미지 잡음 제거, 움직임 감지, 영상 비교

즉, 각 연산 이름에서 알수 있듯이 유사점 및 패턴을 분석하는데 사용하고 차 연산은 차이를 조정하는데 사용하는거 같다.

1차 미분 마스크

에지란 일종의 화소의 밝기가 급격히 변하는 부분으로써, 방향에 따라 엣지를 검출하는 방법이 있는데 그것이 x, y 방향 마스크를 갖고 회선 수식을 적용하는 것이다. 이러한 방식을 1차 미분 마스크라고 한다.

수평 마스크

    -1, 0 , 1,
    -1, 0 , 1,
    -1, 0 , 1

수직 마스크

    -1, -1, -1,
     0,  0,  0,
     1,  1,  1

그럼 이제 샘플 코드를 작성해보자.

샘플 코드

1)차분 연산 예제

[OpenCV]/Blur/Shapening에 대한 고찰 에서 나왔던 예제와 거의 비슷하며, 윈도우 관련 콜백 및 ROI 설정부분은 생략한다.

 for (int j = 1; j < roi.rows - 1; ++j) {
    for (int i = 1; i < roi.cols - 1; ++i) {
        vector<uchar> mask;
        for (int u = -1; u <= 1; ++u) {
            for (int v = -1; v <= 1; ++v) {
                int yy = j + u;
                int xx = i + v;
                mask.emplace_back(grayRoi.at<uchar>(yy, xx));
            }
        }
        uchar max = 0;
        for(int idx = 0; idx < mask.size() / 2; idx++) {
            int start = mask.at(idx);
            int end = mask.at(mask.size() - 1 - idx);
            uchar diff = abs(start - end);
            if (diff > max) max = diff;
        }
        roi.at<Vec3b>(j, i) = Vec3b(max, max, max);
    }
}
roi.copyTo(image(roi_rect));
imshow(title, image);

위의 코드가 핵심로직이며 설명은 아래와 같다.

1) ROI영역만큼 반복문 수행
2) mask는 3*3행으로써, mask에 grayRoi 원소값을 넣음
3) mask.size()크기만큼을 반복문을 수행하되 mask.size/2로 구분지어 start,end를 기준으로 인덱스가 증가할때마다 각 차이의 max를 구함
4) max값을 찾으면 roi(원본이미지의 복사본)에 해당 원소값을 넣음
5) 원본이미지에 복사
순으로 진행된다.

원래 연습용으로 이것저것 만들어보다가 정리안한채로 응용해서 만들어버리니 변수명이나 구조가 짬뽕돼버렸다..--;

어찌됐든 로직은, 3채널 이미지의 원본을 roi로 저장 및 1채널 이미지로 변환후 mask를 회선 수식하여 출력영상을 만든 후 출력영상을 원본이미지에 복사한다.

2)프리윗 에지 검출
1차미분 마스크를 이용하여 에지를 검출하는 방법 중 하나로써, 이 예제는 전체이미지를 기준으로 수직, 수평, 수직+수평으로 에지 탐색한 결과에 대한 샘플 코드이다.

float verticalMask[] = {
    -1, 0 , 1,
    -1, 0 , 1,
    -1, 0 , 1
};  //수평 미분 마스크
float horizontalMask[] = {
    -1, -1 , -1,
     0, 0 , 0,
     1, 1 , 1
}; //수직 미분 마스크
Mat verticalMat,verticalMaskMat(3,3,CV_32F, verticalMask);
Mat horizontalMat,horizontalMaskMat(3,3,CV_32F, horizontalMask);
Mat diffrenceMat;
filter2D(image, verticalMat, CV_32F, verticalMaskMat); //수직으로 회선
filter2D(image, horizontalMat, CV_32F, horizontalMaskMat); //수평으로 회선
magnitude(verticalMat, horizontalMat, diffrenceMat); //수평+수직 회선  differenceMat으로 출력 
diffrenceMat.convertTo(diffrenceMat,CV_8U);
verticalMat.convertTo(verticalMat,CV_8U);
horizontalMat.convertTo(horizontalMat,CV_8U);  
imshow("미분하여 얻은결과",diffrenceMat);
//imshow("수직filter2D 얻은결과",verticalMat);
//imshow("수평filter2D 얻은결과",horizontalMat);

filter2D는 OpenCV에서 제공하는 회선함수이고, magnitude는 두 개의 Mat의 벡터 크기를 계산하는 함수로써, x방향(HoriziontalMat), y방향(VerticalMat)으로 미분된 이미지(벡터)를 계산하여 나온 영상이다.

profile
으아아

0개의 댓글