For those finding your way here from Google, the following function gives a bilinear interpolation over the axes of a matrix and is easily auto differentiable with Zygote and others. You should generally prefer the battle-tested Interpolations.jl implementations where you can, but this one works in a pinch.
function bilininterp(data::AbstractMatrix, x, y)
x1 = floor(Int,x)
x2 = ceil(Int,x)
y1 = floor(Int,y)
y2 = ceil(Int,y)
# Handle case of perfect grid alignment
if x1 == x2
x2 = x1 + 1
end
if y1 == y2
y2 = y1 + 1
end
# Handle boundary conditions
if x1 == 0
x1 += 1
x2 += 1
end
if y1 == 0
y1 += 1
y2 += 1
end
if x1 == size(data,1)
x1 -= 1
x2 -= 1
end
if y1 == size(data,2)
y1 -= 1
y2 -= 1
end
mat = [
1 x1 y1 (x1 * y1)
1 x1 y2 (x1 * y2)
1 x2 y1 (x2 * y1)
1 x2 y2 (x2 * y2)
]
# Get surrounding pixels if finite (fallback to image median otherwise)
col = [
data[x1, y1]
data[x1, y2]
data[x2, y1]
data[x2, y2]
]
# Solve the system of equations without inverting
coeffs = mat \ col
interp =
coeffs[1] + coeffs[2] * x + coeffs[3] * y + coeffs[4] * x * y
return interp
end