Math 逆双线性插值?

Math 逆双线性插值?,math,graphics,geometry,2d,bilinear-interpolation,Math,Graphics,Geometry,2d,Bilinear Interpolation,我有四个2d点,p0=(x0,y0),p1=(x1,y1),等等,它们形成了一个四边形。在我的例子中,四边形不是矩形的,但它至少应该是凸的 p2 --- p3 | | t | p | | | p0 --- p1 s 我用的是双线性插值。S和T在[0..1]范围内,插值点由下式给出: bilerp(s,t) = t*(s*p3+(1-s)*p2) + (1-t)*(s*p1+(1-s)*p0) 问题是。。我有一个2d点p,我知道它在四边形内。

我有四个2d点,p0=(x0,y0),p1=(x1,y1),等等,它们形成了一个四边形。在我的例子中,四边形不是矩形的,但它至少应该是凸的

  p2 --- p3
  |      |
t |  p   |
  |      |
  p0 --- p1
     s
我用的是双线性插值。S和T在[0..1]范围内,插值点由下式给出:

bilerp(s,t) = t*(s*p3+(1-s)*p2) + (1-t)*(s*p1+(1-s)*p0)
问题是。。我有一个2d点p,我知道它在四边形内。我想找到s,t,当使用双线性插值时,它会给我这个点

是否有一个简单的公式来反转双线性插值



谢谢你的解决方案。我将Naaff解决方案的实现作为一个wiki发布。

好吧,如果p是一个2D点,是的,你可以很容易地得到它。在这种情况下,S是T处四边形总宽度的分数分量,T同样是S处四边形总高度的分数分量


但是,如果p是标量,则不一定可能,因为双线性插值函数不一定是整体的。

如果只有一个p值,使得p位于正方形四个角的最小值和最大值之间,则通常不可能找到单一解(s,t)这样,双线性插值将为您提供该值

一般来说,在正方形内部会有无穷多个解(s,t)。它们将沿着一条曲线(双曲线)路径穿过广场

如果你的函数是向量值函数,那么你在正方形的某个未知点有两个已知值?给定正方形每个角的两个参数的已知值,则可能存在一个解决方案,但无法保证这一点。请记住,我们可以将其视为两个独立的问题。它们的解都是沿着一条穿过正方形的双曲轮廓线。如果这对等高线在正方形内相交,则存在解决方案。如果它们不交叉,则不存在解决方案

您还会问,是否存在解决问题的简单公式。对不起,我不太明白。正如我所说,曲线是双曲线


一种解决方案是切换到不同的插值方法。所以不要双线性,而是把正方形分成一对三角形。在每个三角形中,我们现在可以使用真正的线性插值。现在我们可以在每个三角形的2个未知数中求解2个方程组的线性方程组。每个三角形中可能有一个解,除了一个罕见的退化情况,其中相应的分段线性轮廓线恰好是共相关的。

一些回答稍微误解了您的问题。也就是说,他们假设你得到的是未知插值函数的值,而不是你想要找到(s,t)坐标的四边形中的插值位置p(x,y)。这是一个更简单的问题,保证有一个解决方案,即两条直线通过四边形的交点

其中一条线将穿过段p0p1和p2p3,另一条线将穿过p0p2和p1p3,类似于轴对齐的情况。这些线是由p(x,y)的位置唯一定义的,并且显然会在这一点相交

考虑到穿过p0p1和p2p3的线,我们可以想象这样的线族,对于我们选择的每个不同的s值,每个以不同的宽度切割四边形。如果我们修正一个s值,我们可以通过设置t=0和t=1来找到两个端点

因此,首先假设该行具有以下形式: y=a0*x+b0

然后我们知道这条线的两个端点,如果我们固定一个给定的s值。它们是:

(1-s)p0+(s)p1

(1-s)p2+(s)p3

给定这两个端点,我们可以通过将它们插入直线方程,并将a0和b0作为s的函数求解来确定直线族。设置s值将给出特定行的公式。我们现在所需要的就是找出这个族中的哪条线到达我们的点p(x,y)。只要把p(x,y)的坐标插入我们的直线公式,我们就可以解出s的目标值


也可以用相应的方法来求t。

因为你是在2D中工作的,你的
bilerp
函数实际上是两个方程,一个代表x,一个代表y。它们可以改写为以下形式:

