当前位置:   article > 正文

可视可交互!在全志H618上用OpenCV读取图像显示到PyQt5窗口上

可视可交互!在全志H618上用OpenCV读取图像显示到PyQt5窗口上

OpenCV能够处理图像、视频、深度图像等各种类型的视觉数据,在某些情况下,尽管OpenCV可以显示窗口,但PyQt5可能更适合用于创建复杂的交互式应用程序,而自带GPU的H618就成为了这些图像显示的最佳载体。

这里分享一个代码,功能是使用图像处理库opencv从摄像头获取数据,缩放后从pyqt5的窗口中显示出来。

746beff04ec85b26fe404e0475d7bfb4237f765b_2_650x500.png

安装opencv

sudo pip3 install opencv-python

创建一个pyqt5窗口

1. 用Qt Designer画个窗口
这里我在电脑上使用designer软件,创建一个Main Window类型窗体。从左边组件栏中拖出一个label放到窗口中间。

点一下放在窗口中的label,在软件右下角的属性编辑器里可以设置很多东西,这里就不细介绍了。这里我是设置了QFrame启用了边框,QLabel中的texte属性控制显示的文本,QLabel中的alignment属性控制文本对齐方式。

然后保存为.ui结尾的文件

61ad9a89c29d3693824917f8ea700666c2c72e4c_2_501x375.png

2. 将designer绘制的ui文件转化为py文件

python3 -m PyQt5.uic.pyuic ui_main.ui -o ui_main.py

3. 编写main.py程序,调用刚刚画的窗口进行显示
先把刚刚的ui_main.py以及一些qt库给import进来

  1. from ui_main import Ui_MainWindow
  2. import PyQt5
  3. from PyQt5 import QtCore, QtGui, QtWidgets
  4. from PyQt5.QtCore import *
  5. from PyQt5.QtGui import *
  6. # 修正qt的plugin路径,因为某些程序(cv2)会将其改到其他路径
  7. import os
  8. os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.dirname(PyQt5.__file__)

放入一点辅助代码,一个是为了实现从远程命令行运行qt程序显示到桌面上,一个是为了在命令行下可以按ctrl+c快捷键来强制退出qt程序

  1. #【可选代码】允许远程运行
  2. import os
  3. os.environ["DISPLAY"] = ":0.0"
  4. #【建议代码】允许终端通过ctrl+c中断窗口,方便调试
  5. import signal
  6. signal.signal(signal.SIGINT, signal.SIG_DFL)
  7. timer = QtCore.QTimer()
  8. timer.start(100) # You may change this if you wish.
  9. timer.timeout.connect(lambda: None) # Let the interpreter run each 100 ms

定义窗口类,重写窗口的一些触发事件。这里我修改了鼠标点击后会被自动调用的mousePressEvent和窗口绘制时会被调用的paintEvent

  1. class WINDOW(QtWidgets.QMainWindow):
  2. def mousePressEvent(self, event):
  3. # 被左键点击后退出本程序
  4. if event.button() == Qt.LeftButton:
  5. self.close()
  6. exit(-1)
  7. def paintEvent(self,event):
  8. # 修改label的大小和位置
  9. new_width = int(window.width()/10*8)
  10. new_height = int(window.height()/10*8)
  11. lab_x = int((window.width() - new_width) / 2)
  12. lab_y = int((window.height() - new_height) / 2)
  13. ui.label.setGeometry( lab_x, lab_y, new_width, new_height)

加上调用函数进行显示的部分,这个显示pyqt5窗口的基本程序就完成了

  1. # 初始化窗口
  2. import sys
  3. app = QtWidgets.QApplication(sys.argv)
  4. window = WINDOW()
  5. ui = Ui_MainWindow()
  6. ui.setupUi(window)
  7. window.showFullScreen() #全屏显示
  8. # window.show() #按绘制时的尺寸显示
  9. sys.exit(app.exec_())

在核桃派lcd屏上的效果展示

ffe8ec8611e71a48fa612edcc571691d329523a2_2_690x422.png

opencv怎么读取摄像头

调用头文件,opencv的头文件只需要这一个

import cv2

打开摄像头,其中传入的参数1是摄像头编号,一般是从0开始往后排

cap = cv2.VideoCapture(1)

从摄像头读取一帧图像,ret是读取状态,frame是图像数据

ret, frame = cap.read()

怎么把opencv的图像数据显示到qt的label

cap.read函数读到的是bgr格式的,需要先转为rgb格式

rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

将图像转为Qt中用来表示图像的QImage

  1. h, w, ch = rgbImage.shape
  2. qtImage = QImage(rgbImage.data, h, w, ch*w, QtGui.QImage.Format_RGB888)

label的setPixmap方法可以图像数据覆盖label

label.setPixmap(QPixmap.fromImage(qtImage))

