diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a153f4 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# Photochk + +フォルダの中から、中身が同じ写真と見た目が似ている写真を探して一覧にする macOS アプリ。 + +同じ写真のコピー、リサイズ版、メッセージアプリ経由で再圧縮された版、連写のほぼ同じカット — +ファイル名もサイズも違うので Finder では見つからないものを、グループにまとめて見せる。 +残す1枚を選んで、ほかをまとめてゴミ箱へ送れる(削除ではないので Finder から戻せる)。 + +## 使い方 + +```bash +make run +``` + +`make build` でビルドのみ、`make test` でテスト。生成物は `build/Build/Products/Debug/Photochk.app`。 + +配布用に固めるなら `make release`(Release ビルド+`dist/Photochk-1.0.0.zip`)、 +`/Applications` に入れるなら `make install`。 + +> ビルドは ad-hoc 署名(Developer ID 証明書なし)。自分の Mac でビルドして使う分には問題ないが、 +> zip を他の Mac に配ると Gatekeeper に止められる。受け取った側は右クリック →「開く」で起動できる。 + +1. ツールバーの「フォルダを選ぶ」で対象フォルダを指定する +2. 左のグループ一覧から選ぶと、右にそのグループの全枚数が並ぶ +3. 残す1枚以外にチェックを入れる(「一括選択」メニューで「解像度が最も高い1枚を残す」なども選べる) +4. 「N 枚をゴミ箱へ」で移動する + +グループから全部を選ぶことはできない。必ず1枚は残る。 + +各写真には基準(代表)からの距離が出る。代表以外にもバイト単位で中身が同じファイルがある場合は +「同一 A」のように同じ印が付くので、その中のどれを消しても失われるものがないと分かる。 + +## 検出のしくみ + +安い判定から順にかけて候補を絞り込む。 + +1. **完全同一** — ファイルサイズが同じものだけ SHA-256 を計算して、バイト単位の重複を確定する +2. **知覚ハッシュ (dHash)** — 9×8 のグレースケールに落として隣接ピクセルの明暗を比べ、64bit のハッシュを作る。 + ハミング距離が近いペアだけを次に回す +3. **Vision の feature print** — 候補ペアに出てくる画像だけ `GenerateImageFeaturePrintRequest` で + 特徴量を計算し、距離で最終判定する +4. **Union-Find** — 「同一」「類似」のペアから連結成分を作ってグループにする + +各グループの代表は「解像度が最大 → ファイルサイズが最大 → 撮影が最古」で自動的に選ばれる。 + +判定の厳しさは3段階。しきい値は実写画像で距離を測って決めた([docs/calibration.md](docs/calibration.md))。 + +| 段階 | 何が拾えるか | +|---|---| +| 厳しい | コピー・縮小(25% まで)・再圧縮 | +| 標準 | 上記+連写・同一シーンの近いカット | +| ゆるい | 上記+トリミングされた版 | + +対応形式は UTType が画像に該当するものすべて。JPEG / PNG / HEIC / TIFF に加えて各社の RAW も扱える。 + +### 分かっている制限 + +- **向きが違う写真は別物として扱う。** EXIF の向きを反映した「見た目」で比較しているため、 + 回転が焼き込まれた版と EXIF で回している版は同じ写真として検出できるが、 + 実際に 90 度違う向きの写真どうしは検出しない(4方向ぶんの特徴量が必要でコストが4倍になるため) +- 2回目以降のスキャンは `~/Library/Caches/Photochk/fingerprints.plist` のキャッシュで速くなる。 + ファイルサイズと更新日時が変わっていれば自動で計算し直す + +## 構成 + +``` +Sources/Photochk/ +├── Models/ PhotoAsset, SimilarityGroup, ScanSettings +├── Core/ 走査・ハッシュ・特徴量・グルーピング(UI 非依存) +├── Services/ サムネイル生成、ゴミ箱、フォルダのアクセス権 +├── ViewModels/ ScanViewModel +└── Views/ SwiftUI +``` + +外部依存なし。SwiftUI / Vision / CryptoKit / ImageIO / QuickLookThumbnailing のみ。 +App Sandbox 有効で、アクセスできるのはユーザーが選んだフォルダだけ。 + +## 開発 + +Xcode プロジェクトは XcodeGen で `project.yml` から生成する(リポジトリには含めない)。 + +```bash +make generate && open Photochk.xcodeproj +``` + +必要なもの: macOS 15 以降、Xcode 26 以降、`brew install xcodegen`。 + +`Tests/PhotochkTests/SnapshotTests.swift` は `ImageRenderer` で写真タイルなどを実際に描画し、 +PNG を一時フォルダに書き出す(パスはテスト出力に出る)。レイアウトを変えたら開いて目で確認できる。 +`List` や `NavigationSplitView` は AppKit 由来で `ImageRenderer` では描けないため対象外。