C++ 从一种颜色插值到另一种颜色

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13488957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:23:54  来源:igfitidea点击:

Interpolate from one color to another

c++openframeworks

提问by user1240679

I am trying to get an interpolation of one color to another shade of the same color. (for eg: sky blue to dark blue and then back).

我试图将一种颜色插值到另一种相同颜色的阴影。(例如:天蓝色到深蓝色然后返回)。

I stumbled upon some codethat could be used if the range was from 0-255 or 0-1. However, in my case, I have the RGB codes for Color1 and Color2, and want the rotation to occur.

我偶然发现了一些可以在范围为 0-255 或 0-1 时使用的代码。但是,就我而言,我有 Color1 和 Color2 的 RGB 代码,并希望发生旋转。

Color 1: 151,206,255
Color 2: 114,127,157

颜色 1:151,206,255
颜色 2:114,127,157

Any ideas how to go about this?

任何想法如何解决这个问题?

采纳答案by hyde

I suggest you convert RGB to HSV, then adjust its components, then convert back to RGB.

我建议您将 RGB 转换为 HSV,然后调整其组件,然后再转换回 RGB。

Wikipedia has an articleabout it, and it's been discussed here before:

维基百科有一篇关于它的文章,之前在这里讨论过:

HSL to RGB color conversion

HSL 到 RGB 颜色转换

Algorithm to convert RGB to HSV and HSV to RGB in range 0-255 for both

将 RGB 转换为 HSV 并将 HSV 转换为 RGB 的算法,范围为 0-255

Also many frameworks have conversion functions, for example Qt has QColor class.

很多框架也有转换功能,比如Qt有QColor类



But the question was about the actual interpolation... here's a trivial interpolation function:

但问题是关于实际插值......这是一个微不足道的插值函数:

// 0 <= stepNumber <= lastStepNumber
int interpolate(int startValue, int endValue, int stepNumber, int lastStepNumber)
{
    return (endValue - startValue) * stepNumber / lastStepNumber + startValue;
}

So call that for all color components you want to interpolate, in a loop. With RBG interpolation, you need to interpolate every component, in some other color space you may need to interpolate just one.

因此,在循环中为您想要插值的所有颜色分量调用它。使用 RBG 插值,您需要对每个组件进行插值,而在其他一些颜色空间中,您可能只需要插值一个。

回答by Carlos Barcellos

I know this is little bit old, but is worthy if someone is searching for it.

我知道这有点旧,但如果有人正在寻找它,它是值得的。

First of all, you can do interpolation in any color space, including RGB, which, in my opinion, is one of the easiest.

首先,您可以在任何颜色空间中进行插值,包括 RGB,在我看来,这是最简单的颜色空间之一。

Let's assume the variation will be controlled by a fraction value between 0 and 1 (e.g. 0.3), where 0 means full color1 and 1 means full color2.

让我们假设变化将由 0 和 1 之间的分数值控制(例如 0.3),其中 0 表示全彩 1,1 表示全彩 2。

The theory:

理论:

Result = (color2 - color1) * fraction + color1

Applying:

申请:

As the RGB has 3 channels (red, green and blue) we have to perform this math for each one of the channels.

由于 RGB 有 3 个通道(红色、绿色和蓝色),我们必须对每个通道执行此数学运算。

Using your example colors:

使用您的示例颜色:

color1: 151,206,255
color2: 114,127,157

R =  (114-151) * fraction + 151
G =  (127-206) * fraction + 206
B =  (157-255) * fraction + 255

Simple like that!

就这么简单!

回答by Synxis

Convert your RGB colors to HSVthen interpolate each component (not only the color, see end of answer), afterwards you can convert back to RGB.

将您的 RGB 颜色转换为 HSV,然后插入每个组件(不仅是颜色,请参阅答案结尾),然后您可以转换回 RGB。

You can do RGB interpolation, but the results are better with HSV, because in this space color is separated from luminance and saturation (Wikipedia article on HSV). HSV interpolation is more "logical" than the RGB one, because with the latter you can get extra colors while interpolating.

您可以进行 RGB 插值,但使用 HSV 效果更好,因为在此空间中,颜色与亮度和饱和度是分开的(关于 HSV 的维基百科文章)。HSV 插值比 RGB 插值更“合乎逻辑”,因为使用后者,您可以在插值时获得额外的颜色。

Some code for interpolation:

一些插值代码:

template<typename F>
ColorRGB interpolate(ColorRGB a, ColorRGB b, float t, F interpolator)
{
    // 0.0 <= t <= 1.0
    ColorHSV ca = convertRGB2HSV(a);
    ColorHSV cb = convertRGB2HSV(b);
    ColorHSV final;

    final.h = interpolator(ca.h, cb.h, t);
    final.s = interpolator(ca.s, cb.s, t);
    final.v = interpolator(ca.v, cb.v, t);

    return convertHSV2RGB(final);
}

int linear(int a, int b, float t)
{
    return a * (1 - t) + b * t;
}

// use: result = interpolate(color1,color2,ratio,&linear);

回答by Mike M

