How to use Matlab's imresize in python

后端 未结 3 1601
情书的邮戳
情书的邮戳 2021-01-04 22:27

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

相关标签:
3条回答
  • 2021-01-04 22:50

    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.

    0 讨论(0)
  • 2021-01-04 22:57

    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.

    0 讨论(0)
  • 2021-01-04 23:09

    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.

    0 讨论(0)
提交回复
热议问题