Robula
Robula

Reputation: 716

How to find the translation values after a rotate about a point for a 2D image?

I am having issues getting the correct translation values after rotating my image. The code I have so far calculates the bounding box for a given rotation using basic trigonometry, it then applies a translation to the rotation matrix. The issue I am having however is that my translation always seems to be 1 pixel out, by that I mean I get a 1-pixel black border along the top or sides of my rotated image.

Here is my code:

def rotate_image(mat, angle):
    height, width = mat.shape[:2]
    image_center = (width / 2.0, height / 2.0)
    rotation_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)

    # Get Bounding Box
    radians = math.radians(angle)
    sin = abs(math.sin(radians))
    cos = abs(math.cos(radians))
    bound_w = (width * cos) + (height * sin)
    bound_h = (width * sin) + (height * cos)

    # Set Translation
    rotation_mat[0, 2] += (bound_w / 2.0) - image_center[0]
    rotation_mat[1, 2] += (bound_h / 2.0) - image_center[1]

    rotated_mat = cv2.warpAffine(mat, rotation_mat, (int(bound_w), int(bound_h)))
    return rotated_mat

Here is the original image for reference and some examples of the image using that code:

coffee.png – Original coffee.png – Original

coffee.png - 90° - Notice the 1px border across the top coffee.png - 90°

coffee.png - 180° - Notice the 1px border across the top and left coffee.png - 1°

I am not so hot on my math, but I hazard a guess that this is being caused by some rounding issue as we’re dealing with floating point numbers. I would like to know what methods other people use, what would be the simplest and most performant way to rotate and translate an image about its centre point please?

Thank you.

EDIT

As per @Falko's answer, I was not using zero-based calculation. My corrected code is as follows:

def rotate_image(mat, angle):
    height, width = mat.shape[:2]
    image_center = ((width - 1) / 2.0, (height - 1) / 2.0)
    rotation_mat = cv2.getRotationMatrix2D(image_center, angle, 1.0)

    # Get Bounding Box
    radians = math.radians(angle)
    sin = abs(math.sin(radians))
    cos = abs(math.cos(radians))
    bound_w = (width * cos) + (height * sin)
    bound_h = (width * sin) + (height * cos)

    # Set Translation
    rotation_mat[0, 2] += ((bound_w - 1) / 2.0 - image_center[0])
    rotation_mat[1, 2] += ((bound_h - 1) / 2.0 - image_center[1])

    rotated_mat = cv2.warpAffine(mat, rotation_mat, (int(bound_w), int(bound_h)))
    return rotated_mat

I'd still appreciate seeing alternative methods people are using to perform rotation and translation! :)

Upvotes: 4

Views: 1276

Answers (1)

Falko
Falko

Reputation: 17877

I guess your image center is wrong. Take, e.g., a 4x4 image with columns 0, 1, 2 and 3. Then your center is computed as 4 / 2 = 2. But it should be 1.5 between column 1 and 2.

So you better use (width - 1) / 2.0 and (height - 1) / 2.0.

Upvotes: 3

Related Questions