x = t * s * A.x + t * B.x + s * C.x + D.x
y = t * s * A.y + t * B.y + s * C.y + D.y
A*(1-s)^2 + B*2s(1-s) + C*s^2 = 0
其中:

A = p3 - p2 - p1 + p0
B = p2 - p0
C = p1 - p0
D = p0
A = (p0-p) X (p0-p2)
B = ( (p0-p) X (p1-p3) + (p1-p) X (p0-p2) ) / 2
C = (p1-p) X (p1-p3)

重写第一个方程,得到
t
,用
s
,代入第二个方程,然后求解
s
,我认为最容易将问题视为一个相交问题:点p与由p0,p1定义的任意二维双线性曲面相交的参数位置(s,t)是什么,p2和p3

我将采取的解决这个问题的方法与tspauld的建议类似

从x和y的两个方程开始:

x = (1-s)*( (1-t)*x0 + t*x2 ) + s*( (1-t)*x1 + t*x3 )
y = (1-s)*( (1-t)*y0 + t*y2 ) + s*( (1-t)*y1 + t*y3 )
求解t:

t = ( (1-s)*(x0-x) + s*(x1-x) ) / ( (1-s)*(x0-x2) + s*(x1-x3) )
t = ( (1-s)*(y0-y) + s*(y1-y) ) / ( (1-s)*(y0-y2) + s*(y1-y3) )
现在我们可以将这两个方程彼此相等,以消除t。把所有的东西移到左手边并简化,我们得到一个方程式:

x = t * s * A.x + t * B.x + s * C.x + D.x
y = t * s * A.y + t * B.y + s * C.y + D.y
A*(1-s)^2 + B*2s(1-s) + C*s^2 = 0
其中:

A = p3 - p2 - p1 + p0
B = p2 - p0
C = p1 - p0
D = p0
A = (p0-p) X (p0-p2)
B = ( (p0-p) X (p1-p3) + (p1-p) X (p0-p2) ) / 2
C = (p1-p) X (p1-p3)
请注意,我使用了运算符X来表示(例如,p0xp1=x0*y1-y0*x1)。我将这个方程格式化为二次方程,因为这使事情变得更优雅,数值上更稳定。s的解是这个方程的根。我们可以使用Bernstein多项式的二次公式找到根:

s = ( (A-B) +- sqrt(B^2 - A*C) ) / ( A - 2*B + C )
由于+-,二次公式给出了两个答案。如果您只对p位于双线性曲面内的解感兴趣,那么您可以放弃s不在0和1之间的任何答案。要找到t,只需将s替换回上面两个方程中的一个,我们用s来求解t

我要指出一个重要的特殊情况。如果分母A-2*B+C=0,那么二次多项式实际上是线性的。在这种情况下,必须使用一个更简单的方程来求s:

s = A / (A-C)
除非A-C=0,否则这只会给出一个解决方案。如果A=C,则有两种情况:A=C=0表示s的所有值都包含p,否则
(s,t) = (s,t) - J^-1 r,
Iteration  Error
1          0.0610
2          9.8914e-04
3          2.6872e-07
4          1.9810e-14
5          5.5511e-17 (machine epsilon)
function q = bilinearInverse(p,p1,p2,p3,p4,iter)
%Computes the inverse of the bilinear map from [0,1]^2 to the convex
% quadrilateral defined by the ordered points p1 -> p2 -> p3 -> p4 -> p1.
%Uses Newton's method. Inputs must be column vectors.
    q = [0.5; 0.5]; %initial guess
    for k=1:iter
        s = q(1);
        t = q(2);
        r = p1*(1-s)*(1-t) + p2*s*(1-t) + p3*s*t + p4*(1-s)*t - p;%residual
        Js = -p1*(1-t) + p2*(1-t) + p3*t - p4*t; %dr/ds
        Jt = -p1*(1-s) - p2*s + p3*s + p4*(1-s); %dr/dt
        J = [Js,Jt];
        q = q - J\r;
        q = max(min(q,1),0);
    end
end
% Test_bilinearInverse.m
p1=[0.1;-0.1]; 
p2=[2.2;-0.9]; 
p3=[1.75;2.3]; 
p4=[-1.2;1.1];

