1. 程式人生 > >DICOM醫學影象處理:開源庫mDCM與DCMTK的比較分析(一),JPEG無失真壓縮DCM影象

DICOM醫學影象處理:開源庫mDCM與DCMTK的比較分析(一),JPEG無失真壓縮DCM影象

背景介紹:

最近專案需求,需要使用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/ignacioinnovo/mdcm。此處就需要提到fo-dicom了,該開源庫是mDCM的升級版本,裡面增加了幾大特性,詳情可參見GitHub網址:

https://github.com/fo-dicom/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, params); 
// 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,如下圖:

image

   猜測:直接呼叫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(); 
            string fName = string.Format("d:\\dcm\\test.dcm"); 
            DicomFileFormat ff = new DicomFileFormat(); 
            ff.Load(fName, DicomReadOptions.Default | DicomReadOptions.DeferLoadingPixelData);
            DcmJpegLsParameters JpegParameters = new DcmJpegLsParameters(); 
            ff.Dataset.ChangeTransferSyntax(DicomTransferSyntax.JPEGProcess14SV1, JpegParameters);
            string OutFile = string.Format(@"d:\dcm\outfile.dcm"); 
            ff.Save(OutFile, DicomWriteOptions.Default);  
        } 
    } 
}


 

 

工程順利編譯成功,執行除錯後,也同樣出現了大小為1774K的檔案,但是利用Sante DICOM Editor開啟該檔案時,出現錯誤,如下圖所示:

image

利用DCMTK開源庫的工具包dcmdump.exe檢視利用mDCM壓縮後的檔案outfile.dcm,輸出如下錯誤提示:

image

警告(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)元素的解析方式為

clip_image002

元素標籤,即(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,如下圖所示:

clip_image004

因此嘗試在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無失真壓縮的方式。

1) DicomFileFormat::loadFile,匯入檔案,主要是DcmMetaInfo和DcmDataset兩部分;

2) Dataset::chooseReresentation,引數中會出現新舊傳輸語義TransferSyntax,函式根據新的語義對相應資料(主要是畫素資料)進行處理,會呼叫DcmPixelData::canChooseRepresentation、DcmPixelData::chooseRepresentation

3) Dataset::canWriteXfer,引數中是新修改後的傳輸語義。

4) DcmFileFormat::saveFile,引數中需要指出修改後的傳輸語義。

——》隨後會呼叫dcfilefo.cc檔案中的validateMetaInfo函式(該函式中也需要指定新的傳輸語義)。

——》對檔案元資訊的各個元素分別呼叫DcmMetaInfo::search和chekMetaHeaderValue兩個函式(在該函式內,會檢測各個元資訊元素是否存在,不存在會新建之並插入,其引數中就需要指出新的傳輸語義)

——》DcmElement::putString將新的傳輸協議寫入到MetaInfo中。(基本呼叫流程如下圖。

clip_image002[5]

 

作者:[email protected]

時間:2014-08-11