iOS 平臺(tái)上編譯DCMTK靜態(tài)庫(kù)--詳細(xì)流程講解

前言

DICOM

???????DICOM(Digital Imaging and Communications in Medicine)即醫(yī)學(xué)數(shù)字成像和通信,是醫(yī)學(xué)圖像和相關(guān)信息的國(guó)際標(biāo)準(zhǔn)。它定義了質(zhì)量能滿足臨床需要的可用數(shù)據(jù)交換的醫(yī)學(xué)圖像格式。

DICOM被廣泛應(yīng)用于放射醫(yī)療,心血管成像以及放射診療診斷設(shè)備(X射線,CT,核磁共振,超聲等),并且在眼科和牙科等其它醫(yī)學(xué)領(lǐng)域得到越來(lái)越深入廣泛的應(yīng)用。在數(shù)以萬(wàn)計(jì)的在用醫(yī)學(xué)成像設(shè)備中,DICOM是部署最為廣泛的醫(yī)療信息標(biāo)準(zhǔn)之一。當(dāng)前大約有百億級(jí)符合DICOM標(biāo)準(zhǔn)的醫(yī)學(xué)圖像用于臨床使用。

DCMTK

DCMTK是由德國(guó)offis公司提供的開源跨平臺(tái)C++項(xiàng)目,這個(gè)開發(fā)包經(jīng)過10多年的開發(fā)和維護(hù),已經(jīng)基本實(shí)現(xiàn)了DICOM協(xié)議的所有內(nèi)容。該開發(fā)包提供所有的源代碼、支持庫(kù)和幫助文檔。DCMTK提供了在各種操作系統(tǒng)下使用的可能版本,如LINUX、SUN、MACOS、WINDOWS等,用戶可根據(jù)自己的開發(fā)平臺(tái)進(jìn)行編譯。官方網(wǎng)站,開源項(xiàng)目Github

一、編譯環(huán)境

????????在編譯DCMTK過程中,我查了很多相關(guān)的資料,查看網(wǎng)上編譯DCMTK的流程基本上都是基于Xcode4的版本?;綝CMTK版本已經(jīng)更新了很多版本,Xcode和Mac的系統(tǒng)都有更新,編譯的配置也有很大的變化。

????????由于這些弊端,所以記錄一下最新的編譯配置。

編譯環(huán)境:(采用最新的版本編編譯)
Xcode 10.1
Mac OS 10.14
IOS SDK 12
DCMTK (目前最新版本:dcmtk-3.6.4)

二、編譯DCMTK流程

????????下邊會(huì)結(jié)合圖文的形式詳細(xì)講述編譯的整個(gè)過程,根據(jù)下方的流程一步一步的操作,就可以順利的編譯出來(lái)需要的DCMTK庫(kù)。

1. 下載DCMTK源碼

