파이썬 강의/openCV

파이썬 openCV 17. 에지검출 : 유사 연산자(Homogeneity)와 차 연산자(Difference)기법

마리사라 2020. 12. 6. 21:36
반응형

파이썬 openCV 17번째 강의는 에지 검출의 방법으로 유사 연산자(Homogeneity)와 차 연산자(Difference) 기법을 알려드리겠습니다.

 

에지 검출은 대표적으로 7가지의 방법이 있으며, 3번의 강의에 걸쳐 설명하도록 하겠습니다.


0. 엣지(edge)?

영상처리에서 에지 또는 엣지는 밝기가 낮은 값에서 높은 값으로, 또는 높은 값에서 낮은 값으로 변하는 지점을 말합니다. 그렇기에 이는 영상을 구성하는 객체 간의 경계선이 되게 됩니다.

 

이러한 에지를 검출하여 컴퓨터가 물체를 식별하고, 그 모양과 크기를 인지할 수 있도록 하는 정보를 얻을 수 있습니다.

엣지의 패턴

이러한 에지의 패턴으로는 지붕형, 선형, 계단형, 경사형의 4가지가 있습니다.

 

지붕형은 밝기의 차이가 급격히 변하지 않으므로, 에지가 두껍게 나타남을 뜻합니다.

 

선형은 지붕형과 대비되게 밝기의 차이가 급격히 변하는 지점이므로, 엣지가 얇게 나타남을 뜻합니다.

 

계단형은 잘 없는 경우이지만, 선형과 비슷하게 얇지만, 좀 더 샤프(Sharp)하게 나타난다고 표현합니다.

 

경사형은 지붕형과 비슷하게 에지가 두껍지만, 경사에 해당하는 폭의 크기만큼의 두께를 가집니다.


1. openCV에서의 유사 연산자와 차 연산자

 

img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
height, width = gray.shape

엣지 처리기법은 밝기를 가지고 연산하는 기법입니다. 그래서 이미지는 그레이 스케일(흑백)로 처리하게 됩니다.

 

homo = np.zeros(gray.shape, dtype=np.uint8)
diff = np.zeros(gray.shape, dtype=np.uint8)

이후 계산 결과를 담을 빈 배열을 만들어줍니다.

 

이제 유사 연산자 기법과 차 연산자 기법의 원리를 알려드리겠습니다.

 

원 화소와 주변 화소들

유사 연산자 기법은 계산하고자 하는 원래의 화소를 center로 잡고, 그 주변의 화소들과의 뺄셈을 통해 계산하는 방식입니다.

 

New Pixel = max(|center - m1| ... |center-m8|)

유사 연산자 기법은 center를 기준으로 8방향의 모든 화소와 뺄셈을 시행합니다. 그리고 그중에서 가장 큰 값을 출력 화소에 저장합니다. 이러한 방식은 하나의 화소당 뺄셈이 8번씩 계산되기에 시간이 많이 소요되는 방법입니다.

 

차 연산자 기법의 원 화소(m5)와 주변 화소들

차 연산자 기법은 center화소가 따로 없이, 그 주변의 화소만을 이용해서 계산하는 방식입니다.

New Pixel = max(|m1 - m9|, |m3-m7|, |m2 - m8|, |m6 - m4|)

차 연산자 기법은 원래의 화소는 계산에 포함되지 않습니다. 또한 계산이 4번으로, 유사 연산자 기법의 절반이 되어 연산 시간이 빠릅니다.

 

이제 위 공식을 코드로 적용해 보겠습니다.

for i in range(height):
    for j in range(width):
        a = []
        for x in [-1, 0, 1]:
            for y in [-1, 0, 1]:
                if x == 0 and y == 0:
                    continue
                try:
                    center = int(gray[i][j])
                    m = int(gray[i + x][j + y])
                    a.append(abs(center - m))
                except:
                    continue
                out[i][j] = max(a)

유사 연산자 기법을 중첩 for문으로 적용한 코드입니다. 자기 자신의 좌표인 [i, j]를 기준으로 -1, 0, 1만큼 더하여 총 9칸을 계산하는 방식입니다. 이때 자기 자신인 x = 0, y = 0은 계산에서 제외했습니다. 이후 try | except문으로 계산을 하게 됩니다.

이때 try | except문을 사용하는 이유는 [i, j]가 [0, 0]인 경우 [x, y]가 [-1, -1]이 되면 계산하는 좌표가 [-1, -1]을 찾게 되며, 이는 계산 에러가 납니다. 그렇기에 자동으로 에러를 continue 할 수 있도록 try | except문을 사용했습니다. 이는 제로 패딩을 통해 예방할 수 있지만, 제로 패딩은 다음에 알려드리겠습니다.

 

for i in range(height):
    for j in range(width):
        a = []
        for k in [-1, 0, 1]:
            try:
                b = int(abs(gray[i - 1][j - k]))
                c = int(abs(gray[i + 1][j + k]))
                a.append(abs(b - c))
            except:
                continue
        try:
            b = int(abs(gray[i][j - 1]))
            c = int(abs(gray[i][j + 1]))
            a.append(abs(b - c))
        except:
            continue
        diff[i][j] = max(a)

차 연산자 기법을 중첩 for문으로 구현한 결과입니다. 이때 |m2 - m8|은 따로 계산합니다.

 

이제 실행시켜보겠습니다.

 

원본 영상
유사 연산자 기법
차 연산자 기법

이렇게 두 기법은 계산 식은 다르지만 둘 모두 에지를 검출할 수 있음을 알 수 있습니다.


2. 마치며

유사 연산자 기법과 차 연산자 기법은 연산량이 많아서 자주 사용되지 않는 기법입니다. 하지만 다른 기법을 배우기 전에 거쳐가는 과정으로 생각하시고 공부하시면 좋을 것 같습니다.

 

def edge():
    img = cv2.imread('lenna.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    height, width = gray.shape
    homo = np.zeros(gray.shape, dtype=np.uint8)
    diff = np.zeros(gray.shape, dtype=np.uint8)
    for i in range(height):
        for j in range(width):
            a = []
            for x in [-1, 0, 1]:
                for y in [-1, 0, 1]:
                    if x == 0 and y == 0:
                        continue
                    try:
                        center = int(gray[i][j])
                        m = int(gray[i + x][j + y])
                        a.append(abs(center - m))
                    except:
                        continue
                    homo[i][j] = max(a)

    for i in range(height):
        for j in range(width):
            a = []
            for k in [-1, 0, 1]:
                try:
                    b = int(abs(gray[i - 1][j - k]))
                    c = int(abs(gray[i + 1][j + k]))
                    a.append(abs(b - c))
                except:
                    continue
            try:
                b = int(abs(gray[i][j - 1]))
                c = int(abs(gray[i][j + 1]))
                a.append(abs(b - c))
            except:
                continue
            diff[i][j] = max(a)

    cv2.imshow('original', gray)
    cv2.imshow('homo', homo)
    cv2.imshow('diff', diff)
    cv2.waitKey(0)
반응형