使用python优化opencv的骨架函数

使用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

所以我在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.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上进行测量。我没有树莓,请随意添加您的计时,这样我们就可以看到它的效果。

请您附上一张用作输入的示例图像,好吗?你现在的表现如何?目标是什么?