How to fade color

允我心安 提交于 2021-02-06 09:12:06

问题


I would like to fade the color of a pixel out toward white, but obviously maintain the same color. If I have a pixel (200,120,40), will adding 10 to each value to make (210,130,50) make it the same color, just lighter, or will it change the color entirely? For example, I know that (100,100,100) going to (110,110,110) is a greyscale fade. I would like the same with RGB values and I would like to do it numerically, as indicated. Is there an equation to do so?


回答1:


There are a bunch of ways to do this. How you choose to do it will depend on whether you value speed and simplicity or perceptual uniformity. If you need it to be truly uniform you will need to define you RGB colors with a color profile and you'll need the primaries of the profile so you can convert to XYZ and then to LAB where you can manipulate the L channel.

Most of the time you don't need to do that and you can instead use a simple HSB model like Photoshop does in the info palette.

To do this you simply imagine a line between your RGB point and the white point in 3D space and move your color along that line. In practical terms you can just create a parametric equation for that line and move the parameter.

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

A percentage of 0.0 will return the same color and 1.0 will return white. Everything between will be a lighter shade of the same hue. This should give you results that agree with Photoshop's HSB implementation, but will be device dependent and may not be perfectly uniform.

If you have RGB [200, 100, 50] and put in a percentage of .50 it should return RGB[ 227.5 177.5 152.5] Photoshop reports both as a hue of 20º.

It is not hard to do this without numpy, but the element wise operations are convenient.

Edit based on comment:

I'm not suggesting you do this unless you know you really need to do it the hard way. But if you want to convert to LAB you can without too much trouble. The most important thing is that you need to know what color space your RGB numbers are in to begin with or you need to make some assumptions about their meaning. Since sRGB is pretty standard on the web, I'll assume that here.

The conversions aren't that difficult, but it's easy to make mistakes. Happily, there's a pretty nice colormath module with good documentation: https://github.com/gtaylor/python-colormath

Using that you can convert between sRGB and LAB like this:

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 now is a color with a Luminance channel lab.lab_l which you can move up or down between black(0) and white(100). This should be more perceptually uniform than HSB (but, depending on your application, maybe not enough to warrant the work).

You can simply change lab_l and then convert back:

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

new_sRGB is now [198, 198, 198]. colormath took care of the illuminant and gamma issues for you.




回答2:


Simply linearly interpolate between your color and white:

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)

Or without numpy:

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



回答3:


I prefer to use HSV color mode.

To grayer your color you have to decrease Saturation factor.

Standard colorsys module can help in RGB <-> HSV conversions, but please keep in mind: colorsys operates with channel values in range [0, 1), not [0, 256).

There is full code example:

>>> 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:


It's not that simple because in your monitor each color channel is weighted differently. I'd say the best bet is to do this in scikit image by converting to gray, dimming or brightening, and then back-converting to color. Scikit-image will take care of keeping the colors straight.

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

If you want to work directly with hue, saturation and value, check out this example:

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




回答5:


As MarkM suggested, HSB (or HSL) is a simple method for doing this, but will not give perfect hue constance. If this is good enough (I assume you want your own method instead of a module) then this page has code for doing it.

In python it would look like this:

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

Then to brighten:

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

The benefit of this method is that the increment is a percentage of the total brightness. Modifying this to take the percentage as an input would be trivial.

You can reverse engineer the mathematical equations form this code, or see HSL to RGB.




回答6:


The question, "to fade the color of a pixel out toward white" (not some shade of gray), is really about mixing the original pixel color with white, going from 100% original color and 0% white to 0% original color and 100% white. There's not more to it. Doing this in, for example, 101 steps would look like this:

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:


You might want to check out 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




回答8:


@mark-meyer's answer is good, I answered a similar question on the StackMathematica section in great detail with examples PartOne and Part Two.

For THIS question, gradient to full white, here are examples using my gradient explorer:

Left column is staying in sRGB, next is linear xyY, then LAB, and far right is LAB LCh

You'll notice that remaining in sRGB is not substantially different than using L*a*b* in most cases. This is partly because sRGB has a gamma curve that is different but similar to the perception curve of LAB.

You'll notice though that the LCh version has some hue shifts, depending on the starting color. In the case of purple, it requires some offsets near the mid range.

Also with LCh, the direction (clock wise or CCW) of hue rotation makes a difference.

Looking just at the for right LCh, here is magenta start, with no offsets, and the natural rotation:

Same rotation direction, but some offsets to smooth the LCh gradient.

Reversing the hue rotation and different offsets we go through purple instead of orange for LCh:

And here's from yellow, the LCh gets a green tinge without adjusting the hue offset:

But offsetting the mid hue smooths the LCh gradient:

And finally. BLUE is tricky with L*a*b* as it often wants to shift to purple. On the other hand, in LCh blue wants to shift to cyan:

So in truth you can typically stay in sRGB for things like gradients to white, but for gradients between two saturated colors you might prefer to use LCh:

So here where colors get closer to 180° apart, the middle gets desaturated for both sRGB and LAB, as the average value in the middle is a grey - a darker murky grey for sRGB and a lighter grey for LAB. But LCh maintains saturation and instead rotates through the hues.



来源:https://stackoverflow.com/questions/28015400/how-to-fade-color

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!