DICOM:fo-dicom、dcm4che14、dcm4chee等开源库持续自我维护

题记:

DICOM专栏系列虽然写了多年,但是依然不能解决大家日常中遇到的种种问题,其实这恰恰就是程序员(码农)工作的最大乐趣所在。就像每个人的人生一样,所处的环境不同,所遭遇的事件不同,结果自然就不同。程序开发亦是如此,操作系统不同、软件版本不同,本地配置不同都会导致种种问题。

“授人以鱼不如授人以渔”,所以正常的解决之道是希望通过专栏的讲解,能够让大家真正理解每个问题出现的背后原因,从而主动排查并解决问题。对于排查和解决过程中遇到的问题,我会整理总结成博文供大家参考。正如上文所说,每个人的环境不同,自然真正的解决方案不同,所以仅仅是参考。

背景:

DICOM相关开源库很多,按照开发语言不同主流有三类,基于C/C++的dcmtk、基于Java的dcm4che、以及基于C#的fo-dicom(mDCM)。这里再次强调一下DCM4CHEE是基于dcm4che14版本基础上,采用EJB2.x技术开发依托于JBoss的DICOM归档服务框架,可以简单的理解为一个开源的PACS系统。每个库的维护机构和人员不同,因此导致版本和数据格式兼容性上都有所不同。尤其是当在自己的项目中使用的开源库版本升级后,如何平稳过度到最新版本,如何将修复的bug补丁到现有版本尤为关键。

我个人在项目中使用的诸多开源库,都会在github上fork一个个人私有库,平日里根据国内现状以及特殊性修复自己发现的bug,此外间隔一段时间会将主库的版本pull到本地进行merge。这样可以确保现有项目平稳运行,既能适应项目特殊性,又能兼容主版本相关补丁。本篇博文简单列举几个本地修复的问题,详情可参考我的github主页相关项目。

dcm4che14开源库:

1. CDimseService.cFIND()结果返回异常

cFIND()函数的部分源码如下:

// New Cammand Set, see: DICOM Part 7: Message Exchange, 6.3.1 Command
        // Set Structure
        Command rqCmd = dof.newCommand();
        rqCmd.initCFindRQ(assoc.nextMsgID(), UIDs.StudyRootQueryRetrieveInformationModelFIND, priority);
        Dimse findRq = aFact.newDimse(pc.pcid(), rqCmd, keys);
        // Invoke active association with find request Dimse
        FutureRSP future = aassoc.invoke(findRq);
        // Response to the C-FIND request.
        // The result cannot be accessed until it has been set. LINE:2234
        // Get the list of found objects
        dimseList = future.listPending();

        datasetVector = new Vector<Dataset>();

        // Process all elements
        for (int i = 0; i < dimseList.size(); i++) {

            datasetVector.addElement(((Dimse) dimseList.get(i)).getDataset());
        }
        return datasetVector;

从代码以及命名规则上可以判断出assoc.invoke应该是发起了异步请求,再仔细看上述注释的LINE:2234标记的一行,“The result cannot be accessed until it has been set. LINE:2234”。理想状态下面的dimseList = future.listPending();应该是利用FutureRSPImpl进行的同步管理,直至所有结果都返回后获取内部的数据集。

我们再看一下FutureRSPImpl.listPending()函数,源码如下:

   //zssure:用于存储DIMSE返回结果
   private final ArrayList pending = new ArrayList();
   //zssure:获取返回结果
   public synchronized List listPending() {
      return Collections.unmodifiableList(pending);
   }

其中pending就是我们希望获得的最终C-FIND的查询结果,具体对pending的操作如下图所示:

   // DimseListener implementation ---------------------------------
   public void dimseReceived(Association assoc, Dimse dimse) {
      if (dimse.getCommand().isPending()) {
          pending.add(dimse);
      } else {
         set(dimse);
      }
   }

这就是之前多次介绍过的DICOM的状态机中调用的回调函数。用于处理汇总C-FIND的各条记录。

【问题】:但是在本地测试时会出现严重的问题,每次listPending返回的结果格式是随机的。那么究竟是什么问题导致的呢。让我们看看listPending()函数是否能够做到同步处理直至所有的C-FIND结果顺利返回。

