Perception/OpenCV

[OpenCV] 캐니 에지 검출(Canny edge detection)

고집호랑이 2023. 2. 12. 07:30

개요 

살면서 보호색이 뛰어난 동물들이나 곤충들을 본 적 있을 것입니다. 이들은 자신의 주변 환경과 비슷한 색이나 밝기를 가지고 있어서 사람 눈으로 잘 인식할 수 없을 때가 있죠.

 

이렇게 사람은 눈으로 들어온 이미지의 색이나 밝기가 급격하게 변하는 부분으로 테두리를 인식하고 사물을 구별해 냅니다. 

 

컴퓨터에서도 이와 마찬가지로 이미지의 테두리(edge)를 인식하게 할 수 있습니다. 다만 컴퓨터에서의 이미지는 RGB 값 행렬인 픽셀로 이루어져 있기 때문에 픽셀 값이 급격하게 변하는 부분을 이용하면 테두리를 인식할 수 있습니다.

 

에지(edge) 검출 방법

결국 컴퓨터에 들어온 이미지 픽셀 값의 변화율을 측정해 변화율이 큰 부분을 고르면 edge를 인식하게 할 수 있습니다. 

 

픽셀 값의 변화율은 저희에게 익숙한 미분(derviative)으로 구할 수 있습니다. 아래 그림과 같이 미분값이 특정 값 T보다 크다면 edge로 판단하는 것입니다.

 

에지 검출 방법

 

하지만 컴퓨터로 들어온 이미지의 픽셀 값은 행렬로 나타나 있기 때문에 연속함수가 아닌 이산함수입니다. 

 

이산함수는 미분이 불가능하므로 아래와 같은 미분 근사화 방법을 사용해줘야 합니다.

 

3가지 미분 근사화 방법

 

영상에서 h는 픽셀의 간격을 의미하며, 보통 최소 단위인 1을 h로 사용합니다.

 

위의 세가지 미분 근사화 방법 중 자기 자신을 제회하고 바로 앞과 뒤에 있는 픽셀 값을 이용하는 중앙 차분이 오류가 가장 적기 때문에 중앙 차분이 edge detection에서 주로 사용됩니다. 

 

1차원 이산함수인 픽셀값을 중앙 차분 방식으로 미분 근사화를 한다면 아래와 같은 미분 마스크를 사용할 수 있습니다.

 

미분 마스크

 

각각 x, y 방향에 대한 미분 마스크를 그림으로 표현한 것인데, 아래의 수식과 같이 각 픽셀의 영역과 곱한 뒤 더해주는 계산을 해준다면 미분을 근사화할 수 있습니다.

 

미분 마스크를 이용한 미분 근사화

하지만 아시다시피 대부분의 이미지는 너비와 높이가 있는 2차원 이산함수 형태입니다. 따라서 2차원 이미지에서는 x 방향, y 방향 모두에 대해서 각각 미분 근사화를 해주어야 합니다. 

 

x 방향, y 방향에 대해서 각각 미분 마스크를 통한 미분 결과, 즉 편미분을 한 결과를 합쳐 그레이디언트(gradient)로 나타낸 후 그레이디언트의 크기가 임계값(threshold)보다 크다면 edge로 판단하는 것입니다. 

 

각 방향에 대한 미분 마스크는 위에서 설명한 1 x 3 또는 3 x 1 크기를 사용할 수도 있지만, 잡음을 제거하기 위해서 아래와 같은 3 x 3 크기의 정사각형 형태의 미분 마스크를 사용합니다. 

 

여러 종류의 미분마스크

 

위의 3가지 미분 마스크 중에서 오늘 배울 canny edge detection을 포함해 대부분 edge detection에 Sobel 미분 마스크를 사용합니다. 

 

캐니 에지 검출(Canny edge detection)

캐니 에지 검출 방법은 이전의 에지 검출 방법의 단점들을 해결함으로써 현재 많은 컴퓨터 비전 시스템에 사용되고 있습니다. 

 

캐니 에지 검출 방법은 위에서 나온 3 x 3 크기의 소벨 마스크를 이용해 영상의 그레이디언트를 계산한 후, 그레이디언트의 크기와 방향을 모두 고려하여 정확한 에지 위치를 찾습니다.

 

또한 에지는 서로 연결되어 있는 가능성이 높다는 점을 고려해 그레이디언트 크기가 작게 나오는 에지도 놓치지 않고 찾을 수 있습니다.

 

OpenCV에서는 이러한 Canny() 함수제공하는데, Canny() 함수는 다음과 같습니다.

 

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = False); 
# => edges
  • image: 입력 영상
  • threshold1: 하단 임계값
  • threshold2: 상단 임계값
  • edges: 출력되는 에지 영상
  • apertureSize: 사용되는 Sobel 미분 마스크의 커널 크기, 기본값은 3
  • L2gradient: 그레이디언트 계산 수식을 설정, True이면 L2 norm을 사용하고 False이면 L1 norm을 사용, 기본값 False (그레이디언트 크기를 어떻게 계산할지 결정하는 파라미터, L2가 L1보다 더 정확하지만 속도가 느리기 때문에 L1 norm을 기본값으로 사용한다.)

 

하단 임계값과 상단 임계값은 꼭 지정해주어야 하는데, 이 값은 우리가 원하는 edge 검출 결과를 얻기 위해서 실험을 통해 찾아야 합니다. 

 

상단 임계값(threshold2)가 실질적으로 edge를 판단하는 임계값이며, 하단 임계값(threshold1)은 edge 주변도 edge일 확률이 높다는 점을 이용하여 threshold2에 의해 판단된 edge와 인접한 부분을 edge로 판단할 때 사용하는 임계값입니다.

 

아래 그림은 원본 이미지와 상단 임계값(threshold2)을 각각 100, 150으로 설정했을 때의 결과입니다. 임계값이 커질수록 검출되는 edge가 적어지는 것을 확인할 수 있죠.

임계값에 따른 결과 차이

 

이렇게 상단 임계값(threshold2)을 너무 높게 설정하면 edge가 잘 검출되지 않고, 하단 임계값(threshold1)을 너무 낮게 설정하면 원치 않은 edge까지 검출할 것이기 때문에 적절한 값을 잘 찾아야 합니다. 

 

일반적으로 threshold1과 threshold2를 1:2 또는 1:3 비율로 지정합니다.

 

케니 에지 검출 예시

아래의 도로 사진에 케니 에지 검출을 사용하려고 합니다.

 

케니 에지 예시

 

import cv2

# 영상 읽기
image = cv2.imread("../road.jpg") 

# 컬러 영상을 캐니에 입력하면 RGB 각각의 픽셀값을 다 미분하므로 계산량을 줄이기 위해서 흑백으로 바꿔서 입력하는 것이 좋다.
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 

canny = cv2.Canny(gray, 100, 200)

# 에지 검출 결과를 보여줌
cv2.imshow("canny", canny)

# 키를 입력할 때까지 대기
cv2.waitKey(0)

 

캐니 에지 검출 시 입력 영상으로 컬러 영상을 입력하면 RGB 각각의 픽셀값에 대해서 전부 미분하기 때문에 계산량을 줄이기 위해서 흑백으로 바꿔서 입력하는 것이 좋습니다.

 

아래와 같이 이미지의 에지가 잘 검출된 것을 확인할 수 있습니다.

캐니 에지 검출 결과
케니 에지 검출 결과