- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
给定两种 rgb 颜色和一个矩形,我能够创建一个基本的线性渐变。这blog post对如何创建它给出了很好的解释。但是我想为这个算法添加一个变量,角度。我想创建线性渐变,我可以在其中指定颜色的角度。
例如,我有一个矩形 (400x100)。从颜色为红色 (255, 0, 0) 到颜色为绿色 (0, 255, 0) 并且角度为 0°,因此我将具有以下颜色渐变。
鉴于我有相同的矩形,从颜色到颜色。但是这次我将角度更改为 45°。所以我应该有以下颜色渐变。
最佳答案
您的问题实际上由两部分组成:
渐变的强度在感知色彩空间中必须保持不变,否则渐变中的点看起来会不自然地暗或亮。您可以在基于 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
现在是您问题的第 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)
这是上面的结果:
关于algorithm - 颜色渐变算法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22607043/
滑动窗口限流 滑动窗口限流是一种常用的限流算法,通过维护一个固定大小的窗口,在单位时间内允许通过的请求次数不超过设定的阈值。具体来说,滑动窗口限流算法通常包括以下几个步骤: 初始化:设置窗口
表达式求值:一个只有+,-,*,/的表达式,没有括号 一种神奇的做法:使用数组存储数字和运算符,先把优先级别高的乘法和除法计算出来,再计算加法和减法 int GetVal(string s){
【算法】前缀和 题目 先来看一道题目:(前缀和模板题) 已知一个数组A[],现在想要求出其中一些数字的和。 输入格式: 先是整数N,M,表示一共有N个数字,有M组询问 接下来有N个数,表示A[1]..
1.前序遍历 根-左-右的顺序遍历,可以使用递归 void preOrder(Node *u){ if(u==NULL)return; printf("%d ",u->val);
先看题目 物品不能分隔,必须全部取走或者留下,因此称为01背包 (只有不取和取两种状态) 看第一个样例 我们需要把4个物品装入一个容量为10的背包 我们可以简化问题,从小到大入手分析 weightva
我最近在一次采访中遇到了这个问题: 给出以下矩阵: [[ R R R R R R], [ R B B B R R], [ B R R R B B], [ R B R R R R]] 找出是否有任
我正在尝试通过 C++ 算法从我的 outlook 帐户发送一封电子邮件,该帐户已经打开并记录,但真的不知道从哪里开始(对于 outlook-c++ 集成),谷歌也没有帮我这么多。任何提示将不胜感激。
我发现自己像这样编写了一个手工制作的 while 循环: std::list foo; // In my case, map, but list is simpler auto currentPoin
我有用于检测正方形的 opencv 代码。现在我想在检测正方形后,代码运行另一个命令。 代码如下: #include "cv.h" #include "cxcore.h" #include "high
我正在尝试模拟一个 matlab 函数“imfill”来填充二进制图像(1 和 0 的二维矩阵)。 我想在矩阵中指定一个起点,并像 imfill 的 4 连接版本那样进行洪水填充。 这是否已经存在于
我正在阅读 Robert Sedgewick 的《C++ 算法》。 Basic recurrences section it was mentioned as 这种循环出现在循环输入以消除一个项目的递
我正在思考如何在我的日历中生成代表任务的数据结构(仅供我个人使用)。我有来自 DBMS 的按日期排序的任务记录,如下所示: 买牛奶(18.1.2013) 任务日期 (2013-01-15) 任务标签(
输入一个未排序的整数数组A[1..n]只有 O(d) :(d int) 计算每个元素在单次迭代中出现在列表中的次数。 map 是balanced Binary Search Tree基于确保 O(nl
我遇到了一个问题,但我仍然不知道如何解决。我想出了如何用蛮力的方式来做到这一点,但是当有成千上万的元素时它就不起作用了。 Problem: Say you are given the followin
我有一个列表列表。 L1= [[...][...][.......].......]如果我在展平列表后获取所有元素并从中提取唯一值,那么我会得到一个列表 L2。我有另一个列表 L3,它是 L2 的某个
我们得到二维矩阵数组(假设长度为 i 和宽度为 j)和整数 k我们必须找到包含这个或更大总和的最小矩形的大小F.e k=7 4 1 1 1 1 1 4 4 Anwser是2,因为4+4=8 >= 7,
我实行 3 类倒制,每周换类。顺序为早类 (m)、晚类 (n) 和下午类 (a)。我固定的订单,即它永远不会改变,即使那个星期不工作也是如此。 我创建了一个函数来获取 ISO 周数。当我给它一个日期时
假设我们有一个输入,它是一个元素列表: {a, b, c, d, e, f} 还有不同的集合,可能包含这些元素的任意组合,也可能包含不在输入列表中的其他元素: A:{e,f} B:{d,f,a} C:
我有一个子集算法,可以找到给定集合的所有子集。原始集合的问题在于它是一个不断增长的集合,如果向其中添加元素,我需要再次重新计算它的子集。 有没有一种方法可以优化子集算法,该算法可以从最后一个计算点重新
我有一个包含 100 万个符号及其预期频率的表格。 我想通过为每个符号分配一个唯一(且前缀唯一)的可变长度位串来压缩这些符号的序列,然后将它们连接在一起以表示序列。 我想分配这些位串,以使编码序列的预
我是一名优秀的程序员,十分优秀!