上文源码可以看到listPending()中并未出现任何同步操作,而是使用了Java的Collections.unmodifiableList,这个其实就是简单的重构方式,返回pending的只读副本,限制外部对pending这一ArrayList的更改,即使listPending函数使用了synchronized关键字依然达不到等待所有结果返回的目的,因为synchronized关键字限制的是多个线程同时并发进入listPending函数,然而listPending函数并不会再多个线程中使用。所以listPending()函数设计有误,并不能确保所有结果返回。真正的等待结果返回应该使用FutureRSPImpl的isReady函数,从上面dimseReceived函数内部的判断逻辑可知当最后一个结果返回OK(即非Pending)时,FutureRSPImpl会使用set函数将ready标记为设置为true,因此希望同步确保获取所有查询结果的使用方法如下:

        // Invoke active association with find request Dimse
        FutureRSP future = aassoc.invoke(findRq);
        // Response to the C-FIND request.
        // The result cannot be accessed until it has been set.
        //zssure:2016/07/16 NIT
        while(!future.isReady())
        {
            TimeUnit.MILLISECONDS.sleep(500);//you can do something by yourself
        }
        //zssure:2016/07/16 NIT end

fo-dicom开源库:

1. TransferSyntax字段自纠正

之前对于Transfer Syntax介绍过多次,在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网络服务中双方数据传输的前提。此外还有一点需要注意:Transfer Syntax“作用域”,即其能够影响的范围:DICOM协议规定包含头信息(File Meta Information)的文件,头信息(即group=0002)的所有元素默认采用Explicit VR Little Endian存储,数据体Dataset(即group>0002的分组)元素如何存储则由头信息File Meta Information中的Transfer Syntax来决定。

我在github主页的fo-dico私有库中提交了fo-dicom开源库TransferSyntax字段的自校验代码Auto Check TransferSyntax of DICOM file in zssure github。具体的分析思路可参见专栏上一篇博文:DICOM:由fo-dicom库解析DICOM文件引申出来的……

dcm4chee2.X开源存储框架:

近期不少网友邮件咨询DCM4CHEE的相关问题,大致有以下几类:一类是希望使用DCM4CHEE来直接替换自己现有的PACS系统、一类是咨询DCM4CHEE是如何存储和获取图像的,最后一类就是关于DCM4CHEE中中文存储乱码的问题。对于前两类我简单的表达一下个人的观点:

- 第一,DCM4CHEE充当PACS。如果希望使用DCM4CHEE来替代自己现有的PACS作为过渡期是可以的,如果想长久使用不建议。因为目前DCM4CHEE的稳定版本是2.X,这个版本依赖的JDK、使用的JBoss都是很老的版本,存在着诸多问题。此外DCM4CHEE不单单是作为图像存储来设计的,运行过程中各项功能各个模块耦合重,所以实际运行维护过程中比较麻烦。

- 第二,对于DCM4CHEE存储和获取图像,建议直接从官网下载dcm4cheXXX二进制工具包。我建议使用命令行工具直接通过DIMSE-C服务来完成与dcm4chee的交互,并且实际工作中我也很少用可视化工具,所以大家就不要再邮件跟我索要GUI的工具了,我真是没有哈。你想向DCM4CHEE存储数据的时候,DCM4CHEE就是一个简单的PACS,提供C-STORE SCP服务。你使用任何C-STORE SCU客户端都可完成上传任务。如果想从DCM4CHEE获取数据,一种是直接使用C-GET、C-MOVE等来下载DCM文件,一种是使用WADO直接获取。

- 第三,中文乱码问题。这个问题其实不是仅仅在DCM4CHEE中出现的。而是mysql数据库使用过程中最常见的问题。无非就是各个终端(发送、接收、存储)的编码格式不一致导致的。这里我找到了几篇文章10分钟学会理解和解决MySQL乱码问题详解mysql中文乱码的问题:三个字符集搭建可以仔细阅读以下,按照这里面的方案逐个排查自己各个环节的编码格式即可。简单的截几张图示意一下:

作者:[email protected]

时间:2016-07-16

时间: 2024-10-17 04:48:47

DICOM:fo-dicom、dcm4che14、dcm4chee等开源库持续自我维护的相关文章

DICOM:DICOM开源库多线程分析之“ThreadPoolQueue in fo-dicom”

