Matheus Correia
Matheus Correia

Reputation: 101

Calculate pixels belonging to corners of rectangle after rotating image

I have seen some questions related to this topic, but I have been unable to get the results I was looking for even after following them, so I assume something about my math is off. Here's the scenario.

I have got an image that must be rotated by 90 degrees. At the same time, I have got the coordinates (x_min, y_min) and (x_max, y_max) of the corners of a rectangle that is drawn over a certain object in the image. Let's use the image below as an example, the rectangle being used here is defined by (4, 196) and (145, 269).

Image with box

My goal, then, is to rotate the image and do the same with the rectangle, assuring it keeps surrounding the target object.

First, I am rotating the image 90 degrees (clockwise) by using imutils.

90_rotated_image = imutils.rotate_bound(original_image, 90)

Then, I am calculating the new coordinates of the rectangle. For this, I need to know the point around which imutils rotates the image. I have tried a few combinations, including (0,0), but here I will assume it's the center of the image itself.

radians = math.radians(90)

h, w, c = original_image.shape

center_x = w/2
center_y = h/2

# Changes to solve the problem. Center of rotated image is now considered.

h_rotated, w_rotated, c = 90_rotated_image.shape

center_x_rotated = w_rotated/2
center_y_rotated = h_rotated/2

x_min_90 = center_x_rotated + math.cos(radians) * (x_min - center_x) - math.sin(radians) * (y_min - center_y)
y_min_90 = center_y_rotated + math.sin(radians) * (x_min - center_x) + math.cos(radians) * (y_min - center_y)

x_max_90 = center_x_rotated + math.cos(radians) * (x_max - center_x) - math.sin(radians) * (y_max - center_y)
y_max_90 = center_y_rotated + math.sin(radians) * (x_max - center_x) + math.cos(radians) * (y_max - center_y)

Finally, I draw the new rotated rectangle over the rotated image.

start_point = (x_min_90, y_min_90) 
end_point = (x_max_90, y_max_90)

image = cv2.rectangle(90_rotated_image, start_point, end_point, (255, 0, 0), 2)

Here's what I am getting.

Rotated image with wrong box

The question is: what's wrong here? Why won't the rectangle rotate properly and give me the intended result, simulated below.

Intended result

EDIT: To anyone stuck with the same problem, the solution to make the rotation work with rectangular images is quite simple. The center_x and center_y variables added to the result of the rotation (the multiplications involving sin and cos) have to be related to the center of the image post-rotation not pre-rotation as I had initially written into the code. The post has been updated to reflect the solution.

Upvotes: 0

Views: 724

Answers (1)

MBo
MBo

Reputation: 80197

At first, in general case center of image in global coordinates should look like this (I don't know exactly what is rotation center in your case):

center_x = min_x + w/2
center_y = min_y + h/2

Second: arguments of trigonometric function must be in radians, not degrees, so math.cos(math.radians(degrees_angle)) and so on

Third: if before rotation, min_corner is left bottom, after rotation it becomes right bottom (or perhaps left top depending on your coordinate system orientation); same for max_corner. Also using rotation about some center, you have to add center coordinates at the end.

So min/max coordinates for rotated rectangle are:

x_min_90 = center_x + (y_min - center_y)
y_min_90 = center_y - (x_max - center_x)

x_max_90 = center_x + (y_max - center_y)
y_max_90 = center_y - (x_min - center_x)

enter image description here

For this example ABCD -> FGHI:

xmin = 2  ymin = 1
xmax = 8  ymax = 5

center_x = 2 + 3 = 5
center_y = 1 + 2 = 3

xmin90 = 5 + (1 - 3) = 3
y_min_90 = 3 - (8 - 5) = 0
x_max_90 = 5 + (5 - 3) = 7
y_max_90 = 3 - (2 - 5) = 6

Note:

  1. I used mathematical coordinate system (OX right, OY to the top), if your OY axis goes down, then change orientation as needed.
  2. I substituted cos and sin of 90 with 0 and 1. For other angles use cos and sin but don't forget about radians:

How to use formulas from question to get right result for angles 90,180,270 degrees:

x_a_90 = center_x + math.cos(radians) * (x_min - center_x) - math.sin(radians) * (y_min - center_y)
y_a_90 = center_y + math.sin(radians) * (x_min - center_x) + math.cos(radians) * (y_min - center_y)

x_b_90 = center_x + math.cos(radians) * (x_max - center_x) - math.sin(radians) * (y_max - center_y)
y_b_90 = center_y + math.sin(radians) * (x_max - center_x) + math.cos(radians) * (y_max - center_y)

x_min_90 = min(x_a_90, x_b_90)
x_max_90 = max(x_a_90, x_b_90)
y_min_90 = min(y_a_90, y_b_90)
y_max_90 = max(y_a_90, y_b_90)

Upvotes: 1

Related Questions