zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

开源图像处理 分析 图像 医学 JPEG DICOM
2023-09-14 09:10:06 时间

背景:

        上周通过单步调试,找出了开源库mDCM与DCMTK在对DICOM图像进行JPEG无损压缩时的细小差别,并顺利实现了在C++和C#环境下对DICOM图像的压缩。可是问题接踵而至啊,随着项目的深入,发如今单独的測试project中能够实现的mDCM版本号,在嵌入到项目总体中后,却意外地出现了错误,并未顺利实现DICOM图像的JPEG无损压缩。因此须要继续具体对照分析mDCM与DCMTK两者。期望寻找原因。

问题分析:

        开启项目的日志功能后,得到的信息反馈为:

No registered codec for transfer syntax! 在 Dicom.Data.DcmDataset.ChangeTransferSyntax(DicomTransferSyntax newTransferSyntax, DcmCodecParameters parameters),在…………………………处。

        从日志得到的反馈来看,应该是JPEG的编码器注冊失败。而编码器部分包括在mDCM开源库的Dicom.Codec64.dll程序集中。因此单步调试进入,查看project是否顺利载入了Dicom.Codec64.dll模块

        首先单步进入的是上周測试用的独立projectJpegLossLess,通过在Program.cs中调用Dicom.Codec.DicomCodec.RegisterCodecs();使得程序进入到DicomCodec.cs文件,程序执行到静态类DicomCodec的静态方法RegisterCodecs内。

clip_image002

        如上图所看到的。方法RegisterCodecs内部通过C#的程序集的动态载入和反射技术。顺利识别了project引用中加入的Dicom.dl程序集Dicom.Codec64.dll程序集

接下来单步调试到总体project中。程序从主框架转移到我们手动加入的调用Dicom.Codec.DicomCodec.RegisterCodecs();函数处,例如以下图所看到的:

image

         经过几次的调试发现。使用RegisterCodecs函数并未顺利的注冊JPEG编码器,识别出的17个程序集中仅仅有Dicom.dll模块。通过浏览DicomCodec.cs文件源代码发现,RegisterCodecs函数是静态类DicomCodec的静态函数,该函数实现的是自己主动注冊JPEG全部编码器。继续浏览发现。静态类DicomCodec还有类似的其他函数,如public static void RegisterCodec(DicomTransferSyntax ts, Type type);public static void RegisterExternalCodecs(string path, string pattern);两个函数,各自是注冊指定传输语义的解码器和注冊指定路径下的程序集中的解码器。

因为我们利用RegisterCodecs函数并未实现自己主动载入JPEG解码器的功能,并且project中已经加入引用了DicomCodec64.dll程序集。并且在调试时刻VS2012的模块窗体已经显示顺利载入了DicomCodec64.dll程序集。所以此时决定尝试手动载入DicomCodec64.dll程序集,即用以下的代码替换原本的Dicom.Codec.DicomCodec.RegisterCodecs();语句,

       string path = System.IO.Directory.GetCurrentDirectory();
       string pattern = "Dicom.Codec64.dll";
       DicomCodec.RegisterExternalCodecs(path, pattern);

        此刻单步调试能够看到,已经成功的实现了DicomCodec64.dll程序集中JPEG解码器的注冊,完毕了将DICOM图像JPEG压缩的功能与总体project的整合。

学习总结:

1)GetReferencedAssemblies函数是否能返回project中的全部引用程序集?

        通过对照上述的自己主动手动的注冊代码,发现两者的终于都是利用的GetExportedTypes函数来完毕注冊,详细代码都是Type[] types = asm.GetExportedTypes();来提取对应的解码器,唯一不同的是自己主动注冊中是利用AssemblyName[] referenced = main.GetReferencedAssemblies();提取该模块的引用程序集,而手动注冊是利用的Assembly.LoadFile函数载入手动指定的程序集文件,难道是GetReferencedAssemblies函数出现了问题?GetReferencedAssemblies函数究竟能不能返回我们project中全部的引用程序集呢?

        在MSDN搜索一下GetReferencedAssemblies函数的功能。描写叙述为:Gets the AssemblyName objects for all the assemblies referenced by this assembly.

乍一看,好像该函数是能够返回我们project中全部载入的程序集的名称。可是细致分析一下,描写叙述中提到的是"this assembly”,此处this 应该指的是调用RetReferencedAssemblies函数的程序集,因此该函数应该获得的是当前模块所引用的全部程序集。而并非我们起初觉得的整个project的引用程序集。

