如何在R中计算多维颜色的渐变

4

这与这个问题有关,但可能更简单一些。我想知道是否有一个合理的方法来计算一个多维颜色梯度,给定三或四种任意颜色,就像r中rgb()函数处理红、绿、蓝色一样?一维梯度很容易计算(图1),但是对我来说不清楚如何计算两维梯度(图2)三角形内部。边缘很容易,重点在于内部。

# one dimensional color gradient
one.dimensions <- colorRampPalette( c( "orange" , "blue" ) )( 100 )

plot( 1:100 , rep( 1 , 100 ) , col = one.dimensions , cex = 3 , pch = 16 , main  = 'one dimensional gradient' )

enter image description here

# here are the edges of a three-colored triangle
dimensions13 <- colorRampPalette( c( "orange" , "blue" ) )( 100 )
dimensions12 <- colorRampPalette( c( "orange" , "red" ) )( 100 )
dimensions23 <- colorRampPalette( c( "blue" , "red" ) )( 100 )

plot( 1:100 , c( 1:50 , 50:1 ) , type = 'n' , main = 'two dimensional gradient' )
points( 1:100 , rep( 1 , 100 ) , col = dimensions12 , cex = 3 , pch = 16 )
points( seq( 1 , 50 , length = 100 ) , seq( 1 , 50 , length = 100 ) , col = dimensions13 , cex = 3 , pch = 16 )
points( seq( 50 , 100 , length = 100 ) , seq( 50 , 1 , length = 100 ) , col = dimensions23 , cex = 3 , pch = 16 )

enter image description here


1
一个小侧记:当你写二维和三维时,你是不是指一维和二维(例如,x-与x-和y-维度)? - Henrik
@Henrik 好观点 :) - Anthony Damico
没问题!也许还可以改一下情节标题 ;) - Henrik
@Henrik,那个问答非常相关,但我也想在渐变的中心有那些白色的X,看起来两位回答者都没有提供如何做到这一点? :/ - Anthony Damico
这个问题可能会有兴趣:链接 - Marc in the box
显示剩余5条评论
2个回答

3
你可以考虑三种基本的颜色混合策略:
1- 减法混合,使用 R 图形的 alpha 透明度混合。基本上,将具有自己渐变的多个图层叠加在一起。
library(grid)

grid.newpage()
grid.raster(scales::alpha(colorRampPalette(c("white","blue"))(10), 0.3),
            width=1,height=1)
grid.raster(t(scales::alpha(colorRampPalette(c("white","red"))(10), 0.3)),
            width=1,height=1)

一个缺点是最终颜色取决于层的顺序。

enter image description here

CMYK颜色模型可能是另一个灵感来源。

2- 加性。我想出了一个天真的实现方法,如下所示。考虑你的N种基本颜色(比如黄色、绿色、橙色)。为它们分配可见光谱的波长(570纳米、520纳米、600纳米)。每种颜色根据三角形中的位置被赋予一个权重(想象一下N个带有可调强度的激光器)。现在,为了得到与这N个激光源混合相关的颜色,你需要与CIE颜色匹配函数卷积。这是一种物理上合理的混合,将数字映射到视觉感知。然而,显然存在唯一性问题:几个组合可能会产生相同的颜色。毕竟,眼睛只有三种不同类型的感受器,因此N>3永远不会产生双射。

3- 像素化 (点阵处理)。将图像分割成小的相邻区域,类似液晶屏幕,并且每个像素被分成N个子像素,每个子像素有自己的颜色。从远处和/或足够的屏幕/打印分辨率来看,眼睛看不到细节,会为你模糊相邻的颜色。


无论我是否喜欢,这可能是正确的答案 :) 谢谢! - Anthony Damico
@baptiste,你有使用选项2或3的示例吗? - scottyaz

2

三角形

三角形中有一个重心坐标的概念。假设A、B和C是构成该三角形的点,那么三角形内部的每一个点都可以用一个方程来表示:

t * A + s * B + p * C

其中0 <= t, s, p <= 1t + s + p = 1。您可以评估t、s和p,并将它们视为相应三角形顶点定义的颜色的权重,轻松评估每个渲染点的目标颜色。

但请注意,此算法的效果仅适用于三角形。如果您考虑将三角形缝合在一起并使用此方法来评估颜色,则最终会得到三角形渐变图案,这可能是您想要避免的。

随机点集

对于更多随机点集,我使用以下算法:

给定点(x,y),我要计算其颜色,以及点集(x1,y1,color1),(x2,y2,color2),...,(xN,yN,colorN):

  1. 计算到所有点(x1,y1)...(xN,yN)的距离
  2. 计算每个点的高斯函数值:
  3. 根据这些权重计算颜色的加权平均值。

实现看起来像这样:

private float Gauss(float x, float a, float b, float c)
{
    var v1 = (x - b) / (2d * c * c);
    var v2 = -v1 * v1 / 2d;
    var v3 = (float)(a * Math.Exp(v2));
            
    return v3;
}

private float GetWeight(float distance) => Gauss(distance, Peak, 0, Falloff);

public override Color GetColor(PointF p)
{
    float[] distances = Points
        .Select(pt => (pt.AsVector2 - p.ToVector2()).Length())
        .ToArray();

    float r = 0, g = 0, b = 0, sum = 0;

    for (int i = 0; i < Points.Length; i++)
    {
        var weight = GetWeight(distances[i]);

        r += Points[i].Color.R * weight;
        g += Points[i].Color.G * weight;
        b += Points[i].Color.B * weight;
        sum += weight;
    }

    int targetR = Math.Max(0, Math.Min(255, (int)(r / sum)));
    int targetG = Math.Max(0, Math.Min(255, (int)(g / sum)));
    int targetB = Math.Max(0, Math.Min(255, (int)(b / sum)));

    return Color.FromArgb(targetR, targetG, targetB);
}

问题在于您需要选择PeakFalloff值,以满足您的需求。 Peak定义了距离0处(即您恰好位于定义点之一上方的位置)的权重值,而Falloff定义了权重向0迅速消散的速度(尽管它永远不会达到0)。
例如,对于256x256图像,Peak为100,Falloff为5:

Peak of 100, Fallof of 5

100 的峰值,7 的下降:

Peak of 100, Falloff of 7:

100的峰值,9的下降:

Peak of 100, Falloff of 9

注释

我的解决方案可惜更像是一种变通方法,而不是真正的解决方案,因为它严重依赖于选择适当的 Peak 和 Falloff 值。通常情况下,您正在寻找一个二维函数,其行为方式如下:

  • 在定义点 X 处,它具有(精确的)X颜色值
  • 离开点 X 越远,它对最终颜色的影响就越小,其他点的影响就越大。

我的解决方案满足第二个要求,但并不满足第一个要求(颜色接近 X 的颜色,但不一定相等)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接