web-dev-qa-db-ja.com

画像比較 - 高速アルゴリズム

私は画像の基本テーブルを作成し、それから新しい画像が基本の正確な(または近い)複製であるかどうかを判断するためにそれに対して新しい画像を比較しようとしています。

たとえば、同じ画像の保存容量を数百回削減したい場合は、そのコピーを1つ保存して、それへの参照リンクを提供することができます。新しい画像が入力されたら、既存の画像と比較して、重複していないことを確認します。

私の考えの1つは、小さなサムネイルに縮小してから、ランダムに100ピクセルの場所を選んで比較することでした。

374
meade

以下はこの問題を解決するための3つのアプローチです(そして他にもたくさんあります)。

  • 1つ目は、コンピュータビジョンにおける標準的なアプローチ、キーポイントマッチングです。これを実行するにはある程度の背景知識が必要になるかもしれず、遅くなる可能性があります。

  • 第2の方法は基本的な画像処理のみを使用し、第1の方法より潜在的に高速であり、そして実行するのが簡単である。しかしながら、それが理解しやすくするために得られるもの、頑健性に欠けている - 拡大縮小された、回転された、または変色した画像ではマッチングは失敗する。

  • 3番目の方法は高速で堅牢ですが、実装が最も難しい可能性があります。

キーポイントマッチング

100個のランダムな点を選ぶよりも、100個の重要な点を選ぶのが得策です。画像のある部分は他の部分よりも多くの情報を持っています(特にエッジやコーナー)。これらはスマートな画像マッチングに使用したいものです。 Googleの「 キーポイント抽出 」と「 キーポイントマッチング 」とこの問題に関する学術論文をかなりたくさん見つけてください。最近では、 SIFTキーポイント が間違いなく最も人気があります。これは、異なるスケール、回転、および照明の下でイメージを一致させることができるためです。いくつかのSIFTの実装はここで見つけることができます。

キーポイントマッチングの欠点の1つは、単純実装の実行時間です。O(n ^ 2m)。ここで、nは各画像内のキーポイントの数、mはデータベース内の画像の数です。四分木や二値空間分割のように、賢いアルゴリズムの中には最も近いものをより早く見つけるものがあるかもしれません。


代替解法:ヒストグラム法

もう1つの堅牢性は低いが潜在的に高速な解決策は、各画像の特徴ヒストグラムを作成し、入力画像のヒストグラムに最も近いヒストグラムを持つ画像を選択することです。私はこれを学部として実装しました、そして我々は3色ヒストグラム(赤、緑、そして青)と2つのテクスチャヒストグラム、方向とスケールを使いました。以下に詳細を説明しますが、これはデータベースイメージと非常によく似たイメージのマッチングにのみ有効でした。拡大縮小、回転、または変色した画像はこの方法では失敗する可能性がありますが、切り抜きなどの小さな変更でアルゴリズムが破損することはありません。

カラーヒストグラムの計算は簡単です。ヒストグラムバケットの範囲を選択し、各範囲について、その範囲の色を持つピクセル数を合計するだけです。たとえば、「緑色」のヒストグラムを考えてみます。ヒストグラムに4つのバケット(0-63、64-127、128-191、および192-255)を選択したとします。次に、ピクセルごとに緑色の値を調べて、適切なバケットに集計を追加します。集計が完了したら、各バケットの合計を画像全体のピクセル数で割り、緑色のチャンネルの正規化ヒストグラムを取得します。

テクスチャ方向ヒストグラムでは、まず画像上でエッジ検出を行います。各Edgeポイントは、Edgeに垂直な方向を向く法線ベクトルを持ちます。法線ベクトルの角度を0からPIの間の6つのバケットの1つに量子化しました(エッジは180度対称であるため、-PIと0の間の角度を0とPIの間に変換しました)。各方向のエッジ点の数を集計した後、テクスチャ方向を表す正規化されていないヒストグラムが得られます。これは、各バケットを画像内のエッジ点の総数で割ることによって正規化します。

