17장. Batch Processing 과 YOLO 데이터 준비
멈추지 않는 일괄 처리
택배 기사가 하루에 상자 백 개를 나른다고 해 봅시다. 어느 집이 문을 안 열어 줬다고 그날 배달을 모두 멈추지는 않습니다. "부재, 문 앞에 둠" 이라고 적고 다음 집으로 갑니다. 저녁에는 배달 기록을 보고 못 전한 상자만 다시 챙깁니다. 일괄 처리 프로그램도 이렇게 만들어야 합니다. 깨진 파일 하나 때문에 수백 장 처리가 멈추면 안 됩니다.
그림의 네 단계를 파일마다 되풀이합니다. 여기서 문을 두드려 보는 일이 try, 문이 열렸는지 보는 일이 None 확인, 배달 기록이 로그, 다음 집으로 가는 일이 continue 입니다. 1장 프로젝트에서도 깨진 파일을 "읽기 실패"로 적고 넘어갔습니다. 이 장에서는 실패의 이유를 나눠 적고 기록을 파일로 남깁니다.
실패의 갈래와 확인하는 법
| 실패 | 생기는 까닭 | 확인하는 법 | 기록할 이유 |
|---|---|---|---|
| 빈 파일 | 저장하다 끊김 | 파일 크기 stat().st_size == 0 | 빈 파일 |
| 깨진 파일 | 내려받다 끊김 | cv2.imread 가 None | 읽기 실패 |
| 너무 작은 그림 | 아이콘, 미리보기 | 가로나 세로가 기준보다 작다 | 너무 작음 |
| 저장 실패 | 한글 경로, 디스크 가득 | cv2.imwrite 가 False | 저장 실패 |
| 뜻밖의 오류 | 미처 생각 못 한 경우 | try / except 로 잡는다 | 예외 이름 |
걸린 시간 재기
time.perf_counter() 는 아주 잘게 잰 지금 시각(초)을 돌려줍니다. perf 는 performance(성능)의 줄임입니다. 시작과 끝의 차이가 걸린 시간입니다. 걸린 시간은 컴퓨터마다, 같은 컴퓨터에서도 돌릴 때마다 조금씩 다릅니다.
import time
from pathlib import Path
import cv2
def safe_read(path):
"""읽기에 성공하면 (그림, "성공"), 실패하면 (None, 이유) 를 돌려준다."""
if path.stat().st_size == 0:
return None, "빈 파일"
img = cv2.imread(str(path))
if img is None:
return None, "읽기 실패"
return img, "성공"
start = time.perf_counter()
for path in sorted(Path("ch17_inbox").glob("*.png")):
try:
img, status = safe_read(path)
except Exception as e: # 뜻밖의 오류도 이름만 적고 넘어간다
img, status = None, type(e).__name__
print(f"{path.name:12} {status}")
print("걸린 시간(초):", round(time.perf_counter() - start, 4))except Exception as e 는 거의 모든 오류를 잡아 e 라는 이름에 담습니다. type(e).__name__ 은 그 오류의 갈래 이름(ValueError 같은)입니다. 깨진 파일을 읽을 때 터미널에 [ WARN:0@...] 나 libpng error 같은 글이 찍힐 수 있습니다. OpenCV 안쪽에서 찍는 알림이라 프로그램은 멈추지 않습니다.
주의할 점
except로 오류를 잡았으면 반드시 기록에 남깁니다. 잡기만 하고 조용히 넘어가면 무엇이 빠졌는지 아무도 모릅니다.- 진행 표시(
[12/60]같은 줄)는 너무 자주 찍으면 오히려 느려집니다. 몇 장마다 한 번씩 찍습니다.