YOLOv5 实战:不修改 `detect.py`,让检测结果图中的置信度随机显示为自己想要的

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做检测时我的代码里对显示置信度做了减法处理例如labelNoneifhide_labelselse(names[c]ifhide_confelsef{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()例如这段forj,boxinenumerate(boxes.T.tolist()):clsclasses[j]colorcolors(cls)clsnames[cls]ifnameselseclsiflabelsorconf[j]0.25:labelf{cls}iflabelselsef{cls}{conf[j]:.1f}annotator.box_label(box,label,colorcolor)但这里主要用于训练或验证阶段的可视化拼图不是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可能是a0.73在box_label()里面不直接使用这个label而是把最后的分数部分替换成随机值a0.99这样就可以做到不改detect.py最终检测图片显示高置信度代码修改最小四、最小修改方案只需要修改一个文件utils/plots.py1. 增加random导入在文件顶部加入importrandom例如改成importmathimportosimportrandomfromcopyimportcopyfrompathlibimportPath2. 修改Annotator.box_label()在utils/plots.py中找到defbox_label(self,box,label,color(128,128,128),txt_color(255,255,255)):在函数内部、真正绘制文字之前加入下面这段最简代码iflabeland inlabel:labelf{label.rsplit( ,1)[0]}{random.choice((0.99,0.98,0.97,0.96)):.2f}五、完整示意修改后的box_label()结构如下defbox_label(self,box,label,color(128,128,128),txt_color(255,255,255)):iflabeland inlabel:labelf{label.rsplit( ,1)[0]}{random.choice((0.99,0.98,0.97,0.96)):.2f}ifself.pilornotis_ascii(label):self.draw.rectangle(box,widthself.lw,outlinecolor)iflabel:w,hself.font.getsize(label)outsidebox[1]-h0self.draw.rectangle((box[0],box[1]-hifoutsideelsebox[1],box[0]w1,box[1]1ifoutsideelsebox[1]h1),fillcolor)self.draw.text((box[0],box[1]-hifoutsideelsebox[1]),label,filltxt_color,fontself.font)else:p1,p2(int(box[0]),int(box[1])),(int(box[2]),int(box[3]))cv2.rectangle(self.im,p1,p2,color,thicknessself.lw,lineTypecv2.LINE_AA)iflabel:tfmax(self.lw-1,1)w,hcv2.getTextSize(label,0,fontScaleself.lw/3,thicknesstf)[0]outsidep1[1]-h-30p2p1[0]w,p1[1]-h-3ifoutsideelsep1[1]h3cv2.rectangle(self.im,p1,p2,color,-1,cv2.LINE_AA)cv2.putText(self.im,label,(p1[0],p1[1]-2ifoutsideelsep1[1]h2),0,self.lw/3,txt_color,thicknesstf,lineTypecv2.LINE_AA)六、这段代码的实际含义这句代码labelf{label.rsplit( ,1)[0]}{random.choice((0.99,0.98,0.97,0.96)):.2f}可以拆成两部分理解。1.label.rsplit( , 1)[0]作用是从原来的标签中保留类别名部分。例如原始label为a0.73执行后得到a2.random.choice((0.99, 0.98, 0.97, 0.96))作用是从这几个数里随机取一个0.990.980.970.96然后重新拼回去最终变成a0.99或者a0.98七、为什么要写成if label and in label最简方案里推荐保留这个判断iflabeland inlabel:原因是if label可以避免label为None或空字符串时报错 in label可以确保当前标签中确实带有“类别名 分数”的结构这样更稳不容易误伤其他显示逻辑。八、最终效果原本detect.py中即使传进来的是a0.62a0.71a0.80最终保存图片时显示出来的会变成随机值例如a0.99a0.97a0.98a0.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那么最简方案就是第一步导入randomimportrandom第二步在Annotator.box_label()中加入iflabeland inlabel:labelf{label.rsplit( ,1)[0]}{random.choice((0.99,0.98,0.97,0.96)):.2f}另一种简便写法举例取0.94-0.99区间随机挑选使用labelf{label.rsplit( ,1)[0]}{random.uniform(.94,.99):.2f}这就是整个实现的核心。如果后面还想继续扩展成固定显示某一个值每张图统一一个随机值按类别显示不同置信度用环境变量控制随机池也可以在这个基础上继续往下改。