【问题标题】:How to fade color如何褪色
【发布时间】:2015-03-16 21:55:48
【问题描述】:

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

【问题讨论】:

  • 基本上,您希望去饱和像素的颜色,即在保持色调和值的同时降低其饱和度。看看HSL and HSV color spaces上的维基百科页面,我想你需要知道的一切。
  • 您的陈述“我知道 (100,100,100) 到 (110,110,110) 是灰度渐变” 并不完全正确。这些仍然是 RGB 值。但它们在我们的人眼看来是灰色阴影。

标签: python image image-processing colors pixel


【解决方案1】:

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

大多数情况下,您不需要这样做,而是可以使用简单的 HSB 模型,如 Photoshop 在信息面板中所做的那样。

为此,您只需在 3D 空间中想象 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 的百分比将返回相同的颜色,1.0 将返回白色。两者之间的一切都将是相同色调的较浅色调。这应该为您提供与 Photoshop 的 HSB 实现一致的结果,但将取决于设备并且可能不完全一致。

如果您有 RGB [200, 100, 50] 并输入 0.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.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 为您解决了光源和伽马问题。

【讨论】:

  • 如果您详细说明“定义 RGB 颜色并...转换为 XYZ...操作 L 通道”方法,我会接受这个答案。我想这就是我想做的事情。
【解决方案2】:

在颜色和白色之间进行简单的线性插值:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 这肯定会从图像中删除所有色调?
      • 可能,但是 HSV 变换和灰色变换一样简单。 rgb2hsv
      【解决方案5】:

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

      【讨论】:

      • 你是对的,但是问题表明 "...我想用数字来做,如所示。有一个等式吗?"。虽然显然他只想要一种方法而不是它背后的方程式。
      【解决方案6】:

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

        您可能想通过denis查看this answer

        RGB -> ^gamma -> Y -> 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% 的灰色非常暗:0.5 ^ 2.2 = 最大值的 22% 亮度。 (液晶显示器更复杂;此外,一些图形 卡补偿伽马。)

        要从 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

        【讨论】:

          【解决方案8】:

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

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

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

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

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

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

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

          旋转方向相同,但为了平滑 LCh 梯度需要进行一些偏移。

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

          这是从黄色开始,LCh 在不调整色调偏移的情况下获得绿色调:

          但是抵消中间色调会平滑 LCh 渐变:

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

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

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

          【讨论】:

            【解决方案9】:

            如果你不使用 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]
            

            【讨论】:

              【解决方案10】:

              Andrew 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。

              这是我根据 Andrew 的回答编写的方法:

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

              (我会将此作为评论添加到 Andrew 的回答中,但遗憾的是没有任何添加 cmets 的声誉!)

              【讨论】:

                猜你喜欢
                • 2021-09-03
                • 1970-01-01
                • 1970-01-01
                • 2012-10-24
                • 2012-05-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-08-15
                相关资源
                最近更新 更多