이미지에서 밝기 또는 색상이 급격하게 변하는 부분. 따라서 엣지를 찾는 가장 간단한 방법은 이미지를 “미분”하여 밝기(intensity)의 변화율(gradient)을 확인하는 것이다. gradient는 방향과 크기를 갖는 벡터(vector)로, 밝기가 변하는 방향(엣지의 방향)과 그 정도를 나타낸다.
이미지에서 엣지를 찾는 것은 컴퓨터 비전, 이미지 처리 분야에서 아주 중요하고 기초적인 단계라고 할 수 있다.
2차 미분을 사용하여 이미지의 경계를 검출한다.
라플라시안 필터는 주로 다음과 같은 형태이다.
또는
장점: 빠른 연산. 모든 방향 엣지를 잘 감지
단점: 노이즈에 매우 민감함
x축 방향, y축 방향의 1차 미분(gradient)를 계산하여 이미지의 경계를 검출한다. Gaussian smoothing과 미분을 결합하여 노이즈에 강하다. (중간 행 또는 열이 더 큰 가중치를 가짐 → 가우시안 스무딩과 유사한 효과)
소벨 필터는 다음과 같은 형태이다.
그래디언트 크기 (gradient magnitude) :
그래디언트 방향 (gradient direction) :
장점: Laplacian에 비해 노이즈에 강하며, 수평/수직 방향 엣지를 잘 감지
단점: 대각선 방향 엣지 검출에 약함
커널 계산 방법
다음과 같은 3x3 픽셀 이미지가 있다고 하자.
각 위치에서 커널과 이미지의 해당 픽셀 부분을 곱한 후 결과를 모두 더한다.
그러면 x축 방향 소벨 필터를 적용한다고 할 때, 이므로
1x-1 + 2x0 + 3x1 + 1x-2 + 2x0 + 3x2 + 1x-1 + 2x0 + 3x1 = (-1) + 0 + 3 + (-2) + 0 + 6 + (-1) + 0 + 3 = (-4) + 12 = 8 이다. 이 값을 중앙 픽셀에 적용하면 된다.
즉, x방향 소벨 필터 적용 결과는 이 될 것이다. 같은 방식으로 y축 방향 소벨 필터 적용 결과를 구하면 이 된다.
sobel filter와 마찬가지로 x축, y축 방향 미분을 계산한다.
장점: Sobel filter와 유사하나, 더 정확한 gradient 계산 제공 (커널의 가중치가 edge direction에 대한 정확한 정보를 포착하는 데 더 적합하게 설계) → 고해상도 이미지에서 미세한 엣지를 정확히 검출하고자 할 때 사용
단점: Sobel filter에 비해 계산 비용이 높음
sobel filter와 유사하다. (커널 값 차이)
대각선 방향 엣지에 민감하며, 2x2 커널을 사용하고 연산 속도가 빠르다.
또는
sobel filter와 NMS(Non-Maximum Supression)을 사용하여 더 선명한 엣지를 검출한다.
Gaussian Filter → gradient 계산(sobel filter 사용) → Non-Maximum Suppression → Double Thresholding → Hysteresis
Non-Maximum Suppression
: 엣지가 아닌 픽셀을 제거한다. 각 픽셀에서 그래디언트 방향을 따라 그래디언트 크기가 local maximum 값인지 확인한다. local maximum이 아니면 엣지가 아니라고 판단하고 제거한다.
Double Tresholding
: 강한 엣지와 약한 엣지 구분을 위해 두 개의 임계값을 설정한다. 강한 엣지는 높은 임계값보다 큰 그래디언트를 가진 픽셀, 약한 엣지는 두 임계값 사이의 그래디언트를 가진 픽셀이다.
Hysteresis
****: 약한 엣지 중 강한 엣지와 연결된 픽셀만 엣지로 간주한다. 이를 통해 false edge를 제거한다.
# Laplacian filter
laplacian = cv2.Laplacian(img,cv2.CV_64F)
abs_laplacian = cv2.convertScaleAbs(laplacian)
# sobel filter
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
abs_sobelx = cv2.convertScaleAbs(sobelx)
abs_sobely = cv2.convertScaleAbs(sobely)
sobel_combined = cv2.addWeighted(abs_sobelx, 0.5, abs_sobely, 0.5, 0)
# scharr filter
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)
abs_scharrx = cv2.convertScaleAbs(scharrx)
abs_scharry = cv2.convertScaleAbs(scharry)
scharr_combined = cv2.addWeighted(abs_scharrx, 0.5, abs_scharry, 0.5, 0)
# prewitt filter
prewittx = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]])
prewitty = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]])
img_prewittx = cv2.filter2D(img, -1, prewittx)
img_prewitty = cv2.filter2D(img, -1, prewitty)
img_prewitt = img_prewittx + img_prewitty
## roberts filter
robertsx = np.array([[1, 0], [0, -1]])
robertsy = np.array([[0, 1], [-1, 0]])
img_robertsx = cv2.filter2D(img, -1, robertsx)
img_robertsy = cv2.filter2D(img, -1, robertsy)
img_roberts = img_robertsx + img_robertsy
# canny edge
edges = cv2.Canny(img, 100, 200)