PNG、JPG如何转Dicom(dcm),那些年我踩过的坑(Python版)

        Dicom作为医学影像的常见数据格式,是每个深耕于医疗AI的同学无法跳过的一个坑。虽然我只是一名扎根于算法部署方面的小白。但是也不可避免地接触到这类数据。这不,最近接到算法同学给出的算法,需要我自己找公开数据集进行测试。可是Dicom数据集并不常见(PS:测了1000张还嫌不够,大无语),因此只能将目光聚焦于PNG、JPG类型的数据集(直接用PNG、JPG训练的除外)。

        但是PNG、JPG类型的数据转Dicom并不容易,一不小心你就会收获“非标准Dicom”,网上的一些教程我也尝试了,很遗憾:转出来的Dicom不是黑不溜秋,就是无法识别。要么就是c++写的,编译来编译去,令人心烦。也尝试过用现成的Dicom数据,然后使用PNG、JPG的Data替换其中的Pixel Data。但是都无功而返!

        于是乎,我潜心钻研(东Copy西Copy),完成了这份python版本的PNG、JPG转Dicom。

目录

1.Dicom数据格式简介

2.PNG、JPG转Dicom(以PNG为例)

3.进一步完善Dicom 

4.结果展示


1.Dicom数据格式简介

        首先,在你尝试着将PNG、JPG类型的数据转换成Dicom数据之前,你可能需要浅浅了解一下Dicom数据的基本格式。

(1)preamble(前导):不重要,主要是为了向后兼容性和可扩展性而保留的若干个字节。

(2)prefix(前缀):不重要,主要是确认该文件是否符合DICOM标准。前导和前缀是可选的,对于DICOM文件来说,并不是必需的。

(3)File Meta Information(文件元信息头):重要!!!文件元信息头是DICOM文件的必要部分,其中包含了一些关键信息,如DICOM版本号、文件字节顺序、数据元素编码方式等。

(4)DataElements(数据元素):重要!!!是DICOM文件中包含的实际医学图像和相关信息的部分。

2.PNG、JPG转Dicom(以PNG为例)

        OK,知道了Dicom的数据结构,我们就能够针对主要的部分来转换我们的PNG、JPG。废话不多说,上代码!如果你不想看接下来的分析,你只需要修改main函数中的路径即可。

import os
import pydicom
from PIL import Imagedef png_to_dicom(input_png_path, output_dcm_path, patient_name="Anonymous", study_description="PNG to DICOM"):for fileNames in os.listdir(input_png_path):input_filename = os.path.basename(fileNames).split('.')[0]output_filename = input_filename + ".dcm"input_filepath = input_png_path + fileNamesoutput_dcmpath = output_dcm_path + output_filename# 读取PNG图像img = Image.open(input_filepath)# 将PNG图像转换为灰度图像(单通道)pixel_array = img.convert("L")# 创建一个空的FileDataset对象,并添加DICOM数据集元素ds = pydicom.dataset.FileDataset(output_dcm_path, {}, file_meta=pydicom.dataset.Dataset())  # 创建文件元信息头对象# 添加DICOM文件元信息头ds.file_meta.FileMetaInformationGroupLength = 184ds.file_meta.FileMetaInformationVersion = b'\x00\x01'ds.file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.1.1'ds.file_meta.MediaStorageSOPInstanceUID = '1.2.410.200048.2858.20230531153328.1.1.1'ds.file_meta.TransferSyntaxUID = '1.2.840.10008.1.2'ds.file_meta.ImplementationClassUID = '1.2.276.0.7230010.3.0.3.5.4'ds.file_meta.ImplementationVersionName = 'ANNET_DCMBK_100'# 添加DICOM数据集元素ds.PatientName = patient_nameds.StudyDescription = study_descriptionds.Columns, ds.Rows = img.sizeds.SamplesPerPixel = 1ds.BitsAllocated = 8ds.BitsStored = 8ds.HighBit = 7ds.PixelRepresentation = 0# 数据显示格式ds.PhotometricInterpretation = "MONOCHROME2"ds.PixelData = pixel_array.tobytes()  # 直接使用灰度图像的字节数据# 保存DICOM数据集到文件ds.is_little_endian = Trueds.is_implicit_VR = True  # 使用隐式VRds.save_as(output_dcmpath)print(output_dcmpath)if __name__ == "__main__":# 输入PNG图像路径和输出DICOM图像路径input_png_path = "Your_Input_PNG_Path"output_dcm_path = "Your_Output_Dicom_Path"# 将PNG转换为DICOMpng_to_dicom(input_png_path, output_dcm_path)

