2

I needed to know what is the fastest way to rotate an Image in Python.

There is no such question asked or answered to my knowledge in Stack Overflow, so I decided to run an experiment and find out the answer. That is why I would like to share the result with you:

#we will compare which method is faster for rotating a 2D image 
#there are four major libraries for image handling and manipulation 
#PIL 
#OpenCV
#SciPy
#skimage 

import numpy as np
import PIL
import cv2
import matplotlib.pylab as plt
from PIL import Image
from scipy.ndimage import rotate
from scipy.ndimage import interpolation
import scipy
from skimage.transform import rotate




#get data :
PIL_image = Image.open('cat.jpg')
#image = cv2.imread('bambi.jpg') #read gray scale
array_image = np.array(PIL_image)

Define a function for each module:

def rotate_PIL (image, angel, interpolation):
    #PIL.Image.NEAREST (use nearest neighbour), PIL.Image.BILINEAR (linear interpolation in a 2×2 environment), or PIL.Image.BICUBIC 


    return image.rotate(angel,interpolation)
    
    
def rotate_CV(image, angel , interpolation):
    #in OpenCV we need to form the tranformation matrix and apply affine calculations
    #interpolation cv2.INTER_CUBIC (slow) & cv2.INTER_LINEAR
    h,w = image.shape[:2]
    cX,cY = (w//2,h//2)
    M = cv2.getRotationMatrix2D((cX,cY),angel,1)
    rotated = cv2.warpAffine(image,M , (w,h),flags=interpolation)
    return rotated

    

def rotate_scipy(image, angel , interpolation):
    return  scipy.ndimage.interpolation.rotate(image,angel,reshape=False,order=interpolation)

Use timeit to find out which is fastest , PIL , Open CV or Scipy:

%timeit rotate_PIL(PIL_image,20,PIL.Image.NEAREST)
%timeit rotate_CV(array_image,20,cv2.INTER_LINEAR)
%timeit rotate_scipy(array_image,20,0)

The result came out to be:

975 µs ± 203 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.5 ms ± 634 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
36.4 ms ± 11.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

There is a twist though - this was for a jpeg file and PIL just crushed the other two libraries. However I am working mainly with large raw images in TIFF format, so I also decided to test TIFF files:

#test for TIFF image 
#get data :
PIL_image = Image.open('TiffImage.tif')
#image = cv2.imread('bambi.jpg') #read gray scale
array_image = np.array(PIL_image)

%timeit rotate_PIL(PIL_image,20,PIL.Image.NEAREST)
%timeit rotate_CV(array_image,20,cv2.INTER_LINEAR)
%timeit rotate_scipy(array_image,20,0)

65.1 ms ± 9.35 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
38.6 ms ± 6.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
344 ms ± 43.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Which means OpenCV does a better job in TIFF file formats than our clear winner for JPEG files.

If anybody happens to know the reason, or if anyone has similar experiments, please share here. I think this will be a nice public document for comparison.

Also, please let me know if you think my experiment is not valid, or could be improved, for any reason.

ouflak
  • 2,458
  • 10
  • 44
  • 49
  • You could use EXIF orientation metadata: that way, you wouldn't need to modify the image data, but only the metadata. – Flimm Nov 08 '21 at 16:03
  • I imagine `numpy` + `PIL` would be pretty dang fast -- e.g. `import numpy as np; from PIL import Image; img_arr = np.array(Image.open('file.tif')); rotated_img = np.rot90(img_arr)` – Joshua Voskamp Nov 08 '21 at 16:05
  • 2
    "better job in TIFF file formats than our clear winner for JPEG files" -- once you've loaded (and thus decoded) the image to memory, the file format is irrelevant, they all become just arrays of raw pixels. What would make a difference is the size of images. – Dan Mašek Nov 08 '21 at 16:39
  • 3
    Why are you comparing `PIL.Image.NEAREST` and `cv2.INTER_LINEAR`? Why not using the same interpolation method? How does the rotation related to TIFF and JPEG file formats? Reading and decoding the file is unrelated to the rotation operation in the RAM (the format could be related when rotation is 90, 180 or 270 degrees, but I see you are using 20 degrees). Why aren't you posting an executable code sample (so we can reproduce your results)? – Rotem Nov 08 '21 at 16:41
  • 1
    Maybe your TIF contains `float` data and maybe OpenCV is better with them. – Mark Setchell Nov 08 '21 at 16:55

1 Answers1

0

First of all, thanks for all your comments. It helped me think about the problem in a better way and also consider the mistakes I made in the experiment.

I have done a better and reproducible experiment in which I generate an image using numpy arrays in different sizes and use three libraries of Pillow, OpenCV and Scipy to rotate them using the same interpolation function. The answer could be summarized in the following graph. To repeat the experiment or to see how I came to this conclusion, please refer to this link and please comment if you think it could be improved.

Comparison of runtime for three Image manipulation libraries in Python

Brad Larson
  • 170,088
  • 45
  • 397
  • 571