q0 = rand(2,1);
s0 = q0(1); 
t0 = q0(2);
p = p1*(1-s0)*(1-t0) + p2*s0*(1-t0) + p3*s0*t0 + p4*(1-s0)*t0;

iter=5;
q = bilinearInverse(p,p1,p2,p3,p4,iter);

err = norm(q0-q);
disp(['Error after ',num2str(iter), ' iterations: ', num2str(err)])
>> test_bilinearInverse
Error after 5 iterations: 1.5701e-16
function [ss,tt] = bilinearInverseFast(px,py, p1x,p1y, p2x,p2y, p3x,p3y, p4x,p4y, iter)
%Computes the inverse of the bilinear map from [0,1]^2 to the convex
% quadrilateral defined by the ordered points p1 -> p2 -> p3 -> p4 -> p1,
% where the p1x is the x-coordinate of p1, p1y is the y-coordinate, etc.
% Vectorized: if you have a lot of quadrilaterals and 
% points to interpolate, then p1x(k) is the x-coordinate of point p1 on the
% k'th quadrilateral, and so forth.
%Uses Newton's method. Inputs must be column vectors.
    ss = 0.5 * ones(length(px),1);
    tt = 0.5 * ones(length(py),1);
    for k=1:iter
        r1 = p1x.*(1-ss).*(1-tt) + p2x.*ss.*(1-tt) + p3x.*ss.*tt + p4x.*(1-ss).*tt - px;%residual
        r2 = p1y.*(1-ss).*(1-tt) + p2y.*ss.*(1-tt) + p3y.*ss.*tt + p4y.*(1-ss).*tt - py;%residual

        J11 = -p1x.*(1-tt) + p2x.*(1-tt) + p3x.*tt - p4x.*tt; %dr/ds
        J21 = -p1y.*(1-tt) + p2y.*(1-tt) + p3y.*tt - p4y.*tt; %dr/ds
        J12 = -p1x.*(1-ss) - p2x.*ss + p3x.*ss + p4x.*(1-ss); %dr/dt
        J22 = -p1y.*(1-ss) - p2y.*ss + p3y.*ss + p4y.*(1-ss); %dr/dt

        inv_detJ = 1./(J11.*J22 - J12.*J21);

        ss = ss - inv_detJ.*(J22.*r1 - J12.*r2);
        tt = tt - inv_detJ.*(-J21.*r1 + J11.*r2);

        ss = min(max(ss, 0),1);
        tt = min(max(tt, 0),1);
    end
end
[a,b;c,d]^-1 = (1/(ad-bc))[d, -b; -c, a]
% test_bilinearInverseFast.m
n_quads = 1e6; % 1 million quads
iter = 8;

% Make random quadrilaterals, ensuring points are ordered convex-ly
n_randpts = 4;
pp_xx = zeros(n_randpts,n_quads);
pp_yy = zeros(n_randpts,n_quads);
disp('Generating convex point ordering (may take some time).')
for k=1:n_quads
    while true
        p_xx = randn(4,1);
        p_yy = randn(4,1);
        conv_inds = convhull(p_xx, p_yy);
        if length(conv_inds) == 5
            break
        end
    end
    pp_xx(1:4,k) = p_xx(conv_inds(1:end-1));
    pp_yy(1:4,k) = p_yy(conv_inds(1:end-1));
end

pp1x = pp_xx(1,:);
pp1y = pp_yy(1,:);
pp2x = pp_xx(2,:);
pp2y = pp_yy(2,:);
pp3x = pp_xx(3,:);
pp3y = pp_yy(3,:);
pp4x = pp_xx(4,:);
pp4y = pp_yy(4,:);

% Make random interior points
ss0 = rand(1,n_quads);
tt0 = rand(1,n_quads);

ppx = pp1x.*(1-ss0).*(1-tt0) + pp2x.*ss0.*(1-tt0) + pp3x.*ss0.*tt0 + pp4x.*(1-ss0).*tt0;
ppy = pp1y.*(1-ss0).*(1-tt0) + pp2y.*ss0.*(1-tt0) + pp3y.*ss0.*tt0 + pp4y.*(1-ss0).*tt0;
pp = [ppx; ppy];