经过漫长的搜索,最终在一篇stackoverflow的博文(http://stackoverflow.com/questions/3971793/what-when-assembly-getreferencedassemblies-returns-exe-dependency)中找到了对“提取project全部依赖程序集”的相关说明,文中作者不仅给出了实现的方法,并且给出了为什么GetReferencedAssemblies函数没有返回project全部引用程序集的原因(http://msdn.microsoft.com/en-us/magazine/cc163641.aspx)。此处简单的对其归纳一下。并借用一下原作者的图:

        例如以下图所看到的。如果我们在模块A中调用了GetReferencedAssemblies函数。那么依照MSDN中相应的解释。函数应该返回this——即A所引用(更确切的说是直接应用)的程序集B、C、D。然而例如以下图左所看到的。程序集C和D又分别引用了其它的程序集,所以此处我们并未直接获取到整个project中全部的程序集。

因此自己主动载入的时候并未顺利的返回我们须要的Dicom.Codec64程序集。

        讲到这里,我想提取project全部引用程序集的方法已经呼之欲出了。最简单的就是我们能够对GetReferencedAssemblies的首次返回值进行递归调用,那么自然而然就能够得到全部的引用程序集A-J。可是博文中作者是依照上图右中的方式来提取全部引用程序集的,由于递归会影响程序的性能。尤其是程序模块众多的时候。简言之,就是利用算法导论中的“前序遍历”来提取全部的引用程序集,详细代码能够从给出的參考博文下载。

2)C#的静态类与Singleton设计模式

        在对照mDCM与DCMTK两个开源库对于JPEG解码器注冊的源码后,发如今用C++完毕的DCMTK开源库中。使用的是Singleton设计模式的DcmCodecList类来完毕JPEG各种解码器注冊的。而用C#编写的mDCM开源库使用的是C#的静态类public static DicomCodec。这两种方式能够实现同样的功能,因为刚開始从C++转向C#,对于这两者的差别不是非常清楚,因此搜索了一下,仅摘取部分重要片段贴在博文中,便于以后查阅。

【摘要1】:http://bbs.csdn.net/topics/370008452

除了跨程序集的边界问题。static 类和模仿 GoF C++ 版的单件没有本质的差别。

我感兴趣的讨论在于这两者在满足相同的动机的情况下,是否达成了相同的效果,我个人的看法是,静态类有简单和优雅的一面。

其实,在Java和C#方面,GoF的设计模式本身有问题,这就是经典的Double Lock Check问题(看 CLR via C#)。

粗略地说。在C# 4中。这些模式消失了:单件(静态类)、策略(托付和Lambda)、观察者(事件)、装饰(扩展方法)、工厂(部分靠反射实现)、代理(表达式树和动态类)、迭代器(yield return语法),等等。假设你依照GoF的实现来做这些。你反而舍近求远了。

最后,不光是 singleton,我对设计模式一个普遍的看法是,随着编程语言的进步,全部设计模式的实现都将消亡,而思想保存了下来。设计模式的本质也能够说是为了修饰语言的缺陷,一种优雅的语言,不须要设计模式(这个观点是我一个大学同学提出的,他也是一位 Ruby 社区的专家)。

【摘要2】:http://www.cnblogs.com/utopia/archive/2010/03/02/1676390.html

静态类的语义是全局唯一代码段。而单件的语义是全局唯一对象实例;

语义上是全然不同地,不能说起修饰都是“全局唯一”就放一块比較。

假设是这样那么全部public修饰的东西我们不是都得比較一翻了;

另外:假设要研究对象设计,那么请先抛开代码。对象设计是本身哲学性和世界观的表达。

怎样把现实的东西用概念还原表达出来。才是对象设计的实质。

而代码则是体现你头脑里那个概念模型的工具。

【摘要3】:http://blog.csdn.net/lyrebing/article/details/1902235

单例模式的目的是为了在程序中提供类的唯一实例。并且仅提供唯一的訪问点。静态不须要实例,仅提供一个全局功能。

使用单例能够继承,实现接口。而静态类不能。静态方法不能訪问类中的实例字段。由于静态方法不是通过实例来訪问的。而单例中的方法却能够訪问那个唯一实例中的实例字段。

静态方法在运行后。会释放掉它所创建的全部对象。

而单例中的方法却能够保留。静态字段仅是提供全局的功能,大家共享同一内存位置。

訪问单例中的字段是类的唯一实例中的字段,大家仅仅能訪问这个实例的字段。

 

作者:zssure@163.com

时间:2014-08-17