在处理dicom文件过程中,往往需要插入自定义的tag,并保存为dicom文件。在网上查资料,都比较少,经过一番探索,有点收获。与大家分享,希望能帮助到一些朋友,并共同探讨。
一 dicom文件tag
相关dicom介绍在这里不做赘述。dicom的Tag,分为两种:1.保留tag,为dicom自有字段,保存在偶数组号中(如 0x0008, 0x0010);2.private tag,为自定义字段,保存在奇数号码中如( 0x0029, 0x0011)。相关介绍在dicom标准的7.8。
7.8 私有数据元素
实现时可能需要不包含在标准数据元素中的信息通讯。私有数据元素需要包含这些信息。
私有数据元素与指定在7.1部分(即,数据元素标签字段,可选VR字段,长度字段和值字段)中的标准数据元素有相同的结构。使用在私有数据元素的元素标签中的组号码应该是一个奇数号码。私有数据元素应以数据元素标签的递增数字顺序包含在数据集中。私有数据元素的值字段应具有标准6.2部分中指定的VRs中的任意一种。
对于每一个信息对象定义或SOP类定义,按照说明在DICOM标准的第3部分和第4部分中的内容,特定数据元素是必需的。私有数据元素不能代替所需的标准数据元素。
7.8.1 私有数据元素标签
多个实现者定义带有相同(奇数)组号码的情况是可能的。为了避免冲突,私有元素将根据下面的规则来分配私有数据元素标签。
a) 编号为(gggg,0010-00FF)(gggg为奇数)的私有创作者数据元素用来存储由私人使用的组号码为gggg的一组元素。系统将给这一系列私有元素中的第一个未使用的(未赋值的)元素插入标识码。私有标识码的VR将成为LO,VM将等于1。
b) 私有创作者数据元素(gggg,0010)是等同于系统存储元素(gggg,1000-10FF)的1类数据元素,私有创作者数据元素(gggg,0011)等同于系统存储元素(gggg,1100-11FF),以此类推,直到私有创作者数据元素(gggg,00FF)等同于系统存储元素 (gggg,FF00-FFFF)。
c) 私有数据元素的编码器能够动态地将私有数据分配到私有组中的任一可利用块中,并详细说明分配所对应的私有创作者数据元素。私有数据的译码器能够由对应的私有创作者数据元素在私有组中的任何位置使用给定的私有创作者标识码识别存储块。
二 插入私有Tag
网上关于插入私有Tag的资料及demo较少,基本上都指向维基百科的一篇资料(得不到预期结果!!!) https://support.dcmtk.org/redmine/projects/dcmtk/wiki/howto_addprivatedata,代码如下:
1 #include "dcmtk/config/osconfig.h" 2 #include "dcmtk/dcmdata/dctk.h" 3 4 #define PRIVATE_CREATOR_NAME "Your Company Name" 5 6 #define PRIVATE_CREATOR_TAG 0x0029, 0x0010 7 #define PRIVATE_ELEMENT1_TAG 0x0029, 0x1000 8 #define PRIVATE_ELEMENT2_TAG 0x0029, 0x1010 9 #define PRIVATE_ELEMENT3_TAG 0x0029, 0x1020 10 11 #define PRV_PrivateCreator DcmTag(PRIVATE_CREATOR_TAG) 12 #define PRV_PrivateElement1 DcmTag(PRIVATE_ELEMENT1_TAG, PRIVATE_CREATOR_NAME) 13 #define PRV_PrivateElement2 DcmTag(PRIVATE_ELEMENT2_TAG, PRIVATE_CREATOR_NAME) 14 #define PRV_PrivateElement3 DcmTag(PRIVATE_ELEMENT3_TAG, PRIVATE_CREATOR_NAME) 15 16 void registerPrivateTags() 17 { 18 DcmDataDictionary &dict = dcmDataDict.wrlock(); 19 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT1_TAG, EVR_LO, "PrivateText", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 20 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT2_TAG, EVR_US, "PrivateInteger", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 21 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT3_TAG, EVR_OB, "PrivateBlob", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 22 dcmDataDict.unlock(); 23 } 24 25 void addPrivateElements(DcmItem &item) 26 { 27 if (!item.tagExists(PRV_PrivateCreator)) 28 { 29 item.putAndInsertString(PRV_PrivateCreator, PRIVATE_CREATOR_NAME); 30 item.putAndInsertString(PRV_PrivateElement1, "Some Text"); 31 item.putAndInsertUint16(PRV_PrivateElement2, 12345); 32 item.putAndInsertUint8Array(PRV_PrivateElement3, NULL /*data*/, 0 /*length*/); 33 } 34 } 35 36 int main() 37 { 38 DcmFileFormat fileformat; 39 fileformat.loadFile("test_in.dcm"); 40 registerPrivateTags(); 41 addPrivateElements(*fileformat.getDataset()); 42 fileformat.saveFile("test_out.dcm", EXS_LittleEndianExplicit); 43 fileformat.print(COUT); 44 return 0; 45 }
高高兴兴得使用这个demo,结果只能插入PRV_PrivateCreator 字段,做了很多次尝试,结果一样。刚开始怀疑自己哪里出错了,结果在bing上看到一个老哥说遇到跟我一样的问题,意识到可能是代码问题。只好再做其他尝试。
在看dicom标准过程中,看到一段话:
a) 编号为(gggg,0010-00FF)(gggg为奇数)的私有创作者数据元素用来存储由私人使用的组号码为gggg的一组元素。系统将给这一系列私有元素中的第一个未使用的(未赋值的)元素插入标识码。私有标识码的VR将成为LO,VM将等于1。
受到启发,系统将给这一系列私有元素中的第一个未使用的(未赋值的)元素插入标识码。是不是不能使用0x0029, 0x0010标签,应该留给系统插入标识码?(没有依据,纯属猜测)
于是,修改tag标签地址,不使用0x0010标签:
#define PRIVATE_CREATOR_TAG 0x0029, 0x0011
#define PRIVATE_ELEMENT1_TAG 0x0029, 0x0013
#define PRIVATE_ELEMENT2_TAG 0x0029, 0x0014
#define PRIVATE_ELEMENT3_TAG 0x0031, 0x0010
#define PRIVATE_ELEMENT4_TAG 0x0029, 0x0020
结果还真能插入多个tag,打印tag信息,可以看到新插入的几个tag。
当以为成功结果问题的时候,遇到了新的问题:插入int型数据不成功。测试代码如下:
1 #include "dcmtk/config/osconfig.h" 2 #include "dcmtk/dcmdata/dctk.h" 3 4 #define PRIVATE_CREATOR_NAME "Your Company Name" 5 6 #define PRIVATE_CREATOR_TAG 0x0029, 0x0011 7 #define PRIVATE_ELEMENT1_TAG 0x0029, 0x0013 8 #define PRIVATE_ELEMENT2_TAG 0x0029, 0x0014 9 #define PRIVATE_ELEMENT3_TAG 0x0031, 0x0010 10 #define PRIVATE_ELEMENT4_TAG 0x0029, 0x0020 11 12 #define PRV_PrivateCreator DcmTag(PRIVATE_CREATOR_TAG) 13 #define PRV_PrivateElement1 DcmTag(PRIVATE_ELEMENT1_TAG) 14 #define PRV_PrivateElement2 DcmTag(PRIVATE_ELEMENT2_TAG) 15 #define PRV_PrivateElement3 DcmTag(PRIVATE_ELEMENT3_TAG) 16 #define PRV_PrivateElement4 DcmTag(PRIVATE_ELEMENT4_TAG) 17 //#define PRV_PrivateElement1 DcmTag(PRIVATE_ELEMENT1_TAG, PRIVATE_CREATOR_NAME) 18 //#define PRV_PrivateElement2 DcmTag(PRIVATE_ELEMENT2_TAG, PRIVATE_CREATOR_NAME) 19 //#define PRV_PrivateElement3 DcmTag(PRIVATE_ELEMENT3_TAG, PRIVATE_CREATOR_NAME) 20 21 void registerPrivateTags() 22 { 23 DcmDataDictionary &dict = dcmDataDict.wrlock(); 24 dict.addEntry(new DcmDictEntry(PRIVATE_CREATOR_TAG, EVR_US, "PrivateText", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 25 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT1_TAG, EVR_US, "PrivateText", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 26 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT2_TAG, EVR_US, "PrivateInteger", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 27 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT3_TAG, EVR_OB, "PrivateBlob", 1, 1, "private", OFTrue, PRIVATE_CREATOR_NAME)); 28 dcmDataDict.unlock(); 29 } 30 31 void addPrivateElements(DcmItem &item) 32 { 33 if (!item.tagExists(PRV_PrivateCreator)) 34 { 35 item.putAndInsertString(PRV_PrivateCreator, "WHY"); 36 } 37 OFString PrivateCreator; 38 item.findAndGetOFString(PRV_PrivateCreator, PrivateCreator); 39 if (!item.tagExists(PRV_PrivateElement1)) 40 { 41 item.putAndInsertString(PRV_PrivateElement1, PRIVATE_CREATOR_NAME); 42 } 43 OFString PrivateElement1; 44 item.findAndGetOFString(PRV_PrivateElement1, PrivateElement1); 45 if (!item.tagExists(PRV_PrivateElement2)) 46 { 47 // item.putAndInsertUint16(PRV_PrivateElement2, 12); 48 item.putAndInsertOFStringArray(PRV_PrivateElement2, "'a','b'"); 49 } 50 if (!item.tagExists(PRV_PrivateElement3)) 51 { 52 //item.putAndInsertUint16(PRV_PrivateElement3, '10'); 53 item.putAndInsertUint16(PRV_PrivateElement3, 10,0,OFTrue); 54 } 55 Uint16 t = 1; 56 item.findAndGetUint16(PRV_PrivateElement3, t); 57 std::cout << "PRV_PrivateElement3: " << t; 58 59 // item.putAndInsertUint8Array(PRV_PrivateElement3, NULL /*data*/, 0 /*length*/); 60 } 61 62 int main() 63 { 64 DcmFileFormat fileformat; 65 fileformat.loadFile("D:\\te\\{832E0B3A-7509-45C2-88BD-3A0BC5C48D04}.dcm"); 66 // registerPrivateTags(); 67 addPrivateElements(*fileformat.getDataset()); 68 DcmDataDictionary &dict = dcmDataDict.wrlock(); 69 70 dict.addEntry(new DcmDictEntry(PRIVATE_ELEMENT4_TAG, EVR_US, "PrivateText", 1, 2, "private", OFTrue, PRIVATE_CREATOR_NAME)); 71 dcmDataDict.unlock(); 72 73 74 fileformat.getDataset()->putAndInsertString(PRV_PrivateElement4,"PRV_PrivateElement4"); 75 #define DCM_W DcmTagKey(0x0029, 0x0030) 76 fileformat.getDataset()->putAndInsertUint16(DCM_W, 99); 77 fileformat.saveFile("D:\\te\\test_out.dcm", EXS_LittleEndianExplicit); 78 fileformat.print(COUT); 79 return 0; 80 }