如何褪色

How to fade color

我想将像素的颜色淡化为白色,但显然保持相同的颜色。如果我有一个像素 (200,120,40),是否将每个值加 10 以使 (210,130,50) 使其颜色相同,只是更亮,还是会完全改变颜色?例如,我知道 (100,100,100)(110,110,110) 是灰度渐变。我希望 RGB 值也一样,并且我想按照指示以数字方式进行。有方程吗?

没那么简单,因为在您的显示器中每个颜色通道的权重不同。我想说最好的办法是在 scikit 图像中通过转换为灰色、变暗或变亮,然后再转换为颜色来做到这一点。 Scikit-image 将负责保持颜色的直线。

from skimage.color import gray2rgb, rgb2gray
scale_factor = 0.9 #90 percent
img_new = gray2rgb(rgb2gray(img) * scale_factor)

如果您想直接使用色相、饱和度和明度,请查看此示例:

http://scikit-image.org/docs/dev/auto_examples/plot_tinting_grayscale_images.html

有很多方法可以做到这一点。您选择如何做将取决于您是否重视速度和简单性或感知一致性。如果您需要它真正统一,您需要使用颜色配置文件定义 RGB 颜色,并且您需要配置文件的原色,以便您可以转换为 XYZ,然后转换为可以操作 L 通道的 LAB。

大多数时候您不需要这样做,您可以使用简单的 HSB 模型,就像 Photoshop 在信息面板中所做的那样。

为此,您只需在 3D space 中想象 RGB 点和白点之间的一条线,然后沿着该线移动颜色。实际上,您可以为该线创建一个参数方程并移动参数。

import numpy as np

def lighter(color, percent):
    '''assumes color is rgb between (0, 0, 0) and (255, 255, 255)'''
    color = np.array(color)
    white = np.array([255, 255, 255])
    vector = white-color
    return color + vector * percent

百分比 0.0 将 return 相同的颜色,1.0 将 return 白色。两者之间的一切都将是相同色调的较浅阴影。这应该会为您提供与 Photoshop 的 HSB 实施一致的结果,但会因设备而异,并且可能不会完全统一。

如果你有 RGB [200, 100, 50] 并输入 .50 的百分比,它应该 return RGB[ 227.5 177.5 152.5] Photoshop 报告两者的色调都是 20º。

没有numpy不难做到,但是元素操作很方便。

根据评论编辑:

我不是建议你这样做,除非你知道你真的需要用艰难的方式来做。但如果你想转换为 LAB,你可以毫不费力地做到这一点。最重要的是你需要知道你的 RGB 数字是什么颜色 space 开始或者你需要对它们的含义做出一些假设。由于 sRGB 在网络上是相当标准的,我将在此处假设。

转换并不难,但很容易出错。令人高兴的是,有一个非常漂亮的颜色模块和很好的文档:https://github.com/gtaylor/python-colormath

使用它你可以像这样在 sRGB 和 LAB 之间转换:

from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color

sRGB = sRGBColor(126, 126, 126, is_upscaled=True) # or between [0, 1] with out is_upscaled
lab =  convert_color(sRGB, LabColor)

lab 现在是一种带有亮度通道 lab.lab_l 的颜色,您可以在黑色 (0) 和白色 (100) 之间上下移动。这应该比 HSB 在感知上更统一(但是,根据您的应用程序,可能不足以保证工作)。

您可以简单地更改 lab_l 然后再转换回来:

lab.lab_l = 80
new_sRGB = convert_color(lab, color_objects.sRGBColor).get_upscaled_value_tuple()

new_sRGB 现在是 [198, 198, 198]。 colormath 为您处理了光源和伽玛问题。

我更喜欢使用 HSV color mode

要使颜色变灰,您必须降低饱和度。

标准 colorsys 模块可以帮助 RGB <-> HSV 转换,但请记住:colorsys 使用 [0, 1) 范围内的通道值,而不是 [0, 256] ).

有完整的代码示例:

>>> from colorsys import hsv_to_rgb, rgb_to_hsv
>>> color = (200, 120, 40)
>>> normalized_color = (color[0]/256., color[1]/256., color[2]/256.)
>>> normalized_color
(0.78125, 0.46875, 0.15625)
>>> hsv_color = rgb_to_hsv(*normalized_color)
>>> hsv_color
(0.08333333333333333, 0.8, 0.78125)
>>> grayed_hsv_color = (hsv_color[0], 0.6, hsv_color[2])
>>> grayed_rgb_color = hsv_to_rgb(*grayed_hsv_color)
>>> grayed_rgb_color
(0.78125, 0.546875, 0.3125)
>>> denormalized_rgb_color = (int(grayed_rgb_color[0]*256), int(grayed_rgb_color[1]*256), int(grayed_rgb_color[2]*256))
>>> denormalized_rgb_color
(200, 140, 80)