The best color space to use for visual effects is HCL. This is a space created specifically to look good while traversing its dimension, where "look good" does not relate to any physical properties of light or ink, like RGB and CMYK respectively.

用于视觉效果的最佳色彩空间是HCL。这是一个专门为在遍历其维度时看起来不错而创建的空间,其中“看起来不错”与光或墨水的任何物理属性无关,分别如 RGB 和 CMYK。

Using HCL is expensive so the best thing to do is to create a number of intermediary values in this space and then interpolate in RGB which is native. This is what I have done in my animation engine.

使用 HCL 是昂贵的,所以最好的办法是在这个空间中创建一些中间值,然后在原生的 RGB 中进行插值。这就是我在我的动画引擎中所做的。

Here is a snippet from it, in Swift 4.0

这是它的一个片段,在 Swift 4.0 中

extension UIColor {
    typealias Components = (CGFloat, CGFloat, CGFloat, CGFloat)
    enum Space: String {
        case RGB = "RGB"
        case HSB = "HSB"
        case HCL = "HCL"
    }
    func components(in space: UIColor.Space) -> Components {
        switch space {
        case .RGB: return self.rgba // var defined in HandyUIKit's extension
        case .HSB: return self.hsba // var defined in HandyUIKit's extension
        case .HCL: return self.hlca // var defined in HandyUIKit's extension
        }
    }
    func spectrum(to tcol: UIColor, for space: UIColor.Space) -> [UIColor] {
        var spectrum = [UIColor]()
        spectrum.append(self)
        let fcomps  = self.components(in: space)
        let tcomps  = tcol.components(in: space)
        for i in 0 ... 5 {
            let factor  = CGFloat(i) / 5.0
            let comps   = (1.0 - factor) * fcomps + factor * tcomps
            let color   = UIColor(with: comps, in: space)
            spectrum.append(color)
        }
        spectrum.append(tcol)
        return spectrum
    }
    convenience init(with components: Components, in space: Space) {
        switch space {
        case .RGB: self.init(red: components.0, green: components.1, blue: components.2, alpha: components.3)
        case .HSB: self.init(hue: components.0, saturation: components.1, brightness: components.2, alpha: components.3)
        case .HCL: self.init(hue: components.0, luminance: components.1, chroma: components.2, alpha: components.3)
        }
    }
}
func *(lhs:CGFloat, rhs:UIColor.Components) -> UIColor.Components {
    return (lhs * rhs.0, lhs * rhs.1, lhs * rhs.2, lhs * rhs.3)
}
func +(lhs:UIColor.Components, rhs:UIColor.Components) -> UIColor.Components {
    return (lhs.0 + rhs.0, lhs.1 + rhs.1, lhs.2 + rhs.2, lhs.3 + rhs.3)
}

Both the engine and the example above are using HandyUIKitfor the conversions between spaces so please add this project to whatever you are building for the code above to work.

引擎和上面的示例都使用HandyUIKit进行空间之间的转换,因此请将此项目添加到您正在构建的任何内容中,以使上述代码工作。

I have written an articleabout it.

我写了一篇关于它的文章

回答by Mr. Polywhirl

I have adapted Synxis's C example(above) into an executable JavaScript program.

我已经将Synxis 的 C 示例(上面)改编成一个可执行的 JavaScript 程序。

The program interpolates the color yellow, from redand green. The input and output is in RGB-space, but the interpolation is handled in the HSV-space. I also added an RGB interpolation example. As you can see below, a dark-yellowis produced if you interpolate redand greenin RGB-space.

程序从红色绿色中插入黄色。输入和输出在RGB-space 中,但插值在HSV-space 中处理。我还添加了一个 RGB 插值示例。如下所示,如果在RGB-space 中插入红色绿色,则会产生深黄色

/** Main */
var red        = { r : 255, g : 0,   b : 0 };
var green      = { r : 0,   g : 255, b : 0 };
var yellow     = interpolateHsv(red, green, 0.5, linear);
var darkYellow = interpolateRgb(red, green, 0.5, linear);

document.body.innerHTML =
  'Yellow: '      + JSON.stringify(yellow,     null, '  ') + '<br />' +
  'Dark Yellow: ' + JSON.stringify(darkYellow, null, '  ');

/**
 * Returns an HSV interpolated value between two rgb values. 
 *
 * @param {Object} rgbA - rgb() tuple
 * @param {Object} rgbB - rgb() tuple
 * @param {Number} threshold - float between [0.0, 1.0]
 * @param {function} interpolatorFn - interpolator function
 * @return {Object} rbg
 */
function interpolateHsv(rgbA, rgbB, threshold, interpolatorFn) {
  var hsvA = rgbToHsv(rgbA);
  var hsvB = rgbToHsv(rgbB);
  threshold = toArray(threshold, 3);
  return hsvToRgb({
    h : interpolatorFn(hsvA.h, hsvB.h, threshold[0]),
    s : interpolatorFn(hsvA.s, hsvB.s, threshold[1]),
    v : interpolatorFn(hsvA.v, hsvB.v, threshold[2])
  });
}

