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

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

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

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

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

Пример Bradley Adaptive Thresholding 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)]

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

Исходное изображениеВыходное изображение


person BigBerger    schedule 07.04.2015    source источник
comment
Что не работает, правильная генерация выходного изображения? Есть ли у вас невизуальные тесты?   -  person Ashalynd    schedule 08.04.2015
comment
Да, правильное создание выходного изображения не работает, я использовал точно такое же изображение, которое исследовательская работа использует для своего теста, и выходное изображение полностью белое и не похоже на то, что делает выходное изображение исследовательской работы. Что касается невизуальных тестов, я не уверен, что вы имеете в виду.   -  person BigBerger    schedule 08.04.2015


Ответы (2)


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

введите здесь описание изображения

Они вырастут намного больше, чем 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

РЕДАКТИРОВАТЬ: после прочтения документации 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

выход

person derricw    schedule 07.04.2015
comment
Мне потребовалось некоторое время, чтобы принять этот ответ, так как я был занят, но да! Я не понимал, что они добавляют значения больше 255 и что интегральное изображение — это просто абстрактное представление данных, большое спасибо! - person BigBerger; 10.04.2015

Я попытался повторно реализовать алгоритм, но без использования 1D-массива и переключения на 2D-массив 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()

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

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

Image.convert('RGB')

где «Изображение» — экземпляр открытого изображения

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

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

person Simanto Rahman    schedule 20.09.2018