2016-11-05 32 views
2

バイナリマスクのエッジを見つけることを含むリアルタイムアプリケーションを作成したい。可能であれば、GPUなしで何か速いものが必要です。画像あたり0.0005秒以下で、サイズ(1000,1000)でうまくいきます。サイズ(1000,1000)のバイナリイメージの次の例を使用します。Pythonのバイナリマスクから輪郭マスクを高速に検索する方法

(コードを複製するように:)

import numpy as np 
im=np.zeros((1000,1000),dtype=np.uint8) 
im[400:600,400:600]=255 

Image

を高速に物事を行うための最初の論理的な方法は、OpenCVのライブラリを使用していた。予想通り

import cv2 
timeit.timeit(lambda:cv2.Laplacian(im,cv2.CV_8U),number=100)/100 
0.0011617112159729003 

がもたらしました: laplacian

私はこの方法が非常に時間がかかることがわかった。この後、私はfindContoursを試しました:

def usingcontours(im): 
    points=np.transpose(cv2.findContours(im,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[1][0]) 
    tmp=np.zeros_like(im) 
    tmp[tuple(points)]=255 
    return tmp 
timeit.timeit(lambda:usingcontours(im),number=100)/100 
0.0009052801132202148 

これは上記と同じ結果をもたらしました。 これは優れていますが、それでも私が望むほど良くはありません。私はそれが悪いことを知っていたが、私は、最後の手段として、勾配を用いてラプラシアン近似する、numpyのの使用に移っ:

def usinggradient(im): 
    tmp=np.gradient(im) 
    return ((tmp[0]+tmp[1])>0).astype(np.uint8) 
timeit.timeit(lambda:usinggradient(im),number=100)/100 
0.018681130409240722 

だから、誰に私は私のアルゴリズムを加速させることができる方法上の任意の更なるアイデアを持って?私はこのアルゴリズムをバイナリイメージに使用したいと考えているので、より良い実装が必要であると思います。

+0

NumPyとScipyの形態的侵食とビット単位の操作NumPyでこれを行うことができます。 'scipy.ndimage.morphology.binary_dilation'と' np.logical_ * '関数を見てください。 – YXD

+0

'1000 x 1000/0.0005s = 2 x 10^9 pixels/second' - それは1ピクセルあたり1-2クロック・サイクルであり、ベクトル化と並列化でもそれほど動かない。 –

+0

@DanMašek2×10^9ピクセル/秒とは、ビット画像2×10^9ビット/秒を意味します。良いプログラムは、私が可能と信じているすべてのコアを使用していると仮定すると、8コアの2GHz CPUでは、各コアに4つのスレッドがあり、クロックサイクルあたり32ピクセル(ビット)を処理できるため、64 * 10^9ピクセル/秒= 1000×1000/0.00016秒。ですから、O(3 * n)よりも複雑な実装を求めています。バイナリイメージでは可能です。 –

答えて

2

スピードアップのために、最も速いものをcv2.findContoursで選択しました。その中で、私たちはそうのように、簡単なslicingと部品をタプルに変換する高価なものをtransposeとを置き換えることができます -

idx = cv2.findContours(im,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[1][0] 
out = np.zeros_like(im) 
out[idx[:,0,0],idx[:,0,1]] = 255 

ランタイムテスト -

In [114]: # Inputs 
    ...: im=np.zeros((1000,1000),dtype=np.uint8) 
    ...: im[400:600,400:600]=255 
    ...: idx = cv2.findContours(im,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)[1][0] 
    ...: 

In [115]: def original_app(im, idx): 
    ...:  points=np.transpose(idx) 
    ...:  tmp=np.zeros_like(im) 
    ...:  tmp[tuple(points)]=255 
    ...:  return tmp 
    ...: 
    ...: def proposed_app(im, idx): 
    ...:  out = np.zeros_like(im) 
    ...:  out[idx[:,0,0],idx[:,0,1]] = 255 
    ...:  return out 
    ...: 

In [120]: %timeit original_app(im, idx) 
10000 loops, best of 3: 108 µs per loop 

In [121]: %timeit proposed_app(im, idx) 
10000 loops, best of 3: 101 µs per loop 

In [122]: %timeit cv2.findContours(im,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) 
1000 loops, best of 3: 1.55 ms per loop 

だから、そこにいくつかのわずかな改善が提案された方法であります、しかし、それは輪郭発見そのものと比較して無視できるようです。

私はを調べ、クイックテストを実行して、OpenCVバージョンよりもはるかに遅いようです。

+0

回答。さて、スライスして実際にアルゴリズムを加速します。上記のように、findContoursは時間がかかるものです。問題は、findContoursのバイナリ形状の複雑さにもよりますが、奇数(findContours)が他の方法よりもずっと大きく、計算時間が10倍も異なることです(残念なことに、良い代表画像を見つけることができませんでしたこの事実)。 –

+0

私はcv2.CHAIN_APPROX_SIMPLEを使ってfindContoursを高速化すると思いますが、悪い点はバイナリのnumpy配列を入力として使うことができないということです。私の見解では、関数を全体として大きく加速します。メモリにアクセスして取得するのに要する時間の1/8であり、より速いランタイムを提供します。とにかく、もう一度お時間をいただきありがとうございます。 –