#!/usr/bin/env python3 """ 수정된 버그 3개가 실제로 동작하는지 검증하는 재실행 시뮬레이션. youtube_tab_to_pdf.py의 수정된 함수들을 직접 임포트하여 사용합니다. """ import sys from pathlib import Path import cv2 import numpy as np if sys.platform == "win32": sys.stdout.reconfigure(encoding="utf-8", errors="replace") sys.stderr.reconfigure(encoding="utf-8", errors="replace") # 메인 모듈 임포트 (수정된 코드 사용) sys.path.insert(0, str(Path(__file__).parent)) from youtube_tab_to_pdf import ( _find_white_tab_strip, _has_tab_content, _detect_scroll_offset, _extract_tracking_channel, _merge_scroll_candidates, merge_panoramas_list, _detect_measure_bars, compare_frames ) FRAME_DIR = Path("output/temp_frames") OUT_DIR = Path("output/sim_verify") OUT_DIR.mkdir(exist_ok=True) def main(): paths = sorted(FRAME_DIR.glob("f_0*.png")) if not paths: print("❌ 프레임 없음"); return print(f"[VERIFY] {len(paths)}개 프레임 — 수정된 코드로 재검증") # 스트립 Y범위 tops, bots = [], [] for p in paths[:30]: f = cv2.imread(str(p)) if f is None: continue s = _find_white_tab_strip(f) if s: tops.append(s[0]); bots.append(s[1]) med_top = int(np.median(tops)) med_bot = int(np.median(bots)) print(f" 스트립 Y: {med_top}~{med_bot}") # MSE 중복제거 THRESHOLD = 0.95 candidates, compared = [], [] for p in paths: f = cv2.imread(str(p)) if f is None: continue h = f.shape[0] crop = f[max(0, med_top):min(h, med_bot), :] if not _has_tab_content(crop): continue cmp_img = cv2.resize(crop, (480, 120), interpolation=cv2.INTER_AREA) if any(compare_frames(cmp_img, ref) >= THRESHOLD for ref in compared): continue candidates.append(crop) compared.append(cmp_img) print(f"\n[1] MSE 중복제거 후: {len(candidates)}개 후보") # ── BUG1 검증: 씬전환 감지 횟수 ───────────────────────────────────── print(f"\n[2] BUG1 검증 — 씬전환 감지 횟수 (기대: 1~3)") stitched = _merge_scroll_candidates(candidates) print(f" _merge_scroll_candidates 결과: {len(stitched)}개 세그먼트 → 파노라마") for i, s in enumerate(stitched): print(f" 세그먼트 파노라마 {i}: {s.shape[1]}px") cv2.imwrite(str(OUT_DIR / f"seg_pano_{i:02d}.png"), s) # ── BUG2 검증: 파노라마 병합 ──────────────────────────────────────── print(f"\n[3] BUG2 검증 — 파노라마 병합 (기대: 1~2개)") merged = merge_panoramas_list(stitched) print(f" merge_panoramas_list 결과: {len(merged)}개 최종 파노라마") for i, m in enumerate(merged): print(f" 최종 파노라마 {i}: {m.shape[1]}x{m.shape[0]}px") cv2.imwrite(str(OUT_DIR / f"final_pano_{i:02d}.png"), m) # ── BUG3 검증: 마디 구분선 탐지 ──────────────────────────────────── print(f"\n[4] BUG3 검증 — 마디 구분선 탐지 (기대: 간격 모두 ≥100px)") total_measures = 0 all_ok = True for i, m in enumerate(merged): gray = m[:, :, 2] # Red 채널 bars = _detect_measure_bars(gray) total_measures += max(0, len(bars) - 1) # 구분선 사이가 마디 수 print(f" 파노라마 {i}: {len(bars)}개 구분선 탐지", end="") if bars: gaps = [bars[j+1]-bars[j] for j in range(len(bars)-1)] min_gap = min(gaps) if gaps else 0 ok = min_gap >= 100 if not ok: all_ok = False print(f" | 최소간격: {min_gap}px {'✅' if ok else '❌ (오탐 여전히 존재)'}") print(f" 첫5개 좌표: {bars[:5]}") else: print() # ── 최종 판정 ─────────────────────────────────────────────────────── print(f"\n{'='*60}") print("[검증 결과]") seg_ok = len(stitched) <= 5 # 씬전환 5회 이하 (이전 8회 → 개선) merge_ok = len(merged) <= 2 # 파노라마 2개 이하 (이전 3개 → 개선) bar_ok = all_ok # 모든 마디선 간격 ≥100px print(f" BUG1 씬전환 오탐: {'✅ 개선됨' if seg_ok else '❌ 여전히 과다'} ({len(stitched)}개 세그먼트, 이전 9개)") print(f" BUG2 파노라마 분리: {'✅ 개선됨' if merge_ok else '❌ 여전히 분리'} ({len(merged)}개, 이전 3개)") print(f" BUG3 마디선 오탐: {'✅ 개선됨' if bar_ok else '❌ 여전히 오탐'}") print(f" 탐지된 총 마디 수: {total_measures}개") print(f"{'='*60}") if seg_ok and merge_ok and bar_ok: print("\n✅ 모든 버그 수정 확인 — 실제 파이프라인 실행 가능") else: print("\n⚠ 일부 문제 잔존 — 추가 파라미터 조정 필요") if __name__ == "__main__": main()