zoukankan      html  css  js  c++  java
  • DICOM文件添加私有Tag(DCMTK Private Tag)

                                                                                             DICOM文件插入私有tag

      在处理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 }
    View Code

    问题:打印结果,如上,插入的int型tag没有显示出来,插入不成功!!!

    #define DCM_W DcmTagKey(0x0029, 0x0030)
    fileformat.getDataset()->putAndInsertUint16(DCM_W, 99);

    我查看了保留tag的int型tag,插入方式确实是上面的方式,这个可以通过修改某个tag值验证。

    验证方式及结果:

    如:fileformat.getDataset()->putAndInsertUint16(DCM_Rows, 99); 修改行数

    可以看到tag信息中的,rows显示为修改值99,原始是704,与columns值一致。

    查了不少资料,也没有解决,希望有知道的朋友留言探讨!谢谢

    三 将过程数据保存在dicom文件中(序列化,反序列化)

           最开始设想的就是,过程中的参数分为多给private tag插入,因此会遇到string,int,uint16等类型。后来发现,其实可以换个方法实现,将过程数据序列化后,存为一个私有tag。

           过程为:1. 将过程文件序列化,存入dicom中

                       2. 读取dicom文件,解析该私有tag,读取其值,并反序列化,得到原始过程数据。

         我采用的是boost序列方法,可参考 http://zh.highscore.de/cpp/boost/serialization.html,非常详细清晰。

      生成dicom,https://blog.csdn.net/jiangsirl/article/details/7522986

          

          结合起来,写了个demo

      1 #include <boost/archive/text_oarchive.hpp> 
      2 #include <boost/archive/text_iarchive.hpp> 
      3 #include <iostream> 
      4 #include <sstream> 
      5 #include <string>
      6 
      7 #include "dcmtkdcmdatadctk.h"
      8 #include "DCMTKdcmimgledcmimage.h"
      9 
     10 
     11 #pragma comment(linker,"/NOD:LIBCMT")
     12 #pragma comment(lib, "oflog.lib")
     13 #pragma comment(lib, "ofstd.lib")
     14 #pragma comment(lib, "dcmimage.lib")
     15 #pragma comment(lib, "dcmdata.lib")
     16 #pragma comment(lib, "oflog.lib")
     17 #pragma comment(lib, "netapi32.lib")
     18 #pragma comment(lib, "wsock32.lib")
     19 
     20 std::stringstream ss;
     21 using namespace std;
     22 class person
     23 {
     24 public:
     25     person()
     26     {
     27     }
     28 
     29     person(int age)
     30         : age_(age)
     31     {
     32     }
     33 
     34     int age() const
     35     {
     36         return age_;
     37     }
     38 
     39 private:
     40     friend class boost::serialization::access;
     41 
     42     template <typename Archive>
     43     void serialize(Archive &ar, const unsigned int version)
     44     {
     45         ar & age_;
     46     }
     47 
     48     int age_;
     49 };
     50 
     51 void save()
     52 {
     53     boost::archive::text_oarchive oa(ss);
     54     person p(31);
     55     oa << p;
     56 }
     57 
     58 void load()
     59 {
     60     boost::archive::text_iarchive ia(ss);
     61     person p;
     62     ia >> p;
     63     std::cout << p.age() << std::endl;
     64 }
     65 
     66 int main()
     67 {
     68 
     69     char uid[100];
     70     DcmFileFormat fileformat;
     71     DcmMetaInfo *metainfo = fileformat.getMetaInfo();
     72     DcmDataset *dataset = fileformat.getDataset();
     73 
     74     //***meta group******/
     75     metainfo->putAndInsertString(DCM_FileMetaInformationVersion, "us test dcm file");
     76     metainfo->putAndInsertString(DCM_MediaStorageSOPClassUID, UID_RETIRED_UltrasoundImageStorage);
     77     metainfo->putAndInsertString(DCM_MediaStorageSOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
     78     metainfo->putAndInsertString(DCM_TransferSyntaxUID, UID_LittleEndianExplicitTransferSyntax);
     79     metainfo->putAndInsertString(DCM_ImplementationClassUID, "999.999");
     80 
     81     //***identifying group****/
     82     dataset->putAndInsertString(DCM_ImageType, "ORIGINAL\PRIMARY\TEE\0011");
     83     dataset->putAndInsertString(DCM_SOPClassUID, UID_RETIRED_UltrasoundImageStorage);//UID_SecondaryCaptureImageStorage);
     84     dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));
     85     dataset->putAndInsertString(DCM_StudyID, "398474");
     86     dataset->putAndInsertString(DCM_StudyDate, "20100823");
     87     dataset->putAndInsertString(DCM_StudyTime, "080322");
     88     dataset->putAndInsertString(DCM_Modality, "US");//OT
     89 
     90     dataset->putAndInsertString(DCM_Manufacturer, "ACME product");
     91     dataset->putAndInsertString(DCM_ReferringPhysicianName, "ANONY");
     92     dataset->putAndInsertString(DCM_StudyDescription, "STUDY description");
     93     dataset->putAndInsertString(DCM_SeriesDescription, "SERIES DESCRIPTION");
     94     dataset->putAndInsertString(DCM_StageNumber, "1");
     95     dataset->putAndInsertString(DCM_NumberOfStages, "1");
     96     dataset->putAndInsertString(DCM_ViewNumber, "1");
     97     dataset->putAndInsertString(DCM_NumberOfViewsInStage, "1");
     98     /***patient group*****/
     99     dataset->putAndInsertString(DCM_PatientID, "PatientID");
    100     dataset->putAndInsertString(DCM_PatientName, "PatientName");
    101     dataset->putAndInsertString(DCM_PatientSex, "M");
    102     dataset->putAndInsertString(DCM_PatientBirthDate, "20000302");
    103 
    104     /************************************************************************/
    105     /* acquisiton group                                                                     */
    106     /************************************************************************/
    107     //DCM_ProtocolName
    108     /************************************************************************/
    109     /* relation group                                                                      */
    110     /************************************************************************/
    111     dataset->putAndInsertString(DCM_StudyInstanceUID, "999.999.2.19941105.112000");
    112     dataset->putAndInsertString(DCM_SeriesInstanceUID, "999.999.2.19941105.112000.2");
    113     dataset->putAndInsertString(DCM_SeriesNumber, "2");
    114     dataset->putAndInsertString(DCM_AccessionNumber, "1");
    115     //dataset->putAndInsertString(DCM_InstanceNumber,);
    116 
    117     //调窗
    118     //dataset->putAndInsertString(DCM_WindowCenter, "256");
    119     //dataset->putAndInsertString(DCM_WindowWidth, "128");
    120 
    121     const int width = 256;
    122     const int height = 256;
    123     dataset->putAndInsertString(DCM_InstanceNumber, "1");
    124     //dataset->putAndInsertString(DCM_PatientOrientation,"HFL");
    125     dataset->putAndInsertString(DCM_PhotometricInterpretation, "RGB");
    126     dataset->putAndInsertUint16(DCM_SamplesPerPixel, 3);
    127     dataset->putAndInsertUint16(DCM_BitsAllocated, 8);
    128     dataset->putAndInsertUint16(DCM_BitsStored, 8);
    129     dataset->putAndInsertUint16(DCM_HighBit, 7);
    130     dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
    131     dataset->putAndInsertUint16(DCM_PlanarConfiguration, 0);
    132     dataset->putAndInsertString(DCM_PixelAspectRatio, "4\3");
    133     dataset->putAndInsertUint16(DCM_Rows, height);//
    134     dataset->putAndInsertUint16(DCM_Columns, width);//
    135 
    136 #define PRV_PrivateCreator   DcmTag(0x0029, 0x0011)
    137     dataset->putAndInsertString(PRV_PrivateCreator, "PRV_PrivateElement4");
    138     std::stringstream ss;
    139     //boost::archive::text_oarchive oa(ss);
    140     //string i = "1111111111111111111111111121213213421fdsafsdagdsagsfdbghfgjdfghjhgjfghjdgh";
    141     //oa << i;
    142 
    143     boost::archive::text_oarchive oa(ss);
    144     person p(31);
    145     oa << p;
    146 
    147     cout << "ss:" << ss.str() << endl;
    148     dataset->putAndInsertString(PRV_PrivateCreator, ss.str().c_str());
    149     fileformat.print(COUT);
    150     BYTE* pData = new BYTE[width*height * 3];
    151     memset(pData, 0, width*height * 3);
    152     for (int y = 0; y < height; y++) {
    153         //memset(pData+ y*width*3, y & 0xff0000,width*3);
    154         for (int x = 0; x < width * 3; x++)
    155         {
    156             if (x % 3 == 0)
    157                 pData[y*width * 3 + x] = 0xff;
    158             else
    159                 pData[y*width * 3 + x] = rand() % 256;
    160         }
    161     }
    162     dataset->putAndInsertUint8Array(DCM_PixelData, pData, width*height * 3);
    163     delete[] pData;
    164     OFCondition status = fileformat.saveFile("d:\test.dcm",
    165         EXS_LittleEndianImplicit, EET_UndefinedLength, EGL_withoutGL);
    166     if (status.bad())
    167     {
    168         printf("
     cannot write dicom file");
    169         return false;
    170     }
    171 
    172 
    173     DcmFileFormat dfile;
    174     OFCondition status1;
    175     DcmMetaInfo *metainfo1;
    176 
    177     status1 = dfile.loadFile("d:\test.dcm");
    178     metainfo1 = dfile.getMetaInfo();
    179     DcmDataset *dset = dfile.getDataset();
    180     OFString privateTag;
    181     string sPrivate;
    182     dset->findAndGetOFString(PRV_PrivateCreator, privateTag);
    183     sPrivate = privateTag.c_str();
    184     cout << sPrivate << endl;
    185 
    186     string t = "1122222222222222222222222222222222222";
    187     stringstream st;
    188     //st << t;
    189     //cout << "st:" << st.str() << endl;
    190 
    191     string as = "ab";
    192     stringstream ssPrivate;
    193 
    194     ssPrivate << sPrivate;
    195 
    196     //boost::archive::text_iarchive ia(ssPrivate);
    197     //string adverse = "c";
    198     //ia >> adverse;
    199 
    200    // cout << "反序列化:" << adverse << endl;
    201 
    202     boost::archive::text_iarchive ia(ssPrivate);
    203     person p1;
    204     ia >> p1;
    205     std::cout << p1.age() << std::endl;
    206 
    207 
    208     save();
    209     load();
    210     return 0;
    211 }
    View Code

    打印结果:

    可以看到,序列化输入的为age=31,反序列化得到的age也是31。说明成功将一个类对象,序列化后存入dicom文件,解析该文件的tag,反序列化后可以得到原始值。

    大功告成!将自定义结构数据成功存入privateTag中!

    转发链接:https://i.cnblogs.com/EditPosts.aspx?postid=9338142

    欢迎探讨!

  • 相关阅读:
    BUUCTF | [极客大挑战 2019]PHP
    BUUCTF | [极客大挑战 2019]Secret File
    [网鼎杯 2018] Fakebook 复现
    [强网杯2019 随便注]总结
    [Flask(Jinja2)服务端模板注入漏洞(SSTI)]学习简记
    [Flask框架]学习简记
    ADO.NET基础
    SQL Server基础
    AdventureWorks2012下载链接
    SQL Server2012从入门到精通
  • 原文地址:https://www.cnblogs.com/0523jy/p/9338142.html
Copyright © 2011-2022 走看看