20장. 교육 콘텐츠 설계와 종합 실습
과정 전체를 잇는 종합 실습
릴레이 경주는 주자 넷이 바통을 넘기며 달립니다. 한 사람이 아무리 빨라도 바통을 떨어뜨리면 기록이 나오지 않습니다. 종합 실습도 앞 장에서 배운 단계를 이어 달리는 일입니다. 정제가 넘긴 그림을 보정이 받고, 보정이 넘긴 그림을 탐지가 받습니다.
그림의 다섯 단계에 그 단계를 배운 장을 적었습니다. 여기서 주자가 단계 하나이고, 바통이 단계 사이를 오가는 그림과 표입니다. 1장에서 본 파이프라인(모으기, 거르기, 다듬기, 늘리기, 알아보기)에 측정과 채점을 더한 모양입니다. 증강(늘리기)은 학습할 때만 쓰므로 이미 학습한 모델을 쓰는 이 실습에서는 빠집니다.
종합 프로젝트의 그림은 17장에서 쓴 것과 같은 양식으로 새로 그린 가상의 부품 그림입니다. 18장에서 학습한 모델이 알아보려면 그림의 양식이 같아야 하기 때문입니다. 왼쪽부터 보통 그림, 어둡게 찍힌 그림, 하얗고 검은 점(소금·후추 잡티)이 낀 그림, 끝이 잘려 읽히지 않는 파일입니다.
단계와 바통
| 단계 | 받는 것 | 하는 일 | 넘기는 것 | 배운 곳 |
|---|---|---|---|---|
| 정제 | 폴더의 파일 | 깨진 파일과 같은 사진 빼기 | 읽힌 그림 목록 | 16장 |
| 보정 | 그림 | 어두우면 정규화, 잡티가 많으면 Median | 고친 그림 | 5장, 7장 |
| 탐지 | 고친 그림 | YOLO 로 부품의 상자와 이름 | 상자 표 | 18장 |
| 측정 | 상자 표와 그림 | 상자 안에서 Otsu 이진화, 윤곽선 넓이 | 넓이가 붙은 표 | 8장, 12장, 13장 |
| 채점·보고 | 표와 정답표 | IoU 로 맞힘 판정, 그림과 표 저장 | 보고서, 학습지 | 이 장 |
짧은 예시
YOLO 없이 OpenCV 만으로 한 장을 정제부터 측정까지 흘려 봅니다. 단계마다 함수 하나를 두면 나중에 한 단계만 바꿔 끼우기 쉽습니다.
import cv2
import numpy as np
def load(path):
img = cv2.imread(path)
return img # 깨진 파일이면 None
def fix(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
if gray.mean() < 45: # 어두우면 0~255 로 늘린다(5차시의 정규화)
img = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX)
return img
def measure(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 밝은 부품, 어두운 벨트이므로 INV 없이 THRESH_BINARY + Otsu
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return [round(cv2.contourArea(c)) for c in contours if cv2.contourArea(c) >= 100]
for name in ["ch20_demo/part_03.png", "ch20_demo/part_09.png"]:
img = load(name)
if img is None:
print(name, "→ 읽기 실패, 다음 그림으로")
continue
print(name, "→ 부품 넓이:", measure(fix(img)))주의할 점
- 단계를 잇기 전에 단계마다 따로 시험합니다. 다섯 단계를 한꺼번에 돌려 틀리면 어디가 틀렸는지 찾기 어렵습니다.
- 정답표는 마지막 채점에서만 씁니다. 중간 단계에서 정답을 보고 기준값을 고치면 점수가 실제보다 좋게 나옵니다.