我有四个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解决方案的实现发布为维基。
答案 0 :(得分:23)
我认为将问题视为交叉问题是最容易的:参数位置(s,t)是什么,其中点p与由p0,p1,p2和p3定义的任意2D双线性曲面相交。
我将采取的解决此问题的方法与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。将所有内容移到左侧并简化我们得到的形式方程式:
A*(1-s)^2 + B*2s(1-s) + C*s^2 = 0
其中:
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来表示2D cross product(例如,p0 X p1 = x0 * y1 - y0 * x1)。我已经将这个等式格式化为二次Bernstein polynomial,因为这使得事物更加优雅并且在数值上更稳定。 s的解决方案是这个等式的根源。我们可以使用伯恩斯坦多项式的二次公式找到根:
s = ( (A-B) +- sqrt(B^2 - A*C) ) / ( A - 2*B + C )
由于+ - ,二次公式给出了两个答案。如果您只对p位于双线性表面内的解决方案感兴趣,那么您可以丢弃任何s不在0和1之间的答案。要找到t,只需将s替换回上面我们解决的两个方程之一就s而言。
我应该指出一个重要的特例。如果分母A - 2 * B + C = 0,则二次多项式实际上是线性的。在这种情况下,您必须使用更简单的公式来查找s:
s = A / (A-C)
这将为您提供一个解决方案,除非AC = 0.如果A = C,那么您有两种情况:A = C = 0意味着所有值包含p,否则 s的值不包含p。
答案 1 :(得分:9)
可以使用Newton's method迭代求解以下非线性方程组:
p = p0*(1-s)*(1-t) + p1*s*(1-t) + p2*s*t + p3*(1-s)*t.
请注意,有两个方程(方程的x和y分量都相等)和两个未知数(s和t)。
要应用牛顿方法,我们需要residual r,即:
r = p - (p0*(1-s)*(1-t) + p1*s*(1-t) + p2*s*t + p3*(1-s)*t),
和Jacobian matrix J,通过区分残差找到。对于我们的问题,雅可比人是:
J(:,1) = dr/ds = -p0*(1-t) + p1*(1-t) + p2*t - p3*t (first column of matrix)
J(:,2) = dr/dt = -p0*(1-s) - p1*s + p2*s + p3*(1-s) (second column).
要使用Newton的方法,首先从初始猜测(s,t)开始,然后执行以下迭代几次:
(s,t) = (s,t) - J^-1 r,
使用s和t的新值重新计算J和r的每次迭代。在每次迭代中,主要成本是通过求解以J为系数矩阵且r为右手边的2x2线性系统,将雅可比行列式的逆应用于残差(J ^ -1 r)。
该方法的直觉:
直观地说,如果四边形是parallelogram,那么解决问题会容易得多。牛顿方法用连续的平行四边形近似解决四边形问题。在每次迭代中我们
在点(s,t)处使用局部导数信息来近似平行四边形的四边形。
通过求解线性系统,在平行四边形近似下找到正确的(s,t)值。
跳到这个新点并重复。
方法的优点:
正如牛顿型方法所预期的那样,收敛速度非常快。随着迭代的进行,方法不仅越来越接近真实点,而且局部平行四边形近似也变得更加准确,因此收敛速度本身也在增加(在迭代方法术语中,我们说Newton&#39 ; s方法是quadratically convergent)。实际上,这意味着每次迭代时正确数字的数量大约加倍。
这是我做过的随机试验的迭代与错误的代表性表格(见下面的代码):
Iteration Error
1 0.0610
2 9.8914e-04
3 2.6872e-07
4 1.9810e-14
5 5.5511e-17 (machine epsilon)
经过两次迭代后,误差足够小,对于大多数实际用途而言实际上是不可理解的,并且在5次迭代后,结果精确到machine precision的极限。
如果你确定了迭代次数(比如说,对于大多数实际应用程序来说是3次迭代,如果需要非常高的精度,则需要8次迭代),那么算法就有一个非常简单明了的逻辑,其结构非常适合自己高性能计算。无需检查各种特殊边缘情况*,并根据结果使用不同的逻辑。它只是一个包含一些简单公式的for循环。下面我将重点介绍这种方法的优点,而不是传统的基于公式的方法,这些方法可以在互联网的其他答案中找到:
易于编码。只需制作一个for循环并输入几个公式。
没有条件或分支(if / then),这通常会让pipelining efficiency更好。
没有平方根,每次迭代只需要1个分区(如果写得好)。所有其他操作都是简单的加法,减法和乘法。平方根和除法通常比加法/乘法/乘法慢几倍,并且可以在某些架构上(特别是在某些嵌入式系统上)提高cache效率。实际上,如果你深入了解现代编程语言实际上如何计算square roots和divisions,他们都使用牛顿方法的变体,有时在硬件中,有时在软件中取决于建筑。
可以轻松地进行矢量化,以便同时处理具有大量四边形的阵列。请参阅下面的矢量化代码,了解如何执行此操作的示例。
扩展到任意维度。该算法以直接的方式扩展到任意数量维度(2d,3d,4d,...)的逆多线性插值。我在下面包含了一个3D版本,可以想象编写一个简单的递归版本,或者使用自动差异库来转换到n维。牛顿方法通常表现出与维度无关的收敛速度,因此原则上该方法应该可以在当前硬件上扩展到几千维(!)(之后构造和求解n×n矩阵J将可能是限制因素)。
当然,大部分内容还取决于硬件,编译器和许多其他因素,因此您的里程可能会有所不同。
<强>代码:强>
无论如何,这是我的Matlab代码:(我将所有内容发布到公共领域)
基本2D版:
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
快速矢量化2D版本:
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
对于速度,此代码隐式使用以下公式来反演2x2矩阵:
[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
3D版本:
包含一些显示收敛进度的代码。
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
必须注意输入点的排序,因为如果形状具有正体积,则反向多线性插值仅被很好地定义,并且在3D中,更容易选择使形状从内向外变换的点本身。
答案 2 :(得分:6)
这是我对Naaff解决方案的实现,作为社区维基。再次感谢。
这是一个C实现,但应该适用于c ++。它包括模糊测试功能。
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
int equals( double a, double b, double tolerance )
{
return ( a == b ) ||
( ( a <= ( b + tolerance ) ) &&
( a >= ( b - tolerance ) ) );
}
double cross2( double x0, double y0, double x1, double y1 )
{
return x0*y1 - y0*x1;
}
int in_range( double val, double range_min, double range_max, double tol )
{
return ((val+tol) >= range_min) && ((val-tol) <= range_max);
}
/* Returns number of solutions found. If there is one valid solution, it will be put in s and t */
int inverseBilerp( double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double x, double y, double* sout, double* tout, double* s2out, double* t2out )
{
int t_valid, t2_valid;
double a = cross2( x0-x, y0-y, x0-x2, y0-y2 );
double b1 = cross2( x0-x, y0-y, x1-x3, y1-y3 );
double b2 = cross2( x1-x, y1-y, x0-x2, y0-y2 );
double c = cross2( x1-x, y1-y, x1-x3, y1-y3 );
double b = 0.5 * (b1 + b2);
double s, s2, t, t2;
double am2bpc = a-2*b+c;
/* this is how many valid s values we have */
int num_valid_s = 0;
if ( equals( am2bpc, 0, 1e-10 ) )
{
if ( equals( a-c, 0, 1e-10 ) )
{
/* Looks like the input is a line */
/* You could set s=0.5 and solve for t if you wanted to */
return 0;
}
s = a / (a-c);
if ( in_range( s, 0, 1, 1e-10 ) )
num_valid_s = 1;
}
else
{
double sqrtbsqmac = sqrt( b*b - a*c );
s = ((a-b) - sqrtbsqmac) / am2bpc;
s2 = ((a-b) + sqrtbsqmac) / am2bpc;
num_valid_s = 0;
if ( in_range( s, 0, 1, 1e-10 ) )
{
num_valid_s++;
if ( in_range( s2, 0, 1, 1e-10 ) )
num_valid_s++;
}
else
{
if ( in_range( s2, 0, 1, 1e-10 ) )
{
num_valid_s++;
s = s2;
}
}
}
if ( num_valid_s == 0 )
return 0;
t_valid = 0;
if ( num_valid_s >= 1 )
{
double tdenom_x = (1-s)*(x0-x2) + s*(x1-x3);
double tdenom_y = (1-s)*(y0-y2) + s*(y1-y3);
t_valid = 1;
if ( equals( tdenom_x, 0, 1e-10 ) && equals( tdenom_y, 0, 1e-10 ) )
{
t_valid = 0;
}
else
{
/* Choose the more robust denominator */
if ( fabs( tdenom_x ) > fabs( tdenom_y ) )
{
t = ( (1-s)*(x0-x) + s*(x1-x) ) / ( tdenom_x );
}
else
{
t = ( (1-s)*(y0-y) + s*(y1-y) ) / ( tdenom_y );
}
if ( !in_range( t, 0, 1, 1e-10 ) )
t_valid = 0;
}
}
/* Same thing for s2 and t2 */
t2_valid = 0;
if ( num_valid_s == 2 )
{
double tdenom_x = (1-s2)*(x0-x2) + s2*(x1-x3);
double tdenom_y = (1-s2)*(y0-y2) + s2*(y1-y3);
t2_valid = 1;
if ( equals( tdenom_x, 0, 1e-10 ) && equals( tdenom_y, 0, 1e-10 ) )
{
t2_valid = 0;
}
else
{
/* Choose the more robust denominator */
if ( fabs( tdenom_x ) > fabs( tdenom_y ) )
{
t2 = ( (1-s2)*(x0-x) + s2*(x1-x) ) / ( tdenom_x );
}
else
{
t2 = ( (1-s2)*(y0-y) + s2*(y1-y) ) / ( tdenom_y );
}
if ( !in_range( t2, 0, 1, 1e-10 ) )
t2_valid = 0;
}
}
/* Final cleanup */
if ( t2_valid && !t_valid )
{
s = s2;
t = t2;
t_valid = t2_valid;
t2_valid = 0;
}
/* Output */
if ( t_valid )
{
*sout = s;
*tout = t;
}
if ( t2_valid )
{
*s2out = s2;
*t2out = t2;
}
return t_valid + t2_valid;
}
void bilerp( double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double s, double t, double* x, double* y )
{
*x = t*(s*x3+(1-s)*x2) + (1-t)*(s*x1+(1-s)*x0);
*y = t*(s*y3+(1-s)*y2) + (1-t)*(s*y1+(1-s)*y0);
}
double randrange( double range_min, double range_max )
{
double range_width = range_max - range_min;
double rand01 = (rand() / (double)RAND_MAX);
return (rand01 * range_width) + range_min;
}
/* Returns number of failed trials */
int fuzzTestInvBilerp( int num_trials )
{
int num_failed = 0;
double x0, y0, x1, y1, x2, y2, x3, y3, x, y, s, t, s2, t2, orig_s, orig_t;
int num_st;
int itrial;
for ( itrial = 0; itrial < num_trials; itrial++ )
{
int failed = 0;
/* Get random positions for the corners of the quad */
x0 = randrange( -10, 10 );
y0 = randrange( -10, 10 );
x1 = randrange( -10, 10 );
y1 = randrange( -10, 10 );
x2 = randrange( -10, 10 );
y2 = randrange( -10, 10 );
x3 = randrange( -10, 10 );
y3 = randrange( -10, 10 );
/*x0 = 0, y0 = 0, x1 = 1, y1 = 0, x2 = 0, y2 = 1, x3 = 1, y3 = 1;*/
/* Get random s and t */
s = randrange( 0, 1 );
t = randrange( 0, 1 );
orig_s = s;
orig_t = t;
/* bilerp to get x and y */
bilerp( x0, y0, x1, y1, x2, y2, x3, y3, s, t, &x, &y );
/* invert */
num_st = inverseBilerp( x0, y0, x1, y1, x2, y2, x3, y3, x, y, &s, &t, &s2, &t2 );
if ( num_st == 0 )
{
failed = 1;
}
else if ( num_st == 1 )
{
if ( !(equals( orig_s, s, 1e-5 ) && equals( orig_t, t, 1e-5 )) )
failed = 1;
}
else if ( num_st == 2 )
{
if ( !((equals( orig_s, s , 1e-5 ) && equals( orig_t, t , 1e-5 )) ||
(equals( orig_s, s2, 1e-5 ) && equals( orig_t, t2, 1e-5 )) ) )
failed = 1;
}
if ( failed )
{
num_failed++;
printf("Failed trial %d\n", itrial);
}
}
return num_failed;
}
int main( int argc, char** argv )
{
int num_failed;
srand( 0 );
num_failed = fuzzTestInvBilerp( 100000000 );
printf("%d of the tests failed\n", num_failed);
getc(stdin);
return 0;
}
答案 3 :(得分:5)
由于你在2D工作,你的bilerp
函数实际上是2个方程,x表示1,y表示1。它们可以用以下形式重写:
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 = p3 - p2 - p1 + p0
B = p2 - p0
C = p1 - p0
D = p0
重写第一个等式,以t
取代s
,替换为第二个,并求解s
。
答案 4 :(得分:3)
这是我的实施......我猜它比tfiniga更快
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
答案 5 :(得分:1)
如果你只有p的单个值,使得p在广场的四个角上的最小值和最大值之间,那么不,通常不可能找到一个单一的解(s,t) )这样双线性插值会给你这个值。
通常,正方形内部会有无数个解(s,t)。它们将沿着弯曲的(双曲线)路径穿过广场。
如果你的函数是一个值为1的向量,那么你在广场的某个未知点有两个已知值?给定广场每个角落的两个参数的已知值,则可能存在解决方案,但无法保证。请记住,我们可以将其视为两个独立的独立问题。它们中的每一个的解决方案将沿着通过正方形的双曲线轮廓线。如果这对轮廓在正方形内交叉,则存在解决方案。如果他们没有交叉,那么就没有解决方案。
您还会问是否存在一个简单的公式来解决问题。对不起,但不是我看到的。正如我所说,曲线是双曲线的。
一种解决方案是切换到不同的插值方法。因此,不是双线性,而是将正方形分成一对三角形。在每个三角形内,我们现在可以使用真正的线性插值。所以现在我们可以解决每个三角形内2个未知数的2个方程的线性系统。每个三角形可能有一个解决方案,除了罕见的退化情况,其中相应的分段线性轮廓线碰巧是共同发生的。
答案 6 :(得分:1)
有些回复略微误解了您的问题。即。他们假设给你一个未知插值函数的值,而不是你想要找到(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。
答案 7 :(得分:0)
好吧,如果p是2D点,是的,你可以很容易地得到它。在这种情况下,S是T处四边形总宽度的分数分量,T同样是S处四边形总高度的分数分量。
但是,如果p是标量,则不一定可能,因为双线性插值函数不一定是单片的。