佩戴口罩檢測從零開始使用YOLOv5+PyQt5+OpenCV+爬蟲實現(xiàn)(支持圖片、視頻、攝像頭實時檢測,UI美化升級)
全流程教程,從數(shù)據(jù)采集到模型使用到最終展示。 支持圖片檢測、視頻檢測、攝像頭實時檢測,還支持視頻的暫停、結(jié)束等功能。若有任何疑問和建議歡迎評論區(qū)討論。
先放上最終UI實現(xiàn)效果
圖片檢測效果圖
視頻檢測效果圖
攝像頭實時檢測效果圖
文章來源:http://www.zghlxwxcb.cn/news/detail-441443.html
1. 數(shù)據(jù)集的制作
我已經(jīng)處理了一份數(shù)據(jù)形成了對應(yīng)的數(shù)據(jù)集。獲取地址為百度網(wǎng)盤:
鏈接:https://pan.baidu.com/s/1SkraBsZXWCu1YxmoWgDePg
提取碼:0ghw
其中all_mask為全部圖片數(shù)據(jù)集。new_mask_data為一份較小的數(shù)據(jù)集??梢园葱璜@取
文件組織形式為圖片和其對于的標簽文件txt
以第一行五個數(shù)字為例,第一位為類別這里1代表戴口罩,0代表未帶口罩,后四位為目標框的中心點x,y和大小h,w,其大小都是歸一化的即是目標框的大小除以圖片的大小H,W。
自己制作數(shù)據(jù)集可以參考如下步驟
1.1 使用爬蟲采集數(shù)據(jù)集
可以通過爬蟲爬取一些佩戴口罩的圖片和一些未佩戴口罩的圖片。
這里直接上代碼,如果想詳細了解可以參考我的另外一篇文章python爬取百度圖片,可以大量批量爬?。▋H供學(xué)習(xí),很詳細)
import requests#導(dǎo)入請求庫
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)
#添加請求頭,模擬瀏覽器,有些網(wǎng)站可以不加這個,不過最好是加上,油多不壞菜這個道理
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ā)送請求,返回數(shù)據(jù)
html=res.text#把返回的內(nèi)容解析
# 使用正則表達式匹配圖片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#這個里是圖片,我們需要返回二進制數(shù)據(jù)
# 圖片保存的路徑
with open('D:\\code\\person\\mask\\'+str(j)+'mask_img.jpg','wb')as f:
f.write(img)
time.sleep(3)#每當保存一張圖片,先暫停一下,不然太頻繁容易發(fā)現(xiàn)是機器爬蟲,導(dǎo)致無法獲取
all_success=all_success+1
print("爬取{}張圖片成功".format(all_success))
print("爬取{}張圖片成功".format(all_success))
一些爬取的效果圖如下。
1.2 使用labelme對圖片進行標注
labelme是圖形圖像注釋工具,它是用Python編寫的,并將Qt用于其圖形界面。說直白點,它是有界面的, 像軟件一樣,可以交互,但是它又是由命令行啟動的,比軟件的使用稍微麻煩點。其界面如下圖:
github鏈接: labelme https://github.com/wkentaro/labelme
它的功能很多,包括:
- 對圖像進行多邊形,矩形,圓形,多段線,線段,點形式的標注(可用于目標檢-測,圖像分割等任務(wù))。
- 對圖像進行進行 flag形式的標注(可用于圖像分類 和 清理 任務(wù))。
- 視頻標注 - 生成 VOC 格式的數(shù)據(jù)集(for semantic / instancesegmentation)
- 生成 COCO 格式的數(shù)據(jù)集(for instance segmentation)
2. YOLOv5
2.1YOLO算法簡單介紹
YOLO框架(You Only Look Once)與RCNN系列算法不一樣,是以不同的方式處理對象檢測。它將整個圖像放在一個實例中,并預(yù)測這些框的邊界框坐標和及所屬類別概率。使用YOLO算法最大優(yōu)的點是速度極快,每秒可處理45幀,也能夠理解一般的對象表示。
在本節(jié)中,將介紹YOLO用于檢測給定圖像中的對象的處理步驟。
首先,輸入圖像:
然后,YOLO將輸入圖像劃分為網(wǎng)格形式(例如3 X 3):
最后,對每個網(wǎng)格應(yīng)用圖像分類和定位處理,獲得預(yù)測對象的邊界框及其對應(yīng)的類概率。
整個過程是不是很清晰,下面逐一詳細介紹。首先需要將標記數(shù)據(jù)傳遞給模型以進行訓(xùn)練。假設(shè)已將圖像劃分為大小為3 X 3的網(wǎng)格,且總共只有3個類別,分別是行人(c1)、汽車(c2)和摩托車(c3)。因此,對于每個單元格,標簽y將是一個八維向量:
其中:
pc定義對象是否存在于網(wǎng)格中(存在的概率);
bx、by、bh、bw指定邊界框;
c1、c2、c3代表類別。如果檢測對象是汽車,則c2位置處的值將為1,c1和c3處的值將為0;
假設(shè)從上面的例子中選擇第一個網(wǎng)格:
由于此網(wǎng)格中沒有對象,因此pc將為零,此網(wǎng)格的y標簽將為:
?意味著其它值是什么并不重要,因為網(wǎng)格中沒有對象。下面舉例另一個有車的網(wǎng)格(c2=1):
在為此網(wǎng)格編寫y標簽之前,首先要了解YOLO如何確定網(wǎng)格中是否存在實際對象。大圖中有兩個物體(兩輛車),因此YOLO將取這兩個物體的中心點,物體將被分配到包含這些物體中心的網(wǎng)格中。中心點左側(cè)網(wǎng)格的y標簽會是這樣的:
由于此網(wǎng)格中存在對象,因此pc將等于1,bx、by、bh、bw將相對于正在處理的特定網(wǎng)格單元計算。由于檢測出的對象是汽車,所以c2=1,c1和c3均為0。對于9個網(wǎng)格中的每一個單元格,都具有八維輸出向量。最終的輸出形狀為3X3X8。
使用上面的例子(輸入圖像:100X100X3,輸出:3X3X8),模型將按如下方式進行訓(xùn)練:
使用經(jīng)典的CNN網(wǎng)絡(luò)構(gòu)建模型,并進行模型訓(xùn)練。在測試階段,將圖像傳遞給模型,經(jīng)過一次前向傳播就得到輸出y。為了簡單起見,使用3X3網(wǎng)格解釋這一點,但通常在實際場景中會采用更大的網(wǎng)格(比如19X19)。
即使一個對象跨越多個網(wǎng)格,它也只會被分配到其中點所在的單個網(wǎng)格??梢酝ㄟ^增加更多網(wǎng)格來減少多個對象出現(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下載代碼項目。
yolov5代碼地址:yolov5
注意:yolov5的代碼是最新的v8.0版本
可以通過這個鏈接下載6.0版本https://github.com/ultralytics/yolov5/tree/v6.0
2.2.2 安裝yolov5訓(xùn)練所需的第三方庫:
- 檢查是否正確安裝好anaconda。
windows+r打開cmd,輸入 conda -V。若出現(xiàn)版本號,則安裝成功。 - 檢查是否正確安裝好pytorch
import torch
if __name__ == '__main__':
print(torch.zeros(1))
- 進入yolov5文件夾目錄安裝第三方庫
cd [path_to_yolov5]
如下圖所示
安裝第三方庫
pip install -r requirement.txt
如下圖所示,等待安裝完成
2.2.3 下載預(yù)訓(xùn)練的權(quán)重文件
我們需要下載其預(yù)訓(xùn)練的權(quán)重文件然后再此基礎(chǔ)上進行調(diào)整訓(xùn)練,這樣在數(shù)據(jù)集數(shù)量較小時也能取得不錯的檢測準確率。
供選擇的有yolov5s,yolov5m,yolov5l,yolov5x。模型的大小逐漸增大,訓(xù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即類別出進行修改,對于口罩檢測就是戴口罩和不帶口罩兩類其數(shù)量為2。其中anchors參數(shù)表示錨框的大小,可以通過對數(shù)據(jù)集進行knn聚類得到,這里直接使用默認即對COCO數(shù)據(jù)集進行聚類的結(jié)果。
配置mask.yaml 。這里train指定訓(xùn)練數(shù)據(jù)集所在位置,val測試數(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ù)
會在/runs/train/exp/weights/best.pt下生成最終的權(quán)重文件。
將此文件復(fù)制到y(tǒng)olov5目錄下后續(xù)使用
3. Pyqt5
我們通過pyqt來制作展示的界面
3.1介紹
PyQt5 是 Digia的一套 Qt5 應(yīng)用框架與 python 的結(jié)合,同時支持 python2.x和 python3.x。
這里使用的是Python 3.x。Qt庫由 Riverbank Computing開發(fā),是最強大的GUI庫之一 。
PyQt5 是由一系列 Python 模塊組成。超過 620 個類,6000 函數(shù)和方法。能在諸如 Unix、Windows 和Mac OS 等主流操作系統(tǒng)上運行。PyQt5 有兩種證書,GPL和 商業(yè)證書。
3.2 window平臺安裝
PyQt5 有兩種安裝方式,一種是從官網(wǎng)下載源碼安裝,另外一種是使用 pip 安裝。
這里我推薦大家使用pip 安裝。因為它會自動根據(jù)你的Python 版本來選擇合適的 PyQt5 版本,如果是手動下載源碼安裝,難免會選擇出錯。建議使用比較穩(wěn)妥的安裝方式。
pip3 install PyQt5
另外,如果你的網(wǎng)絡(luò)訪問外網(wǎng)不是很好的話建議使用豆瓣的鏡像下載,不然會很很慢或者直接安裝失敗。
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())
如果沒有報錯,彈出了一個標題為"Hello, PyQt5!"的窗口,則說明安裝成功。
若pip安裝pyqt5報錯 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報錯) error: Microsoft Visual C++ 14.0 or greater is required. Get it with “Microsoft
對于運行項目做到此即可,若對pyqt感興趣如圖形界面開發(fā)工具Qt Designer等可以參考文章https://zhuanlan.zhihu.com/p/162866700
4. OpenCV
OpenCV(開源的計算機視覺庫)是基于BSD協(xié)議,因此它可免費用于學(xué)術(shù)和商業(yè)用途。其提供C++,C,Python和Java接口,支持Windows,Linux,Mac OS,iOS和Android。
我們使用OpenCV來處理圖片和視頻,以便于將圖片轉(zhuǎn)為Yolov5模型需要的輸入。
安裝
首先我們得先安裝另一個第三方庫numpy,這是opencv的依賴庫,沒有它無法進行python-opencv開發(fā)。
#安裝numpy:
pip install numpy
#安裝opencv-python:
pip install opencv-python
5. 界面布局
使用pyqt設(shè)計我們界面的布局,新建布局代碼mainwindow_ui .py。pyqt原始提供的組件并不美觀,為了美化組件可以使用qt_material。其安裝和使用都比較簡單。
安裝
pip install qt_material
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'mainwindow_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("目標檢測")
MainWindow.resize(1367, 850)
font = QtGui.QFont()
font.setFamily("Adobe 宋體 Std L")
MainWindow.setFont(font)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(10, 50, 141, 771))
font = QtGui.QFont()
font.setFamily("Adobe 黑體 Std R")
self.groupBox.setFont(font)
self.groupBox.setObjectName("groupBox")
self.btn_loadweight = QtWidgets.QPushButton(self.groupBox)
self.btn_loadweight.setGeometry(QtCore.QRect(10, 20, 121, 31))
self.btn_loadweight.setObjectName("btn_loadweight")
self.btn_loadimg = QtWidgets.QPushButton(self.groupBox)
self.btn_loadimg.setGeometry(QtCore.QRect(10, 60, 121, 31))
self.btn_loadimg.setObjectName("btn_loadimg")
self.btn_loadvideo = QtWidgets.QPushButton(self.groupBox)
self.btn_loadvideo.setGeometry(QtCore.QRect(10, 100, 121, 31))
self.btn_loadvideo.setObjectName("btn_loadvideo")
self.btn_opencamera = QtWidgets.QPushButton(self.groupBox)
self.btn_opencamera.setGeometry(QtCore.QRect(10, 140, 121, 31))
self.btn_opencamera.setObjectName("btn_opencamera")
self.btn_stop = QtWidgets.QPushButton(self.groupBox)
self.btn_stop.setGeometry(QtCore.QRect(10, 500, 121, 31))
self.btn_stop.setObjectName("btn_going")
self.btn_over = QtWidgets.QPushButton(self.groupBox)
self.btn_over.setGeometry(QtCore.QRect(10, 540, 121, 31))
self.btn_over.setObjectName("btn_stop")
self.fps_label = QtWidgets.QLabel(self.groupBox)
self.fps_label.setGeometry(QtCore.QRect(50, 630, 121, 31))
self.fps_label.setObjectName("fps_label")
font.setPointSize(15)
# font.setFamily("Adobe Devanagari")
self.fps_label.setFont(font)
self.btn_takephoto = QtWidgets.QPushButton(self.groupBox)
self.btn_takephoto.setGeometry(QtCore.QRect(10, 380, 121, 31))
self.btn_takephoto.setObjectName("btn_takephoto")
self.btn_clear = QtWidgets.QPushButton(self.groupBox)
self.btn_clear.setGeometry(QtCore.QRect(10, 460, 121, 31))
self.btn_clear.setObjectName("btn_clear")
self.btn_labelimg = QtWidgets.QPushButton(self.groupBox)
self.btn_labelimg.setGeometry(QtCore.QRect(10, 420, 121, 31))
self.btn_labelimg.setObjectName("btn_labelimg")
self.btn_closecamera = QtWidgets.QPushButton(self.groupBox)
self.btn_closecamera.setGeometry(QtCore.QRect(10, 220, 121, 31))
self.btn_closecamera.setObjectName("btn_closecamera")
self.layoutWidget = QtWidgets.QWidget(self.groupBox)
self.layoutWidget.setGeometry(QtCore.QRect(10, 300, 121, 23))
self.layoutWidget.setObjectName("layoutWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
# self.label_2 = QtWidgets.QLabel(self.layoutWidget)
# self.label_2.setObjectName("label_2")
# self.horizontalLayout_2.addWidget(self.label_2)
# self.confSpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget)
# self.confSpinBox.setObjectName("confSpinBox")
# self.horizontalLayout_2.addWidget(self.confSpinBox)
self.layoutWidget_2 = QtWidgets.QWidget(self.groupBox)
self.layoutWidget_2.setGeometry(QtCore.QRect(10, 340, 121, 23))
self.layoutWidget_2.setObjectName("layoutWidget_2")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.layoutWidget_2)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
# self.label_3 = QtWidgets.QLabel(self.layoutWidget_2)
# self.label_3.setObjectName("label_3")
# self.horizontalLayout_3.addWidget(self.label_3)
# self.latencySpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget_2)
# self.latencySpinBox.setObjectName("latencySpinBox")
# self.horizontalLayout_3.addWidget(self.latencySpinBox)
self.layoutWidget1 = QtWidgets.QWidget(self.groupBox)
self.layoutWidget1.setGeometry(QtCore.QRect(10, 260, 121, 23))
self.layoutWidget1.setObjectName("layoutWidget1")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget1)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
# self.label = QtWidgets.QLabel(self.layoutWidget1)
# self.label.setObjectName("label")
# self.horizontalLayout.addWidget(self.label)
# self.iouSpinBox = QtWidgets.QDoubleSpinBox(self.layoutWidget1)
# self.iouSpinBox.setObjectName("iouSpinBox")
# self.horizontalLayout.addWidget(self.iouSpinBox)
self.btn_camera_detect = QtWidgets.QPushButton(self.groupBox)
self.btn_camera_detect.setGeometry(QtCore.QRect(10, 180, 121, 31))
self.btn_camera_detect.setObjectName("btn_camera_detect")
self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_2.setGeometry(QtCore.QRect(170, 50, 1181, 591))
self.groupBox_2.setObjectName("groupBox_2")
self.label_origin = QtWidgets.QLabel(self.groupBox_2)
self.label_origin.setGeometry(QtCore.QRect(10, 30, 571, 551))
font = QtGui.QFont()
font.setPointSize(15)
self.label_origin.setFont(font)
self.label_origin.setStyleSheet("background-color: rgb(85, 170, 255);")
self.label_origin.setAlignment(QtCore.Qt.AlignCenter)
self.label_origin.setObjectName("label_origin")
self.label_detect = QtWidgets.QLabel(self.groupBox_2)
self.label_detect.setGeometry(QtCore.QRect(600, 30, 571, 551))
font = QtGui.QFont()
font.setPointSize(15)
self.label_detect.setFont(font)
self.label_detect.setStyleSheet("background-color: rgb(170, 255, 127);")
self.label_detect.setAlignment(QtCore.Qt.AlignCenter)
self.label_detect.setObjectName("label_detect")
self.label_mian_titlle = QtWidgets.QLabel(self.centralwidget)
self.label_mian_titlle.setGeometry(QtCore.QRect(530, 10, 281, 41))
font = QtGui.QFont()
font.setFamily("Adobe Devanagari")
font.setPointSize(20)
self.label_mian_titlle.setFont(font)
self.label_mian_titlle.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_mian_titlle.setAlignment(QtCore.Qt.AlignCenter)
self.label_mian_titlle.setObjectName("label_mian_titlle")
self.lcdNumber = QtWidgets.QLCDNumber(self.centralwidget)
self.lcdNumber.setGeometry(QtCore.QRect(1050, 20, 301, 31))
font = QtGui.QFont()
font.setFamily("Adobe 黑體 Std R")
self.lcdNumber.setFont(font)
self.lcdNumber.setObjectName("lcdNumber")
self.lcdNumber.setDigitCount(20)
self.groupBox_3 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_3.setGeometry(QtCore.QRect(170, 650, 581, 171))
self.groupBox_3.setObjectName("groupBox_3")
self.textBrowser_print = QtWidgets.QTextBrowser(self.groupBox_3)
self.textBrowser_print.setGeometry(QtCore.QRect(10, 30, 561, 141))
self.textBrowser_print.setObjectName("textBrowser_print")
self.groupBox_4 = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox_4.setGeometry(QtCore.QRect(770, 650, 581, 171))
self.groupBox_4.setObjectName("groupBox_4")
self.textBrowser_detect = QtWidgets.QTextBrowser(self.groupBox_4)
self.textBrowser_detect.setGeometry(QtCore.QRect(10, 30, 561, 141))
self.textBrowser_detect.setObjectName("textBrowser_detect")
self.label_author = QtWidgets.QLabel(self.centralwidget)
self.label_author.setGeometry(QtCore.QRect(20, 20, 111, 16))
font = QtGui.QFont()
font.setFamily("Adobe 黑體 Std R")
font.setPointSize(12)
self.label_author.setFont(font)
self.label_author.setObjectName("label_author")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 1367, 23))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", ""))
self.btn_loadweight.setText(_translate("MainWindow", "加載Weights"))
self.btn_loadimg.setText(_translate("MainWindow", "圖片檢測"))
self.btn_loadvideo.setText(_translate("MainWindow", "視頻檢測"))
self.btn_opencamera.setText(_translate("MainWindow", "打開攝像頭"))
self.btn_over.setText(_translate("MainWindow", "結(jié)束"))
self.fps_label.setText(_translate("MainWindow", "幀率"))
self.btn_stop.setText(_translate("MainWindow", "暫停"))
self.btn_takephoto.setText(_translate("MainWindow", "拍照"))
self.btn_clear.setText(_translate("MainWindow", "清除輸出"))
self.btn_labelimg.setText(_translate("MainWindow", "標注工具"))
self.btn_closecamera.setText(_translate("MainWindow", "關(guān)閉攝像頭"))
# self.label_2.setText(_translate("MainWindow", "conf:"))
# self.label_3.setText(_translate("MainWindow", "latency:"))
# self.label.setText(_translate("MainWindow", "IoU:"))
self.btn_camera_detect.setText(_translate("MainWindow", "攝像頭檢測"))
self.groupBox_2.setTitle(_translate("MainWindow", "顯示區(qū)域"))
self.label_origin.setText(_translate("MainWindow", "原圖區(qū)域"))
self.label_detect.setText(_translate("MainWindow", "檢測區(qū)域"))
self.label_mian_titlle.setText(_translate("MainWindow", "YoLo檢測界面"))
self.groupBox_3.setTitle(_translate("MainWindow", "打印輸出"))
self.groupBox_4.setTitle(_translate("MainWindow", "檢測輸出"))
self.label_author.setText(_translate("MainWindow", " "))
6. 圖片、視頻、攝像頭實時三個模塊整合完整代碼
注意這個代碼要放在yolov5項目代碼中一起使用以及上一節(jié)的布局代碼mainwindow_ui.py。yolov5代碼的獲取和配置在第2節(jié)YOLOV5有描述。yolov5代碼獲取鏈接yolo。單獨運行這個是不行的。
import argparse
import datetime
import os
import random
import sys
import time
import cv2
import numpy as np
import torch
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtCore import QTimer, QDateTime, QDate, QTime, QThread, pyqtSignal
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from torch.backends import cudnn
from models.experimental import attempt_load
from mainwindow_ui import Ui_MainWindow
# from utils.datasets import letterbox
from utils.general import check_img_size, non_max_suppression, scale_coords
# from utils.plots import plot_one_box2
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
# Resize and pad image while meeting stride-multiple constraints
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
# Scale ratio (new / old)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup: # only scale down, do not scale up (for better test mAP)
r = min(r, 1.0)
# Compute padding
ratio = r, r # width, height ratios
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
if auto: # minimum rectangle
dw, dh = np.mod(dw, stride), np.mod(dh, stride) # wh padding
elif scaleFill: # stretch
dw, dh = 0.0, 0.0
new_unpad = (new_shape[1], new_shape[0])
ratio = new_shape[1] / shape[1], new_shape[0] / shape[0] # width, height ratios
dw /= 2 # divide padding into 2 sides
dh /= 2
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
return img, ratio, (dw, dh)
def plot_one_box2(x, img, color=None, label=None, line_thickness=3):
# Plots one bounding box on image img
tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1 # line/font thickness
color = color or [random.randint(0, 255) for _ in range(3)]
c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
if label:
tf = max(tl - 1, 1) # font thickness
t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled
cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
single_info = "Position: (%d, %d), (%d, %d), Obj and Confidence: %s"%(c1[0], c1[1], c2[0], c2[1], label)
return single_info
class UI_Logic_Window(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(UI_Logic_Window, self).__init__(parent)
self.initUI()
# 初始化界面
def initUI(self):
self.setWindowIcon(QIcon("./icon/yolo.png"))
# 創(chuàng)建一個窗口對象
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.timer_video = QtCore.QTimer(self) # 創(chuàng)建定時器
self.timer_photo = QtCore.QTimer(self) # 創(chuàng)建定時器
self.output_folder = 'output/'
self.cap = cv2.VideoCapture()
self.vid_writer = None
self.camera_detect = False
self.num_stop = 1 # 暫停與播放輔助信號,note:通過奇偶來控制暫停與播放
self.openfile_name_model = None # 權(quán)重初始文件名
self.count = 0
self.start_time = time.time() # 打開線程
self.stop_going = 0
# 刷新lcd時間
self.lcd_time = QTimer(self)
self.lcd_time.setInterval(1000)
self.lcd_time.timeout.connect(self.refresh)
self.lcd_time.start()
self.ui.textBrowser_print.append("特別說明:如需啟動檢測,請先加載weights文件!??!")
self.init_slots()
# 刷新時間
def refresh(self):
now_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.ui.lcdNumber.display(now_time)
# 初始化槽函數(shù)
def init_slots(self):
self.ui.btn_loadweight.clicked.connect(self.load_model)
self.ui.btn_loadimg.clicked.connect(self.button_image_open)
self.ui.btn_loadvideo.clicked.connect(self.button_video_open)
self.ui.btn_opencamera.clicked.connect(self.button_camera_open)
self.ui.btn_camera_detect.clicked.connect(self.button_camera_detect)
self.ui.btn_stop.clicked.connect(self.button_stop)
self.ui.btn_over.clicked.connect(self.button_over)
self.ui.btn_closecamera.clicked.connect(self.button_closecamera)
self.ui.btn_clear.clicked.connect(self.button_clear)
self.ui.btn_takephoto.clicked.connect(self.button_takephoto)
self.ui.btn_labelimg.clicked.connect(self.button_labelimg)
self.timer_video.timeout.connect(self.show_video_frame) # 定時器超時,將槽綁定至show_video_frame
self.timer_photo.timeout.connect(self.show_image) # 定時器超時,將槽綁定至show_video_frame
# 加載模型
def load_model(self):
self.openfile_name_model, _ = QtWidgets.QFileDialog.getOpenFileName(self.ui.btn_loadweight, '選擇weights文件',
'weights/', "*.pt;;*.pth")
if not self.openfile_name_model:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打開權(quán)重失敗", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
self.ui.textBrowser_print.append("打開權(quán)重失敗")
else:
self.ui.textBrowser_print.append("加載weights文件地址為:" + str(self.openfile_name_model))
self.model_init() #初始化權(quán)重
# 初始化權(quán)重
def model_init(self):
# 模型相關(guān)參數(shù)配置
parser = argparse.ArgumentParser()
parser.add_argument('--weights', nargs='+', type=str, default='weights/yolov5s6.pt', help='model.pt path(s)')
parser.add_argument('--source', type=str, default='data/images', help='source') # file/folder, 0 for webcam
parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)')
parser.add_argument('--conf-thres', type=float, default=0.25, help='object confidence threshold')
parser.add_argument('--iou-thres', type=float, default=0.45, help='IOU threshold for NMS')
parser.add_argument('--device', default='cuda', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--view-img', action='store_true', help='display results')
parser.add_argument('--save-txt', action='store_true', help='save results to *.txt')
parser.add_argument('--save-conf', action='store_true', help='save confidences in --save-txt labels')
parser.add_argument('--nosave', action='store_true', help='do not save images/videos')
parser.add_argument('--classes', nargs='+', type=int, help='filter by class: --class 0, or --class 0 2 3')
parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS')
parser.add_argument('--augment', action='store_true', help='augmented inference')
parser.add_argument('--update', action='store_true', help='update all models')
parser.add_argument('--project', default='runs/detect', help='save results to project/name')
parser.add_argument('--name', default='exp', help='save results to project/name')
parser.add_argument('--exist-ok', action='store_true', help='existing project/name ok, do not increment')
self.opt = parser.parse_args()
print(self.opt)
# 默認使用opt中的設(shè)置(權(quán)重等)來對模型進行初始化
source, weights, view_img, save_txt, imgsz = self.opt.source, self.opt.weights, self.opt.view_img, self.opt.save_txt, self.opt.img_size
# 改變權(quán)重文件
if self.openfile_name_model:
weights = self.openfile_name_model
print(weights)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.half = self.device.type != 'cpu' # half precision only supported on CUDA
cudnn.benchmark = True
# Load model
self.model = attempt_load(weights, map_location=self.device) # load FP32 model
stride = int(self.model.stride.max()) # model stride
self.imgsz = check_img_size(imgsz, s=stride) # check img_size
if self.half:
self.model.half() # to FP16
# Second-stage classifier
# Get names and colors
self.names = self.model.module.names if hasattr(self.model, 'module') else self.model.names
self.colors = [[random.randint(0, 255) for _ in range(3)] for _ in self.names]
# 設(shè)置提示框
QtWidgets.QMessageBox.information(self, u"Notice", u"模型加載完成", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
self.ui.textBrowser_print.append("模型加載完成")
# 打開圖片
def button_image_open(self):
# 打印信息顯示在界面
name_list = []
try:
img_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打開圖片", "data/images", "*.jpg;;*.png;;All Files(*)")
except OSError as reason:
print('文件打開出錯啦!核對路徑是否正確'+ str(reason))
self.ui.textBrowser_print.append("文件打開出錯啦!核對路徑是否正確")
else:
# 判斷圖片是否為空
if not img_name:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打開圖片失敗", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
self.ui.textBrowser_print.append("打開圖片失敗")
else:
self.ui.textBrowser_print.append("打開圖片成功")
img = cv2.imread(img_name)
print("img_name:", img_name)
self.origin = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
self.origin = cv2.resize(self.origin, (640, 480), interpolation=cv2.INTER_AREA)
self.QtImg_origin = QtGui.QImage(self.origin.data, self.origin.shape[1], self.origin.shape[0],
QtGui.QImage.Format_RGB32)
self.ui.label_origin.setPixmap(QtGui.QPixmap.fromImage(self.QtImg_origin))
self.ui.label_origin.setScaledContents(True) # 設(shè)置圖像自適應(yīng)界面大小
info_show = self.detect(name_list, img)
# print(info_show)
# 獲取當前系統(tǒng)時間,作為img文件名
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
file_extension = img_name.split('.')[-1]
new_filename = now + '.' + file_extension # 獲得文件后綴名
file_path = self.output_folder + 'img_output/' + new_filename
cv2.imwrite(file_path, img)
# 檢測信息顯示在界面
self.ui.textBrowser_detect.append(info_show)
# 檢測結(jié)果顯示在界面
self.result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
self.result = cv2.resize(self.result, (640, 480), interpolation=cv2.INTER_AREA)
self.QtImg = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB32)
self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))
self.ui.label_detect.setScaledContents(True) # 設(shè)置圖像自適應(yīng)界面大小
# 目標檢測
def detect(self, name_list, img):
'''
:param name_list: 文件名列表
:param img: 待檢測圖片
:return: info_show:檢測輸出的文字信息
'''
showimg = img
with torch.no_grad():
img = letterbox(img, new_shape=self.opt.img_size)[0]
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1) # BGR to RGB, to 3x416x416
img = np.ascontiguousarray(img)
img = torch.from_numpy(img).to(self.device)
img = img.half() if self.half else img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
pred = self.model(img, augment=self.opt.augment)[0]
# Apply NMS
pred = non_max_suppression(pred, self.opt.conf_thres, self.opt.iou_thres, classes=self.opt.classes,
agnostic=self.opt.agnostic_nms)
info_show = ""
# Process detections
for i, det in enumerate(pred):
if det is not None and len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], showimg.shape).round()
for *xyxy, conf, cls in reversed(det):
label = '%s %.2f' % (self.names[int(cls)], conf)
name_list.append(self.names[int(cls)])
single_info = plot_one_box2(xyxy, showimg, label=label, color=self.colors[int(cls)],
line_thickness=2)
info_show = info_show + single_info + "\n"
return info_show
# 打開視頻并檢測
def button_video_open(self):
video_name, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打開視頻", "data/video/", "*.mp4;;*.avi;;All Files(*)")
flag = self.cap.open(video_name)
# 判斷攝像頭是否打開
if not flag:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打開視頻失敗", buttons=QtWidgets.QMessageBox.Ok,defaultButton=QtWidgets.QMessageBox.Ok)
else:
# -------------------------寫入視頻----------------------------------#
self.ui.textBrowser_print.append("打開視頻檢測")
fps, w, h, save_path = self.set_video_name_and_path()
self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
self.timer_video.start(30) # 以30ms為間隔,啟動或重啟定時器
# 進行視頻識別時,關(guān)閉其他按鍵點擊功能
self.ui.btn_loadvideo.setDisabled(True)
self.ui.btn_loadimg.setDisabled(True)
self.ui.btn_opencamera.setDisabled(True)
def set_video_name_and_path(self):
# 獲取當前系統(tǒng)時間,作為img和video的文件名
now = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))
# if vid_cap: # video
fps = self.cap.get(cv2.CAP_PROP_FPS)
w = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 視頻檢測結(jié)果存儲位置
save_path = self.output_folder + 'video_output/' + now + '.mp4'
return fps, w, h, save_path
# 定義視頻幀顯示操作
def show_video_frame(self):
name_list = []
flag, img = self.cap.read()
# 顯示視頻數(shù)據(jù)的幀數(shù)
self.count += 1
if self.count % 10 == 0:
self.count = 0
fps = int(30 / (time.time() - self.start_time))
self.ui.fps_label.setText('fps:' + str(fps))
self.start_time = time.time()
if img is not None:
# 原始數(shù)據(jù)的顯示
self.origin = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
self.origin = cv2.resize(self.origin, (640, 480), interpolation=cv2.INTER_AREA)
self.QtImg_origin = QtGui.QImage(self.origin.data, self.origin.shape[1], self.origin.shape[0],
QtGui.QImage.Format_RGB32)
self.ui.label_origin.setPixmap(QtGui.QPixmap.fromImage(self.QtImg_origin))
self.ui.label_origin.setScaledContents(True) # 設(shè)置圖像自適應(yīng)界面大小
# 檢測數(shù)據(jù)的顯示
info_show = self.detect(name_list, img) # 檢測結(jié)果寫入到原始img上
self.vid_writer.write(img) # 檢測結(jié)果寫入視頻
print(info_show)
# 檢測信息顯示在界面
self.ui.textBrowser_detect.append(info_show)
show = cv2.resize(img, (640, 480)) # 直接將原始img上的檢測結(jié)果進行顯示
self.result = cv2.cvtColor(show, cv2.COLOR_BGR2RGB)
showImage = QtGui.QImage(self.result.data, self.result.shape[1], self.result.shape[0],
QtGui.QImage.Format_RGB888)
self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(showImage))
self.ui.label_detect.setScaledContents(True) # 設(shè)置圖像自適應(yīng)界面大小
else:
self.timer_video.stop()
# 讀寫結(jié)束,釋放資源
self.cap.release() # 釋放video_capture資源
self.vid_writer.release() # 釋放video_writer資源
self.ui.label.clear()
# 視頻幀顯示期間,禁用其他檢測按鍵功能
self.ui.btn_loadvideo.setDisabled(True)
self.ui.btn_loadimg.setDisabled(True)
self.ui.btn_opencamera.setDisabled(True)
'''顯示圖片'''
def show_image(self):
flag, self.image = self.cap.read() # 從視頻流中讀取圖片
image_show = cv2.resize(self.image, (620, 420)) # 把讀到的幀的大小重新設(shè)置為顯示的窗口大小
width, height = image_show.shape[:2] # 行:寬,列:高
image_show = cv2.cvtColor(image_show, cv2.COLOR_BGR2RGB) # opencv讀的通道是BGR,要轉(zhuǎn)成RGB
image_show = cv2.flip(image_show, 1) # 水平翻轉(zhuǎn),因為攝像頭拍的是鏡像的。
# 把讀取到的視頻數(shù)據(jù)變成QImage形式(圖片數(shù)據(jù)、高、寬、RGB顏色空間,三個通道各有2**8=256種顏色)
self.photo= QtGui.QImage(image_show.data, height, width, QImage.Format_RGB888)
self.ui.label_origin.setPixmap(QPixmap.fromImage(self.photo)) # 往顯示視頻的Label里顯示QImage
self.ui.label_origin.setScaledContents(True) # 圖片自適應(yīng)
# 使用攝像頭檢測
def button_camera_open(self):
self.camera_detect = True
self.ui.textBrowser_print.append("打開攝像頭")
# 設(shè)置使用的攝像頭序號,系統(tǒng)自帶為0
camera_num = 0
# 打開攝像頭
self.cap = cv2.VideoCapture(camera_num)
# 判斷攝像頭是否處于打開狀態(tài)
bool_open = self.cap.isOpened()
if not bool_open:
QtWidgets.QMessageBox.warning(self, u"Warning", u"打開攝像頭失敗", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
else:
QtWidgets.QMessageBox.information(self, u"Warning", u"打開攝像頭成功", buttons=QtWidgets.QMessageBox.Ok,
defaultButton=QtWidgets.QMessageBox.Ok)
self.ui.btn_loadvideo.setDisabled(True)
self.ui.btn_loadimg.setDisabled(True)
# 啟動攝像頭檢測
def button_camera_detect(self):
self.ui.textBrowser_print.append("啟動攝像頭檢測")
fps, w, h, save_path = self.set_video_name_and_path()
fps = 5 # 控制攝像頭檢測下的fps,Note:保存的視頻,播放速度有點快,我只是粗暴的調(diào)整了FPS
self.vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
self.timer_video.start(30)
self.ui.btn_loadvideo.setDisabled(True)
self.ui.btn_loadimg.setDisabled(True)
self.ui.btn_opencamera.setDisabled(True)
# 視頻暫停按鈕
def button_stop(self):
self.timer_video.blockSignals(False)
# 暫停檢測
# 若QTimer已經(jīng)觸發(fā),且激活
if self.timer_video.isActive() == True and self.num_stop % 2 == 1:
self.ui.btn_stop.setText('繼續(xù)')
self.ui.textBrowser_print.append("視頻暫停播放")
self.num_stop = self.num_stop + 1 # 調(diào)整標記信號為偶數(shù)
self.timer_video.blockSignals(True)
# 繼續(xù)檢測
else:
self.num_stop = self.num_stop + 1
self.ui.btn_stop.setText('暫停')
self.ui.textBrowser_print.append("視頻繼續(xù)播放")
# 停止視頻播放
def button_over(self):
self.ui.textBrowser_print.append("視頻結(jié)束播放")
self.cap.release() # 釋放video_capture資源
self.timer_video.stop() # 停止讀取
self.timer_photo.stop() # 停止讀取
if self.vid_writer != None:
self.vid_writer.release() # 釋放video_writer資源
self.ui.label_origin.clear() # 清空label畫布
self.ui.label_detect.clear() # 清空label畫布
# 啟動其他檢測按鍵功能
self.ui.btn_loadvideo.setDisabled(False)
self.ui.btn_loadimg.setDisabled(False)
self.ui.btn_opencamera.setDisabled(False)
# 結(jié)束檢測時,查看暫停功能是否復(fù)位,將暫停功能恢復(fù)至初始狀態(tài)
# Note:點擊暫停之后,num_stop為偶數(shù)狀態(tài)
if self.num_stop % 2 == 0:
print("Reset stop/begin!")
self.ui.btn_stop.setText(u'暫停')
self.num_stop = self.num_stop + 1
self.timer_video.blockSignals(False)
# 關(guān)閉攝像頭
def button_closecamera(self):
self.ui.textBrowser_print.append("關(guān)閉攝像頭")
self.ui.fps_label.setText("幀率")
self.timer_video.stop() # 停止讀取
self.timer_photo.stop() # 停止讀取
self.cap.release() # 釋放攝像頭
self.ui.label_origin.clear() # 清空label畫布
self.ui.label_detect.clear() # 清空label畫布
self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # 攝像頭
self.ui.btn_loadvideo.setDisabled(False)
self.ui.btn_loadimg.setDisabled(False)
self.ui.btn_opencamera.setDisabled(False)
# 拍照
def button_takephoto(self):
self.ui.textBrowser_print.append("啟動拍照")
self.timer_photo.start(30)
self.show_image()
if self.cap.isOpened():
FName = "data/images" + fr"/img{time.strftime('%Y%m%d%H%M%S', time.localtime())}"
print(FName)
# 原始數(shù)據(jù)的顯示
flag, self.image = self.cap.read() # 從視頻流中讀取圖片
image_show = cv2.resize(self.image, (640, 480)) # 把讀到的幀的大小重新設(shè)置為顯示的窗口大小
image_show = cv2.cvtColor(image_show, cv2.COLOR_BGR2RGB) # opencv讀的通道是BGR,要轉(zhuǎn)成RGB
image_show = cv2.flip(image_show, 1) # 水平翻轉(zhuǎn),因為攝像頭拍的是鏡像的。
# 把讀取到的視頻數(shù)據(jù)變成QImage形式(圖片數(shù)據(jù)、高、寬、RGB顏色空間,三個通道各有2**8=256種顏色)
self.showImage = QtGui.QImage(image_show.data, image_show.shape[1], image_show.shape[0], QImage.Format_RGB888)
self.ui.label_detect.setPixmap(QtGui.QPixmap.fromImage(self.photo))
self.ui.label_detect.setScaledContents(True) # 設(shè)置圖像自適應(yīng)界面大小
self.showImage.save(FName + ".jpg", "JPG", 300)
else:
QMessageBox.critical(self, '錯誤', '攝像頭未打開!')
return None
# 調(diào)用lablelimg批注工具
def button_labelimg(self):
self.ui.textBrowser_print.append("啟動標注工具")
os.system("labelimg")
# 清除顯示區(qū)域
def button_clear(self):
self.ui.textBrowser_print.append("清除顯示區(qū)域")
self.ui.textBrowser_print.clear()
self.ui.textBrowser_detect.clear()
# 窗口居中
def center(self):
qr = self.frameGeometry()
cp = QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
# 設(shè)置窗口大小
self.move(qr.topLeft())
# 關(guān)閉事件
def closeEvent(self, event) -> None:
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?",
QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
if self.cap.isOpened():
self.cap.release()
if self.timer_video.isActive():
self.timer_video.stop()
if self.timer_photo.isActive():
self.timer_photo.stop()
event.accept()
else:
event.ignore()
from qt_material import apply_stylesheet
if __name__ == '__main__':
# QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) # 自適應(yīng)分辨率
app = QtWidgets.QApplication(sys.argv)
current_ui = UI_Logic_Window()
current_ui.show()
extra = {
# Font
'font_family': 'Adobe 華文仿宋 Std L',
'font_size': 14,
}
apply_stylesheet(app, theme='light_blue.xml',extra=extra)
# apply_stylesheet(app, theme='dark_teal.xml')
sys.exit(app.exec_())
7 使用
7.1. 注意在使用時需要先選擇權(quán)重
需要先選擇權(quán)重
7.2.圖片、視頻、攝像頭實時檢測
圖片檢測效果圖
視頻檢測效果圖
攝像頭實時檢測效果圖
7.3.支持視頻暫停、結(jié)束等功能
文章來源地址http://www.zghlxwxcb.cn/news/detail-441443.html
到了這里,關(guān)于佩戴口罩檢測從零開始使用YOLOv5+PyQt5+OpenCV+爬蟲實現(xiàn)(支持圖片、視頻、攝像頭實時檢測,UI美化升級)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!