zoukankan      html  css  js  c++  java
  • DICOM:Transfer Syntax传输语义之奇葩GE Private TS

    背景:

    专栏之前对Transfer Syntax(暂定中文翻译为传输语义8月初博客中提到的DICOM3.0标准中文版开源书籍计划顺利启动。兴许会面临诸多专有名词的翻译工作,欢迎广大博友提意见)进行过多次的介绍。在DICOM医学图像处理:DICOM网络传输中差别过Abstract Syntax与Transfer Syntax,在DICOM:dcmqrscp.exe与storescu.exe中C-STORE服务的差别中介绍过在网络服务中Transfer Syntax的作用。以及上一篇DICOM:dcm4che工具包怎样压缩dcm文件探讨(续篇)中介绍对dcm文件进行压缩时提到的JPEG LossLess压缩语义以及Implicit VR Little Endian。
    Transfer Sytanx在DICOM标准中占有重要的一席之地,既作为必要元素写入到DCM文件元信息(MetaInformation)中,又是DICOM网络服务中两方传输数据的前提(如博文DICOM医学图像处理:DICOM网络传输描写叙述TransferSyntaxx是PresentationContext必备元素)。


    题记:

    最近收到了GE一款设备的数据,使用dcm4che3提供的StoreSCP服务起初无法识别,在开启“–accept-known”后顺利完毕接收,但数据打开后图像“失真”——与原始图像全然不同。因此特意研究了一下GE私有的Transfer Syntax。即1.2.840.113619.5.2。以下让我们看看这奇葩的私有协议。


    这里写图片描写叙述

    dcm4che3的StoreSCP:

    dcm4che3提供的StoreSCP服务在未开启“–accept-known”选项时,仅仅支持sop-classes.properties文件里的标准,开启后能够接受其它未知协议。

    通过查看StoreSCP.java源代码能够看到对未知协议相同採用直接将数据流存储到文件里。

        private void storeTo(Association as, Attributes fmi, 
            PDVInputStream data, File file) throws IOException  {
        LOG.info("{}: M-WRITE {}", as, file);
        file.getParentFile().mkdirs();
        DicomOutputStream out = new DicomOutputStream(file);
        try {
            out.writeFileMetaInformation(fmi);
            data.copyTo(out);
        } finally {
            SafeClose.close(out);
        }
        }
    

    将StoreSCP的“–accept-known”选项开启后。在本地顺利接收到了GE设备的数据。依照上述代码所看到的直接存储到了文件里。可是图像显示结果失真。

    图像失真:

    使用DICOM Viewer打开数据。结果例如以下所看到的:
    这里写图片描写叙述
    即使调节窗宽窗位,也无法顺利显示各种组织。通过查看DICOM相关信息并未找出问题。
    这里写图片描写叙述
    相同採用直接查看二进制的方法。定位到PixelData元素能够看到真实像素数据数值为FA 24。依照GE Private TransferSyntax=1.2.840.113619.5.2的说明,该私有语义是Implicit VR - Big Endian (G.E Private)假设依照Big Endian来解析像素数据为0xFA24=-1500,假设依照Little Endian像素数据为0x24FA=9466.在Sante Editor查看背景数据显示为9466.由此能够推測问题出如今PixelData数据读取有误。即Sante Editor对GE Private TransferSyntax=1.2.840.113619.5.2理解有误。


    这里写图片描写叙述
    这里写图片描写叙述

    奇葩之GE Private TS:

    利用Wireshark抓取GE设备发送到StoreSCP的数据包。依据Wireshark中对DICOM协议数据包的提示。发现GE Private TransferSyntax的确非常奇葩,例如以下所看到的:
    这里写图片描写叙述
    言外之意,GE Private TransferSyntax私有传输语义仅仅对PixelData元素採用Big Endian进行处理。对于其它非PxielData元素依旧採用Implicit VR Little Endian,即GE Private TransferSyntax对标准Implicit VR Little Endian语义所做的改动仅限于PixelData数据。
    这里写图片描写叙述
    结合之前StoreSCP.java中的源代码可知。dcm4che3的StoreSCP通过开启“–accept-known”选项尽管能够接受GE Private TransferSyntax私有语义。可是并未真正理解当中的含义。而是简单的将1.2.840.113619.5.2写入到MetaInformation元信息中。而将PixelData直接复制到文件流中。加之多数DICOM Viewer无法顺利理解GE Private TransferSyntax。因此导致在解析PixelData时依照之前大多数元素的方式直接以Implicit VR Little Endian语义读。导致图像失真

    解决方式:

    參照GE的说明文档,了解GE Private TransferSyntax私有语义后可知,除了PixelData的存储顺序是Big Endian以外,其它元素GE私有语义与Implicit VR Little Endian标准默认语义没有差别。

    因此考虑到DICOM Viewer的兼容性问题。在StoreSCP.java的storeTo函数中对GE Private TransferSyntax私有语义进行单独处理,对PixelData进行转序处理就可以。在完毕PixelData的Big Endian到Little Endian转序后。也就能够将Transfer Syntax直接由GE Private TransferSyntax改成Implicit VR Little Endian

    示范代码例如以下所看到的:

        private void storeTo(Association as, Attributes fmi, PDVInputStream data,
            File file) throws IOException {
        LOG.info("{}: M-WRITE {}", as, file);
        file.getParentFile().mkdirs();
    
        boolean bExchange=false;
        // TransferSyntax=1.2.840.113619.5.2,is GE Private TS,
        // Implicit VR Little Endian for all elements except pixel Data, which is Big Endian
        if(fmi.getString(Tag.TransferSyntaxUID)=="1.2.840.113619.5.2")
        {
            fmi.setString(Tag.TransferSyntaxUID, VR.UI, "1.2.840.10008.1.2");
            bExchange=true;
        }
        DicomOutputStream out = new DicomOutputStream(file);
        try {
            out.writeFileMetaInformation(fmi);
            data.copyTo(out);
    
    
        } finally {
            SafeClose.close(out);
            if(bExchange)
            {
                //这里应该对于GE Private进行单独推断。将Pixel Data数据由Big Endian转换成Little Endian
    
                try {
                    DicomInputStream input=new DicomInputStream(file);
                    Attributes attrs=input.readDataset(-1, -1);
                    byte[] bytes=attrs.getBytes(2145386512);
                    byte[] newBytes=new byte[bytes.length];
                    for(int i=0;i<bytes.length/2;++i)
                    {
                        newBytes[2*i]=bytes[2*i+1];
                        newBytes[2*i+1]=bytes[2*i];
                    }
        //          //或者直接交换
        //          for(int i=0;i<bytes.length;i+=2)
        //          {
        //              byte swap=bytes[i];
        //              bytes[i]=bytes[i+1];
        //              bytes[i+1]=swap;
        //          }
                    attrs.setBytes(2145386512, VR.OW, newBytes);
                    File file2=new File("c:\GE2.dcm");
                    DicomOutputStream out2=new DicomOutputStream(file2);
                    out2.writeDataset(input.getFileMetaInformation(), attrs);
                    input.close();
                    out2.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
        }
    }
    

    改动后又一次打开新接收的数据,能够看到正确的图像例如以下所看到的:
    这里写图片描写叙述
    至此,GE Private TransferSyntax私有语义的奇葩问题就攻克了。






    作者:zssure@163.com
    时间:2015/08/03

  • 相关阅读:
    java之collection总结
    JAVA集合详解(Collection和Map接口)
    Java集合框架(Collection Framework)学习之 Collection与Map概貌
    (二)用less+gulp+requireJs 搭建项目(gulp)
    (一)用less+gulp+requireJs 搭建项目(了解less)
    数组API汇总
    我的vim配置
    Educational Codeforces Round 47 D
    [AHOI2008]上学路线
    [SDOI2016]硬币游戏
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7007177.html
Copyright © 2011-2022 走看看