feat(pipeline): YouTube Tab → PDF 자동 추출 파이프라인 초기 구현

- 5단계 파이프라인: 다운로드 → 프레임 추출 → 패턴 감지 → 중복 제거 → PDF 생성
- 3가지 패턴 지원: overlay, split, scroll
- MSE 기반 픽셀 비교 프레임 중복 제거
- split 모드: 42% 크롭 + 밝기 필터 + Tab 라인 검증
- overlay 모드: 320x120 정규화 + 슬라이딩 윈도우 비교
- 프로젝트 문서 초기 작성 (architecture, tech-stack, STATUS, known-issues)
This commit is contained in:
2026-03-24 23:25:17 +09:00
commit 3d3f74b082
18 changed files with 1989 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
# Project Status
## 기능 현황
| 기능 | 상태 | 비고 |
|------|------|------|
| YouTube 다운로드 | ✅ 완료 | yt-dlp + 쿠키 인증 |
| 프레임 추출 | ✅ 완료 | fps=2 기본값 |
| 패턴 감지 (overlay) | ✅ 완료 | Tab 라인 검증 포함 |
| 패턴 감지 (split) | ✅ 완료 | 밝기 기준 엄격화 |
| 패턴 감지 (scroll) | ✅ 완료 | 기본 폴백 |
| MSE 기반 중복 제거 | ✅ 완료 | 히스토그램 → MSE 전환 |
| 오버레이 정규화 비교 | ✅ 완료 | 320×120 정규화 + 슬라이딩 윈도우 |
| PDF/PNG 생성 | ✅ 완료 | A4 + 롱 이미지 |
## 최근 변경
| 날짜 | 변경 내용 |
|------|-----------|
| 2026-03-24 | 패턴 감지 고도화: overlay→split→scroll 우선순위 |
| 2026-03-24 | 히스토그램 비교 → MSE 픽셀 비교로 전환 |
| 2026-03-24 | split 모드: 42% 크롭 + 밝기 필터 + Tab 라인 검증 |
| 2026-03-24 | overlay 모드: 정규화 + 슬라이딩 윈도우 중복 제거 |
| 2026-03-24 | split 감지 조건 엄격화 (top>180, bottom<100) |
## 알려진 제한사항
- 오버레이형 영상(空奏列車)에서 추출 프레임 수가 아직 많을 수 있음 (MSE 임계값 추가 튜닝 필요)
- 영상 내 Tab이 반복되는 곡은 실제 고유 프레임 수가 적음 (正常 동작)

View File

@@ -0,0 +1,58 @@
# Architecture
> YouTube 기타 TAB 영상 → PDF 자동 추출 파이프라인
## 프로젝트 개요
YouTube 기타 TAB 튜토리얼 영상을 입력받아, 컴퓨터 비전으로 TAB 악보 영역만 추출하고 중복을 제거하여 깨끗한 PDF/PNG로 출력하는 CLI 도구.
## 디렉토리 구조
```
project-root/
├── youtube_tab_to_pdf.py # 메인 파이프라인 (단일 파일)
├── .env # yt-dlp 쿠키 경로 설정
├── .gitignore
├── output/ # 생성된 PDF/PNG/디버그 프레임
├── .agent/ # AI 에이전트 설정
│ ├── AGENT.md
│ ├── references/ # 프로젝트 문서
│ └── workflows/ # 워크플로우 정의
└── docs/devlog/ # 개발 로그
```
## 핵심 모듈 (youtube_tab_to_pdf.py 내부)
| 모듈 | 역할 | 비고 |
|------|------|------|
| `download_video()` | yt-dlp로 영상 다운로드 | .env의 쿠키 경로 사용 |
| `extract_frames()` | 영상 → 프레임 분리 (fps=2) | OpenCV VideoCapture |
| `detect_pattern()` | 영상 패턴 분류 (overlay→split→scroll) | 우선순위: overlay > split > scroll |
| `_detect_tab_overlay()` | 흰 박스 + Tab 라인 검출 | HoughLinesP 기반 |
| `_detect_split_screen()` | 상단 Tab + 하단 핸드캠 분할 검출 | 밝기 기준 (top>180, bottom<100) |
| `_has_tab_lines()` | 수평 Staff 라인 존재 여부 | 모폴로지 + HoughLinesP |
| `compare_frames()` | MSE 기반 프레임 유사도 비교 | 320px 리사이즈, factor 8 |
| `extract_unique_*()` | 패턴별 고유 프레임 추출 | scroll/overlay/split 3종 |
| `generate_pdf()` | 프레임 → A4 PDF + 롱 이미지 생성 | ReportLab + Pillow |
## 데이터 흐름
```mermaid
graph LR
A[YouTube URL] --> B[yt-dlp 다운로드]
B --> C[프레임 추출<br>fps=2]
C --> D{패턴 감지}
D -->|overlay| E1[Tab 오버레이 크롭<br>+ 정규화 비교]
D -->|split| E2[상단 42% 크롭<br>+ 밝기/라인 필터]
D -->|scroll| E3[상단 크롭]
E1 --> F[중복 제거<br>MSE 비교]
E2 --> F
E3 --> F
F --> G[PDF + PNG 생성]
```
## 패턴 감지 우선순위
1. **overlay** — 화면 위에 떠 있는 Tab 박스 (가장 구체적)
2. **split** — 상단 Tab 용지 + 하단 핸드캠 (엄격한 밝기 기준)
3. **scroll** — 상단 크롭 (기본 폴백)

View File

