2016-11-05 80 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像素/秒 - 即每個像素1-2個時鐘週期,甚至可以使用矢量化和並行處理,這些處理工作無需太多工作。 –

+0

@DanMašek2 x 10^9像素/秒錶示按位圖像2x10^9位/秒。假設一個好的程序在一個8核2GHz CPU中使用了所有內核,我認爲每個內核有4個線程,我可以在每個時鐘週期處理32個像素(位),所以64 * 10^9像素/秒= 1000×1000/0.00016s。所以我要求任何現成的實現低於O(3 * n)的複雜度,這在二進制映像中我認爲是可能的。 –

回答

2

我挑選了最快的cv2.findContours來加速。在這裏面,我們可以替代那些昂貴transpose並轉換成元組簡單slicing部分,像這樣 -

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 

所以,有一些邊際改善那裏所提出的方法,但與輪廓發現本身相比,這似乎可以忽略不計。

我看着scikit-image's version並且跑了一個快速測試,看起來好像比OpenCV版本慢得多。

+0

感謝您的支持回答。好的,所以切片確實會加速算法。如上所述,findContours是一個耗時的工具。問題是,findContours也有不同的計算時間,這取決於二進制形狀的複雜性,Ω(findContours)比其他方法大很多,甚至超過10倍(很遺憾,我無法找到一個很好的代表圖像到圖片這個事實)。 –

+0

我猜cv2.CHAIN_APPROX_SIMPLE會讓findContours更快,但是不好的是我不能使用二進制numpy數組作爲輸入,在我看來,這將會加速整個函數的大量減少1/8訪問和檢索內存所需的時間,併爲我提供更快的運行時間。無論如何,再次感謝你的時間。 –