使用python优化opencv的骨架函数
所以我在raspbian(raspberry pi 2模型B)上使用OpenCV。很明显,我在做视觉/图像处理,rasppi就是我得到的(如果可以的话,我会使用计算机) 我需要运行一个骨架函数。我发现了以下实现:使用python优化opencv的骨架函数,python,opencv,image-processing,Python,Opencv,Image Processing,所以我在raspbian(raspberry pi 2模型B)上使用OpenCV。很明显,我在做视觉/图像处理,rasppi就是我得到的(如果可以的话,我会使用计算机) 我需要运行一个骨架函数。我发现了以下实现: import cv2 import numpy as np img = cv2.imread('img.png',0) size = np.size(img) skeleton = np.zeros(img.shape,np.uint8) ret,img = cv2.thresho
import cv2
import numpy as np
img = cv2.imread('img.png',0)
size = np.size(img)
skeleton = np.zeros(img.shape,np.uint8)
ret,img = cv2.threshold(img,127,255,0)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
finished = False
while(not finished):
eroded = cv2.erode(img,kernel)
temp = cv2.dilate(eroded,kernel)
temp = cv2.subtract(img,temp)
skeleton = cv2.bitwise_or(skeleton,temp)
img = eroded.copy()
zeros = size - cv2.countNonZero(img)
if zeros==size:
finished = True
cv2.imshow("skeleton",skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()
当它运行时,速度非常慢,这并不奇怪(我正在对之前的图像进行FFT和带通滤波操作,然后运行骨架操作)。另一个代码很慢,但将完成操作
图片很大,我可以裁剪一些,但我认为这还不够。我试图找到一个优化的版本,但到目前为止还没有找到任何东西。有什么想法或解决方案吗?在这个答案中,我将重点改进您的实现,而不是算法。虽然这不会给我们带来很大的收益,但我认为意识到这一点仍然是有用的 准备工作 让我们从一些样板开始——必要的导入、一些测试图像和一些函数,让我们能够轻松地进行比较:
from timeit import default_timer as timer
import numpy as np
import cv2
# Create a decent size test image...
img = cv2.imread('cage.png',0)
img = cv2.resize(img, (2048, 2048))
cv2.normalize(img, img, 0, 255, cv2.NORM_MINMAX)
def time_fn(fn, img, iters=1):
start = timer()
result = None
for i in range(iters):
result = fn(img)
end = timer()
return (result,((end - start) / iters) * 1000)
def run_test(fn, img, i):
res, t = time_fn(fn, img, 4)
cv2.imwrite("skeleton_%d.png" % i, res[0])
print "Variant %d" % i
print "Input size = (%d, %d)" % img.shape[:2]
print "Ran %d iterations to find skeleton." % res[1]
print "Avg. find_skeleton time = %0.4f s." % (t/1000)
备选案文1(原件)
让我们将您的实现转化为一个函数,并删除一些不必要的位。出于好奇,让我们跟踪骨架化所需的迭代次数
def find_skeleton1(img):
skeleton = np.zeros(img.shape,np.uint8)
_,thresh = cv2.threshold(img,127,255,0)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
iters = 0
while(True):
eroded = cv2.erode(thresh, kernel)
temp = cv2.dilate(eroded, kernel)
temp = cv2.subtract(thresh, temp)
skeleton = cv2.bitwise_or(skeleton, temp)
thresh = eroded.copy()
iters += 1
if cv2.countNonZero(thresh) == 0:
return (skeleton,iters)
让我们看看它如何设置基线
>>> run_test(find_skeleton1, img, 1)
Variant 1
Input size = (2048, 2048)
Ran 338 iterations to find skeleton.
Avg. find_skeleton time = 2.7969 s.
变式2
我们可以做的第一个改进是最小化新数组对象的分配数量,并尽可能多地重用。我们可以创建更多的临时数组(如skeleton
),并在循环中使用OpenCV函数的dst
参数,忽略返回值。因为我们提供了正确形状和数据类型的目标,所以可以重用现有数组
def find_skeleton2(img):
skeleton = np.zeros(img.shape,np.uint8)
eroded = np.zeros(img.shape,np.uint8)
temp = np.zeros(img.shape,np.uint8)
_,thresh = cv2.threshold(img,127,255,0)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
iters = 0
while(True):
cv2.erode(thresh, kernel, eroded)
cv2.dilate(eroded, kernel, temp)
cv2.subtract(thresh, temp, temp)
cv2.bitwise_or(skeleton, temp, skeleton)
thresh = eroded.copy()
iters += 1
if cv2.countNonZero(thresh) == 0:
return (skeleton,iters)
让我们尝试一下,并检查结果是否相同:
>>> print np.array_equal(find_skeleton1(img)[0], find_skeleton2(img)[0])
True
>>> run_test(find_skeleton2, img, 2)
Variant 2
Input size = (2048, 2048)
Ran 338 iterations to find skeleton.
Avg. find_skeleton time = 1.4356 s.
变式3
下一步是消除不必要的副本——有一个非常明显:thresh=correcated.copy()
。请注意,在下面的迭代中,我们立即覆盖腐蚀的的内容。因此,只要它的形状和数据类型正确,我们并不真正关心它包含什么。它们是,这意味着我们可以交换这两个对象,而不是执行复制
def find_skeleton3(img):
skeleton = np.zeros(img.shape,np.uint8)
eroded = np.zeros(img.shape,np.uint8)
temp = np.zeros(img.shape,np.uint8)
_,thresh = cv2.threshold(img,127,255,0)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
iters = 0
while(True):
cv2.erode(thresh, kernel, eroded)
cv2.dilate(eroded, kernel, temp)
cv2.subtract(thresh, temp, temp)
cv2.bitwise_or(skeleton, temp, skeleton)
thresh, eroded = eroded, thresh # Swap instead of copy
iters += 1
if cv2.countNonZero(thresh) == 0:
return (skeleton,iters)
同样,让我们验证结果是否匹配,并进行一些计时
>>> print np.array_equal(find_skeleton1(img)[0], find_skeleton3(img)[0])
True
>>> run_test(find_skeleton3, img, 3)
Variant 3
Input size = (2048, 2048)
Ran 338 iterations to find skeleton.
Avg. find_skeleton time = 0.9839 s.
很少有简单的改动使时间缩短到原来的35%。当然,它仍然会对整个图像进行数百次迭代处理。下一步是研究如何减少工作量——在后面的迭代中,工作图像的重要区域是黑色的,并且对骨架没有任何贡献
NB:在i7-4930K上进行测量。我没有树莓,请随意添加您的计时,这样我们就可以看到它的效果。请您附上一张用作输入的示例图像,好吗?你现在的表现如何?目标是什么?