fix(pipeline): 스티칭 버그 3종 수정 + AI 마디번호 스탬프 제거
- [BUG1] _merge_scroll_candidates: 씬전환 가속도 조건 제거 (9→1 세그먼트) - [BUG2] merge_panoramas_list: 매칭 임계치 0.60→0.50 (파노라마 3→1 병합) - [BUG3] _detect_measure_bars: 마디선 최소간격 100px 필터 추가 (17px 오탐 제거) - remove: _stamp_measure_number 호출 제거 (AI 임의 [1][2][3] 스탬프 삭제) - add: sim_stitch.py, simulate_ocr_pipeline.py, verify_fixes.py (진단/검증 스크립트)
This commit is contained in:
116
verify_fixes.py
Normal file
116
verify_fixes.py
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user