Проблемы с использованием грубого алгоритма оттенков серого?
поэтому я создал несколько программ для редактирования фотографий в python используя PIL и один из них преобразовывал изображение в оттенки серого (я избегаю использования каких-либо функций из PIL).
алгоритм, который я использовал, прост: для каждого пикселя (глубина цвета равна 24) я рассчитал среднее значение R,G и B значения и установите значения RGB в это среднее значение.
моя программа производила изображения в оттенках серого, которые казались точными, но я было интересно, если бы я использовал правильный алгоритм, и я наткнулся ответ к вопросу, где кажется, что "правильный" алгоритм должен вычислить 0.299 R + 0.587 G + 0.114 B.
я решил сравнить свою программу с этим алгоритмом. Я создал изображение в оттенках серого с помощью моей программы и еще один (используя тот же вход) от веб-сайт онлайн (лучший результат Google для 'image to grayscale'.
невооруженным глазом мне показалось, что они совершенно одинаковые, а если и были какие-то изменения, я их не видел. Однако, я решил использовать этот сайт (лучший результат Google для 'compare two images online') для сравнения моих изображений в оттенках серого. Оказалось, что глубоко в пикселях они имели небольшие вариации, но ни одна из них не была заметна человеческому глазу с первого взгляда (различия могут быть замечены, но обычно только тогда, когда изображения накладываются друг на друга или переключаются между ними в течение миллисекунд).
мои вопросы (первый главный вопрос):
- есть ли какие-либо недостатки в использовании моего "грубого" алгоритма оттенков серого?
- есть ли у кого-нибудь входные изображения, где мой алгоритм оттенков серого будет создавать заметно отличное изображение от того, которое было бы "правильным"?
- есть ли какие-либо комбинации цветов/RBG, для которых мой алгоритм также не будет работать?
мой ключевой кусок кода (если необходимо):
def greyScale(pixelTuple):
return tuple([round(sum(pixelTuple) / 3)] * 3)
"правильный" алгоритм (который, кажется, сильно весит зеленый):
def greyScale(pixelTuple):
return tuple([round(0.299 * pixelTuple[0] + 0.587 * pixelTuple[1] + 0.114 * pixelTuple[2])] * 3)
изображение в оттенках серого мой алгоритм производит:

изображение в оттенках серого, которое является "правильным":

когда изображения в оттенках серого сравниваются в интернете (выделены красным цветом различия, используя пух Десять%):

несмотря на различия в пикселях, выделенных выше, изображения в оттенках серого выше выглядят почти одинаково (по крайней мере, для меня).
кроме того, что касается моего первого вопроса, если кому-то интересно, этот сайт сделал некоторый анализ различных алгоритмов для преобразования в оттенки серого, а также имеет некоторые дополнительные алгоритмы.
EDIT:
в ответ на ответ @Szulat, мой алгоритм на самом деле вместо этого создает это изображение (игнорируйте плохую обрезку, исходное изображение имело три круга, но мне нужен был только первый):
в случае, если люди задаются вопросом, какова причина преобразования в оттенки серого (поскольку кажется, что алгоритм зависит от цели), я просто делаю некоторые простые инструменты для редактирования фотографий в python Так что я могу иметь мини-Photoshop и не нужно полагаться на интернет для применения фильтров и эффектов.
причина для щедрости: различные ответы здесь охватывают различные вещи, которые все актуальны и полезны. Это делает его довольно трудно выбрать, какой ответ принять. Я начал щедрость, потому что мне нравится несколько ответов, перечисленных здесь, но также потому, что было бы неплохо иметь один ответ, который охватывает все, что мне нужно для этого вопроса.
8 ответов:
изображение очень похоже, но ваш глаз может сказать разницу, особенно если вы поставите один на место другого:
например, вы можете заметить, что цветы в фоновом режиме выглядят ярче в усреднении преобразования.
дело не в том, что есть что-то внутренне "плохое" в усреднении трех каналов. Причина этой формулы в том, что мы не воспринимаем красный, зеленый и синий одинаково, поэтому их вклад в интенсивность изображения в оттенках серого не должен быть одинаковым; поскольку мы воспринимаем зеленый цвет более интенсивно, зеленые пиксели должны выглядеть ярче в оттенках серого. Однако,как прокомментировал Марк нет уникального идеального преобразования в оттенки серого, так как мы видим в цвете, и в любом случае видение каждого немного отличается, поэтому любая формула просто попытается сделать приближение, чтобы интенсивность пикселей чувствовала себя "правильной" для большинства людей.
самый очевидный пример:
Оригинал
обесцвеченный в Gimp (режим легкости-это то, что делает ваш алгоритм)
обесцвеченные в Gimp (режим яркости-это то, что делают наши глаза)
итак, не усредняйте RGB. Усреднение RGB просто неправильно!
(хорошо, вы правы, усреднение может быть действительным в некоторых неясные приложения, даже если они не имеют физического или физиологического значения, когда значения RGB рассматриваются как цвет. Кстати, "обычный" способ выполнения взвешенного усреднения также неверен более тонким способом из-за гаммы. sRGB должен быть сначала линеаризован, а затем конечный результат преобразован обратно в sRGB (что было бы эквивалентно извлечению компонента L в цветовом пространстве лаборатории))
вы можете использовать любое уравнение преобразования, масштаб, линейность. Вы нашли:
I = 0.299 R + 0.587 G + 0.114 Bоснован на среднем человеческом глазе "средний" первичный цвет (R,G,B) чувствительность восприятия (по крайней мере,для периода времени и населения/HW он был создан; имейте в виду, что эти стандарты были созданы до LED, TFT и т. д. просеивание.)
есть несколько проблем, с которыми вы боретесь:
наши глаза не то же самое
все люди не воспринимают цвета одинаково. Существуют большие различия между полами и меньшие также между регионами; даже поколение и возраст играют определенную роль. Таким образом, даже среднее значение должно обрабатываться как "среднее".
мы имеем различную чувствительность к интенсивности света через видимый спектр. Наиболее чувствительным цветом является зеленый (отсюда и наибольший вес на нем). Но это кривая XYZ пики могут быть на различных длинах волны для разные люди (как и я, я немного сдвинул их, вызвав разницу в распознавании определенных длин волн, таких как некоторые оттенки Аквы - некоторые видят их как зеленые, некоторые как синие, даже если ни у кого из них нет инвалидности по дальтонизму или что-то еще).
мониторы не используют одинаковые длины волн и спектральную дисперсию
Так что если вы берете 2 разных монитора, они могут использовать немного разные длины волн для R, G, B или даже разной ширины спектрального фильтра (просто используйте спектроскоп и смотрите). Да, они должны быть "нормализованы" HW, но это не то же самое, что использование нормализованных длин волн. Это похоже на проблемы с использованием RGB против источников света спектра белого шума.
монитор линейность
люди не видят в линейном масштабе: мы обычно логарифмические / экспоненциальные (зависит от того, как вы на это смотрите), поэтому да, мы можем нормализовать это с помощью HW (или даже SW), но проблема в том, если мы линеаризовать для одного человека это означает, что мы повредить его еще.
Если взять все это вместе, вы можете использовать средние показатели ... или специальное (и дорогое) оборудование для измерения/нормализации по отношению к какому-либо стандарту или откалиброванному человеку (зависит от отрасли).
но это слишком много, чтобы справиться в домашних условиях, поэтому оставьте все это для промышленности и используйте веса для "среднего", как и большинство в мире... К счастью, наш мозг может обрабатывать его, как вы не можете увидеть разницу, если вы не начнете сравнивать оба изображения бок о бок или в анимации :). Поэтому я (хотел бы) сделать:
I = 0.299 R + 0.587 G + 0.114 B R = I G = I B = I
существует много различных методов преобразования в оттенки серого, и они дают разные результаты, хотя различия могут быть легче увидеть с различными цветными изображениями ввода.
Как мы на самом деле не видим в оттенках серого,"лучший" метод несколько зависит от приложения и несколько в глазах смотрящего.
альтернативная формула, на которую вы ссылаетесь, основана на том, что человеческий глаз более чувствителен к изменениям зеленых тонов и поэтому придание им большего веса-аналогично массиву Байера в камере, где есть 2 зеленых пикселя для каждого красного и синего. Wiki-Bayer array
есть много формул для яркости, в зависимости от цвета R,G, B праймериз:
Rec.601/NTSC: Y = 0.299*R + 0.587*G + 0.114*B , Rec.709/EBU: Y = 0.213*R + 0.715*G + 0.072*B , Rec.2020/UHD: Y = 0.263*R + 0.678*G + 0.059*B .как говорится, вы, вероятно, вычисляете яркость, а не яркость, поэтому формулы все равно неверны. Для постоянной яркости вы должны преобразовать в линейный свет
R = R' ^ 2.4 , G = G' ^ 2.4 , B = B' ^ 2.4 ,примените формулу яркости и преобразуйте обратно в гамма-домен
Y' = Y ^ (1/2.4) .кроме того, учтите, что преобразование трехмерного цветового пространства в 1D количество теряет 2/3 информации, которая может укусить вас в следующих шагах обработки. В зависимости от проблемы иногда лучше использовать другую формулу, например V = MAX(R, G,B) (из цветового пространства HSV).
откуда я знаю? Я последователь и друг доктора Пойнтона.
ответы, достаточно, но я хочу обсудить немного больше на эту тему в другом ключе.
Так как я изучил цифровую живопись для интереса, чаще всего я использую ВПГ.
он гораздо более управляем для использования HSV во время рисования, но держите его коротким, главным моментом является s: насыщенность, отделяющая концепцию цвета от света. И превращение S в 0-это уже "компьютерная" серая шкала изображения.
from PIL import Image import colorsys def togrey(img): if isinstance(img,Image.Image): r,g,b = img.split() R = [] G = [] B = [] for rd,gn,bl in zip(r.getdata(),g.getdata(),b.getdata()) : h,s,v = colorsys.rgb_to_hsv(rd/255.,gn/255.,bl/255.) s = 0 _r,_g,_b = colorsys.hsv_to_rgb(h,s,v) R.append(int(_r*255.)) G.append(int(_g*255.)) B.append(int(_b*255.)) r.putdata(R) g.putdata(G) b.putdata(B) return Image.merge('RGB',(r,g,b)) else: return None a = Image.open('../a.jpg') b = togrey(a) b.save('../b.jpg')этот метод действительно зарезервирован "яркий" оригинальный цвет. Однако,без учета того, как человеческий глаз обрабатывает данные.
в ответ на ваш главный вопрос, есть недостатки в использовании единой мерой серый. Это зависит от того, что вы хотите от вашего изображения. Например, если у вас есть цветной текст на белом фоне, если вы хотите, чтобы текст выделялся, вы можете использовать минимум значений r, g, b в качестве меры. Но если у вас есть черный текст на цветном фоне, вы можете использовать максимум значений для того же результата. В моем программном обеспечении я предлагаю вариант максимального, минимального или медианного значения для пользователя, чтобы выбирать. Результаты на непрерывных изображениях тона также освещают. В ответ на комментарии, требующие более подробной информации, код для пикселя приведен ниже (без каких-либо защитных мер).
int Ind0[3] = {0, 1, 2}; //all equal int Ind1[3] = {2, 1, 0}; // top, mid ,bot from mask... int Ind2[3] = {1, 0, 2}; int Ind3[3] = {1, 2, 0}; int Ind4[3] = {0, 2, 1}; int Ind5[3] = {2, 0, 1}; int Ind6[3] = {0, 1, 2}; int Ind7[3] = {-1, -1, -1}; // not possible int *Inds[8] = {Ind0, Ind1, Ind2, Ind3, Ind4, Ind5, Ind6, Ind7}; void grecolor(unsigned char *rgb, int bri, unsigned char *grey) { //pick out bot, mid or top according to bri flag int r = rgb[0]; int g = rgb[1]; int b = rgb[2]; int mask = 0; mask |= (r > g); mask <<= 1; mask |= (g > b); mask <<= 1; mask |= (b > r); grey[0] = rgb[Inds[mask][2 - bri]]; // 2, 1, 0 give bot, mid, top }




Comments