Алгоритм корректирующего слоя оттенка / насыщенности из Photoshop

Кто-нибудь знает, как в фотошопе работают корректирующие слои? Мне нужно создать результирующее изображение с исходным изображением и значениями HSL из корректирующего слоя Hue / Saturation. Преобразование в RGB, а затем умножение на исходный цвет не работает.

Или можно заменить корректирующий слой оттенка / насыщенности обычными слоями с соответствующим образом установленными режимами наложения (Mulitiply, Screen, Hue, Saturation, Color, Luminocity, ...)? Если да, то как?

Спасибо


person lechec    schedule 10.12.2010    source источник


Ответы (6)


Я перепроектировал вычисление для , когда установлен флажок «Раскрасить». Весь приведенный ниже код является псевдокодом.

Входы:

  • hueRGB, который является цветом RGB для HSV(photoshop_hue, 100, 100).ToRGB()
  • насыщенность, которая равна photoshop_saturation / 100.0 (т. е. 0..1)
  • легкость, которая равна photoshop_lightness / 100.0 (т. е. -1..1)
  • значение, которое представляет собой pixel.ToHSV().Value, масштабированное до диапазона 0..1.

Метод раскрашивания отдельного пикселя:

color = blend2(rgb(128, 128, 128), hueRGB, saturation);

if (lightness <= -1)
    return black;
else if (lightness >= 1)
    return white;

else if (lightness >= 0)
    return blend3(black, color, white, 2 * (1 - lightness) * (value - 1) + 1)
else
    return blend3(black, color, white, 2 * (1 + lightness) * (value) - 1)

Где blend2 и blend3:

blend2(left, right, pos):
    return rgb(left.R * (1-pos) + right.R * pos, same for green, same for blue)

blend3(left, main, right, pos):
    if (pos < 0)
        return blend2(left, main, pos + 1)
    else if (pos > 0)
        return blend2(main, right, pos)
    else
        return main
person Roman Starkov    schedule 07.02.2012
comment
Отлично, спасибо, что поделились. Я поиграл с вашим кодом, и кажется, что результат немного ярче, чем ожидалось. Затем я понял, что это происходит из-за этой строки 2 * (1 + яркость) * (значение) - 1, и если мы не умножим (1 + яркость) * (значение) на 2, проблема решится. - person user65721; 15.08.2013
comment
Что такое rgb(128, 128, 128) в первой строке? Цвет rgb исходного одиночного пикселя? - person zwcloud; 09.01.2018
comment
@zwcloud это просто серый цвет #808080 - person Roman Starkov; 09.01.2018

Я понял, как работает Легкость.

Входной параметр яркости b находится в [0, 2], выходной - c (цветовой канал).

if(b<1) c = b * c;
else    c = c + (b-1) * (1-c);

Некоторые тесты:

b = 0  >>>  c = 0  // black
b = 1  >>>  c = c  // same color
b = 2  >>>  c = 1  // white

Однако, если вы выберете какой-то интервал (например, красные вместо основного), Lightness будет вести себя совершенно иначе, больше как Saturation.

person Ivan Kuckir    schedule 18.11.2013
comment
Для b ›1 я думаю, что c = 1 - (2-b) * (1-c) имеет больше смысла. @ Иван Кукир - person lbsweek; 21.02.2017
comment
эти две формулы дадут одинаковый результат - person lbsweek; 21.02.2017

Фотошоп, не знаю. Но теория обычно такова: изображение RGB преобразуется в HSL / HSV внутренними методами конкретного слоя; HSL каждого пикселя затем модифицируется в соответствии с указанными параметрами, и полученный таким образом результат возвращается (для отображения) в RGB.

PaintShopPro7 использовался для разделения H-пространства (предполагая диапазон 0..360) с дискретными приращениями 30 ° (IIRC), поэтому, если вы удаляете только «желтые», то есть только пиксели, H-компонент которых имеет значение 45-75, будут считаться объектом манипуляции.

красные 345..15, апельсины 15..45, желтые 45..75, желто-зеленые 75..105, зеленые 105..135 и т. д.

if (h >= 45 && h < 75)
        s += s * yellow_percent;

Есть альтернативные возможности, такие как применение фильтра спада, как в:

/* For h=60, let m=1... and linearly fall off to h=75 m=0. */
m = 1 - abs(h - 60) / 15;
if (m < 0)
        m = 0;