让我们来详细分析一下这部分代码:

(1)FileMetaInformationGroupLength:指定File Meta Information部分的长度,随意啦,别太离谱就行。

(2)FileMetaInformationVersion:表示File Meta Information部分的版本号。

(3)MediaStorageSOPClassUID:定义了影像的数据类型,每种类型有唯一的UID标识。如“1.2.840.10008.5.1.4.1.1.1.1”代表的是“Digital X-Ray Image Storage - For Presentation”

(4)MediaStorageSOPClassUID:唯一标识一个特定的影像数据实例。

(5)TransferSyntaxUID:表示DICOM图像数据的传输语法,它指定了数据在网络传输中的编码方式。每种方式有唯一的UID标识,比如“1.2.840.10008.1.2”代表的是“Implicit VR Little Endian”。

(6)ImplementationClassUID :用于标识实现DICOM标准的应用程序或设备的唯一标识符。

(7)ImplementationVersionName: 实现DICOM标准的应用程序或设备的版本名称或标识。

        看到这里你可能会问“你这乱七八糟的一堆数字,我怎么知道什么意思?” 聪明的我早就想到了,首先随便选一张标准的Dicom数据,然后执行如下代码:

import pydicom
dataset = pydicom.dcmread("Your_Dicom_Path", force=True)
print(dataset.file_meta)

        然后你会看到下面一堆信息,如果你想更换MediaStorageSOPClassUID和TransferSyntaxUID,那你就要自己去查对应的UID喽,所以以下内容我不建议你自己换,除非你知道你要干嘛:

3.进一步完善Dicom 

         哈哈哈哈,没想到还有吧!实际上,通过第2步,你已经可以获得一张用于展示的Dicom数据格式了。但是仅此而已,如果你想要做算法或者跟我一样,去验证别人的算法。那么,这一步是必不可少的。

        在第2步中,我们为新的Dicom添加了File Meta Information(文件元信息头)和部分DataElements(主要是Pixel Data)。因此这份Dicom是可以正常被读取、浏览的。但是,如果是用于算法训练或者验证算法,是需要确保这份Dicom数据的唯一性的。

        为了便于理解,确保Dicom数据唯一性的代码,我另起了一个新的py文件:

import os
import pydicom# 源文件夹和目标文件夹路径
source_folder = 'Your_Input_Dicom_Path'
target_folder = 'Your_Output_Dicom_Path'patient_pid = 20230726001
accession_number = 202307261001
study_uid = 2023072620001
seriesNumber = 1
seriesInstanceUID = "1.2.410.200048.2858.20230529094313.1"
modality = "CR"
pixelSpacing = [0.160145, 0.160114]
instanceNumber = 1
bodyPartExamined = "CHEST"# 遍历源文件夹中的文件
for filename in os.listdir(source_folder):if filename.endswith('.dcm'):# 构建源文件路径和目标文件路径source_file = os.path.join(source_folder, filename)target_file = os.path.join(target_folder, filename)# 加载源DCM文件dcm_data = pydicom.dcmread(source_file, force=True)# 添加患者PID、Accession Number和Study UID等信息dcm_data.PatientID = str(patient_pid)dcm_data.AccessionNumber = str(accession_number)dcm_data.StudyInstanceUID = str(study_uid)dcm_data.SeriesNumber = seriesNumberdcm_data.SeriesInstanceUID = seriesInstanceUIDdcm_data.Modality = modalitydcm_data.PixelSpacing = pixelSpacingdcm_data.BodyPartExamined = bodyPartExamineddcm_data.InstanceNumber = instanceNumber# 将文件名作为患者名file_name_without_extension = os.path.splitext(filename)[0]dcm_data.PatientName = file_name_without_extension# 保存修改后的DCM文件到目标文件夹dcm_data.save_as(target_file)# 递增计数器patient_pid += 1accession_number += 1study_uid += 1else:print("error!")

同样的,我们来详细解析以下这部分代码 。

(1)patient_pid:患者的唯一标识符,你怎么开心怎么写。

(2)accession_numbe:分配给患者检查的唯一标识号码,唯一地标识特定的检查或一组医学影像,你怎么开心怎么写。

(3)study_uid:对应医学影像研究的ID,你怎么开心怎么写。

(4)seriesNumber:标识图像所属系列的编号,建议按照我这个来。

(5)seriesInstanceUID:唯一标识一个影像系列,建议按照我这个来,或者你找一张标准的Dicom,参考它怎么写。

(6)modality:用于获取图像的影像学模态,建议按照我这个来,或者你找一张标准的Dicom,参考它怎么写。

(7)pixelSpacing:像素在行和列方向上的物理间距,建议按照我这个来,或者你找一张标准的Dicom,参考它怎么写。

(8)instanceNumber:分配给图像中个别实例的唯一编号,通常用于区分一个系列中的不同图像,你怎么开心怎么写。

(9)bodyPartExamined:检查部位,根据实际写,你也可以不写。

4.结果展示

        需要说的是,PNG、JPG亦或是其它类型的数据,在转成Dicom的过程中不可避免地会出现一定的损失。如果在Dicom数据充足的情况下,无论是算法训练还是验证,都建议使用Dicom(直接用PNG训练的除外)。PNG、JPG转Dicom实属无奈之举啊!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3017551.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

NFCP502-W05 电流数据是多少安培?

YOKOGAWA NFCP502-W05 是一款由横河电机(Yokogawa Electric Corporation)生产的微型断路器(Microcircuit Breaker,简称 MCB)。 横河电机是一家日本的跨国公司,专注于自动化和控制系统、仪器和其他相关设备…

【计算机科学速成课】笔记三

文章目录 17.集成电路真空管时代晶体管时代集成电路时代印刷电路板时代光刻时代 17.集成电路 Over the past six episodes, we delved into software, 过去 6 集我们聊了软件 \N 从早期编程方式到现代软件工程 from early programming efforts to modern software engineerin…

Linux进程地址空间第三讲

至今为止, 我们所学到的大多数的知识, 包括语言, 数据结构, 动静态库等等的 都是在下面这3G, 也就是用户空间里的(进程等待, 信号之类的与内核有关的是在上面那1G里的) 所以对于我们来说, 我们…

NXP i.MX8系列平台开发讲解 - 1.1 导读前言

专栏文章目录传送门:返回专栏目录 文章目录 目录 1. 本专辑介绍 2. 学习本专辑作用 3.关于作者 1. 本专辑介绍 本专辑将会介绍Linux 驱动开发,Android BSP 驱动涉及HAL层调试,适用于嵌入式软件开发人员,和有兴趣向该方向发展…

题目----力扣--移除链表元素

题目 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 示例 1: 输入:head [1,2,6,3,4,5,6], val 6 输出:[1,2,3,4,5]示例 2: 输入&…

1-2 ARM单片机GPIO

def:通用输入输出口 GPIO输出模式原理讲解 1:推挽输出 2:复用推挽输出 电流最大是20mA,对于单片机来说总体的输出是由范围的 开漏/复用开漏输出 外部接上拉电阻的开漏输出 线与的概念 注: 与的概念:全1为1&…

动态内存开辟(下)

前言 动态内存开辟以及柔性数组的介绍 一、 几个经典的笔试题 1. 题目一 void Getmemory(char*p) {p (char*)malloc(100); } int main() {char* str NULL;Getmemory(str);strcpy(str, "hello world");printf(str);return 0; } 这段代码我们可以发现两个很明显…

2-5 任务:打印九九表

本次实战的目标是通过编写程序实现打印九九乘法表、字符矩形、字符平行四边形和字符菱形等图形,以及解决百钱买百鸡问题和输出素数等实际问题。在实战过程中,我们将学习并掌握以下知识点。 双重循环的使用:通过双重循环实现九九乘法表的打印&…

