• PyQt5学习笔记--摄像头实时视频展示、多线程处理、视频编解码


    目录

    1--前言

    2--基于Qt Designer设计ui文件

    3--视频的编解码操作

    4--完整代码

    5--结果展示

    6--存在的问题

    7--参考


    1--前言

    ① 创建两个线程,主线程为ui线程,子线程用于读取摄像头视频,将处理后的图像帧数据(处理操作可以人为添加)返回到主线程进行可视化;

    ② 子线程向主线程传递视频帧数据集涉及图像的编码操作,主线程接收子线程的数据时涉及图像的解码操作;

    2--基于Qt Designer设计ui文件

    3--视频的编解码操作

    ① 编码操作:

    1. # 图像编码函数
    2. def Encoder(self, img):
    3. retval, buffer = cv2.imencode('.jpg', img)
    4. jpg_as_bytes = base64.b64encode(buffer)
    5. jpg_as_str = jpg_as_bytes.decode('ascii')
    6. json_object = json.dumps({'img_str': jpg_as_str})
    7. return json_object

    注:img 为 opencv 读取图像的格式(默认uint8, BGR);  编码后的数据 json_object 可以直接通过signal.emit(json_object) 进行发送。

    ② 解码操作:

    1. # 图片解码函数
    2. def Decoder(self, img_json):
    3. jpg_as_str = json.loads(img_json)['img_str']
    4. jpg_as_bytes = jpg_as_str.encode('ascii')
    5. jpg_original = base64.b64decode(jpg_as_bytes)
    6. jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
    7. img = cv2.imdecode(jpg_as_np, flags=1)
    8. return img

    注:输入的 img_json 与编码函数生成的 json_object 格式相同;解码生成的 img 可以通过cv2.imshow("window_name", img) cv2.waitKey(0)进行显示。

    4--完整代码

    1. import sys
    2. import time
    3. import json
    4. import cv2
    5. import base64
    6. import numpy as np
    7. from PyQt5.QtCore import *
    8. from PyQt5.QtGui import *
    9. from PyQt5.QtWidgets import *
    10. from PyQt5 import uic
    11. # 子线程1:打开摄像头,返回当前帧图像到主线程
    12. class Open_Cam(QThread):
    13. Open_Cam_signal = pyqtSignal(str) # 接受来自主线程的信号
    14. def __init__(self, main_signal):
    15. super().__init__()
    16. # 信号绑定槽函数
    17. self.Open_Cam_signal.connect(self.Open_Cam)
    18. self.opencam_complete_signal = main_signal # 返回主线程的信号
    19. def Open_Cam(self, Cam):
    20. # 将json字符串转换
    21. Cam_index = json.loads(Cam) # 0
    22. cap = cv2.VideoCapture(Cam_index)
    23. fps = cap.get(cv2.CAP_PROP_FPS)
    24. while True:
    25. ret, self.frame = cap.read()
    26. '''
    27. 这里你可以添加对图像进行处理的操作
    28. '''
    29. json_object = self.Encoder(self.frame) # 对图片进行编码
    30. self.opencam_complete_signal.emit(json_object) # 发送编码到主线程
    31. cv2.waitKey(int((1 / fps) * 1000))
    32. # 图像编码函数
    33. def Encoder(self, img):
    34. retval, buffer = cv2.imencode('.jpg', img)
    35. jpg_as_bytes = base64.b64encode(buffer)
    36. jpg_as_str = jpg_as_bytes.decode('ascii')
    37. json_object = json.dumps({'img_str': jpg_as_str})
    38. return json_object
    39. def run(self):
    40. while True: # 让子线程一直运行,等待主线程(ui线程)下发的任务
    41. print("子线程1正在等待执行....")
    42. time.sleep(2)
    43. class MyWindow(QWidget):
    44. opencam_complete_signal = pyqtSignal(str) # 子线程返回摄像头图像到主线程的信号
    45. def __init__(self):
    46. super().__init__()
    47. self.init_ui()
    48. self.cam_idx = 0
    49. def init_ui(self):
    50. self.ui = uic.loadUi("./Cam.ui") # 加载由Qt Designer设计的ui文件
    51. # 加载控件
    52. self.open_cam_btn = self.ui.pushButton
    53. self.cam_view = self.ui.graphicsView
    54. # 绑定槽函数
    55. self.open_cam_btn.clicked.connect(self.open_cam)
    56. self.opencam_complete_signal.connect(self.view_cam)
    57. self.Opencam_Thread = Open_Cam(self.opencam_complete_signal) # 子线程1
    58. self.Opencam_Thread.start() # 子线程1运行,等待下发任务
    59. def open_cam(self): # 给子线程1发送打开摄像头的信号
    60. self.Opencam_Thread.Open_Cam_signal.emit(json.dumps(self.cam_idx))
    61. def view_cam(self, img_json): # 接受来自子线程1返回的摄像头数据
    62. self.img_json = img_json
    63. img = self.Decoder(self.img_json)
    64. frame = QImage(img, img.shape[1], img.shape[0], img.strides[0], QImage.Format_RGB888).rgbSwapped()
    65. pix = QPixmap.fromImage(frame)
    66. item = QGraphicsPixmapItem(pix) # 创建像素图元
    67. scene = QGraphicsScene() # 创建场景
    68. scene.addItem(item)
    69. self.cam_view.setScene(scene) # 将场景添加至视图
    70. self.cam_view.fitInView(item) # 自适应大小
    71. # 图片解码函数
    72. def Decoder(self, img_json):
    73. jpg_as_str = json.loads(img_json)['img_str']
    74. jpg_as_bytes = jpg_as_str.encode('ascii')
    75. jpg_original = base64.b64decode(jpg_as_bytes)
    76. jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
    77. img = cv2.imdecode(jpg_as_np, flags=1)
    78. return img
    79. if __name__ == '__main__':
    80. app = QApplication(sys.argv) # 创建对象
    81. w = MyWindow()
    82. # 展示窗口
    83. w.ui.show()
    84. # 程序进行循环等待状态
    85. app.exec_()

    5--结果展示

    6--存在的问题

    上述代码存在当关闭ui窗口时,子线程不会自动结束的问题,后续将补充如何解决这个问题的方案。

    1130补充:上述问题的原因在于我把读视频的while true循环写在了run函数外,为了避免这个问题可以采用定时器读取视频流的方法,可以参考博主的一个学习笔记

    7--参考

    使用python将opencv中读取的numpy矩阵json序列化

  • 相关阅读:
    自动驾驶(apollo)
    [iOS开发]编译过程
    多线程怎么共用一个事务
    python字符串转list,多维度,支持float,并保持原数据结构
    92、船员调度
    SCA算法优化脉冲耦合神经网络的图像自动分割(Matlab代码实现)
    Spring 事务的简单了解
    落地DataOps,必须克服的挑战有哪些?
    旋转框目标检测mmrotate v0.3.1 学习配置
    springboot集成Logback 日志写入数据库
  • 原文地址:https://blog.csdn.net/weixin_43863869/article/details/128065933