首先下載最新的DCMTK源碼:
1.官網(wǎng)上下載最新的(https://www.dcmtk.org/dcmtk.php.en

官網(wǎng)下載

2.DCMTK官方GitHub上下載(https://github.com/DCMTK/dcmtk
GitHub

2.下載編譯工具(CMake)

CMake是一個(gè)跨平臺(tái)的安裝(編譯)工具,可以用簡(jiǎn)單的語(yǔ)句來(lái)描述所有平臺(tái)的安裝(編譯過程)。他能夠輸出各種各樣的makefile或者project文件,能測(cè)試編譯器所支持的C++特性,類似UNIX下的automake。

最新的編譯工具CMake官方網(wǎng)站地址(https://cmake.org/download/

CMake

3.CMake編譯

1.把下載的DCMTK加壓到一個(gè)文件DCMTK_TEST中,
2.建立一個(gè)CMake編譯過項(xiàng)目存放的位置DCMTK_Project。


在這里插入圖片描述

3.打開CMake編譯DCMTK
在這里插入圖片描述
  1. 選擇編譯源碼的路徑和生成項(xiàng)目的路徑
  2. 配置CMake編譯的配置


    在這里插入圖片描述

    6.點(diǎn)擊CMake的Configure按鈕之后配置Configure(默認(rèn)),點(diǎn)擊Done。

在這里插入圖片描述

7.當(dāng)?shù)?步執(zhí)行完,生成如下圖的配置。


在這里插入圖片描述

8.修改自定義的配置分別修改如下后如下圖:


在這里插入圖片描述

9.點(diǎn)擊Configure按鈕,紅色會(huì)恢復(fù)正常白色
10.點(diǎn)擊General按鈕。(速度比較快)
在這里插入圖片描述

11.點(diǎn)擊Open Project 按鈕,Xcode自動(dòng)打開生成的DCMTK靜態(tài)庫(kù)的項(xiàng)目。


項(xiàng)目

三、編譯DCMTK靜態(tài)庫(kù)

1.編譯之后的DCMTK工程支持Mac的靜態(tài)庫(kù)的生成,需要設(shè)置項(xiàng)目配置支持IOS靜態(tài)庫(kù)的生成。

在這里插入圖片描述

2.修改Architectures、Base SDK、Build Active Architectures Only、Supported Platformas。
在這里插入圖片描述

3.修改IOS Deployment Target的版本(修改成需要的版本)
在這里插入圖片描述

4.修改需要編譯的Targets項(xiàng)目的配置
在這里插入圖片描述

5.運(yùn)行ALL_BUILD這個(gè)Targets,選擇真機(jī)或者模擬器,會(huì)出現(xiàn)一個(gè)error。注釋這一行引入libc.h這行代碼,運(yùn)行就不會(huì)出現(xiàn)錯(cuò)誤。
在這里插入圖片描述

6.編譯支持真機(jī)、模擬器的靜態(tài)庫(kù),可以參考文章https://blog.csdn.net/Future_One/article/details/86172828
7.編譯通過查看生成的靜態(tài)庫(kù)
在這里插入圖片描述

四、DCMTK項(xiàng)目使用

1.在源碼dcmtk-master文件夾中查找靜態(tài)庫(kù)的頭文件,放在一個(gè)dcmtk的文件夾中
每一個(gè)模塊的目錄下都 有對(duì)應(yīng)的靜態(tài)庫(kù)的頭文件:
例如:dcmdata模塊
/dcmdata/include/dcmtk/dcmdata/靜態(tài)庫(kù)的頭文件

在這里插入圖片描述

2.把DCMTK_Project項(xiàng)目中生成的頭文件拷貝出來(lái),放在一個(gè)dcmtk的文件夾中


在這里插入圖片描述

3.把所有的頭文件放在一個(gè)dcmtk文件夾中如下圖:


在這里插入圖片描述

4.創(chuàng)建一個(gè)DCMTK_New_Demo的項(xiàng)目,導(dǎo)入靜態(tài)庫(kù)和頭文件。
在這里插入圖片描述

5.添加DCMTK相關(guān)的兩個(gè)系統(tǒng)框架libz.tbd和libiconv.2.4.0.tbd庫(kù)

如果不添加這兩個(gè)庫(kù)會(huì)出現(xiàn)錯(cuò)誤提示不支持模擬器或者真機(jī)(終端使用libo -info 查看對(duì)應(yīng)的庫(kù)都是支持的)

在這里插入圖片描述

6.修改DCMTK頭文件,文件之間相互引用的路徑
由于DCMTK框架是C++寫的一個(gè)開源框架,頭文件路徑都是采用的include的方式引入,修改修改一下引入的方式。

四、DCMTK寫Demo

Demo是一個(gè)讀取dcm文件,分別對(duì)LS、Loss和未壓縮的文件的讀?。ń鈮簩懭氡镜氐拇a也在代碼中)


.h
typedef void (^dcmReturn)(UIImage *imgData, long imgWidth, long  imgHeight);

@interface DCMImgShow : NSObject
-(void)getImgDataFileName:(NSString *)fileName withImgisInverse:(BOOL)isInverse withBlock:(dcmReturn)dicom;
@end



.m

-(void)getImgDataFileName:(NSString *)fileName withImgisInverse:(BOOL)isInverse withBlock:(dcmReturn)dicom{
    
  
    NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:@"dcm"];
    const char *fileNameStr = [filePath cStringUsingEncoding:NSASCIIStringEncoding];
    
    DcmFileFormat *dcmFileFormat = new DcmFileFormat();
    OFCondition status = dcmFileFormat->loadFile(fileNameStr);
    
    if (status.good()) {
        OFString patientName;
        DcmDataset *dcmDataset = dcmFileFormat->getDataset();
        OFCondition condition = dcmDataset->findAndGetOFString(DCM_PatientName,
                                                               patientName);
        if (condition.good()) {
            NSLog(@"pat name %s", patientName.c_str());
        } else {
            NSLog(@"condition. BAD");
        }
        
        
        const char *transferSyntax;
        DcmMetaInfo *dcmMetaInfo = dcmFileFormat->getMetaInfo();
        OFCondition transferSyntaxOfCondition = dcmMetaInfo->findAndGetString(
                                                                              DCM_TransferSyntaxUID, transferSyntax);
        NSLog(@"transferSyntaxOfCondition  %s", transferSyntaxOfCondition.text());
        NSLog(@"transferSyntax  %s", transferSyntax);
        
        // 獲得當(dāng)前的窗寬 窗位
        
        Float64 windowCenter;
        dcmDataset->findAndGetFloat64(DCM_WindowCenter, windowCenter);
        NSLog(@"windowCenter %f", windowCenter);
        
        Float64 windowWidth;
        dcmDataset->findAndGetFloat64(DCM_WindowWidth, windowWidth);
        NSLog(@"windowWidth %f", windowWidth);
        
        E_TransferSyntax xfer = dcmDataset->getOriginalXfer();
        NSLog(@"E_TransferSyntax %d", xfer);
        
         const char * model;
        dcmDataset->findAndGetString(DCM_Modality, model);
        
        NSLog(@"-------------Model: %s",model);
        
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,NSUserDomainMask,YES);
        NSString *pathCache = [paths objectAtIndex:0];
        NSLog(@"----------Cache:---%@",pathCache);
        
       
        // Dicom
        DicomImage *m_dcmImage = NULL;
        if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.70") == 0) {
            
            DJDecoderRegistration::registerCodecs();
            OFCondition chooseOfCondition = dcmDataset->chooseRepresentation(
                                                                             EXS_LittleEndianExplicit, NULL);
            
            m_dcmImage = new DicomImage((DcmObject *) dcmDataset,
                                        xfer); //利用dataset生成DicomImage,需要上面的解壓方法;
            DJDecoderRegistration::cleanup();
            
        }else if (strcmp(transferSyntax, "1.2.840.10008.1.2.4.80") == 0){
            
            // LS 解碼器
            DJLSDecoderRegistration::registerCodecs();
            OFCondition chooseOfCondition = dcmDataset->chooseRepresentation(
                                                                             EXS_LittleEndianExplicit, NULL);
           
//            if (dcmDataset->canWriteXfer(EXS_LittleEndianExplicit)) {
//                OFCondition ofCondition = dcmFileFormat->saveFile(fileNameSave,
//                                                                  EXS_LittleEndianExplicit);
//                
//                if (ofCondition.good()) {
//                    NSLog(@"---------------保存成功----------------");
//                    NSLog(@"---------------------保存成功時(shí)間----%@",[self getTimeNow]);
//                }else{
//                    NSLog(@"-------------------Save Fail ------------------");
//                }
//            }
            
            m_dcmImage = new DicomImage((DcmObject *) dcmDataset,
                                        xfer); //利用dataset生成DicomImage,需要上面的解壓方法;
            DJLSDecoderRegistration::cleanup();
            
        }else{
//            // LS 解碼器
//            DJLSDecoderRegistration::registerCodecs();
//            OFCondition chooseOfCondition = dcmDataset->chooseRepresentation(
//                                                                             EXS_LittleEndianExplicit, NULL);
            
            m_dcmImage = new DicomImage((DcmObject *) dcmDataset,
                                        xfer); //利用dataset生成DicomImage,需要上面的解壓方法;
//            DJLSDecoderRegistration::cleanup();
        }
        long height = m_dcmImage->getHeight();
        long width = m_dcmImage->getWidth();
        long depth = m_dcmImage->getDepth();
        
        long size = m_dcmImage->getOutputDataSize(8);
        m_dcmImage->setWindow(windowCenter, windowWidth);
        NSLog(@"png height %ld ", height);
        NSLog(@"png width %ld ", width);
        NSLog(@"png depth %ld ", depth);
        NSLog(@"png size %ld ", size);
        NSLog(@"int size %ld",sizeof(int));

        unsigned char *pixelData = (unsigned char *) (m_dcmImage->getOutputData(8, 0, 0));

        long size1 = height * width;
        unsigned char temp = NULL;
        
        int * p = (int *)malloc(width * height * sizeof(int));
