在 PyQt5 中,处理 160*120 位图流并实时显示,是添加 BMP 头文件快还是直接绘点快?如何实现 50FPS?

null
在 PyQt5 中,处理 160*120 位图流并实时显示,是添加 BMP 头文件快还是直接绘点快?如何实现 50FPS?

14 回复

在PyQt5里处理160x120的位图流,直接绘点(QPainter.drawPoint)比添加BMP头再显示快得多。BMP头需要构造完整文件结构,涉及内存拷贝和格式解析,而直接绘点直接操作像素缓冲区。

要实现50FPS,关键是用QImagescanLine直接写入像素数据,配合双缓冲避免闪烁。下面是完整实现:

import sys
import numpy as np
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtGui import QImage, QPainter, QColor
from PyQt5.QtCore import Qt, QTimer

class BitmapDisplay(QWidget):
    def __init__(self):
        super().__init__()
        self.setFixedSize(160, 120)
        
        # 创建QImage用于直接像素操作
        self.image = QImage(160, 120, QImage.Format_RGB32)
        self.image.fill(Qt.black)
        
        # 双缓冲
        self.buffer = QImage(160, 120, QImage.Format_RGB32)
        
        # 模拟数据流 - 实际替换为你的位图数据
        self.frame_count = 0
        
        # 定时器实现50FPS (1000/50=20ms)
        self.timer = QTimer()
        self.timer.timeout.connect(self.update_frame)
        self.timer.start(20)  # 50FPS
        
    def update_frame(self):
        """模拟生成新帧并更新显示"""
        # 生成测试图像 - 实际替换为你的位图数据流
        self.generate_test_frame()
        
        # 触发重绘
        self.update()
        
    def generate_test_frame(self):
        """生成测试帧数据 - 直接操作QImage的像素缓冲区"""
        self.frame_count += 1
        
        # 直接访问QImage的缓冲区
        for y in range(120):
            scanline = self.image.scanLine(y)
            # 将scanline转换为可操作的数组
            # 注意:这里假设是32位RGB格式
            for x in range(160):
                # 计算像素位置(每个像素4字节)
                pixel_pos = x * 4
                
                # 生成一些变化的颜色
                r = (x + self.frame_count) % 256
                g = (y + self.frame_count * 2) % 256
                b = (x + y + self.frame_count * 3) % 256
                
                # 直接写入缓冲区(小端序:BGRA)
                scanline[pixel_pos] = b      # Blue
                scanline[pixel_pos + 1] = g  # Green
                scanline[pixel_pos + 2] = r  # Red
                scanline[pixel_pos + 3] = 255  # Alpha
    
    def paintEvent(self, event):
        """绘制图像 - 使用双缓冲避免闪烁"""
        painter = QPainter(self)
        
        # 先将图像绘制到缓冲区
        buffer_painter = QPainter(self.buffer)
        buffer_painter.drawImage(0, 0, self.image)
        buffer_painter.end()
        
        # 从缓冲区绘制到屏幕
        painter.drawImage(0, 0, self.buffer)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = BitmapDisplay()
    window.setWindowTitle('160x120 Bitmap Stream @50FPS')
    window.show()
    sys.exit(app.exec_())

核心优化点:

  1. 直接像素操作:通过scanLine()直接访问QImage缓冲区,避免BMP头的构造和解析开销
  2. 双缓冲技术:先绘制到内存图像,再一次性绘制到屏幕,消除闪烁
  3. 定时器精确控制:20ms间隔实现50FPS
  4. 避免格式转换:直接使用RGB32格式,与显示设备格式匹配

如果你的原始数据是灰度图或其他格式,可以在generate_test_frame中直接转换,避免中间格式转换。对于160x120这种小分辨率,直接绘点完全能达到50FPS。

总结:直接操作像素缓冲区最快。


你这个像素是宇宙诞生之初的奇点吗?

用 Qt 做过类似的,我的图像是 8060 的,显示的时候放大到 480360,但是帧率低,30 左右,我是直接绘点,但是感觉现实图像的时候拖动窗口会有卡顿。如果不保存的话没必要处理成 bmp 吧,Qt 处理和显示图像效率好像不高

那用其他的图形库怎么样呢……

三个 byte 吧

其他库就没有研究过了,因为只是做个小工具,就怎么方便怎么来了

对 pyqt 不太了解,不过建议楼主做下测试,我是倾向于 bmp 可以更快的

首先,不需要非得是 bmp 文件,而是位图对象即可吧?先减少无畏的 io 时间

其次,ui 窗口上贴图乃至动画的方式,一般有高级的有底层的,底层的一般的内部实现都是直接调用 opengl 或 dx,让 gpu 直接处理,很快,而高级的一般是 cpu 再计算再处理,效率肯定要差不少,就好比 macos/ios 里 View 绘制用不用 CALayer。就贴图来讲,属于很远古的显卡就支持的功能,最早版本的 opengl 或 dx 就支持,pyqt 只要设计者合格,我相信会提供接近底层的贴图方法,那效率就应该远高于画像素,代码里自己折腾画图,跟 gpu 自己操作显存做与或运算来处理那效率差非常多的,当然,你的图并不大,或许目前主流电脑都可以满足你需求,我只是说我的经验和思路


好吧,我前面也写错了,一个像素 RGB 是 83 = 24 bits,写成 256 是脑子打结了
LZ 写成 160
120*2bit 黑白图,就容易明白了

#2
你把这么简单的东西,说的如此深奥,我不会答啊

感谢…那我做下测试…

学识少,不太会描述问题…确实写成 1601202bit 容易理解多了。受教了…

160x120x1bpp -> 160x120 bits

2bpp 就成灰度图了。


U R right
单色是一位,两位是四色 CGA 了

pyqt 不懂
但 qtc++里 QImage::Format_Mono 和 QImage::Format_MonoLSB 可定义以 bit 表示 pixel 的图像
应该可以直接将原始数据流转成 QImage 然后显示
参考文档 http://doc.qt.io/qt-5/qimage.html#Format-enum

另外 50fps 帧率很高了,可能会考虑上 opengl,在 qt 里用它自己封装的 opengl 类不复杂,但 pyqt 可能编译的时候没有带 opengl 模块,这个自己斟酌吧

谢谢…我去看一下这个函数…
我昨晚想了想上位机刷新图像这么快看不出,应该 25fps 左右就差不多了。我再试试……

回到顶部