02_char_lora · 데이터 파이프라인

패널 처리 2단계 재설계
거터 중앙 절단 → clean → 흰 여백 트리밍

웹툰 strip을 패널로 자를 때, 거터(패널 사이 흰 띠) 가장자리에서 타이트하게 자르던 방식을 거터 중앙에서 자르도록 바꿨다. 각 패널이 흰 여백을 절반씩 머금은 채 말풍선 제거(clean)를 거치고, 마지막에 내용 바운딩박스로 여백을 잘라낸다. 가장자리에 걸친 풍선·그림이 잘려 LaMa 인페인팅이 깨지는 문제를 막는 것이 핵심.

IP 이직로그 (55화) ep001 89 패널 패널 번호 1:1 유지 clean YOLO 풍선 ∪ comic-text-detector → LaMa

왜 2단계인가

✕ 기존 — 거터 가장자리 절단

거터의 첫 행에서 바로 잘라 여백 없이 타이트. 거터 검출(97% 흰 행)이 살짝 어긋나면 패널 끝 그림이 잘리고, 패널 경계에 걸친 말풍선이 두 동강 나 LaMa가 엉뚱하게 복원한다.

✓ 신규 — 거터 중앙 절단 + 후 트리밍

거터의 시작·끝을 재고 그 한가운데를 절단선으로. 풍선·그림이 온전히 패널 안에 들어와 clean이 안전하다. 흰 여백은 clean 뒤 내용 바운딩박스로 정확히 제거.

절단점이 거터와 1:1 대응이라 패널 개수·번호가 기존과 완전히 동일 (ep001 = 89개 그대로). 따라서 text 트랙의 VLM 메타(panel_NNN)와 정합성이 깨지지 않는다.

파이프라인

STAGE 1

panel_split

strip 스티칭 → 거터 중앙 절단 → panels_raw (여백 보존, 대사 有)

CLEAN

clean_panels

풍선(YOLO conf0.4·45px) ∪ 텍스트(thr0.5·24px) → LaMa → panels_clean

STAGE 2

trim_white

비흰색 바운딩박스로 상하좌우 여백 제거 → panels_final (학습용)

# 작품 무관 — WEBTOON_DS 로 전환
WEBTOON_DS=ijiklog python panel_split.py   # 1차: raw → panels_raw (여백 보존)
WEBTOON_DS=ijiklog python clean_panels.py  # 말풍선 제거: → panels_clean
WEBTOON_DS=ijiklog python trim_white.py    # 2차: → panels_final (타이트)

단계별 비교 (구 방식 vs 신 방식)

각 줄 5컬럼. 구 방식 = 바로 타이트하게 자른 뒤 clean (1.edge → 2.edge→clean = 구 최종) · 신 방식 = 거터 중앙 절단으로 여백 보존 후 clean → 트리밍 (3.mid+margin → 4.mid→clean → 5.trim = 신 최종). 핵심 비교는 2번(구 최종)5번(신 최종).

구 방식 — 자른 뒤 바로 clean

패널 경계에 풍선이 걸리면 잘린 채로 clean에 들어간다. 잘린 풍선의 외곽선·꼬리를 검출기가 온전히 못 잡아 LaMa가 다 못 지우고 경계에 곡선·띠 잔재를 남긴다.

신 방식 — 여백 보존 clean 후 트리밍

풍선이 온전한 상태로 clean되어 인페인팅이 안정적. 깨끗해진 뒤 흰 여백만 바운딩박스로 정확히 제거.

⚠ 구 방식이 깨지는 사례 — 경계에 걸린 풍선

ep001~ep008에서 YOLO 풍선 마스크가 패널 상/하단 경계(3px)에 닿는 패널을 자동 탐색(403개 검출). 그중 잘림이 심한 대표 4개를 구 방식(자르기→클린)신 방식(여백 보존 크롭→클린→트리밍) 두 행으로 비교. 이미지 아래에 각 과정 표기.

ep003 / panel_031 — 상단 풍선이 경계에 걸림

큰 말풍선("아 맞아 맥스, 어제 정류장에서…")이 패널 윗변에 걸쳐 있다.

구 방식
① 원본스티칭 strip 구간
② 자르기가장자리 절단 789px
풍선 윗부분 잘림
③ 클린잘린 풍선이 거대한 회색 얼룩으로 잔존
신 방식
① 원본동일 strip 구간
② 여백 보존 크롭거터 중앙 절단 1005px
풍선 전체 포함
③ 클린온전한 풍선 → 깔끔히 제거
④ 다시 줄이기흰 여백 트리밍 812px
잔재 없음