テクスチャスケールヒストグラムを計算するために、各エッジポイントについて、同じ方向で次に最も近いエッジポイントまでの距離を測定しました。たとえば、エッジ点Aの方向が45度の場合、アルゴリズムは45度の方向を持つ別のエッジ点が見つかるまで(または妥当な偏差内で)その方向に歩きます。エッジポイントごとにこの距離を計算した後、それらの値をヒストグラムにダンプし、エッジポイントの総数で割ることによって正規化します。

これで、各画像について5つのヒストグラムができました。 2つの画像を比較するには、各ヒストグラムバケット間の差の絶対値を求めてからこれらの値を合計します。たとえば、画像AとBを比較するには、次のように計算します。

|A.green_histogram.bucket_1 - B.green_histogram.bucket_1| 

緑色のヒストグラムの各バケットについて、他のヒストグラムについても繰り返してから、すべての結果を合計します。結果が小さいほど、一致度は高くなります。データベース内のすべての画像に対してこの手順を繰り返します。最小の結果との一致が優先されます。おそらくしきい値を設定したいと思うでしょう。それを超えると、アルゴリズムは一致が見つからなかったと判断します。


3番目の選択 - キーポイント+決定木

おそらく他の2つよりもはるかに速い3番目のアプローチは、 セマンティックテキストフォレストを使用することです (PDF)。これには、単純なキーポイントの抽出と、画像を分類するためのコレクション決定木の使用が含まれます。これは、単純なSIFTキーポイントマッチングよりも高速です。コストのかかるマッチングプロセスが回避され、キーポイントがSIFTよりもはるかに単純であるため、キーポイント抽出がはるかに高速になるためです。ただし、SIFTメソッドの回転、スケール、および照明に対する不変性は維持されます。これは、ヒストグラムメソッドにはなかった重要な機能です。

更新

私の間違い - Semantic Texton Forestsの論文は、特に画像のマッチングについてではなく、むしろ地域のラベル付けについてです。マッチングを行う元の論文はこれです: ランダム化木を使ったキーポイント認識 。また、以下の論文はアイデアを発展させ、最新技術を代表しています(c。2010)。

436
Kyle Simek

私が知っている最善の方法は、知覚ハッシュを使うことです。このようなハッシュの良いオープンソース実装は次の場所にあります。

http://phash.org/

主な考え方は、(画像データを直接ハッシュするのではなく)元の画像ファイルの顕著な特徴を識別し、それらの特徴のコンパクトな表現をハッシュすることによって、各画像を小さなハッシュコードまたは「指紋」に縮小することです。これは、画像を小さな拇印サイズの画像に縮小したり、拇印を比較したりするなどの単純化したアプローチよりも、誤検知率が大幅に低下することを意味します。

phashは数種類のハッシュを提供しており、画像、オーディオ、ビデオに使用できます。

78
redcalx

この記事は私の解決策の出発点であり、ここにはたくさんの良いアイデアがありました。主な洞察は、私がphashの速度を悪用することによって、キーポイントベースの画像マッチングの遅さを回避する方法を見つけたということです

一般的な解決策では、いくつかの戦略を採用するのが最善です。各アルゴリズムは特定の種類の画像変換に最適であり、それを利用できます。

一番上に、最も速いアルゴリズムがあります。一番下では最も遅いですが(より正確です)。より速いレベルで良い一致が見つかれば、遅いものをスキップすることができます。

  • 正確な複製のためのファイルハッシュベース(md5、sha1など)
  • 再スケーリングされた画像のための知覚ハッシング(phash)
  • 修正画像のための特徴ベース(SIFT)

私はphashでとても良い結果を得ています。精度は拡大縮小された画像に適しています。 (知覚的に)修正された画像(トリミング、回転、ミラーなど)には適していません。ハッシュ速度を処理するために、干し草の山のハッシュを維持するためにディスクキャッシュ/データベースを使用する必要があります。

Phashについての本当に素晴らしいことは、一度あなたがハッシュデータベースを構築すれば(それは私にとっては約1000画像/秒です)、特にハッシュデータベース全体をメモリに保持できる場合、検索は非常に速くなることです。ハッシュは8バイトしかないので、これはかなり実用的です。

