Resample a Numpy Array

Resample a numpy array

As scipy.signal.resample can be very slow, I searched for other algorithms adapted for audio.

It seems that Erik de Castro Lopo's SRC (a.k.a. Secret Rabbit Code a.k.a. libsamplerate) is one of the best resampling algorithms available.

  • It is used by scikit's scikit.samplerate, but this library seems to be complicated to install (I gave up on Windows).

  • Fortunately, there is an easy-to-use and easy-to-install Python wrapper for libsamplerate, made by Tino Wagner: https://pypi.org/project/samplerate/. Installation with pip install samplerate. Usage:

    import samplerate
    from scipy.io import wavfile
    sr, x = wavfile.read('input.wav') # 48 khz file
    y = samplerate.resample(x, 44100 * 1.0 / 48000, 'sinc_best')

Interesting reading / comparison of many resampling solutions:
http://signalsprocessed.blogspot.com/2016/08/audio-resampling-in-python.html


Addendum: comparison of spectrograms of a resampled frequency sweep (20hz to 20khz):

1) Original

2) Resampled with libsamplerate / samplerate module

3) Resampled with numpy.interp ("One-dimensional linear interpolation"):

Resampling time series data using python/numpy

Possibly something like this does what you need:

import numpy as np
from scipy.interpolate import interp1d

# The test data.
time = np.array([0, 0.0120, 0.0130, 1, 1.02, 1.2, 1.3, 1.32, 2])
values = np.array([1, 3, 2, 3, 4, 5, 6, 7, 8])

# The new time basis we're aiming for.
t_new = np.linspace(0, 2, 21)

# Throw away times that we don't like.
new_data = [[t, v] for t, v in zip(time, values) if t in t_new]
t_clean, v_clean = np.array(new_data).T

# Make the interpolator function.
func = interp1d(t_clean, v_clean, kind="previous")

# Interpolate the data into the new time basis.
v_new = func(t_new)

Now v_new is like:

array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 3., 3., 3., 6., 6., 6., 6.,
6., 6., 6., 8.])

Resample a categorical numpy array

I think you can just simple numpy slicing, which is of the format start:stop:step. This is constant time and reflects possible changes you might make to the resampled array.
In your case it would be: labels[0::64000]

Resampling a numpy array representing an image

Based on your description, you want scipy.ndimage.zoom.

Bilinear interpolation would be order=1, nearest is order=0, and cubic is the default (order=3).

zoom is specifically for regularly-gridded data that you want to resample to a new resolution.

As a quick example:

import numpy as np
import scipy.ndimage

x = np.arange(9).reshape(3,3)

print 'Original array:'
print x

print 'Resampled by a factor of 2 with nearest interpolation:'
print scipy.ndimage.zoom(x, 2, order=0)


print 'Resampled by a factor of 2 with bilinear interpolation:'
print scipy.ndimage.zoom(x, 2, order=1)


print 'Resampled by a factor of 2 with cubic interpolation:'
print scipy.ndimage.zoom(x, 2, order=3)

And the result:

Original array:
[[0 1 2]
[3 4 5]
[6 7 8]]
Resampled by a factor of 2 with nearest interpolation:
[[0 0 1 1 2 2]
[0 0 1 1 2 2]
[3 3 4 4 5 5]
[3 3 4 4 5 5]
[6 6 7 7 8 8]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with bilinear interpolation:
[[0 0 1 1 2 2]
[1 2 2 2 3 3]
[2 3 3 4 4 4]
[4 4 4 5 5 6]
[5 5 6 6 6 7]
[6 6 7 7 8 8]]
Resampled by a factor of 2 with cubic interpolation:
[[0 0 1 1 2 2]
[1 1 1 2 2 3]
[2 2 3 3 4 4]
[4 4 5 5 6 6]
[5 6 6 7 7 7]
[6 6 7 7 8 8]]

Edit: As Matt S. pointed out, there are a couple of caveats for zooming multi-band images. I'm copying the portion below almost verbatim from one of my earlier answers:

Zooming also works for 3D (and nD) arrays. However, be aware that if you zoom by 2x, for example, you'll zoom along all axes.

data = np.arange(27).reshape(3,3,3)
print 'Original:\n', data
print 'Zoomed by 2x gives an array of shape:', ndimage.zoom(data, 2).shape

This yields:

Original:
[[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]

[[ 9 10 11]
[12 13 14]
[15 16 17]]

[[18 19 20]
[21 22 23]
[24 25 26]]]
Zoomed by 2x gives an array of shape: (6, 6, 6)

In the case of multi-band images, you usually don't want to interpolate along the "z" axis, creating new bands.

If you have something like a 3-band, RGB image that you'd like to zoom, you can do this by specifying a sequence of tuples as the zoom factor:

print 'Zoomed by 2x along the last two axes:'
print ndimage.zoom(data, (1, 2, 2))

This yields:

Zoomed by 2x along the last two axes:
[[[ 0 0 1 1 2 2]
[ 1 1 1 2 2 3]
[ 2 2 3 3 4 4]
[ 4 4 5 5 6 6]
[ 5 6 6 7 7 7]
[ 6 6 7 7 8 8]]

[[ 9 9 10 10 11 11]
[10 10 10 11 11 12]
[11 11 12 12 13 13]
[13 13 14 14 15 15]
[14 15 15 16 16 16]
[15 15 16 16 17 17]]

[[18 18 19 19 20 20]
[19 19 19 20 20 21]
[20 20 21 21 22 22]
[22 22 23 23 24 24]
[23 24 24 25 25 25]
[24 24 25 25 26 26]]]

Resample numpy array with interpolation

I came up with a solution but I am wondering if it is correct from a technical/scientific point of view. Is this good procedure to solve my issue? Practically, it is doing what I want...

for i in range(len(S_images)):
if S_images[i].shape[1] !=1524:
S_images[i] = scipy.ndimage.zoom(S_images[i], (1, 1524/1525, 1),order=0, mode='nearest')

for i in range(len(S_images)):
if S_images[i].shape[2] !=5499:
S_images[i] = scipy.ndimage.zoom(S_images[i], (1, 1, 5499/5500 ),order=0, mode='nearest')


for i in range(len(S_images)):
print(S_images[i].shape)

(1, 1524, 5499)
(1, 1524, 5499)
(1, 1524, 5499)
(1, 1524, 5499)
(1, 1524, 5499)

numpy vectorized resampling like pandas DataFrame resample

Here is another way that uses numpy strides under the hood (a is your array):

from skimage.util import view_as_blocks
a = view_as_blocks(a, (4,5))

Now, you can use methods/slicing for parameters you want:

#max
a.max(-1)[0].T
#min
a.min(-1)[0].T
#left
a[...,0][0].T
#right
a[...,-1][0].T

example:

a
#[[ 0 1 2 3 4 5 6 7 8 9]
# [10 11 12 13 14 15 16 17 18 19]
# [20 21 22 23 24 25 26 27 28 29]
# [30 31 32 33 34 35 36 37 38 39]]

output for max
#[[ 4 9]
# [14 19]
# [24 29]
# [34 39]]


Related Topics



Leave a reply



Submit