爸,这下你还敢抽烟么?
AIStudio地址
Github地址
不抽煙,也沒有煙,所以只能以這種形式代替了
爸,這下你還敢抽煙嗎?/邪笑
我爸:這鍋我不背!!
項目背景
??2014年11月24日,衛(wèi)生計生委起草了《公共場所控制吸煙條例(送審稿)》向社會公開征求意見。送審稿明確,所有室內(nèi)公共場所一律禁止吸煙。此外,體育、健身場館的室外觀眾坐席、賽場區(qū)域;公共交通工具的室外等候區(qū)域等也全面禁止吸煙。而《公共場所控制吸煙條例》實施后,衛(wèi)計委將考慮為控?zé)煑l例設(shè)置舉報電話,同時開展監(jiān)測評估,鼓勵全社會參與戒煙活動。??但有很多人秉持“多一事不如少一事”的原則:“欸,有點(diǎn)電話就不打,我就忍著!”,導(dǎo)致社會公共場所禁煙效果普遍不佳。
??因而“公共場所,禁止吸煙”的標(biāo)牌,也漸漸淪為了擺設(shè)。
項目介紹
??本項目基于PaddleX進(jìn)行模型訓(xùn)練,并利用inference導(dǎo)出后部署在本地,并使用百度大腦合成后進(jìn)行“公共場所,禁止吸煙”的語音播報。首先導(dǎo)入使用PaddleX時所需的模塊
!pip install paddlex !pip install paddle2onnx # 進(jìn)行數(shù)據(jù)集解壓 !unzip -oq /home/aistudio/data/data86368/smoke.zip -d /home/aistudio/dataset2??下面這個check.py是我在開發(fā)過程中遇到點(diǎn)小問題在PaddleX的Github issue中找到的,后面我們會提到這個腳本的功能。
(深刻啟示:有問題就去逛issue!!!要相信你現(xiàn)在所遇到的絕大多數(shù)問題已經(jīng)有“先烈”替你趟過了!即使沒有也可以提issue!飛槳的開發(fā)小哥哥們回復(fù)也是炒雞快!!!!)
數(shù)據(jù)處理和數(shù)據(jù)清洗
??在我們解壓好數(shù)據(jù)集后,我們可以看到數(shù)據(jù)集中給了images和Annotations兩個文件夾,分別存放著.jpg和.xml文件。但其實這是不符合PaddleX在目標(biāo)檢測中的數(shù)據(jù)格式要求的。并且,我們打開他的.xml文件可以發(fā)現(xiàn),它的filename、name、path等內(nèi)容均需要我們后期進(jìn)行處理和更改。??當(dāng)然,在本項目中的數(shù)據(jù)處理方式有些許笨拙和低劣。
# 這里修改.xml文件中的<path>元素 !mkdir dataset/Annotations1 import xml.dom.minidom import ospath = r'dataset/Annotations' # xml文件存放路徑 sv_path = r'dataset/Annotations1' # 修改后的xml文件存放路徑 files = os.listdir(path) cnt = 1for xmlFile in files:dom = xml.dom.minidom.parse(os.path.join(path, xmlFile)) # 打開xml文件,送到dom解析root = dom.documentElement # 得到文檔元素對象item = root.getElementsByTagName('path') # 獲取path這一node名字及相關(guān)屬性值for i in item:i.firstChild.data = '/home/aistudio/dataset/JPEGImages/' + str(cnt).zfill(6) + '.jpg' # xml文件對應(yīng)的圖片路徑with open(os.path.join(sv_path, xmlFile), 'w') as fh:dom.writexml(fh)cnt += 1 # 這里修改.xml文件中的<failname>元素 !mkdir dataset/Annotations2 import xml.dom.minidom import ospath = r'dataset/Annotations1' # xml文件存放路徑 sv_path = r'dataset/Annotations2' # 修改后的xml文件存放路徑 files = os.listdir(path)for xmlFile in files:dom = xml.dom.minidom.parse(os.path.join(path, xmlFile)) # 打開xml文件,送到dom解析root = dom.documentElement # 得到文檔元素對象names = root.getElementsByTagName('filename')a, b = os.path.splitext(xmlFile) # 分離出文件名afor n in names:n.firstChild.data = a + '.jpg'with open(os.path.join(sv_path, xmlFile), 'w') as fh:dom.writexml(fh) # 這里修改.xml文件中的<name>元素 !mkdir dataset/Annotations3 #!/usr/bin/env python2 # -*- coding: utf-8 -*-import os import xml.etree.ElementTree as ETorigin_ann_dir = '/home/aistudio/dataset/Annotations2/'# 設(shè)置原始標(biāo)簽路徑為 Annos new_ann_dir = '/home/aistudio/dataset/Annotations3/'# 設(shè)置新標(biāo)簽路徑 Annotations for dirpaths, dirnames, filenames in os.walk(origin_ann_dir): # os.walk游走遍歷目錄名for filename in filenames:print("process...")# if os.path.isfile(r'%s%s' %(origin_ann_dir, filename)): # 獲取原始xml文件絕對路徑,isfile()檢測是否為文件 isdir檢測是否為目錄origin_ann_path = os.path.join(origin_ann_dir, filename) # 如果是,獲取絕對路徑(重復(fù)代碼)new_ann_path = os.path.join(new_ann_dir, filename)tree = ET.parse(origin_ann_path) # ET是一個xml文件解析庫,ET.parse()打開xml文件。parse--"解析"root = tree.getroot() # 獲取根節(jié)點(diǎn)for object in root.findall('object'): # 找到根節(jié)點(diǎn)下所有“object”節(jié)點(diǎn)name = str(object.find('name').text) # 找到object節(jié)點(diǎn)下name子節(jié)點(diǎn)的值(字符串)# 如果name等于str,則刪除該節(jié)點(diǎn)if (name in ["smoke"]):# root.remove(object)pass# 如果name等于str,則修改nameelse:object.find('name').text = "smoke"tree.write(new_ann_path)#tree為文件,write寫入新的文件中。print("OK1") 由于在上述數(shù)據(jù)處理過程中我們產(chǎn)生了很多冗余的文件,故需將其刪除。并將其更改為適合PaddleX的數(shù)據(jù)集格式。 !rm -rf dataset/Annotations !rm -rf dataset/Annotations1 !rm -rf dataset/Annotations2 !mv dataset/Annotations3 dataset/Annotations !mv dataset/images dataset/JPEGImages在原始數(shù)據(jù)集中,存在.jpg文件和.xml文件匹配不對等的情況,這里我們根據(jù).jpg文件名刪除了在Annotations文件夾中無法匹配的.xml文件,使得.jpg和.xml能夠一一對應(yīng)。
import os import shutil path_annotations = 'dataset/Annotations' path_JPEGImage = 'dataset/JPEGImages' xml_path = os.listdir(path_annotations) jpg_path = os.listdir(path_JPEGImage) for i in jpg_path:a = i.split('.')[0] + '.xml'if a in xml_path:passelse:print(i)os.remove(os.path.join(path_JPEGImage,i))因為懶(有現(xiàn)成的為啥還要自己寫呢~),所以就用了PaddleX 自帶的劃分?jǐn)?shù)據(jù)集的命令,這里我們訓(xùn)練集、驗證集、測試集的比例為7:2:1。
!paddlex --split_dataset --format VOC --dataset_dir /home/aistudio/dataset/ --val_value 0.2 --test_value 0.1??當(dāng)然,從一般情況來說,這一步完成之后就可以進(jìn)行模型訓(xùn)練了。但其實在實際過程中,若進(jìn)行模型訓(xùn)練,在訓(xùn)練過程中會報錯。具體報錯,大家感興趣的可以實地體驗一下(嘿嘿嘿)。這里就需要用到我們在開頭所提到的check.py文件。執(zhí)行以下命令,會輸出圖片讀取有誤的圖片路徑。我們用rm -rf命令將其與其對應(yīng)的.xml文件刪除。
%cd dataset/ !cat train_list.txt | python check.py !cat test_list.txt | python check.py !cat val_list.txt | python check.py /home/aistudio/dataset Wrong img file: JPEGImages/790_strawberry.jpg Wrong img file: JPEGImages/791_strawberry.jpg Wrong img file: JPEGImages/000143.jpg Wrong img file: JPEGImages/000144.jpg Wrong img file: JPEGImages/000120.jpg Wrong img file: JPEGImages/780_strawberry.jpg Wrong img file: JPEGImages/845_strawberry.jpg Wrong img file: JPEGImages/smoke132.jpg Corrupt JPEG data: 1 extraneous bytes before marker 0xdb Wrong img file: JPEGImages/000325.jpg Wrong img file: JPEGImages/000193.jpg !rm -rf JPEGImages/000120.jpg !rm -rf JPEGImages/000143.jpg !rm -rf JPEGImages/791_strawberry.jpg !rm -rf JPEGImages/smoke132.jpg !rm -rf JPEGImages/780_strawberry.jpg !rm -rf JPEGImages/000193.jpg !rm -rf JPEGImages/845_strawberry.jpg !rm -rf JPEGImages/000325.jpg !rm -rf JPEGImages/000144.jpg !rm -rf JPEGImages/790_strawberry.jpg !rm -rf Annotations/000120.xml !rm -rf Annotations/000143.xml !rm -rf Annotations/791_strawberry.xml !rm -rf Annotations/smoke132.xml !rm -rf Annotations/780_strawberry.xml !rm -rf Annotations/000193.xml !rm -rf Annotations/845_strawberry.xml !rm -rf Annotations/000325.xml !rm -rf Annotations/000144.xml !rm -rf Annotations/790_strawberry.xml并且刪除之前生成的幾個關(guān)于數(shù)據(jù)集的文本文件,重新執(zhí)行PaddleX劃分?jǐn)?shù)據(jù)集的命令。
!rm -rf val_list.txt !rm -rf train_list.txt !rm -rf test_list.txt !rm -rf labels.txt !paddlex --split_dataset --format VOC --dataset_dir /home/aistudio/dataset/ --val_value 0.2 --test_value 0.1 %cd /home/aistudio/ /home/aistudio模型訓(xùn)練
??在使用PaddleX進(jìn)行模型訓(xùn)練的過程中,我們使用目前PaddleX適配精度最高的PPYolo模型進(jìn)行訓(xùn)練。其模型較大,預(yù)測速度比YOLOv3-DarkNet53更快,適用于服務(wù)端。大家也可以更改其他模型嘗試一下。這里我訓(xùn)練了大概300個epoch(別問,問就是沒算力了也懶得續(xù)點(diǎn)了……)其中第277個epoch效果最佳,map大概在76。(有算力的童鞋可以試著調(diào)參或者繼續(xù)往下面去試試)
import os os.environ['CUDA_VISIBLE_DEVICES'] = '0'from paddlex.det import transforms import paddlex as pdx# 定義訓(xùn)練和驗證時的transforms # API說明 https://paddlex.readthedocs.io/zh_CN/develop/apis/transforms/det_transforms.html train_transforms = transforms.Compose([transforms.MixupImage(mixup_epoch=250), transforms.RandomDistort(),transforms.RandomExpand(), transforms.RandomCrop(), transforms.Resize(target_size=608, interp='RANDOM'), transforms.RandomHorizontalFlip(),transforms.Normalize() ])eval_transforms = transforms.Compose([transforms.Resize(target_size=608, interp='CUBIC'), transforms.Normalize() ])# 定義訓(xùn)練和驗證所用的數(shù)據(jù)集 # API說明:https://paddlex.readthedocs.io/zh_CN/develop/apis/datasets.html#paddlex-datasets-vocdetection train_dataset = pdx.datasets.VOCDetection(data_dir='/home/aistudio/dataset',file_list='/home/aistudio/dataset/train_list.txt',label_list='/home/aistudio/dataset/labels.txt',transforms=train_transforms,parallel_method='thread',shuffle=True) eval_dataset = pdx.datasets.VOCDetection(data_dir='/home/aistudio/dataset',file_list='/home/aistudio/dataset/val_list.txt',label_list='/home/aistudio/dataset/labels.txt',parallel_method='thread',transforms=eval_transforms)# 初始化模型,并進(jìn)行訓(xùn)練 # 可使用VisualDL查看訓(xùn)練指標(biāo),參考https://paddlex.readthedocs.io/zh_CN/develop/train/visualdl.html num_classes = len(train_dataset.labels)# API說明: https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#paddlex-det-ppyolo model = pdx.det.PPYOLO(num_classes=num_classes)# API說明: https://paddlex.readthedocs.io/zh_CN/develop/apis/models/detection.html#train # 各參數(shù)介紹與調(diào)整說明:https://paddlex.readthedocs.io/zh_CN/develop/appendix/parameters.html model.train(num_epochs=540,train_dataset=train_dataset,train_batch_size=8,eval_dataset=eval_dataset,learning_rate=0.000125,save_interval_epochs=1,lr_decay_epochs=[270,320, 480],save_dir='output/ppyolo',use_vdl=True)模型導(dǎo)出
??這里我們將訓(xùn)練過程中保存的模型導(dǎo)出為inference格式模型,其原因在于:PaddlePaddle框架保存的權(quán)重文件分為兩種:支持前向推理和反向梯度的訓(xùn)練模型 和 只支持前向推理的推理模型。二者的區(qū)別是推理模型針對推理速度和顯存做了優(yōu)化,裁剪了一些只在訓(xùn)練過程中才需要的tensor,降低顯存占用,并進(jìn)行了一些類似層融合,kernel選擇的速度優(yōu)化。而導(dǎo)出的inference格式模型包括__model__、__params__和model.yml三個文件,分別表示模型的網(wǎng)絡(luò)結(jié)構(gòu)、模型權(quán)重和模型的配置文件(包括數(shù)據(jù)預(yù)處理參數(shù)等)。
!paddlex --export_inference --model_dir=PaddleDetection/output/ppyolo/epoch_277 --save_dir=./inference_model簡單部署嘗試
??下面我們簡單地部署體驗了一番。這里打開了設(shè)備攝像頭進(jìn)行視頻流預(yù)測,并設(shè)置了在閾值>=0.3的時候會播放“公共場所,禁止吸煙!”
??在本項目中可以看到存在“cigarette.mp3”和“dad.mp3”的音頻文件。“dad.mp3”的具體內(nèi)容這里賣個關(guān)子。大家下載下來體驗一下就知道了hhh。
??而這里的音頻,使用的是百度大腦的語音合成API,具體的大家可以去百度大腦官網(wǎng)看看,在本項目中就不予展示了。
import cv2 import paddlex as pdx from playsound import playsound# 修改模型所在位置 predictor = pdx.deploy.Predictor('D:\\project\\python\\cigarette\\inference_model') cap = cv2.VideoCapture(0)while cap.isOpened():ret, frame = cap.read()if ret:result = predictor.predict(frame)score = result[0]['score']if score >= 0.3:print("*"*100)# 修改音頻所在位置playsound('D:\\project\\python\\cigarette\\cigarette.mp3')# print(result)vis_img = pdx.det.visualize(frame, result, threshold=0.6, save_dir=None)cv2.imshow('cigarette', vis_img)if cv2.waitKey(1) & 0xFF == ord('q'):breakelse:break cap.release()個人相關(guān)介紹
-
筆名:左右
-
華東理工大學(xué)自動化專業(yè)大二在讀
-
號稱:冷板凳常客
-
熱衷于利用人工智能技術(shù)做點(diǎn)有價值的東西,為社會做點(diǎn)小事情;
-
另外也研究點(diǎn)多智能體的東西;
-
偶爾寫點(diǎn)隨筆、攝影、仰望星空…
AiStudio主頁,歡迎互關(guān)喲
Github主頁,歡迎互關(guān)喲
CSDN主頁,歡迎互關(guān)喲
總結(jié)
以上是生活随笔為你收集整理的爸,这下你还敢抽烟么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一个垃圾分类项目带你玩转飞桨(1)
- 下一篇: 『PPYOLO tiny尝鲜』基于Pad