算法:如何计算双线性插值的INVERSE?

时间:2014-04-16 05:11:22

标签: algorithm interpolation

双线性插值算是微不足道的。 但我需要一个执行INVERSE操作的算法。 (算法对伪代码或任何广泛使用的计算机语言都很有用)

例如,这是双线性插值的Visual Basic实现。

' xyWgt ranges (0..1) in x and y. (0,0) will return X0Y0,
(0,1) will return X0Y1, etc.
' For example, if xyWgt is relative location within an image,
' and the XnYn values are GPS coords at the 4 corners of the image,
' The result is GPS coord corresponding to xyWgt.
' E.g. given (0.5, 0.5), the result will be the GPS coord at center of image.
Public Function Lerp2D(xyWgt As Point2D, X0Y0 As Point2D, X1Y0 As Point2D, X0Y1 As Point2D, X1Y1 As Point2D) As Point2D
    Dim xY0 As Point2D = Lerp(X0Y0, X1Y0, xyWgt.X)
    Dim xY1 As Point2D = Lerp(X0Y1, X1Y1, xyWgt.X)

    Dim xy As Point2D = Lerp(xY0, xY1, xyWgt.Y)
    Return xy
End Function

,其中

' Weighted Average of two points.
Public Function Lerp(ByVal a As Point2D, ByVal b As Point2D, ByVal wgtB As Double) As Point2D
    Return New Point2D(Lerp(a.X, b.X, wgtB), Lerp(a.Y, b.Y, wgtB))
End Function

' Weighted Average of two numbers.
' When wgtB==0, returns a, when wgtB==1, returns b.
' Implicitly, wgtA = 1 - wgtB. That is, the weights are normalized.
Public Function Lerp(ByVal a As Double, ByVal b As Double, ByVal wgtB As Double) As Double
    Return a + (wgtB * (b - a))
End Function

在1-D中,我确定了Lerp的反函数:

' Calculate wgtB that would return result, if did Lerp(a, b, wgtB).
' That is, where result is, w.r.t. a and b.
' < 0 is before a, > 1 is after b.
Public Function WgtFromResult(ByVal a As Double, ByVal b As Double, ByVal result As Double) As Double

    Dim denominator As Double = b - a

    If Math.Abs(denominator) < 0.00000001 Then
        ' Avoid divide-by-zero (a & b are nearly equal).
        If Math.Abs(result - a) < 0.00000001 Then
            ' Result is close to a (but also to b): Give simplest answer: average them.
            Return 0.5
        End If
        ' Cannot compute.
        Return Double.NaN
    End If

    ' result = a + (wgt * (b - a))   =>
    ' wgt * (b - a) = (result - a)   =>
    Dim wgt As Double = (result - a) / denominator

    'Dim verify As Double = Lerp(a, b, wgt)
    'If Not NearlyEqual(result, verify) Then
    '    Dim test = 0    ' test
    'End If

    Return wgt
End Function

现在我需要在二维中做同样的事情:

' Returns xyWgt, which if given to Lerp2D, would return this "xy".
' So if xy = X0Y0, will return (0, 0). if xy = X1Y0, will return (1, 0), etc.
' For example, if 4 corners are GPS coordinates in corners of an image,
' and pass in a GPS coordinate,
' returns relative location within the image.
Public Function InverseLerp2D(xy As Point2D, X0Y0 As Point2D, X1Y0 As Point2D, X0Y1 As Point2D, X1Y1 As Point2D) As Point2D
    ' TODO ????
End Function

2 个答案:

答案 0 :(得分:4)

定义双线性插值的逆。

你的代码对我来说不是很易读(不是VB编码器)可能会有一些关于背后的主要想法的附加文本会更好,但是从你的代码中你会得到一些重量我认为。从我的观点来看,它看起来像这样:

双线性插值是2D图像/矩阵调整大小

  • 输入 2D图像 /分辨率矩阵 xs0,ys0 和新分辨率 xs1,ys1
  • 输出 2D图像 /分辨率xs1,ys1
  • 的矩阵

逆双线性插值从调整大小的图像中获取原始2D图像/矩阵。只有当(xs0<=xs1) AND (ys0<=ys1)其他所需信息丢失时,才能执行此操作。

