OpenCV 비전 실무16장 · AI 데이터 정제와 데이터 증강6 / 21← 이전목차다음 →MetaContents

16장. AI 데이터 정제와 데이터 증강

흐린 사진 찾기

안경을 벗고 칠판을 보면 글자의 테두리가 번져 보입니다. 테두리가 또렷하면 글자의 검은 획과 흰 칠판이 딱 갈라지고, 번지면 검정에서 흰색으로 천천히 바뀝니다. 흐린 사진을 찾는 방법도 이 차이를 잽니다. 밝기가 "갑자기" 바뀌는 곳이 많으면 선명하고, "천천히" 바뀌는 곳만 있으면 흐립니다.

흑백으로 바꾸고, Laplacian 을 걸고, 분산을 구하는 세 걸음 흐름도

그림의 세 걸음이 흐림 점수를 매기는 순서입니다. 10장에서 본 Laplacian 은 밝기의 변화량이 바뀌는 곳(경계)에서 큰 + 와 − 값을 내고, 평평한 곳에서는 0 을 냅니다. 선명한 사진은 경계에서 큰 값이 많아 값들이 넓게 퍼지고, 흐린 사진은 작은 값만 있어 0 근처에 모입니다. 값이 퍼진 정도를 분산으로 재면 그것이 흐림 점수입니다. 여기서 또렷한 글자 테두리가 Laplacian 의 큰 값이고, 안경을 벗었을 때의 번진 테두리가 작은 값입니다.

선명한 사과 사진과 흐린 사과 사진, 그리고 각각의 Laplacian 결과와 분산을 나란히 놓은 그림

둘째와 넷째 그림은 Laplacian 결과의 크기를 4배로 키워 흑백으로 띄운 것입니다. 선명한 사과는 과일과 식탁보 무늬의 테두리가 흰 선으로 또렷하고 분산이 353.2 입니다. 흐린 사과는 거의 까맣고 분산이 3.2 입니다. 100배 넘게 차이가 납니다.

분산을 손으로 계산하기

분산은 "평균에서 떨어진 거리를 제곱해서 평균 낸 것"입니다. 4장에서 표준편차를 구할 때 ④ 단계(제곱의 평균)까지가 분산이고, 거기에 제곱근을 씌운 것이 표준편차입니다. 밝기가 50 에서 150 으로 바뀌는 경계를 가로로 지나는 한 줄로 계산해 봅니다.

단계선명한 줄흐린 줄
밝기 6칸50, 50, 50, 150, 150, 15050, 50, 70, 130, 150, 150
① 두 번 빼기(왼쪽 + 오른쪽 − 2 × 가운데)0, 100, −100, 020, 40, −40, −20
② 평균0 ÷ 4 = 00 ÷ 4 = 0
③ 평균과의 차이를 제곱0, 10000, 10000, 0400, 1600, 1600, 400
④ 제곱의 평균 = 분산20000 ÷ 4 = 50004000 ÷ 4 = 1000
(표준편차 = 분산의 제곱근)약 70.7약 31.6

①의 첫 칸은 50 + 50 − 2 × 50 = 0, 둘째 칸은 50 + 150 − 2 × 50 = 100 입니다. 두 줄 모두 밝기가 100 만큼 오르지만, 선명한 줄은 한 칸 만에 오르고 흐린 줄은 세 칸에 걸쳐 오릅니다. 그래서 선명한 줄의 분산이 5배 큽니다.

기준값 정하기

방법하는 일좋은 점과 아쉬운 점
정해진 숫자인터넷에서 흔히 보는 100 같은 값을 그대로 쓴다빠르지만, 사진 크기와 무늬에 따라 맞지 않는다
분포를 보고모든 사진의 점수를 그래프로 그려, 크게 벌어진 틈에 선을 긋는다데이터에 맞는다. 이 장의 방법이다
눈으로 확인기준 근처의 사진 몇 장을 직접 열어 본다마지막 확인에 꼭 한다

짧은 예시

Python
import cv2
import numpy as np

# 손계산과 같은 두 줄: 두 번 빼기를 NumPy 로 한 번에 한다
for row in [[50, 50, 50, 150, 150, 150], [50, 50, 70, 130, 150, 150]]:
    r = np.array(row, dtype=np.float64)
    second = r[:-2] + r[2:] - 2 * r[1:-1]          # 왼쪽 + 오른쪽 - 2 × 가운데
    print(second.tolist(), float(second.var()))    # var: variance(분산)
# 결과: [0.0, 100.0, -100.0, 0.0] 5000.0
#       [20.0, 40.0, -40.0, -20.0] 1000.0

def blur_score(img):
    """흑백으로 바꾸고 Laplacian 의 분산을 돌려준다. 클수록 선명하다."""
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return float(cv2.Laplacian(gray, cv2.CV_64F).var())

print(round(blur_score(cv2.imread("ch16_raw/apple_04.png")), 1))   # 결과: 353.2
print(round(blur_score(cv2.imread("ch16_raw/apple_09.jpg")), 1))   # 결과: 3.2

cv2.Laplacian 의 결과는 + 와 − 가 섞여 있어서 10장처럼 cv2.CV_64F(소수)로 받습니다. uint8 로 받으면 음수가 잘려 분산이 틀어집니다.

주의할 점

  • 어두운 사진은 경계에서 밝기 차이도 작아서 흐림 점수가 낮게 나옵니다. 밝기를 먼저 고친 뒤 흐림 점수를 잽니다(실습 2).
  • 무늬가 거의 없는 사진(흰 벽 앞의 흰 컵)은 선명해도 점수가 낮습니다. 점수는 "이 묶음 안에서" 견줄 때 뜻이 있습니다.
  • 사진 크기가 크게 다르면 점수도 달라집니다. 같은 사진을 줄이면 경계가 촘촘해져 점수가 오릅니다. 크기가 아주 제각각이면 같은 크기로 줄인 뒤 잽니다.
← 이전 절16장 · 너무 어둡거나 밝은 사진다음 절 →16장 · 같은 사진 찾기