线程,信号与槽

我们这里使用qt自带的多线程功能,他的使用很简单,只需要创建一个类并继承自QThread, 然后将要运行的东西写到类里的run方法下面。实例化一个对象后,调用start方法即可创建新线程

  1. class Work(QThread):
  2. def run(self):
  3. pass
  4. work = Work()
  5. work.start()

直接在线程内调用函数去修改qt窗口的内容,不能满足线程安全。

我们需要创建一个信号,把修改qt窗口的语句写到一个槽内,连接他们,在想修改窗口时发出信号,让qt内部去调度,防止跟其他qt内部的线程发生冲突。

因为我们这个线程类继承自QThread,所以可以在类内定义信号。只需要实例化一个pyqtSignal对象即可,调用时括号内的参数决定了槽函数必须有什么类型的参数,以及发送信号时需要传入什么参数。

  1. ```

signal_update_label = pyqtSignal( QPixmap)

  1. 槽函数就是随便定义一个函数,只要函数参数跟信号一样就行。
  2. ```
  3. label:QLabel
  4. def sloat_update_label( self, pixmap:QPixmap):
  5. self.label.setPixmap(pixmap)

连接信号与槽,使用connect方法即可

self.signal_update_label.connect(self.sloat_update_label)

使用emit方法即可发送信号,qt内部会进行调度,将所有连接到本信号的函数都调出来运行,并将参数传给他们。这是qt实现线程安全的重要机制。

self.signal_update_label.emit(QPixmap.fromImage(qtImage))

最终代码

  1. import cv2
  2. from ui_main import Ui_MainWindow
  3. import PyQt5
  4. from PyQt5.QtCore import *
  5. from PyQt5.QtGui import *
  6. from PyQt5.QtWidgets import *
  7. # 修正qt的plugin路径,因为某些程序(cv2)会将其改到其他路径
  8. import os
  9. os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = os.path.dirname(PyQt5.__file__)
  10. #【可选代码】允许Thonny远程运行
  11. import os
  12. os.environ["DISPLAY"] = ":0.0"
  13. #【建议代码】允许终端通过ctrl+c中断窗口,方便调试
  14. import signal
  15. signal.signal(signal.SIGINT, signal.SIG_DFL)
  16. timer = QTimer()
  17. timer.start(100) # You may change this if you wish.
  18. timer.timeout.connect(lambda: None) # Let the interpreter run each 100 ms
  19. # 线程类
  20. class Work(QThread):
  21. signal_update_label = pyqtSignal(QPixmap)
  22. label:QLabel
  23. def sloat_update_label( self, pixmap:QPixmap):
  24. self.label.setPixmap(pixmap)
  25. def run(self):
  26. print("label.width()=", self.label.width())
  27. print("label.height()=", self.label.height())
  28. self.signal_update_label.connect(self.sloat_update_label)
  29. cap = cv2.VideoCapture(1)
  30. while True:
  31. ret, frame = cap.read()
  32. if ret:
  33. # 颜色转换
  34. rgbImage = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
  35. # 按比例缩放
  36. h, w, ch = rgbImage.shape
  37. aspect_ratio = w / h
  38. new_width = self.label.width()
  39. new_height = int(new_width / aspect_ratio)
  40. if new_height > self.label.height():
  41. new_height = self.label.height()
  42. new_width = int(new_height * aspect_ratio)
  43. rgbImage = cv2.resize(rgbImage, (new_width, new_height))
  44. # 显示到label
  45. bytesPerLine = ch * new_width
  46. self.signal_update_label.emit(QPixmap.fromImage(QImage(rgbImage.data, new_width, new_height, bytesPerLine, QImage.Format_RGB888)))
  47. else :
  48. print("cap read error")
  49. return
  50. class WINDOW(QMainWindow):
  51. def mousePressEvent(self, event):
  52. if event.button() == Qt.LeftButton:
  53. self.close()
  54. def paintEvent(self,event):
  55. new_width = int(window.width()/10*8)
  56. new_height = int(window.height()/10*8)
  57. lab_x = int((window.width() - new_width) / 2)
  58. lab_y = int((window.height() - new_height) / 2)
  59. ui.label.setGeometry( lab_x, lab_y, new_width, new_height)
  60. import sys
  61. app = QApplication(sys.argv)
  62. window = WINDOW()
  63. ui = Ui_MainWindow()
  64. ui.setupUi(window)
  65. window.showFullScreen() #全屏显示
  66. # window.show() #按绘制时的尺寸显示
  67. # 创建读取摄像头并显示的线程
  68. work = Work()
  69. work.label = ui.label
  70. work.start()
  71. sys.exit(app.exec_())

c547df99a7f823abfb33c4551ec009af853f6ea2_2_479x500.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/39338
推荐阅读
相关标签
  

闽ICP备14008679号