feat(pipeline): v3-v4 dedup + panorama stitching + 1080p support

- HSV-aware _trim_to_content (white ratio 30-97%)
- pHash cluster dedup: dHash 32x32(1024bit), max_hamming=20
- Panoramic stitching: template matching scroll offset detection
- 4-stage pipeline: MSE -> Panorama -> pHash
- 1080p download priority + MAX_FRAME_WIDTH=1280 cap
- test_pipeline.py with YouTube URLs and --download mode
- 3 new known-issues documented
- devlog + STATUS.md updated
This commit is contained in:
quantlab
2026-03-25 21:58:48 +09:00
parent 3d3f74b082
commit 98381d2893
13 changed files with 836 additions and 421 deletions

View File

@@ -4,26 +4,38 @@
| 기능 | 상태 | 비고 |
|------|------|------|
| YouTube 다운로드 | ✅ 완료 | yt-dlp + 쿠키 인증 |
| 프레임 추출 | ✅ 완료 | fps=2 기본값 |
| YouTube 다운로드 | ✅ 완료 | yt-dlp, 1080p 우선 다운로드 |
| 프레임 추출 | ✅ 완료 | fps=2, MAX_FRAME_WIDTH=1280 캡 |
| 패턴 감지 (overlay) | ✅ 완료 | Tab 라인 검증 포함 |
| 패턴 감지 (split) | ✅ 완료 | 밝기 기준 엄격화 |
| 패턴 감지 (scroll) | ✅ 완료 | 기본 폴백 |
| MSE 기반 중복 제거 | ✅ 완료 | 히스토그램 → MSE 전환 |
| 오버레이 정규화 비교 | ✅ 완료 | 320×120 정규화 + 슬라이딩 윈도우 |
| HSV 기반 Tab 검출 | ✅ 완료 | 2-tier HSV 마스크, 960px 업스케일 |
| MSE 기반 중복 제거 | ✅ 완료 | 480px 정규화 비교 |
| pHash 클러스터 중복제거 | ✅ 완료 | dHash 32×32(1024bit), max_hamming=20 |
| 파노라마 스티칭 | ✅ 완료 | 템플릿 매칭 수평 스크롤 합성 |
| 오버레이 정규화 비교 | ✅ 완료 | 480×180 정규화 + 전체 히스토리 MSE 비교 |
| PDF/PNG 생성 | ✅ 완료 | A4 + 롱 이미지 |
## 처리 파이프라인 (scroll)
```
Raw Frames → HSV Strip 검출 → Median Crop → MSE 1차 → 파노라마 스티칭 → pHash 2차 → PDF
```
## 최근 변경
| 날짜 | 변경 내용 |
|------|-----------|
| 2026-03-25 | 1080p 우선 다운로드 + MAX_FRAME_WIDTH=1280 캡 (OOM 방지) |
| 2026-03-25 | dHash 32×32 + max_hamming=20으로 pHash 정밀도 향상 |
| 2026-03-25 | 파노라마 스티칭: 템플릿 매칭 스크롤 오프셋 검출 + 연속 프레임 합성 |
| 2026-03-25 | HSV 트림: 흰색비율 30~97% 기반 정밀 크롭 |
| 2026-03-25 | overlay 프레임 수 최적화: 858→51프레임 (OVERLAY_SIMILARITY_THRESHOLD=0.55) |
| 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이 반복되는 곡은 실제 고유 프레임 수가 적음 (正常 동작)
- 1080p 처리 시 여전히 중복 프레임 존재 가능 (마디번호 기반 추가 검증 필요)
- 순차 영상 처리 시 메모리 누적 주의 (gc.collect 필수)
- test_pipeline.py 아직 메인 코드와 완전 통합 안 됨

View File

