Photoshop中色相/饱和度调整图层的算法

16

有人知道在Photoshop中调整图层是如何工作的吗?我需要生成一张结果图像,其中包括源图像和色相/饱和度调整图层中的HSL值。将其转换为RGB,然后与源颜色相乘并不起作用。

或者是否可以将色相/饱和度调整图层替换为普通图层,并适当设置混合模式(Multiply、Screen、Hue、Saturation、Color、Luminocity等)? 如果可以,那么如何操作?

谢谢!

6个回答

13

我已经反向工程计算出“启用“着色”复选框”的过程。以下所有代码均为伪代码。

输入参数如下:

  • hueRGB,这是一个RGB颜色,对应于HSV(photoshop_hue, 100, 100).ToRGB()
  • saturation,即 photoshop_saturation / 100.0(范围为0~1)
  • lightness,即 photoshop_lightness / 100.0(范围为-1~1)
  • value,这是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)

其中blend2blend3是:

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

1
太好了,谢谢分享。我已经尝试了你的代码,发现结果比预期的亮一些。后来我意识到这是因为这行代码:2 (1 + lightness)(value)- 1,如果我们不将(1 + lightness)*(value)乘以2,问题就会解决。 - user65721
1
第一行中的 rgb(128, 128, 128) 是什么?是原始单个像素的 RGB 颜色吗? - zwcloud
1
@zwcloud 这只是灰色的颜色 #808080 - Roman Starkov

3
我已经理解了Lightness的工作原理。 输入参数亮度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

然而,如果您选择某个间隔(例如Reds而不是Master),Lightness的行为完全不同,更像Saturation。

对于b>1,我认为c = 1 - (2-b) * (1-c)更有道理。@Ivan Kuckir - lbsweek
这两个公式将得到相同的结果。 - lbsweek

2
Photoshop,不知道。但通常的理论是:RGB图像通过特定层的内部方法转换为HSL/HSV;然后根据指定的参数修改每个像素的HSL,所得到的结果会以RGB形式返回(用于显示)。
PaintShopPro7曾将H空间(假设范围为0..360)分成30°的离散增量(如果我没记错的话),因此如果你只调整“黄色”,即只有H分量值在45-75之间的像素才会被考虑进行处理。
红色 345..15,橙色 15..45,黄色 45..75,黄绿色 75..105,绿色 105..135,等等。
if (h >= 45 && h < 75) s += s * yellow_percent;
还有其他可能性,比如应用一个衰减滤波器,如下所示:
/* 对于h=60,让m=1...并线性衰减到h=75 m=0。*/ m = 1 - abs(h - 60) / 15; if (m < 0) m = 0; s += s * yellow_percent * d;

1
你好,我编写了一个颜色着色器,我的公式如下:
输入RGB是源图像,应该是单色的。
(r+g+b) * 0.333

colorRGB是您的目标颜色
finalRGB是结果

伪代码:

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

我认为它快速而高效


1
我已经将@Roman Starkov的解决方案翻译成了Java,如果有需要的话。但出于某种原因它并没有很好地工作,然后我开始阅读一些资料,并发现解决方案非常简单,需要做两件事情:
  1. 当改变色相或饱和度时,仅替换原始图像的色相和饱和度,而亮度保持原始图像中的状态,这种混合方法称为10.2.4.亮度混合模式: https://www.w3.org/TR/compositing-1/#backdrop
  2. 当在Photoshop中改变亮度时,滑块指示我们需要添加或从原始亮度中减去多少百分比才能达到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;
    }

}

0

当“着色”复选框被选中时,底层图层的亮度与色相和饱和度滑块的值相结合,并根据https://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL中的方程式从HSL转换为RGB。(亮度滑块只是将亮度重新映射到比例尺的子集上,从直方图可以看出效果非常糟糕,我不明白为什么会有人使用它。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接