用pyqt5和opencv实现摄像头数据的实时获取和灰度化处理

前言

好久没有写博客了,最近一直在学习pyqt,感觉Qt的功能比WinForm的强大了许多,再加上原来学习了一点opencv,就想着使用qt显示图片,opencv读取摄像头,顺便再做一个实时灰度化处理。这里使用了qt的QThread多线程处理,详细代码可以参考: Github.

UI模块

这里的UI设计我是是由pyqt自带的designer设计其设计的,直接是拖拉拽就完事了,然后ui文件生成py文件。生成的代码如下:

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'C:\Users\Administrator\PycharmProjects\-\QT显示图片\qt使用摄像头.ui'
#
# Created by: PyQt5 UI code generator 5.13.2
#
# WARNING! All changes made in this file will be lost!


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
  def setupUi(self, Form):
      Form.setObjectName("Form")
      Form.resize(790, 463)
      self.layoutWidget = QtWidgets.QWidget(Form)
      self.layoutWidget.setGeometry(QtCore.QRect(20, 10, 751, 331))
      self.layoutWidget.setObjectName("layoutWidget")
      self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget)
      self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
      self.horizontalLayout.setObjectName("horizontalLayout")
      self.lbl_sourceImage = QtWidgets.QLabel(self.layoutWidget)
      self.lbl_sourceImage.setObjectName("lbl_sourceImage")
      self.horizontalLayout.addWidget(self.lbl_sourceImage)
      self.lbl_dealedImage = QtWidgets.QLabel(self.layoutWidget)
      self.lbl_dealedImage.setObjectName("lbl_dealedImage")
      self.horizontalLayout.addWidget(self.lbl_dealedImage)
      self.layoutWidget1 = QtWidgets.QWidget(Form)
      self.layoutWidget1.setGeometry(QtCore.QRect(180, 390, 401, 25))
      self.layoutWidget1.setObjectName("layoutWidget1")
      self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget1)
      self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
      self.horizontalLayout_2.setObjectName("horizontalLayout_2")
      self.btn_open = QtWidgets.QPushButton(self.layoutWidget1)
      self.btn_open.setObjectName("btn_open")
      self.horizontalLayout_2.addWidget(self.btn_open)
      self.btn_close = QtWidgets.QPushButton(self.layoutWidget1)
      self.btn_close.setObjectName("btn_close")
      self.horizontalLayout_2.addWidget(self.btn_close)
      self.btn_openGarry = QtWidgets.QPushButton(self.layoutWidget1)
      self.btn_openGarry.setObjectName("btn_openGarry")
      self.horizontalLayout_2.addWidget(self.btn_openGarry)

      self.retranslateUi(Form)
      QtCore.QMetaObject.connectSlotsByName(Form)

  def retranslateUi(self, Form):
      _translate = QtCore.QCoreApplication.translate
      Form.setWindowTitle(_translate("Form", "Form"))
      self.lbl_sourceImage.setText(_translate("Form", ""))
      self.lbl_dealedImage.setText(_translate("Form", ""))
      self.btn_open.setText(_translate("Form", "打开摄像头"))
      self.btn_close.setText(_translate("Form", "关闭摄像头"))
      self.btn_openGarry.setText(_translate("Form", "打开灰度图"))

MainWindow模块

然后就是主界面的设计了,这里我使用了后端线程处理数据,然后信号通知主界面做相应的显示工作。具体代码如下:

import sys
from PyQt5.QtCore import Qt, QThread, pyqtSignal,QDateTime
from PyQt5.QtGui import QIcon,QImage,QPixmap
from PyQt5.QtWidgets import QDialog, QApplication, QWidget,QMessageBox,QMenu,QLabel,QGraphicsItem
from QT显示图片.qt使用摄像头 import Ui_Form
import cv2 as cv

