天天看點

PyQt5+QtDesigner編寫攝像頭界面程式(四)——用OpenCV和time讀取和設定攝像頭

文章目錄

    • 前言
    • 攝像頭的讀取和顯示
    • 圖像顯示的顔色控制
    • 攝像頭參數設定
    • 儲存圖檔或視訊
    • 退出程式

前言

上一節我們初步完成了主程式的結構設計,隻要将每一個回調函數編寫完成就可實作最終的功能了,本節來詳細介紹這些回調函數的内容。由于設計到的回調函數較多,我們按功能将其分類,分别為攝像頭圖檔的讀取和顯示、圖像顔色控制、攝像頭參數設定、視訊檔案儲存、退出程式。

攝像頭的讀取和顯示

從筆記本自帶的攝像頭中讀取圖檔,并将其顯示在我們命名為DispLb的label中。這一過程涉及到的‘開始’和‘暫停’兩個按鈕,我們預期的功能是當滑鼠單擊‘開始’按鈕時,程式就不斷地從攝像頭讀取圖像,并将其實時顯示在DispLb中,而一旦我們點選‘暫停’按鈕,就停止從攝像頭讀取圖像,DispLb中顯示的圖像也不再改變。當然,還涉及到一些其它控件的‘enable’或‘disable’。具體涉及到的回調函數包括‘開始’按鈕的回調函數:StartCamera();‘暫停’按鈕的回調函數: StopCamera();另外,由于我們是通過time函數來控制圖像的重複讀取,是以還需要它的回調函數

TimerOutFun();将圖檔通過DispLb顯示出來的函數:DispImg()。這些函數的内容如下所示。

def StartCamera(self):
        self.ShowBt.setEnabled(False)
        self.StopBt.setEnabled(True)
        self.RecordBt.setEnabled(True)
        self.GrayImgCkB.setEnabled(True)
        if self.GrayImgCkB.isChecked()==0:
            self.RedColorSld.setEnabled(True)
            self.RedColorSpB.setEnabled(True)
            self.GreenColorSld.setEnabled(True)
            self.GreenColorSpB.setEnabled(True)
            self.BlueColorSld.setEnabled(True)
            self.BlueColorSpB.setEnabled(True)
        self.ExpTimeSld.setEnabled(True)
        self.ExpTimeSpB.setEnabled(True)
        self.GainSld.setEnabled(True)
        self.GainSpB.setEnabled(True)
        self.BrightSld.setEnabled(True)
        self.BrightSpB.setEnabled(True)
        self.ContrastSld.setEnabled(True)
        self.ContrastSpB.setEnabled(True)
        self.RecordBt.setText('錄像')

        self.Timer.start(1)
        self.timelb=time.clock()
           

StartCamera這個函數的主要功能就是它最後兩行代碼,

self.Timer.start(1)

用來啟動計時器,計時周期為1ms,即每隔1ms程式會自動調用一次TimerOutFun。我們将圖像的具體讀取和顯示放到TimerOutFun中,這樣就可以實作圖像的實時讀取和顯示了。StartCamera的前面那些代碼是用來啟用相機參數設定和‘暫停’按鈕等控件,同時,停用‘開始’按鈕。

def StopCamera(self):
        if self.StopBt.text()=='暫停':
            self.StopBt.setText('繼續')
            self.RecordBt.setText('儲存')
            self.Timer.stop()
        elif self.StopBt.text()=='繼續':
            self.StopBt.setText('暫停')
            self.RecordBt.setText('錄像')
            self.Timer.start(1)
           

由于我們的‘暫停’和‘繼續’功能是複用的同一個按鈕,是以StopCamera函數要分成兩種情況。如果我們點選時按鈕顯示的是‘暫停’,那麼就停止計時器,同時将本按鈕的顯示改成‘繼續’。另外,我們後面的存儲按鈕涉及兩種功能,即暫停狀态下儲存圖檔,實時顯示狀态下儲存錄像,是以這裡也需要更改‘儲存’按鈕的顯示字元。

def TimerOutFun(self):
        success,img=self.camera.read()
        if success:
            self.Image = self.ColorAdjust(img)
            self.DispImg()
            self.Image_num+=1
            if self.RecordFlag:
                self.video_writer.write(img)
            if self.Image_num%10==9:
                frame_rate=10/(time.clock()-self.timelb)
                self.FmRateLCD.display(frame_rate)
                self.timelb=time.clock()
                #size=img.shape
                self.ImgWidthLCD.display(self.camera.get(3))
                self.ImgHeightLCD.display(self.camera.get(4))
        else:
            self.MsgTE.clear()
            self.MsgTE.setPlainText('Image obtaining failed.')
           

