Files
photoai/docs/NEXTGEN_REVIEW.md
T
koriweb 3e73967c7b darktable-inspired reskin + metadata/collections, map, easy mode, select/export
UI overhaul to a darktable tone-and-manner and a set of features adapted from
darktable's proven patterns (reimplemented in our Electron/TS stack; no GPL code).

Design reskin:
- Dark neutral-gray palette + amber accent, flat/squared corners, no card shadows,
  compact darktable-style top bar (logo + pipe-separated view tabs), denser 15px base
- Done via design tokens (Tailwind slate/brand/radius/shadow remap) — minimal churn

Metadata & collections (Phase A/B):
- exifr now captures GPS + camera; asset table ALTER-migrated (gpsLat/gpsLon/camera,
  metaVersion backfill on re-index)
- Collection facet bar (year timeline / camera / color-label) filters the grid

Map & relation finder (Phase C):
- Leaflet + online OSM map tab; geotagged photos as markers
- relationService: related photos by place (GPS<1km) + time (+/-2d) + CLIP similarity

Easy mode (Phase D):
- easyMode setting (menu / onboarding); scales the whole UI (rem) + bigger thumbnails
  + large icon nav with plain labels (4050 accessibility)

Library usability:
- Video thumbnails (representative frame capture in the inference worker)
- Media filter (All / Photos / Videos) to separate them
- Clearer culling labels ("Good shots" / "To cull") + explanation tooltip
- Multi-select tiles -> Export selected to a folder (copy, best-cut extraction) and
  Delete to Recycle Bin (shell.trashItem) behind a confirm dialog
- ONNX Runtime wasm bundled locally (offline) via copy-ort-wasm + asarUnpack

Docs: DARKTABLE_REVIEW (feasibility + roadmap A->D). All typecheck/tests/build green;
boot smoke verified each phase.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 19:22:19 +09:00

207 lines
15 KiB
Markdown

