5

このような比較をすばやく簡単に行う方法はありますか?

stackoverflow からの画像比較の質問はほとんど見つかりませんでしたが、実際にこの質問に対する答えを証明したものはありませんでした。

ファイルシステムに画像ファイルがあり、URL から画像を取得するスクリプトがあります。URL の画像がディスク上の画像と同じかどうかを確認したい。通常、ディスク内の画像と URL を PIL オブジェクトにロードし、見つけた次の関数を使用します。

def equal(im1, im2):
    return ImageChops.difference(im1, im2).getbbox() is None

ただし、品質を 100 にしても画像が圧縮されるため、PIL を使用してディスクに保存された画像がある場合、これは機能しませんim1.save(outfile,quality=100)

私のコードは現在 http://pastebin.com/295kDMspに従っています が、画像は常に再保存されます。

4

3 に答える 3

15

質問のタイトルは、比較する 2 つの正確な画像があることを示唆しており、それは簡単に行われます。ここで、比較する類似の画像がある場合、完全に満足のいく答えが見つからなかった理由が説明されます。期待される結果をもたらすすべての問題に適用できるメトリックはありません (期待される結果はアプリケーションによって異なることに注意してください)。問題の 1 つは、カラー画像のように、複数のバンドを持つ画像を比較することが (共通の合意がないという意味で) 難しいことです。それを処理するために、各バンドで特定のメトリックの適用を検討し、そのメトリックの結果が最低の結果値になります。これは、メトリックが [0, 1] のように十分に確立された範囲を持っていることを前提としています。この範囲の最大値は、イメージが (指定されたメトリックによって) 同一であることを意味します。逆に、

したがって、ここで行うことは、2 つの指標を提供することだけです。それらの 1 つはSSIMで、もう 1 つは NRMSE (平均二乗誤差の根の正規化) と呼びます。それは非常に単純な方法であり、あなたの問題には十分かもしれないので、私は2番目のものを提示することにしました.

例から始めましょう。画像の順序は次のとおりです。f = PNG の元の画像、g1 = 50% の品質の JPEG f(で作成convert f -quality 50 g)、g2 = JPEG の 1% の品質f、h = 「明るくした」g2。

ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力 ここに画像の説明を入力

結果 (四捨五入):

  • NRMSE(f, g1) = 0.96
  • NRMSE(f, g2) = 0.88
  • NRMSE(f, h) = 0.63
  • SSIM(f, g1) = 0.98
  • SSIM(f, g2) = 0.81
  • SSIM(f, h) = 0.55

ある意味では、両方のメトリクスは変更を適切に処理しましたがSSIM、画像が実際に視覚的に異なる場合は低い類似性を報告し、画像が視覚的に非常に類似している場合は高い値を報告することで、より賢明であることが示されました. 次の例では、カラー画像 (f = 元の画像、g = 5% 品質の JPEG) を考えます。

ここに画像の説明を入力 ここに画像の説明を入力

  • NRMSE(f, g) = 0.92
  • SSIM(f, g) = 0.61

そのため、優先するメトリックとそのしきい値を決定するのはユーザー次第です。

さて、指標です。私が NRMSE と呼んでいるのは、単純に 1 - [RMSE / ( maxval- minval)] です。どこmaxvalで比較されている2つの画像からの最大強度であり、それぞれについて同じですminval。RMSE は MSE の平方根で与えられます: sqrt[(sum(A - B) ** 2) / |A|]、ここで |A| は A の要素数を意味します。これにより、RMSE によって与えられる最大値はmaxvalです。画像内の MSE の意味をさらに理解したい場合は、たとえばhttps://ece.uwaterloo.ca/~z70wang/publications/SPM09.pdfを参照してください。メトリック SSIM (構造的類似性) はより複雑であり、以前に含まれていたリンクで詳細を確認できます。メトリクスを簡単に適用するには、次のコードを検討してください。

import numpy
from scipy.signal import fftconvolve

