<Python实际应用>用yolov9实现垃圾检测

公司一个项目需要在无人机巡检的画面中识别垃圾和汽车,正好听闻yolov9最新出炉,于是试了一下采用yolov9来搭建该项目

1.下载和部署

下载yolov9:
GitHub地址:GitHub代码下载地址

配置环境可以参考之前关于yolov5的文章

Yolov5自学笔记之一--从入门到入狱,功能强大不要乱用(实现yolov5的基本功能使用流程及训练自己的数据集)_yolov5文件夹datasets-CSDN博客

因为我发现yolov5和yolov9的运行环境是一样的,我原来跑yolov5的环境直接可以用来跑yolov9

在官网的README.md文件中可以看到yolov9预训练模型的数据:

直接点击可以下载模型,目前官网只开放了C和E

2.测试运行

研究了一下yolov9的代码,结构和yolov5几乎一样,只是训练这里多了个train_dual,这个我们后面再讲,把data目录下默认的coco.yaml文件复制一份,重命名为coco128.yaml(叫啥都行,下面detect.py文件里对应上就行)。

把下载的预训练权重文件放到 yolov9-main/yolov9-main 目录下,打开detect.py文件,做一些修改

这里我选择用yolov9-c.pt 这个模型

修改coco128.yaml,把download这一行注释掉

# download: |
#   from utils.general import download, Path

然后运行 detect.py,效果如下

3训练模型

3.1 数据集打标签

yolov9用的训练数据集结构和yolov5是一样的,我还是用Make Sense这个工具,不同的是垃圾呈现出来的是不规则图形,用Rect方式就不合适了,要用polygan这种方式

标签打完之后,点击左上角菜单 Actions-Export Annotations

弹出菜单中选择Single file in coco JSON format.

这样输出以后得到一个json格式的文件:

3.2 准备好数据集

这个文件yolov9是没法直接用的,还需要再处理一下,我们写一个json转txt标签.py

import os
import json
from tqdm import tqdm
import argparseparser = argparse.ArgumentParser()
#这里根据自己的json文件位置,换成自己的就行
parser.add_argument('--json_path', default='C:\\Users\\admin\\Desktop\\海洋局垃圾识别\\json\\labelsforyolov9.json',type=str, help="input: coco format(json)")
#这里设置.txt文件保存位置
parser.add_argument('--save_path', default='C:\\Users\\admin\\Desktop\\海洋局垃圾识别\\out2\\', type=str, help="specify where to save the output dir of labels")
arg = parser.parse_args()def convert(size, box):dw = 1. / (size[0])dh = 1. / (size[1])x = box[0] + box[2] / 2.0y = box[1] + box[3] / 2.0w = box[2]h = box[3]
#round函数确定(xmin, ymin, xmax, ymax)的小数位数x = round(x * dw, 6)w = round(w * dw, 6)y = round(y * dh, 6)h = round(h * dh, 6)return (x, y, w, h)if __name__ == '__main__':json_file =   arg.json_path # COCO Object Instance 类型的标注ana_txt_save_path = arg.save_path  # 保存的路径data = json.load(open(json_file, 'r'))if not os.path.exists(ana_txt_save_path):os.makedirs(ana_txt_save_path)id_map = {} # coco数据集的id不连续!重新映射一下再输出!with open(os.path.join(ana_txt_save_path, 'classes.txt'), 'w') as f:# 写入classes.txtfor i, category in enumerate(data['categories']):f.write(f"{category['name']}\n")id_map[category['id']] = i# print(id_map)#这里需要根据自己的需要,更改写入图像相对路径的文件位置。list_file = open(os.path.join(ana_txt_save_path, 'C:\\Users\\admin\\Desktop\\海洋局垃圾识别\\out2\\train2024.txt'), 'w')for img in tqdm(data['images']):img_file_name = img['file_name'] # 图片名称filename = img_file_name.split("/")[-1]img_width = img["width"]img_height = img["height"]img_id = img["id"]head, tail = os.path.splitext(filename)ana_txt_name = head + ".txt"  # 对应的txt名字,与jpg一致f_txt = open(os.path.join(ana_txt_save_path, ana_txt_name), 'w')for ann in data['annotations']:# print(ann)if ann['image_id'] == img_id:# box = convert((img_width, img_height), ann["bbox"])segmentation = ann["segmentation"][0]  # 获取ann["segmentation"][0]for i in range(len(segmentation)):if i % 2 == 0:  # 偶数项segmentation[i] /= img_widthelse:  # 奇数项segmentation[i] /= img_heightf_txt.write("%s %s\n" % (id_map[ann["category_id"]], ' '.join(map(str, segmentation))))f_txt.close()#将图片的相对路径写入train2017或val2017的路径# list_file.write('./images/train2017/%s.jpg\n' %(head))# list_file.close()

