QImage 是 Qt 框架中用于图像数据处理的核心类,主要负责图像的加载、存储、像素级操作及格式转换,是 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)