たとえば、100万枚の画像がある場合、100万個の64ビットハッシュ値(8 MB)の配列が必要になります。一部のCPUでは、これはL2/L3キャッシュに収まります。実用的な使用法で私は1ギガハム/秒以上でcorei7比較を見ました、それはCPUへのメモリ帯域幅の問題だけです。 10億イメージのデータベースは64ビットCPU(8GB RAMが必要)で実用的であり、検索は1秒を超えることはありません!

修正/トリミングされた画像の場合は、SIFTのような変換に依存しない機能/キーポイント検出器が適しています。 SIFTは、クロップ/回転/ミラーなどを検出するための優れたキーポイントを生成します。しかし、ディスクリプタの比較は、phashで使用されるハミング距離に比べて非常に遅くなります。これは大きな制限です。 1つの画像を検索するのに比較できる最大のIxJxK記述子があるので、やるべき比較はたくさんあります(I = num個の干し草画像、J =干し草画像あたりの目標キーポイント、K =針画像あたりの目標キーポイント)。

速度の問題を回避するために、サブサイズを決定するために特徴サイズ/半径を使用して、見つかった各キーポイントの周りにphashを使用してみました。これをうまく機能させるためのトリックは、半径を拡大/縮小して(針の画像上で)さまざまなサブストレートレベルを生成することです。通常、最初のレベル(スケールなし)は一致しますが、多くの場合それ以上かかります。これがなぜうまくいくのか100%確信できませんが、phashが機能するには小さすぎる機能を有効にすることが可能であると想像できます(phashは画像を32x32に縮小します)。

別の問題は、SIFTがキーポイントを最適に配布しないことです。画像の一部にエッジが多い部分がある場合、キーポイントはそこに密集し、他の領域には入りません。配布を改善するためにOpenCVでGridAdaptedFeatureDetectorを使用しています。どのグリッドサイズが最適かわからない、私は小さなグリッド(画像の向きに応じて1×3または3×1)を使用しています。

おそらく、すべての干し草の山の画像(および針)を、フィーチャーを検出する前に小さいサイズに拡大したい(私は最大寸法に沿って210ピクセルを使用します)。これは画像内のノイズを減少させ(常にコンピュータビジョンアルゴリズムにとって問題となる)、検出器をより顕著な特徴に焦点を合わせるだろう。

人物の画像の場合、顔検出を試して、拡大縮小する画像サイズとグリッドサイズを決定するためにそれを使用することができます(たとえば、最大顔は100pxに拡大縮小されます)。特徴検出器は(ピラミッドを使用して)複数の縮尺レベルを考慮しますが、使用するレベル数には制限があります(もちろんこれは調整可能です)。

Keypoint Detectorは、必要な機能の数より少ない数が返されたときにおそらく最もうまく機能します。たとえば、400を要求して300に戻っても、それは問題ありません。毎回400まで戻ってきた場合は、おそらくいくつかの優れた機能を省く必要がありました。

針の画像は干し草の山の画像よりもキーポイントが少なくても、まだ良い結果が得られます。たとえばJ = 400やK = 40の場合、ヒット率は約92%です。 J = 400、K = 400の場合、ヒット率は96%に達するだけです。

スケーリング、回転、ミラーリングなどを解決するためにハミング機能の極端な速度を利用することができます。マルチパス手法を使用することができます。各反復で、サブ長方形を変換し、再ハッシュして、検索機能を再度実行します。

33
wally

私はアイデアを持っています、そしてそれはうまくいくことができて、そしてそれは非常に速くなる可能性が最も高いです。あなたは80x60の解像度またはそれに匹敵すると言うために画像をサブサンプリングすることができて、そしてそれをグレイスケールに変換することができます(サブサンプリングの後はより速くなるでしょう)。比較したい両方の画像を処理します。次に、2つの画像(クエリ画像と各データベースからの各画像)の間の差の二乗の正規化された合計、またはさらに良い正規化相互相関を実行します。それから画像が似ているなら、あなたはそれが同じ画像であることを確かめるためにもっと洗練されたテクニックに進むことができます。最新のハードウェアでは毎秒最大10000画像と非常に高速になるでしょうが、明らかにこのアルゴリズムはあなたのデータベースの中の画像の数に関して線形です。回転に対する不変性が必要な場合は、この小さな画像に対して支配的なグラデーションを計算してから、座標系全体を正準方向に回転させることができますが、これは遅くなります。いいえ、ここで拡大縮小するための不変性はありません。

