DICOM:Transfer Syntax传输语义之奇葩GE Private TS

摘要:
背景:列之前(初步翻译为中文,称为传输语义。8月初博客中提到的DICOM3.0标准中文开源图书计划成功推出。发现GEPrivateTransferSyntax确实是一个很棒的作品。例如,从以下内容可以看出:隐含含义,GEPrivatetransferSyntaxprivatetransmission语义仅使用PixelData元素的BigEndian。ImplicitVRLittleEndian仍然用于其他非PXielData元素,即GEPrivateTransferSynta X对标准ImplicitVRTLittleEndan语义的更改仅限于PixelData数据。此外,大多数DICOMViewer无法顺利理解GEPrivateTransferSyntax。
背景:

专栏之前对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

免责声明:文章转载自《DICOM:Transfer Syntax传输语义之奇葩GE Private TS》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【Android】缩略图Thumbnails一文带你了解微服务架构和设计(多图)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于HTTP协议头域详解

HTTP1.1  请求头:消息头   Accept:text/html,image/*  告诉服务器,客户机支持的数据类型 Accept-Charset:ISO-8859-1  告诉服务器,客户机采用的编码   Accept-EnCoding:gzip,compress 告诉服务器,客户机支持的数据压缩格式 Accept-Language:en   客户机...

http协议的POST传数据

PostRequest使用StreamWriter对象写入请求流,不需要使用HttpUtility.UrlEncode显示转码,而下面的需要显示转码,还需要将参数转为字节码 蛋疼…………。 public static string PostRequest(string url, string postData) { HttpWebRequest httpW...

[转]Go的50坑:新Golang开发者要注意的陷阱、技巧和常见错误-高级

from : https://levy.at/blog/11 进阶篇 关闭HTTP的响应 level: intermediate 当你使用标准http库发起请求时,你得到一个http的响应变量。如果你不读取响应主体,你依旧需要关闭它。注意对于空的响应你也一定要这么做。对于新的Go开发者而言,这个很容易就会忘掉。 一些新的Go开发者确实尝试关闭响应主体,但...

[转]常见医疗扫描图像处理步骤

一 数据格式 1.1 dicom DICOM是医学图像中标准文件,这些文件包含了诸多的元数据信息(比如像素尺寸,每个维度的一像素代表真实世界里的长度)。此处以kaggle Data Science Bowl 数据集为例。 data-science-bowl-2017。数据列表如下: 后缀为 .dcm。 每个病人的一次扫描CT(scan)可能有几十到一百多...

java实现视频断点上传文件

一、概述   所谓断点续传,其实只是指下载,也就是要从文件已经下载的地方开始继续下载。在以前版本的HTTP协议是不支持断点的,HTTP/1.1开始就支持了。一般断点下载时才用到Range和Content-Range实体头。HTTP协议本身不支持断点上传,需要自己实现。   二、Range    用于请求头中,指定第一个字节的位置和最后一个字节的位置,一般格...

hbase 取多个版本数据

http://jiajun.iteye.com/blog/945358 HBase如何存取多个版本的值? 废话少说,一般情况下使用Put的这个方法保存一个版本: Java代码 /** *AddthespecifiedcolumnandvaluetothisPutoperation. *@paramfamilyfamilyname *@paramq...