16장. AI 데이터 정제와 데이터 증강
실습 1 — 폴더 점검: 깨진·작은·채널이 다른 파일 찾기
ch16_raw/ 의 파일 38개를 모두 읽어 보고, 빈 파일·깨진 파일·너무 작은 그림·채널이 다른 그림을 찾아 표로 만듭니다. 채널이 다른 그림은 세 채널로 맞춥니다.
1단계. 파일 목록과 크기 보기
import os
import pandas as pd
folder = "ch16_raw"
# 정답표(answer.csv)는 사진이 아니므로 뺀다
names = sorted(n for n in os.listdir(folder) if n != "answer.csv")
rows = []
for name in names:
path = os.path.join(folder, name)
rows.append({
"파일": name,
"클래스": name.split("_")[0], # 이름의 앞부분이 라벨이다
"KB": round(os.path.getsize(path) / 1024, 1),
})
table = pd.DataFrame(rows)
print("파일 수:", len(table))
# 클래스마다 몇 개인지 센다
print(table["클래스"].value_counts().sort_index().to_dict())
# 가장 작은 파일 다섯 개: 크기가 이상하게 작으면 의심한다
print(table.sort_values("KB").head(5).to_string(index=False))파일 수: 38
{'apple': 12, 'lime': 13, 'tangerine': 13}
파일 클래스 KB
tangerine_13.png tangerine 0.0
apple_11.png apple 0.1
tangerine_12.png tangerine 1.8
lime_08.png lime 4.2
lime_09.jpg lime 4.32단계. 저장된 그대로 읽어 모양 보기
import cv2
def inspect(path):
"""파일 하나를 저장된 그대로 읽어 가로, 세로, 채널 수를 돌려준다. 못 읽으면 None."""
# IMREAD_UNCHANGED: 흑백은 (세로, 가로), 투명 채널이 있으면 (세로, 가로, 4) 로 읽는다
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
if img is None:
return None
h, w = img.shape[:2]
channels = 1 if img.ndim == 2 else img.shape[2]
return w, h, channels
shapes = [inspect(os.path.join(folder, n)) for n in table["파일"]]
table["가로"] = [s[0] if s else "-" for s in shapes]
table["세로"] = [s[1] if s else "-" for s in shapes]
table["채널"] = [s[2] if s else "-" for s in shapes]
# 읽지 못했거나, 채널이 3 이 아니거나, 짧은 변이 64 보다 작은 줄만 본다
odd = [s is None or s[2] != 3 or min(s[0], s[1]) < 64 for s in shapes]
print(table[odd].to_string(index=False)) 파일 클래스 KB 가로 세로 채널
apple_11.png apple 0.1 - - -
apple_12.png apple 5.9 200 150 1
lime_12.png lime 11.8 200 150 4
tangerine_12.png tangerine 1.8 48 36 3
tangerine_13.png tangerine 0.0 - - -3단계. 문제와 처리를 판정하기
MIN_SIDE = 64 # 짧은 변이 이보다 작으면 너무 작다(이 장의 기준)
def check_file(path):
"""정제의 첫 관문: (문제, 처리) 를 돌려준다. 앞에서 걸리면 뒤는 보지 않는다."""
if os.path.getsize(path) == 0:
return "빈 파일", "버림"
shape = inspect(path)
if shape is None:
return "깨짐", "버림"
w, h, channels = shape
if min(w, h) < MIN_SIDE:
return "너무 작음", "버림"
if channels == 1:
return "흑백", "고침"
if channels == 4:
return "투명 채널", "고침"
return "없음", "그대로"
results = [check_file(os.path.join(folder, n)) for n in table["파일"]]
table["문제"] = [r[0] for r in results]
table["처리"] = [r[1] for r in results]
print(table.loc[table["문제"] != "없음", ["파일", "KB", "문제", "처리"]].to_string(index=False))
print(table["처리"].value_counts().to_dict()) 파일 KB 문제 처리
apple_11.png 0.1 깨짐 버림
apple_12.png 5.9 흑백 고침
lime_12.png 11.8 투명 채널 고침
tangerine_12.png 1.8 너무 작음 버림
tangerine_13.png 0.0 빈 파일 버림
{'그대로': 33, '버림': 3, '고침': 2}4단계. 채널을 셋으로 맞추고 확인하기
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False
def to_bgr(img):
"""채널 수가 1 이나 4 인 그림을 BGR 세 채널로 맞춘다."""
if img.ndim == 2:
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 같은 밝기를 세 채널에
if img.shape[2] == 4:
return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) # 넷째(투명도) 채널을 뗀다
return img
fixed = {}
for name in table.loc[table["처리"] == "고침", "파일"]:
raw = cv2.imread(os.path.join(folder, name), cv2.IMREAD_UNCHANGED)
fixed[name] = (raw, to_bgr(raw))
print(f"{name}: {raw.shape} → {fixed[name][1].shape}")
fig, axes = plt.subplots(1, 4, figsize=(11, 2.8))
for k, (name, (raw, out)) in enumerate(fixed.items()):
# 원본은 채널 수에 따라 띄우는 법이 다르다: 흑백은 cmap, 투명은 BGRA → RGBA
if raw.ndim == 2:
axes[2 * k].imshow(raw, cmap="gray", vmin=0, vmax=255)
else:
axes[2 * k].imshow(cv2.cvtColor(raw, cv2.COLOR_BGRA2RGBA))
axes[2 * k].set_title(f"{name} 원본")
axes[2 * k + 1].imshow(cv2.cvtColor(out, cv2.COLOR_BGR2RGB))
axes[2 * k + 1].set_title("세 채널로 맞춤")
for ax in axes:
ax.axis("off")
plt.tight_layout()
plt.show()apple_12.png: (150, 200) → (150, 200, 3)
lime_12.png: (150, 200, 4) → (150, 200, 3)
코드 설명
- 1단계의 크기 표에서 가장 작은 두 파일,
tangerine_13.png(0.0KB)와apple_11.png(0.1KB)가 먼저 눈에 띕니다. 다른 사진은 4KB 를 넘습니다. 파일 크기만 정렬해 봐도 이상한 파일을 미리 짐작할 수 있습니다. IMREAD_UNCHANGED로 읽으면apple_12.png는 채널 1,lime_12.png는 채널 4 로 나옵니다. 그냥cv2.imread로 읽었다면 둘 다 채널 3 으로 바뀌어 섞인 것을 몰랐을 것입니다.check_file은 빈 파일 → 깨짐 → 너무 작음 → 채널 순서로 따지고, 처음 걸린 곳에서return으로 멈춥니다. 38개 가운데 버림 3개, 고침 2개, 그대로 33개입니다.tangerine_12.png는 48×36 으로 열리기는 하지만 짧은 변 36 이 64 보다 작아 버렸습니다.to_bgr은 흑백 (150, 200) 과 투명 채널 (150, 200, 4) 을 모두 (150, 200, 3) 으로 맞춥니다. 그림에서 흑백 사과는 세 채널이 되어도 여전히 회색이고, 라임은 투명했던 위쪽 띠에 원래 저장돼 있던 식탁보 색이 드러났습니다.