You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PyQt逐帧显示血管造影DICOM文件的3D numpy数组?

PyQt实现血管造影DICOM时间序列逐帧显示

1. 加载DICOM序列为3D numpy数组

用pydicom库读取DICOM时间序列,按序列编号排序后堆叠成3D数组(形状为 (帧数, 高度, 宽度)),并将像素值归一化到0-255适配Qt显示:

import pydicom
import numpy as np
from pydicom.filereader import dcmread
from pathlib import Path

def load_dicom_series(dicom_dir):
    # 遍历目录下所有DICOM文件
    dicom_files = list(Path(dicom_dir).glob("*.dcm"))
    # 按InstanceNumber排序(时间序列的核心排序字段)
    slices = [dcmread(f) for f in dicom_files]
    slices.sort(key=lambda x: x.InstanceNumber)
    
    # 提取像素数据并堆叠为3D数组
    pixel_arrays = [s.pixel_array for s in slices]
    volume = np.stack(pixel_arrays, axis=0)
    
    # 归一化到0-255灰度范围
    volume = (volume - volume.min()) / (volume.max() - volume.min()) * 255
    volume = volume.astype(np.uint8)
    
    return volume

2. PyQt GUI逐帧显示逻辑

核心是将numpy帧转换为QPixmap,通过按钮或定时器控制帧切换,以下是基础实现:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, 
                             QPushButton, QVBoxLayout, QWidget)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QTimer, Qt

class DICOMViewer(QMainWindow):
    def __init__(self, volume):
        super().__init__()
        self.volume = volume
        self.current_frame = 0
        self.num_frames = volume.shape[0]
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("DICOM时间序列查看器")
        self.setGeometry(100, 100, 800, 600)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        layout = QVBoxLayout(central_widget)
        
        # 图像显示标签
        self.image_label = QLabel()
        self.image_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(self.image_label)
        
        # 控制按钮
        self.prev_btn = QPushButton("上一帧")
        self.prev_btn.clicked.connect(self.show_prev_frame)
        layout.addWidget(self.prev_btn)
        
        self.next_btn = QPushButton("下一帧")
        self.next_btn.clicked.connect(self.show_next_frame)
        layout.addWidget(self.next_btn)
        
        # 自动播放控制
        self.timer = QTimer()
        self.timer.timeout.connect(self.show_next_frame)
        self.play_btn = QPushButton("自动播放")
        self.play_btn.clicked.connect(self.toggle_play)
        layout.addWidget(self.play_btn)
        
        self.update_frame()
        
    def numpy_to_pixmap(self, frame):
        # numpy灰度帧转QPixmap
        height, width = frame.shape
        qimage = QImage(frame.data, width, height, width, QImage.Format_Grayscale8)
        return QPixmap.fromImage(qimage)
    
    def update_frame(self):
        # 更新当前帧显示
        frame = self.volume[self.current_frame]
        pixmap = self.numpy_to_pixmap(frame)
        self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
    
    def show_prev_frame(self):
        if self.current_frame > 0:
            self.current_frame -= 1
            self.update_frame()
    
    def show_next_frame(self):
        if self.current_frame < self.num_frames - 1:
            self.current_frame += 1
            self.update_frame()
        else:
            self.current_frame = 0
            self.update_frame()
    
    def toggle_play(self):
        # 切换自动播放/暂停状态
        if self.timer.isActive():
            self.timer.stop()
            self.play_btn.setText("自动播放")
        else:
            self.timer.start(200)  # 每200ms切换一帧,可调整
            self.play_btn.setText("暂停播放")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    # 替换为你的DICOM序列目录
    dicom_volume = load_dicom_series("path/to/your/dicom/dir")
    viewer = DICOMViewer(dicom_volume)
    viewer.show()
    sys.exit(app.exec_())

3. 集成QT Designer生成的界面

如果已经用QT Designer创建了界面(含QLabel、控制按钮),按以下步骤集成:

步骤1:转换UI文件为Python代码

执行命令将.ui文件转成可导入的py文件:

pyuic5 -x your_ui_file.ui -o ui_viewer.py

步骤2:继承UI类并添加逻辑

import sys
from PyQt5.QtWidgets import QMainWindow, QApplication
from ui_viewer import Ui_MainWindow  # 导入QT Designer生成的UI类
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QTimer, Qt

class DICOMViewer(QMainWindow, Ui_MainWindow):
    def __init__(self, volume):
        super().__init__()
        self.setupUi(self)  # 初始化QT Designer界面
        self.volume = volume
        self.current_frame = 0
        self.num_frames = volume.shape[0]
        
        # 绑定按钮信号
        self.prev_btn.clicked.connect(self.show_prev_frame)
        self.next_btn.clicked.connect(self.show_next_frame)
        self.play_btn.clicked.connect(self.toggle_play)
        
        self.timer = QTimer()
        self.timer.timeout.connect(self.show_next_frame)
        
        self.update_frame()
    
    def numpy_to_pixmap(self, frame):
        height, width = frame.shape
        qimage = QImage(frame.data, width, height, width, QImage.Format_Grayscale8)
        return QPixmap.fromImage(qimage)
    
    def update_frame(self):
        frame = self.volume[self.current_frame]
        pixmap = self.numpy_to_pixmap(frame)
        self.image_label.setPixmap(pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio))
    
    def show_prev_frame(self):
        if self.current_frame > 0:
            self.current_frame -= 1
            self.update_frame()
    
    def show_next_frame(self):
        if self.current_frame < self.num_frames - 1:
            self.current_frame += 1
            self.update_frame()
        else:
            self.current_frame = 0
            self.update_frame()
    
    def toggle_play(self):
        if self.timer.isActive():
            self.timer.stop()
            self.play_btn.setText("自动播放")
        else:
            self.timer.start(200)
            self.play_btn.setText("暂停播放")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    dicom_volume = load_dicom_series("path/to/your/dicom/dir")
    viewer = DICOMViewer(dicom_volume)
    viewer.show()
    sys.exit(app.exec_())

注意事项

  • 安装依赖:pip install pydicom numpy pyqt5
  • 若DICOM为16位像素格式,需调整归一化逻辑适配Qt的8位显示格式
  • 彩色DICOM需修改QImage格式为Format_RGB888,血管造影多为灰度图无需调整

内容的提问来源于stack exchange,提问作者ikimatrab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 04:30:42