具有非对齐输入点的双线性插值

时间:2014-05-28 20:12:37

标签: algorithm interpolation

我有一组非网格对齐的输入值与网格对齐的输出值相关联。给定一个新的输入值,我想找到输出:

Four points in a rectangle with inputs varying on each axis, but outputs representing rectangular inputs.

(这些是X,Y坐标,将不精确的非方形眼动追踪输入设备校准到屏幕上的确切位置。)

这看起来像Bilinear Interpolation,但我的输入值不是网格对齐的。给定输入,我如何计算出合理的输出值?


答案:在这种情况下,我有输入和输出点集,实际需要的是执行inverse bilinear interpolation来查找输入点内的U,V坐标。四,然后使用那些U,V坐标在输出四边形上执行正常的双线性插值(如下面Nico的答案所述)。

2 个答案:

答案 0 :(得分:10)

你可以在任何凸四边形中进行双线性插值。笛卡尔网格稍微简单一些,因为插值参数的计算是微不足道的。在一般情况下,您插入如下:

parameters alpha, beta
interpolated value = (1 - alpha) * ((1 - beta) * p1 + beta * p2) + alpha * ((1 - beta) * p3 + beta * p4)

为了计算参数,您必须求解方程组。将您的输入值放在p1p4的位置,然后求解alphabeta

然后将输出值放在p1p4的位置,并使用计算出的参数计算最终插值输出值。

对于常规网格,参数计算归结为:

alpha = x / cell width
beta  = y / cell height

自动解决方程式。

以下是alpha=0.3beta=0.6

的示例插值

Sample interpolation

实际上,方程式可以通过分析求解。然而,公式非常难看。因此,迭代方法可能更好。方程组有两种解决方案。您需要选择两个参数都在[0,1]中的解决方案。

第一个解决方案:

alpha = -(b e - a f + d g - c h + sqrt(-4 (c e - a g) (d f - b h) +
        (b e - a f + d g - c h)^2))/(2 c e - 2 a g)    
beta  = (b e - a f - d g + c h + sqrt(-4 (c e - a g) (d f - b h) + 
        (b e - a f + d g - c h)^2))/(2 c f - 2 b g)

其中

a = -p1.x + p3.x
b = -p1.x + p2.x
c = p1.x - p2.x - p3.x + p4.x
d = center.x - p1.x
e = -p1.y + p3.y
f = -p1.y + p2.y
g = p1.y - p2.y - p3.y + p4.y
h = center.y - p1.y

第二个解决方案:

alpha = (-b e + a f - d g + c h + sqrt(-4 (c e - a g) (d f - b h) + 
        (b e - a f + d g - c h)^2))/(2 c e - 2 a g)
beta  = -((-b e + a f + d g - c h + sqrt(-4 (c e - a g) (d f - b h) + 
        (b e - a f + d g - c h)^2))/( 2 c f - 2 b g))

答案 1 :(得分:2)

这是我自己的技术,以及用于派生结果值的代码。它需要三个lerps输出值(以及三个百分比计算来确定lerp百分比):

请注意,这不是双线性插值。它不会将输入点的四边形重新映射到输出值的四边形,因为某些输入点可能会导致输出四边形之外的输出值

这里我展示了笛卡尔平面上的非对齐输入值(使用上面问题的样本输入值,为简单起见,乘以10)。

要计算“北”点(顶部绿点),我们计算X轴上的百分比为

  (inputX - northwestX) / (northeastX - northwestX)
= (-4.2 - -19) / (10 - -19)
= 0.51034

我们使用这个百分比通过在前Y值之间进行计算来计算Y轴的截距:

  (targetValue - startValue) * percent + startValue
= (northeastY  - northwestY) * percent + northwestY
= (-8 - -7) * 0.51034 + -7
= -7.51034

我们在'南'边做同样的事情:

  (inputX - southwestX) / (southeastX - southwestX)
= (-4.2 - -11) / (9 - -11)
= 0.34

  (southeastY - southwestY) * percent + southwestY
= (7 - 4) * 0.34 + 4
= 5.02

最后,我们使用这两个值来计算北边和南边之间的最终百分比:

  (inputY - southY) / (northY - southY)
= (1 - 5.02) / (-7.51034 - 5.02)
= 0.3208

有了这三个百分比,我们可以通过点之间的计算来计算我们的最终输出值:

nw = Vector(-150,-100)
ne = Vector( 150,-100)
sw = Vector(-150, 100)
se = Vector( 150, 100)

north  = lerp( nw, ne, 0.51034)       --> (  3.10, -100.00)
south  = lerp( sw, se, 0.34)          --> (-48.00,  100.00)
result = lerp( south, north, 0.3208)  --> (-31.61,   35.84)

最后,这是执行上述操作的一些(Lua)代码。它使用一个可变的Vector对象,该对象支持从另一个向量复制值并将其值转向另一个向量的能力。

-- Creates a bilinear interpolator
-- Corners should be an object with nw/ne/sw/se keys,
-- each of which holds a pair of mutable Vectors
-- { nw={inp=vector1, out=vector2}, … }
function tetragonalBilinearInterpolator(corners)
  local sides = {
    n={ pt=Vector(), pts={corners.nw, corners.ne} },
    s={ pt=Vector(), pts={corners.sw, corners.se} }
  }

  for _,side in pairs(sides) do
    side.minX = side.pts[1].inp.x
    side.diff = side.pts[2].inp.x - side.minX
  end

  -- Mutates the input vector to hold the result
  return function(inpVector)
    for _,side in pairs(sides) do
      local pctX = (inpVector.x - side.minX) / side.diff
      side.pt:copyFrom(side.pts[1].inp):lerp(side.pts[2].inp,pctX)
      side.inpY = side.pt.y
      side.pt:copyFrom(side.pts[1].out):lerp(side.pts[2].out,pctX)
    end
    local pctY = (inpVector.y-sides.s.inpY)/(sides.n.y-sides.s.inpY)
    return inpVector:copyFrom(sides.s.pt):lerp(sides.n.pt,pctY)
  end
end

local interp = tetragonalBilinearInterpolator{
  nw={ inp=Vector(-19,-7),  out=Vector(-150,-100) },
  ne={ inp=Vector( 10,-8),  out=Vector( 150,-100) },
  sw={ inp=Vector(-11, 4),  out=Vector(-150, 100) },
  se={ inp=Vector(  9, 7),  out=Vector( 150, 100) }
}
print(interp(Vector(-4.2, 1))) --> <-31.60 35.84>