赞
踩
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.官网上下载最新的: https://www.dcmtk.org/dcmtk.php.en
2.DCMTK官方GitHub上下载: https://github.com/DCMTK/dcmtk
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
最新的编译工具CMake官方网站地址:https://cmake.org/download/
1.把下载的DCMTK加压到一个文件DCMTK_TEST中,
2.建立一个CMake编译过项目存放的位置DCMTK_Project。
3.打开CMake编译DCMTK
4. 选择编译源码的路径和生成项目的路径
5. 配置CMake编译的配置
6.点击CMake的Configure按钮之后配置Configure(默认),点击Done。
7.当第6步执行完,生成如下图的配置。
8.修改自定义的配置分别修改如下后如下图:
9.点击Configure按钮,红色会恢复正常白色
10.点击General按钮。(速度比较快)
11.点击Open Project 按钮,Xcode自动打开生成的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.编译通过查看生成的静态库
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的方式引入,修改修改一下引入的方式。
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是一个比较庞大的库,包括dcm文件读取、解压dcm文件、生成RGB裸数据、读取dcm的tag、网络、多线程等。工程里可能只需要使用dcmtk框架中的一小部分的功能,可以采用两种方式解决引入过多无用的库和头文件
1.把所有库文件和头文件生成并引入项目中,在逐一删除对应多余的库和头文件
2.直接在CMake中删除不需要的框架配置(如下图)
修改DCMTK在CMake的版本类型配置
把Debug;Release;MinSizeRel;RelWithDebInfo修改成Release。随后生成的.a库文件减小,同时DCMTK的性能也有所提高。
DCMTK是一个比较庞大的读取DICOM的开源库,由于DCMTK开源框架的文档比较少,所有有很多使用者使用比较不爽,欢迎大家相互交流
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。