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

6장. 평균 필터와 Gaussian 필터

얼마나 좋아졌나 재기

정수기 필터를 바꾼 뒤 물이 얼마나 깨끗해졌는지는 맛만 보고 말하기 어렵습니다. 수질 검사지로 숫자를 재야 "전보다 좋아졌다"를 남에게 보여 줄 수 있습니다. 필터를 건 사진도 눈으로만 보면 사람마다 판단이 다릅니다. 깨끗한 원본이 있으면, 결과가 원본과 얼마나 닮았는지 숫자로 잴 수 있습니다.

눈으로 보기, 평균 절대 차이, PSNR 의 세 가지 재는 법을 견준 비교 그림

그림의 세 상자가 이 장에서 쓰는 재는 법입니다. 왼쪽은 사람이 보고, 가운데와 오른쪽은 깨끗한 원본과 견주어 숫자를 냅니다. 여기서 수질 검사지가 평균 절대 차이와 PSNR 이고, 깨끗한 물의 기준이 원본 그림입니다.

두 가지 숫자

숫자구하는 법좋은 쪽읽는 법
평균 절대 차이(MAE)cv2.absdiff(결과, 원본).mean()작을수록"칸마다 평균 몇만큼 다른가". 밝기 단위 그대로라 읽기 쉽다
PSNRcv2.PSNR(원본, 결과)클수록원본과 닮은 정도를 데시벨로. 차이를 제곱해 평균 낸 값이 절반이 되면 약 3dB 오른다

PSNR 은 차이를 제곱해서 쓰므로 크게 틀린 칸을 더 무겁게 셉니다. 사진 분야에서 흔히 쓰는 점수라 논문과 자료에서 자주 봅니다. 노이즈 낀 그림과 필터 결과의 PSNR 을 견주어, 필터를 건 뒤 점수가 오르면 원본에 가까워진 것입니다.

손으로 평균 절대 차이 구하기

칸원본결과차이차이의 크기
1100104-44
21009733
36066-66
420019911
평균(4 + 3 + 6 + 1) ÷ 4 = 3.5

차이를 그냥 더하면 빼기와 더하기가 지워지므로, 크기(절댓값)로 바꿔 더합니다. 4장에서 표준편차를 구할 때 제곱한 것과 같은 까닭입니다.

짧은 예시

Python
import cv2
import numpy as np

img = cv2.imread("ch06_board.png", cv2.IMREAD_GRAYSCALE)
rng = np.random.default_rng(600)
noisy = np.clip(img + rng.normal(0, 20, img.shape), 0, 255).astype(np.uint8)
blur3 = cv2.blur(noisy, (3, 3))

for name, out in [("노이즈 낀 그림", noisy), ("평균 필터 3", blur3)]:
    # absdiff: 칸마다 |a - b|. uint8 끼리 빼도 음수가 0 으로 잘리지 않는다
    mae = float(cv2.absdiff(out, img).mean())
    # PSNR(원본, 견줄 그림): 데시벨 점수
    psnr = cv2.PSNR(img, out)
    print(f"{name}: 평균 차이 {mae:.2f}, PSNR {psnr:.2f} dB")

이 코드를 돌리면 노이즈 낀 그림은 평균 차이 15.81, PSNR 22.23dB 이고, 크기 3 평균 필터를 건 뒤에는 9.38, 24.11dB 로 둘 다 좋아집니다.

주의할 점

  • out - img 처럼 uint8 끼리 빼면 음수가 255 근처로 돌아갑니다. cv2.absdiff 를 쓰거나 astype(np.int16) 으로 바꿔 뺍니다.
  • 두 숫자 모두 깨끗한 원본이 있어야 잴 수 있습니다. 실제 현장의 사진은 원본이 없으므로, 이 장처럼 깨끗한 그림에 노이즈를 더해 "어떤 설정이 좋은지" 미리 견주어 본 뒤 씁니다.
  • 똑같은 두 그림의 PSNR 은 끝없이 커야 하지만, OpenCV 는 361.2 처럼 아주 큰 수를 돌려줍니다.
← 이전 절6장 · 직접 만든 커널다음 절 →6장 · 언제 쓰나와 주의할 점