Bicubic interpolation Python

14,116

Solution 1

Not sure why Wikipedia implementation is not working as expected. Probably, the reason is that these values might be approximated in a different way than what is explained in their site.

px00 = (f12 - f10)/2*deltax
px01 = (f22 - f20)/2*deltax 
px10 = (f13 - f11)/2*deltax 
px11 = (f23 - f21)/2*deltax

py00 = (f21 - f01)/2*deltay
py01 = (f22 - f02)/2*deltay
py10 = (f31 - f11)/2*deltay
py11 = (f32 - f12)/2*deltay

pxy00 = ((f22-f20) - (f02-f00))/4*deltax*deltay
pxy01 = ((f32-f30) - (f12-f10))/4*deltax*deltay
pxy10 = ((f23-f21) - (f03-f01))/4*deltax*deltay
pxy11 = ((f33-f31) - (f13-f11))/4*deltax*deltay

However, I found this document which have a different implementation and it is well explained and easier understood than Wikipedia. The result I obtain using this implementation are quite similar to what obtained by SciPy.

def bicubic_interpolation2(xi, yi, zi, xnew, ynew):

    # check sorting
    if np.any(np.diff(xi) < 0) and np.any(np.diff(yi) < 0) and\
    np.any(np.diff(xnew) < 0) and np.any(np.diff(ynew) < 0):
        raise ValueError('data are not sorted')

    if zi.shape != (xi.size, yi.size):
        raise ValueError('zi is not set properly use np.meshgrid(xi, yi)')

    z = np.zeros((xnew.size, ynew.size))

    deltax = xi[1] - xi[0]
    deltay = yi[1] - yi[0] 
    for n, x in enumerate(xnew):
        for m, y in enumerate(ynew):

            if xi.min() <= x <= xi.max() and yi.min() <= y <= yi.max():

                i = np.searchsorted(xi, x) - 1
                j = np.searchsorted(yi, y) - 1

                x1  = xi[i]
                x2  = xi[i+1]

                y1  = yi[j]
                y2  = yi[j+1]

                px = (x-x1)/(x2-x1)
                py = (y-y1)/(y2-y1)

                f00 = zi[i-1, j-1]      #row0 col0 >> x0,y0
                f01 = zi[i-1, j]        #row0 col1 >> x1,y0
                f02 = zi[i-1, j+1]      #row0 col2 >> x2,y0

                f10 = zi[i, j-1]        #row1 col0 >> x0,y1
                f11 = p00 = zi[i, j]    #row1 col1 >> x1,y1
                f12 = p01 = zi[i, j+1]  #row1 col2 >> x2,y1

                f20 = zi[i+1,j-1]       #row2 col0 >> x0,y2
                f21 = p10 = zi[i+1,j]   #row2 col1 >> x1,y2
                f22 = p11 = zi[i+1,j+1] #row2 col2 >> x2,y2

                if 0 < i < xi.size-2 and 0 < j < yi.size-2:

                    f03 = zi[i-1, j+2]      #row0 col3 >> x3,y0

                    f13 = zi[i,j+2]         #row1 col3 >> x3,y1

                    f23 = zi[i+1,j+2]       #row2 col3 >> x3,y2

                    f30 = zi[i+2,j-1]       #row3 col0 >> x0,y3
                    f31 = zi[i+2,j]         #row3 col1 >> x1,y3
                    f32 = zi[i+2,j+1]       #row3 col2 >> x2,y3
                    f33 = zi[i+2,j+2]       #row3 col3 >> x3,y3

                elif i<=0: 

                    f03 = f02               #row0 col3 >> x3,y0

                    f13 = f12               #row1 col3 >> x3,y1

                    f23 = f22               #row2 col3 >> x3,y2

                    f30 = zi[i+2,j-1]       #row3 col0 >> x0,y3
                    f31 = zi[i+2,j]         #row3 col1 >> x1,y3
                    f32 = zi[i+2,j+1]       #row3 col2 >> x2,y3
                    f33 = f32               #row3 col3 >> x3,y3             

                elif j<=0:

                    f03 = zi[i-1, j+2]      #row0 col3 >> x3,y0

                    f13 = zi[i,j+2]         #row1 col3 >> x3,y1

                    f23 = zi[i+1,j+2]       #row2 col3 >> x3,y2

                    f30 = f20               #row3 col0 >> x0,y3
                    f31 = f21               #row3 col1 >> x1,y3
                    f32 = f22               #row3 col2 >> x2,y3
                    f33 = f23               #row3 col3 >> x3,y3


                elif i == xi.size-2 or j == yi.size-2:

                    f03 = f02               #row0 col3 >> x3,y0

                    f13 = f12               #row1 col3 >> x3,y1

                    f23 = f22               #row2 col3 >> x3,y2

                    f30 = f20               #row3 col0 >> x0,y3
                    f31 = f21               #row3 col1 >> x1,y3
                    f32 = f22               #row3 col2 >> x2,y3
                    f33 = f23               #row3 col3 >> x3,y3

                Z = np.array([f00, f01, f02, f03,
                             f10, f11, f12, f13,
                             f20, f21, f22, f23,
                             f30, f31, f32, f33]).reshape(4,4).transpose()

                X = np.tile(np.array([-1, 0, 1, 2]), (4,1))
                X[0,:] = X[0,:]**3
                X[1,:] = X[1,:]**2
                X[-1,:] = 1

                Cr = [email protected](X)
                R = [email protected]([px**3, px**2, px, 1])

                Y = np.tile(np.array([-1, 0, 1, 2]), (4,1)).transpose()
                Y[:,0] = Y[:,0]**3
                Y[:,1] = Y[:,1]**2
                Y[:,-1] = 1

                Cc = np.linalg.inv(Y)@R

                z[n,m]=([email protected]([py**3, py**2, py, 1]))


    return z

