如何在HSV颜色空间中插入色调值?

nic*_*ick 15 javascript interpolation gradient colors hsv

我正在尝试在HSV颜色空间中插入两种颜色之间以产生平滑的颜色渐变.

我正在使用线性插值,例如:

h = (1 - p) * h1 + p * h2
s = (1 - p) * s1 + p * s2
v = (1 - p) * v1 + p * v2
Run Code Online (Sandbox Code Playgroud)

(其中p是百分比,h1,h2,s1,s2,v1,v2是两种颜色的色调,饱和度和值分量)

这对s和v产生了良好的结果,但对于h则没有.由于色调分量是一个角度,计算需要计算出h1和h2之间的最短距离,然后沿正确的方向(顺时针或逆时针)进行插值.

我应该使用什么公式或算法?


编辑:通过遵循杰克的建议我修改了我的JavaScript渐变功能,它运作良好.对于任何有兴趣的人,这是我最终得到的:

// create gradient from yellow to red to black with 100 steps
var gradient = hsbGradient(100, [{h:0.14, s:0.5, b:1}, {h:0, s:1, b:1}, {h:0, s:1, b:0}]); 

function hsbGradient(steps, colours) {
  var parts = colours.length - 1;
  var gradient = new Array(steps);
  var gradientIndex = 0;
  var partSteps = Math.floor(steps / parts);
  var remainder = steps - (partSteps * parts);
  for (var col = 0; col < parts; col++) {
    // get colours
    var c1 = colours[col], 
        c2 = colours[col + 1];
    // determine clockwise and counter-clockwise distance between hues
    var distCCW = (c1.h >= c2.h) ? c1.h - c2.h : 1 + c1.h - c2.h;
        distCW = (c1.h >= c2.h) ? 1 + c2.h - c1.h : c2.h - c1.h;
     // ensure we get the right number of steps by adding remainder to final part
    if (col == parts - 1) partSteps += remainder; 
    // make gradient for this part
    for (var step = 0; step < partSteps; step ++) {
      var p = step / partSteps;
      // interpolate h, s, b
      var h = (distCW <= distCCW) ? c1.h + (distCW * p) : c1.h - (distCCW * p);
      if (h < 0) h = 1 + h;
      if (h > 1) h = h - 1;
      var s = (1 - p) * c1.s + p * c2.s;
      var b = (1 - p) * c1.b + p * c2.b;
      // add to gradient array
      gradient[gradientIndex] = {h:h, s:s, b:b};
      gradientIndex ++;
    }
  }
  return gradient;
}
Run Code Online (Sandbox Code Playgroud)

Jac*_*ack 11

您应该只需要找出从开始色调到结束色调的最短路径.这可以很容易地完成,因为色调值的范围是0到255.

您可以先从较高的色调中减去较低的色调,然后在较低的色调中加256,再次检查交换操作数的差异.

int maxCCW = higherHue - lowerHue;
int maxCW = (lowerHue+256) - higherHue;
Run Code Online (Sandbox Code Playgroud)

因此,您将获得两个值,较大的一个决定您应该顺时针还是逆时针.然后你必须找到一种方法使插值在模数256的色调上运行,所以如果你是插值246,20如果系数是>= 0.5f你应该将色调重置为0(因为它达到256,hue = hue%256无论如何).

实际上,如果你在0上插值时不关心色调,但只是在计算新色调后应用模数运算符它应该可以工作.


leg*_*s2k 7

虽然这个答案很晚,但公认的答案是不正确的,即色调应该在 [0, 255] 之内;也可以通过更清晰的解释和代码来做更多的正义。

Hue是区间[0, 360)内的角值;一个完整的圆圈,其中 0 = 360。HSV 颜色空间比 RGB 更容易可视化,对人类更直观。HSV 形成一个圆柱体,其中的切片在许多颜色选择器中显示,而 RGB 实际上是一个立方体,对于颜色选择器来说并不是一个很好的选择;大多数使用它的人必须使用比 HSV 选择器所需的更多的滑块。

插入色调时的要求是选择较小的弧线以从一种色调到达另一种色调。所以给定两个色调值,有四种可能性,下面给出了示例角度:

? |  ? 180  |  > 180
--|---------|---------
+ |  40, 60 | 310, 10
? |  60, 40 | 10, 310

if ? = 180 then both +/? rotation are valid options
Run Code Online (Sandbox Code Playgroud)

让我们以+逆时针和?顺时针旋转为例。如果绝对值的差异超过 180,则将其归一化为 ± 360,以确保幅度在 180 以内;这也正确地扭转了方向。

var d = h2 - h1;
var delta = d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0);
Run Code Online (Sandbox Code Playgroud)

现在只需除以delta所需的步数即可获得每个循环迭代的权重,以在插值期间添加到起始角度。

var new_angle = start + (i * delta);
Run Code Online (Sandbox Code Playgroud)

相关功能摘自完整代码如下:

function interpolate(h1, h2, steps) {
  var d = h2 - h1;
  var delta = (d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0)) / (steps + 1.0);
  var turns = [];
  for (var i = 1; d && i <= steps; ++i)
    turns.push(((h1 + (delta * i)) + 360) % 360);
  return turns;
}
Run Code Online (Sandbox Code Playgroud)

? |  ? 180  |  > 180
--|---------|---------
+ |  40, 60 | 310, 10
? |  60, 40 | 10, 310

if ? = 180 then both +/? rotation are valid options
Run Code Online (Sandbox Code Playgroud)
var d = h2 - h1;
var delta = d + ((Math.abs(d) > 180) ? ((d < 0) ? 360 : -360) : 0);
Run Code Online (Sandbox Code Playgroud)