【问题标题】:How to perform Bilinear Interpolation on RGB values?如何对 RGB 值执行双线性插值?
【发布时间】:2012-02-08 19:48:27
【问题描述】:

给定黑色像素坐标,我可以通过数学方程 y = mx + c 插值蓝色像素的坐标值。但是新的 RGB 像素值呢?鉴于图中给出了黑色像素 RGB 值,如何获取蓝色像素的加权平均 RGB 值?

非常感谢任何帮助。提前致谢。

【问题讨论】:

  • 您可能需要转换为适当的色彩空间(例如 HSV),在那里进行插值,然后再转换回 RGB。
  • 嗯,不,一点也不。您不需要转换为 hsv 或任何其他颜色空间。确实有些空间有不同的属性。

标签: image math image-processing color-space


【解决方案1】:

(这可能会很长。我会尽量保持简短,在这种情况下,我可能需要返回我的回复来回答问题。)RGB 中的色彩空间插值通常使用三线性插值,可以构建在一对双线性插值之上。但并不要求使用三线性插值。事实上,其他插值通常更好,例如单纯(或四面体)插值通常由于各种原因优于三线性插值。有几种这样的晶格四面体解剖可以使用。一个是相当标准的。 (我不会在这里详细介绍,至少现在还不会。)此外,没有理由必须在 RGB 中而不是在其他空间中进行插值,尽管有人可能会争辩说 RGB 有其自身的特殊问题,通常在中性点和近中性点的插值。

与 RGB 和插值相关的特征是中性点被定义为 R=G=B 的点。三线性插值沿该中性轴具有最大误差,并且它通常具有沿中性路径穿过颜色空间的误差的特征(扇形)形状。

那么我们如何在 3-d 中进行插值呢?我会假设一个人在色彩空间的规则点阵中进行插值。在这种情况下,可以识别包含任何单个点的立方体。如果您在一组分散的点内进行插值,那么最简单的解决方案通常是建立这些点的三角剖分,然后在任何给定的四面体内进行简单(线性)插值。无论如何,高阶插值在这里都是有问题的,因为它们在某些情况下会导致颜色问题。例如,人们不希望看到沿梯度的逆转。这可能会发生,因为在曲率相对较高的区域中,基于样条的插值会出现严重的振铃问题。如果涉及到色域映射,那么这种转换肯定会成为一个问题。即使不需要色域映射,仍有色域问题需要处理。

有多种方法可以根据分散的数据构建域的三角剖分。 Alpha 形状基于 Delaunay 三角剖分,是一个合理的选择。但是假设您有一个规则的晶格并希望进行三线性插值,那么问题就简化为在 3-d 中的简单立方体内进行插值。

请注意,三线性插值并不是真正的线性插值,与双线性插值一样。这些方案仅沿晶格的轴是线性的,但沿通过颜色空间的任何其他路径,它们具有多项式特征。因此,三线性插值将沿主对角线或沿通过立方体的大多数一般路径显示三次多项式行为。我们可以说服自己三线性插值并不是真正的线性,因为我们在 8 个点之间进行插值。在 3-d 中,4 个点确定了一个真正的线性插值,作为这些自变量的函数,但我们有 8 个点定义了一个立方体。也就是说,我们将从一个 RGB 空间到另一个 RGB 空间的映射视为真正的 3 个独立映射,因此 RGB --> UVW(我在这里选择了 UVW 来表示一些通用的其他颜色空间,其字符可能是也可能不是 RGB .)

诀窍是,我们通过在一对双线性插值之间进行插值来构建三线性插值。我们通过在沿一条边的一对点之间进行线性插值来构建这些双线性插值,然后在它们之间进行第三次插值。所以实际上,我们可以将三线性插值视为由 7 个简单的线性插值组成。有趣的是,可以证明我们首先沿哪个轴进行插值并不重要。因此,我们可以首先沿 R、B、G 轴进行插值,或者选择任何其他顺序 - 对于选择的任何顺序,三线性插值将是唯一且相同的。 (双线性插值也是如此。)

所以诀窍是,我们如何在两个三元组之间进行线性插值?首先,我们需要确定我们位于这些点之间的线段上的位置。例如,考虑我们颜色空间中的两个点,它们位于立方体的红色 (R) 边缘。我将使用您为这些点显示的相同值,因此:

Q1 = [66, 51, 77]
Q2 = [55, 66, 77]

这些是我们将在其间插值的值,本质上是我们映射的输出,但我们还需要知道这些点在输入 RGB 空间中的位置。所以假设这些坐标,基于它们来自的立方体的坐标,是:

P1 = [0, 0, 0]
P2 = [1, 0, 0]

正如我所写的,这是一个 3-d 单位立方体,所以其他点将位于

P3 = [0, 1, 0]
P4 = [1, 1, 0]
P5 = [0, 0, 1]
P6 = [1, 0, 1]
P7 = [0, 1, 1]
P8 = [1, 1, 1]