# NextGen Photo AI — 확장 기획 타당성 검토 & 로드맵
> 상태: **검토(Review) / 계획 초안** · 작성: 2026-06-01
> 대상 기획: "NextGen Photo AI — 지능형 사진 관리 솔루션"
> 현재 베이스: [ARCHITECTURE.md](./ARCHITECTURE.md) (Electron + face-api 폴더 정리기)
---
## 0. 한 줄 결론
기술적으로 **전부 로컬(On-device)로 구현 가능**합니다. 단, 이는 기능 추가가 아니라 **제품의 성격 전환**입니다:
현재 "폴더를 1회 스캔해 인물/날짜로 이동"하는 **무상태(stateless) 정리기** → 사진을 **지속적으로 색인(index)하고 검색·평가·그룹화**하는 **상태 기반 라이브러리(DAM)**.
→ 핵심은 새 기능들이 아니라 그 아래 깔릴 **"라이브러리 인덱스" 기반(임베딩 + 메타 + 썸네일 DB)** 입니다. 이것부터 만들어야 나머지가 전부 그 위에 올라갑니다.
---
## 1. 기능별 타당성 (로컬 구현 관점)
| 기능 | 핵심 기술 | 로컬 가능성 | 난이도 | 비고 |
|------|----------|:-----------:|:------:|------|
| **[P1] AI 품질 평가 / Culling** | 초점=라플라시안 분산, 노출=히스토그램, 감은 눈=face-api 랜드마크(EAR), 미학=NIMA(ONNX) | ✅ 높음 | 중 | 초점/노출/눈은 모델 없이 가능. 미학 점수만 ONNX 모델 필요 |
| **[P1] 자연어 검색** | CLIP 이미지/텍스트 임베딩(transformers.js, ONNX, WebGPU) + 벡터 검색 | ✅ 가능 | 상 | **한국어 쿼리 처리 방식 결정 필요**(§4-1) |
| **[P1] 시각적 유사 검색** | 동일 CLIP 임베딩 코사인 유사도 | ✅ 높음 | 중 | 임베딩만 있으면 쉬움 |
| **[P2] 스마트 그룹화** | 임베딩 클러스터링(k-means/임계값) | ✅ 높음 | 중 | Phase 2 임베딩 재사용 |
| **[P2] 자가 정화(중복/저품질)** | 지각적 해시(pHash/dHash) + 품질 점수 | ✅ 높음 | 중 | pHash는 순수 JS 구현 가능 |
| (시나리오 A) **RAW 파일** | libraw/dcraw (WASM 또는 네이티브) | ⚠️ 가능하나 무거움 | 상 | **범위 포함 여부 결정 필요**(§4-2) |
**품질 평가/Culling이 모델 리스크가 가장 낮고 가치가 높음** → 1순위로 출시 권장.
---
## 2. 필요한 기술 스택 추가
| 영역 | 후보 | 메모 |
|------|------|------|
| ML 런타임 | **transformers.js v3** (@huggingface/transformers) | ONNX Runtime Web, **WebGPU 가속** + WASM 폴백. 전부 로컬 |
| 임베딩 모델 | **CLIP ViT-B/32**(영어, ~경량) 또는 **다국어 CLIP**(jina-clip-v2 등, 무거움) | §4-1 결정에 따름 |
| 한국어 처리(옵션) | **opus-mt-ko-en**(쿼리 번역, 소형) | 영어 CLIP + 쿼리만 KO→EN 번역하는 절충안 |
| 미학 점수(옵션) | NIMA류 ONNX | 없으면 초점/노출/눈만으로 v1 |
| 인덱스 DB | **better-sqlite3** (메타+점수+pHash) + 임베딩(BLOB 또는 별도 바이너리) | JSON은 수천 장에서 한계 → SQLite 필요 |
| 벡터 검색 | 초기 **브루트포스 코사인**(~5만 장까지 OK) → 대규모 시 **sqlite-vec / hnswlib-wasm** | 단계적 |
| 썸네일 | sharp(네이티브) 또는 canvas 리사이즈 + 디스크 캐시 | 그리드 UI 필수 |
| 지각 해시 | dHash/aHash 순수 JS | 중복 탐지 |
| RAW(옵션) | libraw-wasm / dcraw | §4-2 결정 |
> 모델 동봉 시 앱 용량이 **수백 MB** 증가합니다(CLIP ~150~350MB + 런타임). 로컬·오프라인 정책상 동봉 또는 최초 실행 시 1회 다운로드 방식 결정 필요.
---
## 3. 아키텍처 변화 (가장 중요한 부분)
현재 3-프로세스(Main / UI / 추론창)에 **2개 축**을 추가:
```
추가 1) Library Index (영속 상태)
SQLite DB: 파일별 { contentHash, path, exif, faces,
clipEmbedding(512d), qualityScores, pHash, thumbnailRef }
- 파일은 contentHash로 식별 → 경로가 바뀌어도 추적, 중복 인식
- "이동/복사" 정리기와 공존: 정리기도 이 인덱스를 활용
추가 2) AI Worker (백그라운드 색인기)
- 기존 '숨김 추론창' 패턴 확장 또는 Web Worker(+WebGPU)
- 신규/변경 파일을 배치로: CLIP 임베딩 + 품질 점수 + pHash 계산
- 재개 가능(resumable), UI 프리징 0 (별 프로세스/워커)
```
새 데이터 흐름:
```
임포트/스캔 → (AI Worker) 임베딩·점수·해시 계산 → Index DB 저장
검색: 쿼리 → (번역?) → 텍스트 임베딩 → DB 벡터 코사인 Top-K → 결과 그리드
Culling: Index의 품질 점수로 필터 → 후보군/제외 뷰
그룹화: 임베딩 클러스터링 → 클러스터 뷰
정화: pHash 근접쌍 + 저품질 → 정리 제안
```
> **비파괴(non-destructive) 색인**이 핵심: 검색/평가/그룹화는 파일을 옮기지 않고 "제자리에서" 인덱싱합니다. 현재의 이동 기반 정리기와는 철학이 다르므로, 둘을 어떻게 공존시킬지(§4-3)가 설계 관건입니다.
---
## 4. 열린 질문 — 확정됨 (2026-06-01)
| # | 항목 | 결정 |
|---|------|------|
| 1 | 한국어 자연어 검색 | **영어 CLIP + 쿼리 KO→EN 번역**(경량·빠름). Phase 2에서 정확도 실측 |
| 2 | RAW 지원 | **차후(Phase 4)**. 우선 JPEG/PNG/WebP |
| 3 | 라이브러리 모델 | **인덱스 기반 통합** — 비파괴 색인을 기본으로, 기존 이동/복사 정리기도 인덱스 활용 |
| 4 | 진행 범위 | **Phase 0 + Phase 1 먼저** |
| 5 | 모델 배포 | (미정 — Phase 1은 모델 거의 불필요. Phase 2 진입 시 결정) |
| 6 | 타깃 규모 | 초기 **수천~수만 장** 가정(브루트포스 벡터검색으로 충분), 대규모는 Phase 4 |
---
## 5. 단계별 로드맵 (권장 시퀀스)
각 Phase는 독립 출시 가능하도록 설계.
- **Phase 0 — 라이브러리 인덱스 기반** *(필수 선행)*
SQLite 인덱스 + contentHash 식별 + 썸네일 캐시 + AI Worker 스캐폴드(재개 가능 배치). 화면 변화 없음, 토대만.
- **Phase 1 — [P1] 품질 평가 & 스마트 Culling** *(첫 출시 권장)*
초점(라플라시안)·노출(히스토그램)·감은 눈(face-api 랜드마크) 점수 → '고품질 후보 / 제외' 뷰. 모델 리스크 최저, 가치 즉시.
- **Phase 2 — [P1] CLIP 임베딩 + 자연어/유사 검색**
임베딩 색인 + 검색 UI(쿼리바·결과 그리드·"이 사진과 비슷한"). §4-1 결정 반영.
- **Phase 3 — [P2] 스마트 그룹화 + 자가 정화**
임베딩 클러스터링 + pHash 중복/저품질 정리 제안. Phase 1·2 산출물 재사용.
- **Phase 4 — 옵션/결정 의존**
RAW 지원, 미학(NIMA) 점수, 대규모용 ANN 인덱스(sqlite-vec/HNSW).
---
## 6. 리스크 & 솔직한 평가
- **규모**: 이 확장은 현재 앱과 **비슷하거나 더 큰 작업량**입니다(수 주 단위, 다중 Phase). "정리기"에서 "지능형 라이브러리"로의 제품 전환입니다.
- **한국어 검색 품질**: CLIP 계열은 영어 중심 → 번역/다국어 모델 선택에 따라 KPI(Search Success Rate)가 크게 좌우됨. **초기 POC로 실측 검증 필요**.
- **성능/하드웨어**: 수천 장 임베딩은 GPU(WebGPU)에서도 수 분~수십 분. 저사양(WASM)에서는 훨씬 느림 → 백그라운드·재개·진행률 UX 필수.
- **앱 용량/배포**: 모델 동봉 시 수백 MB. 배포 방식 결정 필요.
- **RAW**: 브라우저 환경 RAW 디코딩은 까다로움 → 범위 신중 결정.
## 7. 제안
**Phase 0 + Phase 1**(인덱스 기반 + 품질/Culling)을 1차 목표로 삼는 것을 권장합니다. 모델 리스크가 낮고, 자연어 검색(Phase 2)의 토대(인덱스·워커)를 그대로 재사용합니다. 자연어 검색은 §4-1 결정 후 **소규모 POC로 한국어 정확도부터 실측**하고 본 구현에 들어가는 것을 권합니다.
---
## 8. Phase 0 + Phase 1 상세 실행 계획 (확정 범위)
### 8.1 기술 추가 (이 단계 한정)
- ~~better-sqlite3(네이티브)~~ → **`sql.js`(WASM SQLite)로 확정**. 이유: 이 환경(Node 24 + Python 3.12)에서 네이티브 컴파일이 실패(distutils 제거)했고, 네이티브 모듈은 사용자 PC마다 ABI/빌드툴 문제가 재발한다. WASM은 빌드/재빌드가 전혀 없어 Windows+macOS 배포가 단순. 인메모리 DB를 `userData/index.db`로 export 영속화. (수천~수만 장 메타데이터 규모에 충분. 추후 필요 시 indexDb 추상화 뒤에서 네이티브로 교체 가능.)
- **썸네일은 네이티브(sharp) 없이** AI Worker(렌더러)의 canvas로 생성 → 바이트를 Main이 캐시. 네이티브 의존 0.
> ✅ **Phase 0-a 완료(2026-06-01)**: `indexDb`(sql.js) + asset/quality 스키마 + 영속화. Electron 부팅 시 `userData/index.db` 생성 확인. typecheck/build/스모크 통과.
- Phase 1 품질 점수는 **모델 거의 불필요**: 초점=라플라시안 분산, 노출=휘도 히스토그램, 감은 눈=face-api 랜드마크(EAR). face-api는 이미 추론창에 로드됨.
### 8.2 데이터 모델 (SQLite)
```
asset(
id INTEGER PK, contentHash TEXT UNIQUE, path TEXT, ext TEXT,
sizeBytes INT, mtime INT, width INT, height INT,
exifYear TEXT, exifMonth TEXT, indexedAt INT
)
quality(
assetId FK, focus REAL, exposure REAL, eyesOpen REAL,
flag TEXT -- 'candidate' | 'blurry' | 'eyesClosed' | 'badExposure'
)
-- Phase 2 예약: embedding(assetId, vec BLOB), phash(assetId, hash)
```
- **contentHash**(파일 내용 해시)로 식별 → 경로가 바뀌어도 추적, 정확 중복 인식. `(contentHash, mtime)` 동일하면 재색인 스킵(재개 가능).
- 썸네일: `userData/thumbs/<contentHash>.webp`.
### 8.3 프로세스/모듈 추가
```
Main
indexDb.ts SQLite 래퍼 (better-sqlite3)
libraryStore.ts 색인 대상 라이브러리 폴더 관리(settings)
indexer.ts 오케스트레이터: 라이브러리 워크 → 워커 디스패치 → DB upsert → 진행률
cullingService.ts Phase1 결과 조회/뷰 데이터 제공
AI Worker (기존 숨김 추론창 확장 or 신규 'indexer' 창)
qualityEngine.ts 초점/노출/EAR 점수 + 썸네일 생성 (canvas + face-api 재사용)
UI (신규 화면)
LibraryView 라이브러리 폴더 지정 + 색인 진행률
CullingView '고품질 후보 / 제외(흐림·눈감음·노출)' 그리드 + 점수/오버라이드
```
- 비파괴: 색인은 파일을 옮기지 않음. Culling 결과로 '제외'를 **태그**만 하고, 원하면 사용자가 별도 폴더로 내보내기(기존 fileOps 재사용, 선택).
- 기존 정리기와 통합: 정리 잡도 인덱스(EXIF/얼굴)를 재사용하도록 점진 연결.
### 8.4 작업 순서 (체크리스트)
- [x] **Phase 0-a 완료**: `indexDb`(sql.js로 확정) 스키마/마이그레이션 + Electron 부팅 시 DB 생성 검증
- [x] **Phase 0-b 완료**: contentHash(샘플 sha1) + 라이브러리 폴더 지정 UI(라이브러리 탭) + `indexer` 워크/재개(경로·mtime 스킵)/진행률 IPC/배치 영속화/취소. 헤드리스 파이프라인 검증(중복 0) + 부팅 스모크 통과
- [x] **Phase 0-c 완료**: AI Worker(추론창) canvas로 썸네일(webp) 생성 → `userData/thumbs/<hash>.webp` 캐시(비파괴), 색인 시 자동 생성 + 원본 치수 저장, `photoai-media://thumb` 해시 기반 보안 제공, 라이브러리 탭 썸네일 그리드(페이지네이션). typecheck/build/부팅 스모크 통과
- [x] **Phase 1-a 완료**: `qualityEngine` 초점(라플라시안 분산)/노출(히스토그램 클리핑) 점수 → DB
- [x] **Phase 1-b 완료**: 감은 눈(EAR, face-api 랜드마크, Tiny 검출기) 점수 → `classifyFlag`로 종합 분류(candidate/blurry/eyesClosed/badExposure). 색인 시 `infer:analyze`로 썸네일+품질 1회 로드 통합
- [x] **Phase 1-c (기본) 완료**: 라이브러리 그리드에 컬링 필터(전체/고품질 후보/제외) + 품질 배지. *임계값 튜닝 UI · 사진별 수동 오버라이드는 후속으로 보류*
- [ ] Phase 1-d(옵션): 제외 사진 내보내기/이동 액션 (보류)
### 8.6 Phase 1 마무리 (임계값 튜닝 + 수동 오버라이드) — 완료(2026-06-01)
- [x] 품질 임계값(초점/노출/눈)을 설정에 저장 + 슬라이더 UI. 임계값 변경 시 **저장된 원본 점수로 SQL CASE 실시간 재분류**(재분석 없음)
- [x] 별점(0~5) + 색라벨(5색) 수동 메타(`usermeta` 테이블), 썸네일 호버 편집, 별점 최소 필터
### 8.7 Phase 2 (CLIP 자연어/유사 검색) — 기본 완료(2026-06-01)
- [x] `@huggingface/transformers`(WASM/WebGPU, 네이티브 빌드 0) 도입
- [x] CLIP(`Xenova/clip-vit-base-patch32`) 이미지/텍스트 임베딩 — 추론창 lazy-load
- [x] 한국어 쿼리 자동 번역(`Xenova/opus-mt-ko-en`) → 영어 CLIP
- [x] 임베딩 SQLite BLOB 저장 + "검색 색인 생성"(임베딩 배치, 진행률/취소) — 기본 색인과 분리
- [x] 브루트포스 코사인 검색 + **검색 탭**(임베딩 상태/생성 + 검색바 + 결과 그리드)
- [x] typecheck/build/부팅 스모크 통과
> ⚠️ **Phase 2 런타임 미검증 항목**: CLIP/번역 모델은 **최초 사용 시 HF Hub에서 다운로드**(온라인 1회 필요, 이후 캐시). 한국어 검색 정확도 실측은 후속. 임베딩 생성은 이미지당 수백 ms(WASM) → 대량은 시간 소요(백그라운드).
### 8.8 Phase 2 마무리 — 완료(2026-06-01)
- [x] **ONNX Runtime WASM 오프라인 동봉**: transformers.js의 `ort-wasm-simd-threaded.jsep.wasm`(20.6MB)을 `public/ort`로 복사(`scripts/copy-ort-wasm`, postinstall/prebuild) → `env.backends.onnx.wasm.wasmPaths`를 로컬 경로로 지정(CDN 불필요). electron-builder asarUnpack 처리. (모델 가중치 자체는 여전히 최초 1회 다운로드)
- [x] **쿼리 템플릿**: 텍스트 임베딩 시 `"a photo of {query}"` 템플릿 적용(CLIP 정확도 향상), 단일 스레드(numThreads=1)로 COEP 미보장 환경 대응
### 8.9 Phase 3 (스마트 그룹화 + 자가정화) — 완료(2026-06-01)
- [x] **그룹화**: 임베딩 코사인 유사도 그리디 클러스터링(`groupingService`), 유사도 임계값 슬라이더, 그룹별 **보관 추천(가장 선명)** 자동 표시
- [x] **자가정화**: 그룹의 보관 추천 외 항목을 선택 → **OS 휴지통으로 이동**(`shell.trashItem`, 복구 가능) + 인덱스에서 삭제(연관 메타/임베딩 cascade)
- [x] **그룹·정화 탭** 신설(확인 다이얼로그 포함), typecheck/build/부팅 스모크 통과
- 비고: 저품질 정화는 라이브러리 탭의 '제외 후보' 필터로 이미 접근 가능. 근접중복이 본 단계의 핵심.
- [ ] i18n(ko/en) · 다크모드 · 검증(typecheck/test/build/스모크)
### 8.5 리스크 (이 단계)
- **better-sqlite3 ABI**: Electron 버전별 재빌드 필요 → CI/빌드에서 `electron-builder`가 처리하나, dev 실행 시 `electron-rebuild` 1회 필요할 수 있음.
- **대량 색인 UX**: 수천 장 썸네일/점수 계산은 시간 소요 → 백그라운드·재개·진행률 필수(이미 설계 반영).
- EAR 기반 눈감음은 휴리스틱 → 임계값 튜닝 및 오버라이드 UI로 보완.
> 본 계획 승인 시 **Phase 0-a(인덱스 DB 토대)**부터 착수.