Rotate a 2D image around a specified source in Python
I have a 512x512 px 2D image that I would like to rotate at a specific angle in a specific direction (center of rotation). All this time I have been using Scipy to rotate images using the rotate method. But, I came across because the rotation was always done around the center of the image. With 512x512 pixels, the center of rotation should be around point (x, y) 128,128. How can I rotate the image with a custom rotation center, say around (x, y) 20,128?
source to share
If OpenCV is not an option, you can rotate the image around the so-called pivot point using NumPy ( import numpy as np
) and SciPy ( from scipy import ndimage
) like this:
-
Draw the image
img
so that the pivot is in the center of the image and the image size is doubled:padX = [img.shape[1] - pivot[0], pivot[0]] padY = [img.shape[0] - pivot[1], pivot[1]] imgP = np.pad(img, [padY, padX], 'constant')
(As long as the image shape is in row-column order, it
pivot
is here in XY or column order. You might want to define it differently.) -
Rotate the image around its center (here the rotation angle is 45 degrees):
imgR = ndimage.rotate(imgP, 45, reshape=False)
Please note that we do not allow changing the image, as we crop the image ourselves.
-
Crop the image so that the pivot point is at its original position. So we just undo the indent from step 1:
imgC = imgR[padY[0] : -padY[1], padX[0] : -padX[1]]
You can see the different steps in the following graph (original image, padded, rotated, cropped, 45 degrees around (100, 300)).
Combining it into a convenient function, we get:
def rotateImage(img, angle, pivot):
padX = [img.shape[1] - pivot[0], pivot[0]]
padY = [img.shape[0] - pivot[1], pivot[1]]
imgP = np.pad(img, [padY, padX], 'constant')
imgR = ndimage.rotate(imgP, angle, reshape=False)
return imgR[padY[0] : -padY[1], padX[0] : -padX[1]]
Update
For color images, you will need to add no more channels when filling (zero padding in 3rd dimension):
imgP = np.pad(img, [padY, padX, [0, 0]], 'constant')
Remember to use 0
"before" and "after" shims. Otherwise, you will receive ValueError
.
source to share