DICOM文件的格式详细解说(1)

1 DICOM的详细介绍

DICOM要到这里面看Current Edition,这是标准委员会制定的标准,同时也在与时俱进,不断的进行新的数据格式更新。

2 DICOM的格式

图1 DICOM文件格式的图示

要先说一下数据结构,我们被最多影响的是数据结构和算法的先导知识。什么队列,链表,二叉树,红黑树,无向图等概念,这些概念是一个抽象的逻辑概念。其实数据在内存中是顺序存储的,你看看单片机开发,和汇编语言就回明白了。一个DICOM文件在磁盘上也是顺序存储的,就像一张图片,在电脑硬盘上的存储是拆成一列一列或者是一行一行的存储的,当然了,数据的前面要有一个文件头,说明此图像的基本信息,比如文件格式,颜色通道,数据位,8位还是16位图等,以及长和宽等。当一副图像被读入内存中,也是按照顺序存储的从文件头到文件的结尾。

从图1中,我们可以看到一个DICOM的文件格式也是顺序的,导言部分,按照顺序是128个空字节,然后是‘DICM’四个字节的标识,接着是meta信息,这个多少个字节根据语法决定的。接下来是数据元素,一个接着一个。数据元素的组成如图2所示

图2 数据元素的组成

我们可以看到数据元素是由4个部分组成的,第一部分是Tag,标签,两个字节,使用16进制表示。第二部分是VR值表示,说明后面的数据(Value Field)的类型,比如字符型还是整型数据,这个跟读取方式有关,很重要。第三部分是值的长度,一般都是偶数(为了数据的整齐),表示后面的数据的长度(字节)。

3 实践部分

我们使用纯C++来读取,不使用第三方解析工具,等理解了之后再使用DCMTK来解析。

先介绍几个用到的函数,读取4个字节的函数

void read4Byte(std::fstream &file,char* str){    
for(int i=0;i<4;++i){        
*str=readByte(file);        
str++;    
}
}

上面的这个函数其实还关系到一个局部变量的返回问题,传入一个全局数组,可以避免局部数组返回问题。还可以使用局部在堆上new一个数组的方法,然后返回指针。这个以后再说吧。

使用下面的函数读取Tag标签:
void readTag(std::fstream& file){    
char byte[2];    
unsigned short group;    
unsigned short elem;    
file.read(byte,2);    
group=byte[1]<<8|byte[0];    
file.read(byte,2);    
elem= byte[1]<<8|byte[0];    
std::cout<<"["<<std::setw(4)<<std::setfill('0')<<std::hex<<group<<","<<std::setw(4)<<std::setfill('0')<<std::hex<<elem<<"]"<<std::dec<<" : ";
}

可以看到,每次读取两个字节,因为是16位数据,是无符号整型数据。分成两个部分,group和element。先读区的是group,两个字节。这里的小知识,数据在内存中是怎么存储的。

大家知道,现在的计算机是64位操作系统。计算机位数,简单来说就是计算机一次能处理的二进制数的位数。这个看似简单的概念,却承载着计算机发展史上的诸多变革。

  • 4位和8位时代:
    • 早期的微处理器,如Intel 4004,只有4位,只能处理非常简单的任务。
    • 随着集成电路技术的进步,8位微处理器逐渐成为主流,代表机型有Intel 8080和Zilog Z80。它们被广泛应用于个人电脑和嵌入式系统。
  • 16位和32位时代:
    • 16位微处理器标志着个人电脑时代的到来,代表机型有Intel 8086和Motorola 68000。
    • 32位微处理器的出现,大大提高了计算机的性能,满足了对多任务和大型软件的需求。Intel 80386和80486是这一时代的代表。
  • 64位时代:
    • 64位微处理器能够直接寻址更大的内存空间,处理更复杂的数据,为现代操作系统和应用程序提供了强大的支持。Intel x86-64架构成为主流。

由于历史的原因,现在一个字节还是8位,64位机器一次可以处理8个字节。我们计算机存储多个字节的数据,比如无符号整型数据,是16位,两个字节。计算机存储多个字节数据时有小端字节序和大端字节序(Little-Endian/Big-Endian),一般都是小端字节序,网络传输时大端字节序。