当然,任何通用立方体也可以,没有理由让它成为真正的立方体。任何 3-d 右矩形 4 面棱镜也可以在这里工作。您总是可以将事物转换为单位立方体。

现在,假设我们希望在 P1 和 P2 之间沿着立方体的这条边插入 Q1 和 Q2 定义的域?沿着该边缘选择一些点。您可以看到,只有 R 在这些点之间的边沿变化,因此我们只关心插值点处的 R 值。以沿边缘的距离的百分比来考虑它。插值仅仅是两个端点的加权平均,一个线性组合。因此对于红色通道中从 0 到 1 边缘的红色值为 r 的点,我们的插值将是

Q(r) = Q1*(1-r) + Q2*r

如您所见,当 r 为 1/2 时,即在边缘的中间,我们的插值将减少到

Q(1/2,0,0) = (Q1 + Q2)/2

从逻辑上讲,中点值将是两个端点的平均值。您独立执行每个输出通道的插值。

Q(1/2,0,0) = ([66, 51, 77] + [55, 66, 77])/2 = [60.5, 58.5, 77]

这对恢复端点有用吗?当然可以。当 r = 0 或 r = 1 时,您可以看到它返回的正是对应的 Q1 或 Q2。

再一次,您沿着四条红色边中的每条边对三线性插值进行插值。然后你再做两次插值,也许沿着我们上面得到的四个结果的绿色边缘。最后,沿着蓝色边缘再进行一次插值以获得三线性插值。同样,选择插值轴的顺序无关紧要。结果在数学上是相同的。

如果您停留在双线性插值上,那么就有三个这样的线性插值。是的,双线性插值或三线性插值也可以作为矩形(或立方体)的所有 4 个(或 8 个)角的加权组合来完成。这可以留给未来。

【讨论】:

  • 嗨 Woodchips,我迄今为止自己编写的双线性插值算法只允许将图像缩放 2 倍。我想将它扩展到整数倍,如 3x、4x .. N 次。我一直试图弄清楚如何将原始图像中的可用像素放入新图像的新缓冲区位置,以及如何确定这些像素之间的空白。现在是硬编码,你能当我的老师吗?我现在真的很迷茫。 :) 希望早日收到你的消息。如果你想看,我可以把代码发给你。
【解决方案2】:

您独立地对值进行插值,分别为 R、G 和 B 执行计算。例如,在 (200,50,10) 和 (0,0,0) 之间进行插值得到 (100,25,5) .

【讨论】:

  • 这在 RGB 颜色空间中可以正常工作吗?你不应该在 HSV 空间做这种事情吗?
  • 在 RGB 中对我来说效果很好;我过去曾将它用于图像处理。
  • @Paul - 不。无需转换为另一个色彩空间。 RGB 与任何颜色空间一样有效。这是色彩插值方法方面的专家——我说的。 RGB 空间中唯一需要担心的插值问题是中性点会发生什么,这仅与所使用的插值类型有关。例如,三线性插值会导致中性点的 RGB 空间出现问题。
