Python 如何提高sobel边缘检测器的效率
我正在用Python从头开始编写一个使用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],[
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
矩阵卷积
而不是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
Sobelx
内核:
-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]