OpenCV 비전 실무6장 · 평균 필터와 Gaussian 필터10 / 21← 이전목차다음 →MetaContents

6장. 평균 필터와 Gaussian 필터

직접 만든 커널

빵집 반죽 틀을 떠올려 봅시다. 가게에서 파는 동그란 틀(cv2.blur, cv2.GaussianBlur)을 써도 되지만, 원하는 모양의 틀을 직접 깎아 찍을 수도 있습니다. cv2.filter2D 는 우리가 숫자를 적어 만든 커널을 받아 그대로 필터를 겁니다. 평균 커널을 손으로 만들어 넣으면 cv2.blur 와 같은 결과가 나옵니다.

커널 숫자 표 만들기, 합 확인, filter2D 로 걸기, 결과 견주기의 네 단계 흐름도

그림의 네 단계를 따라 커널을 직접 만들고 씁니다. 둘째 단계의 "합 확인"이 특히 중요합니다. 여기서 반죽 틀이 커널이고, 틀로 찍는 일이 cv2.filter2D 입니다.

커널의 합이 1 이어야 하는 까닭

고른 면(모든 칸 100)에 커널을 걸면 결과는 100 × (커널 숫자의 합) 입니다.

커널숫자의 합고른 면 100 의 결과사진 전체
아홉 칸 모두 1/91100 × 1 = 100밝기 그대로
아홉 칸 모두 1/4.52100 × 2 = 200두 배로 밝아지고, 255 를 넘는 칸은 255 로 잘린다
아홉 칸 모두 1/180.5100 × 0.5 = 50절반으로 어두워진다

노이즈만 줄이고 밝기는 지키려면 커널 숫자의 합이 1 이어야 합니다. 그래서 정수로 적은 커널(1-2-1 무게)은 늘 합(16)으로 나눠 씁니다.

짧은 예시

Python
import cv2
import numpy as np

img = cv2.imread("ch06_board.png", cv2.IMREAD_GRAYSCALE)

# 평균 커널을 직접 만든다. float32 는 32비트 소수
kernel = np.ones((3, 3), dtype=np.float32) / 9
print(round(float(kernel.sum()), 3))    # 결과: 1.0 → 밝기가 지켜진다

# filter2D(그림, 결과 자료형, 커널): -1 은 원본과 같은 자료형(uint8)으로
mine = cv2.filter2D(img, -1, kernel)
print(np.array_equal(mine, cv2.blur(img, (3, 3))))   # 결과: True → blur 와 똑같다

np.ones 는 모든 칸이 1 인 배열입니다. 1/9 을 곱해 아홉 칸이 모두 1/9 인 평균 커널이 됩니다.

주의할 점

  • 커널 숫자의 합이 1 이 아니면 사진 전체가 밝아지거나 어두워집니다. 만든 뒤에는 kernel.sum() 을 찍어 봅니다.
  • cv2.filter2D 는 커널을 뒤집지 않고 그대로 겹쳐 곱합니다. 이 장의 커널은 위아래·좌우가 대칭이라 상관없지만, 대칭이 아닌 커널은 다른 도구(SciPy 등)와 결과가 좌우로 뒤집혀 나올 수 있습니다.
  • 정수 무게 커널을 cv2.filter2D 로 걸면 cv2.GaussianBlur 와 끝자리가 1 다른 칸이 조금 생깁니다. 두 함수가 소수를 반올림하는 방식이 달라서입니다.
← 이전 절6장 · 평균 필터와 Gaussian 필터 견주기다음 절 →6장 · 얼마나 좋아졌나 재기