從零開始使用YOLOv5+PyQt5+OpenCV+爬蟲實(shí)現(xiàn)是否佩戴口罩檢測(cè)
全流程教程,從數(shù)據(jù)采集到模型使用到最終展示。若有任何疑問和建議歡迎評(píng)論區(qū)討論。
先放上最終實(shí)現(xiàn)效果
圖片檢測(cè)效果圖
視頻檢測(cè)效果圖
攝像頭實(shí)時(shí)檢測(cè)效果圖
1. 數(shù)據(jù)集的制作
我已經(jīng)處理了一份數(shù)據(jù)形成了對(duì)應(yīng)的數(shù)據(jù)集。獲取地址為百度網(wǎng)盤:
鏈接:https://pan.baidu.com/s/1SkraBsZXWCu1YxmoWgDePg
提取碼:0ghw
其中all_mask為全部圖片數(shù)據(jù)集。new_mask_data為一份較小的數(shù)據(jù)集??梢园葱璜@取
文件組織形式為圖片和其對(duì)于的標(biāo)簽文件txt
以第一行五個(gè)數(shù)字為例,第一位為類別這里1代表戴口罩,0代表未帶口罩,后四位為目標(biāo)框的中心點(diǎn)x,y和大小h,w,其大小都是歸一化的即是目標(biāo)框的大小除以圖片的大小H,W。
自己制作數(shù)據(jù)集可以參考如下步驟
1.1 使用爬蟲采集數(shù)據(jù)集
可以通過爬蟲爬取一些佩戴口罩的圖片和一些未佩戴口罩的圖片。
這里直接上代碼,如果想詳細(xì)了解可以參考我的另外一篇文章python爬取百度圖片,可以大量批量爬?。▋H供學(xué)習(xí),很詳細(xì))
import requests#導(dǎo)入請(qǐng)求庫
import time
import re
#設(shè)定爬取的總數(shù)
total=90
batch_size=30
all_success=0
for i in range(total//batch_size):
url='https://image.baidu.com/search/acjson?tn=resultjson_com&logid=10371129381236677678&ipn=rj&ct=201326592&is=&fp=result&fr=&word=口罩&queryWord=口罩&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=&z=&ic=&hd=&latest=©right=&s=&se=&tab=&width=&height=&face=&istype=&qc=&nc=1&expermode=&nojc=&isAsync=&pn={0}&rn=30&gsm=3c&1682846532783='.format((i+1)*30)
#添加請(qǐng)求頭,模擬瀏覽器,有些網(wǎng)站可以不加這個(gè),不過最好是加上,油多不壞菜這個(gè)道理
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'}
res=requests.get(url,headers=headers)#發(fā)送請(qǐng)求,返回?cái)?shù)據(jù)
html=res.text#把返回的內(nèi)容解析
# 使用正則表達(dá)式匹配圖片url
img_url_list=re.findall('"thumbURL":"(.*?)"',html)
#print(img_url_list)
for j in range(len(img_url_list)):
res_img=requests.get(img_url_list[j],headers=headers)
img=res_img.content#這個(gè)里是圖片,我們需要返回二進(jìn)制數(shù)據(jù)
# 圖片保存的路徑
with open('D:\\code\\person\\mask\\'+str(j)+'mask_img.jpg','wb')as f:
f.write(img)
time.sleep(3)#每當(dāng)保存一張圖片,先暫停一下,不然太頻繁容易發(fā)現(xiàn)是機(jī)器爬蟲,導(dǎo)致無法獲取
all_success=all_success+1
print("爬取{}張圖片成功".format(all_success))
print("爬取{}張圖片成功".format(all_success))
一些爬取的效果圖如下。
1.2 使用labelme對(duì)圖片進(jìn)行標(biāo)注
labelme是圖形圖像注釋工具,它是用Python編寫的,并將Qt用于其圖形界面。說直白點(diǎn),它是有界面的, 像軟件一樣,可以交互,但是它又是由命令行啟動(dòng)的,比軟件的使用稍微麻煩點(diǎn)。其界面如下圖:
github鏈接: labelme https://github.com/wkentaro/labelme
它的功能很多,包括:
- 對(duì)圖像進(jìn)行多邊形,矩形,圓形,多段線,線段,點(diǎn)形式的標(biāo)注(可用于目標(biāo)檢-測(cè),圖像分割等任務(wù))。
- 對(duì)圖像進(jìn)行進(jìn)行 flag形式的標(biāo)注(可用于圖像分類 和 清理 任務(wù))。
- 視頻標(biāo)注 - 生成 VOC 格式的數(shù)據(jù)集(for semantic / instancesegmentation)
- 生成 COCO 格式的數(shù)據(jù)集(for instance segmentation)
2. YOLOv5
2.1YOLO算法簡單介紹
YOLO框架(You Only Look Once)與RCNN系列算法不一樣,是以不同的方式處理對(duì)象檢測(cè)。它將整個(gè)圖像放在一個(gè)實(shí)例中,并預(yù)測(cè)這些框的邊界框坐標(biāo)和及所屬類別概率。使用YOLO算法最大優(yōu)的點(diǎn)是速度極快,每秒可處理45幀,也能夠理解一般的對(duì)象表示。
在本節(jié)中,將介紹YOLO用于檢測(cè)給定圖像中的對(duì)象的處理步驟。
首先,輸入圖像:
然后,YOLO將輸入圖像劃分為網(wǎng)格形式(例如3 X 3):
最后,對(duì)每個(gè)網(wǎng)格應(yīng)用圖像分類和定位處理,獲得預(yù)測(cè)對(duì)象的邊界框及其對(duì)應(yīng)的類概率。
整個(gè)過程是不是很清晰,下面逐一詳細(xì)介紹。首先需要將標(biāo)記數(shù)據(jù)傳遞給模型以進(jìn)行訓(xùn)練。假設(shè)已將圖像劃分為大小為3 X 3的網(wǎng)格,且總共只有3個(gè)類別,分別是行人(c1)、汽車(c2)和摩托車(c3)。因此,對(duì)于每個(gè)單元格,標(biāo)簽y將是一個(gè)八維向量:
其中:
pc定義對(duì)象是否存在于網(wǎng)格中(存在的概率);
bx、by、bh、bw指定邊界框;
c1、c2、c3代表類別。如果檢測(cè)對(duì)象是汽車,則c2位置處的值將為1,c1和c3處的值將為0;
假設(shè)從上面的例子中選擇第一個(gè)網(wǎng)格:
由于此網(wǎng)格中沒有對(duì)象,因此pc將為零,此網(wǎng)格的y標(biāo)簽將為:
?意味著其它值是什么并不重要,因?yàn)榫W(wǎng)格中沒有對(duì)象。下面舉例另一個(gè)有車的網(wǎng)格(c2=1):
在為此網(wǎng)格編寫y標(biāo)簽之前,首先要了解YOLO如何確定網(wǎng)格中是否存在實(shí)際對(duì)象。大圖中有兩個(gè)物體(兩輛車),因此YOLO將取這兩個(gè)物體的中心點(diǎn),物體將被分配到包含這些物體中心的網(wǎng)格中。中心點(diǎn)左側(cè)網(wǎng)格的y標(biāo)簽會(huì)是這樣的:
由于此網(wǎng)格中存在對(duì)象,因此pc將等于1,bx、by、bh、bw將相對(duì)于正在處理的特定網(wǎng)格單元計(jì)算。由于檢測(cè)出的對(duì)象是汽車,所以c2=1,c1和c3均為0。對(duì)于9個(gè)網(wǎng)格中的每一個(gè)單元格,都具有八維輸出向量。最終的輸出形狀為3X3X8。
使用上面的例子(輸入圖像:100X100X3,輸出:3X3X8),模型將按如下方式進(jìn)行訓(xùn)練:
使用經(jīng)典的CNN網(wǎng)絡(luò)構(gòu)建模型,并進(jìn)行模型訓(xùn)練。在測(cè)試階段,將圖像傳遞給模型,經(jīng)過一次前向傳播就得到輸出y。為了簡單起見,使用3X3網(wǎng)格解釋這一點(diǎn),但通常在實(shí)際場景中會(huì)采用更大的網(wǎng)格(比如19X19)。
即使一個(gè)對(duì)象跨越多個(gè)網(wǎng)格,它也只會(huì)被分配到其中點(diǎn)所在的單個(gè)網(wǎng)格。可以通過增加更多網(wǎng)格來減少多個(gè)對(duì)象出現(xiàn)在同一網(wǎng)格單元中的幾率。
2.2 YOLOv5獲取與調(diào)試
2.2.1 下載yolov5代碼
如果你有g(shù)it,則使用git clone
git clone https://github.com/ultralytics/yolov5 # clone
如果你沒有g(shù)it,你可以使用Dwonload ZIP下載代碼項(xiàng)目。
yolov5代碼地址:yolov5
注意:yolov5的代碼是最新的v8.0版本
可以通過這個(gè)鏈接下載6.0版本https://github.com/ultralytics/yolov5/tree/v6.0
2.2.2 安裝yolov5訓(xùn)練所需的第三方庫:
- 檢查是否正確安裝好anaconda。
windows+r打開cmd,輸入 conda -V。若出現(xiàn)版本號(hào),則安裝成功。 - 檢查是否正確安裝好pytorch
import torch
if __name__ == '__main__':
print(torch.zeros(1))
- 進(jìn)入yolov5文件夾目錄安裝第三方庫
cd [path_to_yolov5]
如下圖所示
安裝第三方庫
pip install -r requirement.txt
如下圖所示,等待安裝完成
2.2.3 下載預(yù)訓(xùn)練的權(quán)重文件
我們需要下載其預(yù)訓(xùn)練的權(quán)重文件然后再此基礎(chǔ)上進(jìn)行調(diào)整訓(xùn)練,這樣在數(shù)據(jù)集數(shù)量較小時(shí)也能取得不錯(cuò)的檢測(cè)準(zhǔn)確率。
供選擇的有yolov5s,yolov5m,yolov5l,yolov5x。模型的大小逐漸增大,訓(xùn)練時(shí)間更長,準(zhǔn)確率提高。
這里我們以yolov5s為例訓(xùn)練。下載地址為yolov5s.pt
所有權(quán)重下載地址可在https://github.com/ultralytics/yolov5/releases/tag/v6.0界面找到
2.2.4 配置自己的yaml文件
配置models/yolov5s_mask.yaml 可以直接復(fù)制yolov5s.yaml文件,然后在nc即類別出進(jìn)行修改,對(duì)于口罩檢測(cè)就是戴口罩和不帶口罩兩類其數(shù)量為2。其中anchors參數(shù)表示錨框的大小,可以通過對(duì)數(shù)據(jù)集進(jìn)行knn聚類得到,這里直接使用默認(rèn)即對(duì)COCO數(shù)據(jù)集進(jìn)行聚類的結(jié)果。
配置mask.yaml 。這里train指定訓(xùn)練數(shù)據(jù)集所在位置,val測(cè)試數(shù)據(jù)集所在位置,nc類別數(shù),names類別的名稱(注意順序,我們txt文件中索引0代表未戴口罩,1代表戴口罩,所以這里順序是‘no_mask’,‘mask’)
train: /home/cmy/newdataset/mask/train/ #注意修改為自己的數(shù)據(jù)集所在位置
val: /home/cmy/newdataset/mask/val/
nc: 2
names: ['no_mask','mask']
2.2.5 開始訓(xùn)練
python3 train.py --img 640 --batch 8 --epochs 50 --data mask.yaml --cfg yolov5s_mask.yaml --weights "yolov5s.pt"
如果出現(xiàn)顯卡空間不足的情況可以改小–bath參數(shù)
會(huì)在/runs/train/exp/weights/best.pt下生成最終的權(quán)重文件。
將此文件復(fù)制到y(tǒng)olov5目錄下后續(xù)使用
2.2.5 編寫detection方法用于后續(xù)檢測(cè)的調(diào)用
后續(xù)進(jìn)行圖片或者視頻檢測(cè)時(shí),只需要傳入YOLOv5模型和圖片,便會(huì)返回檢測(cè)后圖片
import os
import sys
from pathlib import Path
import numpy as np
import torch
FILE = Path(__file__).resolve()
ROOT = FILE.parents[0] # YOLOv5 root directory
if str(ROOT) not in sys.path:
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
from models.experimental import attempt_load
from utils.general import apply_classifier, check_img_size, check_imshow, check_requirements, check_suffix, colorstr, \
increment_path, non_max_suppression, print_args, save_one_box, scale_coords, strip_optimizer, xyxy2xywh, LOGGER
from utils.plots import Annotator, colors
from utils.torch_utils import load_classifier, select_device, time_sync
from utils.augmentations import Albumentations, augment_hsv, copy_paste, letterbox, mixup, random_perspective
@torch.no_grad()
def detection(model,input_img):
imgsz = 640 # inference size (pixels)
conf_thres = 0.25 # confidence threshold
iou_thres = 0.45 # NMS IOU threshold
max_det = 1000 # maximum detections per image
device = '0' # cuda device, i.e. 0 or 0,1,2,3 or cpu
view_img = False # show results
save_txt = False # save results to *.txt
save_conf = False # save confidences in --save-txt labels
save_crop = False # save cropped prediction boxes
nosave = False # do not save images/videos
classes = None # filter by class: --class 0, or --class 0 2 3
agnostic_nms = False # class-agnostic NMS
augment = False # augmented inference
project = ROOT / 'runs/detect', # save results to project/name
name = 'exp' # save results to project/name
exist_ok = False, # existing project/name ok, do not increment
line_thickness = 3 # bounding box thickness (pixels)
hide_labels = False # hide labels
hide_conf = False # hide confidences
half = False # use FP16 half-precision inference
# Directories
# Initialize
device = select_device(device)
weights = 'best.pt'
# # Load model
w = str(weights[0] if isinstance(weights, list) else weights)
classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
check_suffix(w, suffixes) # check weights have acceptable suffix
pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes) # backend booleans
stride = int(model.stride.max())
names = model.module.names if hasattr(model, 'module') else model.names # get class names
imgsz = check_img_size(imgsz, s=stride) # check image size
img0 = input_img # BGR
im0s=img0
# Padded resize
img = letterbox(img0, imgsz, stride=32, auto=pt)[0]
# Convert
img = img.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB
img = np.ascontiguousarray(img)
bs = 1 # batch_size
dt, seen = [0.0, 0.0, 0.0], 0
t1 = time_sync()
img = torch.from_numpy(img).to(device)
img = img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if len(img.shape) == 3:
img = img[None] # expand for batch dim
t2 = time_sync()
dt[0] += t2 - t1
# Inference
if pt:
pred = model(img, augment=augment)[0]
t3 = time_sync()
dt[1] += t3 - t2
# NMS
pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
dt[2] += time_sync() - t3
# Process predictions
for i, det in enumerate(pred): # per image
seen += 1
im0=im0s.copy()
annotator = Annotator(im0, line_width=line_thickness, example=str(names))
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
# Add bbox to image
c = int(cls) # integer class
label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
annotator.box_label(xyxy, label, color=colors(c, True))
# Stream results
im0 = annotator.result()
return im0
3. Pyqt5
我們通過pyqt來制作展示的界面
3.1介紹
PyQt5 是 Digia的一套 Qt5 應(yīng)用框架與 python 的結(jié)合,同時(shí)支持 python2.x和 python3.x。
這里使用的是Python 3.x。Qt庫由 Riverbank Computing開發(fā),是最強(qiáng)大的GUI庫之一 。
PyQt5 是由一系列 Python 模塊組成。超過 620 個(gè)類,6000 函數(shù)和方法。能在諸如 Unix、Windows 和Mac OS 等主流操作系統(tǒng)上運(yùn)行。PyQt5 有兩種證書,GPL和 商業(yè)證書。
3.2 window平臺(tái)安裝
PyQt5 有兩種安裝方式,一種是從官網(wǎng)下載源碼安裝,另外一種是使用 pip 安裝。
這里我推薦大家使用pip 安裝。因?yàn)樗鼤?huì)自動(dòng)根據(jù)你的Python 版本來選擇合適的 PyQt5 版本,如果是手動(dòng)下載源碼安裝,難免會(huì)選擇出錯(cuò)。建議使用比較穩(wěn)妥的安裝方式。
pip3 install PyQt5
另外,如果你的網(wǎng)絡(luò)訪問外網(wǎng)不是很好的話建議使用豆瓣的鏡像下載,不然會(huì)很很慢或者直接安裝失敗。
pip install PyQt5 -i https://pypi.douban.com/simple
執(zhí)行以下代碼:
import sys
from PyQt5.QtWidgets import QWidget, QApplication
app = QApplication(sys.argv)
widget = QWidget()
widget.resize(640, 480)
widget.setWindowTitle("Hello, PyQt5!")
widget.show()
sys.exit(app.exec())
如果沒有報(bào)錯(cuò),彈出了一個(gè)標(biāo)題為"Hello, PyQt5!"的窗口,則說明安裝成功。
若pip安裝pyqt5報(bào)錯(cuò) error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft C++ Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/
error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/
可參考我另外一篇文章 已解決(pip安裝pyqt5報(bào)錯(cuò)) error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft
對(duì)于運(yùn)行項(xiàng)目做到此即可,若對(duì)pyqt感興趣如圖形界面開發(fā)工具Qt Designer等可以參考文章https://zhuanlan.zhihu.com/p/162866700
4. OpenCV
OpenCV(開源的計(jì)算機(jī)視覺庫)是基于BSD協(xié)議,因此它可免費(fèi)用于學(xué)術(shù)和商業(yè)用途。其提供C++,C,Python和Java接口,支持Windows,Linux,Mac OS,iOS和Android。
我們使用OpenCV來處理圖片和視頻,以便于將圖片轉(zhuǎn)為Yolov5模型需要的輸入。
安裝
首先我們得先安裝另一個(gè)第三方庫numpy,這是opencv的依賴庫,沒有它無法進(jìn)行python-opencv開發(fā)。
安裝numpy:pip install numpy
安裝opencv-python: pip install opencv-python
5. 圖片檢測(cè)
5.1界面布局
首先使用pyqt設(shè)計(jì)我們界面的布局,主要為一個(gè)上傳圖片的按鈕和兩個(gè)展示板,一個(gè)展示原始圖片,一個(gè)展示我們模型進(jìn)行檢測(cè)后的圖片。這里主要使用的是網(wǎng)格布局QGridLayout()
class Qdetection1(QWidget):
def __init__(self,model):
super(Qdetection1, self).__init__()
self.initUI()
self.model=model
def initUI(self):
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.button1 = QPushButton('上傳圖片')
self.button1.clicked.connect(self.loadImage)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
因?yàn)楹罄m(xù)有視頻檢測(cè)和攝像頭實(shí)時(shí)檢測(cè),為了將其集成在同一個(gè)界面里,可以使用QTabWidget()
my_tabwidget=QTabWidget()
tab1_widget=Qdetection1(model)
tab2_widget = Qdetection2(model)
tab3_widget = Qdetection3(model)
my_tabwidget.setWindowTitle('目標(biāo)檢測(cè)演示 ')
my_tabwidget.addTab(tab1_widget, '圖片檢測(cè)')
my_tabwidget.addTab(tab2_widget, '視頻檢測(cè)')
my_tabwidget.addTab(tab3_widget, '攝像頭實(shí)時(shí)檢測(cè)')
my_tabwidget.show()
pyqt原始提供的組件并不美觀,為了美化組件可以使用qt_material。其安裝和使用都比較簡單。
安裝
pip install qt_material
使用
import sys
from PyQt5 import QtWidgets
from qt_material import apply_stylesheet
# create the application and the main window
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QMainWindow()
# setup stylesheet
apply_stylesheet(app, theme='dark_teal.xml')
# run
window.show()
app.exec_()
對(duì)比效果
5.2 模型加載
我們需要將訓(xùn)練好的模型讀取加載從而使用。為了提交響應(yīng)速度和防止每個(gè)功能都重復(fù)加載,這里采用了在啟動(dòng)窗口的時(shí)候就進(jìn)行加載。
if __name__ == '__main__':
device = select_device('0')
# Load model
weights = 'best.pt'
w = str(weights[0] if isinstance(weights, list) else weights)
classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
check_suffix(w, suffixes) # check weights have acceptable suffix
pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes) # backend booleans
stride, names = 64, [f'class{i}' for i in range(1000)] # assign defaults
if pt:
model = torch.jit.load(w) if 'torchscript' in w else attempt_load(weights, map_location=device)
stride = int(model.stride.max()) # model stride
names = model.module.names if hasattr(model, 'module') else model.names # get class names
5.3點(diǎn)擊上傳按鈕事件和檢測(cè)展示綁定
當(dāng)我們點(diǎn)擊上傳按鈕后通過cv2讀取文件,通過detection方法檢測(cè)圖片。然后將結(jié)果暫時(shí)到對(duì)應(yīng)的展示板上。
def loadImage(self):
fname, _ = QFileDialog.getOpenFileName(self, '打開文件', '.', '圖像文件(*.jpg *.png)')
if fname is None or fname=="":
print("未選擇圖片")
else:
img = cv2.imread(fname)
np_img=detection(self.model,img)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.imageLabel1.setPixmap(QPixmap(QImage(img.data, img.shape[1], img.shape[0], img.shape[1]*3, QImage.Format_RGB888)))
self.imageLabel2.setPixmap(QPixmap(QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1]*3, QImage.Format_RGB888)))
5.4完整代碼
import torch
from utils.general import check_suffix
from utils.torch_utils import select_device
from pathlib import Path
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time
import cv2
from mydetection import detection
from yolov5.models.experimental import attempt_load
class Qdetection1(QWidget):
def __init__(self,model):
super(Qdetection1, self).__init__()
self.initUI()
self.model=model
def initUI(self):
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.button1 = QPushButton('上傳圖片')
self.button1.clicked.connect(self.loadImage)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
def loadImage(self):
fname, _ = QFileDialog.getOpenFileName(self, '打開文件', '.', '圖像文件(*.jpg *.png)')
if fname is None or fname=="":
print("未選擇圖片")
else:
img = cv2.imread(fname)
np_img=detection(self.model,img)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.imageLabel1.setPixmap(QPixmap(QImage(img.data, img.shape[1], img.shape[0], img.shape[1]*3, QImage.Format_RGB888)))
self.imageLabel2.setPixmap(QPixmap(QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1]*3, QImage.Format_RGB888)))
if __name__ == '__main__':
app = QApplication(sys.argv)
device = select_device('0')
# Load model
weights = 'best.pt'
w = str(weights[0] if isinstance(weights, list) else weights)
classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
check_suffix(w, suffixes) # check weights have acceptable suffix
pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes) # backend booleans
stride, names = 64, [f'class{i}' for i in range(1000)] # assign defaults
if pt:
model = torch.jit.load(w) if 'torchscript' in w else attempt_load(weights, map_location=device)
stride = int(model.stride.max()) # model stride
names = model.module.names if hasattr(model, 'module') else model.names # get class names
my_tabwidget=QTabWidget()
tab1_widget=Qdetection1(model)
my_tabwidget.setWindowTitle('目標(biāo)檢測(cè)演示 ')
my_tabwidget.addTab(tab1_widget, '圖片檢測(cè)')
my_tabwidget.show()
apply_stylesheet(app, theme='light_blue.xml')
sys.exit(app.exec_())
6. 視頻檢測(cè)
視頻檢測(cè)的布局模型加載等都有圖片檢測(cè)相同。主要是綁定事件函數(shù)的處理。
我們self.cap.isOpened()方法來判斷是否到文件結(jié)尾,從而循環(huán)的讀取視頻的每一幀即每一張圖片,將這一幀的圖像使用detection方法進(jìn)行檢測(cè)。為了使其能以視頻的方式展示,每一幀圖像和下一幀圖像之間需要借助cv2.waitKey()方法進(jìn)行暫停。對(duì)于普通的視頻其暫停時(shí)間可以通過計(jì)算視頻播放幀率frameRate=self.cap.get(cv2.CAP_PROP_FPS)
wait_time=int(1000 / frameRate)獲得wait_time。
這里我考量了模型檢測(cè)的耗費(fèi)時(shí)間time_cost,對(duì)其暫停時(shí)間進(jìn)行了一個(gè)動(dòng)態(tài)的處理,模型耗費(fèi)時(shí)間小于wait_time的進(jìn)行暫停補(bǔ)時(shí),否則不暫停。
class Qdetection2(QMainWindow):
def __init__(self,model):
super(Qdetection2, self).__init__()
self.initUI()
self.model=model
def initUI(self):
# self.setFixedSize(960, 700)
self.main_widget =QWidget() # 創(chuàng)建窗口主部件
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.main_widget.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.setCentralWidget(self.main_widget) # 設(shè)置窗口主部件
# layout = QVBoxLayout()
self.button1 = QPushButton('上傳視頻')
self.button1.clicked.connect(self.loadVideo)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
# self.main_layout.addWidget(self.button2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
# self.main_layout.addWidget(self.button2, 1, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
def loadVideo(self):
fname, _ = QFileDialog.getOpenFileName(self, '打開文件')
if fname is None or fname=="":
print("未選擇視頻")
else:
self.cap = cv2.VideoCapture(fname)
frameRate=self.cap.get(cv2.CAP_PROP_FPS)
wait_time=int(1000 / frameRate)
while self.cap.isOpened():
success, frame = self.cap.read()
# RGB轉(zhuǎn)BGR
if not success:
break
# print(success)
time_begin=time.time()
np_img = detection(self.model, frame)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
self.imageLabel1.setPixmap(QPixmap.fromImage(img))
self.imageLabel2.setPixmap(QPixmap(
QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1] * 3, QImage.Format_RGB888)))
time_cost=time.time()-time_begin
time_cost=time_cost*1000
print(time_cost)
if(time_cost<wait_time):
cv2.waitKey(int(wait_time-time_cost))
else:
cv2.waitKey(0)
7. 攝像頭實(shí)時(shí)檢測(cè)
博主終于買了一個(gè)攝像頭??梢蚤_始實(shí)現(xiàn)這個(gè)功能了。
攝像頭實(shí)時(shí)檢測(cè)的布局模型加載等都與視頻檢測(cè)相同。主要是修改視頻的來源改為從攝像頭獲取。
self.cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
這個(gè)模塊的代碼如下
class Qdetection3(QMainWindow):
def __init__(self,model):
super(Qdetection3, self).__init__()
self.initUI()
self.model=model
def initUI(self):
# self.setFixedSize(960, 700)
self.main_widget =QWidget() # 創(chuàng)建窗口主部件
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.main_widget.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.setCentralWidget(self.main_widget) # 設(shè)置窗口主部件
# layout = QVBoxLayout()
self.button1 = QPushButton('開始檢測(cè)')
self.button1.clicked.connect(self.loadVideo)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
# self.main_layout.addWidget(self.button2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
# self.main_layout.addWidget(self.button2, 1, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
def loadVideo(self):
self.cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
while self.cap.isOpened():
success, frame = self.cap.read()
# RGB轉(zhuǎn)BGR
if not success:
break
# print(success)
time_begin=time.time()
np_img = detection(self.model, frame)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
self.imageLabel1.setPixmap(QPixmap.fromImage(img))
self.imageLabel2.setPixmap(QPixmap(
QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1] * 3, QImage.Format_RGB888)))
time_cost=time.time()-time_begin
time_cost=time_cost*1000
print(time_cost)
cv2.waitKey(1)
8. 圖片、視頻、攝像頭三個(gè)模塊整合完整代碼
注意這個(gè)代碼要放在yolov5項(xiàng)目代碼中以及自己編寫的mydetection.py工具一起使用。yolov5代碼的獲取和配置在第2節(jié)YOLOV5有描述。單獨(dú)運(yùn)行這個(gè)是不行的。
import torch
from utils.general import check_suffix
from utils.torch_utils import select_device
from pathlib import Path
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import time
import cv2
from mydetection import detection
from yolov5.models.experimental import attempt_load
class Qdetection1(QWidget):
def __init__(self,model):
super(Qdetection1, self).__init__()
self.initUI()
self.model=model
def initUI(self):
# self.setFixedSize(960, 700)
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
# layout = QVBoxLayout()
self.button1 = QPushButton('上傳圖片')
self.button1.clicked.connect(self.loadImage)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
# self.main_layout.addWidget(self.button2, 1, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
self.setWindowTitle('目標(biāo)檢測(cè)演示 ')
def loadImage(self):
fname, _ = QFileDialog.getOpenFileName(self, '打開文件', '.', '圖像文件(*.jpg *.png)')
if fname is None or fname=="":
print("未選擇圖片")
else:
img = cv2.imread(fname)
np_img=detection(self.model,img)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# cv.imshow('img', img)
# cv.waitKey() # 解決圖片閃退問題
self.imageLabel1.setPixmap(QPixmap(QImage(img.data, img.shape[1], img.shape[0], img.shape[1]*3, QImage.Format_RGB888)))
self.imageLabel2.setPixmap(QPixmap(QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1]*3, QImage.Format_RGB888)))
def loadText(self):
dialog = QFileDialog()
dialog.setFileMode(QFileDialog.AnyFile)
dialog.setFilter(QDir.Files)
if dialog.exec():
filenames = dialog.selectedFiles()
f = open(filenames[0], encoding='utf-8', mode='r')
with f:
data = f.read()
self.contents.setText(data)
class Qdetection2(QMainWindow):
def __init__(self,model):
super(Qdetection2, self).__init__()
self.initUI()
self.model=model
def initUI(self):
# self.setFixedSize(960, 700)
self.main_widget =QWidget() # 創(chuàng)建窗口主部件
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.main_widget.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.setCentralWidget(self.main_widget) # 設(shè)置窗口主部件
# layout = QVBoxLayout()
self.button1 = QPushButton('上傳視頻')
self.button1.clicked.connect(self.loadVideo)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
# self.main_layout.addWidget(self.button2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
# self.main_layout.addWidget(self.button2, 1, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
def loadVideo(self):
fname, _ = QFileDialog.getOpenFileName(self, '打開文件')
if fname is None or fname=="":
print("未選擇視頻")
else:
self.cap = cv2.VideoCapture(fname)
frameRate=self.cap.get(cv2.CAP_PROP_FPS)
wait_time=int(1000 / frameRate)
while self.cap.isOpened():
success, frame = self.cap.read()
# RGB轉(zhuǎn)BGR
if not success:
break
# print(success)
time_begin=time.time()
np_img = detection(self.model, frame)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
self.imageLabel1.setPixmap(QPixmap.fromImage(img))
self.imageLabel2.setPixmap(QPixmap(
QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1] * 3, QImage.Format_RGB888)))
time_cost=time.time()-time_begin
time_cost=time_cost*1000
print(time_cost)
if(time_cost<wait_time):
cv2.waitKey(int(wait_time-time_cost))
else:
cv2.waitKey(0)
class Qdetection3(QMainWindow):
def __init__(self,model):
super(Qdetection3, self).__init__()
self.initUI()
self.model=model
def initUI(self):
# self.setFixedSize(960, 700)
self.main_widget =QWidget() # 創(chuàng)建窗口主部件
self.main_layout = QGridLayout() # 創(chuàng)建主部件的網(wǎng)格布局
self.main_widget.setLayout(self.main_layout) # 設(shè)置窗口主部件布局為網(wǎng)格布局
self.setCentralWidget(self.main_widget) # 設(shè)置窗口主部件
# layout = QVBoxLayout()
self.button1 = QPushButton('開始檢測(cè)')
self.button1.clicked.connect(self.loadVideo)
self.main_layout.addWidget(self.button1)
self.imageLabel1 = QLabel()
self.main_layout.addWidget(self.imageLabel1)
self.imageLabel2 = QLabel()
self.main_layout.addWidget(self.imageLabel2)
# self.main_layout.addWidget(self.button2)
self.main_layout.addWidget(self.button1, 0, 0, 1, 2)
# self.main_layout.addWidget(self.button2, 1, 0, 1, 2)
self.main_layout.addWidget(self.imageLabel1, 2, 0, 1, 1)
self.main_layout.addWidget(self.imageLabel2, 2, 1, 1, 1)
def loadVideo(self):
self.cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
while self.cap.isOpened():
success, frame = self.cap.read()
# RGB轉(zhuǎn)BGR
if not success:
break
# print(success)
time_begin=time.time()
np_img = detection(self.model, frame)
np_img = cv2.cvtColor(np_img, cv2.COLOR_BGR2RGB)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
img = QImage(frame.data, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
self.imageLabel1.setPixmap(QPixmap.fromImage(img))
self.imageLabel2.setPixmap(QPixmap(
QImage(np_img.data, np_img.shape[1], np_img.shape[0], np_img.shape[1] * 3, QImage.Format_RGB888)))
time_cost=time.time()-time_begin
time_cost=time_cost*1000
print(time_cost)
cv2.waitKey(1)
from qt_material import apply_stylesheet
# import qdarkstyle
if __name__ == '__main__':
app = QApplication(sys.argv)
device = select_device('0')
# Load model
weights = 'best.pt'
w = str(weights[0] if isinstance(weights, list) else weights)
classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
check_suffix(w, suffixes) # check weights have acceptable suffix
pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes) # backend booleans
stride, names = 64, [f'class{i}' for i in range(1000)] # assign defaults
if pt:
model = torch.jit.load(w) if 'torchscript' in w else attempt_load(weights, map_location=device)
stride = int(model.stride.max()) # model stride
names = model.module.names if hasattr(model, 'module') else model.names # get class names
my_tabwidget=QTabWidget()
tab1_widget=Qdetection1(model)
tab2_widget = Qdetection2(model)
tab3_widget = Qdetection3(model)
# my_tabwidget.setFixedSize(960, 700)
# my_tabwidget.setFixedSize(1080, 960)
my_tabwidget.setWindowTitle('目標(biāo)檢測(cè)演示 ')
my_tabwidget.addTab(tab1_widget, '圖片檢測(cè)')
my_tabwidget.addTab(tab2_widget, '視頻檢測(cè)')
my_tabwidget.addTab(tab3_widget, '攝像頭實(shí)時(shí)檢測(cè)')
my_tabwidget.show()
apply_stylesheet(app, theme='light_blue.xml')
sys.exit(app.exec_())
考慮在上文查找也麻煩。這里將mydetection.py ,以及yolov5代碼獲取鏈接yolo 再重復(fù)一遍。文章來源:http://www.zghlxwxcb.cn/news/detail-448433.html
import os
import sys
from pathlib import Path
import numpy as np
import torch
FILE = Path(__file__).resolve()
ROOT = FILE.parents[0] # YOLOv5 root directory
if str(ROOT) not in sys.path:
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
from utils.general import apply_classifier, check_img_size, check_imshow, check_requirements, check_suffix, colorstr, \
increment_path, non_max_suppression, print_args, save_one_box, scale_coords, strip_optimizer, xyxy2xywh, LOGGER
from utils.plots import Annotator, colors
from utils.torch_utils import load_classifier, select_device, time_sync
from utils.augmentations import Albumentations, augment_hsv, copy_paste, letterbox, mixup, random_perspective
@torch.no_grad()
def detection(model,input_img):
# model.pt path(s)
imgsz = 640 # inference size (pixels)
conf_thres = 0.25 # confidence threshold
iou_thres = 0.45 # NMS IOU threshold
max_det = 1000 # maximum detections per image
device = '0' # cuda device, i.e. 0 or 0,1,2,3 or cpu
view_img = False # show results
save_txt = False # save results to *.txt
save_conf = False # save confidences in --save-txt labels
save_crop = False # save cropped prediction boxes
nosave = False # do not save images/videos
classes = None # filter by class: --class 0, or --class 0 2 3
agnostic_nms = False # class-agnostic NMS
augment = False # augmented inference
project = ROOT / 'runs/detect', # save results to project/name
name = 'exp' # save results to project/name
exist_ok = False, # existing project/name ok, do not increment
line_thickness = 3 # bounding box thickness (pixels)
hide_labels = False # hide labels
hide_conf = False # hide confidences
half = False # use FP16 half-precision inference
# Directories
# Initialize
device = select_device(device)
weights = 'best.pt'
# # Load model
w = str(weights[0] if isinstance(weights, list) else weights)
classify, suffix, suffixes = False, Path(w).suffix.lower(), ['.pt', '.onnx', '.tflite', '.pb', '']
check_suffix(w, suffixes) # check weights have acceptable suffix
pt, onnx, tflite, pb, saved_model = (suffix == x for x in suffixes) # backend booleans
# stride, names = 64, [f'class{i}' for i in range(1000)] # assign defaults
# if pt:
# model = torch.jit.load(w) if 'torchscript' in w else attempt_load(weights, map_location=device)
# stride = int(model.stride.max()) # model stride
# names = model.module.names if hasattr(model, 'module') else model.names # get class names
stride = int(model.stride.max())
names = model.module.names if hasattr(model, 'module') else model.names # get class names
imgsz = check_img_size(imgsz, s=stride) # check image size
img0 = input_img # BGR
im0s=img0
# Padded resize
img = letterbox(img0, imgsz, stride=32, auto=pt)[0]
# Convert
img = img.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB
img = np.ascontiguousarray(img)
bs = 1 # batch_size
dt, seen = [0.0, 0.0, 0.0], 0
t1 = time_sync()
img = torch.from_numpy(img).to(device)
img = img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if len(img.shape) == 3:
img = img[None] # expand for batch dim
t2 = time_sync()
dt[0] += t2 - t1
# Inference
if pt:
# visualize = increment_path(save_dir / Path(path).stem, mkdir=True) if visualize else False
# pred = model(img, augment=augment, visualize=visualize)[0]
pred = model(img, augment=augment)[0]
t3 = time_sync()
dt[1] += t3 - t2
# NMS
pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
dt[2] += time_sync() - t3
# Process predictions
for i, det in enumerate(pred): # per image
seen += 1
im0=im0s.copy()
annotator = Annotator(im0, line_width=line_thickness, example=str(names))
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
# Add bbox to image
c = int(cls) # integer class
label = None if hide_labels else (names[c] if hide_conf else f'{names[c]} {conf:.2f}')
annotator.box_label(xyxy, label, color=colors(c, True))
# Stream results
im0 = annotator.result()
return im0
歡迎看看我的新文章
歡迎看看我的新文章佩戴口罩檢測(cè)從零開始使用YOLOv5+PyQt5+OpenCV+爬蟲實(shí)現(xiàn)(支持圖片、視頻、攝像頭實(shí)時(shí)檢測(cè),UI美化升級(jí)),UI和功能都有改動(dòng)升級(jí),新改動(dòng)效果如下。文章來源地址http://www.zghlxwxcb.cn/news/detail-448433.html
到了這里,關(guān)于從零開始使用YOLOv5+PyQt5+OpenCV+爬蟲實(shí)現(xiàn)是否佩戴口罩檢測(cè)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!