4、添加标签与置信度:cv2.putText在框上显示类别名称和置信度分数
检测框画好了,但光有框可不行。你想想看,一个框框在那,谁知道它代表什么?是猫是狗还是汽车?
嗯,这就是我们今天要解决的问题——在框上添加标签和置信度分数。说白了,就是让检测结果「开口说话」。
4.1 为什么需要标签和置信度?
我在项目中遇到过这么个事:有一次给客户演示YOLO检测效果,框画得漂漂亮亮的,但客户问了一句:「这个框里是什么?」我当场就尴尬了——框上没文字啊!
从那以后,我养成了一个习惯:只要画框,就必须带上标签和置信度。原因很简单:
- 可读性:一眼就知道检测到了什么
- 可信度:置信度分数告诉你这个检测有多靠谱
- 调试效率:低置信度的框可以快速定位问题
核心原则:标签和置信度不是装饰,是调试信息的一部分。它们能帮你快速判断模型表现。
4.2 cv2.putText 基础用法
OpenCV 里添加文字用的是 cv2.putText()。这个函数参数不少,但常用的就这几个:
cv2.putText(
img, # 图像
text, # 要显示的文字
org, # 文字左下角坐标 (x, y)
fontFace, # 字体类型
fontScale, # 字体大小
color, # 颜色 (B, G, R)
thickness, # 线条粗细
lineType # 线型(可选)
)
我个人习惯用 cv2.FONT_HERSHEY_SIMPLEX 字体,简单清晰,兼容性也好。
4.3 实战:在检测框上添加标签
假设我们已经有了检测结果,每个检测框包含:class_id、confidence、x1、y1、x2、y2。
来看一个完整的示例:
import cv2
import numpy as np
# 类别名称列表(以COCO数据集为例)
class_names = ['person', 'bicycle', 'car', 'motorcycle', 'airplane',
'bus', 'train', 'truck', 'boat', 'traffic light']
def draw_detection(img, class_id, confidence, x1, y1, x2, y2):
"""
在图像上绘制检测框和标签
"""
# 1. 先画检测框
color = (0, 255, 0) # 绿色框
cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)
# 2. 构造标签文本
label = f"{class_names[class_id]}: {confidence:.2f}"
# 3. 计算文字尺寸
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.6
thickness = 2
(text_w, text_h), baseline = cv2.getTextSize(label, font, font_scale, thickness)
# 4. 绘制文字背景(提高可读性)
# 文字放在框的左上角
label_x = x1
label_y = y1 - 10 # 稍微往上偏移
# 如果文字超出图像上边界,就放在框内
if label_y - text_h < 0:
label_y = y1 + text_h + 10
# 画背景矩形
cv2.rectangle(img,
(label_x, label_y - text_h - 5),
(label_x + text_w + 5, label_y + 5),
color, -1) # -1 表示填充
# 5. 写文字(白色文字)
cv2.putText(img, label, (label_x + 2, label_y - 2),
font, font_scale, (255, 255, 255), thickness)
return img
# 模拟一个检测结果
img = np.zeros((480, 640, 3), dtype=np.uint8)
img = draw_detection(img, 0, 0.95, 100, 100, 300, 400)
cv2.imshow('Detection', img)
cv2.waitKey(0)
小技巧:文字背景用填充矩形,可以避免文字和检测框颜色重叠时看不清。白色文字配绿色背景,清晰度最高。
4.4 置信度分数的显示策略
置信度不是随便显示的。我曾经犯过一个错误:把所有检测结果的置信度都显示出来,结果画面上一堆「0.02」「0.03」这种低分框,看着特别乱。
后来我总结了一套策略:
| 置信度范围 | 显示方式 | 颜色建议 |
|---|---|---|
| >= 0.7 | 正常显示,绿色框 | (0, 255, 0) |
| 0.5 ~ 0.7 | 显示,但用黄色框 | (0, 255, 255) |
| 0.3 ~ 0.5 | 显示,红色框,加警告 | (0, 0, 255) |
| < 0.3 | 不显示(过滤掉) | - |
注意:置信度阈值不要设得太低。我一般设 0.5 作为最低显示阈值,低于这个值的框大概率是误检。
4.5 进阶:自适应文字位置
框的位置千变万化,文字放哪最合适?
我常用的策略是:
- 优先放框的左上角:这是最自然的位置
- 如果左上角超出图像边界:放到框的右下角
- 如果框太小:文字放到框外,用连线指示
来看一个自适应位置的实现:
def get_label_position(x1, y1, x2, y2, text_w, text_h, img_h, img_w):
"""
智能选择标签位置
返回 (label_x, label_y) 文字左下角坐标
"""
# 默认放左上角
label_x = x1
label_y = y1 - 10
# 检查是否超出上边界
if label_y - text_h < 0:
# 尝试放右下角
label_x = x2 - text_w - 10
label_y = y2 + text_h + 10
# 如果右下角也超出,放框内顶部
if label_y > img_h:
label_x = x1 + 5
label_y = y1 + text_h + 10
# 确保不超出左右边界
label_x = max(5, min(label_x, img_w - text_w - 10))
return label_x, label_y
4.6 格式化显示的艺术
同样的信息,不同的显示方式,效果天差地别。
我推荐几种格式化方式:
- 简洁版:
person 0.95—— 适合快速调试 - 详细版:
person: 95.2%—— 适合展示给客户 - 多行版:
person\n95.2%—— 适合框比较宽的场景
多行文本的实现稍微麻烦一点,因为 cv2.putText 不支持换行符。需要手动分行绘制:
def draw_multiline_text(img, lines, x, y, font, scale, color, thickness):
"""
绘制多行文本
"""
line_height = cv2.getTextSize("A", font, scale, thickness)[0][1] + 5
for i, line in enumerate(lines):
y_pos = y + i * line_height
cv2.putText(img, line, (x, y_pos), font, scale, color, thickness)
4.7 性能优化建议
如果你是在视频流上做实时检测,文字绘制也可能成为瓶颈。我踩过这个坑:
- 避免频繁调用 getTextSize:可以预先计算好常用文字的尺寸
- 减少文字绘制次数:只绘制置信度高于阈值的框
- 使用固定字体大小:不要根据框大小动态调整字体,那样计算量太大
我的经验:在实时视频流中,文字绘制占用的时间不要超过总处理时间的 5%。如果发现卡顿,优先检查是不是文字绘制太频繁了。
4.8 完整示例:批量绘制检测结果
最后,给一个实际项目中常用的批量绘制函数:
def draw_detections(img, detections, class_names, conf_threshold=0.5):
"""
批量绘制检测结果
detections: [(class_id, confidence, x1, y1, x2, y2), ...]
"""
for det in detections:
class_id, confidence, x1, y1, x2, y2 = det
# 过滤低置信度
if confidence < conf_threshold:
continue
# 根据置信度选择颜色
if confidence >= 0.7:
color = (0, 255, 0) # 绿色
elif confidence >= 0.5:
color = (0, 255, 255) # 黄色
else:
color = (0, 0, 255) # 红色
# 绘制框和标签
img = draw_detection(img, class_id, confidence,
x1, y1, x2, y2, class_names, color)
return img
嗯,到这里你应该已经掌握了如何在检测框上添加标签和置信度。记住,好的可视化调试工具,能让你的模型调优效率翻倍。
下一章,我们会聊聊如何用不同颜色区分不同类别,让检测结果更加一目了然。