DICOM文件格式与编程

DCMTK实现测试文件是否使用 DICOM 第 10 部分格式(附完整源码) 阅读详情

任何图像文件格式无非是由两个部分组成:存参数的 header 和图点数据(pixel data)。
BMP、 JPEG、TIFF 之类的格式的 header 只描述图像的基本参数:如几行、几列、每点用了几位、有没有压缩、调色板等等。Header 往往是固定长度的。
 而医疗影像还要许多其它参数,如病人基本资料、检验基本资料、系列资料、位置资料等等。而且每种Modality 和每种 image 所需要的内容不一样。因此,一般的图像格式不能使用。
 
一、DICOM 的 4 个内容层次
1、 Patient  (病人)
2、 Study   (检验)
3、 Series   (系列)
4、 Image   (图像)
 尽管头几层的内容在很多图像里是相同的,但它们在每个图像文件里都要有。
 每一层叫一个Information Entity或IE(从relational database schema 设计引用而来)。每一层又细分成Module。每个Module里面的最小单元叫做一个attribute或element。
 现在举个例子:CR 图像 (DICOM Part 3, A.2.3, Table A.2-1
1. Patient IE:
   a. Patient Module  (参考 C.7.1.1)
2. Study IE:
   a. Study Module    (参考 C.7.2.1)
   b. Patient Study Module (参考 C.7.2.2)
3. Series IE:
   a. General Series (参考 C.7.3.1)
   b. CR Series (参考 C.8.1..1)
   c. General Equipment (参考 C.7.5.1)
4. Image IE:
   a. Genrral Image (C.7.6.1)
   b. Image Pixel (C.7.6.3)
   c. Contrast/bolus (C.7.6.4)
   d. CR Image (C.8.1.2)
    ...
   i. SOP Common (C.12.1)
 将这些 modules (tables) 里的所有 elements 都找出来就做成了一个 CR 图像的架构。
要注意的是这些 module 有些是一定要的 (modatory) 有些是用户选用的 (user)。到了每个 module 里 attribute/element 表又有分五类 Type 1, 1C, 2, 2C 和 3。 Type 1 是一定要的,2 也是一定要的但是内容可以是空的。Type 3 则可要可不要。所以浓缩一下,一个 CR 图像里的元素 (elements) 也不是太多。把这些表格展开后,这些 elements 组成一个 dataset。
 那么,写到一个文件里或通过网路传送又是个什么格式呢?这就要看 Part 5。一个元素 (element) 的结构是:
1. group tag: 16-bit
2. element tag: 16-bit
3. length (or VR/length): 32-bit
4. data (bytes of length)

对应每一个用到的 element DICOM 标准 Part 6 都定义了一个 group tag 和 element tag。比如说:
patient name:  0x0010, 0x0010
patient ID:    0x0010, 0x0020
...
 VR说的是element格式,比如说patinet name 的VR是PN。格式是 last_name^first_name^middle_name^prefix^surfix。那么我的英文名字就是: Wang^JB^^Dr.^
往外写时要做几个事情:
1. 要把所有元素按 group tage 和 element tag 理一遍 (sort)。从小排到大。
2. 如果是写 DICOM 介质的 DICOM file 还先写 128 bytes preamble (一般是空白),加 "DICM", 加 group 2 Meta header。(讲到 Part 10 时再细说)
3. 如果 dataset 里面含有 Sequence elements, sequence 里面每一个 Item 又是一个 dataset。
 有了这些知识,你就可以开始写一个小小的 BMP 到 DICOM 的转化程序。关键资料:
Modality (0008, 0060): SC
Photometric Interpretation (0028, 0004): RGB
SOP Class UID:  1.2.840.10008.1.5.4.1.1.7
最简单的办法是写一个 structure 然后一个 array。
typedef struct DicomElem
{
  short int group_tag,
  short int element_tag,
  char VR[4],
  int length,
  char data[128]
} DicomElem;

 

DicomElem CRDataSet [] =
{
{ 0x0008, 0x0005, "CS", 10, "ISO_IR 100"},
{ 0x0008, 0x0008, "CS", 16, "ORIGINAL//PRIMARY"},
  ...
{0x0010, 0x0010, "PN", 16, "My^Test^Image^^ "},
{0x0010, 0x0020, "SH", 6, "123456"},
...
{0,0,"",0,""}
};

void WriteCDImage(FILE *fp)
{
  DicomElem elem = CRDataSet[0];
  unsigned long int lComboTag;
  int nCols, nRows;
  unsigned char *pPixelData
  unsigned long int lPixelLength;

  pPixelData = LoadBMPImgeData("MyImage.bmp", nCols, nRows, lPixelLength);

  while(CRDataSet[i].group_tag)
  {
     lComboTag = (CRDataSet[i].group_tag << 16) | CRDataSet[i].element_tag;
    tch(lComboTag)
     {

       case 0x00280010:
           *((short int *)CRDataSet[i].data) = nCols;
        break;
       case 0x00280011:
           *((short int *)CRDataSet[i].data) = nRows;
       break;
       ...
     }
    
   // Write group and element tag
   fwrite(&lComboTag, 1, sizeof(long), fp);

   // Write VR
   fwrite(CRDataSet[i].VR, 1, 2, fp);

   if (lComboTag != 0x7fe00010)
     {
        fwrite(CRDataSet[i].length, 1, sizeof(short), fp);
        fwrite(CRDataSet[i].data, 1, CRDataSet.length, fp);
      }
   else
       {
          fwrite("/0/0", 1, 2, fp); // Two blank bytes after VR
          fwrite(&lPixelLength, 1, sizeof(long), fp);  // Length
         fwrite(pPixelData, 1, lPixelLength, fp);  
       }

  i++;

  }

}

unsigned char *LoadBMPImgeData(char *fileName, int &nCols, int &nRows, unsigned long &lPixelLength)
{
....
}

细节自己去写。

二、DICOM文件读写最难的是两件事情
    DICOM Sequence
    DICOM Pixel Data
 Sequence 在 C 里的类比是一个 structure 的 array,是结构套结构,所以读起来难。更麻烦的是 Sequence 还可以不定义长度, 即长度是 -1。要靠你自己去找 (FFFE, E0DD) 来决定 Sequence 是否结束。
 Array 里面的每个 structure就是 DICOM Sequence 里的一个 Item。Item 的开头是一个特定 element (FFFE, E000)。 如果 Item 的长度是 -1, 要靠找到 (FFFE, E00D) 来决定 Item 的结束。
 Pixel Data (7fe0, 0010) 是一个特殊的 DICOM element。总是在所有元素的最后面。它与 Sequence 有两个相似的地方:长度区总是 32-位 (即 explicit VR 的情况下要在 VR 区后面填两个 bytes,然后再加 4-bytes 的长度。
    如图像是压缩的,每幅图用一个 item 来存。 第一个 item 是个 offset table。每幅图的 offset 是一个 dword (4 bytes),第一幅图的 offset 是 0。

三、DICOM file 细节问题
    element 的 data 长度一定要是一个偶数。
    要注意 big endian 和 little endian 的区别。软件要自动判断机器的 endian 和 DICOM 文件的 transfer syntax。常见的:Intel PC 是 little endian, Sun 是 big endian。
 如果机器的 endian 和数据的 endian 不对的话要做 byte swap。要做 byte swap 的有所有的根数字有关的 binary data, 即 VR = SS, US, SL, UL, FL, FD, OW, AT 等等。DICOM group tag 和 element tag 也要分别做两个 byte 的 swap。Item 指的是 DICOM Sequence 的 Item。

0x7fe0, 0x0010, "OB", ' '' ', 0xffffffff  // tag, VR, 长度
0xFFFE, 0x0000, 0xffffffff  // Offset item begins
0x00000000  // Offset of first image = 0
0x00001000  // Offset of second image = 4096 (for example)
...
0xFFFE, 0xE00D  // End of offset item
0xFFFE, 0x0000, 0x00001000   // first image item and length = 4096 (for example)
... // (4096 bytes)
0xFFFE, 0xE00D  // end of first image
...
0xFFFE, 0xE0DD // End o
of pixel data sequence

DICOMRT-DiBugs:dicomParser解析无头DICOMcornerstone显示RGB-DICOM错误 背景介绍 cornerstone几乎是所有做B/S架构必要的学习框架,之前博文刚介绍过,cornerstone本身并不解析DICOM数据,而是依赖于dicomParser.js这个前端库。dicomParser库对DICOM格式的兼容性,目前是远远落后于dcmtk、dcm4che、fo-dicom这类后端库的,其本身并不是因为前/后端语言的差异,更多的是因为之前的解析都是放在后端,所以对前端库的维护和更新比较匮乏。 DICOMer星球,每日更新知识点,欢迎加入,一起交流学习! 此博文是DiBugs(即DI 阅读详情

相关推荐

22. DICOM图像层级分类-DCMTK-层级分类

本篇文章对应的DICOM原理部分,在专栏从零讲解DICOM协议-成像协议的文章DICOM层级关系,建议先了解原理,有助于理清代码思路。 DICOM有四个从上到下层级关系,分别是Patient(病人),Study(检查),Series(序列),Sop(图像)。   Patient可以有多个Study,Study可以有多个SeriesSeries可以有多个Sop。实际的意义为一个人可以做多次检查,一个检查的图像按照层厚、窗宽窗位、动静脉期等不同可以有不同的序列,一个序列需要多张图像才能完整显示扫描的器官。 根

tianma2012的博客 4648

开源项目常见问题解决方案:dicomParser

开源项目常见问题解决方案:dicomParser dicomParser 是一个用于解析 DICOM P10 字节流的轻量级库,它支持在现代 HTML5 浏览器(IE10+)、Node.js 和 Meteor 中使用。该项目主要使用 JavaScript 编程语言。 新手常见问题及解决方案 问题一:如何安装 dicomParser 问题描述: 新手用户可能不清楚如何正确安装 dicomParser...

gitblog_00819的博客 728

dicomParser:DICOM第10部分数据JavaScript解析器

dicomParser dicomParser是一个轻量级的库,用于在基于HTML5的现代Web浏览器(IE10 +),Node.js和Meteor中解析DICOM P10字节流。 dicomParser快速,易于使用并且没有必需的外部依赖关系。 现场例子 看到此库功能的最好方法是实际查看它的使用情况。 其中包含许多实时示例,这些示例不仅有用,而且还展示了如何使用dicomParser。 请确保您尝试了的它是非常有用的工具,并且是大多数功能的出色示例。 社区 有问题吗? 尝试在我们的网上。 安装 获取打包的源文件: 或通过NPM安装: npm安装dicom-parser 或通过大气安装以用于流星应用 流星添加chafey:dicom-parser 注意-如果需要支持Deflated Explicit VR Little Endian传输语法,请确保安装了pako 用法 // c

(转载)DICOM文件格式编程

任何图像文件格式无非是由两个部分组成:存参数的 header 和图点数据(pixel data)。 BMP、 JPEG、TIFF 之类的格式的 header 只描述图像的基本参数:如几行、几列、每点用了几位、有没有压缩、调色板等等。Header 往往是固定长度的。  而医疗影像还要许多其它参数,如病人基本资料、检验基本资料、系列资料、位置资料等等。而且每种Modality 和每种 image

rendawei636专注医疗软件 1292

DICOM文件格式编程(转)

<br />任何图像文件格式无非是由两个部分组成:存参数的 header 和图点数据(pixel data)。<br />BMP、 JPEG、TIFF 之类的格式的 header 只描述图像的基本参数:如几行、几列、每点用了几位、有没有压缩、调色板等等。Header 往往是固定长度的。<br />而医疗影像还要许多其它参数,如病人基本资料、检验基本资料、系列资料、位置资料等等。而且每种Modality 和每种 image 所需要的内容不一样。因此,一般的图像格式不能使用。<br /><br />一、DICO

linfei2707的专栏 1017

【python】读取dicom文件报错pydicom.errors.InvalidDicomError

【python】读取dicom文件报错pydicom.errors.InvalidDicomError 在使用python进行dicom文件的读取和显示时代码如下: import pydicom import matplotlib.pyplot as plt a = pydicom.read_file(r'C:\Users\shdn\Desktop\004.dcm') print(a) plt.imshow(img) plt.show() 但是有时会遇到以下错误(我是在读取医院所给数据时所出现以下错误):

weixin_46428351的博客 2786

python读取DICOM文件出现错误

dicom文件丢失DICOM文件元信息头或头中丢失“DICM”前缀。

RRawkoy的博客 827

DICOM、patient、study、series、img之间的关系

vczxh的专栏 884

DICOM图像中Patient_ID,series_instance_id study_instance_id 区别

一言以蔽之,就是一个患者patient可以做多次检查study,一次检查中可以包含多个部位的检测series

luanchishen6341的博客 5861

利用Task加载DICOM的Study,SeriesImage的类

这个类主要是利用了Task建立了一个一个工作列表, /******************************************************************** created: 2013/01/21 created: 21:1:2013 16:34 author: Li JianGong

muzizongheng的专栏 2266

使用GDCM读取DICOM Series

从之前的文章得知vtk中的DICOM读取类并不能直接读取压缩格式的DICOM文件,所以需要使用其他类库进行读取,然后转化成vtk可以识别的对象格式。上篇中已经介绍了使用DCMTK的实现,本篇将探索使用GDCM类库的实现。

摸鱼研究院 4799
上一篇: dicom文件的显示(基于dcmtk实现)
下一篇: DICOM简介
okaimee
博客等级 码龄20年 230粉丝 42原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值