4.9 KiB
Photochk
フォルダの中から、中身が同じ写真と見た目が似ている写真を探して一覧にする macOS アプリ。
同じ写真のコピー、リサイズ版、メッセージアプリ経由で再圧縮された版、連写のほぼ同じカット — ファイル名もサイズも違うので Finder では見つからないものを、グループにまとめて見せる。 残す1枚を選んで、ほかをまとめてゴミ箱へ送れる(削除ではないので Finder から戻せる)。
使い方
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枚以外にチェックを入れる(「一括選択」メニューで「解像度が最も高い1枚を残す」なども選べる)
- 「N 枚をゴミ箱へ」で移動する
グループから全部を選ぶことはできない。必ず1枚は残る。
各写真には基準(代表)からの距離が出る。代表以外にもバイト単位で中身が同じファイルがある場合は 「同一 A」のように同じ印が付くので、その中のどれを消しても失われるものがないと分かる。
検出のしくみ
安い判定から順にかけて候補を絞り込む。
- 完全同一 — ファイルサイズが同じものだけ SHA-256 を計算して、バイト単位の重複を確定する
- 知覚ハッシュ (dHash) — 9×8 のグレースケールに落として隣接ピクセルの明暗を比べ、64bit のハッシュを作る。 ハミング距離が近いペアだけを次に回す
- Vision の feature print — 候補ペアに出てくる画像だけ
GenerateImageFeaturePrintRequestで 特徴量を計算し、距離で最終判定する - Union-Find — 「同一」「類似」のペアから連結成分を作ってグループにする
各グループの代表は「解像度が最大 → ファイルサイズが最大 → 撮影が最古」で自動的に選ばれる。
判定の厳しさは3段階。しきい値は実写画像で距離を測って決めた(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 から生成する(リポジトリには含めない)。
make generate && open Photochk.xcodeproj
必要なもの: macOS 15 以降、Xcode 26 以降、brew install xcodegen。
Tests/PhotochkTests/SnapshotTests.swift は ImageRenderer で写真タイルなどを実際に描画し、
PNG を一時フォルダに書き出す(パスはテスト出力に出る)。レイアウトを変えたら開いて目で確認できる。
List や NavigationSplitView は AppKit 由来で ImageRenderer では描けないため対象外。