@@ -0,0 +1,45 @@
# Coding Conventions
> AI 에이전트는 코드를 작성하기 전 이 컨벤션을 확인합니다.
## 네이밍
| 대상 | 규칙 | 예시 |
|------|------|------|
| 변수/함수 | camelCase | `getUserData()` |
| 클래스 | PascalCase | `UserService` |
| 상수 | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| 파일명 | kebab-case | `user-service.js` |
| CSS 클래스 | kebab-case | `.nav-header` |
## 코드 스타일
- 들여쓰기: (2 spaces / 4 spaces / tab)
- 세미콜론: (사용 / 미사용)
- 따옴표: (single / double)
- 줄바꿈: LF (Unix style)
## 커밋 메시지
```
<type>(<scope>): <description>
type: feat|fix|refactor|test|docs|chore|ci|infra
scope: (선택)
```
**예시:**
- `feat(server): add WebSocket reconnection logic`
- `fix(frontend): resolve button overlap on mobile`
- `docs: update API documentation`
## 주석
- 한국어/영어 혼용 가능
- TODO 주석: `// TODO: 설명` 형식
- 복잡한 로직에는 반드시 WHY(왜) 주석 추가
## 테스트
- 테스트 파일 위치: (예: `__tests__/` 또는 `*.test.js`)
- 테스트 네이밍: `should [expected behavior] when [condition]`

View File

@@ -0,0 +1,57 @@
# Known Issues & Lessons Learned
> **이 파일은 SSOT(Single Source of Truth)입니다.**
> 디버깅이나 구현 전에 **반드시** 이 파일을 확인하세요.
> 세션 종료 시 새로 발견된 이슈를 이 파일에 추가합니다.
---
## 포맷
각 항목은 아래 형식을 따릅니다:
```markdown
### [날짜] [키워드] — 한줄 요약
- **증상**: 무엇이 잘못되었는가
- **원인**: 근본 원인
- **해결**: 올바른 해결 방법
- **주의**: 재발 방지를 위한 교훈
```
---
## 공통 이슈
### [2026-03-08] PowerShell curl — Invoke-WebRequest 충돌
- **증상**: `curl` 명령이 예상과 다른 응답 형식을 반환
- **원인**: PowerShell에서 `curl``Invoke-WebRequest`의 별칭
- **해결**: **`curl.exe`**를 명시적으로 사용
- **주의**: HTTP 관련 모든 명령에서 `curl.exe` 사용 필수
### [2026-03-08] PowerShell npm — 실행 정책 오류
- **증상**: `npm run` 명령이 `실행 정책` 관련 오류로 실패
- **원인**: PowerShell 스크립트 실행 정책이 제한적으로 설정됨
- **해결**: `cmd /c npm run dev` 형식으로 cmd를 통해 실행
- **주의**: npm 관련 명령은 항상 `cmd /c` 접두어 사용 권장
---
## 프로젝트별 이슈
### [2026-03-24] 히스토그램 비교 — Tab 프레임 구분 불가
- **증상**: 서로 다른 Tab 페이지가 "동일"로 판정되어 프레임 수가 과소 추출
- **원인**: 히스토그램 상관관계는 밝기 분포만 비교 — Tab 용지 배경이 같으면 다른 내용도 동일로 판정
- **해결**: MSE 픽셀 비교로 전환 (320px 리사이즈, factor 8)
- **주의**: MSE scaling factor는 영상 타입에 따라 다르게 동작할 수 있음
### [2026-03-24] split 감지 — 밝은 배경 오분류
- **증상**: 밝은 배경 기타 연주 영상(空奏列車)이 split으로 오분류
- **원인**: 흰 셔츠/밝은 배경이 상단 밝기 조건 통과, 기타줄이 _has_tab_lines 통과
- **해결**: split 검증 강화 (top>180, bottom<100, diff>80, 4+ lines) + overlay 우선 검사
- **주의**: 패턴 감지 순서는 반드시 overlay → split → scroll 유지
### [2026-03-24] overlay 크롭 크기 불일치
- **증상**: overlay 프레임 비교 시 모든 프레임이 "다르다"로 판정 (1000+개 추출)
- **원인**: _detect_tab_overlay가 프레임마다 다른 크기의 바운딩박스 반환 (69~360px)
- **해결**: 320×120 흰색 캔버스에 정규화 후 비교 + 슬라이딩 윈도우(5프레임)
- **주의**: overlay 프레임 수 최적화는 아직 진행 중 (추가 튜닝 필요)

View File

@@ -0,0 +1,38 @@
# Tech Stack
> AI 에이전트는 구현 전 이 문서를 확인하여 올바른 기술/버전을 사용합니다.
## 언어 & 런타임
| 항목 | 버전 | 비고 |
|------|------|------|
| Python | 3.x | `C:\ProgramData\miniforge3\envs\score\python.exe` |
## 주요 라이브러리
| 항목 | 용도 |
|------|------|
| OpenCV (cv2) | 프레임 추출, 이미지 처리, HoughLinesP 라인 검출 |
| NumPy | 배열 연산, 밝기 계산, MSE 비교 |
| yt-dlp | YouTube 영상 다운로드 |
| Pillow (PIL) | 이미지 변환 (BGR→RGB), 롱 이미지 생성 |
| ReportLab | PDF 생성 (A4 레이아웃) |
| python-dotenv | .env 파일 로딩 (쿠키 경로) |
## 패키지 관리
- 패키지 매니저: conda (miniforge3, `score` 환경)
- 가상환경: `C:\ProgramData\miniforge3\envs\score`
## 개발 도구
| 도구 | 명령어 |
|------|--------|
| 실행 | `C:\ProgramData\miniforge3\envs\score\python.exe youtube_tab_to_pdf.py <URL>` |
| 디버그 실행 | 위 명령어 + `--debug` |
## 환경 변수
| 변수명 | 용도 | 설정 위치 |
|--------|------|-----------|
| COOKIE_PATH | yt-dlp 쿠키 파일 경로 | `.env` |