ep004 / panel_023 — 상단 풍선이 경계에 걸림

말풍선("연봉협상을 다시 할까 해서요.")이 패널 윗변에 걸쳐 있다.

구 방식
① 원본스티칭 strip 구간
② 자르기가장자리 절단 1117px
풍선 윗부분 잘림
③ 클린상단에 어두운 띠 잔재
신 방식
① 원본동일 strip 구간
② 여백 보존 크롭거터 중앙 절단 1411px
풍선 전체 포함
③ 클린온전한 풍선 → 깔끔히 제거
④ 다시 줄이기흰 여백 트리밍 1130px
잔재 없음

ep004 / panel_004 — 풍선이 상·하단 양쪽 경계에 걸림

상단("우리…")·하단("같이 '이직 스터디' 할래요…?") 두 풍선이 모두 경계에 걸쳐, edge 크롭(1254px)이 mid(1728px)보다 474px 작다 = 그만큼 잘려나갔다.

구 방식
① 원본스티칭 strip 구간
② 자르기가장자리 절단 1254px
상·하 풍선 잘림
③ 클린상·하단에 검은 띠 잔재
신 방식
① 원본동일 strip 구간
② 여백 보존 크롭거터 중앙 절단 1728px
두 풍선 전체 포함
③ 클린두 풍선 깔끔히 제거
④ 다시 줄이기흰 여백 트리밍 1132px
잔재 없음

ep005 / panel_006 — 풍선 외 나레이션 박스도 동일 문제

말풍선이 아닌 나레이션 박스("본격적인 도파빈 충전…")의 윗변이 경계에 걸린 변종 사례. 풍선만의 문제가 아님을 보여준다.

구 방식
① 원본스티칭 strip 구간
② 자르기가장자리 절단 772px
박스 윗변 잘림
③ 클린잘린 박스 경계가 어색하게 남음
신 방식
① 원본동일 strip 구간
② 여백 보존 크롭거터 중앙 절단 1281px
박스 전체(테두리 포함)
③ 클린박스 흔적 없이 제거
④ 다시 줄이기흰 여백 트리밍 636px
잔재 없음

왜 잘리나 — 풍선 안쪽은 흰색이라, 폭이 좁은 풍선의 내부 행이 패널 전체 폭 기준 ≥97% 흰색으로 읽혀 거터로 오인된다. 구 방식은 그 거터의 첫 행(=풍선 한가운데)에서 바로 자르므로 풍선이 동강 난다. 신 방식은 거터 중앙에서 자르고 여백을 남겨, 풍선 전체가 한 패널에 담긴다.

대조 — 경계에 안 걸린 일반 패널은 차이 없음

풍선이 경계에서 떨어져 있으면 두 방식 최종 결과는 사실상 동일하다. 정직하게 함께 제시.

panel_050 비교
ep001 / panel_050 — 대사("야, 눈 마주치지마…")·말풍선("윽, 크윽…")이 경계에서 떨어져 양 방식 모두 깨끗(2번 ≈ 5번). 신 방식 856→969→976→864px.
panel_023 비교
ep001 / panel_023 — 큰 패널, 경계 걸침 없음. 신 trim(1155)이 구(1216)보다 살짝 작은 건 trim이 비흰색 기준이라 흐릿한 여백까지 더 깐깐하게 제거하기 때문.

구현 · 파라미터

단계스크립트핵심 파라미터
1차 분할panel_split.py WHITE≥235, GUTTER_FRAC 0.97, MIN_GUTTER 18px, MIN_PANEL 200px
→ 거터 런의 (start+end)//2 를 절단점으로
말풍선 제거clean_panels.py YOLO speech-bubble conf0.4 45px팽창 ∪ comic-text-detector seg0.5 24px팽창 → LaMa
2차 트리밍trim_white.py 비흰색(min<235) 바운딩박스, PAD 4px 여유 (전부 env 파라미터화)

알려진 트레이드오프 — 거터 안에 가는 선(획)이 있으면 97% 흰색 판정은 통과하지만 trim의 비흰색 판정엔 걸려 인접 패널의 옅은 윤곽이 살짝 남을 수 있다. LoRA 학습엔 무시 가능한 노이즈이며, 거슬리면 GUTTER_FRAC를 0.99로 올린다. "여백 보존(풍선 안 잘림)" 대가로 택한 선택.

×