简体   繁体   中英

Downsample array in Python

I have basic 2-D numpy arrays and I'd like to "downsample" them to a more coarse resolution. Is there a simple numpy or scipy module that can easily do this? I should also note that this array is being displayed geographically via Basemap modules.

SAMPLE:在此处输入图片说明

scikit-image has implemented a working version of downsampling here, although they shy away from calling it downsampling for it not being a downsampling in terms of DSP, if I understand correctly:

http://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.block_reduce

but it works very well, and it is the only downsampler that I found in Python that can deal with np.nan in the image. I have downsampled gigantic images with this very quickly.

When downsampling, interpolation is the wrong thing to do. Always use an aggregated approach.

I use block means to do this, using a "factor" to reduce the resolution.

import numpy as np
from scipy import ndimage

def block_mean(ar, fact):
    assert isinstance(fact, int), type(fact)
    sx, sy = ar.shape
    X, Y = np.ogrid[0:sx, 0:sy]
    regions = sy/fact * (X/fact) + Y/fact
    res = ndimage.mean(ar, labels=regions, index=np.arange(regions.max() + 1))
    res.shape = (sx/fact, sy/fact)
    return res

Eg, a (100, 200) shape array using a factor of 5 (5x5 blocks) results in a (20, 40) array result:

ar = np.random.rand(20000).reshape((100, 200))
block_mean(ar, 5).shape  # (20, 40)

imresize and ndimage.interpolation.zoom look like they do what you want

I haven't tried imresize before but here is how I have used ndimage.interpolation.zoom

a = np.array(64).reshape(8,8)
a = ndimage.interpolation.zoom(a,.5) #decimate resolution

a is then a 4x4 matrix with interpolated values in it

Because the OP just wants a courser resolution, I thought I would share my way for reducing number of pixels by half in each dimension. I takes the mean of 2x2 blocks. This can be applied multiple times to reduce by factors of 2.

from scipy.ndimage import convolve
array_downsampled = convolve(array, 
                 np.array([[0.25,0.25],[0.25,0.25]]))[:array.shape[0]:2,:array.shape[1]:2]

Easiest way : You can use the array[0::2] notation, which only considers every second index. Eg

array= np.array([[i+j for i in range(0,10)] for j in range(0,10)])
down_sampled=array[0::2,0::2]

print("array \n", array)
print("array2 \n",down_sampled)

has the output:

array 
[[ 0  1  2  3  4  5  6  7  8  9]
 [ 1  2  3  4  5  6  7  8  9 10]
 [ 2  3  4  5  6  7  8  9 10 11]
 [ 3  4  5  6  7  8  9 10 11 12]
 [ 4  5  6  7  8  9 10 11 12 13]
 [ 5  6  7  8  9 10 11 12 13 14]
 [ 6  7  8  9 10 11 12 13 14 15]
 [ 7  8  9 10 11 12 13 14 15 16]
 [ 8  9 10 11 12 13 14 15 16 17]
 [ 9 10 11 12 13 14 15 16 17 18]]
array2 
[[ 0  2  4  6  8]
 [ 2  4  6  8 10]
 [ 4  6  8 10 12]
 [ 6  8 10 12 14]
 [ 8 10 12 14 16]]

xarray's "coarsen" method can downsample a xarray.Dataset or xarray.DataArray

For example:

import xarray as xr
import numpy as np
import matplotlib.pyplot as plt

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15,5))

# Create a 10x10 array of random numbers
a = xr.DataArray(np.random.rand(10,10)*100, dims=['x', 'y'])

# "Downscale" the array, mean of blocks of size (2x2)
b = a.coarsen(x=2, y=2).mean()

# "Downscale" the array, mean of blocks of size (5x5)
c = a.coarsen(x=5, y=5).mean()


# Plot and cosmetics
a.plot(ax=ax1)
ax1.set_title("Full Data")

b.plot(ax=ax2)
ax2.set_title("mean of (2x2) boxes")

c.plot(ax=ax3)
ax3.set_title("mean of (5x5) boxes")

在此处输入图片说明

This might not be what you're looking for, but I thought I'd mention it for completeness.

You could try installing scikits.samplerate ( docs ), which is a Python wrapper for libsamplerate. It provides nice, high-quality resampling algorithms -- BUT as far as I can tell, it only works in 1D. You might be able to resample your 2D signal first along one axis and then along another, but I'd think that might counteract the benefits of high-quality resampling to begin with.

This will take an image of any resolution and return only a quarter of its size by taking the 4th index of the image array.

import cv2
import numpy as np

def quarter_res_drop(im):

    resized_image = im[0::4, 0::4]
    cv2.imwrite('resize_result_image.png', resized_image)

    return resized_image

im = cv2.imread('Your_test_image.png', 1)

quarter_res_drop(im)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM