由于公司业务需要,尝试将AI辅助诊断报告(PDF格式)转换为DICOM格式,这样,当DICOM影像上传到云平台计算完成后,可以将生成的AI辅助诊断报告推送到医院PACS系统,医生就可以在DICOM viewer中同时看到病人原始影像和AI报告。
思路
DICOM影像通常分为三个层级:Study
、Series
、Instance
,其中Study代表病人一次检查的全部影像,通常放在一个文件夹内,而Series则代表这次检查中的不同影像序列,每个序列的扫描参数、用途各有差异,而Instance则是代表每张影像的实体。为了区分影像,DICOM标准中对于Study、Series、Intance的序号(UID)命名有着明确的规范,另外对于影像的编码格式、传输语法以及影像信息都有要求,在创建DICOM影像时都要注意。
DICOM tag信息主要分为三大部分,第一部分File Meta文件头,指定了影像最重要的基本信息,包含编码格式、传输语法等信息,第二部分则是常规的病人信息、影像信息字段,而影像的像素数据则保存在第三部分Pixel Data
字段中。
为了将PDF报告转换成DICOM格式,可以先利用pdf2image
将PDF转换为常见的图片格式,再将图片转换为bytes
类型数据,写入DICOM tag中的Pixel Data
字段,同时还需要给这些创建的DICOM影像指定UID、编码格式、传输语法等Tag信息。
代码
以下是PDF转DICOM的参考代码,注意这里的应用情况是将PDF转换后的DICOM影像写入到原始DICOM文件夹中,支持含有多页的PDF文件
import os import tempfile import datetime import pydicom from pydicom.dataset import Dataset, FileDataset from pdf2image import convert_from_path
def read_dcm(dicom_dir): dicom_list = os.listdir(dicom_dir) info_list = [] for dicom in dicom_list: ds = pydicom.dcmread(os.path.join(dicom_dir, dicom))
info_dict = { 'MediaStorageSOPInstanceUID': ds.file_meta.MediaStorageSOPInstanceUID, 'MediaStorageSOPClassUID': ds.file_meta.MediaStorageSOPClassUID, 'ImplementationClassUID': ds.file_meta.ImplementationClassUID, 'PatientName': ds.PatientName, 'PatientID': ds.PatientID, 'PatientSex': ds.PatientSex, 'PatientBirthDate': ds.PatientBirthDate, 'PatientAge': ds.PatientAge, 'ContentDate': ds.ContentDate, 'ContentTime': ds.ContentTime, 'SOPClassUID': ds.SOPClassUID, 'SOPInstanceUID': ds.SOPInstanceUID, 'StudyInstanceUID': ds.StudyInstanceUID, 'SeriesInstanceUID': ds.SeriesInstanceUID, 'InstanceNumber': ds.InstanceNumber, 'SeriesNumber': ds.SeriesNumber, } info_list.append(info_dict)
sort_list = sorted(info_list, key=lambda x: x['MediaStorageSOPInstanceUID'])
return sort_list[-1]
def write_dcm(index, img, dicom, dicom_dir): chguid = lambda x: x.replace(x[-1], str(int(x[-1]) + index + 1))
suffix = '.dcm' filename_little_endian = tempfile.NamedTemporaryFile(suffix=suffix).name
file_meta = Dataset() file_meta.MediaStorageSOPClassUID = dicom['MediaStorageSOPClassUID'] file_meta.MediaStorageSOPInstanceUID = chguid(dicom['MediaStorageSOPInstanceUID']) file_meta.ImplementationClassUID = dicom['ImplementationClassUID']
ds = FileDataset(filename_little_endian, {}, file_meta=file_meta, preamble=b"\0" * 128)
ds.PatientName = dicom['PatientName'] ds.PatientID = dicom['PatientID'] ds.PatientSex = dicom['PatientSex'] ds.PatientBirthDate = dicom['PatientBirthDate'] ds.PatientAge = dicom['PatientAge']
dt = datetime.datetime.now() ds.ContentDate = dt time_str = dt.strftime('%H%M%S.%f') ds.ContentTime = time_str
ds.SOPClassUID = dicom['SOPClassUID'] ds.SOPInstanceUID = chguid(dicom['SOPInstanceUID']) ds.StudyInstanceUID = dicom['StudyInstanceUID'] ds.SeriesInstanceUID = dicom['SeriesInstanceUID'] + '.1' ds.InstanceNumber = index + 1 ds.SeriesNumber = str(int(dicom['SeriesNumber']) + 1)
ds.Rows = img.size[1] ds.Columns = img.size[0] ds.SamplesPerPixel = 3 ds.PhotometricInterpretation = "RGB" ds.HighBit = 7 ds.BitsStored = 8 ds.BitsAllocated = 8 ds.PixelRepresentation = 0
ds.PixelData = img.tobytes()
ds.file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRBigEndian ds.is_little_endian = False ds.is_implicit_VR = False
file_name = 'report_{}.dcm'.format(index) file_path = os.path.join(dicom_dir, file_name) ds.save_as(file_path)
def main(pdf_path, dicom_dir): images_list = convert_from_path(pdf_path)
dicom_info = read_dcm(dicom_dir)
for index, img in enumerate(images_list): write_dcm(index, img, dicom_info, dicom_dir)
if __name__ == '__main__': pdf_path = '/home/ray/dcm_report/report.pdf' dicom_dir = '/home/ray/dcm_report/dcm_data'
main(pdf_path, dicom_dir)
|