% Run fast inverse bilinear interpolation code:
disp('Running inverse bilinear interpolation.')
tic
[ss,tt] = bilinearInverseFast(ppx,ppy, pp1x,pp1y, pp2x,pp2y, pp3x,pp3y, pp4x,pp4y, 10);
time_elapsed = toc;

disp(['Number of quadrilaterals: ', num2str(n_quads)])
disp(['Inverse bilinear interpolation took: ', num2str(time_elapsed), ' seconds'])

err = norm([ss0;tt0] - [ss;tt],'fro')/norm([ss0;tt0],'fro');
disp(['Error: ', num2str(err)])
>> test_bilinearInverseFast
Generating convex point ordering (may take some time).
Running inverse bilinear interpolation.
Number of quadrilaterals: 1000000
Inverse bilinear interpolation took: 0.5274 seconds
Error: 8.6881e-16
function ss = trilinearInverse(p, p1,p2,p3,p4,p5,p6,p7,p8, iter)
%Computes the inverse of the trilinear map from [0,1]^3 to the box defined
% by points p1,...,p8, where the points are ordered consistent with
% p1~(0,0,0), p2~(0,0,1), p3~(0,1,0), p4~(1,0,0), p5~(0,1,1),
% p6~(1,0,1), p7~(1,1,0), p8~(1,1,1)
%Uses Gauss-Newton method. Inputs must be column vectors.
    tol = 1e-9;
    ss = [0.5; 0.5; 0.5]; %initial guess
    for k=1:iter
        s = ss(1);
        t = ss(2);
        w = ss(3);

        r = p1*(1-s)*(1-t)*(1-w) + p2*s*(1-t)*(1-w) + ...
            p3*(1-s)*t*(1-w)     + p4*(1-s)*(1-t)*w + ...
            p5*s*t*(1-w)         + p6*s*(1-t)*w + ...
            p7*(1-s)*t*w         + p8*s*t*w - p;

        disp(['k= ', num2str(k), ...
            ', residual norm= ', num2str(norm(r)),...
            ', [s,t,w]= ',num2str([s,t,w])])
        if (norm(r) < tol)
            break
        end

        Js = -p1*(1-t)*(1-w) + p2*(1-t)*(1-w) + ...
             -p3*t*(1-w)     - p4*(1-t)*w + ...
              p5*t*(1-w)     + p6*(1-t)*w + ...
             -p7*t*w         + p8*t*w;

         Jt = -p1*(1-s)*(1-w) - p2*s*(1-w) + ...
               p3*(1-s)*(1-w) - p4*(1-s)*w + ...
               p5*s*(1-w)     - p6*s*w + ...
               p7*(1-s)*w     + p8*s*w;

         Jw = -p1*(1-s)*(1-t) - p2*s*(1-t) + ...
              -p3*(1-s)*t     + p4*(1-s)*(1-t) + ...
              -p5*s*t         + p6*s*(1-t) + ...
               p7*(1-s)*t     + p8*s*t;

        J = [Js,Jt,Jw];
        ss = ss - J\r;
    end
end
%test_trilinearInverse.m
h = 0.25;
p1 = [0;0;0] + h*randn(3,1);
p2 = [0;0;1] + h*randn(3,1);
p3 = [0;1;0] + h*randn(3,1);
p4 = [1;0;0] + h*randn(3,1);
p5 = [0;1;1] + h*randn(3,1);
p6 = [1;0;1] + h*randn(3,1);
p7 = [1;1;0] + h*randn(3,1);
p8 = [1;1;1] + h*randn(3,1);

s0 = rand;
t0 = rand;
w0 = rand;
p = p1*(1-s0)*(1-t0)*(1-w0) + p2*s0*(1-t0)*(1-w0) + ...
            p3*(1-s0)*t0*(1-w0)     + p4*(1-s0)*(1-t0)*w0 + ...
            p5*s0*t0*(1-w0)         + p6*s0*(1-t0)*w0 + ...
            p7*(1-s0)*t0*w0         + p8*s0*t0*w0;

ss = trilinearInverse(p, p1,p2,p3,p4,p5,p6,p7,p8);

