Opencv---RotatedRect
在Robomaster比赛中,RotatedRect类极为常用,灯条、装甲板以至于整车都可以用RotatedRect类来表示。
一、基本概念与用途
RotatedRect
是OpenCV中用于表示旋转矩形的类,常用于需要描述矩形方向(角度)的场景,如:
- 目标检测中的倾斜边界框(如灯条、装甲板、车牌、文本检测)。
- 形状分析中的方向感知(如椭圆拟合后的方向)。
- 几何变换中的旋转矩形表示。
与普通轴对齐矩形 Rect
的区别:
Rect
仅包含位置和尺寸(轴对齐)。RotatedRect
包含中心点、尺寸、旋转角度,可表示任意方向的矩形。
二、类定义与构造函数
1. 成员变量(C++)
class RotatedRect {
public:Point2f center; // 中心点坐标(x, y)Size2f size; // 矩形尺寸(宽, 高)float angle; // 旋转角度(单位:度,逆时针为正)// 构造函数、成员函数...
};
2. 构造方式
- 标准构造:通过中心点、尺寸、角度初始化。
// C++ RotatedRect rRect(Point2f(cx, cy), Size2f(w, h), angle);// Python rRect = cv2.RotatedRect((cx, cy), (w, h), angle)
- 从轮廓/点集拟合:通过
minAreaRect()
函数从点集计算最小面积旋转矩形。// C++ vector<Point2f> points; // 轮廓点集 RotatedRect rRect = minAreaRect(points);// Python rRect = cv2.minAreaRect(cnt) # cnt为轮廓点集(numpy数组)
三、核心知识点讲解
1. 旋转角度(angle)的定义
- 方向:矩形的水平轴(长边)与图像坐标系x轴的夹角,逆时针为正。
- 范围约定:OpenCV中
angle
通常位于 -90° < angle ≤ 0°。当矩形竖放时(高 > 宽),会自动交换宽高,并将角度调整为 -90° < angle ≤ 0°。例如:- 若原始宽=20,高=40,角度=30°,则实际存储为宽=40,高=20,角度=-60°(保证宽≥高,角度在约定范围)。
2. 顶点坐标计算(points()函数)
通过 RotatedRect
的 points()
方法(C++)或 cv2.boxPoints()
(Python)获取矩形的4个顶点,顺序为:
- 左上角顶点(中心点左侧,沿旋转方向第一个点)。
- 右上角顶点(中心点右侧,顺时针第二个点)。
- 右下角顶点。
- 左下角顶点。
示例代码:
// C++
vector<Point2f> vertices;
rRect.points(vertices); // vertices包含4个顶点,顺序如上述// Python
vertices = cv2.boxPoints(rRect) # 返回numpy数组,形状为(4,2)
3. 与轴对齐矩形(Rect)的转换
- 获取包围旋转矩形的最小轴对齐矩形:
Rect boundRect = rRect.boundingRect2f(); // C++,返回浮点型Rect
x, y, w, h = cv2.boundingRect(vertices.astype(int)) # Python,需先转换为整数顶点
- 旋转矩形转普通矩形:仅取顶点坐标的极值,不保留角度信息。
4. 几何运算
- 面积与周长:
float area = rRect.size.width * rRect.size.height; // 面积 float perimeter = 2 * (rRect.size.width + rRect.size.height); // 周长(非旋转周长)
- 旋转矩阵计算:以中心点为原点,计算旋转后的坐标变换矩阵:
Mat rotationMat = getRotationMatrix2D(rRect.center, rRect.angle, 1.0); // 旋转矩阵
5. 绘制与可视化
通过顶点坐标绘制旋转矩形:
// C++
for (int i = 0; i < 4; i++) {line(img, vertices[i], vertices[(i+1)%4], Scalar(0, 255, 0), 2);
}// Python
for i in range(4):cv2.line(img, tuple(vertices[i].astype(int)), tuple(vertices[(i+1)%4].astype(int)), (0, 255, 0), 2)
6. 碰撞检测(两旋转矩形相交判断)
OpenCV未直接提供相交检测函数,需手动实现:
- 将旋转矩形转换为多边形(4顶点)。
- 使用 分离轴定理(SAT) 或 多边形相交算法 判断是否相交。
- 示例思路:
def is_rotated_rect_intersect(r1, r2):box1 = cv2.boxPoints(r1).astype(int)box2 = cv2.boxPoints(r2).astype(int)# 转换为凸多边形,使用cv2.pointPolygonTest判断点是否在另一多边形内for p in box1:if cv2.pointPolygonTest(box2, tuple(p), False) >= 0:return Truefor p in box2:if cv2.pointPolygonTest(box1, tuple(p), False) >= 0:return Truereturn False
四、注意事项与常见误区
-
角度与宽高的绑定关系:
- 当
angle
在 (-90°, 0] 时,size.width ≥ size.height
(长边为水平轴)。 - 若手动设置
angle > 0°
或size.width < size.height
,OpenCV会自动调整宽高和角度,以保证约定范围。
- 当
-
顶点顺序的一致性:
points()
返回的顶点顺序固定,可通过顺时针或逆时针顺序绘制闭合矩形。
-
浮点精度问题:
- 顶点坐标默认为浮点型,绘制时需转换为整数(
astype(int)
),避免亚像素误差。
- 顶点坐标默认为浮点型,绘制时需转换为整数(
-
与椭圆(Ellipse)的区别:
RotatedRect
是矩形,Ellipse
是椭圆,拟合时根据形状选择工具(如fitEllipse()
返回椭圆,minAreaRect()
返回矩形)。
五、应用场景示例
1. 目标检测中的倾斜边界框
# 假设检测到轮廓cnt,提取旋转矩形
cnt = ... # 轮廓点集
rRect = cv2.minAreaRect(cnt)
vertices = cv2.boxPoints(rRect).astype(int)
cv2.polylines(img, [vertices], isClosed=True, color=(0, 255, 0), thickness=2)
2. 形状方向分析
// 计算矩形的主方向(角度)
float angle = rRect.angle;
if (angle < -45) angle += 90; // 转换为0°~90°范围的方向角
3. 图像旋转后的边界框调整
# 对图像旋转θ角后,重新计算旋转矩形的位置
def rotate_image_with_box(img, rRect, theta):rows, cols = img.shape[:2]M = cv2.getRotationMatrix2D(rRect.center, theta, 1.0)rotated_img = cv2.warpAffine(img, M, (cols, rows))# 调整旋转矩形的角度new_rRect = cv2.RotatedRect(rRect.center, rRect.size, rRect.angle + theta)return rotated_img, new_rRect
六、跨语言差异(C++ vs Python)
特性 | C++ | Python |
---|---|---|
构造函数参数 | Point2f , Size2f , float | 元组 (cx, cy) , (w, h) , float |
顶点获取 | points(vector<Point2f>&) | cv2.boxPoints(rRect) 返回numpy数组 |
序列化 | 需手动存储成员变量 | 可转为元组或JSON存储 |
七、数学原理补充(顶点坐标推导)
设中心点为 (c_x, c_y)
,尺寸为 (w, h)
,角度为 θ
,则4个顶点坐标可通过旋转矩阵计算:
-
原始矩形(未旋转)的顶点相对于中心点的坐标:
(±w/2, ±h/2)
(左上:(-w/2, -h/2)
,右上:(w/2, -h/2)
,右下:(w/2, h/2)
,左下:(-w/2, h/2)
)。 -
旋转θ角后的坐标:
[ x ′ y ′ ] = [ cos θ − sin θ sin θ cos θ ] [ x y ] + [ c x c y ] \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cosθ & -\sinθ \\ \sinθ & \cosθ \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} c_x \\ c_y \end{bmatrix} [x′y′]=[cosθsinθ−sinθcosθ][xy]+[cxcy]
-
代入4个顶点的原始坐标,得到旋转后的绝对坐标。
我们太看重了白昼,又太忽视着黑夜。生命,至少有一半是在黑夜中呀。 —史铁生