OpenCV-python小玩意6 计算轮廓长宽
本文介绍了使用OpenCV检测矩形目标轮廓并计算其长宽的方法。主要内容包括:1) 通过cv2.contourArea()找到最大轮廓;2) 使用cv2.minAreaRect()获取轮廓的最小外接旋转矩形;3) 从矩形信息中提取宽度和高度;4) 使用cv2.boxPoints()和cv2.drawContours()绘制旋转矩形。该方法适用于任意角度的矩形检测,通过计算矩形尺寸实现目标测量。
0.我要做什么?
本节任务是在预览界面中找到目标轮廓,计算该轮廓的长宽。
1.初步方案或待解决的问题
由于要检测的目标是一个矩形,所以我们可以先从图像轮廓中提取最大轮廓,计算其最小外接矩形,并获取矩形的尺寸和顶点坐标。
2.函数介绍
2.1 cv2.contourArea()
要找到最大轮廓,就需要先计算每个轮廓的面积,然后找到面积最大的轮廓。而contourArea()方法应运而生。
函数原型如下:
area = cv2.contourArea(
contour, # 轮廓点集
oriented=False # 是否计算有向面积
)
参数说明如下:
| 参数 | 类型 | 作用 | 默认值 |
|---|---|---|---|
contour |
list[list[Point]] |
轮廓点集,通常由 cv2.findContours() 返回 |
- |
oriented |
bool |
是否计算有向面积:False 无向(面积为正值);True 有向(面积符号取决于轮廓方向) |
False |
| 返回值 | 类型 | 说明 |
|---|---|---|
area |
float |
轮廓面积(当 oriented=False 时为正值;当 oriented=True 时可为正负) |
代码例子:
import cv2
# 图像预处理
img = cv2.imread('image/cap.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
# 轮廓检测
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 计算并输出轮廓面积
for i, cnt in enumerate(contours):
area = cv2.contourArea(cnt)
if area > 1000:
print(f"Contour {i + 1} Area: {area}")
cv2.drawContours(img, contours[i], -1, (0, 255, 0), 2) # 绘制单个轮廓在这里啦!
cv2.imshow('image', img)
cv2.waitKey(0)
效果如下图:
2.2 cv2.boundingRect()
找到轮廓后,就可以用cv2.boundingRect()函数来计算轮廓的最小外接矩形了。该函数用于计算点集或轮廓的最小正矩形(非旋转)的核心函数,适用于目标定位、尺寸分析和图像裁剪等场景。
函数原型如下:
x, y, w, h = cv2.boundingRect(points)
参数说明如下:
- 输入:
points- 点集或轮廓(N×2维np.array,数据类型为int32/float32)。 - 输出:返回元组
(x, y, w, h): (x, y):矩形左上角坐标(整数类型)。(w, h):矩形的宽度和高度(整数类型)。
代码举例:
import cv2
# 图像预处理
img = cv2.imread('test.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
# 轮廓检测
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 计算并绘制边界框
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt) # 获取矩形参数
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) # 绘制绿色矩形
cv2.imshow('Bounding Boxes', img)
cv2.waitKey(0)
注意:我们实际情况中,矩形的摆放是随机的,该函数未必就适合。我们需要的可能是适用旋转矩形的minAreaRect方法。
2.3 cv2.rectangle()
既然用到了画矩形,咱们就简单说说这个函数。
函数原型如下:
cv2.rectangle(img, pt1, pt2, color[, thickness[, lineType[, shift]]]) -> img
参数说明:
入参:
| 参数名 | 类型 | 说明 |
|---|---|---|
img |
Mat |
待绘制的图像(NumPy数组格式)。 |
pt1 |
Tuple[int, int] |
矩形对角线的起点坐标,格式为 (x1, y1) 。 |
pt2 |
Tuple[int, int] |
矩形对角线的终点坐标,格式为 (x2, y2)(与 pt1 构成任意对角线)。 |
color |
Tuple[B, G, R] |
矩形颜色: • BGR格式(如蓝色: (255,0,0))。• 灰度图可使用单值(如 255 表示白色)。 |
thickness |
int |
线条粗细(单位:像素): • 正数:边框线宽(如 2)。• 负数(如 -1 或 cv2.FILLED):填充整个矩形。 |
lineType |
int(可选) |
线条类型: • cv2.LINE_8(8连通,默认)• cv2.LINE_4(4连通)• cv2.LINE_AA(抗锯齿,平滑效果好)。 |
shift |
int(可选) |
坐标缩放位数(默认为 0):• shift=1 时坐标值除以 2,shift=2 时除以 4,用于高精度绘制。 |
返回值:
img:绘制好矩形的图像(与输入img相同)。
代码示例:
import cv2
import numpy as np
# 创建空白图像(512x512白色背景)
img = np.ones((512, 512, 3), dtype=np.uint8) * 255 # BGR白色
# 定义对角线坐标:左上角(50,50) → 右下角(300,300)
pt1 = (50, 50)
pt2 = (300, 300)
color = (0, 255, 0) # 绿色边框(BGR格式)
thickness = 5 # 5像素边框
# 绘制矩形
cv2.rectangle(img, pt1, pt2, color, thickness)
cv2.imshow("Rectangle", img)
cv2.waitKey(0)
效果图如下:
2.4 cv2.minAreaRect()
该函数用于计算轮廓的最小外接矩形,返回值为一个旋转矩形,包含矩形的中心坐标、宽度、高度和旋转角度。这就十分适合我们的要求了。
函数原型如下:
rect = cv2.minAreaRect(points)
参数说明如下:
输入参数:points:点集或轮廓坐标,格式为 np.array(形状为 [N, 2]),至少需 3 个非共线点(若为轮廓需从 findContours() 获取)。
返回值(RotatedRect 结构):
元组 ((center_x, center_y), (width, height), angle):
- 中心点
(center_x, center_y):浮点坐标(矩形质心)。 - 尺寸
(width, height):矩形的宽高(非长边优先,取决于旋转规则)。 - 角度
angle:矩形主轴与水平轴的夹角。范围是0到90度,顺时针旋转。(低版本可能不同,要注意区分)
得到这个旋转矩形的信息后,要将其画出来,这需要使用cv2.boxPoints()函数帮我们得到矩形的四个顶点。该函数很简单:
直接参考下面代码用起来:
import cv2
import numpy as np
# 创建空白图像(512x512白色背景)
img = np.ones((512, 512, 3), dtype=np.uint8) * 255 # BGR白色
rect = ((100.0, 100.0), (50.0, 30.0), 30.0) # 中心(100,100),宽50高30,角度30°
box = cv2.boxPoints(rect) # → 4个顶点 [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
print(box)
cv2.drawContours(img, [np.intp(box)], 0, (0, 255, 0), 2)
cv2.imshow("Rectangle", img)
cv2.waitKey(0)
画图问题解决,那么我们就可以开始计算长宽了。
直接从rect元组中拿到width和height,例子如下:
rect = cv2.minAreaRect(largest_contour)
width = rect[1][0]
height = rect[1][1]
或者保险起见,这样写:
rect = cv2.minAreaRect(largest_contour)
(w, h) = rect[1]
long_side = max(w, h) # 长边
short_side = min(w, h) # 短边
3.代码实现
是时候展示真正的代码了!
import cv2
import numpy as np
cv2.namedWindow('image')
cap = cv2.VideoCapture(1, cv2.CAP_DSHOW) # 0是默认摄像头,根据自己的摄像头设置
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 轮廓提取
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest_contour = max(contours, key=cv2.contourArea)
# 最小外接矩形测量
rect = cv2.minAreaRect(largest_contour)
(w, h) = rect[1]
long_side = max(w, h) # 长边
short_side = min(w, h) # 短边
# 绘制结果
box = cv2.boxPoints(rect)
cv2.drawContours(frame, [np.intp(box)], -1, (0, 255, 0), 2)
cv2.putText(frame, f"Length: {long_side:.0f}px", (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.putText(frame, f"Width: {short_side:.0f}px", (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
cv2.imshow('image', frame)
key = cv2.waitKey(120)
if key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
最终结果如下:
4.最后
至此,我们已经完成了尺寸测量仪的全部功能。至于如何将像素长度转换为毫米,有多种方式,后续看情况再说明。
接下来,我想再优化一下界面,将尺寸信息显示在更加合适的位置。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)