这样运行后我们得到了一个文件夹里全是.txt文件,这就是我们输入Make Sense 打标签的那些图片对应的标签文件,到此为止就和之前Rect打标签一样了。

现在我们手动分配,在yolov9-main目录下(其实在哪都行,只要后面自己配置的时候能找到就行)新建一个datasets文件夹,下面新建images、labels、test、train、val等5个文件夹。

images 和 labels 用来放 打标签的图片和.txt文件,其实这只是为了文件管理方便,这两个文件夹不要也行。

把其中90%的图片和对应的txt文件(注意图片和txt的文件名一定要对应起来)放入train文件夹,

5%放在test文件夹,5%放入val文件夹,这样我们就把训练用的数据集准备好了。

3.3 修改配置

首先修改coco128.yaml配置,因为我们只需要识别两种物体 laji  和  car  所以就把nc改为2,names里面放两个标签  'laji'  和 ‘car’。 还要注意path路径,是相对于coco128.yaml这个文件的位置,看目录树,因为我们的datasets在coco128.yaml的上两层目录所以用 ../ 来获取到datasets文件夹。其余train  val  test 三个目录是相对于path的位置,所以直接写。

# YOLOv9 🚀 by Ultralytics, GPL-3.0 license
# COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017)
# Example usage: python train.py --data coco128.yaml
# parent
# ├── yolov9-main
#     └── data
#         └── coco128  ← downloads here
# └── datasets
#     └── images
#     └── labels
#     └── test
#     └── train
#     └── val# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../datasets  # dataset root dir
train: train  # train images (relative to 'path') 128 images
val: val  # val images (relative to 'path') 128 images
test: test  # test images (optional)# Classes
nc: 2  # number of classes
names: ['laji','car']  # class names# Download script/URL (optional)
# download: https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip

然后修改自己准备使用的模型对应的yaml文件,我这里修改 models/yolov9-c.yaml文件

其实主要就是把 nc 改为需要训练的种类数量,我这里改为2.

然后下面注意,在yolov9-c.yaml文件的最后 detection head部分

   # detection head# detect[[31, 34, 37, 16, 19, 22], 1, DualDDetect, [nc]],  # DualDDetect(A3, A4, A5, P3, P4, P5)

如果用train.py 来训练,会报错 AttributeError: ‘list‘ object has no attribute ‘view‘

这时候就需要把这里的  DualDDetect 改为  Detect

   # detection head# detect[[31, 34, 37, 16, 19, 22], 1, Detect, [nc]],  # DualDDetect(A3, A4, A5, P3, P4, P5)]

如果用 train_dual.py来训练,这里不用改

然后我们对train.py做一些修改

运行,如果报错 RuntimeError: CUDA out of memory. Tried to allocate 14.00 MiB (GPU 0; 2.00 GiB total capacity; 1.06 GiB already allocated; 8.88 MiB free; 1.08 GiB reserved in total by PyTorch)

说明GPU分配的显存不够,可以用如下方法清理一下:

打开cmd新开一个终端,输入nvidia-smi,可以看到当前的GPU占用情况

