16장. AI 데이터 정제와 데이터 증강
크기와 형식 맞추기
도시락 가게는 반찬을 칸이 정해진 용기에 담습니다. 큰 반찬은 잘라 넣고, 작은 반찬은 빈 곳이 남아도 그대로 둡니다. 용기마다 같은 자리에 같은 이름표를 붙이고, 같은 포장지로 쌉니다. 그래야 배달하는 사람이 헷갈리지 않습니다. 학습용 사진도 크기, 이름, 형식을 하나로 맞춥니다.
그림의 네 단계가 맞추는 순서입니다. 흑백과 투명 채널 그림을 컬러로 맞추고, 14장에서 만든 레터박스로 128×128 에 담고, 클래스_번호.png 꼴로 이름을 지어, png 로 저장합니다. 여기서 정해진 칸의 용기가 128×128, 빈 곳이 레터박스의 회색 여백, 이름표가 파일 이름 규칙, 포장지가 png 형식입니다.
맞출 것과 이 장의 약속
| 맞출 것 | 이 장의 약속 | 까닭 |
|---|---|---|
| 채널 | 3 (BGR) | 모델에 넣는 배열의 모양이 같아야 한다 |
| 크기 | 128×128, 레터박스, 여백 회색 (128, 128, 128) | 과일이 찌그러지지 않고 잘리지도 않는다(14장) |
| 이름 | 클래스_번호.png, 번호는 세 자리(apple_001.png) | 이름만 봐도 라벨을 알고, 이름순 정렬이 번호순과 같다 |
| 형식 | png | 저장할 때 값이 바뀌지 않는다(2장). jpg 는 저장할 때마다 조금씩 바뀐다 |
| 폴더 | train/클래스/, test/클래스/ | 학습용과 시험용을 섞지 않는다 |
짧은 예시
import cv2
GRAY = (128, 128, 128) # 여백 색: 모든 사진에 같게 쓴다(14차시)
def letterbox(img, size=128, color=GRAY):
"""긴 변을 size 에 맞춰 비율대로 줄이고, 모자란 칸을 양쪽에 나눠 덧댄다(14차시와 같다)."""
h, w = img.shape[:2]
scale = size / max(h, w)
nw, nh = round(w * scale), round(h * scale)
small = cv2.resize(img, (nw, nh), interpolation=cv2.INTER_AREA)
top, left = (size - nh) // 2, (size - nw) // 2
return cv2.copyMakeBorder(small, top, size - nh - top, left, size - nw - left,
cv2.BORDER_CONSTANT, value=color)
img = cv2.imread("ch16_raw/apple_05.png") # 세로로 긴 사진
print(img.shape, "→", letterbox(img).shape) # 결과: (240, 180, 3) → (128, 128, 3)
# 이름 규칙: 클래스와 세 자리 번호. :03d 는 "빈 앞자리를 0 으로 채운 세 자리 정수"
print([f"apple_{k:03d}.png" for k in (1, 2, 10)]) # 결과: ['apple_001.png', 'apple_002.png', 'apple_010.png']apple_05.png 는 가로 180, 세로 240 이라, 긴 변(세로)을 128 에 맞추면 가로가 96 이 되고 양옆에 16칸씩 회색 여백이 붙습니다. 번호를 세 자리로 쓰면 apple_010 이 apple_002 뒤에 옵니다. 그냥 apple_10 으로 쓰면 이름순으로 apple_1 과 apple_2 사이에 끼어 순서가 헷갈립니다.
주의할 점
- 크기를 맞추는 일은 거르기가 끝난 뒤에 합니다. 흐림 점수는 원래 크기에서 재야 정확하고, 작은 사진을 128 로 키운 뒤에는 "너무 작다"를 알 수 없습니다.
- 여기서는 사진이 수십 장이라
for반복으로 충분합니다. 폴더가 여럿이고 사진이 수천 장이면 17장에서 일괄 처리 도구로 다듬습니다.