如何使用pydicom将多张JPG图像生成DICOM文件并修复像素数组问题
多帧DICOM生成代码修正方案
核心问题修复点
- 像素数组拼接逻辑错误:原代码直接将多个单帧字节数组转为numpy数组赋值给
PixelData,多帧DICOM要求所有帧的像素字节按顺序拼接为单个连续字节流 - 单帧残留配置未更新:
ImagesInAcquisition、ImagePositionPatient等标签仍为单图参数,多帧场景下需要适配调整 - 缺少输入校验:未校验所有输入图像的宽高一致性,易引发DICOM解析异常
修正后完整代码
import pydicom from pydicom.dataset import Dataset from PIL import Image import numpy as np from os import walk # 自然排序函数(原代码未给出,补充实现) def natural_keys(text): import re return [int(c) if c.isdigit() else c for c in re.split(r'(\d+)', text)] # Populate required values for file meta information meta = pydicom.Dataset() meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian meta.MediaStorageSOPClassUID = pydicom._storage_sopclass_uids.MRImageStorage meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid() # build dataset ds = Dataset() ds.file_meta = meta ds.fix_meta_info() # 通用DICOM配置 ds.is_little_endian = True ds.is_implicit_VR = False ds.SOPClassUID = pydicom._storage_sopclass_uids.MRImageStorage ds.SeriesInstanceUID = pydicom.uid.generate_uid() ds.StudyInstanceUID = pydicom.uid.generate_uid() ds.FrameOfReferenceUID = pydicom.uid.generate_uid() ds.ImageOrientationPatient = r"1\0\0\0\-1\0" ds.ImageType = r"ORIGINAL\PRIMARY\AXIAL" ds.RescaleIntercept = "0" ds.RescaleSlope = "1" # 病例信息配置 ds.PatientName = "Anonymous" ds.PatientID = "123456" ds.Modality = "MR" ds.StudyDate = '20200225' ds.ContentDate = '20200225' def ensure_even(stream): # 适配部分DICOM查看器要求,字节流长度必须为偶数 if len(stream) % 2: return stream + b"\x00" return stream pixel_data_list = [] img_width = None img_height = None for root, dir, filenames in walk(folder): filenames.sort(key=natural_keys) for filename in filenames: full_path = folder + filename # 转灰度图 img = Image.open(full_path).convert('L') # 校验所有图像尺寸一致 if img_width is None: img_width = img.width img_height = img.height else: if img.width != img_width or img.height != img_height: raise ValueError(f"输入图像{filename}尺寸与首图不一致,无法拼接多帧DICOM") # 转为numpy数组 arr = np.array(img) pixel_data_list.append(arr.tobytes()) # 8位灰度图配置 ds.BitsStored = 8 ds.BitsAllocated = 8 ds.HighBit = 7 ds.PixelRepresentation = 0 ds.PhotometricInterpretation = "MONOCHROME2" ds.SamplesPerPixel = 1 ds.file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian # 多帧配置 frame_count = len(pixel_data_list) ds.NumberOfFrames = frame_count ds.ImagesInAcquisition = str(frame_count) # 拼接所有帧的像素字节流 ds.PixelData = b''.join([ensure_even(frame) for frame in pixel_data_list]) # 图像尺寸配置 ds['PixelData'].is_undefined_length = False ds.Columns = img_width ds.Rows = img_height # 校验保存 pydicom.dataset.validate_file_meta(ds.file_meta, enforce_standard=True) new_filename = f"{name}.dcm" ds.save_as(new_filename, write_like_original=False)
低优先级问题解决思路(压缩/彩色模式)
- 压缩功能:安装
pylibjpeg及对应编码插件,将TransferSyntaxUID修改为对应压缩语法,再通过pydicom的压缩接口处理像素数据即可 - 彩色模式:将
PhotometricInterpretation改为RGB,SamplesPerPixel改为3,PlanarConfiguration设为0,输入图像不做灰度转换直接提取RGB通道数组拼接即可
内容的提问来源于stack exchange,提问作者Johann
相关产品推荐
相关产品推荐