我們将需要對每一幀圖像都重複執行的操作放在函數TimerOutFun中,該函數的功能為:從攝像頭讀取圖像,調用ColorAdjust函數來調整圖檔的顔色,調用DispImg函數來顯示圖像,儲存視訊,擷取并顯示攝像頭幀頻和圖像尺寸。從攝像頭讀取圖像可以通過調用OpenCV庫的read函數實作。儲存視訊是通過OpenCV的VideoWriter函數實作的,擷取并計算幀頻則是通過time.clock()函數擷取代碼執行的時間實作。

def DispImg(self):
        if self.GrayImgCkB.isChecked():
            img = cv2.cvtColor(self.Image, cv2.COLOR_BGR2GRAY)
        else:
            img = cv2.cvtColor(self.Image, cv2.COLOR_BGR2RGB)
        qimg = qimage2ndarray.array2qimage(img)
        self.DispLb.setPixmap(QPixmap(qimg))
        self.DispLb.show()
           

從攝像頭擷取的圖像在DispLb中顯示之前,需要對其格式進行轉換。Label可接受的圖像類型為QPixmap,且其顔色通道依次為R、G、B;而通過OpenCV的read函數讀取的圖像是二維矩陣格式的,且顔色通道依次為B、G、R,是以需要先調用cvtColor函數對矩陣的顔色進行調整,然後再調用array2qimage函數将其轉為QImage格式,再通過QPixmap函數轉為QPixmap格式進行顯示。

圖像顯示的顔色控制

涉及到圖像顯示顔色控制的主要是ColorFm中的CheckBox:GrayImgCkB和R、G、B三個顔色通道對應的Slider和SpinBox。相應的回調函數為SetGray、SetR、SetG、SetB、ColorAdjust。這些函數的代碼如下所示。

def SetGray(self):
        if self.GrayImgCkB.isChecked():
            self.RedColorSld.setEnabled(False)
            self.RedColorSpB.setEnabled(False)
            self.GreenColorSld.setEnabled(False)
            self.GreenColorSpB.setEnabled(False)
            self.BlueColorSld.setEnabled(False)
            self.BlueColorSpB.setEnabled(False)
        else:
            self.RedColorSld.setEnabled(True)
            self.RedColorSpB.setEnabled(True)
            self.GreenColorSld.setEnabled(True)
            self.GreenColorSpB.setEnabled(True)
            self.BlueColorSld.setEnabled(True)
            self.BlueColorSpB.setEnabled(True)
           

SetGray函數所實作的功能是對三個顔色通道的slider和spinbox啟用與否的控制,若為真,則将圖像轉為灰階圖,自然就不存在三個顔色通道,是以相應的控件都要‘disable’ ,否則就要将它們‘enable’。至于實作灰階轉換的代碼則是在DispImg函數中實作的。

def SetR(self):
        R=self.RedColorSld.value()
        self.R=R/255
           
def SetG(self):
        G=self.GreenColorSld.value()
        self.G=G/255
           
def SetB(self):
        B=self.BlueColorSld.value()
        self.B=B/255
           
def ColorAdjust(self,img):
        try:
            B=img[:,:,0]
            G=img[:,:,1]
            R=img[:,:,2]
            B=B*self.B
            G=G*self.G
            R=R*self.R
            img1=img
            img1[:,:,0]=B
            img1[:,:,1]=G
            img1[:,:,2]=R
            return img1
        except Exception as e:
            self.MsgTE.setPlainText(str(e))
           

圖像顯示顔色的控制則是通過以上四個函數實作的。前三個函數分别定義三個顔色通道的強度縮小系數,第四個函數将函數對相應的顔色強度進行縮小。

攝像頭參數設定

攝像頭的參數通常包括幀頻、曝光時間、AOI、增益系數等,不同的攝像頭通常會給出不同的可調節的參數和參數的調節範圍。本例中,可供調節的參數包括曝光、增益、亮度、對比度四個,我們通過控制台上的四組slider/spinbox來控制對應的參數,相應的回調函數分别為SetExposure、SetGain、SetBrightness、SetContrast,具體的函數代碼如下所示。

def SetExposure(self):
        try:
            exposure_time_toset=self.ExpTimeSld.value()
            self.camera.set(15,exposure_time_toset)
            self.MsgTE.setPlainText('The exposure time is set to '+str(self.camera.get(15)))
        except Exception as e:
            self.MsgTE.setPlainText(str(e))
           

以SetExposure函數為例,代碼内容為:首先通過

self.ExpTimeSld.value()

獲得要設定的曝光值,然後調用

self.camera.set(15,exposure_time_toset)

函數來将曝光值賦給攝像頭,改變其曝光參數。最後,通過

self.camera.get(15)

函數獲得設定完成後相機的實際曝光值,并通過MsgTE顯示出來。除此之外,我們還使用了try…Except…結構,若設定的參數格式或範圍不符合攝像頭的要求,通常攝像頭要麼報錯,要麼忽視該該指令,曝光參數維持原值。是以如果我們在設定完曝光參數後,MsgTE中顯示的值與我們的設定值一緻,說明設定成功,如果不一緻,則說明我們設定的值超出攝像頭的參數範圍。我們可以先将Slider和SpinBox的範圍設定得比較大,例如-100到+100,然後反複設定參數并觀察相機曝光參數的改變,進而試探出相機的曝光參數範圍。對增益、亮度、對比度的設定與曝光類似,就不再一一介紹。

