gpt4 book ai didi

algorithm - 颜色渐变算法

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:50:56 24 4
gpt4 key购买 nike

给定两种 rgb 颜色和一个矩形,我能够创建一个基本的线性渐变。这blog post对如何创建它给出了很好的解释。但是我想为这个算法添加一个变量,角度。我想创建线性渐变,我可以在其中指定颜色的角度。

例如,我有一个矩形 (400x100)。从颜色为红色 (255, 0, 0) 到颜色为绿色 (0, 255, 0) 并且角度为 0°,因此我将具有以下颜色渐变。

enter image description here

鉴于我有相同的矩形,从颜色到颜色。但是这次我将角度更改为 45°。所以我应该有以下颜色渐变。

enter image description here

最佳答案

您的问题实际上由两部分组成:

  1. 如何在两种颜色之间生成平滑的颜色渐变。
  2. 如何在角度上呈现渐变。

渐变的强度在感知色彩空间中必须保持不变,否则渐变中的点看起来会不自然地暗或亮。您可以在基于 sRGB 值的简单插值的渐变中轻松看到这一点,尤其是红绿色渐变在中间太暗。对线性值而不是 Gamma 校正值使用插值可以使红绿渐变更好,但代价是背景白色渐变。通过将光强度与颜色分开,您可以获得两全其美的效果。

通常当需要感知色彩空间时,Lab color space将被提议。我认为有时它走得太远了,因为它试图适应这样一种看法,即蓝色比同等强度的其他颜色(如黄色)更暗。这是事实,但我们习惯于在我们的自然环境中看到这种效果,并且在梯度中你最终会得到过度补偿。

A power-law function of 0.43研究人员通过实验确定最适合将灰度光强度与感知亮度相关联。

我把Ian Boyd准备的精彩 sample 带到这里了并在最后添加了我自己提出的方法。我希望您会同意这种新方法在所有情况下都更胜一筹。

Algorithm MarkMix
Input:
color1: Color, (rgb) The first color to mix
color2: Color, (rgb) The second color to mix
mix: Number, (0..1) The mix ratio. 0 ==> pure Color1, 1 ==> pure Color2
Output:
color: Color, (rgb) The mixed color

//Convert each color component from 0..255 to 0..1
r1, g1, b1 ← Normalize(color1)
r2, g2, b2 ← Normalize(color1)

//Apply inverse sRGB companding to convert each channel into linear light
r1, g1, b1 ← sRGBInverseCompanding(r1, g1, b1)
r2, g2, b2 ← sRGBInverseCompanding(r2, g2, b2)

//Linearly interpolate r, g, b values using mix (0..1)
r ← LinearInterpolation(r1, r2, mix)
g ← LinearInterpolation(g1, g2, mix)
b ← LinearInterpolation(b1, b2, mix)

//Compute a measure of brightness of the two colors using empirically determined gamma
gamma ← 0.43
brightness1 ← Pow(r1+g1+b1, gamma)
brightness2 ← Pow(r2+g2+b2, gamma)

//Interpolate a new brightness value, and convert back to linear light
brightness ← LinearInterpolation(brightness1, brightness2, mix)
intensity ← Pow(brightness, 1/gamma)

//Apply adjustment factor to each rgb value based
if ((r+g+b) != 0) then
factor ← (intensity / (r+g+b))
r ← r * factor
g ← g * factor
b ← b * factor
end if

//Apply sRGB companding to convert from linear to perceptual light
r, g, b ← sRGBCompanding(r, g, b)

//Convert color components from 0..1 to 0..255
Result ← MakeColor(r, g, b)
End Algorithm MarkMix

这是 Python 中的代码:

def all_channels(func):
def wrapper(channel, *args, **kwargs):
try:
return func(channel, *args, **kwargs)
except TypeError:
return tuple(func(c, *args, **kwargs) for c in channel)
return wrapper

@all_channels
def to_sRGB_f(x):
''' Returns a sRGB value in the range [0,1]
for linear input in [0,1].
'''
return 12.92*x if x <= 0.0031308 else (1.055 * (x ** (1/2.4))) - 0.055

@all_channels
def to_sRGB(x):
''' Returns a sRGB value in the range [0,255]
for linear input in [0,1]
'''
return int(255.9999 * to_sRGB_f(x))

