에지 탐지란 배경과 전경을 분리하는 작업이며, 객체 인식을 위해서는 에지 탐지가 필수적이다. 이전 글에서 살펴본 블러링이 영상을 흐릿하게 했다면, 에지 탐지를 통해 경계에 있는 pixel만 골라서 영상을 선명하게 하는 것을 Sharping(샤프닝)이라고 한다.

 

1. 기본 미분 필터

경계를 검출하기 위해서는 pixel 값이 급격하게 변하는 지점을 찾아야 하는데, 이는 미분 연산을 통해 찾을 수 있다. 그러나 pixel 값은 이산적인 데이터를 지니기 때문에 정확하게 미분 연산을 수행할 수 없다. 따라서, 근사화를 거쳐 계산이 이루어진다. 이를 컨볼루션 커널로 만들면 다음과 같다.

 

좌측은 세로 방향의 경계를, 우측은 가로 방향의 경계를 탐지한다.

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 기본 미분 kernel 생성
G_x = np.array([[-1,1]])
G_y = np.array([[-1], [1]])

# 필터 적용
edge_x = cv2.filter2D(img, -1, G_x)
edge_y = cv2.filter2D(img, -1, G_y)

cv2.imshow('Edge_x', edge_x) # 수평 에지
cv2.imshow('Edge_y', edge_y) # 수직 에지

cv2.waitKey()

수평 edge(좌) / 수직 edge(우)

x방향과 y방향의 각 미분 커널을 cv2.filter2D()에 적용하면 세로 방향, 가로 방향 경계가 검출됨을 확인할 수 있다.

 

2. 로버츠 교차 필터

로버츠 교차 필터는 기본 미분 커널에서 개선된 것이다. 대각선 방향으로 1과 -1을 배치해서 사선 경계 검출 효과를 높이는데 시도하며 실행 속도를 높였지만 edge 강도가 약하다는 단점이 있다. 로버츠 커널은 다음과 같다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 로버츠 교차 kernel 생성
G_x = np.array([[1,0],[0,-1]])
G_y = np.array([[0, 1],[-1, 0]])

# 필터 적용
edge_x = cv2.filter2D(img, -1, G_x)
edge_y = cv2.filter2D(img, -1, G_y)

cv2.imshow('Edge_x', edge_x)
cv2.imshow('Edge_y', edge_y)

cv2.waitKey()
cv2.destroyAllWindows()

수평 edge(좌) / 수직 edge(우)

 

3. 프리윗 필터

프리윗 필터는 edge 강도를 높여서 방향에 따라 동등한 edge를 찾는 장점을 지닌다. 하지만, 사선에 대한 검출은 약한 것이 단점이며, 이 커널은 다음과 같다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 프리윗 kernel 생성
G_x = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]])
G_y = np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]])

# 필터 적용
edge_x = cv2.filter2D(img, -1, G_x)
edge_y = cv2.filter2D(img, -1, G_y)

cv2.imshow('Edge_x', edge_x)
cv2.imshow('Edge_y', edge_y)

cv2.waitKey()
cv2.destroyAllWindows()

수평 edge(좌) / 수직 edge(우)

 

4. Sobel 필터

Sobel 필터는 중심 pixel의 차분 비중을 2배로 높여 수평, 수직, 사선 방향 모두 강하게 검출할 수 있다. 1차 미분법을 적용한 대표적인 필터로 로버츠, 프리윗 필터에 비해 많이 사용된다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# Sobel kernel 직접 생성 후 적용
G_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
G_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])

edge_x = cv2.filter2D(img, -1, G_x)
edge_y = cv2.filter2D(img, -1, G_y)

# cv2.Sobel()로 kernel 생성 및 적용
sobel_x = cv2.Sobel(img, -1, 1, 0, ksize = 3)
sobel_y = cv2.Sobel(img, -1, 0, 1, ksize = 3)

cv2.imshow('Edge_x', edge_x)
cv2.imshow('Edge_y', edge_y)

cv2.waitKey()
cv2.destroyAllWindows()

Sobel 커널을 직접 생성할 수도 있지만 cv2.Sobel()을 사용하면 커널을 생성하고 적용하는 것까지 한 번에 수행할 수 있다. cv2.Sobel()은 입력 영상, 출력 영상 dtype, x 미분 차수, y 미분 차수, [커널 크기, 미분 계수, 연산 결과에 더할 값]이 인자로 구성된다. cv2.imshow('Edge_x', edge_x)와 cv2.imshow('Edge_x', sobel_x)은 같게 출력된다.

수평 edge(좌) / 수직 edge(우)

 

5. Scharr 필터

Sobel 필터의 경우 커널의 크기가 작거나 클 때 중심에서 멀어질수록 edge에 대한 방향성의 정확도가 떨어지는 단점이 있다. 이를 개선한 것이 Scharr 필터이며, 커널은 다음과 같다.

 

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# scharr kernel 직접 생성 후 적용
G_x = np.array([[-3, 0, 3], [-10, 0, 10], [-3, 0, 3]])
G_y = np.array([[-3, -10, -3], [0, 0, 0], [3, 10, 3]])

edge_x = cv2.filter2D(img, -1, G_x)
edge_y = cv2.filter2D(img, -1, G_y)

# cv2.Scharr()를 통해 kernel 생성 및 적용
scharr_x = cv2.Scharr(img, -1, 1, 0)
scharr_y = cv2.Scharr(img, -1, 0, 1)

cv2.imshow('Edge_x', edge_x)
cv2.imshow('Edge_y', edge_y)

cv2.waitKey()
cv2.destroyAllWindows()

수평 edge(좌) / 수직 edge(우)

cv2.Scharr()은 커널 크기를 지정하는 ksize가 인자로 필요하지 않는 것을 제외하고는 cv2.Sobel()과 동일하게 사용할 수 있다. 또한, cv2.imshow('Edge_x', edge_x)와 cv2.imshow('Edge_x', scharr_x)은 같게 출력된다.

 

6. 라플라시안 필터

위의 5가지는 1차 미분 필터 및 개선된 필터에 대해 살펴보았다. 1차 미분 필터를 한 번 더 미분한 것이 2차 미분 필터이며, 라플라시안 필터가 대표적인 예시이다. 2차 미분 필터를 사용하면 더욱 확실한 경계를 검출할 수 있는 것이 장점이다. 1차 미분은 edge 검출을, 2차 미분은 영상 개선을 할 때 주로 사용하므로 용도에 따라 사용하는 방향성이 달라진다.

라플라시안 필터를 커널로 표현하면 다음과 같다.

 

cv2.Laplacian()을 통해 라플라시안 필터를 사용할 수 있으며, 사용 방법은 cv2.Sobel()과 같다.

import cv2
import numpy as np

img = cv2.imread('image.jpg')

edge = cv2.Laplacian(img, -1)

cv2.imshow('Laplacian', edge)
cv2.waitKey()
cv2.destroyAllWindows()

Laplacian 필터 적용

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기