파이썬 강의/openCV

파이썬 openCV 18. 에지검출 : 로버츠(roberts), 프리윗(prewitt), 소벨(sobel)

마리사라 2020. 12. 7. 20:53
반응형

파이썬 openCV 18번째 강의는 에지 검출 : 로버츠(roberts), 프리윗(prewitt), 소벨(sobel)입니다. 이 세 가지는 대표적인 에지 검출의 방법 중 하나입니다. 세 가지 모두 방법이 비슷하여 한번에 알려드리도록 하겠습니다.


0. 로버츠(roberts), 프리윗(prewitt), 소벨(sobel)?

로버츠, 프리윗, 소벨 모두 에지검출의 방법 중 하나입니다. 이 세 가지는 이전 강의에서의 유사 연산자와 차 연산자와는 달리 미분을 이용한 방법입니다.

 

에지 패턴

이전 강의에서 4가지의 에지 패턴이 있다고 말씀드렸습니다. 이때 패턴들을 보시면 밝기의 고저가 있음을 확인하실 수 있습니다. 이러한 기울기를 미분하면 값이 나오게 되고, 그 값을 토대로 에지를 검출하게 됩니다. 하지만 일반적인 프로그램에서는 직접 미분을 하지 않고, 아래와 같은 공식을 토대로 에지를 검출합니다.

 

로버츠 공식
프리윗 공식
소벨 공색

각각의 공식은 장단점이 존재합니다.

 

로버츠는 공식이 간단하여 계산 속도가 빠르며, 소벨은 매우 뚜렷한 에지가 검출됩니다. 마지막으로 프리윗은 소벨보다 간단하여 속도가 빠르지만 로버츠보다 더 좋은 에지를 검출할 수 있습니다.


1. openCV에서의 로버츠(roberts), 프리윗(prewitt), 소벨(sobel)

이제 로버츠, 프리윗, 소벨을 적용해 보겠습니다.

 

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


항상 그렇듯, 처음에는 이미지를 불러와 줍니다. 저번 강의에서도 말씀드렸다 시피 에지 검출은 그레이 스케일에서 진행되므로 그레이 스케일로 변경해주는 것도 잊지 않습니다.

 

roberts_x = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 0]])
roberts_y = np.array([[0, 0, -1], [0, 1, 0], [0, 0, 0]])

prewitt_x = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])
prewitt_y = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]])

sobel_x = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
sobel_y = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])

이제 각각의 공식에 맞는 배열을 생성해 줍니다. 

 

roberts_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, roberts_x))
roberts_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, roberts_y))

prewitt_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, prewitt_x))
prewitt_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, prewitt_y))

sobel_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, sobel_x))
sobel_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, sobel_y))

이제 각각의 공식에 맞게 계산해주겠습니다. 이때 필요한 함수는 filter2D와 convertScaleAbs입니다. filter2D는 저번에도 알려드렸다 시피 마스크를 적용하는 함수입니다. 즉 그레이 스케일에 각각의 공식의 가로와 세로 공식에 맞게 적용하는 부분입니다.

 

convertScaleAbs는 각각의 값을 절대값화시키고 정수화 시키는 작업입니다. 이를 거쳐주어야 출력하였을 때 정상적인 이미지가 나옵니다.

 

prewitt = cv2.addWeighted(prewitt_x, 1, prewitt_y, 1, 0)
roberts = cv2.addWeighted(roberts_x, 1, roberts_y, 1, 0)
sobel = cv2.addWeighted(sobel_x, 1, sobel_y, 1, 0)

이제 마지막으로 각각의 x와 y값을 하나의 이미지로 합쳐줍니다. 그를 위해 addWeighted함수를 사용합니다. 이 함수는 굳이 깊게 설명하지 않겠습니다.

 

이제 결과를 확인해 보겠습니다.

 

원본 영상
로버츠 적용 영상
프리윗 적용 영상
소벨 적용 영상

결과는 로버츠가 가장 연한(얇은) 선이 나오며, 소벨과 프리윗은 비슷한 결과가 나옵니다. 하지만 소벨이 조금 더 진한(두꺼운) 에지가 검출됩니다.


2. 마치며

로버츠, 프리윗, 소벨의 공식중에서 가장 많이 사용되는 것은 가장 좋은 에지를 검출할 수 있는 소벨 문입니다. 따라서 나머지 두 공식은 이러한 것이 있다 정도로만 알아두시면 좋을 것 같습니다.

 

def differential():
    img = cv2.imread('lenna.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    roberts_x = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, 0]])
    roberts_y = np.array([[0, 0, -1], [0, 1, 0], [0, 0, 0]])

    prewitt_x = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])
    prewitt_y = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]])

    sobel_x = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
    sobel_y = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])

    roberts_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, roberts_x))
    roberts_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, roberts_y))

    prewitt_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, prewitt_x))
    prewitt_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, prewitt_y))

    sobel_x = cv2.convertScaleAbs(cv2.filter2D(gray, -1, sobel_x))
    sobel_y = cv2.convertScaleAbs(cv2.filter2D(gray, -1, sobel_y))

    prewitt = cv2.addWeighted(prewitt_x, 1, prewitt_y, 1, 0)
    roberts = cv2.addWeighted(roberts_x, 1, roberts_y, 1, 0)
    sobel = cv2.addWeighted(sobel_x, 1, sobel_y, 1, 0)

    cv2.imshow('original', gray)
    cv2.imshow('roberts', roberts)
    cv2.imshow('prewitt', prewitt)
    cv2.imshow('sobel', sobel)
    cv2.waitKey(0)

 

반응형