@all_channels
def from_sRGB(x):
''' Returns a linear value in the range [0,1]
for sRGB input in [0,255].
'''
x /= 255.0
if x <= 0.04045:
y = x / 12.92
else:
y = ((x + 0.055) / 1.055) ** 2.4
return y

def all_channels2(func):
def wrapper(channel1, channel2, *args, **kwargs):
try:
return func(channel1, channel2, *args, **kwargs)
except TypeError:
return tuple(func(c1, c2, *args, **kwargs) for c1,c2 in zip(channel1, channel2))
return wrapper

@all_channels2
def lerp(color1, color2, frac):
return color1 * (1 - frac) + color2 * frac



def perceptual_steps(color1, color2, steps):
gamma = .43
color1_lin = from_sRGB(color1)
bright1 = sum(color1_lin)**gamma
color2_lin = from_sRGB(color2)
bright2 = sum(color2_lin)**gamma
for step in range(steps):
intensity = lerp(bright1, bright2, step, steps) ** (1/gamma)
color = lerp(color1_lin, color2_lin, step, steps)
if sum(color) != 0:
color = [c * intensity / sum(color) for c in color]
color = to_sRGB(color)
yield color

red-green gradient

green-blue gradient

blue-red gradient

black-white gradient

red-white gradient

red-black gradient

现在是您问题的第 2 部分。您需要一个方程式来定义代表渐变中点的线,以及与对应于渐变端点颜色的线的距离。将端点放在矩形的最远角是很自然的,但根据问题中的示例判断,这不是您所做的。我选择了 71 像素的距离来近似示例。

生成渐变的代码需要与上面显示的代码稍作更改,以便更加灵活。它不是将梯度分解为固定数量的步长,而是根据参数 t 在 0.0 到 1.0 之间的范围内连续计算。

class Line:
''' Defines a line of the form ax + by + c = 0 '''
def __init__(self, a, b, c=None):
if c is None:
x1,y1 = a
x2,y2 = b
a = y2 - y1
b = x1 - x2
c = x2*y1 - y2*x1
self.a = a
self.b = b
self.c = c
self.distance_multiplier = 1.0 / sqrt(a*a + b*b)

def distance(self, x, y):
''' Using the equation from
https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation
modified so that the distance can be positive or negative depending
on which side of the line it's on.
'''
return (self.a * x + self.b * y + self.c) * self.distance_multiplier

class PerceptualGradient:
GAMMA = .43
def __init__(self, color1, color2):
self.color1_lin = from_sRGB(color1)
self.bright1 = sum(self.color1_lin)**self.GAMMA
self.color2_lin = from_sRGB(color2)
self.bright2 = sum(self.color2_lin)**self.GAMMA

def color(self, t):
''' Return the gradient color for a parameter in the range [0.0, 1.0].
'''
intensity = lerp(self.bright1, self.bright2, t) ** (1/self.GAMMA)
col = lerp(self.color1_lin, self.color2_lin, t)
total = sum(col)
if total != 0:
col = [c * intensity / total for c in col]
col = to_sRGB(col)
return col

def fill_gradient(im, gradient_color, line_distance=None, max_distance=None):
w, h = im.size
if line_distance is None:
def line_distance(x, y):
return x - ((w-1) / 2.0) # vertical line through the middle
ul = line_distance(0, 0)
ur = line_distance(w-1, 0)
ll = line_distance(0, h-1)
lr = line_distance(w-1, h-1)
if max_distance is None:
low = min([ul, ur, ll, lr])
high = max([ul, ur, ll, lr])
max_distance = min(abs(low), abs(high))
pix = im.load()
for y in range(h):
for x in range(w):
dist = line_distance(x, y)
ratio = 0.5 + 0.5 * dist / max_distance
ratio = max(0.0, min(1.0, ratio))
if ul > ur: ratio = 1.0 - ratio
pix[x, y] = gradient_color(ratio)

>>> w, h = 406, 101
>>> im = Image.new('RGB', [w, h])
>>> line = Line([w/2 - h/2, 0], [w/2 + h/2, h-1])
>>> grad = PerceptualGradient([252, 13, 27], [41, 253, 46])
>>> fill_gradient(im, grad.color, line.distance, 71)

这是上面的结果:

Red-Green gradient on a 45 degree angle

关于algorithm - 颜色渐变算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22607043/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com