2つの画像のトリミング/比率が少し異なっていても、2つの画像が「同じ」であることをどのように検出しますか


11

2つの異なる画像があります。

100px ここに画像の説明を入力してくださいまたは400pxここに画像の説明を入力してください

そして

幅100px ここに画像の説明を入力してくださいまたは400pxここに画像の説明を入力してください

あなたが見ることができるように、2つは人間の観点から明らかに「同じ」です。次に、それらが同じであることをプログラムで検出します。私は次のrmagickように呼ばれるruby gemを介して画像マジックを使用しています:

img1 = Magick::Image.from_blob(File.read("image_1.jpeg")).first
img2 = Magick::Image.from_blob(File.read("image_2.jpeg")).first

if img1.difference(img2).first < 4000.0 # I have found this to be a good threshold, but does not work for cropped images
  puts "they are the same!!!"
end

これは、同じ比率/トリミングを持つ画像に適していますが、トリミングがわずかに異なり、同じ幅にサイズ変更されている場合は理想的ではありません。

トリミングが異なる画像にそれを行う方法はありますか?私は次のようなことを言える解決策に興味があります。1つの画像が他の画像の内部に含まれ、どこかをカバーしています(例:画像の90%)。

PS。それが役立つ場合は、画像をより高い解像度で取得できます(例:ダブル)


2
RMagickについては不明ですが、ImageMagickのcompareコマンドラインツールには-subimage-searchスイッチがあります。
ステファン

それは興味深いです、そのようなコマンドはどのように見えますか?
Niels Kristian

2
自分で使ったことはありません。これが役立つかもしれません:stackoverflow.com/q/29062811/477037
Stefan

ありがとう、それは素晴らしい情報です。しかし、ルビーからこれを行う方法を理解することはできません...
Niels Kristian

1
画像は低品質ですか?いいえの場合は、より品質の高い、より大きなバージョンの画像を共有してください。
MH304

回答:


6

機能のマッチングを確認することをお勧めします。アイデアは、2つの画像で特徴を見つけ、それらを一致させることです。この方法は、通常、別の画像でテンプレート(ロゴなど)を見つけるために使用されます。特徴は、本質的に、コーナーやオープンスペースなど、画像で人間が面白いと感じるものとして説明できます。そこには多くの種類の特徴検出手法がありますが、私の推奨は、特徴検出アルゴリズムとしてスケール不変特徴変換(SIFT)を使用することです。SIFTは、画像の変換、スケーリング、回転に対して不変であり、照明の変化に対して部分的に不変であり、局所的な幾何学的歪みに対してロバストです。これは、画像の比率がわずかに異なる可能性がある仕様に一致するようです。

提供された2つの画像を前提としてFLANN機能マッチャーを使用して機能を一致させる試みを次に示します。2つの画像が同じであるかどうかを判断するには David G. Loweによる「スケール不変キーポイントからの特徴的な画像機能」で説明されている比率テストに合格した一致数を追跡する所定のしきい値を基にします。テストの簡単な説明は、比率テストでは一致が曖昧であり、削除する必要があるかどうかをチェックするため、外れ値の削除手法として扱うことができます。このテストに合格した一致の数をカウントして、2つの画像が同じかどうかを判断できます。機能の一致結果は次のとおりです。

Matches: 42

ドットは検出されたすべての一致を表し、緑の線は比率テストに合格した「良好な一致」を表します。比率テストを使用しない場合、すべてのポイントが描画されます。このようにして、このフィルターをしきい値として使用し、最もよく一致する機能のみを保持できます。


Pythonで実装しましたが、Railsにはあまり詳しくありません。これがお役に立てば幸いです。

コード

import numpy as np
import cv2

# Load images
image1 = cv2.imread('1.jpg', 0)
image2 = cv2.imread('2.jpg', 0)

# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp1, des1 = sift.detectAndCompute(image2, None)
kp2, des2 = sift.detectAndCompute(image1, None)

# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

