Files
dronevideoplayer/PROGRESS.md
minsung 2aae3d1c0d feat: StationOverlay 렌더링 최적화 및 스무딩 적용 close #1
- 텍스트(측점/POI) 전 프레임 사전 계산 Map (requestIdleCallback 백그라운드)
- 드론 데이터 이동 평균 스무딩 (smoothFrame ±N프레임)
- 30fps→60fps 프레임 간 선형 보간 (performance.now() 기반)
- EMA(지수이동평균) 표시 위치 스무딩 (α=0.01 기본값)
- 글씨 2배 크기, bold, strokeText 테두리, 배경 박스 제거
- 카메라 파라미터 패널에 smooth/EMA α 슬라이더 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 15:11:39 +09:00

211 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROGRESS.md — abcvideo 진행 상태
> 이 파일은 각 단계의 진행 상태를 기록합니다.
> 에이전트는 작업 시작/완료 시 반드시 이 파일을 업데이트합니다.
---
## 현재 상태 요약
- 마지막 완료 단계: 검증 (VERIFICATION)
- 현재 진행 단계: 전체 완료 + 검증 완료
- 블로커: FFmpeg 미설치 (HLS/프레임 기능은 FFmpeg 설치 후 동작)
- 검증 결과: VERIFICATION.md 참조 (모든 항목 통과, V3-3 RAF 버그 1건 수정 완료)
---
## 단계별 진행 기록
### 단계 1: 프로젝트 구조 세팅
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- 루트 package.json (npm workspaces)
- shared/src/types.ts (공유 타입)
- server/ 스캐폴드 + tsx dev 환경
- client/ Vite + React 18 + TypeScript + Tailwind
- storage/ 디렉토리 구조
- npm install 성공 (514 packages)
- server TypeScript 컴파일 에러 없음
- GET /api/health 응답 확인
- 미완료/이슈:
- better-sqlite3 ^12.8.0으로 업그레이드 (Node 24 prebuilt binary 문제)
- 다음 단계 참고:
- server/package.json에 better-sqlite3 ^12.8.0 사용 중
- client Vite dev는 아직 미확인 (브라우저 확인 필요)
- server/src/app.ts는 최소 스캐폴드 — 단계 2에서 확장
### 단계 2: 백엔드 서버
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- server/src/config.ts — 환경변수 기반 설정
- server/src/services/ffmpeg.ts — FFprobe/FFmpeg spawn 래퍼 (probeVideo, runFFmpeg, getVideoFps 등)
- server/src/services/streaming.ts — Range Request 스트리밍 (10MB 청크, 1MB highWaterMark)
- server/src/services/storage.ts — better-sqlite3 초기화, Annotation CRUD, 임시파일 정리
- server/src/middleware/security.ts — pathTraversalGuard, validateMimeType, securityHeaders
- server/src/routes/stream.ts — GET /api/stream/:videoId
- server/src/routes/hls.ts — POST /convert, GET /status, GET /progress(SSE), GET /index.m3u8, GET /:segment
- server/src/routes/frame.ts — GET /api/frame/:videoId?time=|frame=
- server/src/routes/upload.ts — 청크 업로드 (init/chunk/complete/status), @tus/server 대신 자체 구현
- server/src/routes/meta.ts — GET /api/videos, GET /api/meta/:videoId, DELETE /api/videos/:videoId
- server/src/routes/annotations.ts — CRUD + export(vtt/srt/json/csv)
- server/src/app.ts — 전체 통합, 헬스체크, 에러 핸들러
- server/.env — 환경변수 파일
- server/tsconfig.json — rootDirs 수정 (shared 타입 접근 허용)
- GET /api/health, /api/videos, /api/annotations/:videoId 정상 응답 확인
- 미완료/이슈:
- @tus/server 2.3.0이 ESM-only("type":"module")라 CommonJS 서버와 호환 불가 → 자체 청크 업로드 구현으로 대체
- FFmpeg 시스템 PATH 미등록 상태 → HLS 변환/프레임 추출은 FFmpeg 설치 후 동작
- 다음 단계 참고:
- 청크 업로드 클라이언트는 POST /api/upload/init → POST /api/upload/chunk (X-Upload-Id, X-Chunk-Index 헤더, application/octet-stream body) → POST /api/upload/complete 순서 사용
- HLS 변환 완료 후 /api/hls/:videoId/index.m3u8 로 플레이리스트 접근
- 서버는 포트 3001에서 실행 중
### 단계 3: 기본 플레이어
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/types/player.ts — VideoSource, PlayerState 타입
- client/src/store/playerStore.ts — Zustand 플레이어 상태 스토어
- client/src/store/annotationStore.ts — 주석 Zustand 스토어
- client/src/hooks/useVideoPlayer.ts — Video.js 8 초기화, loadLocalFile, loadServerStream, switchToHls
- client/src/components/player/VideoPlayer.tsx — forwardRef + VideoPlayerHandle API
- client/src/components/player/HlsConversionStatus.tsx — SSE 진행률 바
- client/src/components/sidebar/VideoList.tsx — 서버 영상 목록
- client/src/App.tsx — 전체 레이아웃 통합
- npm install: video.js@^8.23.0, @videojs/http-streaming, hls.js@^1.6.15, interactjs@^1.10.27, subsrt-ts@^2.0.3
- TypeScript 에러 없음, 빌드 성공 (dist 1.47MB)
- 미완료/이슈:
- interact.js 패키지명 오류 → interactjs (하이픈 없음) 으로 설치
- 빌드 경고: chunk > 500kB (video.js 특성상 정상, 운영환경 분할 불필요)
- 다음 단계 참고:
- VideoPlayer는 forwardRef<VideoPlayerHandle> 사용 — App에서 playerRef.current?.loadServerStream() 호출
- hls.js backBufferLength: 30 설정 완료
- MemoOverlay는 interactjs 사용 (import from 'interactjs')
### 단계 4: 프레임 추출
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/utils/frameCapture.ts — 단일 Canvas 재사용, captureFrame, downloadDataUrl
- client/src/components/player/FrameCaptureButton.tsx — Shift+S 캡처 버튼
- VideoPlayer에 캡처 통합 (getVideoElement → captureFrame → downloadDataUrl)
- 미완료/이슈: 서버 FFmpeg 정확 추출(로컬 파일 정확 추출 시)은 미구현 — Canvas 즉시 미리보기만 구현
- 다음 단계 참고: (없음)
### 단계 5: 단위 이동
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/hooks/useFrameStep.ts — requestVideoFrameCallback FPS 감지, stepForward/stepBackward
- client/src/hooks/useKeyboard.ts — CLAUDE.md 키보드 단축키 전체 구현
- 프레임/초/장면 단위 이동 완성 (Space/←/→/J/L/,/./[/]/F/M/+/-/Shift+S/Shift+M/0-9)
- 미완료/이슈: (없음)
- 다음 단계 참고: (없음)
### 단계 6: 텍스트 오버레이
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/hooks/useAnnotations.ts — 주석 CRUD API 연동
- client/src/components/overlay/MemoOverlay.tsx — interactjs 드래그, x%/y% 좌표
- client/src/components/sidebar/AnnotationPanel.tsx — 자막/메모 탭, 내보내기(VTT/SRT/JSON/CSV)
- client/src/components/AddAnnotationModal.tsx — 주석 추가 모달
- client/src/utils/timecode.ts — secondsToTimecode, timecodeToSeconds, frameToSeconds, secondsToFrame
- 미완료/이슈:
- WebVTT track 요소를 통한 자막형 렌더링은 미구현 (주석 저장/표시는 완성, track 연동은 단계 7)
- 다음 단계 참고:
- 자막형 WebVTT track 연동은 단계 7 UI/UX 통합 시 추가 가능
- 내보내기는 /api/annotations/:videoId/export?format=vtt|srt|json|csv 백엔드 API 사용
### 검증 (VERIFICATION)
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- VERIFICATION.md 작성 및 전 항목 검증 실행
- V1 정적 검증: server/client tsc 에러 없음, 빌드 성공
- V2 서버 API: 헬스체크/영상목록/업로드/주석 CRUD/내보내기/보안 모두 통과
- V3 코드 품질: 10/10 통과 (MemoOverlay RAF 버그 발견 및 수정)
- V4 파일 구조: 19개 필수 파일 전부 존재 확인
- 수정 사항:
- `VideoPlayerHandle``getVideoElement()` 추가 (VideoPlayer.tsx)
- App.tsx RAF 루프 추가 → MemoOverlay `currentTime` 60fps 업데이트
### 기능 확장: 카메라 파라미터 보정 패널 (2026-03-26)
- 상태: ✅ 완료
- 완료 항목:
- client/src/utils/geoProjection.ts — CameraParams 인터페이스 추가, projectPoint 시그니처 변경
- yawOffset(number) → params(CameraParams) 로 통합
- Roll 회전 수학 추가 (Rodrigues, fwd 축 기준): right_r = right0·cosR up0·sinR
- 주점 오프셋(cx0, cy0) 핀홀 투영에 적용
- 초점 배율(focalScale) 적용: f_eff = focalLen × focalScale
- pxRaw/pyRaw 필드 추가 (클램프 전 원본값, FOV 이탈 감지용)
- DEFAULT_CAMERA_PARAMS export
- client/src/components/overlay/StationOverlay.tsx — 파라미터 패널 UI 전면 개편
- ParamRow 컴포넌트: 슬라이더 + 직접 수치 입력(Enter/blur 커밋) 양방향 동기화
- 자세 보정 섹션: Yaw(-180~+180°), Pitch(-45~+45°), Roll(-45~+45°)
- 내부표정 섹션: f×(0.3~3.0배율), cx₀(-0.5~+0.5), cy₀(-0.5~+0.5)
- 현재 유효값 실시간 표시: yaw_eff, pitch_eff, f_eff(mm), hFOV(°)
- 측점 Canvas 마커 구현: 다이아몬드 ◆ + 측점명 레이블 + 거리(m)
- FOV 이탈 측점 → 가장자리 방향 화살표(drawEdgeArrow)
- POI 마커: + 심볼 + 레이블
- 지면 가이드선: 측점 → 화면 하단 점선
- 나침반 HUD: hFOV를 focalScale 반영하여 동적 계산
- 초기화 버튼, 드론 경로 체크박스
- 빌드 성공: tsc + vite build 에러 없음 (2026-03-26)
- 발견 사항:
- SRT gb_roll=0.0 (전 프레임 고정) → Roll 보정은 이 영상에서 효과 없으나 다른 영상 대비 구조 완성
- 자기 편각(-8°) 보정이 yawOffset 슬라이더의 주된 용도
- focalScale로 실효 FOV를 조정할 수 있어 정밀 보정 가능
- 미완료/이슈:
- GCP 클릭 기반 자동 Resection 미구현 (yaw/pitch 자동 계산)
- 파라미터 설정값 영속 저장(localStorage) 미구현
### 기능 확장: 지리정보 오버레이 + 측점 검증 (2026-03-25)
- 상태: ✅ 완료
- 완료 항목:
- server/src/services/geoMatch.ts — 블렌더 방식 월드 ENU 좌표계 구현
- geoToEnu(): 위경도 → ENU(m), 기준점 위도로 cos(lat) 고정 (전 프레임 일관성)
- projectEnu(): 상대 ENU 벡터 + 카메라 회전 → 핀홀 투영
- project3D(): geoToEnu + projectEnu 래퍼, origin 파라미터 지원
- findFramesForPoi / findPoisForFrame — frames[0]를 월드 원점으로 전달
- getDroneFrames() 신규 export
- server/src/routes/geo.ts — yawOffset 쿼리 파라미터, GET /api/geo/frames 엔드포인트
- client/src/utils/geoProjection.ts — 서버와 동일한 블렌더 방식 ENU 투영, ref 파라미터 추가
- client/src/components/overlay/StationOverlay.tsx — yaw 보정 슬라이더(-180~+180°), 드론 경로 오버레이, pathFrames[0] 원점 전달
- client/src/components/geo/StationVerify.tsx — 측점 목록 + 클릭 검증 패널 (신규)
- client/src/App.tsx — 우측 패널에 "측점" 탭 추가
- server/client 빌드 모두 에러 없음 확인
- 발견 사항:
- cos(lat) 보정을 각 프레임 위도가 아닌 기준점(frame 0) 위도로 고정해야 전 구간 일관성 보장
- CSV yaw/pitch/roll = 짐벌(카메라) 방향 (pitch=-29.8°는 짐벌 고정 틸트, roll=0은 짐벌 보정)
- frame 1791 기준 158K300: px=70.1% (사용자 관측 ~73%와 일치)
- 미완료/이슈:
- Z값(표고) 미적용 — 측점 z값이 ~0m이라 수직 오차 존재, 실제 표고 데이터 확보 후 보정 필요
- GCP 캘리브레이션 UI 미구현 (클릭으로 yaw/pitch 오프셋 자동 계산)
### 지리정보 오버레이 — Python advanced_tuner_v2 포팅 (2026-03-30)
- 상태: ✅ 완료
- 완료 항목:
- geoProjection.ts 재구현: R_b2w=Rz(-yaw)*Rx(pitch)*Ry(roll), R_w2c=R_align@R_b2w.T (Python 동치)
- pitch/roll 절대값 → 오프셋 방식 (Python spn_pitch/roll 기본값 0과 동일)
- sensorH 기본값 20.25mm (=36×9/16, 16:9 영상 기준, 기존 24mm 수직 스케일 오류 수정)
- CameraParams에 offX/offY/offZ 추가 (Python off_x/y/z 동치)
- geoMatch.ts: center.csv 224점 로더 추가 (getCenterlinePoints)
- GET /api/geo/centerline 엔드포인트 추가
- StationOverlay: center.csv 중심선 빨간 선분 시각화 (Python 방식), Catmull-Rom next6 스플라인 제거
- 파라미터 패널: 위치 보정(offX/Y/Z), 센서 크기(senW/H) 슬라이더 추가
- DEFAULT_CAMERA_PARAMS = Python 기본값 (모든 오프셋 0, focal=24, sensor=36)
- 다음 단계 참고:
- off_x/y/z 슬라이더로 GPS 위치 오차 보정 필요
- swap_xy 옵션 미구현 (Python에는 있음, 필요시 추가)
### 단계 7: UI/UX 통합
- 상태: ✅ 완료 (2026-03-24)
- 완료 항목:
- client/src/components/ErrorBoundary.tsx — React 에러 바운더리 (오류 발생 시 복구 UI)
- client/src/components/HelpOverlay.tsx — 키보드 단축키 도움말 오버레이
- client/src/main.tsx — ErrorBoundary로 App 래핑
- client/src/hooks/useKeyboard.ts — ? 키 (Shift+Slash) → onToggleHelp 콜백 추가
- client/src/components/player/VideoPlayer.tsx — onToggleHelp prop 수신 및 useKeyboard에 전달
- client/src/App.tsx — showHelp 상태 + HelpOverlay 렌더링 통합
- README.md — 프로젝트 설치/실행/단축키/환경변수 문서 작성
- TypeScript 타입 에러 없음, 프로덕션 빌드 성공 (1,480 kB / gzip 453 kB)
- 미완료/이슈:
- 빌드 경고: chunk > 500kB (video.js 특성상 정상)
- 다음 단계 참고: 전체 7단계 완료