이 글에서는 파이썬(Python)을 이용해 이미지나 동영상 파일의 가장자리(edge)를 감지하는 방법을 단계별로 살펴봅니다. 이를 위해서는 OpenCV 라이브러리가 필요합니다. OpenCV는 컴퓨터 비전(computer vision) 분야를 위해 설계된 오픈소스 라이브러리로, 원래 인텔(Intel)이 개발했으며 BSD 라이선스에 따라 누구나 자유롭게 사용할 수 있습니다.
OpenCV의 기능을 사용하려면 먼저 pip를 통해 설치해야 합니다. 설치 과정에서 의존성 모듈인 NumPy도 함께 다운로드되므로 별도로 준비할 필요가 없습니다.
sudo pip3 install opencv-python
이번 예제에서는 입력으로 하나의 동영상 파일을 사용합니다. 웹캠을 연결하면 실시간으로 가장자리 검출 결과를 확인할 수도 있습니다.
여기서는 동영상 파일을 사용합니다. 원본 영상(동영상의 한 프레임)은 아래와 같습니다.

가장자리 검출은 어떻게 작동할까?
가장자리를 검출하려면 픽셀의 밝기가 급격하게 변화하는 지점을 수학적으로 판별해야 합니다.
구체적으로는 이미지의 그레이스케일(grayscale) 버전에 대한 그래디언트(gradient)를 구합니다. 컴퓨터 비전에서 픽셀이 검은색에서 흰색으로 전환되면 양의 기울기(positive slope), 흰색에서 검은색으로 전환되면 음의 기울기(negative slope)로 해석합니다. 이처럼 밝기 변화가 큰 경계선이 바로 '가장자리'입니다.
이미지의 미분을 이용한 가장자리 검출
이미지는 행렬(matrix) 형태로 저장되며, 행렬의 각 요소에는 픽셀 정보가 담겨 있습니다. 미분값을 구하려면 라플라시안(Laplacian) 연산자가 필요합니다. 라플라시안을 얻으려면 먼저 소벨(Sobel) 미분을 계산해야 하며, 소벨 미분은 이미지의 그래디언트 변화량을 구하는 데 사용됩니다.
수평 소벨 미분(Sobel X)
소벨 미분은 원본 이미지와 또 다른 행렬(커널(kernel))의 합성곱(convolution)을 통해 얻습니다. 간단한 경우 커널은 3x3 크기의 행렬입니다.
OpenCV에는 sobel() 함수가 내장되어 있어 이를 통해 소벨 미분을 쉽게 계산할 수 있습니다. Sobel X(수평 방향 미분)를 구할 때는 y 방향 차수를 0으로 지정합니다.
예제 코드
import cv2
import numpy as np
capture = cv2.VideoCapture('sample_video.mp4') # 동영상 파일에서 프레임 읽기
while(capture.isOpened()): # 동영상이 끝날 때까지 반복
ret, frame = capture.read() # 프레임 가져오기
# BGR 색상을 HSV(Hue Saturation Value) 모드로 변환
hsv_col = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Sobel X 미분 적용
sobelx = cv2.Sobel(frame,cv2.CV_64F,1,0,ksize=5)
cv2.imshow('SobelX',sobelx)
k = cv2.waitKey(5) & 0xFF
if k == 27: # 27은 ESC 키의 ASCII 코드, ESC를 누르면 종료
break
capture.release()
cv2.destroyAllWindows() # 창을 닫은 후 메모리 정리
실행 결과

수직 소벨 미분(Sobel Y)
같은 방식으로 sobel() 함수를 사용해 수직 방향의 소벨 미분(Sobel Y)도 구할 수 있습니다. 이 경우에는 x 방향 차수를 0으로 지정합니다.
예제 코드
import cv2
import numpy as np
capture = cv2.VideoCapture('sample_video.mp4') # 동영상 파일에서 프레임 읽기
while(capture.isOpened()): # 동영상이 끝날 때까지 반복
ret, frame = capture.read() # 프레임 가져오기
# BGR 색상을 HSV(Hue Saturation Value) 모드로 변환
hsv_col = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# Sobel Y 미분 적용
sobely = cv2.Sobel(frame,cv2.CV_64F,0,1,ksize=5)
cv2.imshow('SobelY',sobely)
k = cv2.waitKey(5) & 0xFF
if k == 27: # 27은 ESC 키의 ASCII 코드, ESC를 누르면 종료
break
capture.release()
cv2.destroyAllWindows() # 창을 닫은 후 메모리 정리
실행 결과

라플라시안 미분(Laplacian)
마지막으로 이미지의 라플라시안 미분을 살펴보겠습니다. OpenCV의 Laplacian() 함수를 사용하면 복잡한 연산 과정 없이도 미분값을 손쉽게 구할 수 있습니다. 라플라시안은 x, y 두 방향의 2차 미분을 동시에 반영하기 때문에 노이즈에 민감할 수 있으며, 실무에서는 가우시안 블러(Gaussian Blur)로 노이즈를 줄인 후 적용하는 것이 일반적입니다.
예제 코드
import cv2
import numpy as np
capture = cv2.VideoCapture('sample_video.mp4') # 동영상 파일에서 프레임 읽기
while(capture.isOpened()): # 동영상이 끝날 때까지 반복
ret, frame = capture.read() # 프레임 가져오기
# BGR 색상을 HSV(Hue Saturation Value) 모드로 변환
hsv_col = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# 라플라시안 미분 적용
laplacian = cv2.Laplacian(frame,cv2.CV_64F)
cv2.imshow('Laplacian',laplacian)
k = cv2.waitKey(5) & 0xFF
if k == 27: # 27은 ESC 키의 ASCII 코드, ESC를 누르면 종료
break
capture.release()
cv2.destroyAllWindows() # 창을 닫은 후 메모리 정리
실행 결과