//        int *p = new int[size1];
        
       if(strcmp(model,"SC") == 0){
            unsigned char r = NULL;
            unsigned char g = NULL;
            unsigned char b = NULL;
            for (int j = 0; j < size1; ++j) {
                r = pixelData[j * 3] ;
                g = pixelData[j * 3 + 1] ;
                b = pixelData[j * 3 + 2] ;
                p[j] = r  | g << 8 | b << 16 | 0xff000000;
            }
       }else{
           for (int i = 0; i < size1; ++i) {
               temp = pixelData[i];
               p[i] = temp | (temp << 8) | (temp << 16) | 0xff000000;
           }
       }

        if (pixelData != NULL) {
            NSLog(@"pixelData not null");
        }

        NSData *imgData = [NSData dataWithBytes:(Byte *)p length:size1 * sizeof(int)];
        
        // 釋放內(nèi)存
        free(pixelData);
        free(p);
        
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)imgData);
        
        CGImageRef imageRef = CGImageCreate(width,             //width
                                            height,            //height
                                            8,                 //bits per component
                                            32,                //bits per pixel
                                            width*4,           //bytesPerRow
                                            colorSpace,        //colorspace
                                            kCGImageAlphaNone | kCGImageAlphaNoneSkipLast,        //kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder16Little,// bitmap info
                                            provider,               //CGDataProviderRef
                                            NULL,                   //decode
                                            true,                  //should interpolate
                                            kCGRenderingIntentDefault   //intent
                                            );
        
        if (isInverse) {
            UIImage *testImage = [UIImage imageWithCGImage:imageRef];
            
            dicom(testImage, width, height);
            CGImageRelease(imageRef);
            CGDataProviderRelease(provider);
            CGColorSpaceRelease(colorSpace);
            return;
        }
        
        size_t                  bytesPerRow;
        bytesPerRow = CGImageGetBytesPerRow(imageRef);
        
        CFDataRef   data;
        UInt8*      buffer;
        data = CGDataProviderCopyData(provider);
        buffer = (UInt8*)CFDataGetBytePtr(data);
        UIImage *testImage = [UIImage imageWithCGImage:imageRef];
        
        // 返回當(dāng)前的img 數(shù)據(jù)
        dicom(testImage, width, height);
        CGImageRelease(imageRef);
        CGDataProviderRelease(provider);
        CGColorSpaceRelease(colorSpace);
        CFRelease(data);
    }
}