s += s * yellow_percent * d;
person user502515    schedule 10.12.2010

Здравствуйте, я написал шейдер Colorize, и мое уравнение выглядит следующим образом

inputRGB - исходное изображение, которое должно быть монохромным.

(r+g+b) * 0.333

colorRGB - ваш конечный цвет
finalRGB - результат

псевдокод:

finalRGB = inputRGB * (colorRGB + inputRGB * 0.5);

Я думаю это быстро и эффективно

person Jaroslav Stehlik    schedule 26.09.2014

Я перевел решение @Roman Starkov на java, если оно было необходимо, но по какой-то причине оно не сработало так хорошо, затем я начал немного читать и обнаружил, что решение очень простое, нужно сделать 2 вещи:

  1. При изменении оттенка или насыщенности замените исходное изображение только оттенком и насыщенностью, а яркость останется такой, как была в исходном изображении, этот метод наложения называется 10.2.4. режим наложения яркости: https://www.w3.org/TR/compositing-1/#backdrop

  2. При изменении яркости в фотошопе ползунок показывает, сколько процентов нам нужно добавить или отнять от исходной яркости, чтобы получить белый или черный цвет в HSL.

например: если исходный пиксель имеет яркость 0,7, а ползунок яркости = 20, значит, нам нужно больше 0,3 яркости, чтобы получить 1

поэтому нам нужно добавить к исходному пикселю яркости: 0,7 + 0,2 * 0,3; это будет новое значение смешанной яркости для нового пикселя.

Реализация решения @Roman Starkov на Java:

//newHue, which is photoshop_hue (i.e. 0..360)
//newSaturation, which is photoshop_saturation / 100.0 (i.e. 0..1)
//newLightness, which is photoshop_lightness / 100.0 (i.e. -1..1)

//returns rgb int array of new color
private static int[] colorizeSinglePixel(int originlPixel,int newHue,float newSaturation,float newLightness)
{
    float[] originalPixelHSV = new float[3];
    Color.colorToHSV(originlPixel,originalPixelHSV);
    float originalPixelLightness = originalPixelHSV[2];

    float[] hueRGB_HSV = {newHue,100.0f,100.0f};
    int[] hueRGB = {Color.red(Color.HSVToColor(hueRGB_HSV)),Color.green(Color.HSVToColor(hueRGB_HSV)),Color.blue(Color.HSVToColor(hueRGB_HSV))};


    int color[] = blend2(new int[]{128,128,128},hueRGB,newSaturation);
    int blackColor[] = new int[]{Color.red(Color.BLACK),Color.green(Color.BLACK),Color.blue(Color.BLACK)};
    int whileColor[] = new int[]{Color.red(Color.WHITE),Color.green(Color.WHITE),Color.blue(Color.WHITE)};

    if(newLightness <= -1)
    {
        return blackColor;
    }
    else if(newLightness >=1)
    {
        return whileColor;
    }
    else if(newLightness >=0)
    {
        return blend3(blackColor,color,whileColor, (int) (2*(1-newLightness)*(originalPixelLightness-1) + 1));
    }
    else
    {
        return blend3(blackColor,color,whileColor, (int) ((1+newLightness)*(originalPixelLightness) - 1));
    }
}

private static int[] blend2(int[] left,int[] right,float pos)
{
    return new int[]{(int) (left[0]*(1-pos)+right[0]*pos),(int) (left[1]*(1-pos)+right[1]*pos),(int) (left[2]*(1-pos)+right[2]*pos)};
}

private static int[] blend3(int[] left,int[] main,int[] right,int pos)
{
    if(pos < 0)
    {
        return blend2(left,main,pos+1);
    }
    else if(pos > 0)
    {
        return blend2(main,right,pos);
    }
    else
    {
        return main;
    }

}
person Stav Bodik    schedule 02.07.2020

Когда установлен флажок «Colorize», яркость нижележащего слоя комбинируется со значениями ползунков Hue и Saturation и преобразуется из HSL в RGB в соответствии с уравнениями в https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL. (Ползунок «Яркость» просто переназначает яркость на подмножество шкалы, как вы можете видеть по гистограмме; эффект ужасен, и я не понимаю, зачем кому-то его использовать.)

person Russell Cottrell    schedule 14.01.2017