美文网首页
iOS 平台上编译DCMTK静态库--详细流程讲解

iOS 平台上编译DCMTK静态库--详细流程讲解

作者: Flame_Dream | 来源:发表于2019-01-25 21:43 被阅读0次

    前言

    DICOM

           DICOM(Digital Imaging and Communications in Medicine)即医学数字成像和通信,是医学图像和相关信息的国际标准。它定义了质量能满足临床需要的可用数据交换的医学图像格式。

    DICOM被广泛应用于放射医疗,心血管成像以及放射诊疗诊断设备(X射线,CT,核磁共振,超声等),并且在眼科和牙科等其它医学领域得到越来越深入广泛的应用。在数以万计的在用医学成像设备中,DICOM是部署最为广泛的医疗信息标准之一。当前大约有百亿级符合DICOM标准的医学图像用于临床使用。

    DCMTK

    DCMTK是由德国offis公司提供的开源跨平台C++项目,这个开发包经过10多年的开发和维护,已经基本实现了DICOM协议的所有内容。该开发包提供所有的源代码、支持库和帮助文档。DCMTK提供了在各种操作系统下使用的可能版本,如LINUX、SUN、MACOS、WINDOWS等,用户可根据自己的开发平台进行编译。官方网站,开源项目Github

    一、编译环境

            在编译DCMTK过程中,我查了很多相关的资料,查看网上编译DCMTK的流程基本上都是基于Xcode4的版本。基本DCMTK版本已经更新了很多版本,Xcode和Mac的系统都有更新,编译的配置也有很大的变化。

            由于这些弊端,所以记录一下最新的编译配置。

    编译环境:(采用最新的版本编编译)
    Xcode 10.1
    Mac OS 10.14
    IOS SDK 12
    DCMTK (目前最新版本:dcmtk-3.6.4)

    二、编译DCMTK流程

            下边会结合图文的形式详细讲述编译的整个过程,根据下方的流程一步一步的操作,就可以顺利的编译出来需要的DCMTK库。

    1. 下载DCMTK源码

    首先下载最新的DCMTK源码:
    1.官网上下载最新的(https://www.dcmtk.org/dcmtk.php.en

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

    2.下载编译工具(CMake)

    CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。

    最新的编译工具CMake官方网站地址(https://cmake.org/download/

    CMake

    3.CMake编译

    1.把下载的DCMTK加压到一个文件DCMTK_TEST中,
    2.建立一个CMake编译过项目存放的位置DCMTK_Project。


    在这里插入图片描述
    3.打开CMake编译DCMTK 在这里插入图片描述
    1. 选择编译源码的路径和生成项目的路径
    2. 配置CMake编译的配置


      在这里插入图片描述

      6.点击CMake的Configure按钮之后配置Configure(默认),点击Done。

    在这里插入图片描述

    7.当第6步执行完,生成如下图的配置。


    在这里插入图片描述

    8.修改自定义的配置分别修改如下后如下图:


    在这里插入图片描述
    9.点击Configure按钮,红色会恢复正常白色
    10.点击General按钮。(速度比较快)
    在这里插入图片描述

    11.点击Open Project 按钮,Xcode自动打开生成的DCMTK静态库的项目。


    项目

    三、编译DCMTK静态库

    1.编译之后的DCMTK工程支持Mac的静态库的生成,需要设置项目配置支持IOS静态库的生成。

    在这里插入图片描述
    2.修改Architectures、Base SDK、Build Active Architectures Only、Supported Platformas。
    在这里插入图片描述
    3.修改IOS Deployment Target的版本(修改成需要的版本)
    在这里插入图片描述
    4.修改需要编译的Targets项目的配置
    在这里插入图片描述
    5.运行ALL_BUILD这个Targets,选择真机或者模拟器,会出现一个error。注释这一行引入libc.h这行代码,运行就不会出现错误。
    在这里插入图片描述
    6.编译支持真机、模拟器的静态库,可以参考文章https://blog.csdn.net/Future_One/article/details/86172828
    7.编译通过查看生成的静态库
    在这里插入图片描述

    四、DCMTK项目使用

    1.在源码dcmtk-master文件夹中查找静态库的头文件,放在一个dcmtk的文件夹中
    每一个模块的目录下都 有对应的静态库的头文件:
    例如:dcmdata模块
    /dcmdata/include/dcmtk/dcmdata/静态库的头文件

    在这里插入图片描述

    2.把DCMTK_Project项目中生成的头文件拷贝出来,放在一个dcmtk的文件夹中


    在这里插入图片描述

    3.把所有的头文件放在一个dcmtk文件夹中如下图:


    在这里插入图片描述
    4.创建一个DCMTK_New_Demo的项目,导入静态库和头文件。
    在这里插入图片描述
    5.添加DCMTK相关的两个系统框架libz.tbd和libiconv.2.4.0.tbd库

    如果不添加这两个库会出现错误提示不支持模拟器或者真机(终端使用libo -info 查看对应的库都是支持的)

    在这里插入图片描述

    6.修改DCMTK头文件,文件之间相互引用的路径
    由于DCMTK框架是C++写的一个开源框架,头文件路径都是采用的include的方式引入,修改修改一下引入的方式。

    四、DCMTK写Demo

    Demo是一个读取dcm文件,分别对LS、Loss和未压缩的文件的读取(解压写入本地的代码也在代码中)

    
    .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);
            
            // 获得当前的窗宽 窗位
            
            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(@"---------------------保存成功时间----%@",[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)];
            
            // 释放内存
            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];
            
            // 返回当前的img 数据
            dicom(testImage, width, height);
            CGImageRelease(imageRef);
            CGDataProviderRelease(provider);
            CGColorSpaceRelease(colorSpace);
            CFRelease(data);
        }
    }
    
    

    Dcmtk框架的显示dcm的 Demo

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

    五、DCMTK配置扩展

    DCMTK是一个比较庞大的库,包括dcm文件读取、解压dcm文件、生成RGB裸数据、读取dcm的tag、网络、多线程等。工程里可能只需要使用dcmtk框架中的一小部分的功能,可以采用两种方式解决引入过多无用的库和头文件
    1.把所有库文件和头文件生成并引入项目中,在逐一删除对应多余的库和头文件
    2.直接在CMake中删除不需要的框架配置(如下图)


    在这里插入图片描述

    六、性能优化

    修改DCMTK在CMake的代码版本类型配置


    修改程序的版本

    把Debug;Release;MinSizeRel;RelWithDebInfo修改成Release。随后生成的.a库文件减小,同时DCMTK的性能也有所提高。

    总结

    DCMTK是一个比较庞大的读取DICOM的开源库,由于DCMTK开源框架的文档比较少,所有有很多使用者使用比较不爽,欢迎大家相互交流

    相关文章

      网友评论

          本文标题:iOS 平台上编译DCMTK静态库--详细流程讲解

          本文链接:https://www.haomeiwen.com/subject/hcavjqtx.html