小端字节序(Little-Endian)是一种在计算机系统中存储多字节数据的顺序。在这种顺序下,最低有效字节(Least Significant Byte,LSB) 存储在最低的内存地址,而最高有效字节(Most Significant Byte,MSB) 存储在最高的内存地址

为什么会有小端字节序?

  • 历史原因: 不同的计算机架构师在设计计算机时,选择了不同的字节序存储方式。

  • 硬件设计: 某些硬件架构可能更适合小端字节序,例如早期的x86处理器。

  • 软件兼容性: 为了兼容已有的软件和硬件,一些系统选择保留小端字节序。

小端字节序的示例

假设我们要存储一个16位的整数170(十进制),其二进制表示为0000000101010000。在小端字节序系统中,这个整数会在内存中存储为:

地址          值

1000    10100000 (A0)

1001    00000001 (01)

可以看到,最低字节A0存储在地址1000,而最高字节01存储在地址1001。

小端字节序与大端字节序

  • 大端字节序(Big-Endian): 与小端字节序相反,大端字节序将MSB存储在最低的内存地址。

  • 选择: 不同的计算机系统采用不同的字节序,例如x86架构通常是小端序,而PowerPC和大多数网络协议(如TCP/IP)采用大端序。

所以我们用了下面的方法,将读取到的高8位向左移动8位,然后跟低8位的数据或一下,组成无符号整型数据

group=byte[1]<<8|byte[0];

然后再读取下一个element数据,同样处理。但是显示的时候需要使用16进制,这个是DICOM标准规定的,只要是要用这个标签去查dicom词典,知道标签的含义,比如,姓名,数据的位数,长,宽等。

还有读取字符串的函数:

void readString(std::fstream &file, char *str,int length){

for(int i=0;i<length;++i){

*str=readByte(file);

str++;

}

}

读取整型数据的函数:

int getInt(std::fstream& file){

char byte[4];

file.read(byte,4);

int value=byte[3]<<24|byte[2]<<16|byte[1]<<8|byte[0];

value&=0xff;   //这个要有,只取低8位的数据。

//cout<<value<<endl;

return value;

}

还有一些函数在这里

char readByte(std::fstream& file){    
char byte[1];    
file.read(byte,1);    
return byte[0];
}
void read2Byte(std::fstream& file){    
char byte[2];    
file.read(byte,2);    
std::cout<<" "<<byte[0]<<byte[1]<<" ";
}
然后我们看主函数中的内容和输出
string filename="../data/CT000011.dcm"; // 文件名  
std::fstream file;                 //定义一个stream 在文件头#include <fstream>
file.open(filename.c_str(),ios::in|ios::binary);  // open the dicom file as binary    // 以二进制模式打开文件
file.seekg(128,ios::beg);  // skip the 128 byte //跳过前面的128个空字节
接下来就开始读区Data Element(数据元素了)
char str[128];  // 用来存储读取的字符串,128位应该够用了   
read4Byte(file,str);    // 读取‘DICM’四个字符
std::cout<<"DICOM marker (4 Bytes):"<<std::string(str)<<std::endl;

****DICOM marker (4 Bytes):DICM

// begin read tag and its value   readTag(file);    
read2Byte(file);  这个会显示UL,表示一个4个字节的整型数据  
int length=getShort(file);    
std::cout<<length<<" # ";    //长度 是4个字节
//readString(file,str,length);   std::cout<<getInt(file)<<std::endl;  // 显示获取的整型数据是214

****[0002,0000] : UL 4 # 214

readTag(file);    
read2Byte(file);    
length=getShort(file);    
std::cout<<length<<" # ";    
read2Byte(file);    
read2Byte(file);    
read2Byte(file);  //skip 6 Byte by the transfer syntax definition.    
// reference:https://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html    std::cout<<std::endl;

****[0002,0001] : OB 0 # 。//这个OB是一个与传输语法有关的长度,在这里是6个字节,直接使用三次读取2个字节的方法跳过。

