【搞事情】利用PyQt爲目標檢測SSD300添加界面(三)

【原創文章】歡迎正常授權轉載(聯繫作者)
【反對惡意複製粘貼,如有發現必維權】
【微信公衆號原文傳送門


軟件最終效果


系列文章鏈接
1. 問題總體描述及三種方案;
2. 方案1詳解(附代碼);

這篇文章將詳細介紹方案2的實現(代碼獲取見文章末尾)。

老規矩先看看下載好的代碼文件構成。
在這裏插入圖片描述
其中 “ssd” 文件夾中是SSD檢測的關鍵文件,關於這部分之前寫文章了,裏面詳細介紹瞭如何訓練一個屬於自己的SSD300,有代碼、有預訓練的權值文件,不清楚的請移步這裏


需求分析

先簡單做一個需求分析,看看我們要怎樣實現。
在這裏插入圖片描述
首先,要解決的問題:電腦性能太好(如果你有的話,土豪交朋友嗎?),導致使用幀循環的方法時,視頻會被“加速”播放,我們想讓它按正常的速度播放。
之後,我們先了解一個概念FPS(每秒幀數),對應的可以計算出一幀應該顯示幾秒。例如:

(FPS: Frames Per Second)
FPS = 20  
(TPF: times Per Frame,這個是我自己造的,哈哈哈哈)    
TPF = 1 / 20 (單位:s)

只要控制 讀取圖像–>檢測–>顯示 的節奏,讓顯示圖像的時間與視頻的 FPS 對應,那麼看起來視頻就是正常播放的啦。

通過 計時器(QTimer) 可以很好的實現這個需求,計時器時間一到就會發送 “超時信號 給對應的槽函數(用於檢測顯示),槽函數收到信號後就開始執行,槽函數執行結束後等待再一次被調用。

最後一個需求是要通過界面的按鈕來控制 開始結束 ,這個就很簡單,構造函數裏實例化計時器,然後在對應的槽函數裏 開始停止 計時器就可以了。


代碼分析

下面開始詳細介紹代碼。主要介紹下面幾個函數,其他的函數在上篇文章中已經講過或者比較簡單,就不介紹了。(偷個懶,嘿嘿嘿)

1. 構造函數

def __init__(self, parent=None):
"""
  ...上一篇文章已經介紹過...
  ...這裏就不寫了...
  ...這裏說點不一樣的...
"""
# 視頻文件路徑
    self.camera_index = None    # 用於保存視頻文件路徑
    self.FPS = None             # 用於保存視頻文件FPS# 初始化計時器
    self.timer = QTimer(self)  # 更新計時器
    self.timer.timeout.connect(self.timer_update) # 超時信號連接對應的槽函數

在構造函數中初始化必要的變量,同時實例化一個 QTimer計時器 ,並將 “超時”信號 與對應的槽函數綁定起來。代碼中self.timer_update爲計時器超時信號的槽函數,這裏作爲參數傳入不可以加’()’,後面會詳細說。

2. ‘開始’點擊槽函數

def on_pushButton_start_clicked(self):
    """
    Slot documentation goes here.
    """
    # TODO: not implemented yet
    # 獲取數據流
    self.cap = cv2.VideoCapture(self.camera_index)
    if self.cap.isOpened():
        # 獲取視頻的FPS
        # FPS ---- 每秒多少幀
        self.FPS = self.cap.get(cv2.CAP_PROP_FPS)
        if isinstance(self.FPS, float):         # 正常獲取的FPS是float
            self.FPS = int(self.FPS)            # 如果正確獲取FPS就保存在變量
        else:
            self.FPS = 20                       # 沒正確獲取則設爲 20幀/s# 計時器開始計時
        # 計時器的參數爲 ms 爲了正常速度播放,計時器的參數計算爲 1/FPS * 1000 = 1000/FPS
        self.timer.start(int(1000/self.FPS))# 鎖定開始按鈕
        self.pushButton_start.setEnabled(False)
    else:
        QMessageBox.warning(self, '數據流打開警告', '數據流打開錯誤!\n請重新嘗試。')

