- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我在 Python 中编写了一个自适应颜色阈值函数(因为 OpenCV 的 cv2.adaptiveThreshold 不符合我的需要)而且速度太慢了。我已经尽可能提高了它的效率,但是在 1280x720 的图像上它仍然需要将近 500 毫秒。
我将不胜感激任何可以使此功能更有效的建议!
该函数的作用如下:它使用一个像素厚度的十字形状作为结构元素。对于图像中的每个像素,它计算 ksize
的平均值四个方向的相邻像素独立 (即 ksize
个像素在左侧同一行、上方同一列、右侧同一行和下方同一列中的平均值)。我以四个平均值结束,每个方向一个。如果像素比左右平均值或顶部和底部平均值(加上一些常数 C
)更亮,则该像素仅符合阈值标准。
我使用 numpy.roll()
同时为所有像素递增地计算这些平均值,但我仍然需要这样做 ksize
次。 ksize
通常为 20-50。
这是代码,相关部分实际上就是 for 循环内部发生的事情:
def bilateral_adaptive_threshold(img, ksize=20, C=0, mode='floor', true_value=255, false_value=0):
mask = np.full(img.shape, false_value, dtype=np.int16)
left_thresh = np.zeros_like(img, dtype=np.float32) #Store the right-side average of each pixel here
right_thresh = np.zeros_like(img, dtype=np.float32) #Store the left-side average of each pixel here
up_thresh = np.zeros_like(img, dtype=np.float32) #Store the top-side average of each pixel here
down_thresh = np.zeros_like(img, dtype=np.float32) #Store the bottom-side average of each pixel here
for i in range(1, ksize+1):
roll_left = np.roll(img, -i, axis=1)
roll_right = np.roll(img, i, axis=1)
roll_up = np.roll(img, -i, axis=0)
roll_down = np.roll(img, i, axis=0)
roll_left[:,-i:] = 0
roll_right[:,:i] = 0
roll_up[-i:,:] = 0
roll_down[:i,:] = 0
left_thresh += roll_right
right_thresh += roll_left
up_thresh += roll_down
down_thresh += roll_up
left_thresh /= ksize
right_thresh /= ksize
up_thresh /= ksize
down_thresh /= ksize
if mode == 'floor':
mask[((img > left_thresh+C) & (img > right_thresh+C)) | ((img > up_thresh+C) & (img > down_thresh+C))] = true_value
elif mode == 'ceil':
mask[((img < left_thresh-C) & (img < right_thresh-C)) | ((img < up_thresh-C) & (img < down_thresh-C))] = true_value
else: raise ValueError("Unexpected mode value. Expected value is 'floor' or 'ceil'.")
return mask
最佳答案
正如您在问题中所暗示的那样,该函数的主要部分是获取计算平均值所需的 4 个总和数组——这里,整个函数的平均 210 毫秒中的 190 毫秒。所以,让我们专注于此。
首先,必要的进口和便利的计时功能。
from timeit import default_timer as timer
import numpy as np
import cv2
## ===========================================================================
def time_fn(fn, img, ksize=20, iters=16):
start = timer()
for i in range(iters):
fn(img, ksize)
end = timer()
return ((end - start) / iters) * 1000
## ===========================================================================
# Our test image
img = np.uint8(np.random.random((720,1280)) * 256)
# Original code
def windowed_sum_v1(img, ksize=20):
left_thresh = np.zeros_like(img, dtype=np.float32)
right_thresh = np.zeros_like(img, dtype=np.float32)
up_thresh = np.zeros_like(img, dtype=np.float32)
down_thresh = np.zeros_like(img, dtype=np.float32)
for i in range(1, ksize+1):
roll_left = np.roll(img, -i, axis=1)
roll_right = np.roll(img, i, axis=1)
roll_up = np.roll(img, -i, axis=0)
roll_down = np.roll(img, i, axis=0)
roll_left[:,-i:] = 0
roll_right[:,:i] = 0
roll_up[-i:,:] = 0
roll_down[:i,:] = 0
left_thresh += roll_right
right_thresh += roll_left
up_thresh += roll_down
down_thresh += roll_up
return (left_thresh, right_thresh, up_thresh, down_thresh)
>>> print "V1: %f ms" % time_fn(windowed_sum_v1, img, 20, 16)
V1: 188.572077 ms
numpy.roll
肯定会涉及一些开销,但没有必要在这里深入研究。请注意,在滚动数组后,将溢出数组边缘的行或列归零。然后将其添加到累加器中。添加零不会改变结果,所以我们不妨避免这种情况。相反,我们可以添加整个数组的渐进式更小和适当偏移的切片,避免
roll
和(在某种程度上)减少所需的添加总数。
# Summing up ROIs
def windowed_sum_v2(img, ksize=20):
h,w=(img.shape[0], img.shape[1])
left_thresh = np.zeros_like(img, dtype=np.float32)
right_thresh = np.zeros_like(img, dtype=np.float32)
up_thresh = np.zeros_like(img, dtype=np.float32)
down_thresh = np.zeros_like(img, dtype=np.float32)
for i in range(1, ksize+1):
left_thresh[:,i:] += img[:,:w-i]
right_thresh[:,:w-i] += img[:,i:]
up_thresh[i:,:] += img[:h-i,:]
down_thresh[:h-i,:] += img[i:,:]
return (left_thresh, right_thresh, up_thresh, down_thresh)
>>> print "Results equal (V1 vs V2): %s" % (np.array_equal(windowed_sum_v1(img), windowed_sum_v2(img)))
Results equal (V1 vs V2): True
>>> print "V2: %f ms" % time_fn(windowed_sum_v2, img, 20, 16)
V2: 110.861794 ms
cv2.filter2D
,计算如下:
1.0
并且内核 anchor 定的点的权重为
0.0
.
ksize=8
,我们可以使用以下内核和 anchor 位置。
# Using filter2d
def windowed_sum_v3(img, ksize=20):
kernel_l = np.array([[1.0] * (ksize) + [0.0]])
kernel_r = np.array([[0.0] + [1.0] * (ksize)])
kernel_u = np.array([[1.0]] * (ksize) + [[0.0]])
kernel_d = np.array([[0.0]] + [[1.0]] * (ksize))
left_thresh = cv2.filter2D(img, cv2.CV_32F, kernel_l, anchor=(ksize,0), borderType=cv2.BORDER_CONSTANT)
right_thresh = cv2.filter2D(img, cv2.CV_32F, kernel_r, anchor=(0,0), borderType=cv2.BORDER_CONSTANT)
up_thresh = cv2.filter2D(img, cv2.CV_32F, kernel_u, anchor=(0,ksize), borderType=cv2.BORDER_CONSTANT)
down_thresh = cv2.filter2D(img, cv2.CV_32F, kernel_d, anchor=(0,0), borderType=cv2.BORDER_CONSTANT)
return (left_thresh, right_thresh, up_thresh, down_thresh)
>>> print "Results equal (V1 vs V3): %s" % (np.array_equal(windowed_sum_v1(img), windowed_sum_v3(img)))
Results equal (V1 vs V3): True
>>> print "V2: %f ms" % time_fn(windowed_sum_v3, img, 20, 16)
V3: 46.652996 ms
# Integer only
def windowed_sum_v4(img, ksize=20):
kernel_l = np.array([[1] * (ksize) + [0]], dtype=np.int16)
kernel_r = np.array([[0] + [1] * (ksize)], dtype=np.int16)
kernel_u = np.array([[1]] * (ksize) + [[0]], dtype=np.int16)
kernel_d = np.array([[0]] + [[1]] * (ksize), dtype=np.int16)
left_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_l, anchor=(ksize,0), borderType=cv2.BORDER_CONSTANT)
right_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_r, anchor=(0,0), borderType=cv2.BORDER_CONSTANT)
up_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_u, anchor=(0,ksize), borderType=cv2.BORDER_CONSTANT)
down_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_d, anchor=(0,0), borderType=cv2.BORDER_CONSTANT)
return (left_thresh, right_thresh, up_thresh, down_thresh)
# Integer only, but returning floats
def windowed_sum_v5(img, ksize=20):
result = windowed_sum_v4(img, ksize)
return map(np.float32,result)
>>> print "Results equal (V1 vs V4): %s" % (np.array_equal(windowed_sum_v1(img), windowed_sum_v4(img)))
Results equal (V1 vs V4): True
>>> print "Results equal (V1 vs V5): %s" % (np.array_equal(windowed_sum_v1(img), windowed_sum_v5(img)))
Results equal (V1 vs V5): True
>>> print "V4: %f ms" % time_fn(windowed_sum_v4, img, 20, 16)
V4: 14.712223 ms
>>> print "V5: %f ms" % time_fn(windowed_sum_v5, img, 20, 16)
V5: 20.859744 ms
filter2D
直接返回平均值。这只是一个很小的改进(~3%)。
C
的加减法。 ,通过提供适当的
delta
为
filter2D
称呼。这再次削减了几个百分点。
input < threshold
input - input < threshold - input
0 < threshold - input
0 < adjusted_threshold # determined using adjusted kernel
ksize
)来实现这一点。使用 numpy,这似乎只有很小的区别,尽管按照我的理解,我们可能会在算法的那部分节省一半的读取(而
filter2D
大概仍然读取并乘以相应的值,即使权重是0)。
def bilateral_adaptive_threshold5(img, ksize=20, C=0, mode='floor', true_value=255, false_value=0):
mask = np.full(img.shape, false_value, dtype=np.uint8)
kernel_l = np.array([[1] * (ksize) + [-ksize]], dtype=np.int16)
kernel_r = np.array([[-ksize] + [1] * (ksize)], dtype=np.int16)
kernel_u = np.array([[1]] * (ksize) + [[-ksize]], dtype=np.int16)
kernel_d = np.array([[-ksize]] + [[1]] * (ksize), dtype=np.int16)
if mode == 'floor':
delta = C * ksize
elif mode == 'ceil':
delta = -C * ksize
else: raise ValueError("Unexpected mode value. Expected value is 'floor' or 'ceil'.")
left_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_l, anchor=(ksize,0), delta=delta, borderType=cv2.BORDER_CONSTANT)
right_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_r, anchor=(0,0), delta=delta, borderType=cv2.BORDER_CONSTANT)
up_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_u, anchor=(0,ksize), delta=delta, borderType=cv2.BORDER_CONSTANT)
down_thresh = cv2.filter2D(img, cv2.CV_16S, kernel_d, anchor=(0,0), delta=delta, borderType=cv2.BORDER_CONSTANT)
if mode == 'floor':
mask[((0 > left_thresh) & (0 > right_thresh)) | ((0 > up_thresh) & (0 > down_thresh))] = true_value
elif mode == 'ceil':
mask[((0 < left_thresh) & (0 < right_thresh)) | ((0 < up_thresh) & (0 < down_thresh))] = true_value
return mask
关于Python:如何使这个颜色阈值功能更有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42540173/
我正在用 R 编写程序。我卡在这里。 我有像这样的矢量 X=c(84.05, 108.04, 13.95, -194.05, 64.03, 208.05, 84.13, 57.04) 我想在用 180
我正在编写一个应用程序,该应用程序涉及使用手指或手写笔在屏幕上书写。我有那部分工作。在 ACTION_DOWN 上,开始绘制;在 ACTION_MOVE 上,添加线段;在 ACTION_UP 上,完成
我正在尝试构建 OCR 以从图像中提取文本,我正在使用轮廓来形成文本字符的边界, 经过几次更改 cv2.threshold 的试验后,我在形成文本字符的边界时得到了最适合的轮廓。 #files = o
我正在尝试使用 OpenCV 的 cv::threshold函数(更具体 THRESH_OTSU ),只是我想用蒙版(任何形状)来做,以便在计算过程中忽略外部(背景)。 图像是单 channel (必
对于学校项目,我试图用 Python 编写一个程序来跟踪学生的运动。为了做到这一点,我正在使用 OpenCV。 在互联网上查找了一些教程后,我注意到几乎每个人都使用阈值来实现这一点,因为几乎每一步都需
我使用 jest 来驱动 selenium 测试,它报告一个需要 12 秒的测试缓慢(持续时间以红色突出显示)。在这种情况下,12 秒就可以了。 如何将阈值配置为 30 秒? 最佳答案 Jest is
我想找到 list1 中与 list2 中的值足够接近的值(基于指定的阈值),即与下面的代码类似的功能。然而,与 pyhton 的 set 交集相比,下面的 intersect_with_thresh
我正在尝试创建一张图表上有两个系列并带有阈值选项的浮线图。我知道当我只有 1 个系列时如何启用阈值(就像这里 http://people.iola.dk/olau/flot/examples/thre
我已正确应用 d3 (v 4.0) 直方图函数对数据数组进行分箱。我的代码如下所示: var bins = d3.histogram() .domain([data_points_min,
我正在使用带有自然语言全文的 Mysql FULLTEXT 搜索,不幸的是,我遇到了 FULLTEXT 50% 阈值,如果给定的关键字出现在总行数的 50% 时间,则不允许我搜索行。 我搜索并找到了一
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 8 年前。 Improve th
这是我的绘图数据 var data = [{ data: [[4, 80], [8, 50], [9, 130]], color: "r
是否可以制作Canny忽略短边还是忽略低梯度边?在我的例子中,我将卡片放在木头上,并在 canny 之后看到木结构的许多边缘 canny 函数中的两个阈值有什么用? 最佳答案 Large intens
我正在尝试使用 OpenCV 的 cv::threshold 函数(更具体的 THRESH_OTSU),只是我想使用掩码(任何形状) ), 以便在计算过程中忽略外部(背景)。 图像是单 channel
我正在寻找根据提供的音频、频率范围(例如 20hz-1000hz)和阈值缩放 PNG 文件,以获得平滑的效果。 例如,当有脚踢时,比例平滑到 120%,我想让那些音频可视化器,如 dubstep 等.
我正在尝试找到最佳阈值,以使我的逻辑回归具有最高的 f1 分数。但是,当我写下以下几行时: val f1Score = metrics.fMeasureByThreshold f1Score.fore
我使用 Flot 创建了一个实时(每 10 毫秒更新一次)垂直样条图。图表可见here on Codepen 。我包括了 Flot multiple threshold plugin ,但我希望阈值使
我有一个数据框,其中包含从第 1 天到第 7 天的三个人(John、Terry、Henry)的分数。 1 2 3 4 5 6 7
我正在尝试实现多级 Otsu 阈值,更具体地说,我需要 3 个阈值/4 个类。 我知道关于 SO 的 2 个类似问题:#34856019 和 #22706742。问题是我没有得到好的结果:我已经阅读了
The documentation在 THRESH_BINARY 上说: dst(x,y) = maxval if src(x,y) > thresh else 0 这对我来说并不意味着这不适用于彩色
我是一名优秀的程序员,十分优秀!