Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何提高sobel边缘检测器的效率_Python_Performance_Numpy_Edge Detection_Sobel - Fatal编程技术网

Python 如何提高sobel边缘检测器的效率

Python 如何提高sobel边缘检测器的效率,python,performance,numpy,edge-detection,sobel,Python,Performance,Numpy,Edge Detection,Sobel,我正在用Python从头开始编写一个使用rpi摄像头的程序。目前,我已经实现了到greyscale的转换和一些其他基本img操作,它们在我的model Brpi3上运行得相对较快 但是,我使用的sobel操作符()的边缘检测函数比其他函数慢得多,尽管它确实可以工作。这是: def sobel(img): xKernel = np.array([[-1,0,1],[-2,0,2],[-1,0,1]]) yKernel = np.array([[-1,-2,-1],[0,0,0],[

我正在用Python从头开始编写一个使用
rpi
摄像头的程序。目前,我已经实现了到
greyscale
的转换和一些其他基本
img
操作,它们在我的
model B
rpi3
上运行得相对较快

但是,我使用的
sobel
操作符()的边缘检测函数比其他函数慢得多,尽管它确实可以工作。这是:

def sobel(img):
    xKernel = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])
    yKernel = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])
    sobelled = np.zeros((img.shape[0]-2, img.shape[1]-2, 3), dtype="uint8")
    for y in range(1, img.shape[0]-1):
        for x in range(1, img.shape[1]-1):
            gx = np.sum(np.multiply(img[y-1:y+2, x-1:x+2], xKernel))
            gy = np.sum(np.multiply(img[y-1:y+2, x-1:x+2], yKernel))
            g = abs(gx) + abs(gy) #math.sqrt(gx ** 2 + gy ** 2) (Slower)
            g = g if g > 0 and g < 255 else (0 if g < 0 else 255)
            sobelled[y-1][x-2] = g
    return sobelled