readTag(file);    
read2Byte(file);    
length=getShort(file);    
std::cout<<length<<" # ";    
readString(file,str,length);    
std::cout<<std::string(str)<<std::endl;    
std::memset(str,0,sizeof(str));  // cause we use the str varible again and again
****[0002,0002] : UI 26 # 1.2.840.10008.5.1.4.1.1.2

这里最后使用memset方法将str清空,以免影响后面存储字符串。然后就开始循环使用上面的这一段就可以先读取一部分头部文件了。结果如下图

                                           图3 输出结果

跟使用DCMTK的输出基本是一致的,只是没有查询DICOM词典获取标签的含义,但是还是有一些小小的差异。

                            图4 使用DCMTK解析的Metainfo结果

小伙伴们,还没有解析到图像呢,等待下一集吧。

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

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

相关文章

Trilium Notes笔记本地化部署与简单使用指南打造个人知识库

文章目录 前言1. 安装docker与docker-compose2. 启动容器运行镜像3. 本地访问测试4.安装内网穿透5. 创建公网地址6. 创建固定公网地址 前言 今天和大家分享一款在G站获得了26K的强大的开源在线协作笔记软件&#xff0c;Trilium Notes的中文版如何在Linux环境使用docker本地部署…

贝励德故事:注册电气工程师有多难考?(知乎)

之所以说是条不归路&#xff0c;给你看看我的复习资料就知道了 这是需要买的手册和规范&#xff0c;加起来有五十斤重了。你还得在三小时内找到知识点结合题意计算作答&#xff0c;计算器不能出一点差错。这个难度可想而知。 这是我备考两年多做过的题&#xff0c;看过的总结&a…

Spring Boot框架下的足球青训俱乐部管理

2 相关技术简介 2.1 Java技术 Java是一门伟大的纯面向对象的编程语言和编程语言。同时&#xff0c;它还是Java语言从嵌入式开发到企业级开发的平台。Java凭借其一次编译&#xff0c;任何地方执行的优点&#xff0c;使得盛行的web应用程序有大量的Java编译&#xff0c;很好地支…

AMD CDNA™2 GPU 中的寄存器压力

Register pressure in AMD CDNA™2 GPUs — ROCm Blogs 注意&#xff1a; 此博客以前是 AMD实验室笔记 博客系列的一部分。 GPU kernel 中的寄存器压力对高性能计算 (HPC) 应用程序的整体性能有着巨大的影响。理解和控制寄存器的使用可以让开发者精心设计代码&#xff0c;以最…

jQuery——元素尺寸

1、内容尺寸 height&#xff08;&#xff09;&#xff1a;height width&#xff08;&#xff09;&#xff1a;width 2.内部尺寸 innerHeight&#xff08;&#xff09;&#xff1a;height padding innerWidth&#xff08;&#xff09;&#xff1a;width padding 3、外部尺寸…

MES数据的集成方式

为了实现与其他关键系统的数据共享和协同工作&#xff0c;不同的集成方式应运而生。MES系统与其他系统的常见集成模式&#xff0c;包括封装接口调用模式、直接集成模式、数据聚合模型、中间件集成模式以及XML的信息集成模式等。 1. 封装接口调用模式 封装接口调用是一种常见的…

2024年9月中国干旱监测报告(FYDI-2.0指数)

目录 引言 旱情监测与分析 资料来源 引言 2024年9月&#xff0c;北方的降水逐渐增多&#xff0c;进入华西秋雨集中期&#xff0c;从青藏高原北部一直延伸到东北多地&#xff0c;常出现大范围的云带&#xff0c;西北地区的降雨强度较大。南方地区降水分布不均&#xff0c;受…

如何测试网络质量?

如何测试网络质量&#xff1f; 通过百度网盘分享的文件&#xff1a;winMTR 链接&#xff1a;https://pan.baidu.com/s/1Zfw4jciNhng35nfwBlF75Q 提取码&#xff1a;6622 –来自百度网盘超级会员V2的分享 下载WINMTR工具&#xff0c;在启动处输入www.baidu.com 判断方法&…

