web-dev-qa-db-ja.com

画像比較アルゴリズム

画像を互いに比較して、それらが異なるかどうかを確認しようとしています。最初に、RGB値のピアソン相関を作成しようとしました。これは、画像が少しずれている場合を除き、非常に良好に機能します。ですから、画像が100%同一であるが少し動かした場合、相関値が悪くなります。

より良いアルゴリズムのための提案はありますか?

ところで、私は数千の画像を比較しようとしています...

編集:これは私の写真の例です(顕微鏡):

im1:

enter image description here

im2:

enter image description here

im3:

enter image description here

im1とim2は同じですが、少しシフト/カットされています。im3は完全に異なるものとして認識されるべきです...

編集:ピーター・ハンセンの提案で問題は解決しました!とてもうまくいきます!すべての回答に感謝します!いくつかの結果はここにあります http://labtools.ipk-gatersleben.de/image%20comparison/image%20comparision.pdf

42
snowflake

類似の質問 は1年前に尋ねられ、画像のピクセル化に関するものを含む多数の回答がありますが、これは少なくとも事前資格審査ステップとして提案するつもりでした非常に迅速に画像)。

また、より多くの参照と良い答えがあるまだ初期の質問へのリンクがあります。

上記の3つの画像(それぞれim1.jpg、im2.jpg、im3.jpgとして保存)を使用して、Scipyでいくつかのアイデアを使用した実装を次に示します。最終出力では、im1がそれ自体と比較されてベースラインとして表示され、次に各画像が他の画像と比較されます。

>>> import scipy as sp
>>> from scipy.misc import imread
>>> from scipy.signal.signaltools import correlate2d as c2d
>>>
>>> def get(i):
...     # get JPG image as Scipy array, RGB (3 layer)
...     data = imread('im%s.jpg' % i)
...     # convert to grey-scale using W3C luminance calc
...     data = sp.inner(data, [299, 587, 114]) / 1000.0
...     # normalize per http://en.wikipedia.org/wiki/Cross-correlation
...     return (data - data.mean()) / data.std()
...
>>> im1 = get(1)
>>> im2 = get(2)
>>> im3 = get(3)
>>> im1.shape
(105, 401)
>>> im2.shape
(109, 373)
>>> im3.shape
(121, 457)
>>> c11 = c2d(im1, im1, mode='same')  # baseline
>>> c12 = c2d(im1, im2, mode='same')
>>> c13 = c2d(im1, im3, mode='same')
>>> c23 = c2d(im2, im3, mode='same')
>>> c11.max(), c12.max(), c13.max(), c23.max()
(42105.00000000259, 39898.103896795357, 16482.883608327804, 15873.465425120798)

したがって、im1はそれ自体と比較して42105のスコアを与え、im2はim1と比較してそれほど大きな違いはありませんが、im3は他のいずれとも比較してその半分以下の値を示します。他の画像を試して、これがどれだけうまく機能し、どのように改善できるかを確認する必要があります。

実行時間が長い...マシン上で数分。他の質問への回答で言及された「jpgファイルサイズの比較」やピクセル化など、非常に異なる画像を比較する時間を無駄にしないために、事前フィルタリングを試してみます。サイズの異なる画像があるという事実は事態を複雑にしますが、予想される屠殺の程度について十分な情報を提供しなかったので、それを考慮した具体的な答えを出すのは困難です。

37
Peter Hansen

画像ヒストグラム比較でこれを実行しました。私の基本的なアルゴリズムはこれでした:

  1. 画像を赤、緑、青に分割
  2. 赤、緑、青のチャネルの正規化されたヒストグラムを作成し、それらをベクトル(r0...rn, g0...gn, b0...bn)に連結します。nは「バケット」の数です。256で十分です。
  3. 別の画像のヒストグラムからこのヒストグラムを引き、距離を計算します

numpypilを使用したコードを次に示します

r = numpy.asarray(im.convert( "RGB", (1,0,0,0, 1,0,0,0, 1,0,0,0) ))
g = numpy.asarray(im.convert( "RGB", (0,1,0,0, 0,1,0,0, 0,1,0,0) ))
b = numpy.asarray(im.convert( "RGB", (0,0,1,0, 0,0,1,0, 0,0,1,0) ))
hr, h_bins = numpy.histogram(r, bins=256, new=True, normed=True)
hg, h_bins = numpy.histogram(g, bins=256, new=True, normed=True)
hb, h_bins = numpy.histogram(b, bins=256, new=True, normed=True)
hist = numpy.array([hr, hg, hb]).ravel()

2つのヒストグラムがある場合、次のような距離を取得できます。

diff = hist1 - hist2
distance = numpy.sqrt(numpy.dot(diff, diff))

2つの画像が同一の場合、距離は0になり、発散するほど距離が大きくなります。

私の写真では非常にうまく機能しましたが、テキストやロゴのようなグラフィックでは失敗しました。

13

問題がピクセルのシフトに関するものである場合は、周波数変換と比較する必要があります。

