경계검출 - 케니 엣지 (Canny Edge)
케니 엣지 검출기는 John F. Canny가 1986년에 개발한 경계 검출 알고리즘입니다. 현재까지도 가장 널리 사용되는 경계 검출 방법 중 하나로, 최적의 경계 검출기로 평가받고 있습니다.
케니 엣지의 특징
케니 엣지 검출기는 다음 세 가지 기준을 만족하는 최적의 경계 검출기입니다:
-
낮은 오류율: 실제 경계가 아닌 점을 경계로 찾거나, 실제 경계인 점을 놓치는 확률이 낮아야 함
-
높은 지역화: 검출된 경계점이 실제 경계의 중심에 가까워야 함
-
단일 경계 응답: 하나의 경계에 대해 하나의 경계점만 검출되어야 함
케니 엣지 검출 과정
케니 엣지 검출은 다음 5단계로 진행됩니다:
1단계: 가우시안 필터링
노이즈를 제거하기 위해 가우시안 필터를 적용합니다.
import cv2
import numpy as np
# 가우시안 블러 적용
blurred = cv2.GaussianBlur(image, (5, 5), 1.4)
2단계: 그래디언트 계산
소벨 필터를 사용해 x, y 방향의 그래디언트를 계산합니다.
# 소벨 필터로 그래디언트 계산
grad_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3)
grad_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3)
# 그래디언트 크기와 방향 계산
magnitude = np.sqrt(grad_x**2 + grad_y**2)
direction = np.arctan2(grad_y, grad_x)
3단계: 비최대 억제 (Non-Maximum Suppression)
그래디언트 방향에서 국소 최대값이 아닌 픽셀을 제거합니다.
4단계: 이중 임계값 (Double Thresholding)
두 개의 임계값을 사용해 강한 경계와 약한 경계를 구분합니다.
5단계: 연결성 분석 (Connectivity Analysis)
약한 경계 중에서 강한 경계와 연결된 것만 최종 경계로 선택합니다.
OpenCV에서 케니 엣지 사용법
기본 문법
edges = cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)
매개변수 설명
|
매개변수 |
타입 |
설명 |
|---|---|---|
|
image |
numpy.ndarray |
입력 이미지 (그레이스케일) |
|
threshold1 |
float |
첫 번째 임계값 (낮은 값) |
|
threshold2 |
float |
두 번째 임계값 (높은 값) |
|
apertureSize |
int |
소벨 마스크 크기 (기본값: 3) |
|
L2gradient |
bool |
L2 그래디언트 사용 여부 (기본값: False) |
기본 사용 예제
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 이미지 읽기
img = cv2.imread('image.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 케니 엣지 검출
edges = cv2.Canny(gray, 50, 150)
# 결과 출력
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.imshow(gray, cmap='gray')
plt.title('원본 이미지')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(edges, cmap='gray')
plt.title('케니 엣지')
plt.axis('off')
plt.show()
임계값 설정 가이드
임계값 비율 규칙
일반적으로 높은 임계값 : 낮은 임계값 = 2:1 ~ 3:1 비율로 설정합니다.
임계값별 결과 비교
import cv2
import matplotlib.pyplot as plt
# 이미지 준비
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 다양한 임계값으로 테스트
thresholds = [(50, 150), (100, 200), (50, 100), (100, 300)]
plt.figure(figsize=(15, 10))
for i, (low, high) in enumerate(thresholds):
edges = cv2.Canny(img, low, high)
plt.subplot(2, 2, i+1)
plt.imshow(edges, cmap='gray')
plt.title(f'임계값: {low}, {high}')
plt.axis('off')
plt.tight_layout()
plt.show()
임계값 선택 가이드
-
낮은 임계값 (50-100): 더 많은 경계 검출, 노이즈 포함 가능성 높음
-
높은 임계값 (150-250): 강한 경계만 검출, 일부 경계 놓칠 가능성
-
적응적 선택: 이미지 특성에 따라 실험적으로 최적값 찾기
고급 활용 예제
가우시안 블러와 함께 사용
import cv2
import numpy as np
def improved_canny(image, low_threshold, high_threshold, blur_size=5):
"""
개선된 케니 엣지 검출 함수
"""
# 노이즈 제거를 위한 가우시안 블러
blurred = cv2.GaussianBlur(image, (blur_size, blur_size), 0)
# 케니 엣지 검출
edges = cv2.Canny(blurred, low_threshold, high_threshold)
return edges
# 사용 예제
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
edges = improved_canny(img, 50, 150, blur_size=5)
cv2.imshow('Improved Canny Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
자동 임계값 계산
def auto_canny(image, sigma=0.33):
"""
자동으로 임계값을 계산하는 케니 엣지 함수
"""
# 이미지의 중간값 계산
median = np.median(image)
# 임계값 자동 계산
lower = int(max(0, (1.0 - sigma) * median))
upper = int(min(255, (1.0 + sigma) * median))
# 케니 엣지 적용
edges = cv2.Canny(image, lower, upper)
return edges, lower, upper
# 사용 예제
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
edges, low_thresh, high_thresh = auto_canny(img)
print(f"자동 계산된 임계값 - 낮음: {low_thresh}, 높음: {high_thresh}")
cv2.imshow('Auto Canny', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
실제 응용 분야
객체 윤곽선 검출
import cv2
import numpy as np
def detect_object_contours(image_path):
"""
객체의 윤곽선을 검출하는 함수
"""
# 이미지 읽기
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 케니 엣지 검출
edges = cv2.Canny(gray, 50, 150)
# 윤곽선 찾기
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 결과 이미지에 윤곽선 그리기
result = img.copy()
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)
return result, edges
# 사용 예제
result_img, edge_img = detect_object_contours('object.jpg')
cv2.imshow('Object Contours', result_img)
cv2.imshow('Canny Edges', edge_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
주의사항 및 팁
주의사항
중요: 케니 엣지는 그레이스케일 이미지에서만 작동합니다.
컬러 이미지는 먼저 그레이스케일로 변환해야 합니다.
-
노이즈가 많은 이미지에서는 사전에 블러링 처리 필요
-
임계값은 이미지마다 다르게 설정해야 함
-
조명 변화가 큰 이미지에서는 결과가 불안정할 수 있음
성능 향상 팁
-
전처리: 가우시안 블러나 미디언 필터로 노이즈 제거
-
후처리: 모폴로지 연산으로 경계선 정리
-
적응적 설정: 이미지별로 최적 임계값 실험적 결정
# 성능 향상을 위한 완전한 예제
def enhanced_canny_detection(image_path):
"""
향상된 케니 엣지 검출 파이프라인
"""
# 1. 이미지 읽기
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 노이즈 제거
denoised = cv2.bilateralFilter(gray, 9, 75, 75)
# 3. 케니 엣지 검출
edges = cv2.Canny(denoised, 50, 150)
# 4. 모폴로지 연산으로 후처리
kernel = np.ones((3, 3), np.uint8)
edges_closed = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
return edges_closed
# 사용
result = enhanced_canny_detection('input_image.jpg')
cv2.imshow('Enhanced Canny', result)
cv2.waitKey(0)
cv2.destroyAllWindows()