欢迎光临
我们一直在努力

PySide6从0开始学习的笔记(二十四) QImage

        QImage 是 Qt 框架中用于图像数据处理的核心类,主要负责图像的加载、存储、像素级操作及格式转换,是 Qt 图形处理的基础组件之一。

一、核心特点与用途:

  • 像素级访问:支持直接读写单个像素(如 pixel()/setPixel()、bits() 直接操作内存),适合图像算法处理(如滤镜、阈值分割等)。
  • 多格式支持:原生支持 PNG、JPG、BMP、GIF 等常见图像格式,可通过 load()/save() 便捷读写文件。
  • 颜色空间与格式:提供丰富的像素格式(如 ARGB32、RGB888、灰度图等),支持透明度处理,适配不同显示 / 处理场景。
  • 设备无关性:基于内存中的图像数据,不依赖具体绘图设备(如屏幕、打印机),可独立于 UI 组件存在,适合后台图像处理。
  • 与其他 Qt 图形类协作:可快速转换为 QPixmap(适合 UI 绘制,优化屏幕显示)、QBitmap(黑白图像)或 QPainter 绘图对象,衔接 Qt 绘图系统。
  • 二、典型使用场景:

    • 图像加载与保存(如读取本地图片、生成并导出图像);
    • 自定义图像处理(如修改像素颜色、裁剪、缩放、旋转);
    • 作为绘图数据源(通过 QPainter::drawImage() 绘制到控件或其他绘图设备)。

    三、关键区别(与 QPixmap):https://blog.csdn.net/xulibo5828/article/details/157357417

    • QImage:侧重数据处理,可在非 UI 线程操作,像素访问高效;
    • QPixmap:侧重UI 绘制,与绘图设备关联,优化了屏幕渲染性能,不建议非 UI 线程使用。

            简单说,QImage 是 Qt 中 “处理图像数据” 的核心,QPixmap 是 “显示图像” 的优化类,二者可通过 fromImage()/toImage() 相互转换。

    四、应用代码

    1、核心属性
    • 宽度、高度等参数:

    from PySide6.QtGui import QImage

    # 1. 加载图像(基础初始化)
    image = QImage("test.png")

    # 2. 获取核心属性
    print("图像宽度:", image.width()) # 宽度(像素)
    print("图像高度:", image.height()) # 高度(像素)
    print("图像尺寸:", image.size()) # 尺寸(QSize对象,含width/height)
    print("像素格式:", image.format()) # 像素存储格式(如Format_RGB888)
    print("每行字节数:", image.bytesPerLine()) # 每行像素占用的字节数(对齐优化后)
    print("是否为空:", image.isNull()) # 是否加载成功/有效
    print("深度(位/像素):", image.depth()) # 每个像素的位数(如24位RGB=24,32位RGBA=32)

    • QImage的原始像素数据:bits()  (重点知识)

            QImage是 Qt 框架中存储图像数据的类,bits() 方法会返回图像 原始像素数据的内存地址指针(本质是指向连续字节缓冲区的指针)处存储的图像像素的原始字节数据。

    典型用法:

    bits = qimage.bits()
    arr = np.frombuffer(bits, dtype=np.uint8).copy()

            需注意:bits()返回的是 “内存视图”的指针 —— 直接操作可能影响原始 QImage,且生命周期受 QImage 对象约束(QImage 销毁后指针失效)。

     arr = np.frombuffer(bits, dtype=np.uint8).copy()的解释:

    • np.frombuffer():NumPy 的函数,作用是 从字节缓冲区(这里就是 bits 指向的内存)创建数组,不主动拷贝数据(共享内存):
      • 第一个参数 bits:传入字节缓冲区(Qt 的bits()返回值可被 NumPy 识别为缓冲区对象);
      • 第二个参数 dtype=np.uint8:指定数组元素类型为 “无符号 8 位整数”(对应图像像素的每个颜色通道取值范围 0-255,是图像数据的标准类型)。
    • .copy():关键操作 —— 将 NumPy 创建的 “共享内存数组” 转为 独立的拷贝数组:
      • 避免后续修改arr时影响原始 QImage;
      • 脱离 QImage 的生命周期约束(即使 QImage 被销毁,arr仍有效)。

    最终得到的结果

    • arr 是一个 1 维 NumPy 数组,元素是uint8类型,长度 = 图像宽度 × 高度 × 每个像素的字节数(比如 640×480 的 RGB 图,长度 = 640×480×3=921600)。
    • 后续通常需要 reshape 为 2D(加通道维度),比如 arr = arr.reshape(height, width, channels),才能作为 “图像形状” 的数组使用。

    height, width = qimage.height(), qimage.width()
    bits = qimage.bits()
    arr = np.frombuffer(bits, dtype=np.uint8).copy()
    mat = arr.reshape((height, width, 4))

    2、创建 / 加载

    from PySide6.QtGui import QImage, QColor

    # 方式1:从文件加载(最常用)
    image = QImage("test.jpg")
    if image.isNull():
    print("图像加载失败!")

    # 方式2:创建空白图像(指定尺寸、像素格式)
    # 参数:宽度、高度、像素格式
    blank_image = QImage(800, 600, QImage.Format_RGBA8888)
    # 填充背景色(白色,全透明)
    blank_image.fill(QColor(255, 255, 255, 0))

    # 方式3:从原始字节数据创建(如相机/网络流)
    # 场景:处理无文件头的原始像素数据
    width, height = 640, 480
    # 生成测试字节数据(640*480*4字节,RGBA格式)
    raw_data = bytes([255, 0, 0, 255] * width * height) # 纯红色
    # 参数:数据、宽度、高度、每行字节数、像素格式
    raw_image = QImage(raw_data, width, height, width*4, QImage.Format_RGBA8888)

    3、像素级操作

    # 1. 读取像素(两种方式)
    # 方式1:返回QColor对象(直观,适合少量操作)
    from PySide6.QtGui import QImage, QColor

    image = QImage("E:\\HiKi_MVS\\BasicDemo\\\\33.bmp")
    # 1. 读取像素(两种方式)
    # 方式1:返回QColor对象(直观,适合少量操作)
    color = image.pixelColor(100, 100) # (x,y)坐标
    print(f"R:{color.red()}, G:{color.green()}, B:{color.blue()}, A:{color.alpha()}")

    # 方式2:返回整数格式的像素值(高效,适合批量操作)
    pixel_value = image.pixel(100, 100) # 0xAARRGGBB(ARGB32格式)
    # 解析RGB值
    r = (pixel_value >> 16) & 0xFF
    g = (pixel_value >> 8) & 0xFF
    b = pixel_value & 0xFF
    print(f"R:{r}, G:{g}, B:{b}")

    # 2. 修改像素
    # 方式1:用QColor设置
    image.setPixelColor(100, 100, QColor(0, 255, 0, 255)) # 设为绿色

    # 方式2:用整数像素值设置(ARGB32格式)
    green_value = 0xFF00FF00 # A=255, R=0, G=255, B=0
    image.setPixel(100, 100, green_value)

    # 3. 批量操作像素(高效方式)
    # 获取原始字节数据指针(返回bytes对象,只读)
    bits = image.bits()
    # 转为numpy数组(便于批量处理)
    import numpy as np
    pixel_array = np.frombuffer(bits, dtype=np.uint8).reshape((image.height(), image.width(), 4))
    # 批量修改:将所有像素的R通道减半
    pixel_array[:, :, 0] = pixel_array[:, :, 0] // 2 # 假设是RGBA格式,0=R通道

    4、图像几何变换(缩放 / 旋转 / 翻转)

    # 1. 缩放(保持宽高比)
    # 参数:目标尺寸、缩放模式、宽高比模式
    scaled_image = image.scaled(400, 300,
    aspectRatioMode=QImage.KeepAspectRatio, # 保持宽高比
    transformMode=QImage.SmoothTransformation) # 平滑缩放(高质量)

    # 2. 旋转(顺时针,单位:度)
    rotated_image = image.rotated(90) # 旋转90度
    # 旋转并保持尺寸(不裁剪)
    rotated_image_full = image.rotated(180, expand=True)

    # 3. 翻转
    flipped_h = image.mirrored(horizontal=True, vertical=False) # 水平翻转
    flipped_v = image.mirrored(horizontal=False, vertical=True) # 垂直翻转

    # 4. 裁剪(截取指定区域)
    # 参数:x, y, 宽度, 高度
    cropped_image = image.copy(50, 50, 200, 200) # 从(50,50)截取200×200的区域

    5、格式转换

    使用QImage.format()可以查询格式,QImage的所有图像格式枚举范围位于:QImage.Format:

    class QImage(PySide6.QtGui.QPaintDevice):

    class Format(enum.Enum):

    Format_Invalid : QImage.Format = … # 0x0
    Format_Mono : QImage.Format = … # 0x1
    Format_MonoLSB : QImage.Format = … # 0x2
    Format_Indexed8 : QImage.Format = … # 0x3
    Format_RGB32 : QImage.Format = … # 0x4
    Format_ARGB32 : QImage.Format = … # 0x5
    Format_ARGB32_Premultiplied: QImage.Format = … # 0x6
    Format_RGB16 : QImage.Format = … # 0x7
    Format_ARGB8565_Premultiplied: QImage.Format = … # 0x8
    Format_RGB666 : QImage.Format = … # 0x9
    Format_ARGB6666_Premultiplied: QImage.Format = … # 0xa
    Format_RGB555 : QImage.Format = … # 0xb
    Format_ARGB8555_Premultiplied: QImage.Format = … # 0xc
    Format_RGB888 : QImage.Format = … # 0xd
    Format_RGB444 : QImage.Format = … # 0xe
    Format_ARGB4444_Premultiplied: QImage.Format = … # 0xf
    Format_RGBX8888 : QImage.Format = … # 0x10
    Format_RGBA8888 : QImage.Format = … # 0x11
    Format_RGBA8888_Premultiplied: QImage.Format = … # 0x12
    Format_BGR30 : QImage.Format = … # 0x13
    Format_A2BGR30_Premultiplied: QImage.Format = … # 0x14
    Format_RGB30 : QImage.Format = … # 0x15
    Format_A2RGB30_Premultiplied: QImage.Format = … # 0x16
    Format_Alpha8 : QImage.Format = … # 0x17
    Format_Grayscale8 : QImage.Format = … # 0x18
    Format_RGBX64 : QImage.Format = … # 0x19
    Format_RGBA64 : QImage.Format = … # 0x1a
    Format_RGBA64_Premultiplied: QImage.Format = … # 0x1b
    Format_Grayscale16 : QImage.Format = … # 0x1c
    Format_BGR888 : QImage.Format = … # 0x1d
    Format_RGBX16FPx4 : QImage.Format = … # 0x1e
    Format_RGBA16FPx4 : QImage.Format = … # 0x1f
    Format_RGBA16FPx4_Premultiplied: QImage.Format = … # 0x20
    Format_RGBX32FPx4 : QImage.Format = … # 0x21
    Format_RGBA32FPx4 : QImage.Format = … # 0x22
    Format_RGBA32FPx4_Premultiplied: QImage.Format = … # 0x23
    NImageFormats : QImage.Format = … # 0x24

    在使用图像格式的时候可以写成:

    QImage.Format.Format_Grayscale8

    简写:

    QImage.Format_Grayscale8

    如果对于格式名称不熟悉,推荐第一种写法。

    转换示例:

    # 1. 转换像素存储格式(如RGBA→灰度图)
    gray_image = image.convertToFormat(QImage.Format_Grayscale8)

    # 2. 转换为RGB32格式(Qt绘图优先格式)
    rgb32_image = image.convertToFormat(QImage.Format_RGB32)

    # 3. 转换为QPixmap(用于界面显示)
    from PySide6.QtGui import QPixmap
    pixmap = QPixmap.fromImage(image)

    # 4. QPixmap转回QImage(用于处理)
    image_from_pixmap = pixmap.toImage()

    注意:QPixmap的定义和使用必须在UI主线程内进行,否则会出错!

    6、图像保存

    # 方式1:基础保存(自动识别格式)
    image.save("output.png")

    # 方式2:指定格式+参数
    image.save("output.jpg", format="JPG", quality=95) # JPG质量0-100
    image.save("output.png", format="PNG", quality=9) # PNG压缩级别0-9

    # 方式3:保存到字节流(网络传输/内存缓存)
    from PySide6.QtCore import QByteArray, QBuffer
    byte_array = QByteArray()
    buffer = QBuffer(byte_array)
    buffer.open(QBuffer.WriteOnly)
    image.save(buffer, "PNG") # 写入缓冲区
    image_bytes = byte_array.data() # 获取字节数据

    五、重点学习

    1、与opencv的格式转换(太常用了)
    • opencv读取文件后转换为QImage

            opencv从文件中读取到内存中cv2.Mat 对象默认是 BGR 格式,而 QImage 默认使用 RGB 格式:

    import sys
    import cv2
    import numpy as np
    from PySide6.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout
    from PySide6.QtGui import QImage, QPixmap

    def cv2_imread_to_qimage(file_path):
    """
    用OpenCV读取图像文件,转换为QImage
    :param file_path: 图像文件路径(支持jpg/png/bmp等)
    :return: QImage对象(失败返回空QImage)
    """
    # 1. OpenCV读取图像(BGR格式)
    # cv2.IMREAD_UNCHANGED:保留透明通道(如有)
    cv_mat = cv2.imread(file_path, cv2.IMREAD_UNCHANGED)
    if cv_mat is None or cv_mat.size == 0:
    print(f"OpenCV读取图像失败:{file_path}")
    return QImage()

    # 2. 根据图像类型处理通道
    height, width = cv_mat.shape[:2]
    channel = cv_mat.shape[2] if len(cv_mat.shape) == 3 else 1

    qimage = QImage()
    # 情况1:灰度图(单通道)
    if channel == 1:
    qimage = QImage(
    cv_mat.data, # 原始像素数据
    width, # 宽度
    height, # 高度
    cv_mat.strides[0], # 每行字节数(自动对齐,避免手动计算错误)
    QImage.Format_Grayscale8 # 灰度格式
    )
    # 情况2:彩色图(3通道,BGR→RGB)
    elif channel == 3:
    # 转换通道:BGR → RGB
    rgb_mat = cv2.cvtColor(cv_mat, cv2.COLOR_BGR2RGB)
    qimage = QImage(
    rgb_mat.data,
    width,
    height,
    rgb_mat.strides[0],
    QImage.Format_RGB888 # RGB888格式
    )
    # 情况3:带透明通道(4通道,BGRA→RGBA)
    elif channel == 4:
    # 转换通道:BGRA → RGBA
    rgba_mat = cv2.cvtColor(cv_mat, cv2.COLOR_BGRA2RGBA)
    qimage = QImage(
    rgba_mat.data,
    width,
    height,
    rgba_mat.strides[0],
    QImage.Format_RGBA8888 # RGBA格式
    )

    # 3. 深拷贝!避免cv_mat释放后qimage数据失效(核心!)
    return qimage.copy()

    # 测试:显示转换后的QImage
    if __name__ == "__main__":
    app = QApplication(sys.argv)

    # 替换为你的图像路径
    image_path = "E:/HiKi_MVS/BasicDemo/33.png" # 可测试jpg/png(含透明通道)
    qimage = cv2_imread_to_qimage(image_path)

    # 检查转换结果
    if qimage.isNull():
    sys.exit("图像转换失败!")

    # 显示QImage
    window = QWidget()
    window.setWindowTitle("OpenCV → QImage 转换示例")
    layout = QVBoxLayout()

    label = QLabel()
    label.setPixmap(QPixmap.fromImage(qimage)) # QImage转QPixmap显示
    layout.addWidget(label)

    window.setLayout(layout)
    window.resize(qimage.width(), qimage.height())
    window.show()

    sys.exit(app.exec())

    • QImage的数据转换为opencv的cv2.Mat

    from PySide6.QtGui import QImage, QColor

    image = QImage("E:/HiKi_MVS/BasicDemo/33.png")
    gray_image = image.convertToFormat(QImage.Format.Format_Grayscale8)

    import cv2
    import numpy as np
    from PySide6.QtGui import QImage

    def qimage_to_cvmat(qimage: QImage) -> np.ndarray:
    """
    将QImage转换为OpenCV的cv2.Mat(numpy.ndarray)
    :param qimage: 输入的QImage对象
    :return: cv2.Mat对象(灰度图:单通道;彩色图:BGR;透明图:BGRA)
    """
    # 1. 检查QImage是否有效
    if qimage.isNull():
    return np.array([]) # 返回空数组

    height, width = qimage.height(), qimage.width()

    # 2. 转换QImage格式
    if qimage.isGrayscale(): # 灰度图
    bits = qimage.bits()
    arr = np.frombuffer(bits, dtype=np.uint8).copy()
    mat = arr.reshape((height, width))
    else: # 彩色图
    # 所有彩色图都转为RGBA8888
    qimg = qimage.convertToFormat(QImage.Format_RGBA8888)
    bits = qimg.bits()
    arr = np.frombuffer(bits, dtype=np.uint8).copy()
    mat = arr.reshape((height, width, 4))
    mat = cv2.cvtColor(mat, cv2.COLOR_RGBA2BGRA)
    return mat

    if __name__ == '__main__':
    cv_mat = qimage_to_cvmat(image)
    print(cv_mat.shape)
    print(cv_mat.dtype)
    print(cv_mat.strides)
    cv2.imshow("cv_mat", cv_mat)
    cv2.waitKey(0)

    2、从字节流转换
    • 从BAYER_RG格式的字节流创建QImage(相机采图常使用的方式) # 从原始字节流some_bytes的内存指针处拷贝字节数据并按照图像宽度和高度值调整维度
      image_array = np.frombuffer(some_bytes, dtype=np.uint8).copy().reshape(
      (Height, Width))
      # 将BAYER_RG格式转换为RGB
      rgb_image = cv2.cvtColor(image_array, cv2.COLOR_BAYER_RG2RGB)
      # 创建QImage对象
      qimage = QImage(
      rgb_image.data,
      self.stConvertParam.nWidth,
      self.stConvertParam.nHeight,
      QImage.Format.Format_RGB888)
      # 可用于保存文件以及别的操作
      qimage.save(self.save_file_path)

    赞(0)
    未经允许不得转载:171主机测评 » PySide6从0开始学习的笔记(二十四) QImage
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址