count = 0
# Ratio test as per Lowe's paper (0.7)
# Modify to change threshold 
for i,(m,n) in enumerate(matches):
    if m.distance < 0.15*n.distance:
        count += 1
        matchesMask[i]=[1,0]

# Draw lines
draw_params = dict(matchColor = (0,255,0),
                   # singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = 0)

# Display the matches
result = cv2.drawMatchesKnn(image2,kp1,image1,kp2,matches,None,**draw_params)
print('Matches:', count)
cv2.imshow('result', result)
cv2.waitKey()

2
超面白いアプローチ、私はそれをスピンして戻ってきます...
Niels Kristian

PS。画像を拡大して更新しました
Niels Kristian

1
@nathancy例では、緑のドットは一致するが、青のドットは一致しないようになっていますか?一致しないドットが多すぎるように見えますか?
Draco Ater

2
@DracoAter良い質問です。青いドットはすべての一致を表していますが、比率テストに合格した「良い一致」のみを緑色で描画しています。比率テストを使用しない場合、すべてのポイントが描画されますが、比率テストを使用してフィルタリングし、「より良い」一致を描画します。このようにして、OPはこのテストをしきい値として使用し、最も一致する機能のみを維持できます。したがって、すべての青い点はSIFTが見つけた機能ですが、緑色で描かれた良いものを維持するためにフィルタリングします
ナサンシー

ありがとう。競争は答えで困難でした、多くの素晴らしいもの:-)
Niels Kristian

4

ImageMagickは非常に古く、高度で多くの機能を備えたツールであるため、ほとんどの機能をカバーするインターフェースを構築することは困難です。それだけでなく、rmagickはすべての機能をカバーすることはできません(Pythonが試みた多くの試みも行いません)。

多くのユースケースでは、コマンドラインメソッドを実行してそこから読み取るだけで十分安全ではるかに簡単になると思います。ルビーでは次のようになります。

require 'open3'

def check_subimage(large, small)
    stdin, stdout, stderr, wait_thr = Open3.popen3("magick compare -subimage-search -metric RMSE #{large} #{small} temp.jpg")
    result = stderr.gets
    stderr.close
    stdout.close
    return result.split[1][1..-2].to_f < 0.2
end

if check_subimage('a.jpg', 'b.jpg')
    puts "b is a crop of a"
else
    puts "b is not a crop of a"
end

重要なことをカバーしてから、追加のメモについて話します。

このコマンドは、magick compareを使用して、2番目のイメージ(small)が最初のイメージ()のサブイメージであるかどうかを確認しlargeます。この関数は、smallがlargeよりも厳密に小さいこと(高さと幅の両方)をチェックしません。類似度に入力した数値は0.2(20%エラー)で、提供した画像の値は約0.15です。これを微調整したいかもしれません!厳密なサブセットである画像は0.01未満になることがわかりました。

  • 90%のオーバーラップがある場合にエラーを減らしたい(数値が小さい)が、2番目の画像に最初の画像にはない余分なものがある場合は、一度実行してから、最初の大きな画像をサブ画像が含まれる場所にトリミングできます。 、次に、切り抜かれた画像を「小さい」画像として、元の「小さい」画像を大きい画像としてもう一度実行します。
  • Rubyでオブジェクト指向の優れたインターフェースが本当に必要な場合、rmagickはMagicCore APIを使用します。この(ドキュメントへのリンク)コマンドはおそらくそれを実装するために使用するものであり、rmagickへのprを開くか、cextを自分でパッケージ化できます。
  • open3を使用すると、スレッドが開始されます(docsを参照)。閉じるstderrstdout、「必要」ではないですが、あなたは、ことになっています。
  • 3番目の引数である「一時」イメージは、分析を出力するファイルを指定します。ざっと見て、それを必要としない方法を見つけることができませんでしたが、自動的に上書きされるだけなので、デバッグ用に保存しておくとよいでしょう。たとえば、次のようになります。

