OpenCV 비전 실무18장 · OpenCV 전처리와 YOLO 연계10 / 21← 이전목차다음 →MetaContents

18장. OpenCV 전처리와 YOLO 연계

지나친 전처리의 함정

얼룩이 묻은 안경을 닦을 때 너무 세게 문지르면 얼룩과 함께 렌즈 코팅까지 벗겨집니다. 깨끗해 보이지만 오히려 잘 안 보입니다. 사진 손질도 그렇습니다. 잡음을 줄이려고 블러를 세게 걸면 잡음과 함께 너트의 모서리와 구멍 테두리도 뭉개집니다. YOLO 는 이런 모서리와 테두리를 보고 볼트, 너트, 와셔를 가립니다.

알맞은 손질과 지나친 손질을 견준 비교 그림

왼쪽 카드의 알맞은 손질은 잡음만 줄이고 모서리와 구멍은 남깁니다. 오른쪽 카드의 지나친 손질은 둘 다 지웁니다. 여기서 얼룩이 잡음, 렌즈 코팅이 모서리와 구멍 테두리입니다.

어둡고 잡음 낀 그림을 그대로, 밝기만, 블러 5 뒤 밝기, 블러 15 뒤 밝기로 손질하고 너트 하나를 크게 본 그림

윗줄은 어둡게와 잡음을 함께 넣은 그림을 네 가지로 손질한 것이고, 아랫줄은 윗줄의 다홍 네모 안의 너트를 크게 본 것입니다. 밝기만 올리면 잡음이 함께 커져 알록달록한 점투성이가 됩니다. 블러 5×5 뒤에 밝기를 올리면 잡음이 줄고 육각형 모서리가 남습니다. 블러 15×15 는 잡음이 거의 없지만 육각형의 모서리가 둥글어지고 구멍 테두리가 흐려집니다.

점수로 드러난 함정은 "밝기만" 쪽이었습니다. 프로젝트에서 잡음 낀 사진에 밝기만 올리면 헛것이 5개에서 10개로 늘어 F1 이 0.873 에서 0.737 로 떨어졌고, 어둡게+잡음 판은 밝기만 올려서는 하나도 맞히지 못했습니다(F1 0). 잡음을 그대로 둔 채 밝기를 키운 손질이 오히려 해가 된 경우입니다.

눈에 보기 좋은 것과 모델에 좋은 것

6장에서는 원본과의 차이(MAE)로 잡음 제거를 견줬습니다. 그런데 MAE 가 작다고 모델에 좋은 것은 아닙니다. 교안 PC 에서 잰 값입니다.

깨끗한 원본에 건 블러Canny(50, 150) 경계 칸 수비고
없음754너트와 와셔의 테두리가 다 남는다
5×5762거의 그대로
9×9741거의 그대로
15×15110경계가 대부분 사라진다

잡음 낀 그림을 블러로 고친 뒤 원본과의 MAE 는 5×5 가 4.89, 15×15 가 3.34 로 15×15 가 더 원본에 가깝게 나옵니다. 그러나 위 표처럼 15×15 는 경계를 대부분 지웁니다. MAE 는 넓은 바탕이 원본과 비슷해진 것을 높게 치고, 작은 물체의 모서리가 사라진 것은 거의 따지지 않기 때문입니다. 다만 실습 5에서 잡음 판에 블러 15×15 를 건 결과는 F1 1.0 으로, 이 장의 부품에서는 점수가 떨어지지 않았습니다. 볼트, 너트, 와셔는 크기와 생김새가 크게 달라 테두리가 흐려져도 가릴 수 있었던 것으로 봅니다. 크기가 비슷한 물체나 더 작은 물체에서는 같은 블러가 해가 될 수 있습니다.

Python
import cv2
import numpy as np

img = cv2.imread("ch17_raw/part_022.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
for k in (5, 9, 15):
    blurred = cv2.GaussianBlur(gray, (k, k), 0)          # 창 크기 k×k, 표준편차는 창 크기에 맞춰 자동
    edges = cv2.Canny(blurred, 50, 150)                  # 11차시의 Canny
    print(f"블러 {k}x{k}: 경계 칸 {int((edges > 0).sum())}개")
print("블러 없음: 경계 칸", int((cv2.Canny(gray, 50, 150) > 0).sum()), "개")

# 잡음 낀 그림을 블러로 고친 뒤 원본과의 MAE(6차시): 작을수록 원본에 가깝다
rng = np.random.default_rng(1801)
noisy = np.clip(img.astype(np.float64) + rng.normal(0, 20, img.shape), 0, 255).astype(np.uint8)
for k in (5, 15):
    fixed = cv2.GaussianBlur(noisy, (k, k), 0)
    print(f"블러 {k}x{k} 뒤 MAE:", round(float(cv2.absdiff(fixed, img).mean()), 2))

주의할 점

  • 손질의 세기는 "사진이 보기 좋은가"가 아니라 "모델의 점수가 오르는가"로 정합니다. 실습 5에서 블러 15×15 는 경계 칸을 754 에서 110 으로 줄이는 세기인데도 점수는 떨어지지 않았습니다. 데이터가 바뀌면 결과도 바뀌므로 그때마다 점수로 확인합니다.
  • 이 장의 부품은 작습니다(너트가 가로세로 20칸 남짓). 물체가 작을수록 같은 블러에도 모양이 더 많이 뭉개집니다.
← 이전 절18장 · 전처리가 결과에 주는 영향다음 절 →18장 · 비교 실험 설계