Адаптивный алгоритм порога Брэдли

в настоящее время я работаю над реализацией алгоритма порога под названием Bradley Adaptive Thresholding.

я следовал в основном по двум ссылкам, чтобы выяснить, как реализовать этот алгоритм. Я также успешно смог реализовать два других алгоритма порога, в основном,метод Отсу и Сбалансированная Гистограмма.

вот две ссылки, которые я следовал, чтобы создать Bradley Adaptive Thresholding алгоритм.

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

Пример Адаптивного Порога Брэдли Github

вот раздел моего исходного кода в 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)]

и, наконец, вот входные и выходные изображения. Это те же изображения, которые используются в оригинальной исследовательской работе в первой ссылке, которую я вам предоставил. Примечание: выходное изображение почти полностью белое, и это может быть трудно увидеть, но я предоставил его в любом случае, если кто-то действительно хотел иметь его для справки.

Input ImageOutput Image

2 ответов


вы не можете создать интегральное изображение с PIL так, как вы это делаете, потому что изображение, в которое вы упаковываете данные, не может принимать значения более 255. Значения в интегральном изображении становятся очень большими, потому что они представляют собой суммы пикселей выше и слева (см. стр. 3 вашей белой книги ниже).

enter image description here

Они будут расти намного больше, чем 255, поэтому нужно 32 бита на пиксель для их хранения.

Вы можете проверить это создав изображение PIL в режиме "L", а затем установив пиксель на 1000000 или некоторое большое число. Затем, когда вы прочитаете значение, оно вернет 255.

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

EDIT: после прочтения документации PIL вы можете использовать PIL, если создадите свое интегральное изображение в режиме" I "вместо" L". Это должно обеспечить 32 бита на пиксель.

по этой причине я рекомендую Numpy вместо PIL.

Ниже приведена перезапись вашей пороговой функции с помощью Numpy вместо PIL, и я получаю правильный / ожидаемый результат. Обратите внимание, что я создаю свой интегральный образ с помощью массива uint32. Я использовал тот же самый пример C на Github, который вы использовали для своего перевода:

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


Я попытался повторно реализовать алгоритм, но без использования массива 1D и переключения на массив 2D numpy, чтобы лучше пойти с оригинальным алгоритмом, упомянутым в фактической статье. Im использует это для исследования анализа данных с использованием моделей глубокого обучения. Это реализация:

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

обратите внимание, что это часть класса. Он в основном имеет две переменные: _image, который указывает на фактическое изображение, и _pixels, который является классом PixelAccess, который позволяет получить доступ к пикселям в качестве заданных значений. Я используется деление пола ( / / ) вместо обычного деления ( / ), поскольку оно гарантирует, что все значения целочисленны. Пока результаты выглядят хорошо. Я использовал типы данных C для управления использованием памяти и сохранения значений в фиксированных положениях. Насколько я понимаю, это помогает контролировать небольшое количество данных для минимизации фрагментированных данных.

плюс это последний квартал 2018 года. Люди по-прежнему используют PIL, и, честно говоря, пока это делает свою работу. Это отлично подходит для цветового пространства RGB. Если вы используете это на общих изображениях вы можете преобразовать данные изображения в пространство RGB с помощью:

Image.convert('RGB')

где "Image" является экземпляром open image

это занимает пару секунд на изображениях, которые считаются HD, как 1200x700 изображений, но это заняло несколько долей секунды на образце изображения. Результате Изображения

надеюсь, это кому-то поможет.