How to use Matlab's imresize in python

25,146

Solution 1

The scipy.misc.imresize function is a bit odd for me. For one thing, this is what happens when I specify the sample 2D image you provided to a scipy.misc.imresize call on this image with a scale of 1.0. Ideally, it should give you the same image, but what we get is this (in IPython):

In [35]: from scipy.misc import imresize

In [36]: import numpy as np

In [37]: dtest = np.array(([1,2,3],[4,5,6],[7,8,9]))

In [38]: out = imresize(dtest, 1.0)

In [39]: out
Out[39]: 
array([[  0,  32,  64],
       [ 96, 127, 159],
       [191, 223, 255]], dtype=uint8)

Not only does it change the type of the output to uint8, but it scales the values as well. For one thing, it looks like it makes the maximum value of the image equal to 255 and the minimum value equal to 0. MATLAB's imresize does not do this and it resizes an image in the way we expect:

>> dtest = [1,2,3;4,5,6;7,8,9];
>> out = imresize(dtest, 1)

out =

     1     2     3
     4     5     6
     7     8     9

However, you need to be cognizant that MATLAB performs the resizing with anti-aliasing enabled by default. I'm not sure what scipy.misc.resize does here but I'll bet that there is no anti-aliasing enabled.

Edit - November 23rd, 2016

As noted by Eric in his comments below, if you pre-cast the image to the desired type, you will get the expected results:

In [10]: dtest = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)

In [11]: out = imresize(dtest, 1.0)

In [12]: out
Out[12]: 
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]], dtype=uint8)

We can see that the image is not scaled to the [0,255] range. To finally get where you need to go, we must obtain a floating-point representation of the image. scipy.misc.imresize has an additional flag called 'mode' and you can specify this as 'F' to ensure the output is floating point.

In [14]: scale = 1.4

In [15]: out = imresize(dtest, 1/scale, mode='F')

In [16]: out
Out[16]: 
array([[ 2.5 ,  3.75],
       [ 6.25,  7.5 ]], dtype=float32)

As you will see later, the results that you see with scipy.misc.resize don't match with what you see in MATLAB.

For the best results, don't specify a scale - specify a target output size to reproduce results. As such, 1/scale in your case is close to a 2 x 2 size output, and so here's what you would do in MATLAB:

>> dtest = [1,2,3;4,5,6;7,8,9];
>> out = imresize(dtest, [2,2], 'bilinear', 'AntiAliasing', false)

out =

    2.0000    3.5000
    6.5000    8.0000

You can see that some of the values in the matrix don't align with scipy.misc.resize. To match what you see in MATLAB. The closest thing to what you want is either OpenCV's resize function, or scikit-image's resize function. Both of these have no anti-aliasing. If you want to make both Python and MATLAB match each other, use the bilinear interpolation method. imresize in MATLAB uses bicubic interpolation by default and I know for a fact that MATLAB uses custom kernels to do so, and so it will be much more difficult to match their outputs if you use bicubic interpolation between the methods. See this post for some more informative results:

MATLAB vs C++ vs OpenCV - imresize

With Python OpenCV:

In [93]: import numpy as np

In [94]: import cv2

In [95]: dtest = np.array(([1,2,3],[4,5,6],[7,8,9]), dtype='float')

In [96]: out = cv2.resize(dtest, (2,2))

In [97]: out
Out[97]: 
array([[ 2. ,  3.5],
       [ 6.5,  8. ]])

With scikit-image:

In [100]: from skimage.transform import resize

In [101]: dtest = np.array(([1,2,3],[4,5,6],[7,8,9]), dtype='uint8')

In [102]: out = resize(dtest, (2,2), order=1, preserve_range=True)

In [103]: out
Out[103]: 
array([[ 2. ,  3.5],
       [ 6.5,  8. ]])

One last interesting thing to note is that MATLAB, OpenCV and scikit-image when specifying a floating point scale act differently with each other. I did some experiments and by specifying a floating point size, I was unable to get the results to match. Besides which, scikit-image does not support taking in a scale factor which is more reason to explicitly state an output size rather than a scale.

Solution 2

To add one more option I found, while exploring the excellent answer by @rayryeng.

The scipy.misc.imresize uses PIL (and hence converts the image to scaled integers). But the page links to another function: scipy.ndimage.zoom

>>> from scipy import ndimage
>>> dtest = np.array(([1,2,3],[4,5,6],[7,8,9]), dtype='float')
>>> ndimage.zoom(dtest, 2/3)
array([[ 1.,  3.],
       [ 7.,  9.]])
>>> ndimage.zoom(dtest, 2/3, prefilter=False)
array([[ 2.33333333,  3.66666667],
       [ 6.33333333,  7.66666667]])

It does not give me the same result as matlab, but it comes close:

>> dtest = [1,2,3;4,5,6;7,8,9];
>> imresize(dtest, [2,2])

ans =
    2.1296    3.5648
    6.4352    7.8704

Depending on what you want to achieve, this could be useful. For me it has the advantage of not needing to include another package to the project, since scipy is already used.

Solution 3

After a lot of digging, the only solution that I found which replicates matlab's imresize with anti-aliasing is the code by Alex (fatheral) at https://github.com/fatheral/matlab_imresize . Currently it only uses the bicubic kernel, but can be easily expanded to any other kernel provided in Matlab.

Share:
25,146
user3960019
Author by

user3960019

Updated on August 03, 2022

Comments

  • user3960019
    user3960019 over 1 year

    I'm transferring Matlab's imresize code into python. I found the scipy's imresize, but I get a different results from Matlab.

    How to get the same results as Matlab by python.

    Python/scipy imresize

    from scipy.misc import imresize
    import numpy as np
    
    dtest = np.array(([1,2,3],[4,5,6],[7,8,9]))
    scale = 1.4
    dim = imresize(dtest,1/scale)
    

    Matlab imresize

    dtest = [1,2,3;
             4,5,6;
             7,8,9];
    scale = 1.4;
    dim = imresize(dtest,1/scale);
    

    These two pieces of code return different results.