企业数字化转型的深层次问题与战略解读——基于TOGAF框架的深入分析与解决方案

数字化转型的必然性与复杂性 随着全球化和技术进步的推动&#xff0c;数字化转型成为企业保持竞争力、提升效率、满足客户需求的重要战略选择。然而&#xff0c;数字化转型并不仅仅是技术的简单引入&#xff0c;它涉及到业务模式、运营流程、组织架构以及企业文化的深刻变革。…

成都大学体育场馆预约系统—计算机毕业设计源码37087

摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&#xff0c;使信息存…

软件开发人员绩效考核方案(参考)

1、产品&运营绩效考核表 2、开发绩效考核表 3、测试绩效考核表 4、CPI指标库 软件全套资料部分文档清单&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查…

【网站打包app】Prime Web 1.0.10 – 将网站转换为 Flutter 应用程序 |Web View 应用程序 |Web 到 App

Prime Web – 将您的网站转换为 Flutter 应用程序。您只需替换您的URL&#xff0c;就可以将您的网站转换为Android和iOS应用程序。 Prime Web Flutter 应用程序功能 推送通知下拉刷新下载和上传RTL 支持亮度和深色模式OneSignal 通知支持所有数据库应用链接和分享

Codeforces Round 976 (Div. 2 ABCDE题)视频讲解

A. Find Minimum Operations Problem Statement You are given two integers n n n and k k k. In one operation, you can subtract any power of k k k from n n n. Formally, in one operation, you can replace n n n by ( n − k x ) (n-k^x) (n−kx) for any no…

「漏洞复现」EDU 某智慧平台 PersonalDayInOutSchoolData SQL注入漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任。工具来自网络&#xff0c;安全性自测&#xff0c;如有侵权请联系删…

半导体器件基础09:MOS管特性和应用(2)

说在开头&#xff1a;关于德布罗意的电子波&#xff08;1&#xff09; 德布罗意家族的历史悠久&#xff0c;他的祖先中出了许许多多将军、元帅、部长&#xff0c;参加过法国几乎所有的战争和各种革命&#xff0c;后来受到路易.腓力的册封&#xff0c;继承了这最高世袭身份的头…

数据中心交换机与普通交换机之间的区别到底在哪里?

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 上午好&#xff0c;我的网工朋友。 数据中心交换被设计用来满足数据中心特有的高性能、高可靠性和可扩展性需求。 与此同时&#xff0c;普通交换机…

全面提升MySQL性能:从硬件到配置再到代码的最佳实践

MySQL 是全球最流行的开源关系型数据库管理系统之一&#xff0c;广泛应用于各种规模的应用程序中。随着应用规模的增长&#xff0c;数据库的性能优化成为提升系统整体性能的关键因素。本文将从多个角度探讨如何对MySQL进行性能优化&#xff0c;帮助开发者和DBA解决实际问题&…

免费 Oracle 各版本 离线帮助使用和介绍

文章目录 Oracle 各版本 离线帮助使用和介绍概要在线帮助下载离线文档包&#xff1a;解压离线文档&#xff1a;访问离线文档&#xff1a;导航使用&#xff1a;目录介绍Install and Upgrade&#xff08;安装和升级&#xff09;&#xff1a;Administration&#xff08;管理&#…

Android 13.0 系统wifi列表显示已连接但无法访问网络问题解决

1.前言 在13.0的系统rom产品定制化开发中,在wifi模块也很重要,但是在某些情况下对于一些wifi连接成功后,确显示已连接成功,但是无法访问互联网 的情况,所以实际上这时可以正常上网的,就是显示的不正常,所以就需要分析连接流程然后解决问题 如图所示: 2.系统wifi列表显示…

linux文件编程_进程

1. 进程相关概念 面试中关于进程&#xff0c;应该会问的的几个问题&#xff1a; 1.1. 什么是程序&#xff0c;什么是进程&#xff0c;有什么区别&#xff1f; 程序是静态的概念&#xff0c;比如&#xff1a; 磁盘中生成的a.out文件&#xff0c;就叫做&#xff1a;程序进程是…