def sobel(img):
xKernel=np.array([[-1,0,1],-2,0,2],-1,0,1])
yKernel=np.数组([[-1,-2,-1],[0,0,0],[1,2,1]]
sobelled=np.zero((img.shape[0]-2,img.shape[1]-2,3),dtype=“uint8”)
对于范围内的y(1,img.shape[0]-1):
对于范围内的x(1,图像形状[1]-1):
gx=np.sum(np.multiply(img[y-1:y+2,x-1:x+2],xKernel))
gy=np.和(np.乘(img[y-1:y+2,x-1:x+2],yKernel))
g=abs(gx)+abs(gy)#math.sqrt(gx**2+gy**2)(较慢)
如果g>0且g<255,则g=g(如果g<0,则为0,否则为255)
sobelled[y-1][x-2]=g
索贝尔德酒店
并使用猫的
灰度
图像运行它:

我得到的回答似乎是正确的:

该库的应用,特别是该功能,是在一个下棋机器人上,在该机器人中,边缘检测将有助于识别棋子的位置。问题是运行需要15秒,这是一个重要的问题,因为这会增加机器人移动所需的时间

我的问题是:如何加快速度?

到目前为止,我已经尝试了两种方法:

  • 而不是
    平方
    然后
    添加
    ,然后
    平方根
    gx
  • gy
    值来获得总梯度,我只是
    求和
    绝对
    值。这将速度提高了相当多

  • 使用
    rpi
    摄像机的较低
    分辨率
    图像。这显然是一种使这些操作运行得更快的简单方法,但实际上并不可行,因为它在最低可用分辨率
    480x360
    下仍然非常慢,这大大降低了相机的最大分辨率
    3280x2464

  • 编写嵌套for循环以执行
    矩阵卷积
    而不是
    np.sum(np.multiply(…)
    。结果是速度稍微慢了一点,这让我很惊讶,因为
    np.multiply
    返回一个新数组,我认为使用
    循环
    应该更快。我认为这可能是因为
    numpy
    主要是用
    C
    编写的,或者新数组没有实际存储,所以不会花费很长时间,但我不太确定


  • 任何帮助都将不胜感激-我认为改进的主要内容是点
    3
    ,即
    矩阵
    乘法和求和。

    即使您正在构建自己的库,您也绝对应该使用库进行卷积,他们将在后端用C或Fortran进行最终操作,这将大大加快速度

    但是如果您愿意,可以自己做,使用线性可分离过滤器。我的想法是:

    图片:

    1 2 3 4 5
    2 3 4 5 1
    3 4 5 1 2
    
    Sobel
    x
    内核:

    -1 0 1
    -2 0 2
    -1 0 1
    
    结果:

    8, 3, -7
    
    在卷积的第一个位置,您将计算9个值。首先,为什么?您永远不会添加中间的列,不必费心将其相乘。但这不是线性可分离滤波器的重点。这个想法很简单。将内核放在第一个位置时,第三列将乘以
    [1,2,1]
    。但两步之后,将第三列乘以
    [-1,-2,-1]
    。真是浪费!你已经计算过了,现在你只需要否定它。这就是线性可分离滤波器的想法。请注意,可以将过滤器分解为两个向量的矩阵外积:

    [1]
    [2]  *  [-1, 0, 1]
    [1]
    
    在这里取外积得到相同的矩阵。因此,这里的想法是将操作分为两部分。首先将整个图像与行向量相乘,然后与列向量相乘。取行向量

    -1 0 1
    
    在整个图像中,我们以

    2  2  2
    2  2 -3
    2 -3 -3
    
    然后通过将列向量进行相乘和求和,我们再次得到

    8, 3, -7
    
    另一个可能有用也可能无用的妙招(取决于您在内存和效率之间的权衡):

    请注意,在单行乘法中,忽略中间值,只需从左值减去右值。这意味着您所做的实际上是减去这两幅图像:

    3 4 5     1 2 3
    4 5 1  -  2 3 4
    5 1 2     3 4 5
    
    如果将图像的前两列切掉,则得到左矩阵,如果将最后两列切掉,则得到右矩阵。所以你可以简单地计算卷积的第一部分

    result_h = img[:,2:] - img[:,:-2]
    
    然后,您可以循环查找sobel运算符的剩余列。或者,你甚至可以更进一步,做我们刚才做的事情。这一次对于垂直情况,您只需添加第一行和第三行以及两次第二行;或者,使用numpy加法:

    result_v = result_h[:-2] + result_h[2:] + 2*result_h[1:-1]
    
    Sobel x kernel:
    -1 0 1
    -2 0 2
    -1 0 1
    
    你完了!在不久的将来,我可能会在这里添加一些时间安排。对于一些信封背面的计算(即1000x1000图像上的仓促Jupyter笔记本计时):

    新方法(图像总和):每个循环8.18 ms±399µs(7次运行的平均值±标准偏差,每个循环100次)

    旧方法(双回路):每个回路7.32 s±207 ms(7次运行的平均值±标准偏差,每个回路1次)

    是的,你读对了:1000倍加速


    下面是一些比较这两者的代码:

    import numpy as np
    
    def sobel_x_orig(img):
        xKernel = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])
        sobelled = np.zeros((img.shape[0]-2, img.shape[1]-2))
        for y in range(1, img.shape[0]-1):
            for x in range(1, img.shape[1]-1):
                sobelled[y-1, x-1] = np.sum(np.multiply(img[y-1:y+2, x-1:x+2], xKernel))
        return sobelled
    
    def sobel_x_new(img):
        result_h = img[:,2:] - img[:,:-2]
        result_v = result_h[:-2] + result_h[2:] + 2*result_h[1:-1]
        return result_v
    
    img = np.random.rand(1000, 1000)
    sobel_new = sobel_x_new(img)
    sobel_orig = sobel_x_orig(img)
    
    assert (np.abs(sobel_new-sobel_orig) < 1e-12).all()
    
    将numpy导入为np
    定义sobel_x_orig(img):
    xKernel=np.array([[-1,0,1],-2,0,2],-1,0,1])
    sobelled=np.zero((img.shape[0]-2,img.shape[1]-2))
    对于范围内的y(1,img.shape[0]-1):
    对于范围内的x(1,图像形状[1]-1):
    
    sum = -inputvalue[y-1][x-1] - 2 * inputvalue[y][x-1] - inputvalue[y+1][x-1]
    + inputvalue[y-1][x+1] + 2 * inputvalue[y][x+1] + inputvalue[y+1][x+1]