Bradley自适应阈值算法
Bradley adaptive thresholding algorithm
我目前正致力于实施一种名为 Bradley Adaptive Thresholding
的阈值算法。
我一直在关注两个 link 以弄清楚如何实现这个算法。我还成功地实现了另外两种阈值算法,主要是 Otsu's Method and Balanced Histogram Thresholding.
这是我为了创建 Bradley Adaptive Thresholding
算法而一直关注的两个 link。
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.7883&rep=rep1&type=pdf
Bradley Adaptive Thresholding Github Example
这是 Python
中我的源代码部分,我在其中 运行 算法和保存图像。我使用 Python Imaging Library
而没有其他工具来完成我想做的事情。
def get_bradley_binary(inp_im):
w, h = inp_im.size
s, t = (w / 8, 0.15)
int_im = Image.new('L', (w, h))
out_im = Image.new('L', (w, h))
for i in range(w):
summ = 0
for j in range(h):
index = j * w + i
summ += get_pixel_offs(inp_im, index)
if i == 0:
set_pixel_offs(int_im, index, summ)
else:
temp = get_pixel_offs(int_im, index - 1) + summ
set_pixel_offs(int_im, index, temp)
for i in range(w):
for j in range(h):
index = j * w + i
x1,x2,y1,y2 = (i-s/2, i+s/2, j-s/2, j+s/2)
x1 = 0 if x1 < 0 else x1
x2 = w - 1 if x2 >= w else x2
y1 = 0 if y1 < 0 else y1
y2 = h - 1 if y2 >= h else y2
count = (x2 - x1) * (y2 - y1)
a1 = get_pixel_offs(int_im, y2 * w + x2)
a2 = get_pixel_offs(int_im, y1 * w + x2)
a3 = get_pixel_offs(int_im, y2 * w + x1)
a4 = get_pixel_offs(int_im, y1 * w + x1)
summ = a1 - a2 - a3 + a4
temp = get_pixel_offs(inp_im, index)
if temp * count < summ * (1.0 - t):
set_pixel_offs(out_im, index, 0)
else:
set_pixel_offs(out_im, index, 255)
return out_im
这是我的代码部分,说明了您以前从未见过的这些 set 和 get 方法的实现。
def get_offs(image, x, y):
return y * image.size[0] + x
def get_xy(image, offs):
return (offs % image.size[0], int(offs / image.size[0]))
def set_pixel_xy(image, x, y, data):
image.load()[x, y] = data
def set_pixel_offs(image, offs, data):
x, y = get_xy(image, offs)
image.load()[x, y] = data
def get_pixel_offs(image, offs):
return image.getdata()[offs]
def get_pixel_xy(image, x, y):
return image.getdata()[get_offs(image, x, y)]
最后,这是输入和输出图像。这些图像与我提供给您的第一篇 link 中原始研究论文中使用的图像相同。注意:输出的图像几乎是全白的,可能很难看清,但我还是提供了它,以防有人真的想要它作为参考。
您不能像现在这样使用 PIL 创建积分图像,因为您将数据打包到其中的图像不能接受超过 255 的值。积分图像中的值变得非常大,因为它们是上方和左侧的像素(请参阅下面的白皮书第 3 页)。
它们将比 255 大得多,因此您需要每个像素 32 位来存储它们。
您可以通过在 "L" 模式下创建 PIL 图像然后将像素设置为 1000000 或某个较大的数字来对此进行测试。然后当你读回该值时,它将 return 255.
>>> from PIL import Image
>>> img = Image.new('L', (100,100))
>>> img.putpixel((0,0), 100000)
>>> print(list(img.getdata())[0])
255
编辑:阅读 PIL 文档后,如果您在 "I" 模式而不是 "L" 模式下创建积分图像,则可以使用 PIL。这应该为每个像素提供 32 位。
因此我推荐 Numpy 而不是 PIL。
下面是使用 Numpy 而不是 PIL 重写的阈值函数,我得到了 correct/expected 结果。请注意,我使用 uint32 数组创建积分图像。我在 Github 上使用了与您用于翻译的完全相同的 C 示例:
import numpy as np
def adaptive_thresh(input_img):
h, w = input_img.shape
S = w/8
s2 = S/2
T = 15.0
#integral img
int_img = np.zeros_like(input_img, dtype=np.uint32)
for col in range(w):
for row in range(h):
int_img[row,col] = input_img[0:row,0:col].sum()
#output img
out_img = np.zeros_like(input_img)
for col in range(w):
for row in range(h):
#SxS region
y0 = max(row-s2, 0)
y1 = min(row+s2, h-1)
x0 = max(col-s2, 0)
x1 = min(col+s2, w-1)
count = (y1-y0)*(x1-x0)
sum_ = int_img[y1, x1]-int_img[y0, x1]-int_img[y1, x0]+int_img[y0, x0]
if input_img[row, col]*count < sum_*(100.-T)/100.:
out_img[row,col] = 0
else:
out_img[row,col] = 255
return out_img
我尝试重新实现该算法,但没有使用一维数组并切换到二维 numpy 数组以更好地与实际论文中提到的原始算法一致。我用它来研究使用深度学习模型的数据分析。这是实现:
import numpy, gc
from ctypes import *
def adaptive_threshold(self):
gc.collect()
gc.disable()
w, h = self._image.width, self._image.height
s, t = w//8, 0.15
summ = c_uint32(0)
count = c_uint32(0)
pixels = self._pixels
int_img = numpy.ndarray(shape=(w, h), dtype=c_int64)
for i in range(w):
summ.value = 0
for j in range(h):
summ.value += sum(pixels[i, j])
if i != 0:
int_img[i, j] = int_img[i - 1, j] + summ.value
else:
int_img[i, j] = summ.value
x1, x2, y1, y2 = c_uint16(0), c_uint16(0), c_uint16(0), c_uint16(0)
for i in range(w):
for j in range(h):
x1.value = max(i - s // 2, 0)
x2.value = min(i + s // 2, w - 1)
y1.value = max(j - s // 2, 0)
y2.value = min(j + s // 2, h - 1)
count.value = (x2.value - x1.value) * (y2.value - y1.value)
summ.value = int_img[x2.value][y2.value] - int_img[x1.value][y2.value] - \
int_img[x2.value][y1.value] + int_img[x1.value][y1.value]
if sum(pixels[i, j]) * count.value < summ.value * (1.0 - t):
pixels[i, j] = 0, 0, 0
else:
pixels[i, j] = 255, 255, 255
gc.enable()
注意这是 class 的一部分。它主要有两个变量,_image 指向实际图像,_pixels 是 PixelAccess class 允许访问像素作为设置值。我使用底除法 (//) 而不是常规除法 (/),因为它确保所有值都是整数。到目前为止,结果看起来不错。我使用 C 数据类型来控制内存使用并将值保持在固定位置。我的理解是,控制少量数据分配有助于最大程度地减少碎片化数据。
此外,这是 2018 年的最后一个季度。人们仍在使用 PIL,坦率地说,它现在可以完成这项工作。这对 RGB 颜色 space 非常有效。如果您在通用图像上使用它,您可能需要使用以下方法将图像数据转换为 RGB space:
Image.convert('RGB')
其中 'Image' 是打开图像的实例
处理 1200x700 等高清图像需要几秒钟,但处理示例图像只需要几分之一秒。
Result Image
希望这对某人有所帮助。
我目前正致力于实施一种名为 Bradley Adaptive Thresholding
的阈值算法。
我一直在关注两个 link 以弄清楚如何实现这个算法。我还成功地实现了另外两种阈值算法,主要是 Otsu's Method and Balanced Histogram Thresholding.
这是我为了创建 Bradley Adaptive Thresholding
算法而一直关注的两个 link。
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.7883&rep=rep1&type=pdf
Bradley Adaptive Thresholding Github Example
这是 Python
中我的源代码部分,我在其中 运行 算法和保存图像。我使用 Python Imaging Library
而没有其他工具来完成我想做的事情。
def get_bradley_binary(inp_im):
w, h = inp_im.size
s, t = (w / 8, 0.15)
int_im = Image.new('L', (w, h))
out_im = Image.new('L', (w, h))
for i in range(w):
summ = 0
for j in range(h):
index = j * w + i
summ += get_pixel_offs(inp_im, index)
if i == 0:
set_pixel_offs(int_im, index, summ)
else:
temp = get_pixel_offs(int_im, index - 1) + summ
set_pixel_offs(int_im, index, temp)
for i in range(w):
for j in range(h):
index = j * w + i
x1,x2,y1,y2 = (i-s/2, i+s/2, j-s/2, j+s/2)
x1 = 0 if x1 < 0 else x1
x2 = w - 1 if x2 >= w else x2
y1 = 0 if y1 < 0 else y1
y2 = h - 1 if y2 >= h else y2
count = (x2 - x1) * (y2 - y1)
a1 = get_pixel_offs(int_im, y2 * w + x2)
a2 = get_pixel_offs(int_im, y1 * w + x2)
a3 = get_pixel_offs(int_im, y2 * w + x1)
a4 = get_pixel_offs(int_im, y1 * w + x1)
summ = a1 - a2 - a3 + a4
temp = get_pixel_offs(inp_im, index)
if temp * count < summ * (1.0 - t):
set_pixel_offs(out_im, index, 0)
else:
set_pixel_offs(out_im, index, 255)
return out_im
这是我的代码部分,说明了您以前从未见过的这些 set 和 get 方法的实现。
def get_offs(image, x, y):
return y * image.size[0] + x
def get_xy(image, offs):
return (offs % image.size[0], int(offs / image.size[0]))
def set_pixel_xy(image, x, y, data):
image.load()[x, y] = data
def set_pixel_offs(image, offs, data):
x, y = get_xy(image, offs)
image.load()[x, y] = data
def get_pixel_offs(image, offs):
return image.getdata()[offs]
def get_pixel_xy(image, x, y):
return image.getdata()[get_offs(image, x, y)]
最后,这是输入和输出图像。这些图像与我提供给您的第一篇 link 中原始研究论文中使用的图像相同。注意:输出的图像几乎是全白的,可能很难看清,但我还是提供了它,以防有人真的想要它作为参考。
您不能像现在这样使用 PIL 创建积分图像,因为您将数据打包到其中的图像不能接受超过 255 的值。积分图像中的值变得非常大,因为它们是上方和左侧的像素(请参阅下面的白皮书第 3 页)。
它们将比 255 大得多,因此您需要每个像素 32 位来存储它们。
您可以通过在 "L" 模式下创建 PIL 图像然后将像素设置为 1000000 或某个较大的数字来对此进行测试。然后当你读回该值时,它将 return 255.
>>> from PIL import Image
>>> img = Image.new('L', (100,100))
>>> img.putpixel((0,0), 100000)
>>> print(list(img.getdata())[0])
255
编辑:阅读 PIL 文档后,如果您在 "I" 模式而不是 "L" 模式下创建积分图像,则可以使用 PIL。这应该为每个像素提供 32 位。
因此我推荐 Numpy 而不是 PIL。
下面是使用 Numpy 而不是 PIL 重写的阈值函数,我得到了 correct/expected 结果。请注意,我使用 uint32 数组创建积分图像。我在 Github 上使用了与您用于翻译的完全相同的 C 示例:
import numpy as np
def adaptive_thresh(input_img):
h, w = input_img.shape
S = w/8
s2 = S/2
T = 15.0
#integral img
int_img = np.zeros_like(input_img, dtype=np.uint32)
for col in range(w):
for row in range(h):
int_img[row,col] = input_img[0:row,0:col].sum()
#output img
out_img = np.zeros_like(input_img)
for col in range(w):
for row in range(h):
#SxS region
y0 = max(row-s2, 0)
y1 = min(row+s2, h-1)
x0 = max(col-s2, 0)
x1 = min(col+s2, w-1)
count = (y1-y0)*(x1-x0)
sum_ = int_img[y1, x1]-int_img[y0, x1]-int_img[y1, x0]+int_img[y0, x0]
if input_img[row, col]*count < sum_*(100.-T)/100.:
out_img[row,col] = 0
else:
out_img[row,col] = 255
return out_img
我尝试重新实现该算法,但没有使用一维数组并切换到二维 numpy 数组以更好地与实际论文中提到的原始算法一致。我用它来研究使用深度学习模型的数据分析。这是实现:
import numpy, gc
from ctypes import *
def adaptive_threshold(self):
gc.collect()
gc.disable()
w, h = self._image.width, self._image.height
s, t = w//8, 0.15
summ = c_uint32(0)
count = c_uint32(0)
pixels = self._pixels
int_img = numpy.ndarray(shape=(w, h), dtype=c_int64)
for i in range(w):
summ.value = 0
for j in range(h):
summ.value += sum(pixels[i, j])
if i != 0:
int_img[i, j] = int_img[i - 1, j] + summ.value
else:
int_img[i, j] = summ.value
x1, x2, y1, y2 = c_uint16(0), c_uint16(0), c_uint16(0), c_uint16(0)
for i in range(w):
for j in range(h):
x1.value = max(i - s // 2, 0)
x2.value = min(i + s // 2, w - 1)
y1.value = max(j - s // 2, 0)
y2.value = min(j + s // 2, h - 1)
count.value = (x2.value - x1.value) * (y2.value - y1.value)
summ.value = int_img[x2.value][y2.value] - int_img[x1.value][y2.value] - \
int_img[x2.value][y1.value] + int_img[x1.value][y1.value]
if sum(pixels[i, j]) * count.value < summ.value * (1.0 - t):
pixels[i, j] = 0, 0, 0
else:
pixels[i, j] = 255, 255, 255
gc.enable()
注意这是 class 的一部分。它主要有两个变量,_image 指向实际图像,_pixels 是 PixelAccess class 允许访问像素作为设置值。我使用底除法 (//) 而不是常规除法 (/),因为它确保所有值都是整数。到目前为止,结果看起来不错。我使用 C 数据类型来控制内存使用并将值保持在固定位置。我的理解是,控制少量数据分配有助于最大程度地减少碎片化数据。
此外,这是 2018 年的最后一个季度。人们仍在使用 PIL,坦率地说,它现在可以完成这项工作。这对 RGB 颜色 space 非常有效。如果您在通用图像上使用它,您可能需要使用以下方法将图像数据转换为 RGB space:
Image.convert('RGB')
其中 'Image' 是打开图像的实例
处理 1200x700 等高清图像需要几秒钟,但处理示例图像只需要几分之一秒。 Result Image
希望这对某人有所帮助。