FFTは問題ないはずです( numpyには2Dマトリックスの実装があります )が、この種のタスクにはWaveletの方が優れているといつも聞いています^ _ ^

パフォーマンスについては、すべての画像が同じサイズである場合、よく覚えていれば、FFTWパッケージは各FFT入力サイズに特化した関数を作成したので、同じコードを再利用して素晴らしいパフォーマンスを得ることができます... numpyがFFTWに基づいているかどうかはわかりませんが、そうでない場合は、少し調べてみてください。

ここにプロトタイプがあります...少し試して、どのしきい値が画像に適合するかを確認してください。

import Image
import numpy
import sys

def main():
    img1 = Image.open(sys.argv[1])
    img2 = Image.open(sys.argv[2])

    if img1.size != img2.size or img1.getbands() != img2.getbands():
        return -1

    s = 0
    for band_index, band in enumerate(img1.getbands()):
        m1 = numpy.fft.fft2(numpy.array([p[band_index] for p in img1.getdata()]).reshape(*img1.size))
        m2 = numpy.fft.fft2(numpy.array([p[band_index] for p in img2.getdata()]).reshape(*img2.size))
        s += numpy.sum(numpy.abs(m1-m2))
    print s

if __== "__main__":
    sys.exit(main())

続行する別の方法は、画像をぼかしてから、2つの画像からピクセル値を減算することです。差が非nilの場合、画像の1つを各方向に1 pxシフトし、再度比較できます。差が前のステップよりも小さい場合は、グラデーションの方向にシフトし、差が減算されるまで繰り返します。特定のしきい値より低いか、再び増加します。ぼかしカーネルの半径が画像のシフトよりも大きい場合、それは動作するはずです。

また、 Pano Tools のように、複数の博覧会をブレンドしたり、パノラマを撮影したりするための写真ワークフローで一般的に使用されるツールのいくつかを試すことができます。

6
fortran

質問をより適切に指定する必要がありますが、これら5つの画像を見ると、生物はすべて同じ方向を向いているようです。これが常に当てはまる場合は、2つの画像間で 正規化相互相関 を実行し、類似度としてピーク値を取得してみてください。 Pythonで正規化された相互相関関数を知りませんが、同様の fftconvolve() 関数があり、自分で循環相互相関を行うことができます。

a = asarray(Image.open('c603225337.jpg').convert('L'))
b = asarray(Image.open('9b78f22f42.jpg').convert('L'))
f1 = rfftn(a)
f2 = rfftn(b)
g =  f1 * f2
c = irfftn(g)

これは、画像のサイズが異なるため、書かれたとおりには機能せず、出力はまったく重み付けも正規化もされません。

出力のピーク値の位置は2つの画像間のオフセットを示し、ピークの大きさは類似性を示します。良いマッチと悪いマッチの違いを区別できるように、それを重み付け/正規化する方法があるはずです。

まだ正規化する方法を理解していないので、これは私が望むほど良い答えではありませんが、私がそれを理解したら更新します、そしてそれはあなたに調べるアイデアを与えます。

6
endolith

私はかなり前に画像処理コースをいくつか行ってきましたが、一致するときは通常、画像をグレースケールにしてから、エッジのみが見えるように画像のエッジをシャープにすることから始めたことを思い出してください。その後、ソフトウェア(ソフトウェア)は、差が最小になるまで画像をシフトおよび減算できます。

その差が設定したしきい値よりも大きい場合、画像は等しくないため、次の画像に進むことができます。次に、より小さいしきい値を持つ画像を分析できます。

可能な限り一致するものを根本的に間引くことができるとは思いますが、実際に一致するものを判断するには、一致する可能性のあるものを個人的に比較する必要があります。

昔のコードを実際に表示することはできません。そのコースではKhoros/Cantataを使用しました。

2
extraneon

まず、相関は、CPUを非常に集中的に使用しており、類似性の不正確な尺度です。個々のピクセル間の差がある場合は、単に平方の合計に行かないのですか?

最大シフトが制限されている場合の単純な解決策:シフトされる可能性のあるすべての画像を生成し、最適な画像を見つけます。シフトされたすべての画像で一致する可能性のあるピクセルのサブセットに対してのみ、一致変数(つまり相関)を計算してください。また、最大シフトは画像のサイズよりも大幅に小さくする必要があります。

