YOLOv5 实战:不修改 `detect.py`,让检测结果图中的置信度随机显示为自己想要的
YOLOv5 实战:不修改 detect.py,让检测结果图中的置信度随机显示为自己想要的
摘要
这篇文章记录一个比较实用的小技巧:仅使用部分场景下使用,并非真实修改模型,仅对绘制出的置信度显示进行修改,仅此而已,在 不修改 detect.py 的前提下,直接通过修改 utils/plots.py 中的显示逻辑,让 YOLOv5 在 detect.py 输出结果图时,把框上的置信度随机显示为 0.99、0.98、0.97、0.96 中的一个,即使detect.py中将conf数值减小也不影响该方式的显示。
这个方法的特点很明确:
- 不动
detect.py - 修改量非常小
- 只影响图片上的显示结果
- 不改变模型真实输出的置信度
如果你也遇到类似需求,可以直接参考本文的最小修改方案。
一、问题背景
在使用 YOLOv5 的 detect.py 做检测时,我的代码里对显示置信度做了减法处理,例如:
label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf - 0.15:.2f}')
这样最终保存出来的检测图片中,框上的置信度会比原始值少 0.15。
现在我想实现这样一个效果:
- 不修改
detect.py - 继续使用
detect.py进行检测 - 最终图片上的置信度不再显示
conf - 0.15 - 而是随机显示为
0.99、0.98、0.97、0.96中的一个
二、为什么改 plot_images() 没有效果
很多人第一反应会去改 utils/plots.py 里的 plot_images(),例如这段:
for j, box in enumerate(boxes.T.tolist()):
cls = classes[j]
color = colors(cls)
cls = names[cls] if names else cls
if labels or conf[j] > 0.25:
label = f'{cls}' if labels else f'{cls} {conf[j]:.1f}'
annotator.box_label(box, label, color=color)
但这里主要用于训练或验证阶段的可视化拼图,不是 detect.py 最终保存检测图时走的主流程。
detect.py 真正走的是这条链路:
detect.py中先拼接好label- 调用
annotator.box_label(...) - 再由
utils/plots.py中的Annotator.box_label()把文字真正画到图上
所以,如果想在 不改 detect.py 的前提下改变最终显示结果,最直接的方法就是:
拦截 Annotator.box_label() 中的 label,在真正绘制前重写它。
三、最简实现思路
核心思路非常简单:
detect.py传进来的label可能是:
a 0.73
- 在
box_label()里面,不直接使用这个label - 而是把最后的分数部分替换成随机值:
a 0.99
这样就可以做到:
- 不改
detect.py - 最终检测图片显示高置信度
- 代码修改最小
四、最小修改方案
只需要修改一个文件:
utils/plots.py
1. 增加 random 导入
在文件顶部加入:
import random
例如改成:
import math
import os
import random
from copy import copy
from pathlib import Path
2. 修改 Annotator.box_label()
在 utils/plots.py 中找到:
def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
在函数内部、真正绘制文字之前,加入下面这段最简代码:
if label and ' ' in label:
label = f"{label.rsplit(' ', 1)[0]} {random.choice((0.99, 0.98, 0.97, 0.96)):.2f}"
五、完整示意
修改后的 box_label() 结构如下:
def box_label(self, box, label='', color=(128, 128, 128), txt_color=(255, 255, 255)):
if label and ' ' in label:
label = f"{label.rsplit(' ', 1)[0]} {random.choice((0.99, 0.98, 0.97, 0.96)):.2f}"
if self.pil or not is_ascii(label):
self.draw.rectangle(box, width=self.lw, outline=color)
if label:
w, h = self.font.getsize(label)
outside = box[1] - h >= 0
self.draw.rectangle((box[0],
box[1] - h if outside else box[1],
box[0] + w + 1,
box[1] + 1 if outside else box[1] + h + 1), fill=color)
self.draw.text((box[0], box[1] - h if outside else box[1]), label, fill=txt_color, font=self.font)
else:
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA)
if label:
tf = max(self.lw - 1, 1)
w, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0]
outside = p1[1] - h - 3 >= 0
p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3
cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA)
cv2.putText(self.im, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2),
0, self.lw / 3, txt_color, thickness=tf, lineType=cv2.LINE_AA)
六、这段代码的实际含义
这句代码:
label = f"{label.rsplit(' ', 1)[0]} {random.choice((0.99, 0.98, 0.97, 0.96)):.2f}"
可以拆成两部分理解。
1. label.rsplit(' ', 1)[0]
作用是从原来的标签中,保留类别名部分。
例如原始 label 为:
a 0.73
执行后得到:
a
2. random.choice((0.99, 0.98, 0.97, 0.96))
作用是从这几个数里随机取一个:
0.990.980.970.96
然后重新拼回去,最终变成:
a 0.99
或者:
a 0.98
七、为什么要写成 if label and ' ' in label
最简方案里推荐保留这个判断:
if label and ' ' in label:
原因是:
if label可以避免label为None或空字符串时报错' ' in label可以确保当前标签中确实带有“类别名 + 分数”的结构
这样更稳,不容易误伤其他显示逻辑。
八、最终效果
原本 detect.py 中即使传进来的是:
a 0.62
a 0.71
a 0.80
最终保存图片时,显示出来的会变成随机值,例如:
a 0.99
a 0.97
a 0.98
a 0.96
注意,这里改的是:
图片上的显示结果
并不是:
模型真实输出的置信度
九、注意事项
1. 这只是改显示,不改模型真实分数
这个方法不会提升模型本身的检测能力,也不会改变网络真实输出。
它只是在最终画框时,把显示的分数替换掉。
2. 不影响 detect.py 文件本身
这个方案最大的优点就是:
detect.py完全不动- 修改量非常小
- 直接在显示层拦截
3. 如果保存了原始 txt,里面通常还是原始置信度
如果同时使用了 --save-txt --save-conf,保存下来的 txt 一般还是模型原始输出,不会因为这个显示逻辑而改变。
也就是说:
- 图片显示是随机高分
- txt 里的真实值通常还是原始值
十、总结
如果想实现:
- 不修改
detect.py - 继续使用
detect.py检测 - 最终图片中的置信度随机显示为
0.99/0.98/0.97/0.96
那么最简方案就是:
第一步:导入 random
import random
第二步:在 Annotator.box_label() 中加入
if label and ' ' in label:
label = f"{label.rsplit(' ', 1)[0]} {random.choice((0.99, 0.98, 0.97, 0.96)):.2f}"
另一种简便写法
举例取0.94-0.99区间随机挑选使用:
label = f"{label.rsplit(' ',1)[0]} {random.uniform(.94,.99):.2f}"
这就是整个实现的核心。
如果后面还想继续扩展成:
- 固定显示某一个值
- 每张图统一一个随机值
- 按类别显示不同置信度
- 用环境变量控制随机池
也可以在这个基础上继续往下改。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)