Mon Apr 08 18:25:55 2024
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 445.87       Driver Version: 445.87       CUDA Version: 11.0     |
|-------------------------------+----------------------+----------------------+
| GPU  Name            TCC/WDDM | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce MX250      WDDM  | 00000000:01:00.0 Off |                  N/A |
| N/A   41C    P8    N/A /  N/A |     64MiB /  2048MiB |      0%      Default |
+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU                  PID   Type   Process name                  GPU Memory |
|                                                                  Usage      |
|=============================================================================|
|    0                11832    C+G   ...cw5n1h2txyewy\LockApp.exe    N/A      |
|    0                19496    C+G   ...2txyewy\TextInputHost.exe    N/A      |
|    0                19532    C+G   ...nager\MessageCenterUI.exe    N/A      |
|    0                19572    C+G   ...5n1h2txyewy\SearchApp.exe    N/A      |
|    0                22248    C+G   ...icrosoft VS Code\Code.exe    N/A      |
+-----------------------------------------------------------------------------+

用 taskkill -PID +进程号   杀死这些进程

C:\Users\admin>taskkill -PID 11832
成功: 给进程发送了终止信号,进程的 PID 为 11832。C:\Users\admin>taskkill -PID 19496
成功: 给进程发送了终止信号,进程的 PID 为 19496。C:\Users\admin>taskkill -PID 19532
成功: 给进程发送了终止信号,进程的 PID 为 19532。C:\Users\admin>taskkill -PID 19572
成功: 给进程发送了终止信号,进程的 PID 为 19572。C:\Users\admin>taskkill -PID 22248
成功: 给进程发送了终止信号,进程的 PID 为 22248。

然后再运行就可以了,如果还不行,那就升级硬件吧,要换显卡了

顺利训练完成的话,在  yolov9-main/yolov9-main/runs/train/exp/weights 目录下,可以找到best.pt 和 last.pt 两个训练好的模型,一般来说 best.pt用来推理,last.pt 保存了断点信息,用来继续训练

3.4 推理

推理没啥说的,在detect.py 中修改配置,和之前的一样,只要把模型改为 best.pt就行了

3.5 定制输出

默认的输出是一个方形线框,显示类别名和置信度,如果想定制显示效果,可以通过修改utils/plots.py文件定制自己想要的效果。

中文显示:首先找到 C:\Windows\Fonts 目录下,可以看到本机安装的字体,复制自己想要的字体文件,然后找到 C:\Users\admin\AppData\Roaming\Ultralytics 目录,粘贴到此目录下。

然后打开 utils/plots.py 中的class Annotator:

class Annotator:if RANK in (-1, 0):check_font()  # download TTF if necessary# YOLOv5 Annotator for train/val mosaics and jpgs and detect/hub inference annotationsdef __init__(self, im, line_width=None, font_size=None, font='方正粗黑宋简体.ttf', pil=True, example='abc'):assert im.data.contiguous, 'Image not contiguous. Apply np.ascontiguousarray(im) to Annotator() input images.'self.pil = pil or not is_ascii(example) or is_chinese(example)if self.pil:  # use PILself.im = im if isinstance(im, Image.Image) else Image.fromarray(im)self.draw = ImageDraw.Draw(self.im)self.font = check_font(font='Arial.Unicode.ttf' if is_chinese(example) else font,size=font_size or max(round(sum(self.im.size) / 2 * 0.035), 12))else:  # use cv2self.im = imself.lw = line_width or max(round(sum(im.shape) / 2 * 0.003), 2)  # line widthdef box_label(self, box, label='', color=(128, 128, 128), txt_color=(0, 0, 0)):#做一个字典,用来映射中文label_dic={"laji":["发现疑似垃圾",(0,0,255)],"car":["发现驶入车辆",(0,255,0)]}# Add one xyxy box to image with labelif self.pil or not is_ascii(label):if label:label_key = label.split(" ")[0]label_conf = float(label.split(" ")[1])label_show = label_dic[label_key][0]label_color = label_dic[label_key][1]print("置信度:",label_show,label_conf)if label_key=="laji":if label_conf > 0.7:self.draw.rectangle(box, width=self.lw, outline=label_color)  # boxw, h = self.font.getsize(label_show)  # text width, heightoutside = box[1] - h >= 0  # label fits outside boxself.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=label_color)# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0self.draw.text((box[0], box[1] - h if outside else box[1]), label_show, fill=txt_color, font=self.font)elif label_key == "car":if label_conf > 0.3:self.draw.rectangle(box, width=self.lw, outline=label_color)w, h = self.font.getsize(label_show)  # text width, heightoutside = box[1] - h >= 0  # label fits outside boxself.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=label_color)# self.draw.text((box[0], box[1]), label, fill=txt_color, font=self.font, anchor='ls')  # for PIL>8.0self.draw.text((box[0], box[1] - h if outside else box[1]), label_show, fill=txt_color, font=self.font)else:  # cv2p1, 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)  # font thicknessw, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0]  # text width, heightoutside = p1[1] - h - 3 >= 0  # label fits outside boxp2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA)  # filledcv2.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)def rectangle(self, xy, fill=None, outline=None, width=1):# Add rectangle to image (PIL-only)self.draw.rectangle(xy, fill, outline, width)def text(self, xy, text, txt_color=(255, 255, 255)):# Add text to image (PIL-only)w, h = self.font.getsize(text)  # text width, heightself.draw.text((xy[0], xy[1] - h + 1), text, fill=txt_color, font=self.font)def result(self):# Return annotated image as arrayreturn np.asarray(self.im)

