DICOM文件格式与编程

来源:互联网 发布:大数据分析网站 编辑:程序博客网 时间:2024/05/20 17:58

任何图像文件格式无非是由两个部分组成:存参数的 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 begins0x00000000  // Offset of first image = 00x00001000  // Offset of second image = 4096 (for example)...0xFFFE, 0xE00D  // End of offset item0xFFFE, 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



 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 干了活不给钱怎么办 干了活要不到钱怎么办 活干完了钱不给怎么办 微信语音聊天音量很小怎么办 一手软件崩溃钱卡住了怎么办 身上皮肤很黑怎么办?好想穿短裙 家里有很多小飞虫怎么办 家里有垃圾中飞出虫子怎么办 雷蛇笔记本很烫怎么办 登录监控器的账号锁了怎么办 悦借钱登录账号被锁怎么办 365账号登录被锁怎么办 台式电脑cpu温度过高怎么办 UG2.0打不开错误—15怎么办 键盘空格键删除键失灵怎么办 耳麦检测声音不分左右怎么办 吃生萝卜胃疼怎么办 幼兔拉稀怎么办没精神 自酿米酒酸了怎么办 用冰敷脸感觉红烫了怎么办 吃了海兔的内脏怎么办 吃了鱿鱼的吸盘怎么办 想开个烧烤店没学过怎么办 墨鱼汁弄衣服上怎么办 干鱿鱼泡开发黄怎么办 吃了芥末胃疼怎么办 手撕鱿鱼咸了怎么办 孕妇吃了点芥末怎么办 葡萄酒上面有一层白霉怎么办 手机一不小心把视频删了怎么办 柑橘7月份果实小怎么办 鹦鹉鱼身上有小白点怎么办 鹦鹉鱼身上烂了怎么办 红鹦鹉鱼变黑了怎么办 唱美声总夹嗓子怎么办 发财树树干出褶怎么办 月季花夏天换盆出现黄叶怎么办 刚种的月季枯了怎么办 新买的月季黄叶了怎么办 月季花叶孑轰发黄怎么办? 新种的月季苗弱怎么办?