logo

필터링

평균·가우시안·샤프닝·방향성 선 탐지 필터 적용

실습 준비

import cv2 as cv
import numpy as np
from yeom import to_pil

파일 열기

src = cv.imread("temple.webp")

공간 필터링에 사용할 파르테논 신전 원본 이미지

공간 필터링

공간 필터링(spatial filtering): 커널(kernel)을 이미지 위에서 이동하며 주변 픽셀의 가중합으로 출력 픽셀 계산.

  • 저주파 통과 필터: 밝기 변화를 부드럽게 하여 잡음과 세부 무늬 감소
  • 고주파 강조 필터: 밝기가 빠르게 변하는 경계 강조
  • 커널의 크기와 가중치에 따라 필터 효과 변화

커널이 이미지 가장자리를 벗어나면 참조할 바깥 픽셀이 없다. OpenCV 기본값은 가장자리를 거울처럼 반사하는 cv.BORDER_REFLECT_101. borderType으로 변경 가능.

평균 필터와 가우시안 필터

평균 필터(mean filter): 커널 안의 모든 픽셀에 같은 가중치 적용. 다음 커널은 가중치 합이 1이어서 전체 밝기를 대체로 유지하면서 주변 값의 평균 계산.

mean_kernel = np.ones((9, 9), dtype=np.float32) / 81  # 81 = 9 * 9
mean_blur = cv.filter2D(src, -1, mean_kernel)  # -1은 입력과 같은 자료형으로 출력

float(mean_kernel.sum())
실행 결과
1.0

가우시안 필터(Gaussian filter): 중심 픽셀의 가중치는 크게, 중심에서 멀어질수록 작게 설정.

gaussian_blur = cv.GaussianBlur(src, (9, 9), 0)  # 0은 커널 크기에서 표준편차를 자동 계산

to_pil(cv.hconcat([mean_blur, gaussian_blur]))

원본과 9×9 평균 필터, 9×9 가우시안 필터 결과 비교

커널 크기와 표준편차

cv.GaussianBlur()ksize는 일반적으로 양의 홀수. 커널을 대칭으로 놓으려면 중심 픽셀이 하나여야 하기 때문. 현재 예제처럼 sigmaX=0이면 OpenCV가 주어진 커널 크기에서 표준편차를 계산하므로 (8, 8) 같은 양의 짝수 크기는 사용할 수 없다. 반대로 ksize=(0, 0)과 양의 sigmaX를 지정하면 표준편차에서 커널 크기를 자동 계산한다.

같은 크기에서도 평균 필터는 모든 위치를 똑같이 섞어 더 강한 흐림. 가우시안 필터는 가까운 픽셀의 영향을 더 크게 반영해 기둥과 지붕의 큰 구조를 조금 더 유지.

샤프닝

샤프닝(sharpening): 중심 픽셀을 강조하고 상하좌우 픽셀을 빼서 경계 주변의 밝기 차이 확대.

sharpen_kernel = np.array(
    [
        [0, -1, 0],
        [-1, 5, -1],
        [0, -1, 0],
    ],
    dtype=np.float32,
)

sharpened = cv.filter2D(src, -1, sharpen_kernel)

float(sharpen_kernel.sum())
실행 결과
1.0
to_pil(sharpened)

파르테논 신전 원본과 샤프닝 적용 결과 비교

샤프닝 결과에서는 기둥·돌·지붕의 윤곽이 더 뚜렷. 커널의 가중치 합이 1이어서 일정한 밝기 영역은 유지되지만 경계와 함께 하늘의 미세한 잡음도 강조.

cv.filter2D()의 출력 깊이가 -1이면 입력과 같은 uint8. 계산값은 0보다 작으면 0, 255보다 크면 255로 잘리므로 경계 주변의 밝고 어두운 테두리가 더 선명.

샤프닝은 잡음도 강조

강한 샤프닝을 반복하면 실제 경계뿐 아니라 센서 잡음과 압축 무늬도 함께 증가. 필요하면 먼저 약한 가우시안 필터로 잡음 감소.

방향성 선 탐지 필터

방향성 선 탐지 필터: 특정 방향의 중심 픽셀에는 양의 가중치, 양옆에는 음의 가중치를 적용해 주변보다 밝거나 어두운 선 강조. 색보다 밝기 배열에 집중하도록 그레이스케일로 변환.

gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)

수직선 탐지

수직선 탐지 커널은 가운데 열의 가중치를 크게 설정. 왼쪽·오른쪽보다 가운데가 밝거나 어두운 수직 구조에 강하게 반응.

vertical_line_kernel = np.array(
    [
        [-1, 2, -1],
        [-1, 2, -1],
        [-1, 2, -1],
    ],
    dtype=np.float32,
)
vertical_line_response = cv.filter2D(
    gray,
    cv.CV_32F,  # 음수와 255를 넘는 응답 보존
    vertical_line_kernel,
)
vertical_line_view = cv.convertScaleAbs(vertical_line_response)  # 응답의 절댓값을 8비트로 표시

to_pil(cv.hconcat([gray, vertical_line_view]))

그레이스케일 원본과 수직선 탐지 필터 응답 비교

신전의 기둥과 세로 이음새에서 강한 응답. 방향이 다른 지붕의 수평 구조는 상대적으로 약하게 표시.

수평선 탐지

수평선 탐지 커널은 가운데 행의 가중치를 크게 설정. 위·아래보다 가운데가 밝거나 어두운 수평 구조에 강하게 반응.

horizontal_line_kernel = np.array(
    [
        [-1, -1, -1],
        [2, 2, 2],
        [-1, -1, -1],
    ],
    dtype=np.float32,
)
horizontal_line_response = cv.filter2D(
    gray,
    cv.CV_32F,  # 음수와 255를 넘는 응답 보존
    horizontal_line_kernel,
)
horizontal_line_view = cv.convertScaleAbs(horizontal_line_response)  # 응답의 절댓값을 8비트로 표시

to_pil(cv.hconcat([gray, horizontal_line_view]))

그레이스케일 원본과 수평선 탐지 필터 응답 비교

신전 지붕의 층과 바닥 돌의 가로 경계에서 강한 응답. 두 커널의 가중치 합은 0이므로 밝기가 일정한 영역의 응답은 0에 가까움.

Previous
ArUco 마커