前言
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)
2.下载编译工具(CMake)
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
最新的编译工具CMake官方网站地址(https://cmake.org/download/)
3.CMake编译
1.把下载的DCMTK加压到一个文件DCMTK_TEST中,
2.建立一个CMake编译过项目存放的位置DCMTK_Project。
3.打开CMake编译DCMTK
- 选择编译源码的路径和生成项目的路径
-
配置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开源框架的文档比较少,所有有很多使用者使用比较不爽,欢迎大家相互交流