3.5 KiB
3.5 KiB
しきい値の実測記録
Sensitivity のしきい値は当てずっぽうではなく、実写画像で距離を測って決めている。
判定を変えたくなったらこの手順をなぞって測り直すこと。
測定に使った素材
macOS 内蔵の動画壁紙(実写のタイムラプス)からフレームを切り出したものを元画像とした。
| ファイル | 内容 |
|---|---|
shotA.jpg |
元画像(1600×900, JPEG) |
A_copy.jpg |
cp によるバイト単位の完全コピー |
A_half.jpg |
50% に縮小 |
A_quarter.jpg |
25% に縮小 |
A_lowq.jpg |
同じ寸法で JPEG 品質 25 に再圧縮 |
A_crop.jpg |
中央 1400×900 にトリミング |
A_rot90.jpg |
ピクセルごと 90 度回転 |
shotA_burst.jpg |
0.5 秒後のフレーム(連写相当) |
shotB.jpg / shotC.jpg |
30 秒後 / 90 秒後のフレーム(同一シーンの別カット) |
sonoma.jpg |
まったく別の写真 |
測定結果(Vision の feature print 距離 / dHash のハミング距離)
shotA.jpg を基準にした値。
| 相手 | feature print | ハミング | 判定したい? |
|---|---|---|---|
| 完全コピー・再エンコード | 0.000 | 0 | ○(SHA-256 で先に確定) |
| 50% 縮小 | 0.049 | 5 | ○ |
| 低画質 再圧縮 | 0.116 | 1 | ○ |
| 25% 縮小 | 0.162 | 4 | ○ |
| 連写(0.5 秒差) | 0.008 | 0 | ○ |
| 同一シーン 30 秒差 | 0.017 | 6 | ○ |
| 同一シーン 90 秒差 | 0.039 | 14 | ○ |
| トリミング | 0.417 | 11 | 「ゆるい」でのみ |
| 90 度回転 | 0.802 | 28 | ✗(後述) |
| 無関係な写真 | 1.378 | 24 | ✗ |
決めたしきい値
| 段階 | feature print | ハミング | 何が拾えるか |
|---|---|---|---|
| 厳しい | 0.20 | 8 | コピー・縮小(25% まで)・再圧縮 |
| 標準 | 0.30 | 12 | 上記+連写・同一シーンの近いカット |
| ゆるい | 0.55 | 20 | 上記+トリミングされた版 |
「ゆるい」の 0.55 でも無関係な写真の 1.38 とは大きく離れているので、誤検出の余地は小さい。 当初案の「厳しい = 0.15」では 25% 縮小(0.162)を取りこぼしたため 0.20 に上げた。
回転の扱い(意図的な仕様)
dHash も Vision も EXIF の向きを反映してから比較している。つまり 「画面に表示したときの見た目」どうしを比べている。
- 回転がピクセルに焼き込まれた版と、EXIF の向きタグで回している版 → 距離 0.002 で検出できる (LINE や AirDrop を経由して向きが焼き込まれた、という現実的なケース)
- 実際に向きが違う写真どうし(元画像 vs 90 度回転) → 距離 0.80 で検出しない
後者を拾うには4方向ぶんの特徴量を計算する必要があり、コストが4倍になる割に 写真整理の用途では出番が少ないため、対応していない。
測り直す手順
ffmpeg -ss 0 -i "/System/Library/Desktop Pictures/.wallpapers/Tahoe Day/Tahoe Day.mov" -frames:v 1 -vf scale=1600:-1 shotA.jpg
sips -Z 800 shotA.jpg --out A_half.jpg
sips -s format jpeg -s formatOptions 25 shotA.jpg --out A_lowq.jpg
sips -c 900 1400 shotA.jpg --out A_crop.jpg
これらを1つのフォルダに置き、GenerateImageFeaturePrintRequest(cropAndScaleAction = .scaleToFit)で
総当たりの距離行列を出す。アプリ本体の FeaturePrinter.compute(for:) と同じ設定であることを確認すること。