91 lines
4.9 KiB
Markdown
91 lines
4.9 KiB
Markdown
# 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` では描けないため対象外。
|