😏경계검출 - 케니엣지

경계검출 - 케니 엣지 (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()

참고 자료