ここに画像の説明を入力してください

  • 完全な出力は、10092.6(0.154003)@ 0,31の形式です。最初の数値は655535のrmse値で、2番目の数値(私が使用するもの)は正規化されたパーセンテージです。最後の2つの数値は、小さな画像が始まる元の画像の場所を表します。
  • 「類似」画像の客観的な真実の根拠がないため、私はRMSEを選択しました(ここでより多くのメトリックオプションを参照してください)。これは、値の違いのかなり一般的な尺度です。絶対エラーカウント(AE)は良い考えのように思えるかもしれませんが、一部のクロッピングソフトウェアはピクセルを完全に保持しないため、ファズを調整する必要があり、正規化された値ではない可能性があるため、エラーカウントを比較する必要があります画像のサイズと何もない。

1
それはキャロルでいくつかの本当に素晴らしい情報です。ありがとう
Niels Kristian

これが他のケースでどのように機能するか知りたい!
Carol Chen

1
超すばらしい答えをありがとう。できれば、これにも100pの報酬を与えました:-)
Niels Kristian

3

両方の画像のヒストグラムを取得して比較します。これらのために大幅な変更がない限り、これはトリミングとズームに非常にうまく機能します。

これは、画像を直接差し引く現在のアプローチよりも優れています。しかし、このアプローチにはまだいくつかあります。


アドバイスありがとうございます。
Niels Kristian

これは、目標を達成する方法を示していないため、あまり有用な答えではありません。これは、「この用語をGoogleにして自分で理解する」のと同等です。
anothermh

ヒストグラムは、人々が画像処理で最初に学ぶものの1つです。誰かがそれをググる必要があるなら、私は深くお詫び申し上げます。
Raviteja Narra

3

通常、テンプレートマッチングはこれらの状況で良い結果をもたらします。テンプレートマッチングは、テンプレート画像(2番目の画像)に一致(類似)する画像の領域を見つけるための手法です。このアルゴリズムは、ソース画像(2番目)の最適な位置のスコアを提供します。

TM_CCOEFF_NORMEDメソッドを使用するopencvで0〜1のスコアを与えます。スコアが1の場合、テンプレートイメージはソースイメージの一部(Rect)ですが、ライトニングまたはパースペクティブにわずかな変更がある場合2つの画像では、スコアは1よりも低くなります。

次に、類似性スコアのしきい値を検討することで、それらが同じかどうかを確認できます。このしきい値は、いくつかのサンプル画像で試行錯誤することによって取得できます。私はあなたの画像を試し、スコア0.823863を得ました 。以下は、コード(opencv C ++)と、マッチングによって取得された2つのイメージ間の共通領域です。

ここに画像の説明を入力してください

Mat im2 = imread("E:/1/1.jpg", 1);
//Mat im2;// = imread("E:/1/1.jpg", 1);
Mat im1 = imread("E:/1/2.jpg", 1);

//im1(Rect(0, 0, im1.cols - 5, im1.rows - 5)).copyTo(im2);

int result_cols = im1.cols - im2.cols + 1;
int result_rows = im1.rows - im2.rows + 1;

Mat result = Mat::zeros(result_rows, result_cols, CV_32FC1);

matchTemplate(im1, im2, result, TM_CCOEFF_NORMED);

double minVal; double maxVal;
Point minLoc; Point maxLoc;
Point matchLoc;

minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

cout << minVal << " " << maxVal << " " << minLoc << " " << maxLoc << "\n";
matchLoc = maxLoc;

rectangle(im1, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);
rectangle(result, matchLoc, Point(matchLoc.x + im2.cols, matchLoc.y + im2.rows), Scalar::all(0), 2, 8, 0);

imshow("1", im1);
imshow("2", result);
waitKey(0);

超すばらしい答えをありがとう。できれば、これにも100pの報酬を与えました:-)
Niels Kristian

2

find_similar_regionメソッドを検討してください。2つの画像のうち小さい方をターゲット画像として使用します。画像とターゲット画像のファズ属性にさまざまな値を試します。


おかげで、私はそれを機能させることができません-あなたはできますか?
Niels Kristian
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.