7

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?

2
  • 1
    not the most elegant and I'm not sure of the math here but if you just want something that works I would scale it up by 9 (900X900) and then down by 20 (45X45) so you can do scaling by integer at both steps Commented Dec 6, 2015 at 22:06
  • 1
    I am currently using up-scaling to the nearest multiplier of the final size (135 in that case), and then down-scaling by block-averaging. It works, but it is kind of a ugly, especially for huge matrices. Commented Dec 6, 2015 at 22:12

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.