10장. Sobel 과 Laplacian 경계 검출
가로 기울기와 세로 기울기
아파트 단지 지도에서 "동쪽으로 한 칸 가면 몇 층 높아지나"와 "남쪽으로 한 칸 가면 몇 층 높아지나"를 따로 적어 두면, 어느 방향으로 걷든 가파른 정도를 알 수 있습니다. 그림도 가로 방향의 변화(gx)와 세로 방향의 변화(gy)를 따로 잽니다. 가로로 빼면 세로로 선 경계가, 세로로 빼면 가로로 누운 경계가 드러납니다.
왼쪽 상자가 가로 기울기, 오른쪽 상자가 세로 기울기입니다. 빼는 방향과 찾아지는 선의 방향은 서로 직각입니다. 네모의 왼쪽 변은 세로로 서 있지만, 그 변을 가로질러 왼쪽에서 오른쪽으로 걸을 때 밝기가 바뀌므로 가로 기울기에서 드러납니다. 여기서 동쪽으로 한 칸이 gx, 남쪽으로 한 칸이 gy 입니다.
가운데 그림(|gx|)에는 네모의 왼쪽·오른쪽 변과 계단 띠의 세로 선이 밝게 나오고, 네모의 위·아래 변은 나오지 않습니다. 오른쪽 그림(|gy|)은 거꾸로 위·아래 변과 띠의 위·아래 끝만 나옵니다. 원과 세모의 비스듬한 변은 양쪽에 모두 조금씩 나옵니다. 이 그림은 다음 절에서 볼 Sobel 로 계산했지만, 어느 쪽 경계가 나오는지는 [-1, 0, 1] 과 같습니다.
두 방향의 차이 커널
| 견줄 것 | 가로 기울기 gx | 세로 기울기 gy |
|---|---|---|
| 커널 | [[-1, 0, 1]] (1줄 3칸) | [[-1], [0], [1]] (3줄 1칸) |
| 빼는 방향 | 오른쪽 − 왼쪽 | 아래 − 위 |
| + 가 나오는 곳 | 오른쪽으로 갈수록 밝아질 때 | 아래로 갈수록 밝아질 때 |
| 드러나는 경계 | 세로로 선 경계 | 가로로 누운 경계 |
짧은 예시
import cv2
import numpy as np
img = cv2.imread("ch10_shapes.png", cv2.IMREAD_GRAYSCALE)
# 1줄 3칸짜리 차이 커널. .T(transpose, 뒤집어 세우기)로 3줄 1칸짜리를 만든다
kx = np.array([[-1, 0, 1]], dtype=np.float32)
ky = kx.T
# filter2D(그림, 결과 자료형, 커널): 결과를 소수(CV_64F)로 받아야 음수가 남는다
gx = cv2.filter2D(img, cv2.CV_64F, kx)
gy = cv2.filter2D(img, cv2.CV_64F, ky)
print(gx[80, 57:63].tolist()) # 결과: [0.0, 0.0, 150.0, 150.0, 0.0, 0.0] → 네모의 왼쪽 변
print(gy[37:43, 100].tolist()) # 결과: [0.0, 0.0, 150.0, 150.0, 0.0, 0.0] → 네모의 위 변6장에서 본 cv2.filter2D 에 차이 커널을 넣었을 뿐입니다. 손계산의 150 이 그대로 나옵니다. .T 는 transpose(자리 바꾸기)를 줄인 이름으로, 가로로 누운 표를 세로로 세웁니다.
주의할 점
cv2.filter2D의 둘째 인자를-1로 주면 결과가 원본과 같은uint8이 되어 음수가 0 으로 잘립니다. 기울기를 잴 때는cv2.CV_64F처럼 소수 자료형을 줍니다.- 6장에서 본 것처럼
cv2.filter2D는 커널을 뒤집지 않고 그대로 겹쳐 곱합니다. 그래서[-1, 0, 1]은 정말로 "오른쪽 − 왼쪽"을 계산합니다.