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
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.
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.
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.
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.