逆双线性插值算法

  1. 在图片中找到原始栅格

    仅限第一个生产线并将具有相似斜率的点组合在一起(下图中的绿色线条图中的绿色线条)。计算相邻线之间的交叉点并计算交叉点之间最常见的最小距离。

    使用交叉距离的直方图,因为应该有更多的候选者是原始栅格大小的倍数,因此请选择最小的一个。仅从这些乘法中选择以避免压缩失真问题。这些是原始图像网格上的点(半双线性插值)

  2. 插入栅格网格线

    通过子弹#1 中的计算网格对点进行分组,并获得所有&#39;蓝色&#39;的颜色。分。

  3. 获取栅格网格点

    在蓝点(处理列)上应用项目符号#1 #2 结果为原始图像。不要忘记再次计算网格大小,因为列可以有不同的行,然后是行。

    Inverse Bilinear interpolation

    • 图形x轴是处理线/行轴
    • 图表y轴是颜色/分量强度值
  4. [Edit1]很好奇,所以我做了一些测试

    这种方法可用于(bi)线性缩放图像,zoom >= 2.0用于较小的缩放,至少对于下面代码的当前状态没有精确度提升(可以使用一些调整更好)。

    小心将de-interpolate与插值匹配(如果不在下面使用我的),因为坐标映射+/- 1像素位置可能存在一些差异

    如果您计算了源分辨率,那么其余部分是 C ++ 中的代码:

    //---------------------------------------------------------------------------
    const int dmax=5;   // max difference of derivation (threshold)
    //---------------------------------------------------------------------------
    float divide(float a,float b)
        {
        if (fabs(b)<1e-6) return 0.0;
        return a/b;
        }
    //---------------------------------------------------------------------------
    // (x,y) = intersection of line(xa0,ya0,xa1,ya1) and line(xb0,yb0,xb1,yb1)
    // return true if lines intersect
    // ta , tb are parameters for intersection point inside line a,b
    int line_intersection(float &x ,float &y ,
                          float xa0,float ya0,float xa1,float ya1,float &ta,
                          float xb0,float yb0,float xb1,float yb1,float &tb,float _zeroacc=1e-6)
        {
        float   dxa,dya,dxb,dyb,q;
        dxa=xa1-xa0; dya=ya1-ya0; ta=0;
        dxb=xb1-xb0; dyb=yb1-yb0; tb=0;
        q=(dxa*dyb)-(dxb*dya);
        if (fabs(q)<=_zeroacc) return 0;            // no intersection
        ta=divide(dxb*(ya0-yb0)+dyb*(xb0-xa0),q);
        tb=divide(dxa*(ya0-yb0)+dya*(xb0-xa0),q);
        x=xa0+(dxa*ta);
        y=ya0+(dya*ta);
        return 1;                                   // x,y = intersection ta,tb = parameters
        }
    //---------------------------------------------------------------------------
    void lin_interpolate(int *dst,int dsz,int *src,int ssz)
        {
        int x,x0,x1,c0,c1;
        int cnt,d0=ssz,d1=dsz;
        for (cnt=0,x0=0,x1=1,x=0;x<dsz;x++)
            {
            c0=src[x0];
            c1=src[x1];
            dst[x]=c0+(((c1-c0)*cnt)/d1);
            cnt+=d0; while (cnt>=d1) { cnt-=d1; x0++; x1++; if (x1>=ssz) x1=ssz-1; }
            }
        }
    //---------------------------------------------------------------------------
    void lin_deinterpolate(int *dst,int dsz,int *src,int ssz)
        {
        float px,py,ta,tb;
        int x,x0,x1,x2,x3,x4,x5;
        int   d0,d1,cnt;
        int  *der=new int[ssz]; // derivation by 'x' (address in array) ... slopes
        int *dmap=new int[ssz]; // corresponding x-positions in dst[]
        // init derivation table
        for (x0=0,x1=1;x1<ssz;x0++,x1++) der[x1]=src[x1]-src[x0]; der[0]=der[1];
        // init coordinate conversion table
        for (d0=dsz,d1=ssz,cnt=0,x0=0,x=0;x<ssz;x++) { dmap[x]=x0; cnt+=d0; while (cnt>=d1) { cnt-=d1; x0++; } }
        // init original lines
        int lins=0;
        int *lin0=new int[ssz<<1];
        int *lin1=new int[ssz<<1];
        for (x0=0,x1=0,x=0;x<ssz;x++)
            {
            d0=der[x0]-der[x]; if (d0<0) d0=-d0;
            if (d0<=dmax) x1=x;
            if ((d0>dmax)||(x+1>=ssz))
                {
                if (x0!=x1)
                    {
                    lin0[lins]=x0;
                    lin1[lins]=x1;
                    lins++;
                    x0=x1; x=x1;
                    }
                else{
                    x0=x; x1=x;
                    }
                }
            }
    
        // first naive interpolation
        lin_interpolate(dst,dsz,src,ssz);
    
        // update inaccurate points
        for (d0=0,d1=1;d1<lins;d0++,d1++)
            {
            x=lin0[d1]-lin1[d0];            // distance betwen lines
            if ((x<1)||(x>2)) continue;     // use only inacurate points
            // if lines found and intersect in the right place (x1<=px<=x2) copy result py to dst
            x0=lin0[d0];
            x1=lin1[d0];
            x2=lin0[d1];
            x3=lin1[d1];
            if (line_intersection(px,py,x0,src[x0],x1,src[x1],ta,x2,src[x2],x3,src[x3],tb))
             if ((px>=x1)&&(px<=x2))
                {
                dst[dmap[int(ceil(px))]]=int(py);
    //          der[int(px)]=-300;
                }
            }
        delete[]  der;
        delete[] lin0;
        delete[] lin1;
        delete[] dmap;
        }
    //---------------------------------------------------------------------------
    
    • lin_interpolate是一维线性插值src[ssz] -> dst[dsz]
    • lin_deinterpolate是一维逆线性插值src[ssz] -> dst[dsz]

    现在要在2D中执行此操作:

    双线性插值:

    首先通过插入1D重新缩放所有行,然后通过插值1D重新缩放列。重写两个函数以逐行而不是行或在列重新缩放之前和之后转置图像。您还可以将每列复制到行缓冲区重新缩放,然后复制回来,选择您最喜欢的列。

    反向或反向双线性插值:

    与Bilinear Interpolate相同,但顺序相反!所以首先通过插入1D重新缩放列,然后通过插值1D重新缩放所有行。如果不这样做,那么准确度可能会有点差。

    对于整数10位灰度图像,我测试的是反双线性的精度比天然反双线性好3倍。

    好的,这是测试图像的一些真正的gfx线:

    real data

    • 红色箭头表示最大准确度提升的位置
    • 白色大点是原始图像中的点
    • 绿线/点是线性插值后的点
    • Aqua线被检测到具有相同斜率的精确点(difference<=treshold)
    • 蓝点是去插值后的输出点(近可用水线的交点)
    • 在最好的情况下是白点中间的蓝点,但并不总是因为整数舍入错误

    PS。提供的代码未经过优化

    对于2D,您不需要resize/alloc每行/每列的所有缓冲区{}}}每行所有行一次,每列所有行一次

