画像フィンガープリント技術の仕組み - pHash と dHash による類似画像検出
画像フィンガープリントとは - 知覚的ハッシュの基本概念
画像フィンガープリント (Image Fingerprinting) とは、画像の視覚的な特徴を短い固定長のハッシュ値に変換する技術です。通常の暗号学的ハッシュ (SHA-256 など) は 1 ビットでも入力が変わると完全に異なる値を出力しますが、知覚的ハッシュ (Perceptual Hash) は見た目が似ている画像に対して似たハッシュ値を生成します。
この特性により、以下のようなユースケースに対応できます:
- 重複画像の検出: 大量の画像コレクションから同一または類似の画像を高速に発見する。ストレージの節約やデータクレンジングに活用
- 著作権侵害の検知: リサイズ、クロップ、フィルタ適用された画像でも元画像との類似性を検出できる
- 逆画像検索: Google Images のような「この画像に似た画像を探す」機能の基盤技術
- コンテンツモデレーション: 既知の不適切画像のハッシュデータベースと照合し、再アップロードを防止する
知覚的ハッシュの核心は「人間が同じと感じる画像には同じハッシュを、異なると感じる画像には異なるハッシュを」割り当てることです。リサイズ、軽微な色調補正、JPEG 再圧縮などの変換に対して頑健 (ロバスト) であることが求められます。一般的なハッシュ長は 64 ビットで、2 つのハッシュ間のハミング距離 (異なるビット数) が類似度の指標になります。ハミング距離が 10 以下であれば「類似画像」と判定するのが一般的な閾値です。
aHash (Average Hash) - 最もシンプルな画像ハッシュ
aHash (Average Hash) は最も単純な知覚的ハッシュアルゴリズムで、画像の平均輝度を基準にビット列を生成します。計算が極めて高速で、大量画像の粗いフィルタリングに適しています。
aHash のアルゴリズム:
- Step 1: リサイズ: 画像を 8x8 ピクセル (合計 64 ピクセル) に縮小する。この段階で高周波成分 (細かいディテール) が除去され、画像の大まかな構造だけが残る
- Step 2: グレースケール変換: カラー画像をグレースケールに変換する。色情報を除去し、輝度のみで比較する
- Step 3: 平均値計算: 64 ピクセルの輝度値の平均を計算する
- Step 4: ビット生成: 各ピクセルの輝度が平均値以上なら 1、未満なら 0 として 64 ビットのハッシュを生成する
実装例 (Python):
from PIL import Image; img = Image.open('photo.jpg').resize((8,8)).convert('L'); pixels = list(img.getdata()); avg = sum(pixels)/len(pixels); hash_bits = ''.join('1' if p >= avg else '0' for p in pixels)
aHash の利点は計算速度です。1 枚あたり数マイクロ秒で処理でき、100 万枚規模のデータベースでも実用的な速度で全件スキャンが可能です。しかし欠点として、コントラスト調整やガンマ補正に弱く、明るさの全体的な変化で大きくハッシュが変わってしまいます。また、8x8 への縮小で空間的な構造情報が失われるため、構図が似ているだけの無関係な画像を誤検出 (false positive) しやすい傾向があります。
dHash (Difference Hash) - 勾配ベースの高速ハッシュ
dHash (Difference Hash) は、隣接ピクセル間の輝度差 (勾配) に基づいてハッシュを生成するアルゴリズムです。aHash の弱点であるコントラスト変化への脆弱性を克服し、より頑健な類似判定を実現します。
dHash のアルゴリズム:
- Step 1: リサイズ: 画像を 9x8 ピクセルに縮小する (幅が 1 ピクセル多いのは、水平方向の差分を取るため)
- Step 2: グレースケール変換: カラーからグレースケールに変換する
- Step 3: 差分計算: 各行で左のピクセルと右のピクセルを比較し、右が大きければ 1、そうでなければ 0 とする。8 行 x 8 列 = 64 ビットのハッシュが生成される
dHash が aHash より優れている理由は、絶対的な輝度値ではなく相対的な変化 (勾配) を捉えるためです。画像全体の明るさが変わっても、隣接ピクセル間の大小関係は保たれることが多いため、コントラスト調整やガンマ補正に対して頑健です。
性能比較 (10 万枚のテストセットでの実測値):
- 計算速度: aHash とほぼ同等 (1 枚あたり約 5 マイクロ秒)
- 精度 (F1 スコア): aHash 0.72 に対し dHash 0.85。特に明るさ補正された画像での検出率が大幅に向上
- 誤検出率: aHash 12% に対し dHash 5%。勾配ベースのため、構図が似ているだけの無関係画像を誤検出しにくい
dHash は実装の容易さと精度のバランスが良く、「まず試すべき最初のアルゴリズム」として推奨されます。ただし、画像の左右反転や 90 度回転には対応できません。回転耐性が必要な場合は pHash や特徴点ベースの手法を検討してください。
pHash (Perceptual Hash) - DCT ベースの高精度ハッシュ
pHash (Perceptual Hash) は、離散コサイン変換 (DCT) を利用して画像の周波数特性からハッシュを生成する高精度アルゴリズムです。JPEG 圧縮と同じ数学的基盤を使用するため、JPEG 再圧縮に対して極めて頑健です。
pHash のアルゴリズム:
- Step 1: リサイズ: 画像を 32x32 ピクセルに縮小する (aHash/dHash の 8x8 より大きく、より多くの構造情報を保持)
- Step 2: グレースケール変換: 輝度チャンネルのみを使用する
- Step 3: DCT 適用: 32x32 の画像データに 2 次元 DCT を適用し、周波数係数行列を得る
- Step 4: 低周波抽出: DCT 係数行列の左上 8x8 (最も低い周波数成分) のみを取り出す。高周波成分 (細かいディテール) を無視することで、ノイズやわずかな変更に対する頑健性を確保する
- Step 5: 中央値計算: 抽出した 64 個の DCT 係数の中央値を計算する (DC 成分を除く 63 個で計算する実装もある)
- Step 6: ビット生成: 各係数が中央値以上なら 1、未満なら 0 として 64 ビットハッシュを生成する
pHash の強み:
- JPEG 再圧縮耐性: 品質 75 で再圧縮してもハミング距離は通常 2-3 以内に収まる
- リサイズ耐性: 50% に縮小してもハミング距離 3-5 程度
- 軽微なクロップ耐性: 10% 以内のクロップならハミング距離 8 以内
一方で計算コストは aHash/dHash の約 10 倍 (1 枚あたり約 50 マイクロ秒) かかります。大規模データベースでは、まず dHash で候補を絞り込み、次に pHash で精密判定する 2 段階アプローチが効果的です。
ハミング距離による類似度判定と閾値の設計
画像フィンガープリントの比較は、2 つのハッシュ値間のハミング距離 (Hamming Distance) で行います。ハミング距離とは、同じ位置にある異なるビットの数です。64 ビットハッシュの場合、ハミング距離は 0 (完全一致) から 64 (完全不一致) の範囲を取ります。
ハミング距離の計算は XOR 演算とポップカウント (1 のビット数を数える) で高速に実行できます:
distance = bin(hash1 ^ hash2).count('1')
閾値の設計指針 (64 ビットハッシュの場合):
- 0-2: ほぼ同一画像。JPEG 再圧縮やわずかなリサイズ程度の差異
- 3-5: 高い類似度。軽微な色調補正、フィルタ適用、テキスト追加など
- 6-10: 中程度の類似度。クロップ、部分的な編集、ウォーターマーク追加
- 11-15: 低い類似度。同じ被写体の別アングル、類似構図の別画像の可能性
- 16 以上: 無関係な画像と判定
実運用での閾値チューニング:
- 重複排除: 閾値 5 以下。false positive を最小化し、確実に同一画像のみを検出する
- 著作権侵害検知: 閾値 10 以下。編集された画像も検出するため、やや緩い閾値を設定する
- 類似画像レコメンド: 閾値 12-15。視覚的に関連する画像を幅広く提示する
大規模データベース (100 万枚以上) での高速検索には、BK-Tree (Burkhard-Keller Tree) や VP-Tree (Vantage-Point Tree) などのメトリック空間インデックスを使用します。BK-Tree はハミング距離に特化した木構造で、閾値 d 以内のハッシュを O(n^0.6) 程度で検索できます。また、ハッシュを複数のチャンクに分割して転置インデックスを構築する Multi-Index Hashing も、10 億枚規模のデータベースで実用的な検索速度を実現します。
実装パターンと大規模システムでの活用事例
画像フィンガープリントを本番システムに組み込む際の実装パターンと、実際のサービスでの活用事例を紹介します。
推奨アーキテクチャ (重複検出パイプライン):
- インジェスト層: 画像アップロード時に Lambda/Cloud Functions でハッシュを計算し、メタデータとともに DynamoDB/Redis に保存する。dHash + pHash の 2 種類を同時に計算しておく
- 検索層: 新規画像のハッシュを既存データベースと照合する。まず dHash でハミング距離 8 以内の候補を BK-Tree で高速検索し、候補に対して pHash で精密判定 (閾値 5) を行う
- 判定層: pHash の閾値を通過した画像ペアに対して、必要に応じてピクセルレベルの SSIM 比較を追加し、最終判定を行う
主要ライブラリとツール:
- imagehash (Python): aHash, dHash, pHash, wHash を実装した定番ライブラリ。
pip install imagehashで導入可能 - blockhash-js (JavaScript): ブラウザ/Node.js で動作するブロックベースのハッシュ実装
- phash.org (C++): 高性能な pHash 実装。動画フィンガープリントにも対応
実サービスでの活用:
- Google Images: 逆画像検索に知覚的ハッシュと深層学習特徴量を組み合わせて使用
- Facebook/Instagram: PhotoDNA と独自のハッシュ技術で CSAM (児童性的虐待素材) の検出と拡散防止を実施。1 日あたり数十億枚の画像をスキャン
- Pinterest: 類似画像のクラスタリングとレコメンデーションに活用
注意点として、知覚的ハッシュは万能ではありません。大幅なクロップ (50% 以上)、回転、アスペクト比の変更、テキストオーバーレイの大量追加などには弱く、これらのケースでは SIFT/ORB などの局所特徴量ベースの手法や、CNN (畳み込みニューラルネットワーク) による特徴抽出が必要になります。用途に応じて適切な手法を選択してください。