背景: 上篇博文介绍了dcm4chee中使用的Leader/Follower线程池模型,主要目的是节省上下文切换,提高运行效率.本博文同属[DICOM开源库多线程分析]系列,着重介绍fo-dicom中使用的ThreadPoolQueue线程池. ThreadPoolQueue in fo-dicom: 先看一下ThreadPoolQueue代码中自定义的数据结构, public class ThreadPoolQueue<T> { private class WorkItem { public

DICOM医学图像处理:开源库mDCM与DCMTK的比较分析(一),JPEG无损压缩DCM图像(续)

背景: 上周通过单步调试,找出了开源库mDCM与DCMTK在对DICOM图像进行JPEG无损压缩时的细小区别,并顺利实现了在C++和C#环境下对DICOM图像的压缩.但是问题接踵而至啊,随着项目的深入,发现在单独的测试工程中可以实现的mDCM版本,在嵌入到项目整体中后,却意外地出现了错误,并未顺利实现DICOM图像的JPEG无损压缩.因此需要继续详细对比分析mDCM与DCMTK两者,期望寻找原因. 问题分析: 开启项目的日志功能后,得到的信息反馈为: No registered codec fo

DICOM:DICOM三大开源库对比分析之“数据加载”

背景: 上一篇博文DICOM:DICOM万能编辑工具之Sante DICOM Editor介绍了DICOM万能编辑工具,在日常使用过程中发现,"只要Sante DICOM Editor打不开的数据,基本可以判定此DICOM文件格式错误(准确率达99.9999%^_^)".在感叹Sante DICOM Editor神器牛掰的同时,想了解一下其底层是如何实现的.通过日常使用以及阅读软件帮助手册推断其底层依赖库很可能是dcmtk,就如同本人使用dcmtk.fo-dicom.dcm4che3等

DICOM:DICOM3.0标准中文版开源书籍编辑之”github仓库合并“

背景: 作为分布式版本控制系统的代表git和github已经成为大多数开发人员首选版本控制工具.由于其不同与SVN的集中式版本管理,因此在协同工作时的方式略有不同,下面让我们来对比分析一下(这里以本人的DICOM3.0标准中文版开源书籍为例): 合并他人的Github仓库(Merge Other's Repo on Github): 1. 查看当前状态 F:\GitTest\zssuretest\DICOM-Chinese>git status On branch master Your bra

C++开源库大全(转)

程序员要站在巨人的肩膀上,C++拥有丰富的开源库,这里包括:标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等. 标准库 C++ Standard Library:是一系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的一部分. Standard Template Library:标准模板库 C POSIX library : POSIX系统的C标准库规范 ISO C++ Standards Committee :C++标准委员会 框架 C++通用框架和库

DICOM:DICOM Print服务中PresentationContext协商之 MetaSOPClass与SOPClass对比分析

背景: 最近项目中遇到的实际问题较多,且大多是较隐蔽的.不易被发现的错误.究其根源来看,还是对DICOM3.0协议中的细节掌握不够仔细,因而导致在实际编码过程中,常常想当然.前一篇中剖析了由于DicomClient中的AddRequest与Send函数调用逻辑错误导致的System.ObjectDisposedException异常,接下来要讲的是关于DICOM胶片打印的问题,由于在Association Negotiation中PresentationContext协商失误导致DICOM Pr

站在巨人的肩膀上,C++开源库大全

程序员要站在巨人的肩膀上,C++拥有丰富的开源库,这里包括:标准库.Web应用框架.人工智能.数据库.图片处理.机器学习.日志.代码分析等. 标准库 C++ Standard Library:是一系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的一部分. Standard Template Library:标准模板库 C POSIX library : POSIX系统的C标准库规范 ISO C++ Standards Committee :C++标准委员会 框架 C++通用框架和库

开源库BaseRecyclerViewAdapterHelper

相信大家RecyclerView应该不会陌生,大多数开发者应该都使用上它了,它也是google推荐替换ListView的控件,但是用过它的同学应该都知道它在某些方面并没有ListView使用起来方便,需要我们额外的编写代码,今天就给大家介绍一个开源库BaseRecyclerViewAdapterHelper,有了它让你使用RecyclerView的时候,和ListView一样的好用! 那么你要问了,BaseRecyclerViewAdapterHelper能做什么? 优化Adapter代码(减少

【计算机视觉】OpenCV的最近邻开源库FLANN

FLANN介绍 FLANN库全称是Fast Library for Approximate Nearest Neighbors,它是目前最完整的(近似)最近邻开源库.不但实现了一系列查找算法,还包含了一种自动选取最快算法的机制. flann::Index_类 该类模板是最近邻索引类,该类用于抽象不同类型的最近邻搜索的索引. 以下是flann::Index_类的声明: template <typename T> class #ifndef _MSC_VER FLANN_DEPRECATED #e