DICOM医学图像处理:开源库mDCM与DCMTK的比较分析(一),JPEG无损压缩DCM图像(续)...
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
背景:
? ? ? ? 上周通過單步調(diào)試,找出了開源庫mDCM與DCMTK在對DICOM圖像進行JPEG無損壓縮時的細小區(qū)別,并順利實現(xiàn)了在C++和C#環(huán)境下對DICOM圖像的壓縮。但是問題接踵而至啊,隨著項目的深入,發(fā)現(xiàn)在單獨的測試工程中可以實現(xiàn)的mDCM版本,在嵌入到項目整體中后,卻意外地出現(xiàn)了錯誤,并未順利實現(xiàn)DICOM圖像的JPEG無損壓縮。因此需要繼續(xù)詳細對比分析mDCM與DCMTK兩者,期望尋找原因。
問題分析:
? ? ? ? 開啟項目的日志功能后,得到的信息反饋為:
No registered codec for transfer syntax! 在 Dicom.Data.DcmDataset.ChangeTransferSyntax(DicomTransferSyntax newTransferSyntax, DcmCodecParameters parameters),在…………………………處。
? ? ? ? 從日志得到的反饋來看,應該是JPEG的編碼器注冊失敗。而編碼器部分包含在mDCM開源庫的Dicom.Codec64.dll程序集中。因此單步調(diào)試進入,查看工程是否順利加載了Dicom.Codec64.dll模塊。
? ? ? ? 首先單步進入的是上周測試用的獨立工程JpegLossLess,通過在Program.cs中調(diào)用Dicom.Codec.DicomCodec.RegisterCodecs();使得程序進入到DicomCodec.cs文件,程序運行到靜態(tài)類DicomCodec的靜態(tài)方法RegisterCodecs內(nèi)。
? ? ? ? 如上圖所示,方法RegisterCodecs內(nèi)部通過C#的程序集的動態(tài)加載和反射技術(shù),順利識別了工程引用中添加的Dicom.dl程序集和Dicom.Codec64.dll程序集。
接下來單步調(diào)試到整體工程中,程序從主框架轉(zhuǎn)移到我們手動添加的調(diào)用Dicom.Codec.DicomCodec.RegisterCodecs();函數(shù)處,如下圖所示:
? ? ? ? ?經(jīng)過幾次的調(diào)試發(fā)現(xiàn),使用RegisterCodecs函數(shù)并未順利的注冊JPEG編碼器,識別出的17個程序集中只有Dicom.dll模塊。通過瀏覽DicomCodec.cs文件源碼發(fā)現(xiàn),RegisterCodecs函數(shù)是靜態(tài)類DicomCodec的靜態(tài)函數(shù),該函數(shù)實現(xiàn)的是自動注冊JPEG所有編碼器。繼續(xù)瀏覽發(fā)現(xiàn),靜態(tài)類DicomCodec還有類似的其它函數(shù),如public static void RegisterCodec(DicomTransferSyntax ts, Type type);和public static void RegisterExternalCodecs(string path, string pattern);兩個函數(shù),分別是注冊指定傳輸語義的解碼器和注冊指定路徑下的程序集中的解碼器。由于我們利用RegisterCodecs函數(shù)并未實現(xiàn)自動加載JPEG解碼器的功能,而且工程中已經(jīng)添加引用了DicomCodec64.dll程序集,并且在調(diào)試時刻VS2012的模塊窗口已經(jīng)顯示順利加載了DicomCodec64.dll程序集。所以此時決定嘗試手動加載DicomCodec64.dll程序集,即用下面的代碼替換原本的Dicom.Codec.DicomCodec.RegisterCodecs();語句,
? ? ? ?string path = System.IO.Directory.GetCurrentDirectory();
?????? string pattern = "Dicom.Codec64.dll";
?????? DicomCodec.RegisterExternalCodecs(path, pattern);
? ? ? ? 此刻單步調(diào)試可以看到,已經(jīng)成功的實現(xiàn)了DicomCodec64.dll程序集中JPEG解碼器的注冊,完成了將DICOM圖像JPEG壓縮的功能與整體工程的整合。
學習總結(jié):
1)GetReferencedAssemblies函數(shù)能否返回工程中的所有引用程序集?
? ? ? ? 通過對比上述的自動和手動的注冊代碼,發(fā)現(xiàn)兩者的最終都是利用的GetExportedTypes函數(shù)來完成注冊,具體代碼都是Type[] types = asm.GetExportedTypes();來提取相應的解碼器,唯一不同的是自動注冊中是利用AssemblyName[] referenced = main.GetReferencedAssemblies();提取該模塊的引用程序集,而手動注冊是利用的Assembly.LoadFile函數(shù)加載手動指定的程序集文件,難道是GetReferencedAssemblies函數(shù)出現(xiàn)了問題?GetReferencedAssemblies函數(shù)到底能不能返回我們工程中所有的引用程序集呢?
? ? ? ? 在MSDN搜索一下GetReferencedAssemblies函數(shù)的功能,描述為:Gets the AssemblyName objects for all the assemblies referenced by this assembly.
乍一看,好像該函數(shù)是可以返回我們工程中所有加載的程序集的名稱。但是仔細分析一下,描述中提到的是"this assembly”,此處this 應該指的是調(diào)用RetReferencedAssemblies函數(shù)的程序集,因此該函數(shù)應該獲得的是當前模塊所引用的所有程序集,而并不是我們起初認為的整個工程的引用程序集。經(jīng)過漫長的搜索,終于在一篇stackoverflow的博文(http://stackoverflow.com/questions/3971793/what-when-assembly-getreferencedassemblies-returns-exe-dependency)中找到了對“提取工程所有依賴程序集”的相關(guān)說明,文中作者不僅給出了實現(xiàn)的方法,而且給出了為什么GetReferencedAssemblies函數(shù)沒有返回工程所有引用程序集的原因(http://msdn.microsoft.com/en-us/magazine/cc163641.aspx)。此處簡單的對其歸納一下,并借用一下原作者的圖:
? ? ? ? 如下圖所示,假設(shè)我們在模塊A中調(diào)用了GetReferencedAssemblies函數(shù),那么按照MSDN中對應的解釋,函數(shù)應該返回this——即A所引用(更確切的說是直接應用)的程序集B、C、D。然而如下圖左所示,程序集C和D又分別引用了其他的程序集,所以此處我們并未直接獲取到整個工程中所有的程序集。因此自動加載的時候并未順利的返回我們需要的Dicom.Codec64程序集。
? ? ? ? 說到這里,我想提取工程所有引用程序集的方法已經(jīng)呼之欲出了,最簡單的就是我們可以對GetReferencedAssemblies的首次返回值進行遞歸調(diào)用,那么自然而然就可以得到所有的引用程序集A-J。但是博文中作者是按照上圖右中的方式來提取所有引用程序集的,因為遞歸會影響程序的性能,尤其是程序模塊眾多的時候。簡言之,就是利用算法導論中的“前序遍歷”來提取所有的引用程序集,具體代碼可以從給出的參考博文下載。
2)C#的靜態(tài)類與Singleton設(shè)計模式
? ? ? ? 在對比mDCM與DCMTK兩個開源庫對于JPEG解碼器注冊的源代碼后,發(fā)現(xiàn)在用C++完成的DCMTK開源庫中,使用的是Singleton設(shè)計模式的DcmCodecList類來完成JPEG各種解碼器注冊的,而用C#編寫的mDCM開源庫使用的是C#的靜態(tài)類public static DicomCodec。這兩種方式可以實現(xiàn)相同的功能,由于剛開始從C++轉(zhuǎn)向C#,對于這兩者的區(qū)別不是很清楚,因此搜索了一下,僅摘取部分重要片段貼在博文中,便于以后查閱。
【摘要1】:http://bbs.csdn.net/topics/370008452
除了跨程序集的邊界問題,static 類和模仿 GoF C++ 版的單件沒有本質(zhì)的區(qū)別。我感興趣的討論在于這兩者在滿足同樣的動機的情況下,是否達成了同樣的效果,我個人的看法是,靜態(tài)類有簡單和優(yōu)雅的一面。事實上,在Java和C#方面,GoF的設(shè)計模式本身有問題,這就是經(jīng)典的Double Lock Check問題(看 CLR via C#)。
粗略地說,在C# 4中,這些模式消失了:單件(靜態(tài)類)、策略(委托和Lambda)、觀察者(事件)、裝飾(擴展方法)、工廠(部分靠反射實現(xiàn))、代理(表達式樹和動態(tài)類)、迭代器(yield return語法),等等,如果你按照GoF的實現(xiàn)來做這些,你反而舍近求遠了。
最后,不光是 singleton,我對設(shè)計模式一個普遍的看法是,隨著編程語言的進步,所有設(shè)計模式的實現(xiàn)都將消亡,而思想保存了下來。設(shè)計模式的本質(zhì)也可以說是為了修飾語言的缺陷,一種優(yōu)雅的語言,不需要設(shè)計模式(這個觀點是我一個大學同學提出的,他也是一位 Ruby 社區(qū)的專家)。
【摘要2】:http://www.cnblogs.com/utopia/archive/2010/03/02/1676390.html
靜態(tài)類的語義是全局唯一代碼段,而單件的語義是全局唯一對象實例;
語義上是完全不同地,不能說起修飾都是“全局唯一”就放一塊比較;
如果是這樣那么所有public修飾的東西我們不是都得比較一翻了;
另外:如果要研究對象設(shè)計,那么請先拋開代碼。對象設(shè)計是本身哲學性和世界觀的表達。
如何把現(xiàn)實的東西用概念還原表達出來,才是對象設(shè)計的實質(zhì)。而代碼則是體現(xiàn)你頭腦里那個概念模型的工具。
【摘要3】:http://blog.csdn.net/lyrebing/article/details/1902235
單例模式的目的是為了在程序中提供類的唯一實例,而且僅提供唯一的訪問點。靜態(tài)不需要實例,僅提供一個全局功能。使用單例可以繼承,實現(xiàn)接口,而靜態(tài)類不能。靜態(tài)方法不能訪問類中的實例字段,因為靜態(tài)方法不是通過實例來訪問的。而單例中的方法卻可以訪問那個唯一實例中的實例字段。靜態(tài)方法在執(zhí)行后,會釋放掉它所創(chuàng)建的所有對象。而單例中的方法卻可以保留。靜態(tài)字段僅是提供全局的功能,大家共享同一內(nèi)存位置。訪問單例中的字段是類的唯一實例中的字段,大家只能訪問這個實例的字段。
?
作者:zssure@163.com
時間:2014-08-17
轉(zhuǎn)載于:https://my.oschina.net/zssure/blog/354805
總結(jié)
以上是生活随笔為你收集整理的DICOM医学图像处理:开源库mDCM与DCMTK的比较分析(一),JPEG无损压缩DCM图像(续)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis缓存 ava-Jedis操作R
- 下一篇: 快速批量导入庞大数据到SQL SERVE