Bradley-Roth 自适应阈值算法 - 如何获得更好的性能?

2024-04-14

我有以下图像阈值代码,使用 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 个像素的平均值,我得到的时间会增加该图像需要一分钟。

如果我使用尺寸为 2592x1936 的图像,窗口大小仅为 5,则需要近 10 分钟才能完成。

那么,我怎样才能改善这些时间呢? numpy 数组会更快吗? im.getpixel 是否比将图像加载到像素访问模式更快?还有其他提高速度的技巧吗?提前致谢。


参考我们的评论,我在这里编写了该算法的 MATLAB 实现:从图像中的统一背景中提取页面 https://stackoverflow.com/questions/30487127/extract-a-page-from-a-uniform-background-in-an-image/30496377#30496377,而且在大图像上速度相当快。

如果您想更好地解释该算法,请参阅我的其他答案:Bradley 自适应阈值——困惑(问题) https://stackoverflow.com/questions/29593939/bradley-adaptive-thresholding-confused-questions/29599155#29599155。如果您想更好地理解我编写的代码,这可能是一个很好的起点。

由于 MATLAB 和 NumPy 相似,因此这是 Bradley-Roth 阈值算法的重新实现,但在 NumPy 中。我将 PIL 图像转换为 NumPy 数组,对此图像进行处理,然后转换回 PIL 图像。该函数接受三个参数: 灰度图像image,窗口的大小s和阈值t。该阈值与您所拥有的阈值不同,因为它完全遵循论文。门槛t is a 百分比每个像素窗口的总面积之和。如果总面积小于此阈值,则输出应该是黑色像素 - 否则它是白色像素。默认为s and t分别是列数除以 8 并四舍五入后的 15%:

import numpy as np
from PIL import Image

def bradley_roth_numpy(image, s=None, t=None):

    # Convert image to numpy array
    img = np.array(image).astype(np.float)

    # Default window size is round(cols/8)
    if s is None:
        s = np.round(img.shape[1]/8)

    # Default threshold is 15% of the total
    # area in the window
    if t is None:
        t = 15.0

    # Compute integral image
    intImage = np.cumsum(np.cumsum(img, axis=1), axis=0)

    # Define grid of points
    (rows,cols) = img.shape[:2]
    (X,Y) = np.meshgrid(np.arange(cols), np.arange(rows))

    # Make into 1D grid of coordinates for easier access
    X = X.ravel()
    Y = Y.ravel()

    # Ensure s is even so that we are able to index into the image
    # properly
    s = s + np.mod(s,2)

    # Access the four corners of each neighbourhood
    x1 = X - s/2
    x2 = X + s/2
    y1 = Y - s/2
    y2 = Y + s/2

    # Ensure no coordinates are out of bounds
    x1[x1 < 0] = 0
    x2[x2 >= cols] = cols-1
    y1[y1 < 0] = 0
    y2[y2 >= rows] = rows-1

    # Ensures coordinates are integer
    x1 = x1.astype(np.int)
    x2 = x2.astype(np.int)
    y1 = y1.astype(np.int)
    y2 = y2.astype(np.int)

    # Count how many pixels are in each neighbourhood
    count = (x2 - x1) * (y2 - y1)

    # Compute the row and column coordinates to access
    # each corner of the neighbourhood for the integral image
    f1_x = x2
    f1_y = y2
    f2_x = x2
    f2_y = y1 - 1
    f2_y[f2_y < 0] = 0
    f3_x = x1-1
    f3_x[f3_x < 0] = 0
    f3_y = y2
    f4_x = f3_x
    f4_y = f2_y

    # Compute areas of each window
    sums = intImage[f1_y, f1_x] - intImage[f2_y, f2_x] - intImage[f3_y, f3_x] + intImage[f4_y, f4_x]

    # Compute thresholded image and reshape into a 2D grid
    out = np.ones(rows*cols, dtype=np.bool)
    out[img.ravel()*count <= sums*(100.0 - t)/100.0] = False

    # Also convert back to uint8
    out = 255*np.reshape(out, (rows, cols)).astype(np.uint8)

    # Return PIL image back to user
    return Image.fromarray(out)


if __name__ == '__main__':
    img = Image.open('test.jpg').convert('L')
    out = bradley_roth_numpy(img)
    out.show()
    out.save('output.jpg')

如果需要,图像被读入并转换为灰度。将显示输出图像,并将其保存到您将脚本运行到名为的图像的同一目录中output.jpg。如果您想覆盖这些设置,只需执行以下操作:

out = bradley_roth_numpy(img, windowsize, threshold)

尝试这样做以获得良好的结果。使用默认参数并使用 IPython,我使用以下方法测量了平均执行时间timeit,这就是我从您在帖子中上传的图片中得到的信息:

In [16]: %timeit bradley_roth_numpy(img)
100 loops, best of 3: 7.68 ms per loop

这意味着在您上传的图像上重复运行此函数 100 次,最好的 3 次执行时间平均每次运行 7.68 毫秒。

当我设置阈值时,我也得到了这个图像:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Bradley-Roth 自适应阈值算法 - 如何获得更好的性能? 的相关文章

随机推荐