Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

3d rotation on image

I'm trying to get some code that will perform a perspective transformation (in this case a 3d rotation) on an image.

import os.path
import numpy as np
import cv

def rotation(angle, axis):
    return np.eye(3) + np.sin(angle) * skew(axis) \
               + (1 - np.cos(angle)) * skew(axis).dot(skew(axis))

def skew(vec):
    return np.array([[0, -vec[2], vec[1]],
                     [vec[2], 0, -vec[0]],
                     [-vec[1], vec[0], 0]])

def rotate_image(imgname_in, angle, axis, imgname_out=None):
    if imgname_out is None:
        base, ext = os.path.splitext(imgname_in)
        imgname_out = base + '-out' + ext
    img_in = cv.LoadImage(imgname_in)
    img_size = cv.GetSize(img_in)
    img_out = cv.CreateImage(img_size, img_in.depth, img_in.nChannels)
    transform = rotation(angle, axis)
    cv.WarpPerspective(img_in, img_out, cv.fromarray(transform))
    cv.SaveImage(imgname_out, img_out)

When I rotate about the z-axis, everything works as expected, but rotating around the x or y axis seems completely off. I need to rotate by angles as small as pi/200 before I start getting results that seem at all reasonable. Any idea what could be wrong?

like image 350
user1196203 Avatar asked Feb 08 '12 03:02

user1196203


1 Answers

First, build the rotation matrix, of the form

    [cos(theta)  -sin(theta)  0]
R = [sin(theta)   cos(theta)  0]
    [0            0           1]

Applying this coordinate transform gives you a rotation around the origin.

If, instead, you want to rotate around the image center, you have to first shift the image center to the origin, then apply the rotation, and then shift everything back. You can do so using a translation matrix:

    [1  0  -image_width/2]
T = [0  1  -image_height/2]
    [0  0   1]

The transformation matrix for translation, rotation, and inverse translation then becomes:

H = inv(T) * R * T

I'll have to think a bit about how to relate the skew matrix to the 3D transformation. I expect the easiest route is to set up a 4D transformation matrix, and then to project that back to 2D homogeneous coordinates. But for now, the general form of the skew matrix:

    [x_scale 0       0]
S = [0       y_scale 0]
    [x_skew  y_skew  1]

The x_skew and y_skew values are typically tiny (1e-3 or less).

Here's the code:

from skimage import data, transform
import numpy as np
import matplotlib.pyplot as plt

img = data.camera()

theta = np.deg2rad(10)
tx = 0
ty = 0

S, C = np.sin(theta), np.cos(theta)

# Rotation matrix, angle theta, translation tx, ty
H = np.array([[C, -S, tx],
              [S,  C, ty],
              [0,  0, 1]])

# Translation matrix to shift the image center to the origin
r, c = img.shape
T = np.array([[1, 0, -c / 2.],
              [0, 1, -r / 2.],
              [0, 0, 1]])

# Skew, for perspective
S = np.array([[1, 0, 0],
              [0, 1.3, 0],
              [0, 1e-3, 1]])

img_rot = transform.homography(img, H)
img_rot_center_skew = transform.homography(img, S.dot(np.linalg.inv(T).dot(H).dot(T)))

f, (ax0, ax1, ax2) = plt.subplots(1, 3)
ax0.imshow(img, cmap=plt.cm.gray, interpolation='nearest')
ax1.imshow(img_rot, cmap=plt.cm.gray, interpolation='nearest')
ax2.imshow(img_rot_center_skew, cmap=plt.cm.gray, interpolation='nearest')
plt.show()

And the output:

Rotations of cameraman around origin and center+skew

like image 66
Stefan van der Walt Avatar answered Sep 28 '22 08:09

Stefan van der Walt