def SetGain(self):
        gain_toset=self.GainSld.value()
        try:
            self.camera.set(14,gain_toset)
            self.MsgTE.setPlainText('The gain is set to '+str(self.camera.get(14)))
        except Exception as e:
            self.MsgTE.setPlainText(str(e))
           
def SetBrightness(self):
        brightness_toset=self.BrightSld.value()
        try:
            self.camera.set(10,brightness_toset)
            self.MsgTE.setPlainText('The brightness is set to ' + str(self.camera.get(10)))
        except Exception as e:
            self.MsgTE.setPlainText(str(e))
           
def SetContrast(self):
        contrast_toset=self.ContrastSld.value()
        try:
            self.camera.set(11,contrast_toset)
            self.MsgTE.setPlainText('The contrast is set to ' + str(self.camera.get(11)))
        except Exception as e:
            self.MsgTE.setPlainText(str(e))
           

儲存圖檔或視訊

這部分包括兩個功能,若圖像顯示被停止了,點選按鈕後就儲存圖檔;若圖像在實時顯示,點選按鈕後就開始錄像,再次點選停止錄像。設計到的控件有兩個,一個是設定儲存檔案路徑的按鈕FilePathBt,一個是儲存圖檔或視訊的RecordBt,前者的回調函數為SetFilePath(),後者的回調函數為RecordCamera()。具體的代碼如下所示。

def SetFilePath(self):
        dirname = QFileDialog.getExistingDirectory(self, "浏覽", '.')
        if dirname:
            self.FilePathLE.setText(dirname)
            self.RecordPath=dirname+'/'
           

本函數通過調用QFileDialog.getExistingDirectory函數彈出對話框,讓使用者自己選擇路徑,并将選擇好的路徑顯示在FilePathLE中。預設的路徑為‘D:/Python/PyQt/’,這個是在上一節介紹的PrepParameters()函數中設定好的。

def RecordCamera(self):
        tag=self.RecordBt.text()
        if tag=='儲存':
            try:
                image_name=self.RecordPath+'image'+time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))+'.jpg'
                print(image_name)
                cv2.imwrite(image_name, self.Image)
                self.MsgTE.clear()
                self.MsgTE.setPlainText('Image saved.')
            except Exception as e:
                self.MsgTE.clear()
                self.MsgTE.setPlainText(str(e))
        elif tag=='錄像':
            self.RecordBt.setText('停止')

            video_name = self.RecordPath + 'video' + time.strftime('%Y%m%d%H%M%S',time.localtime(time.time())) + '.avi'
            fps = self.FmRateLCD.value()
            size = (self.Image.shape[1],self.Image.shape[0])
            fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')
            self.video_writer = cv2.VideoWriter(video_name, fourcc,self.camera.get(5), size)
            self.RecordFlag=1
            self.MsgTE.setPlainText('Video recording...')
            self.StopBt.setEnabled(False)
            self.ExitBt.setEnabled(False)
        elif tag == '停止':
            self.RecordBt.setText('錄像')
            self.video_writer.release()
            self.RecordFlag = 0
            self.MsgTE.setPlainText('Video saved.')
            self.StopBt.setEnabled(True)
            self.ExitBt.setEnabled(True)
           

本函數實作的功能正如之前所述,分成了儲存圖檔和儲存視訊兩種功能。如果按鈕顯示的是‘儲存’,單擊後就調用OpenCV的imwrite函數儲存一幅圖檔,圖檔的名稱會根據時間自動命名。如果按鈕顯示的是‘錄像’,單擊後就調用OpenCV的VideoWriter函數建立一個視訊檔案,檔案名同樣根據時間自動命名,同時将錄像标簽RecordFlag改為1,這樣在TimerOutFun中每次讀取圖檔後都會将其寫入視訊檔案中。如果按鈕顯示的是‘停止’,單擊後就停止錄像,儲存視訊檔案。還有一些關聯控件的設定,大家可以自行了解。

退出程式

退出按鈕ExitBt的回調函數的代碼如下所示,包括停止計時器、釋放攝像頭、退出幾個功能。

def ExitApp(self):
        self.Timer.Stop()
        self.camera.release()
        self.MsgTE.setPlainText('Exiting the application..')
        QCoreApplication.quit()
           

至此,我們已經完成了程式所有代碼的編寫,點選運作該程式,就可以看到程式正确運作了! 運作的程式界面如下圖所示。完整的代碼檔案可以從這裡下載下傳,回複即可擷取提取碼。

PyQt5+QtDesigner編寫攝像頭界面程式(四)——用OpenCV和time讀取和設定攝像頭

下一節将介紹如何将程式打包成可以在windows環境獨立運作的exe檔案。