Dcmtk框架的顯示dcm的 Demo

下載地址https://github.com/FlameDream/DCMTK_Demo

五、DCMTK配置擴(kuò)展

DCMTK是一個(gè)比較龐大的庫(kù),包括dcm文件讀取、解壓dcm文件、生成RGB裸數(shù)據(jù)、讀取dcm的tag、網(wǎng)絡(luò)、多線程等。工程里可能只需要使用dcmtk框架中的一小部分的功能,可以采用兩種方式解決引入過多無(wú)用的庫(kù)和頭文件
1.把所有庫(kù)文件和頭文件生成并引入項(xiàng)目中,在逐一刪除對(duì)應(yīng)多余的庫(kù)和頭文件
2.直接在CMake中刪除不需要的框架配置(如下圖)


在這里插入圖片描述

六、性能優(yōu)化

修改DCMTK在CMake的代碼版本類型配置


修改程序的版本

把Debug;Release;MinSizeRel;RelWithDebInfo修改成Release。隨后生成的.a庫(kù)文件減小,同時(shí)DCMTK的性能也有所提高。

總結(jié)

DCMTK是一個(gè)比較龐大的讀取DICOM的開源庫(kù),由于DCMTK開源框架的文檔比較少,所有有很多使用者使用比較不爽,歡迎大家相互交流

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容