如何淡化色彩

时间:2015-01-18 22:12:35

标签: python image image-processing colors pixel

我想将像素的颜色淡化为白色,但显然保持相同的颜色。如果我有一个像素(200,120,40),将为每个值添加10以使(210,130,50)使其颜色相同,只是更轻,还是会完全改变颜色?例如,我知道转到(100,100,100)的{​​{1}}是灰度渐变。我希望RGB值相同,我想按数字进行,如图所示。是否有这样的等式?

8 个答案:

答案 0 :(得分:15)

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

大多数情况下,您不需要这样做,而是可以使用像Photoshop一样的简单HSB模型。

要做到这一点,您只需想象一下RGB点与3D空间中的白点之间的一条线,并沿着该线移动颜色。实际上,您可以为该行创建参数方程并移动参数。

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的百分比将返回相同的颜色,1.0将返回白色。之间的一切都将是相同色调的浅色调。这应该给你的结果与Photoshop的HSB实现一致,但将取决于设备,可能不完全统一。

如果你有RGB [200, 100, 50]并输入.50的百分比,那么它应该返回RGB[ 227.5 177.5 152.5] Photoshop报告的色调为20º。

没有numpy这样做并不难,但元素操作很方便。

根据评论进行修改:

我不是建议你这样做,除非你知道你真的需要这么做。但是如果你想转换为LAB,你可以毫不费力地进行操作。最重要的是你需要知道你的RGB数字开头的颜色空间,或者你需要对它们的含义做一些假设。由于sRGB在网络上非常标准,我会在这里假设。

转换并不困难,但很容易出错。令人高兴的是,有一个非常好的colormath模块和良好的文档: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_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为您处理光源和伽玛问题。

答案 1 :(得分:5)

只需在颜色和白色之间进行线性插值:

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)]

答案 2 :(得分:4)

我更喜欢使用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)

答案 3 :(得分:2)

它并不那么简单,因为在您的显示器中,每个颜色通道的权重都不同。我说最好的选择是在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

答案 4 :(得分:2)

正如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

答案 5 :(得分:1)

您可能需要this answer查看denis

  

RGB - &gt; ^ gamma - &gt; Y - &gt; L *

     

在色彩科学中,常见的RGB值,如html rgb(10%,20%,30%)   ),被称为“非线性”或Gamma corrected。 “线性”   值定义为

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

许多PC的gamma为2.2。有时会写出通常的R G B.   作为R'G'B'(R'= Rlin ^(1 / gamma))(纯粹的舌头咔哒声)但是在这里   我会放弃'。

     

CRT显示器的亮度与RGBlin = RGB ^ gamma成正比,   所以CRT上的50%灰色很暗:.5 ^ 2.2 =最大值的22%   亮度。 (LCD显示更复杂;此外,还有一些图形   卡补偿伽马。)

     

要从RGB获得称为L*的亮度度量,首先除以R G.   B乘255,并计算

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

这是XYZ色彩空间中的Y;它是颜色“亮度”的量度。   (真正的公式不完全是x ^ gamma,但是接近;坚持   x ^ gamma用于第一遍。)

     

最后,       L * = 116 * Y ^ 1/3 - 16“...渴望感性均匀性......与人类对亮度的感知紧密相关。” - 维基百科Lab color space

答案 6 :(得分:1)

问题是“将像素的颜色淡化为白色”(不是一些灰色阴影),实际上是将原始像素颜色与白色混合,从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
}

答案 7 :(得分:0)

@ mark-meyer的回答很好,我在StackMathematica部分用示例PartOnePart Two.

详细回答了类似的问题

对于此问题,渐变为全白,以下是使用我的渐变资源管理器的示例:

左列停留在sRGB中,下一个是线性xyY,然后是LAB,最右边是LAB L C h

enter image description here

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

您会注意到,虽然LCh版本具有某些色相变化,具体取决于起始颜色。如果是紫色,则需要在中间范围附近偏移一些。

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

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

enter image description here

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

enter image description here

反转色相旋转和不同的偏移量,对于LCh,我们经过紫色而不是橙色:

enter image description here

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

enter image description here

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

enter image description here

最后。使用L*a*b*时,BLUE很棘手,因为它经常要转换为紫色。另一方面,在LCh中,蓝色想要转换为青色:

enter image description here

因此,实际上,通常您可以将sRGB留在sRGB中,以实现白色渐变,但是对于两种饱和颜色之间的渐变,您可能更喜欢使用LCh:

enter image description here

因此,在这里颜色之间的距离接近180°时,sRGB和LAB的中间值都变得不饱和,因为中间的平均值是灰色-sRGB的暗灰色和LAB的浅灰色。但是LCh保持饱和,而是通过色调旋转。