【解决方案3】:
/*
  resize an image using bilinear interpolation
*/
void bilerp(unsigned char *dest, int dwidth, int dheight, unsigned char *src, int swidth, int sheight)
{
  float a, b;
  float red, green, blue, alpha;
  float dx, dy;
  float rx, ry;
  int x, y;
  int index0, index1, index2, index3;

  dx = ((float) swidth)/dwidth;
  dy = ((float) sheight)/dheight;
  for(y=0, ry = 0;y<dheight-1;y++, ry += dy)
  {
    b = ry - (int) ry;
    for(x=0, rx = 0;x<dwidth-1;x++, rx += dx)
    {
      a = rx - (int) rx;
      index0 = (int)ry * swidth + (int) rx;
      index1 = index0 + 1;
      index2 = index0 + swidth;     
      index3 = index0 + swidth + 1;

      red = src[index0*4] * (1.0f-a)*(1.0f-b);
      green = src[index0*4+1] * (1.0f-a)*(1.0f-b);
      blue = src[index0*4+2] * (1.0f-a)*(1.0f-b);
      alpha = src[index0*4+3] * (1.0f-a)*(1.0f-b);
      red += src[index1*4] * (a)*(1.0f-b);
      green += src[index1*4+1] * (a)*(1.0f-b);
      blue += src[index1*4+2] * (a)*(1.0f-b);
      alpha += src[index1*4+3] * (a)*(1.0f-b);
      red += src[index2*4] * (1.0f-a)*(b);
      green += src[index2*4+1] * (1.0f-a)*(b);
      blue += src[index2*4+2] * (1.0f-a)*(b);
      alpha += src[index2*4+3] * (1.0f-a)*(b);
      red += src[index3*4] * (a)*(b);
      green += src[index3*4+1] * (a)*(b);
      blue += src[index3*4+2] * (a)*(b);
      alpha += src[index3*4+3] * (a)*(b);

      red = red < 0 ? 0 : red > 255 ? 255 : red;
      green = green < 0 ? 0 : green > 255 ? 255 : green;
      blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
      alpha = alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;

      dest[(y*dwidth+x)*4] = (unsigned char) red;
      dest[(y*dwidth+x)*4+1] = (unsigned char) green;
      dest[(y*dwidth+x)*4+2] = (unsigned char) blue;
      dest[(y*dwidth+x)*4+3] = (unsigned char) alpha;
    }
    index0 = (int)ry * swidth + (int) rx;
    index1 = index0;
    index2 = index0 + swidth;     
    index3 = index0 + swidth;   

    red = src[index0*4] * (1.0f-a)*(1.0f-b);
    green = src[index0*4+1] * (1.0f-a)*(1.0f-b);
    blue = src[index0*4+2] * (1.0f-a)*(1.0f-b);
    alpha = src[index0*4+3] * (1.0f-a)*(1.0f-b);
    red += src[index1*4] * (a)*(1.0f-b);
    green += src[index1*4+1] * (a)*(1.0f-b);
    blue += src[index1*4+2] * (a)*(1.0f-b);
    alpha += src[index1*4+3] * (a)*(1.0f-b);
    red += src[index2*4] * (1.0f-a)*(b);
    green += src[index2*4+1] * (1.0f-a)*(b);
    blue += src[index2*4+2] * (1.0f-a)*(b);
    alpha += src[index2*4+3] * (1.0f-a)*(b);
    red += src[index3*4] * (a)*(b);
    green += src[index3*4+1] * (a)*(b);
    blue += src[index3*4+2] * (a)*(b);
    alpha += src[index3*4+3] * (a)*(b);

    red = red < 0 ? 0 : red > 255 ? 255 : red;
    green = green < 0 ? 0 : green > 255 ? 255 : green;
    blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
    alpha = alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;

    dest[(y*dwidth+x)*4] = (unsigned char) red;
    dest[(y*dwidth+x)*4+1] = (unsigned char) green;
    dest[(y*dwidth+x)*4+2] = (unsigned char) blue;
    dest[(y*dwidth+x)*4+3] = (unsigned char) alpha;
  }
  index0 = (int)ry * swidth + (int) rx;
  index1 = index0;
  index2 = index0 + swidth;     
  index3 = index0 + swidth;   

  for(x=0, rx = 0;x<dwidth-1;x++, rx += dx)
  {
    a = rx - (int) rx;
    index0 = (int)ry * swidth + (int) rx;
    index1 = index0 + 1;
    index2 = index0;     
    index3 = index0;

    red = src[index0*4] * (1.0f-a)*(1.0f-b);
    green = src[index0*4+1] * (1.0f-a)*(1.0f-b);
    blue = src[index0*4+2] * (1.0f-a)*(1.0f-b);
    alpha = src[index0*4+3] * (1.0f-a)*(1.0f-b);
    red += src[index1*4] * (a)*(1.0f-b);
    green += src[index1*4+1] * (a)*(1.0f-b);
    blue += src[index1*4+2] * (a)*(1.0f-b);
    alpha += src[index1*4+3] * (a)*(1.0f-b);
    red += src[index2*4] * (1.0f-a)*(b);
    green += src[index2*4+1] * (1.0f-a)*(b);
    blue += src[index2*4+2] * (1.0f-a)*(b);
    alpha += src[index2*4+3] * (1.0f-a)*(b);
    red += src[index3*4] * (a)*(b);
    green += src[index3*4+1] * (a)*(b);
    blue += src[index3*4+2] * (a)*(b);
    alpha += src[index3*4+3] * (a)*(b);

    red = red < 0 ? 0 : red > 255 ? 255 : red;
    green = green < 0 ? 0 : green > 255 ? 255 : green;
    blue = blue < 0 ? 0 : blue > 255 ? 255 : blue;
    alpha = alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;

    dest[(y*dwidth+x)*4] = (unsigned char) red;
    dest[(y*dwidth+x)*4+1] = (unsigned char) green;
    dest[(y*dwidth+x)*4+2] = (unsigned char) blue;
    dest[(y*dwidth+x)*4+3] = (unsigned char) alpha;
  }

   dest[(y*dwidth+x)*4] = src[((sheight-1)*swidth+swidth-1)*4];
   dest[(y*dwidth+x)*4+1] = src[((sheight-1)*swidth+swidth-1)*4+1];
   dest[(y*dwidth+x)*4+2] = src[((sheight-1)*swidth+swidth-1)*4+2];
   dest[(y*dwidth+x)*4+3] = src[((sheight-1)*swidth+swidth-1)*4+3];
}  

此处维护的代码

https://github.com/MalcolmMcLean/babyxrc/blob/master/src/resize.c

【讨论】:

    猜你喜欢
    • 2012-01-29
    • 2016-09-20
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2020-04-22
    • 2012-10-08
    • 1970-01-01
    相关资源
    最近更新 更多