- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在写一个 computer vision library从头开始使用 Python 来使用 rpi
相机。目前,我已经实现了转换为 greyscale
和一些其他基本的 img
操作,它们在我的 model B
rpi3 上运行得相对较快
。
但是,我使用 sobel
运算符 ( wikipedia description ) 的边缘检测函数虽然可以运行,但比其他函数慢得多。在这里:
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
并使用这张猫的灰度
图像运行它:
我收到这样的回复,这似乎是正确的:
该库的应用,尤其是此功能,是在下棋机器人上进行的,其中边缘检测将有助于识别棋子的位置。问题是它需要 >15
秒才能运行,这是一个严重的问题,因为它会大大增加机器人移动所需的时间。
我的问题是:我怎样才能加快速度?
到目前为止,我已经尝试了一些事情:
不是平方
然后相加
,然后是平方根
gx
和gy
值以获得总梯度,我只是求和
绝对
值。这大大提高了速度。
使用来自 rpi
相机的较低分辨率
图像。这显然是使这些操作运行得更快的简单方法,但它并不是真正可行,因为它在 480x360
的最小可用分辨率下仍然非常慢,这大大低于相机的最大 3280x2464
。
编写嵌套的 for 循环来代替 np.sum(np.multiply(...))
进行矩阵卷积
。这最终变得稍微慢,这让我感到惊讶,因为自从 np.multiply
返回一个新数组以来,我认为使用 应该更快循环
。我认为这可能是由于 numpy
主要是用 C
编写的,或者新数组实际上并未存储,因此不会花费很长时间但我不太确定。
如有任何帮助,我们将不胜感激 - 我认为需要改进的主要是点 3
,即 matrix
乘法和求和。
最佳答案
即使您正在构建自己的库,您确实绝对应该使用库进行卷积,它们将在后端用 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]
大功告成!我可能会在不久的将来在这里添加一些时间安排。对于一些复杂的计算(即 1000x1000 图像上的 Jupyter notebook 时间仓促):
new method (sums of the images): 8.18 ms ± 399 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
old method (double for-loop):7.32 s ± 207 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
是的,您没有看错: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()
当然,1e-12
是一些严重的公差,但这是每个元素,所以应该没问题。但是我也有一个 float
图像,对于 uint8
图像你当然会有更大的差异。
请注意,您可以对任何线性可分离滤波器 执行此操作!这包括高斯滤波器。还要注意,一般来说,这需要很多操作。在 C 或 Fortran 或其他语言中,它通常只是作为单行/列向量的两个卷积来实现,因为最后,它需要实际循环遍历每个矩阵的每个元素;无论您只是将它们相加还是相乘,因此在 C 中以这种方式添加图像值并不比仅进行卷积更快。但是遍历 numpy
数组非常慢,所以这种方法在 Python 中要快得多。
关于python - 如何提高索贝尔边缘检测器的效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46513267/
第一个 .on 函数比第二个更有效吗? $( "div.container" ).on( "click", "p", function(){ }); $( "body" ).on( "click",
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 已关闭 7 年前。 Improve
我有这样的查询: $('#tabContainer li'); JetBrains WebStorm IDE 将其突出显示为低效查询。它建议我改用这个: $('#tabContainer').find
我刚刚在 coursera ( https://www.coursera.org/saas/) 上听了一个讲座,教授说 Ruby 中的一切都是对象,每个方法调用都是在对象上调用发送方法,将一些参数传递
这可能是用户“不喜欢”的另一个问题,因为它更多的是与建议相关而不是与问题相关。 我有一个在保存和工作簿打开时触发的代码。 它在 f(白天与夜晚,日期与实际日期)中选择正确的工作表。 周一到周三我的情况
这只是我的好奇心,但是更有效的是递归还是循环? 给定两个功能(使用通用lisp): (defun factorial_recursion (x) (if (> x 0) (*
这可能是一个愚蠢的问题,但是while循环的效率与for循环的效率相比如何?我一直被教导,如果可以使用for循环,那我应该这样做。但是,实际上之间的区别是什么: $i = 0; while($i <
我有一个Elasticsearch索引,其中包含几百万条记录。 (基于时间戳的日志记录) 我需要首先显示最新记录(即,按时间戳降序排列的记录) 在时间戳上排序desc是否比使用时间戳的函数计分功能更有
使用Point2D而不是double x和y值时,效率有很大差异吗? 我正在开发一个程序,该程序有许多圆圈在屏幕上移动。他们各自从一个点出发,并越来越接近目的地(最后,他们停下来)。 使用 .getC
我正在编写一个游戏,并且有一个名为 GameObject 的抽象类和三个扩展它的类(Player、Wall 和 Enemy)。 我有一个定义为包含游戏中所有对象的列表。 List objects; 当
我是 Backbone 的初学者,想知道两者中哪一个更有效以及预期的做事方式。 A 型:创建一个新集合,接受先前操作的结果并从新集合中提取 key result = new Backbone.Coll
最近,关于使用 LIKE 和通配符搜索 MS SQL 数据库的最有效方法存在争论。我们正在使用 %abc%、%abc 和 abc% 进行比较。有人说过,术语末尾应该始终有通配符 (abc%)。因此,根
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想知道,这样做会更有效率吗: setVisible(false) // if the component is invisible 或者像这样: if(isVisible()){
我有一个静态方法可以打开到 SQL Server 的连接、写入日志消息并关闭连接。我在整个代码中多次调用此方法(平均每 2 秒一次)。 问题是 - 它有效率吗?我想也许积累一些日志并用一个连接插入它们
这个问题在这里已经有了答案: Best practice to avoid memory or performance issues related to binding a large numbe
我为我的 CS 课(高中四年级)制作了一个石头剪刀布游戏,我的老师给我的 shell 文件指出我必须将 do while 循环放入运行者中,但我不明白为什么?我的代码可以工作,但她说最好把它写在运行者
我正在编写一个需要通用列表的 Java 应用程序。该列表需要能够经常动态地调整大小,对此的明显答案是通用的Linkedlist。不幸的是,它还需要像通过调用索引添加/删除值一样频繁地获取/设置值。 A
我的 Mysql 语句遇到了真正的问题,我需要将几个表连接在一起,查询它们并按另一个表中值的平均值进行排序。这就是我所拥有的... SELECT ROUND(avg(re.rating
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Is there a difference between i==0 and 0==i? 以下编码风格有什么
我是一名优秀的程序员,十分优秀!