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 шт):
Найдите максимальное и минимальное значение результата gradmin, gradmax, и отнормируйте текущий диапазон на диапазон 0..1
newvalue = (value - gradmin) / (gradmax - gradmin)
В opencv есть функция normalize - посмотрите, в pillow тоже может быть что-то подобное