正如 MarkM 所建议的,HSB(或 HSL)是一种简单的方法,但不会提供完美的色调常数。如果这足够好(我假设你想要你自己的方法而不是模块)然后 this page 有代码可以做到这一点。

在 python 中看起来像这样:

def rgb_to_hsl(rgb):
    '''
    Converts an rgb (0..255) tuple to hsl
    '''
    r, g, b = rgb

    _r = r / 255                     # RGB in percentage
    _g = g / 255
    _b = b / 255

    rgbMin = min(_r, _g, _b)    
    rgbMax = max(_r, _g, _b)
    rgbDelta = rgbMax - rgbMin

    l = ( rgbMax + rgbMin ) / 2

    if rgbDelta == 0:                        #Greyscale
        h = 0
        s = 0
    else:                                    # Chromatic data...
        if l < 0.5: s = rgbDelta / (rgbMax + rgbMin)
        else:       s = rgbDelta / (2 - rgbMax - rgbMin)

        deltaR = (((rgbMax - _r) / 6) + rgbDelta/2) / rgbDelta
        deltaG = (((rgbMax - _g) / 6) + rgbDelta/2) / rgbDelta
        deltaB = (((rgbMax - _b) / 6) + rgbDelta/2) / rgbDelta

        if   _r == rgbMax: h = deltaB - deltaG
        elif _g == rgbMax: h = 1/3 + deltaR - deltaB
        elif _b == rgbMax: h = 2/3 + deltaG - deltaR

        if h < 0: h += 1
        if h > 1: h -= 1

    return (h, s, l)

def hsl_to_rgb(hsl):
    '''
    Converts a hsl tuple to rgb(0..255)
    '''
    h, s, l = hsl
    if s == 0:                      #Greyscale
        r = l * 255
        g = l * 255
        b = l * 255
    else:
        if l < 0.5: var_2 = l * (1 + s)
        else:       var_2 = l + s - (s * l)

        var_1 = 2 * l - var_2

        r = 255 * hue_to_RGB(var_1, var_2, h + 1/3)
        g = 255 * hue_to_RGB(var_1, var_2, h)
        b = 255 * hue_to_RGB(var_1, var_2, h - 1/3)
    return r, g, b   

def hue_to_RGB (v1, v2, vH):
    '''
    Helper for hsl_to_rgb
    '''
    if vH < 0: vH += 1
    if vH > 1: vH -= 1
    if (6 * vH) < 1: return v1 + (v2 - v1) * 6 * vH
    if (2 * vH) < 1: return v2
    if (3 * vH) < 2: return v1 + (v2 - v1) * 6 * (2/3 - vH)
    return v1

然后调亮:

def lighten(rgb):
    '''
    Given RGB values, returns the RGB values of the same colour slightly
    brightened (towards white) 
    '''
    h,s, l = rgb_to_hsl(rgb)
    l = min(l+0.1, 1)              #limit to 1
    return hsl_to_rgb((h, s, l))

这种方法的好处是增量是亮度的百分比。修改它以将百分比作为输入将是微不足道的。

您可以根据此代码对数学方程式进行逆向工程,或参阅 HSL to RGB

在你的颜色和白色之间简单地线性插值:

def lerp(a, b, t):
    return a*(1 - t) + b*t

import numpy as np
white = np.array([255, 255, 255])
my_color = np.array([...])
lightened25 = lerp(my_color, white, 0.25)

或者没有 numpy:

lightened25 = [lerp(c, w, 0.25) for c, w in zip(my_color, white)]

您可能想查看 this answer by denis

RGB -> ^gamma -> Y -> L*

In color science, the common RGB values, as in html rgb( 10%, 20%, 30% ), are called "nonlinear" or Gamma corrected. "Linear" values are defined as

Rlin = R^gamma,  Glin = G^gamma,  Blin = B^gamma

where gamma is 2.2 for many PCs. The usual R G B are sometimes written as R' G' B' (R' = Rlin ^ (1/gamma)) (purists tongue-click) but here I'll drop the '.

