Python 对图像中高于阈值的像素值求和的最快方法
Fastest way to sum the values of the pixels above a threshold in an image with Python
我正在尝试找到最佳方法来检索大于某个阈值的像素 value 的总和。例如,如果我的阈值是 253,我有 10 个像素为 254,另外 10 个像素为 255,我希望得到 10*254 + 10*255 = 5090
- 超过阈值的像素的总强度。
我找到了一种方法 np.histogram
:
import cv2, time
import numpy as np
threshold = 1
deltaImg = cv2.imread('image.jpg')
t0=time.time()
histogram = np.histogram(deltaImg,256-threshold,[threshold,256])
histoSum = sum(histogram[0]*histogram[1][:-1])
print(histoSum)
print("time = %.2f ms" % ((time.time()-t0)*1000))
这行得通,我得到了大于所选阈值的像素总和 valus。
但是,我不确定这是 best/fastest 的方法。显然,阈值越大,动作就会越快。
有没有人知道如何使用更快的算法获得正确的结果?
给你:
import numpy as np
image = np.random.randint(0,256,(10,10))
threshold = 1
res = np.sum(image[image > threshold])
本次操作:
%%timeit
res = np.sum(image[image >=threshold])
需要 5.43 µs ± 137 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
.
虽然 OP 的方法从根本上来说是不准确的,但其基本思想仍可用于设计对整数数组(例如灰度图像)有效的方法:
def sum_gt_hist(arr, threshold):
values = np.arange(threshold, np.max(arr) + 1)
hist, edges = np.histogram(arr, values + 0.5)
return sum(values[1:] * hist)
然而,这并不理想,因为它比它应该的更复杂(np.histogram()
是一个相对复杂的函数,它计算的中间信息比需要的多得多)并且只适用于整数值。
在 中提出了一种更简单且仍然纯粹的 NumPy 方法:
import numpy as np
def sum_gt_np(arr, threshold):
return np.sum(arr[arr > threshold])
虽然以上是首选的仅限 NumPy 的解决方案,但使用基于 Numba 的简单解决方案可以获得更快的执行速度(和内存效率):
import numba as nb
@nb.njit
def sum_gt_nb(arr, threshold):
arr = arr.ravel()
result = 0
for x in arr:
if x > threshold:
result += x
return result
用代表图像的随机 100x100 数组对上述内容进行基准测试,将得到:
import numpy as np
np.random.seed(0)
arr = np.random.randint(0, 256, (100, 100)) # generate a random image
threshold = 253 # set a threshold
funcs = sum_gt_hist, sum_gt_np, sum_gt_nb
for func in funcs:
print(f"{func.__name__:16s}", end=' ')
print(func(arr, threshold), end=' ')
%timeit func(arr, threshold)
# sum_gt_hist 22397 355 µs ± 8.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# sum_gt_np 22397 10.1 µs ± 438 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# sum_gt_nb 22397 1.19 µs ± 33.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
这表明 sum_gt_nb()
比 sum_gt_np()
快得多,而 sum_gt_np()
又比 sum_gt_hist()
快得多。
我不知道这是否是最快的,但它非常简单。在 Python/OpenCV 中,阈值为零,只有低于阈值的像素将高于阈值的值保持为原始值。然后简单地计算不为零的值。
我创建了一个 100 像素宽的简单渐变图像,从顶部的 255 到底部的 0,灰度级增量为 1。
输入:
import cv2
import numpy as np
# read image
img = cv2.imread('ramp.png')
print(img.shape)
# convert img to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold to zero below threshold, but keep values above threshold
# note: to count all values of 254 and 255, use threshold at 253
thresh = cv2.threshold(gray, 253, 255, cv2.THRESH_TOZERO)[1]
# sum pixel values
# zero values don't contribute to sum
sum1 = np.sum(thresh)
print("actual count:", sum1)
# compute the expected count
sum2 = 100*254+100*255
print("computed count:", sum2)
# show results
cv2.imshow('thresh', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
actual count: 50900
computed count: 50900
我正在尝试找到最佳方法来检索大于某个阈值的像素 value 的总和。例如,如果我的阈值是 253,我有 10 个像素为 254,另外 10 个像素为 255,我希望得到 10*254 + 10*255 = 5090
- 超过阈值的像素的总强度。
我找到了一种方法 np.histogram
:
import cv2, time
import numpy as np
threshold = 1
deltaImg = cv2.imread('image.jpg')
t0=time.time()
histogram = np.histogram(deltaImg,256-threshold,[threshold,256])
histoSum = sum(histogram[0]*histogram[1][:-1])
print(histoSum)
print("time = %.2f ms" % ((time.time()-t0)*1000))
这行得通,我得到了大于所选阈值的像素总和 valus。 但是,我不确定这是 best/fastest 的方法。显然,阈值越大,动作就会越快。
有没有人知道如何使用更快的算法获得正确的结果?
给你:
import numpy as np
image = np.random.randint(0,256,(10,10))
threshold = 1
res = np.sum(image[image > threshold])
本次操作:
%%timeit
res = np.sum(image[image >=threshold])
需要 5.43 µs ± 137 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
.
虽然 OP 的方法从根本上来说是不准确的,但其基本思想仍可用于设计对整数数组(例如灰度图像)有效的方法:
def sum_gt_hist(arr, threshold):
values = np.arange(threshold, np.max(arr) + 1)
hist, edges = np.histogram(arr, values + 0.5)
return sum(values[1:] * hist)
然而,这并不理想,因为它比它应该的更复杂(np.histogram()
是一个相对复杂的函数,它计算的中间信息比需要的多得多)并且只适用于整数值。
在
import numpy as np
def sum_gt_np(arr, threshold):
return np.sum(arr[arr > threshold])
虽然以上是首选的仅限 NumPy 的解决方案,但使用基于 Numba 的简单解决方案可以获得更快的执行速度(和内存效率):
import numba as nb
@nb.njit
def sum_gt_nb(arr, threshold):
arr = arr.ravel()
result = 0
for x in arr:
if x > threshold:
result += x
return result
用代表图像的随机 100x100 数组对上述内容进行基准测试,将得到:
import numpy as np
np.random.seed(0)
arr = np.random.randint(0, 256, (100, 100)) # generate a random image
threshold = 253 # set a threshold
funcs = sum_gt_hist, sum_gt_np, sum_gt_nb
for func in funcs:
print(f"{func.__name__:16s}", end=' ')
print(func(arr, threshold), end=' ')
%timeit func(arr, threshold)
# sum_gt_hist 22397 355 µs ± 8.67 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
# sum_gt_np 22397 10.1 µs ± 438 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# sum_gt_nb 22397 1.19 µs ± 33.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
这表明 sum_gt_nb()
比 sum_gt_np()
快得多,而 sum_gt_np()
又比 sum_gt_hist()
快得多。
我不知道这是否是最快的,但它非常简单。在 Python/OpenCV 中,阈值为零,只有低于阈值的像素将高于阈值的值保持为原始值。然后简单地计算不为零的值。
我创建了一个 100 像素宽的简单渐变图像,从顶部的 255 到底部的 0,灰度级增量为 1。
输入:
import cv2
import numpy as np
# read image
img = cv2.imread('ramp.png')
print(img.shape)
# convert img to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold to zero below threshold, but keep values above threshold
# note: to count all values of 254 and 255, use threshold at 253
thresh = cv2.threshold(gray, 253, 255, cv2.THRESH_TOZERO)[1]
# sum pixel values
# zero values don't contribute to sum
sum1 = np.sum(thresh)
print("actual count:", sum1)
# compute the expected count
sum2 = 100*254+100*255
print("computed count:", sum2)
# show results
cv2.imshow('thresh', thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
actual count: 50900
computed count: 50900