우리는 어떻게 물체들을 서로 구별할 수 있을까? 아마 물체의 테두리를 통해 구별할 수 있을 것이다. 사람은 눈으로 들어온 장면에서 물체의 색이나 명암이 급격하게 변하는 곳을 테두리로 인식하기 때문이다. 그렇기 때문에 우리는 가끔 풀에 앉은 벌레들을 인식하지 못하는 경우가 있다. 벌레가 주변 환경과 비슷한 색과 밝기를 가지고 있기 때문이다.
컴퓨터도 메커니즘을 갖는다. 이미지에서 픽셀값이 급격하게 변하는 부분을 에지로 인식하게 된다.

물체의 경계에서 급격한 명암 변화가 있으므로 변화량을 측정하는 미분을 활용하여 에지를 검출할 수 있다.
미분식을 알다시피 아래와 같다.
영상의 픽셀은 정수값을 가지므로 최소 변화량 이 될 것이다. 그러므로
위처럼 변하게 된다. 이를 미분 마스크로 변환할 수 있다.
실제 연산을 진행할 때는 를 기준으로 연산을 한다. 즉, -1을 기준으로 연산을 하면 된다.
실제 영상이 일때, 미분 마스크를 이용해 아래와 같이 1차 미분 결과를 얻을 수 있다.
1차 미분 결과에 다시 한번 미분 마스크로 연산을 하면 2차 미분 결과를 얻을 수 있다.

위처럼 2차 미분 마스크를 바로 구할 수도 있다.
1차 미분결과에서는 봉우리를 통해, 2차 미분결과에서는 Zero crossing point를 통해 에지를 추정할 수 있다.
-1과 1로만 이루어진 미분 마스크를 이용하면, 이 마스크는 대칭도 아닐 뿐더러 실제 영상에 있는 잡음을 흡수하기 위해 보통 아래와 같이 크기 3의 미분 마스크로 확장하여 사용한다.

1차원 마스크를 2차원으로 확장한 대표적인 연산자는 아래와 같다.
이번 포스팅에선 Sobel 연산자를 다뤄보기로 하겠다. 실제로 Sobel 연산자가 Prewitt 연산자보다 더 널리 사용되기도 한다.
x, y 방향에 대해서 각각 Sobel 연산자를 통한 편미분 결과를 합쳐 그래디언트로 나타낸 후, 이를 이용하여 에지 강도와 그래디언트 방향을 알아낼 수 있다.
에지 강도는 pixel(y,x)이 에지일 가능성 또는 신뢰도를 나타내고, 그래디언트 방향은 pixel값이 가장 급격하게 증가하는 방향을 의미한다.
에지 방향은 그래디언트 방향과 90° 차이가 나는데, 에지의 진행 방향을 뜻한다.

이제 실제로 OpenCV를 이용하여 소벨 에지를 검출해볼 것이다.
import cv2 as cv
img = cv.imread('img1.jpg')
# 계산량을 감소시키기 위해 흑백 이미지로 변환
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# 소벨 연산자 적용, CV_32F: 32bit float pointing number
grad_x = cv.Sobel(gray, cv.CV_32F, dx=1, dy=0, ksize=3)
grad_y = cv.Sobel(gray, cv.CV_32F, dx=0, dy=1, ksize=3)
# 절댓값을 취해 양수 이미지로 변환
sobel_x = cv.convertScaleAbs(grad_x)
sobel_y = cv.convertScaleAbs(grad_y)
# 에지 강도 계산
edge_strength = cv.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0)
cv.imshow('Original', gray)
cv.imshow('sobelx', sobel_x)
cv.imshow('sobely', sobel_y)
cv.imshow('edge strength', edge_strength)
cv.waitKey(0)
cv.destroyAllWindows()
#src: 입력 영상, ddepth: 출력 영상 데이터 타입, dx: x방향 미분차수, dy: y방향 미분차수, ksize: 커널사이즈
cv.Sobel(src, ddepth, dx, dy, ksize)
