6
\$\begingroup\$

The Problem

I am currently writing a script that converts images into numerical array representation and then calculates "in-between" images based on linear interpolation between the start and end array.

My code does exactly what I want but goes over many nested loops which strikes me as something that will lead to very high computation times for many interpolation steps or big images.

The Code

The code is in python

import numpy as np

# Helper function that calculates the interpolation between two points
def interpolate_points(p1, p2, n_steps=3):
    # interpolate ratios between the points
    ratios = np.linspace(0, 1, num=n_steps)
    # linear interpolate vectors
    vectors = list()
    for ratio in ratios:
        v = (1.0 - ratio) * p1 + ratio * p2
        vectors.append(v)
    return np.asarray(vectors)

# final function that interpolates arrays
def interpolate_arrays(start_array,end_array,n_steps=10):
    n = 0
    array_interpolation = []
    while n < n_steps:
        i = 0
        x = []
        while i < len(start_array):
            e = interpolate_points(start_array[i],end_array[i],n_steps)[n]
            x.append(e)
            i += 1
        array_interpolation += [x]
        n += 1
    return array_interpolation

This results in:

#Test
X1 = [1,1]
X2 = [3,3]

interpolate_arrays(X1,X2,n_steps=3)
#[[1.0, 1.0], [2.0, 2.0], [3.0, 3.0]]
\$\endgroup\$
2
\$\begingroup\$

There are some easy wins here. Your interpolate_points doesn't need a loop:

def interpolate_points(p1, p2, n_steps=3):
    """Helper function that calculates the interpolation between two points"""
    # interpolate ratios between the points
    ratios = np.linspace(0, 1, num=n_steps)
    # linear interpolate vectors
    vectors = (1.0 - ratios) * p1 + ratios * p2
    return vectors

Also, even without further vectorization, you should be making use of range in your main function:

def interpolate_arrays(start_array, end_array, n_steps=10):
    """final function that interpolates arrays"""
    array_interpolation = []
    for n in range(n_steps):
        x = []
        for i in range(len(start_array)):
            e = interpolate_points(start_array[i], end_array[i], n_steps)[n]
            x.append(e)
        array_interpolation += [x]
    return array_interpolation

However, all of that can be replaced with a call to interp1d:

import numpy as np
from scipy.interpolate import interp1d


def interpolate_arrays(bounds, n_steps=10):
    """final function that interpolates arrays"""
    bounds = np.array(bounds)

    fun = interp1d(
        x=[0, 1],
        y=bounds.T,
    )
    y = fun(np.linspace(0, 1, n_steps))

    return y


def test():
    X1 = [1.5, 1]
    X2 = [5.5, 3]

    y = interpolate_arrays([X1, X2], n_steps=3)
    assert y.T.tolist() == [[1.5, 1.0], [3.5, 2.0], [5.5, 3.0]]

Even easier:

def interpolate_arrays(X1, X2, n_steps=10):
    """final function that interpolates arrays"""
    return np.linspace(X1, X2, n_steps)


def test():
    X1 = [1.5, 1]
    X2 = [5.5, 3]

    y = interpolate_arrays(X1, X2, n_steps=3)
    assert y.tolist() == [[1.5, 1.0], [3.5, 2.0], [5.5, 3.0]]

Notes:

  • If you use interp1d, it would be better if your inputs and outputs are both two-dimensional np.ndarray; in their current form they need a transposition
  • Write some unit tests such as the one shown, although it would be a better idea to call isclose since this is floating-point math
  • If you want, it is trivial to make this extrapolate as well as interpolate

Basically: if there is a math thing in your head, before even thinking about what it would take to implement it yourself, do a search through scipy/numpy to see if it has already been done for you.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks a lot, this is answer is both a helpful guide for improvements as well as a good "SO just give me the final code"-style answer. \$\endgroup\$ – Fnguyen Apr 27 at 15:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.