cv2.minAreaRect角度问题与cv2.boxPoints点顺序问题
1. cv2.minAreaRect
cv2.minAreaRect是用于计算一组点的最小外接矩形的函数。
输入:points为轮廓或多边形点集,形状为(N,2),类型为numpy.ndarray 或 list,数据类型为整数或浮点数。
输出:包含三个元素的元组((cx, cy), (width, height), angle),其中cx,cy,width, height,angle均为float类型,分别代表中最小外接矩形的心点坐标,宽,高,旋转角度。
2.cv2.minAreaRect返回值的角度范围与角度的定义及宽高的确定
从上面的输出中可以看到最后一个返回值为angle,对于角度范围于定义如下:
OpenCV 4.5及之后版本:角度范围为(0, 90],表示x轴顺时针旋转到第一次接触到矩形边界时的角度,定义该边为width
OpenCV 4.5之前版本:角度范围为[-90, 0),表示x轴逆时针旋转到第一次接触到矩形边界时的角度,定义该边为width
这里以高版本的OpenCV (OpenCV 4.5及之后版本)为例,看示意图:
当与水平有一定的倾斜角度a时,x轴顺时针旋转a先重合的边定义为返回值中的width,另外一边定义为height。

x轴顺时针旋转a后就重合的一边为返回值的width。

但如果是正矩形,首先我们知道了角度范围为(0, 90],不可能为0,必定要旋转,所以对于没有倾斜角度的矩形,其旋转角度为90度。根据旋转规则,示意图如下:

x轴顺时针旋转a后就重合的一边为返回值的width。

对于正矩形,从上图可以看出与我们平时视觉认为的宽高有所不同。
3. cv2.boxPoints返回值点坐标顺序
这里以opencv4.5.3为例,其他版本没有验证过。
使用rect=cv2.minAreaRect(points)得到最小外接矩形的中心点坐标,宽,高,旋转角度外,常常需要获取这个最小外接矩形的四个顶点的坐标,需要使用box = cv2.boxPoints(rect)获取,然后使用box = np.int0(box)或box =np.int_(box)进一步将坐标转换成整数。这里先说结论,如果矩形没有倾斜的角度,那么得到的box的四个点坐标顺序为左上,右上,右下,左下;如果有倾斜角(0,90),则第一个至第四个点顺序为左(xmin),上(ymin),右(xmax),下(ymax);具体看下面程序与示意图:
这里角度仍然以顺时针角度(0,90]范围作为讨论:
import cv2 import numpy as np image = np.zeros((500, 500, 3), dtype=np.uint8) # 定义矩形的中心点、宽度、高度和旋转角度 center = (250, 250) # 中心点坐标 width = 100 height = 50 angle = 45 # 旋转角度,单位为度 rect = ((center[0], center[1]), (width, height), angle) box = cv2.boxPoints(rect) # 计算旋转矩形的四个顶点 box = np.int0(box) # 将顶点坐标转换为整数 for i, point in enumerate(box): print(f"点 {i + 1}: {point}") # 打印矩形的四个顶点坐标 print("矩形的四个顶点坐标:") cv2.drawContours(image, [box], 0, (0, 255, 0), 2)
# 在每个顶点位置绘制文本数字
for i, point in enumerate(box):
cv2.putText(image, str(i + 1), tuple(point), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.imshow("Rotated Rectangle", image) cv2.waitKey(0) cv2.destroyAllWindows()

将上述程序的angle=45改为90度,效果如下:

与前面讨论的90度情况下一致,宽为竖边100,高为横边50。
4. 获取某个轮廓(多边形点集)的最小外接矩形
程序使用的原始图

import cv2 import numpy as np # 读取图片 image = cv2.imread('oval.png') # 替换为你的图片路径 if image is None: print("图片加载失败,请检查路径!") exit() # 将图片转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #生成二值图 ### binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 使用大津法进行阈值化生成二值图 _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) cv2.imshow("binary", binary) cv2.waitKey(0) cv2.destroyAllWindows() # 查找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 假设最大的轮廓是椭圆 if contours: # 找到面积最大的轮廓 largest_contour = max(contours, key=cv2.contourArea) # 计算最小外接矩形 rect = cv2.minAreaRect(largest_contour) box = cv2.boxPoints(rect) box = np.int0(box) # 打印最小外接矩形的信息 print("最小外接矩形信息:") print(f"中心点: {rect[0]}") print(f"宽度: {rect[1][0]}") print(f"高度: {rect[1][1]}") print(f"旋转角度: {rect[2]}") # 在原图上绘制最小外接矩形 cv2.drawContours(image, [box], 0, (0, 0, 255), 2) # 使用红色绘制 # 显示结果 cv2.imshow("Image with Min Area Rectangle", image) cv2.waitKey(0) cv2.destroyAllWindows()
运行结果如下:


小结:对cv2.minAreaRect与cv2.boxPoints进行了分析,本文测试均是采用opencv4.5.3版本在分析cv2.boxPoints返回值的角度问题时,只讨论(0,90]度的情况是为了保持与cv2.minAreaRect中返回的角度一致,尽管cv2.boxPoints(rect) 中rect里面的angle输入值是可以超过这个范围的,这里不再讨论其输出与规律;
若有不足之处。欢迎评价与讨论!

浙公网安备 33010602011771号