NumPy + Pillow для обнаружения контурных перепадов на изображении

На этот небольшой опыт программирования меня вдохновила книга "Цифровая обработка изображения" Гонсалес Р. а именно раздел 10.2.5 Простые методы обнаружения контурных перепадов и я решил это все реализовать на Python. Отправным пунктом стало это пояснение введите сюда описание изображения

Загружаем изображение, RGB переводим в режим L, теперь каждый пиксель это число в интервале [0,255], приводим к интервалу [0,1] просто разделив на 255

from PIL import Image
import numpy as np
img = Image.open("su3216.jpg").convert('L')
img_massiv = (1 / 255) * np.asarray(img)

Далее, для того что бы вычислить градиент в точке(пикселе), нужно взять окрестность (маску) 3x3 и воспользоваться оператором Превитта или оператором Собеля

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

И тогда градиент в пикселе z5 вычисляется по формуле введите сюда описание изображения

В коде я решил это просто с помощью вложенных циклов, изображение для пробы скачал 128x128 пикселей

for i in range(1, 127):
   for j in range(1, 127):
     img_massiv[i][j] = (img_massiv[i + 1][j - 1] + img_massiv[i + 1][j] + img_massiv[i + 
         1][j + 1]) - (img_massiv[i - 1][j - 1] + img_massiv[i - 1][j] + img_massiv[i - 1][j + 1])

Но дальше возник ступор, Гонсалес как то выводит этот градиент в виде изображения введите сюда описание изображения

Но как это сделать, если градиенты пикселей выходят за пределы интервала [0,1] ну естественно, они еще и отрицательные могут быть

from PIL import Image
import numpy as np
img = Image.open("su3216.jpg").convert('L') #открыть изображение (127x127) и преобразовать к оттенкам серого интервал [0.255]
img_massiv = (1 / 255) * np.asarray(img)# преобразовать изображение в массив, привести значение яркости к интервалу [0.1]
new_img_massiv = np.zeros((126, 126)) #новый массив для изображения, так как для крайних пикселей градиент не посчитаешь
for i in range(1, 127):
    for j in range(1, 127):
        a = img_massiv[i + 1][j - 1] + img_massiv[i + 1][j] + img_massiv[i + 1][j + 1]
        b = img_massiv[i - 1][j - 1] + img_massiv[i - 1][j] + img_massiv[i - 1][j + 1]
        new_img_massiv[i-1][j-1] = a-b # градиент пикселя
        if new_img_massiv[i-1][j-1] < 0: #если градиент отрицательный
            new_img_massiv[i - 1][j - 1] = -1*new_img_massiv[i-1][j-1]
        if new_img_massiv[i-1][j-1] > 1: #если градиент больше 1
            new_img_massiv[i - 1][j - 1] = 1

img = Image.fromarray(new_img_massiv, mode="L") #обратно массив в изображение
img.show()

Если кто знает, подскажите, почему не работает?

В общем, как оказалось есть еще книга Гонсалес Р.С. и др. Цифровая обработка изображений в среде MATLAB. В которой сам Гонсалес вышеприведенные алгоритмы решает при помощи встроенной функции edge пакета Matlab. Все оказалось гораздо проще, чем думалось, но все равно интересно было реализовать данную функцию.

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

В общем я разобрался в чем ошибка, не нужно преобразовывать изображение в массив, что то с ним делать, потом обратно преобразовывать в изображение, потому что координаты изображение в Pillow отсчитываются от верхнего левого угла. Можно просто считывать цвет пикселя и записывать цвет пикселя при помощи методов Pillow:

img.getpixel()
img.putpixel()

Но потом я наткнулся на видео https://www.youtube.com/watch?v=Tit5EFDjiCs в котором автор считает градиент по X как разность яркости пикселя справа и яркость текущего пикселя, а градиент по Y как разность яркости вышележащего пикселя и яркость текущего пикселя. В питоне реализовал оба алгоритма, и что получилось, у алгоритма с Ютуба гораздо лучше визуальный результат (оператор Превитта от учебника Гонсалеса справа) введите сюда описание изображения И я пришел к выводу, что Гонсалес в учебнике описывает данные алгоритмы не реализовывая их в программном виде, а использует готовую функцию edge из пакета Matlab, поэтому у него и хорошие иллюстрации к данным примеры.


Ответы (1 шт):

Автор решения: MBo

Найдите максимальное и минимальное значение результата gradmin, gradmax, и отнормируйте текущий диапазон на диапазон 0..1

newvalue = (value - gradmin) / (gradmax - gradmin)

В opencv есть функция normalize - посмотрите, в pillow тоже может быть что-то подобное

→ Ссылка