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。下面让我们看看这奇葩的私有协议。
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第1张图片

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:Transfer Syntax传输语义之奇葩GE Private TS_第2张图片
即使调节窗宽窗位,也无法顺利显示各种组织。通过查看DICOM相关信息并未找出问题。
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第3张图片
同样采用直接查看二进制的方法,定位到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理解有误。
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第4张图片
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第5张图片

奇葩之GE Private TS:

利用Wireshark抓取GE设备发送到StoreSCP的数据包,根据Wireshark中对DICOM协议数据包的提示,发现GE Private TransferSyntax的确很奇葩,如下所示:
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第6张图片
言外之意,GE Private TransferSyntax私有传输语义只对PixelData元素采用Big Endian进行处理,对于其他非PxielData元素依然采用Implicit VR Little Endian,即GE Private TransferSyntax对标准Implicit VR Little Endian语义所做的修改仅限于PixelData数据。
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第7张图片
结合之前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();
            }
        }

    }
}

修改后重新打开新接收的数据,可以看到正确的图像如下所示:
DICOM:Transfer Syntax传输语义之奇葩GE Private TS_第8张图片
至此,GE Private TransferSyntax私有语义的奇葩问题就解决了。




作者:[email protected]
时间:2015/08/03

你可能感兴趣的:(DICOM,ts,dcm4che3,StoreSCP,GE-Self-TS)