I need to downsample a 2D numpy array by a non-integer factor (e.g. 100x100 array to a 45x45 array) in a way that performs local averaging, just like Photoshop/gimp would do that for an image. I need double precision. Current options can't do it well.
scipy.ndimage.zoom
does not perform averaging, and basically uses
nearest-neighbor sampling (see previous question scipy.ndimage.interpolation.zoom uses nearest-neighbor-like algorithm for scaling-down )
scipy.misc.imresize
converts an array to an int8; I need more
precision and floating point
skimage.transform.rescale
also uses nearest-neighbor and forwards you to skimage.transform.downscale_local_mean
for local averaging,
skimage.transform.downscale_local_mean
can only perform integer scaling factor (and pads image with zeros if the factor is non-integer). Integer scaling factor is a trivial numpy excersice.
Did I miss any other options?
I ended up writing a small function that upscales the image using scipy.ndimage.zoom
, but for downscaling it first upscales it to be the multiple of the original shape, and then downscales by block-averaging. It accepts any other keyword arguments for scipy.zoom
(order
and prefilter
)
I'm still looking for a cleaner solution using available packages.
def zoomArray(inArray, finalShape, sameSum=False, **zoomKwargs):
inArray = np.asarray(inArray, dtype = np.double)
inShape = inArray.shape
assert len(inShape) == len(finalShape)
mults = []
for i in range(len(inShape)):
if finalShape[i] < inShape[i]:
mults.append(int(np.ceil(inShape[i]/finalShape[i])))
else:
mults.append(1)
tempShape = tuple([i * j for i,j in zip(finalShape, mults)])
zoomMultipliers = np.array(tempShape) / np.array(inShape) + 0.0000001
rescaled = zoom(inArray, zoomMultipliers, **zoomKwargs)
for ind, mult in enumerate(mults):
if mult != 1:
sh = list(rescaled.shape)
assert sh[ind] % mult == 0
newshape = sh[:ind] + [sh[ind] / mult, mult] + sh[ind+1:]
rescaled.shape = newshape
rescaled = np.mean(rescaled, axis = ind+1)
assert rescaled.shape == finalShape
if sameSum:
extraSize = np.prod(finalShape) / np.prod(inShape)
rescaled /= extraSize
return rescaled
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With