Brightness on a CRT display is proportional to RGBlin = RGB ^ gamma, so 50% gray on a CRT is quite dark: .5 ^ 2.2 = 22% of maximum brightness. (LCD displays are more complex; furthermore, some graphics cards compensate for gamma.)

To get the measure of lightness called L* from RGB, first divide R G B by 255, and compute

Y = .2126 * R^gamma + .7152 * G^gamma + .0722 * B^gamma

This is Y in XYZ color space; it is a measure of color "luminance". (The real formulas are not exactly x^gamma, but close; stick with x^gamma for a first pass.)

Finally, L* = 116 * Y ^ 1/3 - 16 "... aspires to perceptual uniformity ... closely matches human perception of lightness." -- Wikipedia Lab color space

问题 "to fade the color of a pixel out toward white"(不是某种灰色阴影)实际上是关于将原始像素颜色与白色混合,从 100% 原始颜色和 0% 白色变为 0% 原始颜色和 100%白色的。没有更多的了。例如,在 101 步中执行此操作将如下所示:

r0= 200; // as in the question
g0= 120;
b0=  40;
for(i= 100; i >= 0; i--){
  r= (i * r0 + (100 - i) * 255) / 100;
  g= (i * g0 + (100 - i) * 255) / 100;
  b= (i * b0 + (100 - i) * 255) / 100;
  // use this color (r, g, b) somehow
}

@mark-meyer的回答很好,我在StackMathematica部分回答了一个类似的问题,很详细的例子PartOne and Part Two.

对于这个问题,渐变到全白,这里是使用我的渐变浏览器的例子:

左边一列是sRGB,接下来是线性xyY,然后是LAB,最右边是LAB LCh

您会注意到,在大多数情况下,保留在 sRGB 中与使用 L*a*b* 没有本质区别。这部分是因为sRGB有一条伽马曲线,与LAB的感知曲线不同但相似。

您会注意到 LCh 版本有一些色调变化,具体取决于起始颜色。在紫色的情况下,它需要在中间范围附近进行一些偏移。

对于 LCh,色调旋转的方向(顺时针或逆时针)也会有所不同。

只看右边的 LCh,这里是品红色开始,没有偏移,自然旋转:

相同的旋转方向,但有一些偏移以平滑 LCh 梯度。

反转色调旋转和不同的偏移,我们通过紫色而不是橙色为 LCh:

这里是黄色,LCh 在不调整色调偏移的情况下得到绿色调:

但是抵消中间色调可以平滑 LCh 梯度:

最后。蓝色对 L*a*b* 很棘手,因为它经常想变成紫色。另一方面,在 LCh 中,蓝色要转移到青色:

所以实际上您通常可以在 sRGB 中使用渐变到白色,但对于两种饱和色之间的渐变,您可能更喜欢使用 LCh:

所以在这里,当颜色相差接近 180° 时,sRGB 和 LAB 的中间值都会去饱和,因为中间的平均值是灰色 - sRGB 是较暗的灰色,LAB 是较浅的灰色。但是 LCh 保持饱和度,而不是在色调中旋转。

如果您不使用 NumPy,则可以使用此函数来淡化颜色。我希望你需要这个。

def fadecolor(color,percent):
    rgb = color
    r = int(rgb[0]*1-percent)
    g = int(rgb[1]*1-percent)
    b = int(rgb[2]*1-percent)
    return (r,g,b)

或者列表推导法

def fadecolor(color,percent):
    return [int(i*1-percent) for i in color]

A​​ndrew Svetlov 的回答很好,在这里指导了我,但其中有一个小错误 - grayed_hsv_color = (hsv_color[0], 0.6, hsv_color[2]) 行应该读作 grayed_hsv_color = (hsv_color[0], hsv_color[1] * 0.6, hsv_color[2]) - 这样你就降低了输入颜色而不只是将其饱和度硬编码为 0.6。

这是我根据安德鲁的回答编写的方法:

@classmethod
def desaturate_hex(cls, hex, factor):
    rgb = cls.hex_to_rgb(hex)
    normalised_rgb = tuple(map(lambda x: x / 256, rgb))
    hsv = rgb_to_hsv(*normalised_rgb)
    desaturated_hsv = (hsv[0], hsv[1] * (1 - factor), hsv[2])
    desaturated_rgb = hsv_to_rgb(*desaturated_hsv)
    denormalised_rgb = tuple(map(lambda x: int(x * 256), desaturated_rgb))
    return cls.rgb_to_hex(denormalised_rgb)

(我本来可以将此添加为对安德鲁的回答的评论,但遗憾的是没有任何声誉来添加评论!)