もっと一般的なもの、または大きなデータベース(何百万もの画像)を使用したい場合は、画像検索理論を検討する必要があります(大量の論文が過去5年間に登場しました)。他の答えにいくつかのポインタがあります。しかし、やり過ぎるかもしれませんし、提案されたヒストグラムアプローチが役にたちます。けれども私は多くの異なった速いアプローチの組み合わせがいっそう良くなると思うだろうが。

6
Denis C

Cartmanが指摘したように、正確な重複を見つけるためにどんな種類のハッシュ値でも使用できます。

近い画像を見つけるための1つの出発点は、ここで です 。これは、修正された画像がまだ本質的に同じシーンを表示しているかどうかを確認するためにCG会社が使用するツールです。

6
Tobiesque

私は、画像のサイズをほぼアイコンサイズ、たとえば48 x 48にドロップしてからグレースケールに変換してから、ピクセル間の差、つまりデルタを取ることでうまくいくはずだと思います。実際のピクセルの色ではなく、ピクセルの色の変化を比較しているので、画像がわずかに明るいか暗いかは関係ありません。明るすぎる/暗すぎるピクセルは失われるため、大きな変更は重要になります。これを1行にまたがって適用することも、精度を高めるために必要なだけ適用することもできます。匹敵するキーを形成するために、最大で47×47 = 2,209の減算を行うことになります。

5
Tanoshimi

100個のランダムな点を選ぶことは、似たような(あるいは時々違った)画像が同じものとしてマークされることを意味するかもしれません。画像の形式(png、jpegなど)が異なる場合、サイズが異なる場合、またはメタデータが異なる場合、MD5ハッシュは機能しません。すべての画像を小さいサイズに縮小するのが賢明です。ピクセルごとの比較は、優れた画像ライブラリ/高速言語を使用している限りは時間がかかりすぎず、サイズは十分に小さいです。

あなたはそれらを小さくすることを試みることができます、そして、それらが同じであるならば、より大きいサイズでもう一つの比較を実行してください - 速度と正確さの良い組み合わせであるかもしれません...

3
HarryM

大量の画像がある場合は、確率的だが効率的な結果を得るために複数のハッシュを使用する ブルームフィルタ を調べてください。画像の数がそれほど多くない場合は、md5のような暗号化ハッシュで十分です。

2
jdigital

私の会社は毎月約2400万の画像が製造業者から入ってきています。カタログにアップロードする画像が新しい画像であることを確認するための迅速な解決策を探していました。

私は理想的な解決策を見つけることを試みるために私はインターネットを遠くから広く検索したと言いたい。私も自分のエッジ検出アルゴリズムを開発しました。
複数のモデルの速度と精度を評価しました。背景が白の私の画像は、つぶれには非常に適しています。 redcalxが言ったように、私はphashかahashをお勧めします。 MD5ハッシュやその他の暗号化ハッシュを使用しないでください。そうでない限り、あなたは完全なイメージマッチだけが欲しいです。画像間でサイズ変更や操作を行うと、異なるハッシュが生成されます。

Phash/ahashに関しては、これをチェックしてください: imagehash

私は自分のコードと正確さを投稿することによって* redcalxの*投稿を拡張したいと思いました。

私がやること:

from PIL import Image
from PIL import ImageFilter
import imagehash

img1=Image.open(r"C:\yourlocation")
img2=Image.open(r"C:\yourlocation")
if img1.width<img2.width:
    img2=img2.resize((img1.width,img1.height))
else:
    img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
totalaccuracy=phashvalue+ahashvalue

これが私の結果の一部です:

item1  item2  totalaccuracy
desk1  desk2       3
desk2  phone1     22
chair1 desk1      17
phone1 chair1     34

項目は画像の実際の被写体を表し、数字は方向のスケールを表します。

お役に立てれば!

0
Tanner Clark