Pillow ImageQt 模块
下面给你一个 Pillow(PIL Fork)中 ImageQt 模块 的完整入门指南,包含 PIL.Image 与 PyQt / PySide 图像互转、实时显示、动态更新、性能优化、跨平台兼容及高级 GUI 应用。
(基于 Pillow ≥ 9.0,Python 3.8+)
1. 安装依赖
# 安装 PyQt5(或 PySide6)
pip install PyQt5
# 或使用 PySide6(推荐 Qt 官方支持)
# pip install PySide6
# 安装 Pillow
pip install --upgrade pillow
2. 基本导入
from PIL import Image, ImageQt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QWidget
from PyQt5.QtGui import QPixmap, QImage
import sys
注意:
ImageQt模块会根据环境中可用的 Qt 绑定自动选择PyQt5,PyQt6,PySide2,PySide6
3. 核心功能:ImageQt.ImageQt(img)
将 PIL.Image 转换为 QImage,可用于 QLabel、QPixmap 等 Qt 控件。
img = Image.open("photo.jpg")
qimage = ImageQt.ImageQt(img) # 自动转换为 QImage
4. 基础示例:显示 PIL 图像
class ImageWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Pillow + PyQt5")
self.setGeometry(100, 100, 800, 600)
# 打开 PIL 图像
pil_img = Image.open("photo.jpg").convert("RGB")
# 转为 QImage
qimg = ImageQt.ImageQt(pil_img)
# 创建 QLabel 显示
label = QLabel(self)
label.setPixmap(QPixmap.fromImage(qimg))
label.resize(pil_img.size)
self.setCentralWidget(label)
# 运行
app = QApplication(sys.argv)
window = ImageWindow()
window.show()
sys.exit(app.exec_())
5. 动态更新图像(实时预览)
import time
from PyQt5.QtCore import QTimer
class LivePreview(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("实时滤镜预览")
self.setGeometry(100, 100, 800, 600)
self.label = QLabel(self)
self.setCentralWidget(self.label)
self.img = Image.open("photo.jpg").convert("RGB")
# 定时器每 100ms 更新
self.timer = QTimer()
self.timer.timeout.connect(self.update_filter)
self.timer.start(100)
self.angle = 0
def update_filter(self):
# 动态旋转 + 模糊
rotated = self.img.rotate(self.angle, expand=True)
from PIL import ImageFilter
blurred = rotated.filter(ImageFilter.GaussianBlur(2))
qimg = ImageQt.ImageQt(blurred)
self.label.setPixmap(QPixmap.fromImage(qimg))
self.label.resize(blurred.size)
self.angle = (self.angle + 5) % 360
app = QApplication(sys.argv)
window = LivePreview()
window.show()
sys.exit(app.exec_())
6. 性能优化技巧
| 技巧 | 说明 |
|---|---|
缓存 QImage | 避免重复转换 |
使用 QPixmap | 比 QLabel.setPixmap 更快 |
| 缩小预览图 | img.resize((400,300)) 再转换 |
| 分离线程 | 图像处理放子线程,避免卡 UI |
示例:线程安全更新
from PyQt5.QtCore import QThread, pyqtSignal
import numpy as np
class Worker(QThread):
finished = pyqtSignal(Image.Image)
def __init__(self, img):
super().__init__()
self.img = img
def run(self):
# 模拟耗时处理
time.sleep(1)
arr = np.array(self.img)
arr = arr + 50 # 提亮
result = Image.fromarray(arr.clip(0, 255).astype('uint8'))
self.finished.emit(result)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.label = QLabel(self)
self.setCentralWidget(self.label)
self.img = Image.open("photo.jpg").convert("RGB")
self.worker = Worker(self.img)
self.worker.finished.connect(self.update_image)
self.worker.start()
def update_image(self, pil_img):
qimg = ImageQt.ImageQt(pil_img)
self.label.setPixmap(QPixmap.fromImage(qimg))
7. 高级应用
7.1 图像编辑器(缩放 + 滤镜)
from PyQt5.QtWidgets import QPushButton, QVBoxLayout
class Editor(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("图像编辑器")
self.img = Image.open("photo.jpg").convert("RGB")
central = QWidget()
layout = QVBoxLayout()
self.label = QLabel()
self.update_display()
btn_blur = QPushButton("高斯模糊")
btn_blur.clicked.connect(self.apply_blur)
layout.addWidget(self.label)
layout.addWidget(btn_blur)
central.setLayout(layout)
self.setCentralWidget(central)
def update_display(self):
qimg = ImageQt.ImageQt(self.img)
self.label.setPixmap(QPixmap.fromImage(qimg).scaled(600, 400, transformMode=1))
def apply_blur(self):
self.img = self.img.filter(ImageFilter.GaussianBlur(5))
self.update_display()
7.2 截图工具(结合 ImageGrab)
from PIL import ImageGrab
class ScreenshotTool(QMainWindow):
def __init__(self):
super().__init__()
btn = QPushButton("截图", self)
btn.clicked.connect(self.capture)
self.setCentralWidget(btn)
def capture(self):
img = ImageGrab.grab() # 全屏截图
qimg = ImageQt.ImageQt(img)
label = QLabel()
label.setPixmap(QPixmap.fromImage(qimg))
label.show()
7.3 视频帧显示(OpenCV + PIL + Qt)
import cv2
class VideoPlayer(QMainWindow):
def __init__(self):
super().__init__()
self.label = QLabel(self)
self.setCentralWidget(self.label)
self.cap = cv2.VideoCapture(0)
self.timer = QTimer()
self.timer.timeout.connect(self.update_frame)
self.timer.start(30)
def update_frame(self):
ret, frame = self.cap.read()
if ret:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = Image.fromarray(frame)
qimg = ImageQt.ImageQt(img)
self.label.setPixmap(QPixmap.fromImage(qimg).scaled(640, 480))
8. 跨版本兼容(PyQt5 / PySide6)
def pil_to_qimage(pil_img):
"""自动适配 PyQt5 / PySide6"""
try:
from PyQt5.QtGui import QImage
except ImportError:
try:
from PySide6.QtGui import QImage
except ImportError:
raise ImportError("需要 PyQt5 或 PySide6")
return ImageQt.ImageQt(pil_img)
9. 常见问题 & 解决方案
| 问题 | 原因 | 解决 |
|---|---|---|
ImportError: cannot import name 'ImageQt' | 未安装 Qt 绑定 | pip install PyQt5 |
| 图像显示为黑屏 | 颜色通道顺序错误 | .convert("RGB") |
| 内存泄漏 | 频繁转换不释放 | 使用 QPixmap.cacheKey() 缓存 |
| 窗口闪烁 | 频繁 setPixmap | 使用 QGraphicsView + QGraphicsPixmapItem |
10. 官方文档
- Pillow
ImageQt:https://pillow.readthedocs.io/en/stable/reference/ImageQt.html - PyQt5 文档:https://www.riverbankcomputing.com/static/Docs/PyQt5/
一键运行示例:pil_qt_viewer.py
#!/usr/bin/env python3
import sys
from PIL import Image, ImageQt
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow
class Viewer(QMainWindow):
def __init__(self, path):
super().__init__()
self.setWindowTitle(f"PIL Viewer - {path}")
img = Image.open(path).convert("RGB")
qimg = ImageQt.ImageQt(img)
label = QLabel()
label.setPixmap(QPixmap.fromImage(qimg))
self.setCentralWidget(label)
self.resize(img.size)
app = QApplication(sys.argv)
if len(sys.argv) < 2:
print("用法: python pil_qt_viewer.py <image_path>")
sys.exit(1)
window = Viewer(sys.argv[1])
window.show()
sys.exit(app.exec_())
使用:
python pil_qt_viewer.py photo.jpg
总结:ImageQt 是 Pillow 与 Qt 的桥梁
| 功能 | 推荐方式 |
|---|---|
| 显示图像 | ImageQt.ImageQt(img) → QPixmap |
| 实时更新 | QTimer + setPixmap |
| 大图预览 | img.resize() 先缩略 |
| 线程处理 | QThread + 信号 |
需要我帮你实现 图像标注工具、滤镜预览面板、批量处理 GUI、视频帧提取播放器 等完整项目吗?直接说需求,我给你完整可运行代码!