#创建新线程
class DealImgThread(QThread):
   #设置一个原图像信号,一个灰度转换后的图
   singoutSource=pyqtSignal(QImage)
   singoutGarry=pyqtSignal(QImage)
   def __init__(self,parent=None):
       super(DealImgThread,self).__init__(parent)
       self.cv=cv
       self.cvCap=self.cv.VideoCapture(0)
       #设置灰度转换是否打开
       self.garryIsOpen=False
       self.threadIsOpen=True

   def openGarry(self):
       if(self.garryIsOpen==False):
           self.garryIsOpen=True

   # def start(self):
   #     self.threadIsOpen=True

   def end(self):
       if(self.threadIsOpen):
           self.threadIsOpen=False


   def run(self):
       self.threadIsOpen=True
       while self.threadIsOpen:
           ret,frame=self.cvCap.read()
           frame=self.cv.flip(frame,1)
           h,w,ch=frame.shape
           bytesPerLine=3*w
           qImg=QImage(frame.data, w, h, bytesPerLine,QImage.Format_RGB888).rgbSwapped()
           self.singoutSource.emit(qImg)
           #打开灰度转换功能
           if(self.garryIsOpen==True):
               #这里不太知道怎么把QImage转换为灰度图,就用了个折中的办法,先转化为灰度图,再转化为三通道的BGR图
               garryImg = self.cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
               garryImg=self.cv.cvtColor(garryImg, cv.COLOR_GRAY2BGR)
               gImg = QImage(garryImg.data, w, h, bytesPerLine, QImage.Format_RGB888).rgbSwapped()
               self.singoutGarry.emit(gImg)
           self.cv.waitKey(20)


class MianWindow(QWidget):
   def __init__(self,parent=None):
       super(MianWindow,self).__init__(parent)
       self.ui=Ui_Form()
       self.ui.setupUi(self)
       self.cvThread=DealImgThread()
       #原始图片信号连接
       self.cvThread.singoutSource.connect(self.showImg)
       #灰度转换后信号连接
       self.cvThread.singoutGarry.connect(self.showGarry)
       self.ui.btn_open.clicked.connect(self.openScarme)
       self.ui.btn_openGarry.clicked.connect(self.cvThread.openGarry)
       self.ui.btn_close.clicked.connect(self.cvThread.end)
       #一般图标在初始化的时候就要设置,要不然后面的显示不了
       self.setWindowIcon(QIcon(r'ico\Qt.ico'))
       #线程结束通知
       self.cvThread.finished.connect(self.CameraThreadIsClose)

   def showImg(self,img):
       #先取的原来lable的尺寸,然后再转换一下
       temp = self.ui.lbl_sourceImage.size()
       img=img.scaled(temp)
       self.ui.lbl_sourceImage.setPixmap(QPixmap.fromImage(img))
       # now = QDateTime.currentDateTime().toString('hh:mm:ss.zzz')
       # print(now + ':原图触发!')

   def CameraThreadIsClose(self):
       self.msgBox=QMessageBox()
       self.msgBox.setWindowIcon(QIcon(r'ico\Qt.ico'))
       self.msgBox.information(self,'信息提示框','线程执行结束!!!',buttons=QMessageBox.Yes)

   def openScarme(self):
       if(self.cvThread.isRunning()==False):
           self.cvThread.start()

   def showGarry(self,img):
       temp = self.ui.lbl_dealedImage.size()
       img=img.scaled(temp)
       self.ui.lbl_dealedImage.setPixmap(QPixmap.fromImage(img))
       # now=QDateTime.currentDateTime().toString('hh:mm:ss.zzz')
       # print(now+':Garry触发!')


if __name__=='__main__':
   app = QApplication(sys.argv)
   form = MianWindow()
   form.show()
   sys.exit(app.exec_())

后记

这次仔细想了一下Qthread,感觉它的新线程可以直接通过pyqtSignal与主线程通讯,这不是违背了那个新线程无法直接控制主线程创建的控件这个规律吗?(因为C#的就是这样说的)。后来想想可能是Qt对QThread进行了高层封装吧,已经帮我们解决了那个问题。还有我发现再主线程调用QThread里面的sleep和wait都是针对主线程操作的,并没有影响所创建的线程。我的理解是线程任务一旦run起来,就没有办法人为干涉,主线程和子线程是相互独立的,就和我们主线程里面运行的一个任务,我们只能在代码中修改,不能再运行时候修改。也许这和子线程一个道理,所以我们就得用一些bool量来进行子线程任务的开始结束操作了。附一张运行时的照片:(照片里的人属实帅!)在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值