«Растягивание» гистограмм (уровней) в Numpy, Python

У меня есть изображение в градациях серого, фон которого в цветовой шкале 0-255 имеет средне-белый цвет со средним значением цвета пикселя 246; передний план средне-серый со средним значением цвета пикселя 186.

Я хотел бы «сдвинуть» каждый пиксель выше 246 до 255, каждый пиксель ниже 186 до нуля и «растянуть» все между ними. Есть ли какой-либо готовый алгоритм/процесс для этого в numpy или python, или новые уровни/гистограмма должны быть рассчитаны «вручную» (как я делал до сих пор)?

Это эквивалентно открытию окна уровней в Gimp или Photoshop и выбору с помощью белой и черной пипетки соответственно светлой области, которую мы хотим сделать белой, и более темной области, которую мы хотим сделать черной: приложение изменяет уровни/ гистограмма («растягивает» значения между выбранными точками) соответственно.

Некоторые изображения того, что я пытаюсь:

«Растягивание» гистограмм (уровней) в Numpy, Python«Растягивание» гистограмм (уровней) в Numpy, Python«Растягивание» гистограмм (уровней) в Numpy, Python

Пожалуйста, покажите, что вы пробовали, возможно, с изображением.

amanb 07.04.2019 19:50

Происходит довольно много вещей, но я попытался суммировать все это в нескольких скриншотах.

Josef M. Schomburg 08.04.2019 08:46

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

Josef M. Schomburg 08.04.2019 08:55

Вы можете сделать именно то, что вы описали: (1) Порог для всех пикселей выше 246 до 255 (2) Порог для всех пикселей ниже 186 до нуля (3) Нормализовать все пиксели между ними до максимума 244. Я не думаю, что есть какой-либо готовый алгоритм для этого, так как все 3 шага для достижения желаемого результата тривиальны.

T A 10.04.2019 14:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
5
2 096
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вот один из способов -

def stretch(a, lower_thresh, upper_thresh):
    r = 255.0/(upper_thresh-lower_thresh+2) # unit of stretching
    out = np.round(r*(a-lower_thresh+1)).astype(a.dtype) # stretched values
    out[a<lower_thresh] = 0
    out[a>upper_thresh] = 255
    return out

Согласно OP, набор критериев был:

  • «сдвиньте» каждый пиксель выше 246 на 255, следовательно, 247 и выше должны стать 255.

  • каждый пиксель ниже 186 до zero, следовательно, 185 и ниже должен стать 0.

  • Следовательно, исходя из двух вышеупомянутых требований, 186 должно стать чем-то большим, чем 0, и так далее, пока 246 не станет меньше 255.

Кроме того, мы также можем использовать np.where, чтобы сделать его немного более компактным -

def stretch(a, lower_thresh, upper_thresh):
    r = 255.0/(upper_thresh-lower_thresh+2) # unit of stretching
    out = np.round(r*np.where(a>=lower_thresh,a-lower_thresh+1,0)).clip(max=255)
    return out.astype(a.dtype)

Пробный запуск -

# check out first row input, output for variations
In [216]: a
Out[216]: 
array([[186, 187, 188, 246, 247],
       [251, 195, 103,   9, 211],
       [ 21, 242,  36,  87,  70]], dtype=uint8)

In [217]: stretch(a, lower_thresh=186, upper_thresh=246)
Out[217]: 
array([[  4,   8,  12, 251, 255], 
       [255,  41,   0,   0, 107],
       [  0, 234,   0,   0,   0]], dtype=uint8)

Оба ответа хороши, но вы выбрали этот из-за его объяснительных качеств (для тех, кто изучает python).

Josef M. Schomburg 14.04.2019 14:03

Если ваше изображение uint8 и имеет типичный размер изображения, одним из эффективных методов является настройка таблицы поиска:

L, H = 186, 246
lut = np.r_[0:0:(L-1)*1j, 0.5:255.5:(H-L+3)*1j, 255:255:(255-H-1)*1j].astype('u1')

# example
from scipy.misc import face
f = face()

rescaled = lut[f]

Для небольших изображений быстрее (в моей настройке он пересекается около 100 000 пикселей шкалы серого) для прямого преобразования:

fsmall = (f[::16, ::16].sum(2)//3).astype('u1')

slope = 255/(H-L+2)
rescaled = ((1-L+0.5/slope+fsmall)*slope).clip(0, 255).astype('u1')

Другие вопросы по теме