@@ -54,4 +54,22 @@
- **증상**: overlay 프레임 비교 시 모든 프레임이 "다르다"로 판정 (1000+개 추출)
- **원인**: _detect_tab_overlay가 프레임마다 다른 크기의 바운딩박스 반환 (69~360px)
- **해결**: 320×120 흰색 캔버스에 정규화 후 비교 + 슬라이딩 윈도우(5프레임)
- **주의**: overlay 프레임 수 최적화는 아직 진행 중 (추가 튜닝 필요)
- **주의**: overlay 프레임 수 최적화는 아직 진행 중 (추가 튜닝 필요)
### [2026-03-25] pHash 16×16 — Tab 프레임 과도합병
- **증상**: 서로 다른 Tab 페이지가 pHash 클러스터링에서 동일 그룹으로 합병 (20→9 프레임)
- **원인**: 16×16 dHash(256비트)는 Tab 구조(6선 + 숫자)를 구분하기엔 해상도 부족. 모든 Tab이 유사한 hash 생성
- **해결**: dHash 32×32(1024비트)로 확대 + max_hamming 50→20 조정
- **주의**: hash_size와 max_hamming은 항상 쌍으로 조정해야 함 (비트수 대비 비율)
### [2026-03-25] 1080p 프레임 — 메모리 부족/프로세스 행
- **증상**: 1920×1080 프레임 500+개 로딩 시 프로세스 무한 대기 (3.5GB+ RAM)
- **원인**: extract_frames가 모든 프레임을 list에 보관, 1080p는 프레임당 ~6MB
- **해결**: MAX_FRAME_WIDTH=1280 캡 + gc.collect() 추가. 4K→1280px 다운스케일
- **주의**: 영상 3개 순차 처리 시 GC 없으면 누적 메모리로 swap thrashing 발생
### [2026-03-25] yt-dlp 다운로드 — 360p 폴백
- **증상**: `bestvideo[height>=720]` 포맷으로 요청했으나 640×360 파일 다운로드
- **원인**: format string의 `/best` 폴백이 720p 없을 때 360p 선택. 또는 mp4 전용 필터가 해상도 제한
- **해결**: 명시적 1080p 우선 + 720p 폴백 체인 분리 (`bestvideo[height>=1080]/.../best[height>=720]/best`)
- **주의**: 캐시된 파일이 있으면 재다운로드 안 함 — 해상도 변경 시 기존 파일 삭제 필요

View File

@@ -87,9 +87,9 @@ git log --oneline -20
| 커밋 유형 | Vikunja 액션 |
|-----------|-------------|
| 기존 태스크 해당 작업 **완료** | `C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py done {ID}` |
| 신규 작업 완료 (기존 태스크 없음) | `C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --done --labels Backend,Priority:High` |
| 작업 중 발견된 **미완료 TODO** | `C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:Mid` |
| 기존 태스크 해당 작업 **완료** | `C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py done {ID}` |
| 신규 작업 완료 (기존 태스크 없음) | `C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --done --labels Backend,Priority:High` |
| 작업 중 발견된 **미완료 TODO** | `C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:Mid` |
> [!IMPORTANT]
> 모든 커밋이 기존 또는 신규 태스크에 매핑되었는지 확인.
@@ -97,13 +97,13 @@ git log --oneline -20
### 2-2. 완료 처리
```powershell
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py done {TASK_ID}
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py done {TASK_ID}
```
### 2-3. 신규 태스크 생성
```powershell
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:High
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py create "제목" "설명" --labels Backend,Priority:High
```
### 라벨 규칙
@@ -138,11 +138,11 @@ git diff --name-only .agent/references/
```powershell
# STATUS.md가 변경된 경우
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\wiki_helper.py update "Status" .agent\references\STATUS.md
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\wiki_helper.py update "Status" .agent\references\STATUS.md
```
```powershell
# architecture.md가 변경된 경우
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\wiki_helper.py update "Architecture" .agent\references\architecture.md
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\wiki_helper.py update "Architecture" .agent\references\architecture.md
```
> [!TIP]

View File

@@ -32,7 +32,7 @@ if sys.stdout.encoding != "utf-8":
# ============================================================
API_BASE = "https://plan.variet.net/api/v1"
TOKEN = "tk_070f8e0b715e818bb7178c3815ed5389040eddca"
PROJECT_ID = 7 # Variet Agent 프로젝트
PROJECT_ID = 12 # guitar_score 프로젝트
# ============================================================
HEADERS = {

View File

@@ -15,7 +15,7 @@ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# ============================================================
GITEA_BASE_URL = "https://git.variet.net"
GITEA_OWNER = "Variet"
GITEA_REPO = "variet-agent" # Variet Agent 프로젝트
GITEA_REPO = "guitar_score" # guitar_score 프로젝트
GITEA_TOKEN = "3a01b4b15a39921572e64c413353e870d4d2161b"
# ============================================================

View File

@@ -44,7 +44,7 @@ description: 프로젝트 서비스 연동 정보 + 작업 프로토콜 (서비
> 직접 API 호출 대신 반드시 helper 스크립트를 사용하세요.
```powershell
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py list todo
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py list todo
```
### Vikunja 라벨 체계

View File

@@ -49,7 +49,7 @@ git log --oneline -5
### 3. Vikunja TODO 태스크
```powershell
C:\ProgramData\miniforge3\envs\variet-agent\python.exe .agent\workflows\helpers\vikunja_helper.py list todo
C:\ProgramData\miniforge3\envs\score\python.exe .agent\workflows\helpers\vikunja_helper.py list todo
```
### 4. 종합 보고