def ssim(im1, im2, window, k=(0.01, 0.03), l=255):
    """See https://ece.uwaterloo.ca/~z70wang/research/ssim/"""
    # Check if the window is smaller than the images.
    for a, b in zip(window.shape, im1.shape):
        if a > b:
            return None, None
    # Values in k must be positive according to the base implementation.
    for ki in k:
        if ki < 0:
            return None, None

    c1 = (k[0] * l) ** 2
    c2 = (k[1] * l) ** 2
    window = window/numpy.sum(window)

    mu1 = fftconvolve(im1, window, mode='valid')
    mu2 = fftconvolve(im2, window, mode='valid')
    mu1_sq = mu1 * mu1
    mu2_sq = mu2 * mu2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = fftconvolve(im1 * im1, window, mode='valid') - mu1_sq
    sigma2_sq = fftconvolve(im2 * im2, window, mode='valid') - mu2_sq
    sigma12 = fftconvolve(im1 * im2, window, mode='valid') - mu1_mu2

    if c1 > 0 and c2 > 0:
        num = (2 * mu1_mu2 + c1) * (2 * sigma12 + c2)
        den = (mu1_sq + mu2_sq + c1) * (sigma1_sq + sigma2_sq + c2)
        ssim_map = num / den
    else:
        num1 = 2 * mu1_mu2 + c1
        num2 = 2 * sigma12 + c2
        den1 = mu1_sq + mu2_sq + c1
        den2 = sigma1_sq + sigma2_sq + c2
        ssim_map = numpy.ones(numpy.shape(mu1))
        index = (den1 * den2) > 0
        ssim_map[index] = (num1[index] * num2[index]) / (den1[index] * den2[index])
        index = (den1 != 0) & (den2 == 0)
        ssim_map[index] = num1[index] / den1[index]

    mssim = ssim_map.mean()
    return mssim, ssim_map


def nrmse(im1, im2):
    a, b = im1.shape
    rmse = numpy.sqrt(numpy.sum((im2 - im1) ** 2) / float(a * b))
    max_val = max(numpy.max(im1), numpy.max(im2))
    min_val = min(numpy.min(im1), numpy.min(im2))
    return 1 - (rmse / (max_val - min_val))


if __name__ == "__main__":
    import sys
    from scipy.signal import gaussian
    from PIL import Image

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

    if img1.size != img2.size:
        print "Error: images size differ"
        raise SystemExit

    # Create a 2d gaussian for the window parameter
    win = numpy.array([gaussian(11, 1.5)])
    win2d = win * (win.T)

    num_metrics = 2
    sim_index = [2 for _ in xrange(num_metrics)]
    for band1, band2 in zip(img1.split(), img2.split()):
        b1 = numpy.asarray(band1, dtype=numpy.double)
        b2 = numpy.asarray(band2, dtype=numpy.double)
        # SSIM
        res, smap = ssim(b1, b2, win2d)

        m = [res, nrmse(b1, b2)]
        for i in xrange(num_metrics):
            sim_index[i] = min(m[i], sim_index[i])

    print "Result:", sim_index

指定された値がそれらよりも大きいssim場合、画像の比較を拒否することに注意してください。windowwindow通常非常に小さく、デフォルトは 11x11 であるため、画像がそれよりも小さい場合、(メトリックの名前から) 比較する「構造」があまりないため、他のもの (他の関数などnrmse) を使用する必要があります。ssimおそらく、Matlab ではこれがはるかに高速に実行されるため、を実装するためのより良い方法があります。

于 2012-12-14T19:18:19.103 に答える
1

平方差を使用して、独自の比較を行うことができます。次に、95% などのしきい値を設定します。それらが類似している場合は、ダウンロードする必要はありません。圧縮の問題を解消します

于 2012-12-14T09:44:32.753 に答える
0

Bartlomiej Lewandowskiの提案に沿って、ヒストグラム エントロピーを比較することをお勧めします。これは、簡単で比較的迅速に計算できます。

def histogram_entropy(im):
    """ Calculate the entropy of an images' histogram.
    Used for "smart cropping" in easy-thumbnails;
    see also https://raw.github.com/SmileyChris/easy-thumbnails/master/easy_thumbnails/utils.py
    """
    if not isinstance(im, Image.Image):
        return 0  # Fall back to a constant entropy.

    histogram = im.histogram()
    hist_ceil = float(sum(histogram))
    histonorm = [histocol / hist_ceil for histocol in histogram]

... この関数は、私が作成したauto-square-crop フィルターで使用するものですが、エントロピー値を使用して、2 つの画像 (サイズが異なっていても) を比較できます。

この種のアイデアの適用例は他にもあります。特定の例をあなたのやり方で送ってほしい場合は、コメントでお知らせください。

于 2012-12-14T09:55:49.863 に答える