Изменение контраста в Numpy

Я хочу написать чистую функцию Numpy, чтобы изменить контрастность изображения RGB (представленного как массив Numpy *****), однако функция, которую я написал, не работает, и я не понимаю, почему.

Вот пример изображения:

И вот функция, которая использует PIL и отлично работает:

def change_contrast(img, factor):
 def contrast(pixel):
 return 128 + factor * (pixel - 128)
 return img.point(contrast)

from PIL import Image

img = Image.fromarray(img.astype(np.*****))
img1 = change_contrast(img, factor=2.0)

Вывод:

Теперь вот чистая функция Numpy, которая, на мой взгляд, делает то же самое, что и другая функция выше, но она не работает вообще:

def change_contrast2(img, factor):
 return 128 + factor * (img - 128)

img1 = change_contrast2(img, factor=2.0)

где img - массив Numpy. Вывод:

Я не понимаю, что происходит и будет рад любым намекам!

2 ответа

То, что вы видите, - это недостижение целых чисел без знака:

>>> a = np.array((64, 128, 192), dtype=np.*****)
>>> a
array([ 64, 128, 192], dtype=*****)
>>> a-128
array([192, 0, 64], dtype=*****) # note the "wrong" value at pos 0

Один из способов избежать этого - принуждение или продвижение по типу:

factor = float(factor)
np.clip(128 + factor * img - factor * 128, 0, 255).astype(np.*****)

Так как фактором является float, то dtype factor * img продукта factor * img повышается до плавающего. Поскольку float может обрабатывать отрицательные числа, это устраняет нижний поток.

Чтобы иметь возможность конвертировать обратно в ***** мы ***** диапазон, который может быть выражен этим типом.


Попробуйте это, создайте свой массив numpy с типом int32 и выполните операцию контрастности. Затем перед построением изображения используйте следующую функцию, чтобы преобразовать ее в *****. Как отметил Павел в комментариях, проблема в том, что numpy обертывается с помощью over/underflows.

def as_*****(img) :
 latch = np.zeros_like(img)
 latch[:] = 255
 zeros = np.zeros_like(img)

 d = np.maximum(zeros, img)
 d = np.minimum(latch, d)

 return np.asarray(d, dtype='*****')

Вероятно, есть более эффективный способ сделать это, но он работает.

licensed under cc by-sa 3.0 with attribution.