Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate a 2D image around specified origin in Python

I have a 2D image of 512x512 pixels that I would like to rotate with a certain angle at a certain origin (rotation center). All this time, I uses Scipy to rotate images with its rotate method. But, I got stumbled because the rotation always done around the center of the image. With 512x512 pixels the rotation center should be around point (x,y) 128,128. How can I rotate the image with a custom rotation center, let's say around (x,y) 20,128?

like image 993
Habib Rosyad Avatar asked Aug 23 '14 04:08

Habib Rosyad


1 Answers

If OpenCV is not an option, you can do image rotation around a so called pivot point with NumPy (import numpy as np) and SciPy (from scipy import ndimage) the following way:

  1. Pad the image img such that the pivot point is in the image center 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')
    

    (While the image shape is in row-column order, pivot is in X-Y or column-row order here. You might want to define it differently.)

  2. Rotate the image around its center (here the rotation angle is 45 degrees):

    imgR = ndimage.rotate(imgP, 45, reshape=False)
    

    Note that we disallow reshaping the image, since we'll crop the image ourselves.

  3. Crop the image such that the pivot point is at its original position. Therefore, we simply reverse the padding from step 1:

    imgC = imgR[padY[0] : -padY[1], padX[0] : -padX[1]]
    

You can see the different steps in the following plot (original image, padded, rotated, cropped; 45 degrees around (100, 300)).

enter image description here

Wrapping it up in a handy function yields:

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 colored images you'd have to avoid adding more channels while padding (zero padding in 3rd dimension):

imgP = np.pad(img, [padY, padX, [0, 0]], 'constant')

Don't forget to use a 0 for both "before" and "after" padding. Otherwise you get a ValueError.

like image 189
Falko Avatar answered Sep 17 '22 18:09

Falko