答案 1 :(得分:0)

为简化起见,我们首先考虑单个内插值 z 假设有四个值 z 00 z 01 z 10 < / sub>, z 10 ,以及两个权重 w 0 w < sub> 1 应用于第一个和第二个索引,给出

z 0 = z 00 + w 0 < / sub>×( z 10 - z 00
  z 1 = z 01 + w 0 ×( z 11 - z 01

最后

z = z 0 + w 1 ×( z 1 - z 0
 = z 00 + w 0 ×( z 10 - z 00 )+ w 1 ×( z 01 - z 00 )+ w 1 × w < sub> 0 ×( z 11 - z 10 - z 01 + z 00

因此,对于您的问题,您将不得不反转二维二次方程

x = x 00 + w 0 ×( x 10 - x 00 )+ w 1 ×( x 01 - x 00 )+ w 1 × w 0 ×( x 11 - x 10 < / sub> - x 01 + x 00
  y = y 00 + w 0 ×( y 10 - y 00 )+ w 1 ×( y 01 - y 00 )+ w 1 ×< em> w 0 ×( y 11 - y 10 - y 01 + y 00

不幸的是,没有一个简单的公式可以从 w 0 w 1 > x 和 y 。但是,您可以将其视为非线性最小二乘问题并最小化

X <子>瓦特 的(瓦特 <子> 0 瓦特 <子> 1 ) - x 2 +( y w w 0 w 1 ) - y 2

您可以使用Levenberg–Marquardt algorithm高效执行。

修改:进一步思考

我想到你可能会对( x y )到( w 0)的插值感到满意 w 1 )而不是实际的逆。在rev(fwd( w 0 w 1 ))可能的意义上,这将不太准确远离( w 0 w 1 )而不是实际的逆。

您在不规则网格而不是常规网格上进行插值这一事实将使这变得更加棘手。理想情况下,您应该使用非重叠三角形连接( x y )点,并使用barycentric coordinates进行线性插值。
为了数值稳定,你应该避免浅的尖三角形。幸运的是,Delaunay triangulation满足了这一要求,而且难以在两个维度上构建。

如果您希望反向插值采用与前向插值类似的形式,可以使用basis functions

1
X
ý
x × y

并计算系数 a i b i < / sub>, c i d i i 等于0或1),以便

w 0 = a 0 + b 0 < / sub>× x + c 0 × y + d 0 × x × y
w 1 = a 1 + b 1 × x + c 1 × y + d 1 × x × y

通过替换 x y w 0 w <的相关已知值/ em> 1 你可以得到每个 w 的四个线性方程式,你可以求解它们来得到它的系数。
理想情况下,您应该使用数值稳定的矩阵求逆算法来处理近似奇异矩阵(例如SVD),但是如果你'可能能够逃脱Gaussian elimination小心。

对不起,我不能给你任何更简单的选择,但我担心这真的是一个相当棘手的问题!