我有以下图像阈值处理代码,使用Bradley-Roth图像阈值方法。
from PIL import Image
import copy
import time
def bradley_threshold(image, threshold=75, windowsize=5):
ws = windowsize
image2 = copy.copy(image).convert('L')
w, h = image.size
l = image.convert('L').load()
l2 = image2.load()
threshold /= 100.0
for y in xrange(h):
for x in xrange(w):
#find neighboring pixels
neighbors =[(x+x2,y+y2) for x2 in xrange(-ws,ws) for y2 in xrange(-ws, ws) if x+x2>0 and x+x2<w and y+y2>0 and y+y2<h]
#mean of all neighboring pixels
mean = sum([l[a,b] for a,b in neighbors])/len(neighbors)
if l[x, y] < threshold*mean:
l2[x,y] = 0
else:
l2[x,y] = 255
return image2
i = Image.open('test.jpg')
windowsize = 5
bradley_threshold(i, 75, windowsize).show()
当windowsize
较小且图像较小时,此方法工作得很好。我一直在使用这张图片进行测试:
当使用窗口大小为5的时候,我遇到了大约5到6秒的处理时间,但是如果我将窗口大小增加到20,并且算法在每个方向上检查20个像素的平均值,我就会得到该图像的时间超过1分钟。
如果我使用一个大小为2592x1936的图像,而窗口大小仅为5,则需要近10分钟才能完成。
那么,我该如何改善这些时间呢?numpy数组会更快吗?im.getpixel是否比将图像加载到像素访问模式更快?有没有其他的提速小贴士?提前谢谢。
发布于 2015-10-13 07:52:38
使用%prun
在IPython中分析您的代码会产生如下结果:
ncalls tottime percall cumtime percall filename:lineno(function)
50246 2.009 0.000 2.009 0.000 <ipython-input-78-b628a43d294b>:15(<listcomp>)
50246 0.587 0.000 0.587 0.000 <ipython-input-78-b628a43d294b>:17(<listcomp>)
1 0.170 0.170 2.829 2.829 <ipython-input-78-b628a43d294b>:5(bradley_threshold)
50246 0.058 0.000 0.058 0.000 {built-in method sum}
50257 0.004 0.000 0.004 0.000 {built-in method len}
也就是说,几乎所有的运行时间都是由Python循环(慢)和非向量化算法(慢)造成的。因此,如果您使用numpy数组重写,我希望会有很大的改进;或者,如果您不知道如何向量化您的代码,则可以使用cython。
发布于 2016-05-26 19:44:07
好的,我来晚了一点。不管怎样,让我分享一下我的想法:
你可以通过使用动态编程来计算均值来加速它,但是让scipy和numpy来做所有的脏活要容易得多,而且速度也快得多。(请注意,我在代码中使用了Python3,因此在代码中将xrange更改为range )。
#!/usr/bin/env python3
import numpy as np
from scipy import ndimage
from PIL import Image
import copy
import time
def faster_bradley_threshold(image, threshold=75, window_r=5):
percentage = threshold / 100.
window_diam = 2*window_r + 1
# convert image to numpy array of grayscale values
img = np.array(image.convert('L')).astype(np.float) # float for mean precision
# matrix of local means with scipy
means = ndimage.uniform_filter(img, window_diam)
# result: 0 for entry less than percentage*mean, 255 otherwise
height, width = img.shape[:2]
result = np.zeros((height,width), np.uint8) # initially all 0
result[img >= percentage * means] = 255 # numpy magic :)
# convert back to PIL image
return Image.fromarray(result)
def bradley_threshold(image, threshold=75, windowsize=5):
ws = windowsize
image2 = copy.copy(image).convert('L')
w, h = image.size
l = image.convert('L').load()
l2 = image2.load()
threshold /= 100.0
for y in range(h):
for x in range(w):
#find neighboring pixels
neighbors =[(x+x2,y+y2) for x2 in range(-ws,ws) for y2 in range(-ws, ws) if x+x2>0 and x+x2<w and y+y2>0 and y+y2<h]
#mean of all neighboring pixels
mean = sum([l[a,b] for a,b in neighbors])/len(neighbors)
if l[x, y] < threshold*mean:
l2[x,y] = 0
else:
l2[x,y] = 255
return image2
if __name__ == '__main__':
img = Image.open('test.jpg')
t0 = time.process_time()
threshed0 = bradley_threshold(img)
print('original approach:', round(time.process_time()-t0, 3), 's')
threshed0.show()
t0 = time.process_time()
threshed1 = faster_bradley_threshold(img)
print('w/ numpy & scipy :', round(time.process_time()-t0, 3), 's')
threshed1.show()
这使得它在我的机器上运行得更快:
$ python3 bradley.py
original approach: 3.736 s
w/ numpy & scipy : 0.003 s
注:请注意,我在scipy中使用的均值在边界上的表现与您的代码中的均值略有不同(对于均值计算窗口不再完全包含在图像中的位置)。然而,我认为这不应该是一个问题。
另一个较小的区别是,来自for循环的窗口并不完全位于像素的中心,因为xrange(-ws,ws)与ws=5的偏移量产生-5,-4-,...,3,4,并导致平均值为-0.5。这可能不是故意的。
https://stackoverflow.com/questions/33091755
复制相似问题