3

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! :)

Robula
  • 544
  • 1
  • 10
  • 25

1 Answers1

2

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.

Falko
  • 15,326
  • 12
  • 50
  • 91
  • Of course!! We count from 0, not 1.. I'm working with matrices, not pixels! Thank you for pointing that out! :) Now I'm doing a -1 on both width/height for my center point and again on my bound_w/bound_h translation calculation. It fits! Thank you – Robula Nov 25 '15 at 11:57