さらに高度な画像処理技術を使用する場合は、 [〜#〜] sift [〜#〜] を参照することをお勧めします。これは非常に強力な方法であり、理論的には平行移動、回転、スケールに依存しない画像。

1
jilles de wit

Ubuntu 16.04(2017年4月現在)でインポートを正しく動作させるために、python 2.7およびこれらをインストールしました:

Sudo apt-get install python-dev
Sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
Sudo apt-get install python-scipy
Sudo pip install pillow

次に、Snowflakeのインポートを次のように変更しました。

import scipy as sp
from scipy.ndimage import imread
from scipy.signal.signaltools import correlate2d as c2d

8年後、Snowflakeのスクリプトは私にとって素晴らしいものでした!

0
Doug Null

画像ヒストグラムの類似性のJaccardインデックスに基づくソリューションを提案します。参照: https://en.wikipedia.org/wiki/Jaccard_index#Weighted_Jaccard_similarity_and_distance

ピクセル色の分布の違いを計算できます。これは確かに翻訳に対してかなり不変です。

from PIL.Image import Image
from typing import List

def jaccard_similarity(im1: Image, im2: Image) -> float:
    """Compute the similarity between two images.
    First, for each image an histogram of the pixels distribution is extracted.
    Then, the similarity between the histograms is compared using the weighted Jaccard index of similarity, defined as:
    Jsimilarity = sum(min(b1_i, b2_i)) / sum(max(b1_i, b2_i)
    where b1_i, and b2_i are the ith histogram bin of images 1 and 2, respectively.

    The two images must have same resolution and number of channels (depth).

    See: https://en.wikipedia.org/wiki/Jaccard_index
    Where it is also called Ruzicka similarity."""

    if im1.size != im2.size:
        raise Exception("Images must have the same size. Found {} and {}".format(im1.size, im2.size))

    n_channels_1 = len(im1.getbands())
    n_channels_2 = len(im2.getbands())
    if n_channels_1 != n_channels_2:
        raise Exception("Images must have the same number of channels. Found {} and {}".format(n_channels_1, n_channels_2))

    assert n_channels_1 == n_channels_2

    sum_mins = 0
    sum_maxs = 0

    hi1 = im1.histogram()  # type: List[int]
    hi2 = im2.histogram()  # type: List[int]

    # Since the two images have the same amount of channels, they must have the same amount of bins in the histogram.
    assert len(hi1) == len(hi2)

    for b1, b2 in Zip(hi1, hi2):
        min_b = min(b1, b2)
        sum_mins += min_b
        max_b = max(b1, b2)
        sum_maxs += max_b

    jaccard_index = sum_mins / sum_maxs

    return jaccard_index

平均二乗誤差に関して、Jaccardインデックスは常に[0,1]の範囲にあるため、異なる画像サイズ間の比較が可能です。

次に、2つの画像を比較できますが、同じサイズに再スケーリングした後です!または、ピクセル数を何らかの方法で正規化する必要があります。私はこれを使用しました:

import sys

from skincare.common.utils import jaccard_similarity

import PIL.Image
from PIL.Image import Image

file1 = sys.argv[1]
file2 = sys.argv[2]

im1 = PIL.Image.open(file1)  # type: Image
im2 = PIL.Image.open(file2)  # type: Image

print("Image 1: mode={}, size={}".format(im1.mode, im1.size))
print("Image 2: mode={}, size={}".format(im2.mode, im2.size))

if im1.size != im2.size:
    print("Resizing image 2 to {}".format(im1.size))
    im2 = im2.resize(im1.size, resample=PIL.Image.BILINEAR)

j = jaccard_similarity(im1, im2)
print("Jaccard similarity index = {}".format(j))

画像のテスト:

$ python CompareTwoImages.py im1.jpg im2.jpg
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(373, 109)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.7238955686269157
$ python CompareTwoImages.py im1.jpg im3.jpg 
Image 1: mode=RGB, size=(401, 105)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (401, 105)
Jaccard similarity index = 0.22785529941822316
$ python CompareTwoImages.py im2.jpg im3.jpg 
Image 1: mode=RGB, size=(373, 109)
Image 2: mode=RGB, size=(457, 121)
Resizing image 2 to (373, 109)
Jaccard similarity index = 0.29066426814105445

もちろん、サイズ変更時に色の分布を変更するため、さまざまなリサンプリングフィルター(NEARESTやLANCZOSなど)の実験を検討することもできます。

さらに、2番目の画像はアップサンプリングではなくダウンサンプリングされる可能性があるため、画像を交換すると結果が変わることを考慮してください(結局、再スケーリングよりもトリミングがお客様のケースに適している場合があります)。

0
fnunnari

次のようなことができると思います。

  • 参照画像と比較画像の垂直/水平変位を推定します。動きベクトルを使用した単純なSAD(絶対差の合計)でできます。

  • それに応じて比較画像をシフトします

  • あなたがやろうとしていたピアソン相関を計算する

シフト測定は難しくありません。

  • 比較画像で領域(たとえば32x32程度)を取得します。
  • 水平方向にxピクセル、垂直方向にyピクセルシフトします。
  • SAD(絶対差の合計)w.r.tを計算します元画像
  • 小さい範囲(-10、+ 10)でxとyのいくつかの値に対してこれを行います
  • 差が最小になる場所を見つける
  • その値をシフトモーションベクトルとして選択します

注意:

Xとyのすべての値でSADが非常に高くなっている場合は、とにかく画像が非常に異なっており、シフト測定が不要であると想定できます。

0
Shailesh Kumar