該函數的主要功能是:打開視頻數據流獲取視頻流的FPS計時器開始計時。需要注意的是計時器的時間設置問題。
函數self.timer.start(時間,單位:ms)的參數與 1/FPS 之間還需要乘以 1000 ,同時這個值還應該考慮到圖像預處理以及檢測的時間,適當的減小這個值,如果不考慮的話可能會出現“慢速播放”。

3. ‘結束’點擊槽函數

該函數比較簡單,主要功能是停止計時器的功能,同時爲下一次檢測做準備。

def on_pushButton_end_clicked(self):
    """
    Slot documentation goes here.
    """
    # TODO: not implemented yet
    # 重設
    self.resst_detector()    # 詳細代碼在下方
    # 清除顯示
    self.textEdit.clear()
    
def resst_detector(self):
    """
    重設檢測器,爲下一次檢測準備
    :return:
    """
    # 釋放攝像頭
    if hasattr(self, 'cap'):
        self.cap.release()
        del self.cap
    # 釋放‘開始’按鈕
    self.pushButton_start.setEnabled(True)
    # 顯示空白圖片
    self.show_img(self.img_none)
    # 停止計時器
    self.timer.stop()

4. 計時器槽函數

def timer_update(self):
    """
    計時器槽函數
    :return:
    """
    if self.cap.isOpened():
        # 讀取圖像
        ret, self.img_scr = self.cap.read()
        # 如果視頻讀取完畢
        if not ret:
            # 計時器停止計時
            self.timer.stop()
            # 對話框提示
            QMessageBox.information(self, '播放提示', '視頻已播放完畢!')
            # 釋放攝像頭
            if hasattr(self, 'cap'):
                self.cap.release()
                del self.cap
            # 釋放‘開始’按鈕
            self.pushButton_start.setEnabled(True)# 預處理圖片
        # 轉爲RGB
        self.img_scr = cv2.cvtColor(self.img_scr, cv2.COLOR_BGR2RGB)# 檢測
        self.preds = self.ssd.Predict(self.img_scr)
        # 過濾
        self.preds = self.filter(self.preds, inclued_class=self.include_class)
        self.img_scr = self.draw_img(self.img_scr, self.preds)
​
        h, w = self.img_scr.shape[:2]
        self.text = self.decode_preds(self.preds, w=w, h=h)
        self.textEdit.setText(self.text)# 顯示圖像
        self.show_img(self.img_scr)# 響應UI
        QApplication.processEvents()
    else:
        self.textEdit.setText('數據流未打開!!!\n請檢查')
        self.resst_detector()    # 沒有打開就重設一下

看過上篇文章的是不是很熟悉,沒錯。基本就是幀循環裏面的東西。

5. ‘文件打開’槽函數

爲了方便更換被檢測視頻,創建了一個按鈕用於打開文件對話框選擇文件,基本功能就是爲了實現給變量self.camera_index賦值。

def on_pushButton_open_clicked(self):
    """
    Slot documentation goes here.
    """
    # TODO: not implemented yet
    # 打開文件對話框
    path = QFileDialog.getOpenFileName(self, '打開待檢測視頻', './', '*.avi;;*.mp4;;AllFile(*.*)', '')
    if path[0] != '':    # 點‘取消’,path[0]的值會爲‘’
        path = os.path.normpath(os.path.abspath(path[0]))
        self.camera_index = path
        self.textEdit.setText('{}已選中!'.format(path))
    else:
        self.textEdit.setText('當前未選中任何文件')
​預告:方案3應該下週整理完畢並更新

關注下方公衆號,回覆關鍵字即可獲取下載地址。
  • 本文配套源代碼下載地址::

    回覆“SSD界面2”獲取。


如果你讀後有收穫,歡迎關注我的微信公衆號
上面有更多完全免費教程,我也會不定期更新
ღ ღ ღ 打開微信掃描下方二維碼關注 ღ ღ ღ

在這裏插入圖片描述

發佈了13 篇原創文章 · 獲贊 24 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章