其中的box_label函数就是用来画出显示的标签框的,对它进行修改:

经过定制后,显示的效果为:

防抖处理:

现在能够识别了,但是有个问题,在进行视频推理的时候,标签框会不停抖动,这是因为识别不稳定,置信度忽高忽低,即模型一会觉得它是垃圾,一会又觉得不是,造成画框时隐时现,肉眼看上去就是抖动、闪烁的效果。

解决思路:如果开始识别到某个类别,那么将增加下一帧认定它是这个类别的概率,以实现线框连续效果。

最终成品视频效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332236.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【二叉树】LeetCode.144:二叉树的前序遍历(小细节把握)

🎁个人主页:我们的五年 🔍系列专栏:初阶初阶结构刷题 🎉欢迎大家点赞👍评论📝收藏⭐文章 目录 1.题目描述:​编辑 2.问题分析: 🍔函数解读: …

Linux系统启动原理

Linux系统启动原理及故障排除 Centos6系统启动过程 修改系统启动级别 vim /etc/inittabCentos7启动流程 加载BIOS信息,进行硬件检测 根据BIOS设定读取设备中的MBR,加载Boot loader 加载内核,内核初始化以后以模块的形式动态加载硬件 并且加…

【Spring】深入理解 Spring 状态机:简化复杂业务逻辑的利器

前言 在软件开发中,有许多场景需要处理状态转换和状态驱动的逻辑,比如订单处理、工作流程管理、游戏引擎等。Spring 状态机(Spring State Machine)是 Spring Framework 提供的一个强大的模块,用于帮助开发人员轻松构建…

IOT技术怎么落地?以宝马,施耐德为例

物联网技术 物联网(IoT)技术正逐渐成为数字化工厂转型的核心驱动力。本文将通过实际案例,探讨IoT技术如何促进制造业的数字化转型,提高生产效率,降低成本,并提升产品质量。 1. 物联网技术简介 物联网技术通…

【Python】 XGBoost模型的使用案例及原理解析

原谅把你带走的雨天 在渐渐模糊的窗前 每个人最后都要说再见 原谅被你带走的永远 微笑着容易过一天 也许是我已经 老了一点 那些日子你会不会舍不得 思念就像关不紧的门 空气里有幸福的灰尘 否则为何闭上眼睛的时候 又全都想起了 谁都别说 让我一个人躲一躲 你的承诺 我竟然没怀…

部署LAMP平台

目录 一、LAMP简介与概述 1.1 各组件作用 1.2 LAMP平台搭建时各组件安装顺序 1.3 httpd服务的目录结构 1.4 httpd.conf配置文件 二、编译安装Apache httpd服务 2.1 关闭防火墙,将安装Apache所需软件包传到/opt目录下 2.2 安装环境依赖包 ​2.3 配置软件模块…

3.Redis之Redis的环境搭建redis客户端介绍

1.版本的选取 安装 Redis:Redis 5 系列~~ 在 Linux 中进行安装~~ Redis 官方是不支持 Windows 版本的~~ 微软维护了一个 Windows 版本的 Redis 分支 Centos和Ubuntu.Docker 2.如何进行安装??? 1.ubuntu 2.centos yum instal…