视频素材库在哪里找免费手机版?8个可以用手机浏览的素材网

在视觉内容占据主导地位的今天,合适的视频素材可以大大提升项目的吸引力和效果。以下列出的视频素材网站为广告制作者、社交媒体策略师及电影制作人提供了从传统到现代风格的各种视频素材选择,满足不同的创作需求。 1. 蛙学府(中国&#xff…

大模型系列之解读MoE

Mixtral 8x7B 的推出, 使我们开始更多地关注 基于MoE 的大模型架构, 那么,什么是MoE呢? 1. MoE溯源 MoE的概念起源于 1991 年的论文 Adaptive Mixture of Local Experts(https://www.cs.toronto.edu/~hinton/absps/jjn…

艺术的新领域——探索元宇宙艺术展带来的沉浸式艺术体验

在数字化的浪潮中,元宇宙艺术展成为了一种全新的展览形式,它通过虚拟现实、3D建模技术和互动平台,将传统艺术与现代科技巧妙结合,提供了一种前所未有的艺术欣赏方式。此类展览不仅展示了艺术作品的新颖呈现,还为参观者…

京东生产环境十万并发秒杀系统三高架构

文章目录 三高——高并发、高可用、高可扩展用数据库乐观锁解决超卖阿里巴巴:为了提升数据库性能,对数据库的源码级别做了改造——在DB内部实现内存队列,一次性接收很多的请求,一次性更新。京东:redis,mq&a…

【C++ 关键字】const 关键字详解

文章目录 1. const 概念2.常量指针 和 指针常量 的区别2.1 常量指针(底层 const)2.2 指针常量 (顶层 const) 3.const 关键字的作用4.const 和 define 的区别5.const 总结 1. const 概念 const 是一个关键字,被修饰的值不能改变,是…

AI模型:windows本地运行下载安装ollama运行Google CodeGemma可离线运行数据模型【自留记录】

AI模型:windows本地运行下载安装ollama运行Google CodeGemma可离线运行数据模型【自留记录】 CodeGemma 没法直接运行,需要中间软件。下载安装ollama后,使用ollama运行CodeGemma。 类似 前端本地需要安装 node.js 才可能跑vue、react项目 1…

字节和旷视提出HiDiffusion,无需训练,只需要一行代码就可以提高 SD 生成图像的清晰度和生成速度。代码已开源。

字节和旷视提出HiDiffusion,无需训练,只需要一行代码就可以提高 SD 生成图像的清晰度和生成速度。代码已开源。 支持将图像生成的分辨率提高至40964096,同时将图像生成速度提升1.5至6倍。 支持所有 SD 模型同时也支持 SD 模型的下游模型&…

在Unity中实现分页数据显示和分页控制

参考:用两种简单的方式实现unity的分页效果 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.Rendering.VirtualTexturing; using UnityEngine.TerrainUtils;public class PageControll…

上网行为审计软件分享|三款热门上网行为监控软件推荐

“小王,去找一款软件给我们公司安上,你去搜上网行为审计软件,看看买哪家合适” 这是某公司老板交给助理的一项工作,原话是这样的。 可见其实这类软件大多是人还是比较陌生的。 上网行为审计软件顾名思义就是对上网行为也就是电…

PTA|小字辈

题目 本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。 输入格式: 输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,我们把家族成员从 1 到 N 编号。随后第二行给出 N 个编号&#x…

streamlit通过子目录访问

运行命令: streamlit hello 系统默认使用8501端口启动服务: 如果想通过子目录访问服务,可以这么启动服务 streamlit hello --server.baseUrlPath "app" 也可以通过以下命令换端口 streamlit hello --server.port 9999 参考&…

面试题:String类型长度有限制吗?最大多少?

简介 Java中String是有长度限制的。String还有长度限制?是的有,而且在JVM编译中还有规范,String长度限制的场景(将某固定文件转码成Base64的形式用字符串存储,在运行时需要的时候在转回来,当时文件比较大),那这个规范限制到底是怎么样的,我们分析下。 …