def f(x,y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))

x = np.linspace(-6, 6, 11)
y = np.linspace(-6, 6, 11)

xx, yy = np.meshgrid(x, y)

z = f(xx, yy)

x_new = np.linspace(-6, 6, 100)
y_new = np.linspace(-6, 6, 100)

xx_new, yy_new = np.meshgrid(x_new, y_new)

z_new = bicubic_interpolation2(x, y, z, x_new, y_new)

z_true = f(xx_new, yy_new) 

f_scipy = interpolate.interp2d(x, y, z, kind='cubic')

z_scipy = f_scipy(x_new, y_new)

fig, ax = plt.subplots(2, 2, sharey=True, figsize=(16,12))

img0 = ax[0, 0].scatter(xx, yy, c=z, s=100)
ax[0, 0].set_title('original points')
fig.colorbar(img0, ax=ax[0, 0], orientation='vertical', shrink=1, pad=0.01)

img1 = ax[0, 1].imshow(z_new, vmin=z_new.min(), vmax=z_new.max(), origin='lower',
           extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
ax[0, 1].set_title('bicubic our code')
fig.colorbar(img1, ax=ax[0, 1], orientation='vertical', shrink=1, pad=0.01)


img2 = ax[1, 0].imshow(z_scipy, vmin=z_scipy.min(), vmax=z_scipy.max(), origin='lower',
           extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
ax[1, 0].set_title('bicubic scipy')
fig.colorbar(img2, ax=ax[1, 0], orientation='vertical', shrink=1, pad=0.01)


img3 = ax[1, 1].imshow(z_true, vmin=z_true.min(), vmax=z_true.max(), origin='lower',
           extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
ax[1, 1].set_title('true model')
fig.colorbar(img3, ax=ax[1, 1], orientation='vertical', shrink=1, pad=0.01)

plt.subplots_adjust(wspace=0.05, hspace=0.15)

plt.show()

enter image description here

fig, ax = plt.subplots(1, 2, sharey=True, figsize=(10, 6))

ax[0].plot(xx[0,:], z[5,:], 'or', label='original')
ax[0].plot(xx_new[0,:], z_true[int(100/10*5),:], label='true')
ax[0].plot(xx_new[0,:], z_new[int(100/10*5), :], label='our interpolation')

ax[1].plot(xx[0,:], z[5,:], 'or', label='original')
ax[1].plot(xx_new[0,:], z_true[int(100/10*5),:], label='true')
ax[1].plot(xx_new[0,:], z_scipy[int(100/10*5), :], label='scipy interpolation')


for axes in ax:
    axes.legend()
    axes.grid()


plt.show()

enter image description here

Solution 2

For future notice, I think the problem is that the algorithm detailed on Wikipedia is for bicubic interpolation on the unit square. If instead you are interpolating on a rectilinear grid, your vector x needs to be modified slightly. See the section 'Extension to rectilinear grids', now included on the Wikipedia page. https://en.wikipedia.org/wiki/Bicubic_interpolation#Extension_to_rectilinear_grids

Solution 3

Upgrade solution of Khalil Al Hooti. I hope it's better.

def BiCubicInterp(X, Y, Z, h = 0.01):
    new_Z = []
    new_X = []
    new_Y = []
    new_n_x = int((X[1] - X[0]) / h) 
    new_n_y = int((Y[1] - Y[0]) / h) 
    count_X = len(X)
    count_Y = len(Y)

    X_m = np.array([[-1, 0, 1, 8],
                    [1, 0, 1, 4],
                    [-1, 0, 1, 2],
                    [1, 1, 1, 1]])

    Y_m = np.array([[-1, 1, -1, 1],
                    [0, 0, 0, 1],
                    [1, 1, 1, 1],
                    [8, 4, 2, 1]])
    X_m = np.linalg.inv(X_m)
    Y_m = np.linalg.inv(Y_m)

    for i in range(1, count_X):
        px = X[i - 1]
        k = i - 1
        for s in range(new_n_x):
            for j in range(1, count_Y):
                py = Y[j - 1]
                l = j - 1
                for r in range(new_n_y):
                    x1  = X[k]
                    x2  = X[k+1]
                    y1  = Y[l]
                    y2  = Y[l+1]
                    P_x = (px - x1)/(x2 - x1)
                    P_y = (py - y1)/(y2 - y1)
                
                    f00 = Z[(count_Y + l-1) % count_Y, (count_X + k - 1) % count_X]     
                    f01 = Z[(count_Y + l-1) % count_Y, (count_X + k) % count_X]     
                    f02 = Z[(count_Y + l-1) % count_Y, (count_X + k + 1) % count_X] 
                    f03 = Z[(count_Y + l-1) % count_Y, (count_X + k + 2) % count_X] 
                
                    f10 = Z[(count_Y + l) % count_Y, (count_X + k - 1) % count_X]      
                    f11 = Z[(count_Y + l) % count_Y, (count_X + k) % count_X]  
                    f12 = Z[(count_Y + l) % count_Y, (count_X + k + 1) % count_X] 
                    f13 = Z[(count_Y + l) % count_Y, (count_X + k + 2) % count_X] 
                
                    f20 = Z[(count_Y + l + 1) % count_Y, (count_X + k - 1) % count_X]     
                    f21 = Z[(count_Y + l + 1) % count_Y, (count_X + k) % count_X]     
                    f22 = Z[(count_Y + l + 1) % count_Y, (count_X + k + 1) % count_X] 
                    f23 = Z[(count_Y + l + 1) % count_Y, (count_X + k + 2) % count_X] 
                
                    f30 = Z[(count_Y + l + 2) % count_Y, (count_X + k - 1) % count_X]      
                    f31 = Z[(count_Y + l + 2) % count_Y, (count_X + k) % count_X]  
                    f32 = Z[(count_Y + l + 2) % count_Y, (count_X + k + 1) % count_X]  
                    f33 = Z[(count_Y + l + 2) % count_Y, (count_X + k + 2) % count_X]  
                
                    Z_m = np.array([[f00, f01, f02, f03],
                                    [f10, f11, f12, f13],
                                    [f20, f21, f22, f23],
                                    [f30, f31, f32, f33]])
                    Cr = np.dot(Z_m, X_m)
                    R = np.dot(Cr, np.array([P_x**3, P_x**2, P_x, 1]).T)
                    Cc = np.dot(Y_m, R)
                    new_Z.append((np.dot(np.array([P_y**3, P_y**2, P_y, 1]), Cc)))
                    new_X.append(px)
                    new_Y.append(py)
                    py += h
                    py = round(py, 2)
            px += h
            px = round(px, 2)
    return new_X, new_Y, new_Z

from scipy.interpolate import griddata
x = np.linspace(0, 3, 4)
y = np.linspace(0, 3, 4)
X_ = np.linspace(0, 3, 30)
Y_ = np.linspace(0, 3, 30)

xs2, ys2, zs2 = BiCubicInterp(x, y, cords)
zs = bicubic_interpolation2(x, y, cords, X_, Y_)
x, y = np.meshgrid(x, y)
zs3 = griddata(np.array([x.reshape(1, -1)[0], y.reshape(1,-1)[0]]).T, cords.reshape(1, -1)[0], (xs2, ys2), method='cubic')

xs2 = np.array(xs2)
ys2 = np.array(ys2)
zs2 = np.array(zs2)
fig, axs = plt.subplots(1, 4, figsize = (32, 10))

shape2 = floor(np.sqrt(len(zs2)))
X2 = np.reshape(xs2,(shape2, shape2))
Y2 = np.reshape(ys2,(shape2, shape2))
Z2 = np.reshape(zs2,(shape2, shape2))
shape3 = floor(np.sqrt(len(zs3)))
Z3 = np.reshape(zs3,(shape3, shape3))

vmax = max([np.amax(zs2), np.amax(zs3), np.amax(zs), np.amax(cords)])
vmin = max([np.amin(zs2), np.amin(zs3), np.amin(zs), np.amin(cords)])
pt = axs[0].contourf(X2, Y2, Z2,  levels = 1000, label = 'Интерполируемые бикубически точки', cmap = 'jet', vmin = vmin, vmax = vmax)
axs[1].contourf(X2, Y2, Z3,  levels = 1000, label = 'Интерполируемые треугольниками точки', cmap = 'jet', vmin = vmin, vmax = vmax)
axs[2].contourf(X_, Y_, zs,  levels = 1000, label = 'Интерполируемые бикубически точки (начальное решение)', cmap = 'jet', vmin = vmin, vmax = vmax)
axs[3].contourf(x, y, cords,  levels = 1000, label = 'Интерполируемые бикубически точки (начальное решение)', cmap = 'jet', vmin = vmin, vmax = vmax)
axs[0].set_ylabel('y')
axs[0].set_xlabel('x')
axs[1].set_ylabel('y')
axs[1].set_xlabel('x')
axs[2].set_ylabel('y')
axs[2].set_xlabel('x')
axs[3].set_ylabel('y')
axs[3].set_xlabel('x')
axs[0].set_title('JacKira')
axs[1].set_title('scipy.interpolate.griddata method=\"cubic\"')
axs[2].set_title('Khalil')
axs[3].set_title('Input Data')

enter image description here

Share:
14,116
Khalil Al Hooti
Author by

Khalil Al Hooti

Geophysicist

Updated on June 04, 2022

Comments

  • Khalil Al Hooti
    Khalil Al Hooti almost 2 years

    I have developed Bicubic interpolation for demonstration to some undergraduate students using Python Programming language.

    The methodology is as explained in wikipedia, The code is working fine except the results I am getting are slightly different than what is obtained when using scipy library.

    The interpolation code is shown below in the function bicubic_interpolation.

    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits import mplot3d
    from scipy import interpolate
    import sympy as syp
    import pandas as pd
    pd.options.display.max_colwidth = 200
    %matplotlib inline
    
    def bicubic_interpolation(xi, yi, zi, xnew, ynew):
    
        # check sorting
        if np.any(np.diff(xi) < 0) and np.any(np.diff(yi) < 0) and\
        np.any(np.diff(xnew) < 0) and np.any(np.diff(ynew) < 0):
            raise ValueError('data are not sorted')
    
        if zi.shape != (xi.size, yi.size):
            raise ValueError('zi is not set properly use np.meshgrid(xi, yi)')
    
        z = np.zeros((xnew.size, ynew.size))
    
        deltax = xi[1] - xi[0]
        deltay = yi[1] - yi[0] 
        for n, x in enumerate(xnew):
            for m, y in enumerate(ynew):
    
                if xi.min() <= x <= xi.max() and yi.min() <= y <= yi.max():
    
                    i = np.searchsorted(xi, x) - 1
                    j = np.searchsorted(yi, y) - 1
    
                    x0  = xi[i-1]
                    x1  = xi[i]
                    x2  = xi[i+1]
                    x3  = x1+2*deltax
    
                    y0  = yi[j-1]
                    y1  = yi[j]
                    y2  = yi[j+1]
                    y3  = y1+2*deltay
    
                    px = (x-x1)/(x2-x1)
                    py = (y-y1)/(y2-y1)
    
                    f00 = zi[i-1, j-1]      #row0 col0 >> x0,y0
                    f01 = zi[i-1, j]        #row0 col1 >> x1,y0
                    f02 = zi[i-1, j+1]      #row0 col2 >> x2,y0
    
                    f10 = zi[i, j-1]        #row1 col0 >> x0,y1
                    f11 = p00 = zi[i, j]    #row1 col1 >> x1,y1
                    f12 = p01 = zi[i, j+1]  #row1 col2 >> x2,y1
    
                    f20 = zi[i+1,j-1]       #row2 col0 >> x0,y2
                    f21 = p10 = zi[i+1,j]   #row2 col1 >> x1,y2
                    f22 = p11 = zi[i+1,j+1] #row2 col2 >> x2,y2
    
                    if 0 < i < xi.size-2 and 0 < j < yi.size-2:
    
                        f03 = zi[i-1, j+2]      #row0 col3 >> x3,y0
    
                        f13 = zi[i,j+2]         #row1 col3 >> x3,y1
    
                        f23 = zi[i+1,j+2]       #row2 col3 >> x3,y2
    
                        f30 = zi[i+2,j-1]       #row3 col0 >> x0,y3
                        f31 = zi[i+2,j]         #row3 col1 >> x1,y3
                        f32 = zi[i+2,j+1]       #row3 col2 >> x2,y3
                        f33 = zi[i+2,j+2]       #row3 col3 >> x3,y3
    
                    elif i<=0: 
    
                        f03 = f02               #row0 col3 >> x3,y0
    
                        f13 = f12               #row1 col3 >> x3,y1
    
                        f23 = f22               #row2 col3 >> x3,y2
    
                        f30 = zi[i+2,j-1]       #row3 col0 >> x0,y3
                        f31 = zi[i+2,j]         #row3 col1 >> x1,y3
                        f32 = zi[i+2,j+1]       #row3 col2 >> x2,y3
                        f33 = f32               #row3 col3 >> x3,y3             
    
                    elif j<=0:
    
                        f03 = zi[i-1, j+2]      #row0 col3 >> x3,y0
    
                        f13 = zi[i,j+2]         #row1 col3 >> x3,y1
    
                        f23 = zi[i+1,j+2]       #row2 col3 >> x3,y2
    
                        f30 = f20               #row3 col0 >> x0,y3
                        f31 = f21               #row3 col1 >> x1,y3
                        f32 = f22               #row3 col2 >> x2,y3
                        f33 = f23               #row3 col3 >> x3,y3
    
    
                    elif i == xi.size-2 or j == yi.size-2:
    
                        f03 = f02               #row0 col3 >> x3,y0
    
                        f13 = f12               #row1 col3 >> x3,y1
    
                        f23 = f22               #row2 col3 >> x3,y2
    
                        f30 = f20               #row3 col0 >> x0,y3
                        f31 = f21               #row3 col1 >> x1,y3
                        f32 = f22               #row3 col2 >> x2,y3
                        f33 = f23               #row3 col3 >> x3,y3
    
                    px00 = (f12 - f10)/2*deltax
                    px01 = (f22 - f20)/2*deltax 
                    px10 = (f13 - f11)/2*deltax 
                    px11 = (f23 - f21)/2*deltax
    
                    py00 = (f21 - f01)/2*deltay
                    py01 = (f22 - f02)/2*deltay
                    py10 = (f31 - f11)/2*deltay
                    py11 = (f32 - f12)/2*deltay
    
                    pxy00 = ((f22-f20) - (f02-f00))/4*deltax*deltay
                    pxy01 = ((f32-f30) - (f12-f10))/4*deltax*deltay
                    pxy10 = ((f23-f21) - (f03-f01))/4*deltax*deltay
                    pxy11 = ((f33-f31) - (f13-f11))/4*deltax*deltay
    
    
                    f = np.array([p00,  p01,  p10, p11,
                                  px00,  px01,  px10, px11,
                                  py00, py01,  py10,  py11,
                                  pxy00,  pxy01, pxy10, pxy11])
    
                    a = A@f
    
                    a = a.reshape(4,4).transpose()
                    z[n,m] = np.array([1, px, px**2, px**3]) @ a @ np.array([1, py, py**2, py**3])
    
        return z
    

    In the function bicubic_interpolation the inputs are xi= old x data range, yi= old y range, zi= old values at grids points (x,y), xnew, and ynew are the new horizontal data ranges. All inputs are 1D numpy arrays except zi which is 2D numpy array.

    The data I am testing the function on are shown below. I am comparing the results as well with scipy and true model (function f).

    def f(x,y):
        return np.sin(np.sqrt(x ** 2 + y ** 2))
    
    x = np.linspace(-6, 6, 11)
    y = np.linspace(-6, 6, 11)
    
    xx, yy = np.meshgrid(x, y)
    
    z = f(xx, yy)
    
    x_new = np.linspace(-6, 6, 100)
    y_new = np.linspace(-6, 6, 100)
    
    xx_new, yy_new = np.meshgrid(x_new, y_new)
    
    z_new = bicubic_interpolation(x, y, z, x_new, y_new)
    
    z_true = f(xx_new, yy_new) 
    
    f_scipy = interpolate.interp2d(x, y, z, kind='cubic')
    
    z_scipy = f_scipy(x_new, y_new)
    
    fig, ax = plt.subplots(2, 2, sharey=True, figsize=(16,12))
    
    img0 = ax[0, 0].scatter(xx, yy, c=z, s=100)
    ax[0, 0].set_title('original points')
    fig.colorbar(img0, ax=ax[0, 0], orientation='vertical', shrink=1, pad=0.01)
    
    img1 = ax[0, 1].imshow(z_new, vmin=z_new.min(), vmax=z_new.max(), origin='lower',
               extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
    ax[0, 1].set_title('bicubic our code')
    fig.colorbar(img1, ax=ax[0, 1], orientation='vertical', shrink=1, pad=0.01)
    
    
    img2 = ax[1, 0].imshow(z_scipy, vmin=z_scipy.min(), vmax=z_scipy.max(), origin='lower',
               extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
    ax[1, 0].set_title('bicubic scipy')
    fig.colorbar(img2, ax=ax[1, 0], orientation='vertical', shrink=1, pad=0.01)
    
    
    img3 = ax[1, 1].imshow(z_true, vmin=z_true.min(), vmax=z_true.max(), origin='lower',
               extent=[x_new.min(), x_new.max(), y_new.max(), y_new.min()])
    ax[1, 1].set_title('true model')
    fig.colorbar(img3, ax=ax[1, 1], orientation='vertical', shrink=1, pad=0.01)
    
    plt.subplots_adjust(wspace=0.05, hspace=0.15)
    
    plt.show()
    

    The results are shown below:

    enter image description here

    The matrix A (inside funtion bicubic_interpolation) is as explained in the Wikipedia site and simply can be obtained using the following code:

    x = syp.Symbol('x')
    y = syp.Symbol('y')
    a00, a01, a02, a03, a10, a11, a12, a13 = syp.symbols('a00 a01 a02 a03 a10 a11 a12 a13')
    a20, a21, a22, a23, a30, a31, a32, a33 = syp.symbols('a20 a21 a22 a23 a30 a31 a32 a33')
    
    p = a00 + a01*y + a02*y**2 + a03*y**3\
    + a10*x + a11*x*y + a12*x*y**2 + a13*x*y**3\
    + a20*x**2 + a21*x**2*y + a22*x**2*y**2 + a23*x**2*y**3\
    + a30*x**3 + a31*x**3*y + a32*x**3*y**2 + a33*x**3*y**3 
    
    px = syp.diff(p, x)
    py = syp.diff(p, y)
    pxy = syp.diff(p, x, y)
    
    df = pd.DataFrame(columns=['function', 'evaluation'])
    
    for i in range(2):
        for j in range(2):
            function = 'p({}, {})'.format(j,i)
            df.loc[len(df)] = [function, p.subs({x:j, y:i})]
    for i in range(2):
        for j in range(2):
            function = 'px({}, {})'.format(j,i)
            df.loc[len(df)] = [function, px.subs({x:j, y:i})]
    for i in range(2):
        for j in range(2):
            function = 'py({}, {})'.format(j,i)
            df.loc[len(df)] = [function, py.subs({x:j, y:i})]
    for i in range(2):
        for j in range(2):
            function = 'pxy({}, {})'.format(j,i)
            df.loc[len(df)] = [function, pxy.subs({x:j, y:i})]
    
    eqns = df['evaluation'].tolist()
    symbols = [a00,a01,a02,a03,a10,a11,a12,a13,a20,a21,a22,a23,a30,a31,a32,a33]
    A = syp.linear_eq_to_matrix(eqns, *symbols)[0]
    A = np.array(A.inv()).astype(np.float64)
    
    print(df)
    
    print(A) 
    

    enter image description here

    enter image description here

    I would like to know where the problem is with the bicubic_interpolation function and why it is slightly different than the result obtained by scipy? Any help is greatly appreciated!

  • Zhanwen Chen
    Zhanwen Chen over 5 years
    how can this interpolation method apply to images, where there are no x and y, only z?
  • Khalil Al Hooti
    Khalil Al Hooti over 5 years
    I am not expert in image processing! I think what already is available in python scientific ecosystem is sufficient and definitely better than this implementation for that matter, they support parallel processing too. I was testing this for just educational purposes. I also recently modified the code and will update the answer here soon. Regards, @ZhanwenChen
  • Will.Evo
    Will.Evo almost 5 years
    I am having a weird result using your bi cubic function. When I try to interpolate an image from 512x512 to 1024x1024, there is an empty row at the bottom of the interpolated image and empty column on the right side of image. Any ideas as to why?
  • Eliot K
    Eliot K almost 3 years
    Can you add some clarification as to why your answer is better and to help users understand what is different?
  • JacKira
    JacKira almost 3 years
    For users this solution more comfortable, because new meshgrid returns from procedure. I was deciding task of bicubic interpolation for C# implementation in work project, but didn't find ready-made simple solution. The best way was to take code by Khalil and transform it to fit the capabilities of C#, and i tried made code more readable.
  • JacKira
    JacKira almost 3 years
    When i debugging Khalil solution, i noticed what Z matrix changes to the cyclic shift rule and just code it. I hope this solution more simple for understanding in the future.
  • Safi
    Safi over 2 years
    the above cited link for Bicubic is no more functional. I found the document over here. anyflip.com/lxkj/dlpa