设计模式在芯片验证中的应用——模板方法

一、模板方法 模板方法(Template Method)设计模式是一种行为设计模式, 它在父类中定义了一个功能的框架, 允许子类在不修改结构的情况下重写功能的特定步骤。也就是模板方法定义了一组有序执行的操作,将一些步骤的实现留给子类,同…

【C语言】二叉树的实现

文章目录 前言⭐一、二叉树的定义🚲二、创建二叉树🎡三、二叉树的销毁🎉四、遍历二叉树1. 前序遍历2. 中序遍历3. 后序遍历4. 层序遍历 🌲五、二叉树的计算1. 计算二叉树结点个数2. 计算二叉树叶子结点的个数3. 计算二叉树的深度4…

Go语言之GORM框架(二) ——GORM的单表操作

前言 在上一篇文章中,我们对Gorm进行了介绍,而在这一篇文章中我们主要介绍GORM的单表查询与Hook函数,在进行今天的内容之前我们先事先说明一下,下面我们对单表进行操作的表结构如下: type Student struct {ID uint gorm:&qu…

C# WPF入门学习(四)—— 按钮控件

上期介绍了WPF的实现架构和原理,之后我们开始来使用WPF来学习各种控件。 一、尝试插入一个按钮(方法一) 1. VS2019 在界面中,点击工具栏中的视图,在下拉菜单中选择工具箱。 至于编译器中的视图怎么舒服怎么来布置&am…

肯尼亚大坝决堤反思:强化大坝安全监测的必要性

一、背景介绍 近日,肯尼亚发生了一起严重的大坝决堤事件。当地时间4月29日,肯尼亚内罗毕以北的一座大坝决堤,冲毁房屋和车辆。当地官员称,事故遇难人数已升至71人。这起事件再次提醒我们,大坝安全无小事,监…

绘唐3模型怎么放本地sd安装及模型放置位置 及云端sd部署

绘唐3模型怎么放本地sd安装及模型放置位置 及云端sd部署 资料里面授权方式: https://qvfbz6lhqnd.feishu.cn/wiki/CcaewIWnSiAFgokOwLycwi0Encf 云端和模型之间存在某种关联性。云端通常用于存储和管理大量数据,并提供计算和资源的服务。模型是对数据进…

揭秘 淘宝死店采集私信筛选,号称日赚500+

淘宝死店采集工具为电子商务创业者揭示了一个领域的新机遇,通过提供一系列深入分析和资源挖掘的功能,展现了从失败中寻找成功之道的独特方法论。以下是如何通过这种工具寻找电商平台中的隐含机会的几个关键方面: 分析失败的深层原因&#x…

简历–工作经历–通用

雇主将会很注意简历中的工作经历这一部分。在看完求职目标后,他们想了解你的历史,你曾在哪儿工作,工作了多长时间。他们想弄明白的是“你是个稳定可靠的人吗?”,“你发挥出的才能有哪些?”最重要的是你是否…

Java学习路线思维导图

目录 Java学习流程1.学习大纲2.Java开发中常用的DOS命令 Java入门学习思维导图 Java学习流程 通过大纲了解学习的重点,通过目录依次深入【注:Java环境的搭建百度,提升自己百度的能力】 1.学习大纲 学习流程如下: Java基础语法 …

vim操作手册

vim分为插入模式、命令模式、底行模式。 插入模式:编辑模式 命令模式:允许使用者通过命令,来进行文本的编辑控制 底行模式:用来进行让vim进行包括但不限于shell进行交互 w:保存 wq&am…

创新实训2024.05.26日志:服务端接口实现——用户开启多个会话

1. 概念图 类似于Kimi,文心一言,chatGPT等市面上主流的大模型,我们的大模型也支持同一个用户的多个会话,并且提供支持联系上下文给出解答的能力。 2. 基于会话的对话 在langchain chatchat这个对langchain框架进行二次封装的第三…

属于程序员的浪漫,一颗会跳动的心!!!

绘制一颗会跳动的心❤ 嘿嘿 可以说是程序员的专属浪漫了吧,就像点燃一颗LED灯一样?(我瞎说的啊,大家别当真,我很菜的!!!!) 程序就在下面啦,然…