/**
 * Returns an RGB interpolated value between two rgb values. 
 *
 * @param {Object} rgbA - rgb() tuple
 * @param {Object} rgbB - rgb() tuple
 * @param {Number} threshold - float between [0.0, 1.0]
 * @param {function} interpolatorFn - interpolator function
 * @return {Object} rbg
 */
function interpolateRgb(rgbA, rgbB, threshold, interpolatorFn) {        
  threshold = toArray(threshold, 3);
  return {
    r : ~~interpolatorFn(rgbA.r, rgbB.r, threshold[0]),
    g : ~~interpolatorFn(rgbA.g, rgbB.g, threshold[1]),
    b : ~~interpolatorFn(rgbA.b, rgbB.b, threshold[2])
  };
}

/**
 * Returns an interpolated value between two values. 
 *
 * @param {Number} valueA - color channel int value
 * @param {Number} valueB - color channel int value
 * @param {Number} threshold - float between [0.0, 1.0]
 * @param {function} interpolatorFn - interpolator function
 * @return {int}
 */
function linear(valueA, valueB, threshold) {
  return valueA * (1 - threshold) + valueB * threshold;
}

/**
 * Converts an RGB color value to HSV. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
 * Assumes r, g, and b are contained in the set [0, 255] and
 * returns h, s, and v in the set [0, 1].
 *
 * @param {Object} rgb - Color in rgb mode
 * @return {Object} - Color in hsv mode
 */
function rgbToHsv(rgb) {
  var r = rgb.r / 255,
      g = rgb.g / 255,
      b = rgb.b / 255;
  var max = Math.max(r, g, b), min = Math.min(r, g, b);
  var h, s, v = max;
  var d = max - min;
  s = max === 0 ? 0 : d / max;
  if (max == min) {
    h = 0; // achromatic
  } else {
    switch(max) {
      case r: h = (g - b) / d + (g < b ? 6 : 0); break;
      case g: h = (b - r) / d + 2; break;
      case b: h = (r - g) / d + 4; break;
    }
    h /= 6;
  }
  return {
    h : h,
    s : s,
    v : v
  };
}

/**
 * Converts an HSV color value to RGB. Conversion formula
 * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
 * Assumes h, s, and v are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param {Object} hsv - Color in hsv mode
 * @return {Object} - Color in rgb mode
 */
function hsvToRgb(hsv){
  var r, g, b, i, f, p, q, t,
      h = hsv.h,
      s = hsv.s,
      v = hsv.v;
  i = Math.floor(h * 6);
  f = h * 6 - i;
  p = v * (1 - s);
  q = v * (1 - f * s);
  t = v * (1 - (1 - f) * s);
  switch(i % 6){
    case 0: r = v, g = t, b = p; break;
    case 1: r = q, g = v, b = p; break;
    case 2: r = p, g = v, b = t; break;
    case 3: r = p, g = q, b = v; break;
    case 4: r = t, g = p, b = v; break;
    case 5: r = v, g = p, b = q; break;
  }
  return {
    r : r * 255,
    g : g * 255,
    b : b * 255
  };
}

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function toArray(arr, size) {
  var isNum = isNumeric(arr);
  arr = !Array.isArray(arr) ? [arr] : arr;
  for (var i = 1; i < size; i++) {
    if (arr.length < size) {
      arr.push(isNum ? arr[0] : 0);
    }
  }
  return arr;
}

回答by Sam Soffes

Here's a Swift 2 version based on @hyde's answer:

这是基于@hyde 回答的 Swift 2 版本:

import UIKit

func interpolate(start start: CGFloat, end: CGFloat, progress: CGFloat) -> CGFloat {
  return (end - start) * progress + start
}


extension UIColor {
  func interpolateTo(color end: UIColor, progress: CGFloat) -> UIColor {
    var r1: CGFloat = 0
    var g1: CGFloat = 0
    var b1: CGFloat = 0
    var a1: CGFloat = 0
    getRed(&r1, green: &g1, blue: &b1, alpha: &a1)

    var r2: CGFloat = 0
    var g2: CGFloat = 0
    var b2: CGFloat = 0
    var a2: CGFloat = 0
    end.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)

    return UIColor(
      red: interpolate(start: r1, end: r2, progress: progress),
      green: interpolate(start: g1, end: g2, progress: progress),
      blue: interpolate(start: b1, end: b2, progress: progress),
      alpha: interpolate(start: a1, end: a2, progress: progress)
    )
  }
}

You can use it like this:

你可以这样使用它:

color1.interpolateTo(color: color2, progress: t)

Where tis the percentage (0-1) of your interpolation.

t插值的百分比 (0-1)在哪里。

回答by Eran W

I see that you have tagged this question under "openframeworks" tag. so all you need to do is use the method ofColor::getLerpedor ofColor::lerp

我看到您在“openframeworks”标签下标记了这个问题。所以你需要做的就是使用该方法ofColor::getLerpedofColor::lerp

getLerpedreturns new value, while lerpmodifies the color.

getLerped返回新值,同时lerp修改颜色。

for example:

例如:

ofColor c1(151,206,255);
ofColor c2(114,127,157);


float p = 0.2f;
ofColor c3 = c1.getLerped(c2, p);

or

或者

c1.lerp(c2, 0.3f);