2011-10-06 21 views
15

PIL画像から2つのNumpy配列(3次元uint8)を変換しました。Numpy画像の中にサブ画像を見つける

最初のイメージに2番目のイメージが含まれているかどうかを確認し、見つかった場合は最初のイメージ内の左上のピクセルの座標を見つけます。

(4!非常に遅い)純粋なPythonループを使用するのではなく、純粋にNumpyで行う方法がありますか?

2D例:このような何かをする方法

a = numpy.array([ 
    [0, 1, 2, 3], 
    [4, 5, 6, 7], 
    [8, 9, 10, 11] 
]) 
b = numpy.array([ 
    [2, 3], 
    [6, 7] 
]) 

position = a.find(b) 

positionその後、(0, 2)だろう。

答えて

8

これは、scipyのcorrelate2dを使用して次にargmaxを使用して相互相関のピークを見つけることで実行できます。

Here's数学とアイデアのより完全な説明、およびいくつかの例。

純粋なナンシーで、scipyを使用しない場合や、画像が大きい場合は、おそらく相互相関に基づくFFTベースのアプローチを使用することをお勧めします。

編集:質問は具体的に純粋なNumpyソリューションを求めた。しかし、OpenCVやその他の画像処理ツールを使用することができれば、明らかにこれらのいずれかを使用する方が簡単です。そのような例は、以下のPiQuerによって提供されています。私はあなたがそれを使用することができればお勧めします。

32

私はOpenCVmatchTemplate機能でこれをやっています。 OpenCVには優れたPythonバインディングがあり、numpyを内部的に使用しているので、画像は単にnumpyの配列にすぎません。たとえば、100x100ピクセルのBGRファイルtestimage.bmpがあるとします。位置(30,30)に10x10のサブ画像を取り、元の画像を探します。

import cv2 
import numpy as np 

image = cv2.imread("testimage.bmp") 
template = image[30:40,30:40,:] 

result = cv2.matchTemplate(image,template,cv2.TM_CCOEFF_NORMED) 
print np.unravel_index(result.argmax(),result.shape) 

出力:

(30, 30) 

あなたが元にテンプレートを一致させるために、いくつかのアルゴリズムを選択することができます、cv2.TM_CCOEFF_NORMEDはちょうどそれらの一つです。詳細はドキュメントを参照してください。アルゴリズムの中には、一致を最小値として示すものもあれば、結果配列の最大値として示すものもあります。警告の言葉:OpenCVはデフォルトでBGRのチャンネルオーダーを使用しますので、注意してください。 cv2.imreadで読み込んだ画像と画像を比較すると、PILからnumpyに変換されます。フォーマット間の変換には常にcv2.cvtColorを使用できます。

所定の閾値confidence上記全てマッチを見つけるために、私はマッチングを抽出するために、本の線に沿って何かを使用することは私の結果アレイから座標:

match_indices = np.arange(result.size)[(result>confidence).flatten()] 
np.unravel_index(match_indices,result.shape) 

これは、長さ2の配列の組を与えますそれぞれが一致する座標です。

+0

より完全な答え、ありがとう〜私は選ばれた答えを変更することができることを望むが、私はできるtom10からいくつかの担当者を盗むためではない –

+0

ただ、好奇心のうちではなく;)、なぜあなたは受け入れ答えを変更することはできません?私はstackoverflowに新しいですが、私が投稿した自分自身の最初の質問では、受け入れられた回答を "トグル"できることを示し、meta.stackoverflow.comの他の質問は、*再*受け入れるべきであることを示しています。 – PiQuer

+1

**編集**:私はこの質問で尋ねたアカウントが私の現在のアカウントと同じではないためです。私はその他のアカウントにログインするために使用されたOpenIDドメインのコントロールを失ってしまったので、ログインしてそれを変更することはできません。 –

2

N次元配列の正規化された相互相関のスタンドアロン実装を書き終わったところです。hereから入手できます。

相互相関は、scipy.ndimage.correlateを使用して直接計算するか、周波数領域で、与えられた入力サイズで最も速い方に応じてscipy.fftpack.fftn/ifftnを使用して計算されます。

+0

偶然のdownvoteのために申し訳ありません。 (モバイル機器。)質問を編集すると私はダウンボートを元に戻します。 (それはロックされているので、現時点ではできません) – funroll

2

あなたは実際には次のように実装regexを使用して、単純な文字列検索にこの問題を軽減することができます - 2つのPIL.Imageオブジェクトを受け取り、haystackneedleの座標を検出します。これはピクセル単位の検索を使用するよりも約127倍高速です。

def subimg_location(haystack, needle): 
    haystack = haystack.convert('RGB') 
    needle = needle.convert('RGB') 

    haystack_str = haystack.tostring() 
    needle_str = needle.tostring() 

    gap_size = (haystack.size[0] - needle.size[0]) * 3 
    gap_regex = '.{' + str(gap_size) + '}' 

    # Split b into needle.size[0] chunks 
    chunk_size = needle.size[0] * 3 
    split = [needle_str[i:i+chunk_size] for i in range(0, len(needle_str), chunk_size)] 

    # Build regex 
    regex = re.escape(split[0]) 
    for i in xrange(1, len(split)): 
     regex += gap_regex + re.escape(split[i]) 

    p = re.compile(regex) 
    m = p.search(haystack_str) 

    if not m: 
     return None 

    x, _ = m.span() 

    left = x % (haystack.size[0] * 3)/3 
    top = x/haystack.size[0]/3 

    return (left, top) 
+0

賢い!そして、軽量なので、OpenCVのようなものを引っ張ることに比べて。あなたのコードには1つの問題があります: '.'はデフォルトで改行にマッチしないので、ターゲット外の10のチャンネル値はマッチしません。正規表現の先頭に '(?s)'を付けたり、 're.DOTALL'でコンパイルすることで修正されました。 – dhaffey

関連する問題