7장. Median 과 Bilateral 필터, 필터 비교
네 필터 한눈에 견주기
같은 재료로 국을 끓여도 체로 거르는 방법, 숟가락으로 건지는 방법, 국자로 살살 걷어 내는 방법이 저마다 다릅니다. 노이즈를 거르는 네 필터도 이웃을 섞는 방법이 다르고, 그래서 잘 거르는 노이즈가 다릅니다.
왼쪽 둘(평균, Gaussian)은 6장에서, 오른쪽 둘(Median, Bilateral)은 이 장에서 배웠습니다. 왼쪽 둘은 이웃의 밝기를 보지 않고 자리만 보고 섞고, 오른쪽 둘은 이웃의 밝기를 보고 섞을지 말지를 정합니다. 여기서 체, 숟가락, 국자가 네 필터이고, 국에 뜬 건더기가 노이즈입니다.
네 필터 비교표
| 견줄 것 | 평균 (cv2.blur) | Gaussian (cv2.GaussianBlur) | Median (cv2.medianBlur) | Bilateral (cv2.bilateralFilter) |
|---|---|---|---|---|
| 섞는 법 | 이웃을 똑같이 | 가까울수록 크게 | 줄 세워 가운데 값 | 가깝고 비슷한 칸만 |
| 가우시안 노이즈 | 줄인다 | 잘 줄인다 | 줄인다 | 잘 줄인다 |
| Salt & Pepper 노이즈 | 얼룩으로 번진다 | 얼룩으로 번진다 | 가장 잘 지운다 | 거의 못 지운다 |
| 경계 보존 | 약하다 | 약하다 | 꽤 지킨다(가는 선은 지운다) | 가장 잘 지킨다 |
| 속도 | 빠르다 | 빠르다 | 중간 | 가장 느리다 |
| 커널 인자 | (k, k) 튜플 | (k, k) 튜플과 sigma | 홀수 하나 | d, sigmaColor, sigmaSpace |
이 표의 "잘", "약하다"는 이 장의 실습 5와 프로젝트에서 실제 숫자로 확인합니다.
짧은 예시
import cv2
import numpy as np
gray = cv2.imread("ch07_sign.png", cv2.IMREAD_GRAYSCALE)
# 실습 코드와 같은 방법으로 점 비율 10% 를 더한다
r = np.random.default_rng(711).random(gray.shape)
noisy = gray.copy()
noisy[r < 0.05] = 0
noisy[r > 0.95] = 255
# 네 필터를 같은 그림에 돌려 PSNR 을 견준다. 커널 크기는 5 로 맞춘다
filters = {
"평균": cv2.blur(noisy, (5, 5)),
"Gaussian": cv2.GaussianBlur(noisy, (5, 5), 0),
"Median": cv2.medianBlur(noisy, 5),
"Bilateral": cv2.bilateralFilter(noisy, 9, 75, 75),
}
for name, out in filters.items():
print(f"{name:10} PSNR {cv2.PSNR(gray, out):.2f}")이 코드를 돌리면 Median 이 가장 높고 Bilateral 이 가장 낮게 나옵니다. 점 노이즈에서 Bilateral 이 약하다는 표의 내용 그대로입니다.
주의할 점
- 견줄 때는 커널 크기를 비슷하게 맞춥니다. 한 필터만 큰 커널을 주면 공정한 비교가 아닙니다.
- 비교표는 이 장의 가상 그림에서 얻은 경향입니다. 실제 사진은 노이즈가 섞여 있고 물체도 복잡하므로, 자기 사진으로 다시 재어 봅니다.