How do I change the angle of a cv2 rectangle?

Viewed 527

I am using a cv2 rectangle for bounding box purposes and I want to rotate it about its axis by some angle. Since the warpAffine function is used to rotate images, I am unable to use it here.

3 Answers

There's no built-in function to do this in cv2. You can get around this by determining the coordinates of the four corners of your rotated rectangle, the using cv2.polylines to plot it.

Here's a function that does exactly so, and can be used as a drop-in replacememt for cv2.rectangle - the only additional argumenty is rotation (which should be in degrees):

import numpy as np
from numpy import cos, sin

def rotated_rectangle(image, start_point, end_point, color, thickness, rotation=0):
    center_point = [(start_point[0]+end_point[0])//2, (start_point[1]+end_point[1])//2]
    height = end_point[1] - start_point[1]
    width = end_point[0] - start_point[0]
    angle = np.radians(rotation)
    
    # Determine the coordinates of the 4 corner points
    rotated_rect_points = []
    x = center_point[0] + ((width / 2) * cos(angle)) - ((height / 2) * sin(angle))
    y = center_point[1] + ((width / 2) * sin(angle)) + ((height / 2) * cos(angle))
    rotated_rect_points.append([x,y])
    x = center_point[0] - ((width / 2) * cos(angle)) - ((height / 2) * sin(angle))
    y = center_point[1] - ((width / 2) * sin(angle)) + ((height / 2) * cos(angle))
    rotated_rect_points.append([x,y])
    x = center_point[0] - ((width / 2) * cos(angle)) + ((height / 2) * sin(angle))
    y = center_point[1]- ((width / 2) * sin(angle)) - ((height / 2) * cos(angle))
    rotated_rect_points.append([x,y])
    x = center_point[0] + ((width / 2) * cos(angle)) + ((height / 2) * sin(angle))
    y = center_point[1] + ((width / 2) * sin(angle)) - ((height / 2) * cos(angle))
    rotated_rect_points.append([x,y])
    cv2.polylines(image, np.array([rotated_rect_points], np.int32), True, color, thickness)

It's already some time I used it, but as far as I remember, all these ROI types are just plain x-y rectangles. No rotation is possible (you can also see from the constructors, there is no way how to define the rotation). So it is always wide along x-axis and high along y-axis.

If you want to just draw rectangle, I propose to just determine all four corner points and then by simple lines draw the bounding box. Eventually you can use polylines to do it in one command.

If you need to apply an affine transformation to points instead of a whole image, you can use cv.transform. cv.getRotationMatrix2D gets you an affine transformation to rotate your rectangle points around any arbitrary center.

OpenCV has the notion of a RotatedRect. You can trivially construct them given width, height, and an angle. You don't need to know any trigonometry and you need no calculations at all.

RotatedRect::points() gives you the corners of the rectangle. You can use those to draw the rectangle as a polygon or "contour".

The equivalent in Python is the cv.boxPoints function. It takes the python tuple of numbers that represents the RotatedRect C++ structure.

The documentation for these functions/methods also refers to a tutorial that is included in OpenCV documentation. It uses drawContours and passes the four corner points as one contour.

Related