標籤:style blog http color 使用 os io strong
背景介紹:
最近項目需求,需要使用C#進行最新的UI和相關DICOM3.0醫學映像模組的開發。在C++語言下,我使用的是應用最廣泛的DCMTK開源庫,在本專欄的起初階段的大多數博文都是對DCMTK開源庫的介紹和學習。目前由於項目需要,現開始對mDCM開源庫繼續學習分析,因此本專欄接下來的文章會大多以mDCM開源庫為例進行醫學映像的講解,DCMTK由於是C++語言開發的,所以作為我學習和剖析mDCM開源庫的原始依據,我們並未放棄對DCMTK開源庫的學習,而是通過更加仔細的研讀和分析DCMTK的C++源碼,從而更好的切更迅速的切換到C#語言環境下的醫學影像處理。
DCMTK、mDCM(fo-dicom)的關係:
DCMTK的官網上有詳細的說明文檔,對該開源庫的各個類,以及類之間的依賴關係進行了清晰的闡述。是學習DICOM3.0醫學最新標準不可或缺的資源。其官網網址是:http://www.dcmtk.org/,活躍的開發人員論壇地址是:http://forum.dcmtk.org/index.php。
mDCM目前瞭解是從DCMTK開源庫轉過來的,或者說是該開源項目的另一個分支,是對用C#語言對C++版本的醫學映像開源庫的再次組織和封裝,其項目託管在GitHub上的官方網址是:https://github.com/rcd/mdcm。此處就需要提到fo-dicom了,該開源庫是mDCM的升級版本,裡面增加了幾大特性,詳情可參見GitHub網址:https://github.com/rcd/fo-dicom。
大致上這三者的關係就是如此,所以更說明了我們依然要以DCMTK開源庫為依據,來快速學習和剖析mDCM(fo-dicom)開源庫,要很好的藉助於DCMTK開源庫豐富而詳細的說明文檔,以及活躍的開發人員論壇。下面我們就通過對DCM映像進行無損壓縮這一任務來對比學習一下mDCM與DCMTK開源庫的不同。
DCMTK與mDCM對DCM映像進行JPEG無損壓縮的對比學習:
DCMTK的說明文檔中對於dcmjpeg包的介紹中,就直接給出了一個利用JPEG無損壓縮的執行個體。具體代碼如下:
/***************************************************************************** dcmjpeg程式包 dcmjpeg提供了一個壓縮/解壓縮庫以及可用工具。該模組包含一些類,可將DICOM映像對象在非壓縮和JPEG壓縮表示(傳輸協議)之間轉換。無失真和有失真JPEG處理都被支援。這個模組實現了一族codec(編碼解碼器,由DcmCodec類派生而來),可以將這些codec在codec list中註冊,codec list是由dcmdata模組儲存的。 主要介面類: --DJEncoderRegistration: 一個singleton(孤立)類,為所有支援的JPEG處理註冊編碼器。在djencode.h中定義。 --DJDecoderRegistration: 一個singleton(孤立)類,為所有支援的JPEG處理註冊解碼器。在djdecode.h中定義。 --DJCodecEncoder: JPEG編碼器的一個抽象codec類。This abstract class contains most of the application logic needed for a dcmdata codec object that implements a JPEG encoder using the DJEncoder interface to the underlying JPEG implementation. This class only supports compression, it neither implements decoding nor transcoding. 在djcodece.h中定義。 --DJCodecDecoder: JPEG解碼器的一個抽象codec類。This abstract class contains most of the application logic needed for a dcmdata codec object that implements a JPEG decoder using the DJDecoder interface to the underlying JPEG implementation. This class only supports decompression, it neither implements encoding nor transcoding. 工具: dcmcjpeg: Encode DICOM file to JPEG transfer syntax dcmdjpeg: Decode JPEG-compressed DICOM file dcmj2pnm: Convert DICOM images to PGM, PPM, BMP, TIFF or JPEG dcmmkdir: Create a DICOMDIR file 舉例: --用無失真JPEG壓縮一幅DICOM影像檔。 *****************************************************************************/ DJEncoderRegistration::registerCodecs(); // register JPEG codecs DcmFileFormat fileformat; if (fileformat.loadFile("test.dcm").good()) { DcmDataset *dataset = fileformat.getDataset(); DcmItem *metaInfo = fileformat.getMetaInfo(); DJ_RPLossless params; // codec parameters, we use the defaults // this causes the lossless JPEG version of the dataset to be created dataset->chooseRepresentation(EXS_JPEGProcess14SV1TransferSyntax, ¶ms); // check if everything went well if (dataset->canWriteXfer(EXS_JPEGProcess14SV1TransferSyntax)) { // force the meta-header UIDs to be re-generated when storing the file // since the UIDs in the data set may have changed delete metaInfo->remove(DCM_MediaStorageSOPClassUID); delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID); // store in lossless JPEG format fileformat.saveFile("test_jpeg.dcm", EXS_JPEGProcess14SV1TransferSyntax); } } DJEncoderRegistration::cleanup(); // deregister JPEG codecs |
(具體的工程配置如前一篇博文所述http://blog.csdn.net/zssureqh/article/details/38460445,在此就不在重複介紹了)
通過這段代碼可以順利實現對DCM映像的JPEG無損壓縮。如所示,
利用Sante DICOM Editor專業DCM映像瀏覽編輯器開啟壓縮前後的映像,發現映像品質沒有差別,壓縮前實際大小為4572K,壓縮後為1774K,壓縮效果良好。
設想:既然mDCM開源庫就是對DCMTK開源庫的封裝,那麼兩個開源庫中應該會有相對應的功能相同或類似的函數。有DCMTK執行個體中的代碼可知,樣本中只調用了
DcmFileFormat的loadFile、saveFile和DcmDataset的chooseRepresentation和canWriteXfer四個函數,而且從函數名稱上看,就知道實際達到壓縮效果的應該是DcmDataset的chooseRepresentation和canWriteXfer的兩個函數,那麼接下來我們看看mDCM開源庫下的DcmDataset是否有相對應的函數呢?
通過VS2012的物件瀏覽器可以看到,mDCM開源庫下的Dicom.Data命名空間中DcmDataset類中的確擁有一個類似的函數ChangeTransferSyntax,如:
猜測:直接調用mDCM的Load、ChangeTransferSyntax和Save三個函數,應該可以實現與DCMTK相同的效果,即完成對DCM的JPEG無損壓縮。
具體代碼如下,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Dicom; using Dicom.Data; using Dicom.Codec; using Dicom.Codec.JpegLs; namespace JpegLossLess { class Program { static void Main(string[] args) { DicomCodec.RegisterCodecs(); //Dicom.Codec.JpegLs.DcmJpegLsCodec.Register(); string fName = string.Format("d:\\dcm\\test.dcm"); DicomFileFormat ff = new DicomFileFormat(); ff.Load(fName, DicomReadOptions.Default | DicomReadOptions.DeferLoadingPixelData); //ff.Load(fName, DicomReadOptions.None); DcmPixelData pixels = new DcmPixelData(ff.Dataset); DcmJpegLsParameters JpegParameters = new DcmJpegLsParameters(); ff.FileMetaInfo.TransferSyntax = DicomTransferSyntax.JPEGProcess14SV1; ff.Dataset.ChangeTransferSyntax(DicomTransferSyntax.JPEGProcess14SV1, JpegParameters); string OutFile = string.Format(@"d:\dcm\outfile.dcm"); ff.Save(OutFile, DicomWriteOptions.Default); } } } |
工程順利編譯成功,運行調試後,也同樣出現了大小為1774K的檔案,但是利用Sante DICOM Editor開啟該檔案時,出現錯誤,如所示:
利用DCMTK開源庫的工具包dcmdump.exe查看利用mDCM壓縮後的檔案outfile.dcm,輸出如下錯誤提示:
警告(Warning)提示(0008,0000)資料元素的數值有誤,錯誤(Error)是出現了無法識別的標籤和資料(f752,0e57),經過查看DICOM3.0標準,並未發現有(f752,0e57)該標籤,利用UltraEdit開啟DCMTK壓縮後的檔案test_jpeg.dcm和mDCM壓縮的檔案outfile.dcm,通過尋找功能發現,(f752,0e57)欄位實際上是標準的JPEG無損壓縮後的(7fe0,0010)欄位的Value Field內容(如所示),因此猜測應該是mDCM壓縮後的檔案頭中某個欄位寫入有誤,導致在讀取資料體的時候並未按照原本的DICOM3.0標準去讀取。
解決方案:
利用DCMTK的工程來讀取我們利用mDCM壓縮後的檔案outfile.dcm,結果單步調試進入後,利用Load函數讀取Jpeg壓縮後的映像時,metainfo部分是沒有問題的。但是當讀取到dataset時,對於(0008,0000)元素的讀取有誤,正確的(0008,0000)元素的解析方式為
元素標籤,即(group,element)為:08 00 00 00——(0008,0000)
元素類型,即VR為:55 4C——UL
元素長度,即VL為:04 00——0004(長度為4)
元素值域,即Value Field:B8 00 00 00——00000008(值為184)
但是在讀取dataset時,將55 4c 04 00全部當成了長度來讀取,因此猜測是將原本為ExplicitUL格式的元素當做了ImplicitVR格式來讀取了,檔案流的指標_streamPosition直接從0x0000000000000160直接跳轉到了0x0000000000044db5,如所示:
因此嘗試在mDCM的c#工程中添加手動修改檔案元資訊中傳輸語義的語句,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Dicom; using Dicom.Data; using Dicom.Codec; using Dicom.Codec.JpegLs; namespace JpegLossLess { class Program { static void Main(string[] args) { /**************************************************** * 對比C++中DCMTK對於DICOM進行JPEG無損壓縮,來學習C# * 中Dicom庫的使用 * 2014-08-06 * zssure ****************************************************/ DicomCodec.RegisterCodecs(); string fName = string.Format(@"d:\dcm\test.dcm"); DicomFileFormat ff = new DicomFileFormat(); ff.Load(fName, DicomReadOptions.Default | DicomReadOptions.DeferLoadingPixelData); DcmJpegLsParameters JpegParameters = new DcmJpegLsParameters(); ff.FileMetaInfo.TransferSyntax = DicomTransferSyntax.JPEGProcess14SV1; ff.Dataset.ChangeTransferSyntax(DicomTransferSyntax.JPEGProcess14SV1, JpegParameters); string OutFile = string.Format(@"d:\dcm\outfileJpeg22.dcm"); ff.Save(OutFile, DicomWriteOptions.Default); } } } |
工程編譯後,能夠順利完成壓縮DCM的功能,至此利用mDCM對DICOM映像進行JPEG無損壓縮的目的已經實現。
總結:
DICOM3.0標準的第10部分中,有對於dcm檔案儲存體格式的詳細介紹,其中對於傳輸語義的介紹如下:
“
1)Except for the 128 byte preamble and the 4 byte prefix, the File Meta Information shall be encoded using theExplicit VR Little Endian Transfer Syntax (UID=1.2.840.10008.1.2.1) as defined in DICOM PS 3.5. Values of each File Meta Element shall be padded when necessary to achieve an even length, as specified in PS 3.5 by their corresponding Value Representation. The Unknown (UN) Value Representation shall not be used in the File Meta Information. For compatibility with future versions of this Standard, any Tag (0002,xxxx) not defined in Table 7.1-1 shall be ignored. Values of all Tags (0002,xxxx) are reserved for use bythis Standard and later versions of DICOM. Data Elements with a group of 0002 shall not be used in datasets other than within the File Meta Information
2)The Transfer Syntax used to encode the DataSet cannot be changed within the Data Set; i.e., the Transfer Syntax UID Data Element may not occur anywhere within the Data Set, e.g., nested within a Sequence Item.
”
因此DCM檔案元資訊中的標籤(0002,0010),即傳輸語義,對於DCM檔案的資料體Dataset的讀取起到關鍵的作用。通過此次的mDCM開源庫與DCMTK開源庫的比較發現,兩者雖然大多的函數都相同,且名稱和功能都類似,但是對於細節部分應該注意。
現在對兩個開源庫對DCM檔案的JPEG無損壓縮功能所需要調用的函數進行一個對比分析,以找到兩者之間的差別所在,具體分析如下表
mDCM |
DCMTK |
1) DicomFileFormat.Load,開啟檔案(也是通過檔案流的方式一一讀取DCM檔案的各個資訊到記憶體中) 2) DicomFileFormat.Dataset.ChangeTransferSyntax,該函數與DCMTK中的chooseRepresentation函數類似,在參數中都需要指出新的傳輸語義,函數內部會根據新的傳輸語義來修改資料體的儲存方式。該函數主要完成的功能是: 比較新舊傳輸語義、根據新舊語義決定資料體是否解壓縮或壓縮(Dicom.Codec.Encode或者Dicom.Codec.Decode)。 3) DicomFileFormat.Save,隱藏檔,但是該函數中並不需要填寫新的傳輸語義 【注】:這一點與DCMTK中的saveFile函數不同。這也就是上個周C#版本的mDCM實現對DCM資料的JPEG無損壓縮後無法順利讀取的原因。因為資料體儲存格式不是按照檔案元資訊中指定的傳輸語義儲存的,或者說檔案元資訊中的傳輸語義沒有修改為JPEG無損壓縮的方式。 |
4) DicomFileFormat::loadFile,匯入檔案,主要是DcmMetaInfo和DcmDataset兩部分; 5) Dataset::chooseReresentation,參數中會出現新舊傳輸語義TransferSyntax,函數根據新的語義對相應資料(主要是像素資料)進行處理,會調用DcmPixelData::canChooseRepresentation、DcmPixelData::chooseRepresentation 6) Dataset::canWriteXfer,參數中是新修改後的傳輸語義。 7) DcmFileFormat::saveFile,參數中需要指出修改後的傳輸語義。——》隨後會調用dcfilefo.cc檔案中的validateMetaInfo函數(該函數中也需要指定新的傳輸語義)。——》對檔案元資訊的各個元素分別調用DcmMetaInfo::search和chekMetaHeaderValue兩個函數(在該函數內,會檢測各個元資訊元素是否存在,不存在會建立之並插入,其參數中就需要指出新的傳輸語義)——》DcmElement::putString將新的傳輸協議寫入到MetaInfo中。(基本調用流程如。 |
[email protected]
時間:2014-08-11