バイナリ2DマトリックスのPython輪郭
質問
バイナリNxM行列の形状の周りの凸包を計算したい。凸包アルゴリズムは座標のリストを想定しているため、numpy.argwhere(im)ですべての形状点座標を取得します。ただし、これらのポイントのほとんどは、凸包に寄与していません(形状の内側にあります)。凸包の計算時間は少なくとも入力として取得するポイントの数に比例するため、事前に多数の無駄なポイントをフィルター処理し、アウトラインにまたがるポイントのみを渡すというアイデアを考案しました。アイデアは非常に単純で、バイナリNxM行列の各行に対して、最小インデックスと最大インデックスのみを使用します。例えば:
im = np.array([[1,1,1,0],
[1,0,1,1],
[1,1,0,1],
[0,0,0,0],
[0,1,1,1]], dtype=np.bool)
outline = somefunc(im)
その後、アウトラインを読み取る必要があります(タプルまたは5x2 numpy配列として、私は気にしません):
[(0,0),(0,2),(1,0),(1,3),(2,0),(2,3),(4,1),(4,3)]
この形状(im)の周囲のタイトな凸包は、これらのポイントのサブセット(輪郭)でなければなりません。つまり、" somefunc()"内側の点をフィルタリングするのに効率的であり、凸包計算の時間を節約します。
上記のトリックを行うコードはありますが、何度も実行する必要があるため、誰かがより賢い(より速く読む)アプローチを望んでいます。私が持っているコードは:
# I have a 2D binary field. random for the purpose of demonstration.
import numpy as np
im = np.random.random((320,360)) > 0.9
# This is my algorithm so far. Notice that coords is sorted.
coords = np.argwhere(im)
left = np.roll(coords[:,0], 1, axis=0) != coords[:,0]
outline = np.vstack([coords[left], coords[left[1:]], coords[-1]])
もう1つのアイデアは、Pythonのreduce()を使用することでした。そのため、座標のリストを一度だけ実行する必要があります。しかし、良い還元関数を見つけるのは難しいです。
ご協力いただければ幸いです!
編集
その間、 im
から outline
に直接移動するより速い方法を見つけました。少なくとも大きな画像の場合、これは非常に高速です。外部ソリューションが明らかにない場合、この質問に対するソリューションとしてそれを仮定しています。
さらに、誰かがもっと速い方法を知っているなら、声を上げてください:)
解決
受け入れられる答えがない場合、解決策として最善のコードを投稿します。
def outline(im):
''' Input binary 2D (NxM) image. Ouput array (2xK) of K (y,x) coordinates
where 0 <= K <= 2*M.
'''
topbottom = np.empty((1,2*im.shape[1]), dtype=np.uint16)
topbottom[0,0:im.shape[1]] = np.argmax(im, axis=0)
topbottom[0,im.shape[1]:] = (im.shape[0]-1)-np.argmax(np.flipud(im), axis=0)
mask = np.tile(np.any(im, axis=0), (2,))
xvalues = np.tile(np.arange(im.shape[1]), (1,2))
return np.vstack([topbottom,xvalues])[:,mask].T
他のヒント
この割り当ては、最後の2つのステップと同じことを達成するようです:
outline = np.array(dict(reversed(coords)).items() + dict(coords).items())
ただし、高速であるかどうかはわかりません。
より一般的な解決策として、何らかのエッジ検出方法を使用してエッジポイントのみを見つけることができます。 (Google ..)NumPyにはSobelフィルターが組み込まれていると思います。