disp(['error= ', num2str(norm(ss - [s0;t0;w0]))])
test_trilinearInverse
k= 1, residual norm= 0.38102, [s,t,w]= 0.5         0.5         0.5
k= 2, residual norm= 0.025324, [s,t,w]= 0.37896     0.59901     0.17658
k= 3, residual norm= 0.00037108, [s,t,w]= 0.40228     0.62124     0.15398
k= 4, residual norm= 9.1441e-08, [s,t,w]= 0.40218     0.62067     0.15437
k= 5, residual norm= 3.3548e-15, [s,t,w]= 0.40218     0.62067     0.15437
error= 4.8759e-15
void invbilerp( float x, float y, float x00, float x01, float x10, float x11,  float y00, float y01, float y10, float y11, float [] uv ){

// substition 1 ( see. derivation )
float dx0 = x01 - x00;
float dx1 = x11 - x10;
float dy0 = y01 - y00;
float dy1 = y11 - y10;

// substitution 2 ( see. derivation )
float x00x = x00 - x;
float xd   = x10 - x00;
float dxd  = dx1 - dx0; 
float y00y = y00 - y;
float yd   = y10 - y00;
float dyd  = dy1 - dy0;

// solution of quadratic equations
float c =   x00x*yd - y00y*xd;
float b =   dx0*yd  + dyd*x00x - dy0*xd - dxd*y00y;
float a =   dx0*dyd - dy0*dxd;
float D2 = b*b - 4*a*c;
float D  = sqrt( D2 );
float u = (-b - D)/(2*a);

// backsubstitution of "u" to obtain "v"
float v;
float denom_x = xd + u*dxd;
float denom_y = yd + u*dyd;
if( abs(denom_x)>abs(denom_y) ){  v = -( x00x + u*dx0 )/denom_x;  }else{  v = -( y00y + u*dy0 )/denom_y;  }
uv[0]=u;
uv[1]=v;

/* 
// do you really need second solution ? 
u = (-b + D)/(2*a);
denom_x = xd + u*dxd;
denom_y = yd + u*dyd;
if( abs(denom_x)>abs(denom_y) ){  v = -( x00x + u*dx0 )/denom_x;  }else{  v2 = -( y00y + u*dy0 )/denom_y;  }
uv[2]=u;
uv[3]=v;
*/ 
}
// starting from bilinear interpolation
(1-v)*(  (1-u)*x00 + u*x01 ) + v*( (1-u)*x10 + u*x11 )     - x
(1-v)*(  (1-u)*y00 + u*y01 ) + v*( (1-u)*y10 + u*y11 )     - y

substition 1:
dx0 = x01 - x00
dx1 = x11 - x10
dy0 = y01 - y00
dy1 = y11 - y10

we get:
(1-v) * ( x00 + u*dx0 )  + v * (  x10 + u*dx1  )  - x   = 0
(1-v) * ( y00 + u*dy0 )  + v * (  y10 + u*dy1  )  - y   = 0

we are trying to extract "v" out
x00 + u*dx0   + v*(  x10 - x00 + u*( dx1 - dx0 ) )  - x = 0
y00 + u*dy0   + v*(  y10 - y00 + u*( dy1 - dy0 ) )  - y = 0

substition 2:
x00x = x00 - x
xd   = x10 - x00
dxd  = dx1 - dx0 
y00y = y00 - y
yd   = y10 - y00
dyd  = dy1 - dy0 

// much nicer
x00x + u*dx0   + v*(  xd + u*dxd )  = 0
y00x + u*dy0   + v*(  yd + u*dyd )  = 0

// this equations for "v" are used for back substition
v = -( x00x + u*dx0 ) / (  xd + u*dxd  )
v = -( y00x + u*dy0 ) / (  yd + u*dyd  )

// but for now, we eliminate "v" to get one eqution for "u"  
( x00x + u*dx0 ) / (  xd + u*dxd )  =  ( y00y + u*dy0 ) / (  yd + u*dyd  )

put denominators to other side

( x00x + u*dx0 ) * (  yd + u*dyd )  =  ( y00y + u*dy0 ) * (  xd + u*dxd  )

x00x*yd + u*( dx0*yd + dyd*x00x ) + u^2* dx0*dyd = y00y*xd + u*( dy0*xd + dxd*y00y ) + u^2* dy0*dxd  

// which is quadratic equation with these coefficients 
c =   x00x*yd - y00y*xd
b =   dx0*yd  + dyd*x00x - dy0*xd - dxd*y00y
a =   dx0*dyd - dy0*dxd