平滑的颜色过渡算法

时间:2014-02-17 17:38:46

标签: c++ colors

我正在寻找一种通用算法来平滑地在两种颜色之间转换。

例如,此图片取自Wikipedia,并显示从橙色到蓝色的转换。

enter image description here

当我尝试使用我的代码(C ++)做同样的事情时,想到的第一个想法就是使用HSV色彩空间,但是显示出令人烦恼的中间颜色。

enter image description here

实现这一目标的好方法是什么?似乎与对比度的降低有关,或者可能使用不同的色彩空间?

5 个答案:

答案 0 :(得分:14)

我过去做过很多这些。平滑可以通过许多不同的方式执行,但它们在这里可能采用的方式是简单的线性方法。这就是说,对于每个R,G和B分量,它们只是找出连接这两个点的“y = m * x + b”方程式,并用它来计算其间的分量。

m[RED]   = (ColorRight[RED]   - ColorLeft[RED])   / PixelsWidthAttemptingToFillIn
m[GREEN] = (ColorRight[GREEN] - ColorLeft[GREEN]) / PixelsWidthAttemptingToFillIn
m[BLUE]  = (ColorRight[BLUE]  - ColorLeft[BLUE])  / PixelsWidthAttemptingToFillIn

b[RED]   = ColorLeft[RED]
b[GREEN] = ColorLeft[GREEN]
b[BLUE]  = ColorLeft[BLUE]

现在之间有任何新颜色:

NewCol[pixelXFromLeft][RED]   = m[RED]   * pixelXFromLeft + ColorLeft[RED]    
NewCol[pixelXFromLeft][GREEN] = m[GREEN] * pixelXFromLeft + ColorLeft[GREEN]
NewCol[pixelXFromLeft][BLUE]  = m[BLUE]  * pixelXFromLeft + ColorLeft[BLUE]

有许多数学方法可以创建转换,我们真正想要做的是了解您真正希望看到的转换。如果要查看上图中的确切过渡,则值得查看该图像的颜色值。我及时编写了一个程序来查看这些图像并以图形方式输出值。以下是上述伪彩色标度的程序输出。

enter image description here

基于查看图表,它比上面所述的线性更复杂。蓝色组件看起来大部分是线性的,红色可以模拟为线性,但绿色看起来更圆润。我们可以对绿色进行数学分析,以更好地理解它的数学函数,并使用它来代替。您可能会发现线性插值在0到70像素之间具有增加的斜率,在像素70之后具有线性递减斜率就足够了。

如果查看屏幕底部,此程序会给出每种颜色分量的一些统计测量值,例如最小值,最大值和平均值,以及图像读取的宽度像素数。

答案 1 :(得分:9)

R,G,B值的简单线性插值可以做到。

trumpetlicks has shown您使用的图像不是纯线性插值。但我认为插值可以为您提供所需的效果。下面我将在顶部显示线性插值,在底部显示原始图像。

Top half linear interpolation, bottom half original

这是产生它的(Python)代码:

for y in range(height/2):
    for x in range(width):
        p = x / float(width - 1)
        r = int((1.0-p) * r1 + p * r2 + 0.5)
        g = int((1.0-p) * g1 + p * g2 + 0.5)
        b = int((1.0-p) * b1 + p * b2 + 0.5)
        pix[x,y] = (r,g,b)

答案 2 :(得分:6)

HSV色彩空间不是用于平滑过渡的非常好的色彩空间。这是因为h值hue仅用于在“色轮”周围任意定义不同的颜色。这意味着如果你在车轮上两种颜色之间走得很远,你将不得不穿过一堆其他颜色。一点也不顺利。

使用RGB(或CMYK)会更有意义。这些“组件”颜色空间更好地定义为平滑过渡,因为它们代表颜色需要的每个“组件”的数量。

每个组件值R,G和B的线性转换(参见@trumpetlicks答案)应该看起来“非常好”。任何超过“非常好”的东西都需要一个真正的人来调整这些值,因为我们的眼睛在不同颜色组中感知颜色值的差异和不对称性在RBG或CMYK(或任何标准)中都没有表示

维基百科图片使用的是Photoshop使用的算法。不幸的是,该算法尚未公开。

答案 3 :(得分:6)

我一直在研究如何根据调色板构建algorithm that takes a grayscale image as input and colorises it artificially

输入■■■■■■■■

enter image description here enter image description here

与许多其他解决方案一样,该算法使用线性插值来进行颜色之间的转换。在您的示例中,应使用以下参数调用smooth_color_transition()

QImage input("gradient.jpg");

QVector<QColor> colors;
colors.push_back(QColor(242, 177, 103)); // orange
colors.push_back(QColor(124, 162, 248)); // blue-ish

QImage output = smooth_color_transition(input, colors);    
output.save("output.jpg");

从算法中比较 原始图像 VS 输出 可以在下面看到:

enter image description here (输出)

enter image description here (原创)

输出中可以观察到的视觉假象已经存在于输入(灰度)中。当输入图像调整为189x51时,输入图像得到了这些假象。

这是使用更复杂的调色板创建的另一个示例:

输入■■■■■■■■

答案 4 :(得分:3)

对我来说似乎更容易使用RGB值创建渐变。您应该首先根据渐变的宽度计算每个值的颜色变化。对于R,G和B值,需要执行以下伪代码。

if (redValue1 == redValue2) {
    redDifference = 0
} else {
    redDifference = absoluteValue(redValue1 - redValue2) / widthOfGradient
}

if (redValue1 > redValue2) {
    redDifference *= -1
}

然后,您可以使用这些值渲染每个像素,如下所示:

for (int i = 0; i < widthOfGradient; i++) {
    int r = round(redValue1 + (i * redDifference))
    // ...repeat for green and blue

    drawLine(i, r, g, b)
}

我知道您指定使用的是C ++,但我创建了一个JSFiddle,演示了以第一个渐变为例:http://jsfiddle.net/eumf7/