Bradley Adaptive Threshold Algorithm

I am currently working on an implementation of a threshold algorithm called Bradley Adaptive Thresholding .

I followed mainly two links to figure out how to implement this algorithm. I was also able to successfully implement two other threshold algorithms, mainly the Otsu method and the balanced histogram threshold .

Here are two links I followed to create the Bradley Adaptive Thresholding algorithm.

http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.420.7883&rep=rep1&type=pdf

Github Adaptive Threshold Bradley Example

Here is a section of my Python source code where I run the algorithm and save the image. I use the Python Imaging Library and no other tools to accomplish what I want to do.

 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 = (is/2, i+s/2, js/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 

Here is a section of my code that illustrates the implementation of these set and get methods that you have not seen before.

 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)] 

And finally, here are the input and output images. These are the same images that were used in the original research work in the first link that I provided to you. Note. The output image is almost completely white and it can be difficult to see, but I still provided it in case someone really wants to get it for reference.

Input imageOutput image

+10
python python-imaging-library
source share
2 answers

You cannot create an integral image with PIL the way you do it, because the image into which you are packing data cannot take values ​​over 255. The values ​​in the integrated image become very large because they are the sum of the pixels above and to the left ( see page 3 of your white paper below).

enter image description here

They will grow much more than 255, so 32 bits per pixel are needed to store them.

You can verify this by creating a PIL image in L mode and then setting the pixel to 1,000,000 or more. Then, when you read back the value, it will return 255.

 >>> from PIL import Image >>> img = Image.new('L', (100,100)) >>> img.putpixel((0,0), 100000) >>> print(list(img.getdata())[0]) 255 

EDIT: After reading the PIL documentation, you can use PIL if you are creating your entire image in "I" mode instead of "L" mode. This should provide 32 bits per pixel.

For this reason, I recommend Numpy instead of PIL.

The following is a rewrite of your threshold function using Numpy instead of PIL, and I get the correct / expected result. Note that I am creating my whole image using the uint32 array. I used the same C example on Github that you used for your translation:

 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 

output

+11
source share

I tried to re-implement the algorithm, but without using a one-dimensional array and switching to a two-dimensional array, in order to better use the original algorithm mentioned in this article. I use this to research data analysis using deep learning models. This is the implementation:

 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() 

Please note that this is part of the class. It basically has two variables: _image, which indicates the actual image, and _pixels, which is the PixelAccess class, which provides access to pixels as setpoints. I used gender division (//) instead of normal division (/) because it ensures that all values ​​are integer. So far, the results look good. I used C data types to control memory usage and store values ​​in fixed positions. As far as I understand, this helps to control a small amount of data to minimize fragmented data.

Plus, this is the last quarter of 2018. People still use PIL, and frankly, it does it now. This works great for RGB color space. If you use this on generic images, you can convert the image data to RGB space using:

 Image.convert('RGB') 

where "Image" is an instance of an open image

For images that are considered to be 1200x700 HD images, it takes a few seconds, but on a sample image it takes only a few fractions of a second. Result Image

Hope this helps someone.

0
source share

All Articles