一. windows 端编译 dcmtk支持 jpeg2000 编解码dcm文件
dcmjp2k模块非免费:https://dicom.offis.de/en/dcmtk-expansion-modules/dcmjp2k/
为了解决该问题dcmtk支持jpeg2000编解码dcm文件,在GitHub上看到
1.准备工作: 从dcmtk官网中 下载dcmtk源码 和openjpeg源码:
https://dicom.offis.de/download/dcmtk/dcmtk366/support/openjpeg-v2.4.0.tar.gz
https://dicom.offis.de/download/dcmtk/dcmtk366/dcmtk-3.6.6.tar.gz
将源码解压,在windows端编译 openjpeg-v2.4.0 dcmtk-3.6.6 为 Release x64版本的库。使用cmake工具编译。
2.从githup中下载源码:https://github.com/DraconPern/fmjpeg2koj
3.将fmjpeg2koj目录中 include文件拷贝到自己的工程目录下,及8个.cc文件
4.写测试代码 支持jpeg2000解压,取图片像素数据。
dicom_open.h
#ifndef DICOM_OPEN_H
#define DICOM_OPEN_H
typedef struct _DcmFileMessgae
{
int isCompressIndex; //dicom文件是否压缩 0:未压缩 1:压缩
const char* patientID; //患者ID
const char* patientName; //患者姓名
const char* patientSex; //患者性别
const char* patientAge; //患者年龄
const char* patientBirthDate; //患者生日
const char* patientAddress; //患者家庭地址
const char* modality; //dcm文件类型 CT MR DX US
const char* photoIntertion; //图像模式 MONOCHROME2/MONOCHROME1
const char* transferSyntax; //dicom压缩语法
const char* studyDate; //检查日期
const char* studyTime; //检查时间
const char* studyID; //检查ID
const char* studyInstanceUID; //检查UID
const char* studyDescription; //检查描述
const char* seriesDate; //检查日期
const char* seriesTime; //检查时间
const char* seriesInstanceUID;
const char* seriesDescription; //检查描述和说明
const char* sopInstanceUID;
const char* sopClassUID;
const char* contentDate;
const char* seriesNumber; //序列号
long int numFrame; //图片数量
unsigned short mWidth; //图片宽度
unsigned short mHeight; //图片高度
unsigned short wCenter; //窗位
unsigned short wWidth; //窗高
unsigned short bitsCount; //一个像素取样点存储时使用到的位数 8 12 16
unsigned short bitsAllcated; //一个像素取样点存储时分配到的位数
unsigned short bitsSample; //1灰度图 3彩色图
unsigned short pixelReNum; //有符号存储 或无符号存储
unsigned short bitsHigh; //最高位序号,它定义了存储点在分配的内存中的排列方式
unsigned short instanceNumber; //
}DcmFileMessage;
/*
function: 初始化读取dcm文件,获取dcm文件信息,解压dcm文件
@filepath: dcm文件路径
@pt: dcm文件信息结构体
return: 正常返回0
*/
int initDcmFile_t(const char *filepath, DcmFileMessage *pt);
/*
function: 保存dicom图片数据为bmp图片
@bmppath: bmp文件路径
@numIndex: dcm第几张图片序列号
return: 0=正常返回0
*/
int saveBmpFile_t(const char *bmppath, int numIndex);
/*
function: 释放dcm文件内存
*/
int freeDcmFile_t();
dicom_open.cpp
#include "dicom_open.h"
#include "dcmtk/dcmimage/diregist.h"
#include "dcmtk/dcmimgle/dcmimage.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcpixel.h"
#include "dcmtk/ofstd/ofcond.h"
#include "dcmtk/ofstd/ofstring.h"
#include "dcmtk/dcmdata/dcitem.h"
#include "dcmtk/dcmdata/dctagkey.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcmetinf.h"
#include "dcmtk/dcmjpeg/djdecode.h"
#include "dcmtk/dcmdata/dcrledrg.h"
#include "dcmtk/dcmjpls/djdecode.h"
#include "fmjpeg2k/djdecode.h"
#include <iostream>
#include <string>
using namespace std;
static DcmFileFormat *fileFormat = nullptr;
static DcmDataset *dataSet = nullptr;
static E_TransferSyntax xfer; //dicom传输语法
/*
function: 初始化读取dcm文件,获取dcm文件信息,解压dcm文件
@filepath: dcm文件路径
@pt: dcm文件信息结构体
return: 正常返回0
*/
int initDcmFile_t(const char *filepath, DcmFileMessage *pt)
{
if (fileFormat)
{
delete fileFormat;
fileFormat = nullptr;
}
if (fileFormat == nullptr)
{
fileFormat = new DcmFileFormat();
}
OFCondition condition = fileFormat->loadFile(filepath);
if(condition.bad())
{
printf("dcm file load failed\n");
return -1;
}
dataSet = fileFormat->getDataset(); //dicom数据集
DcmMetaInfo *metaInfo= fileFormat->getMetaInfo();
xfer = dataSet->getOriginalXfer(); //dicom传输语法
// 获取dicom文件压缩语法
metaInfo->findAndGetString(DCM_TransferSyntaxUID, pt->transferSyntax);
if(pt->transferSyntax == nullptr)
{
pt->isCompressIndex = 0;
pt->transferSyntax = "1.2.840.10008.1.2";
}
else
{
pt->isCompressIndex = 1;
}
dataSet->findAndGetString(DCM_PatientID, pt->patientID);
dataSet->findAndGetString(DCM_PatientName, pt->patientName);
dataSet->findAndGetString(DCM_PatientSex, pt->patientSex);
dataSet->findAndGetString(DCM_PatientAge, pt->patientAge);
dataSet->findAndGetString(DCM_PatientBirthDate, pt->patientBirthDate);
dataSet->findAndGetString(DCM_PatientAddress, pt->patientAddress);
dataSet->findAndGetString(DCM_StudyDate, pt->studyDate);
dataSet->findAndGetString(DCM_StudyTime, pt->studyTime);
dataSet->findAndGetString(DCM_StudyID, pt->studyID);
dataSet->findAndGetString(DCM_StudyInstanceUID, pt->studyInstanceUID);
dataSet->findAndGetString(DCM_StudyDescription, pt->studyDescription);
dataSet->findAndGetString(DCM_SeriesDate, pt->seriesDate);
dataSet->findAndGetString(DCM_SeriesTime, pt->seriesTime);
dataSet->findAndGetString(DCM_SeriesInstanceUID, pt->seriesInstanceUID);
dataSet->findAndGetString(DCM_SeriesDescription, pt->seriesDescription);
dataSet->findAndGetString(DCM_SOPInstanceUID, pt->sopInstanceUID);
dataSet->findAndGetString(DCM_SOPClassUID, pt->sopClassUID);
dataSet->findAndGetString(DCM_ContentDate, pt->contentDate);
dataSet->findAndGetString(DCM_PhotometricInterpretation, pt->photoIntertion); //获取图像模式
dataSet->findAndGetString(DCM_Modality, pt->modality); //图像类型 CT DX MR
dataSet->findAndGetLongInt(DCM_NumberOfFrames, pt->numFrame); //获取图片数量
if(pt->numFrame == 0)
pt->numFrame =1;
dataSet->findAndGetUint16(DCM_BitsStored, pt->bitsCount); //图像位数 8 12 16
dataSet->findAndGetUint16(DCM_BitsAllocated, pt->bitsAllcated); //图像存储
dataSet->findAndGetUint16(DCM_SamplesPerPixel, pt->bitsSample); //图像像素字节
dataSet->findAndGetUint16(DCM_Columns, pt->mWidth); //图像宽度
dataSet->findAndGetUint16(DCM_Rows, pt->mHeight); //图像高度
dataSet->findAndGetUint16(DCM_WindowCenter, pt->wCenter); //图像窗位
dataSet->findAndGetUint16(DCM_WindowWidth, pt->wWidth); //图像宽位
dataSet->findAndGetUint16(DCM_PixelRepresentation, pt->pixelReNum); //判断数据是有符号存储,还是无符号存储
dataSet->findAndGetUint16(DCM_HighBit, pt->bitsHigh);
dataSet->findAndGetUint16(DCM_InstanceNumber, pt->instanceNumber);
dataSet->findAndGetString(DCM_SeriesNumber, pt->seriesNumber);
if (!pt->patientID)
pt->patientID = "";
if (!pt->patientName)
pt->patientName = "";
if (!pt->patientSex)
pt->patientSex = "";
if (!pt->patientAge)
pt->patientAge = "";
if (!pt->patientAddress)
pt->patientAddress = "";
if (!pt->patientBirthDate)
pt->patientBirthDate = "";
if (!pt->studyDate)
pt->studyDate = "";
if (!pt->studyTime)
pt->studyTime = "";
if (!pt->studyID)
pt->studyID = "";
if (!pt->studyInstanceUID)
pt->studyInstanceUID = "";
if (!pt->studyDescription)
pt->studyDescription = "";
if (!pt->seriesDate)
pt->seriesDate = "";
if (!pt->seriesTime)
pt->seriesTime = "";
if (!pt->seriesInstanceUID)
pt->seriesInstanceUID = "";
if (!pt->seriesDescription)
pt->seriesDescription = "";
if (!pt->sopInstanceUID)
pt->sopInstanceUID = "";
if (!pt->sopClassUID)
pt->sopClassUID = "";
if (!pt->contentDate)
pt->contentDate = "";
if (!pt->photoIntertion)
pt->photoIntertion = "";
if (!pt->modality)
pt->modality = "";
if (!pt->seriesNumber)
pt->seriesNumber = "";
/* jpeg */
string loss_JPEGProcess1 = "1.2.840.10008.1.2.4.50";
string loss_JPEGProcess2_4 = "1.2.840.10008.1.2.4.51";
string loss_JPEGProcess6_8 = "1.2.840.10008.1.2.4.53";
string loss_JPEGProcess10_12 = "1.2.840.10008.1.2.4.55";
string loss_JPEGProcess14 = "1.2.840.10008.1.2.4.57";
string loss_JPEGProcess14SV1 = "1.2.840.10008.1.2.4.70";
/* RLELoss */
string loss_RLELossless = "1.2.840.10008.1.2.5";
/* jpegls */
string loss_JPEGLSLossless = "1.2.840.10008.1.2.4.80";
string loss_JPEGLSLossy = "1.2.840.10008.1.2.4.81";
/* jpeg2000 */
string loss_JPEG2000LosslessOnly = "1.2.840.10008.1.2.4.90";
string loss_JPEG2000 = "1.2.840.10008.1.2.4.91";
// 开始解压dcm文件
if (pt->isCompressIndex)
{
if (pt->transferSyntax == loss_JPEGProcess1 || pt->transferSyntax == loss_JPEGProcess2_4 || pt->transferSyntax == loss_JPEGProcess6_8 ||
pt->transferSyntax == loss_JPEGProcess10_12 || pt->transferSyntax == loss_JPEGProcess14 || pt->transferSyntax == loss_JPEGProcess14SV1)
{
DJDecoderRegistration::registerCodecs();
dataSet->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DJDecoderRegistration::cleanup();
}
else if (pt->transferSyntax == loss_RLELossless)
{
DcmRLEDecoderRegistration::registerCodecs();
dataSet->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DcmRLEDecoderRegistration::cleanup();
}
else if (pt->transferSyntax == loss_JPEGLSLossless || pt->transferSyntax == loss_JPEGLSLossy)
{
DJLSDecoderRegistration::registerCodecs();
dataSet->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
DJLSDecoderRegistration::cleanup();
}
else if (pt->transferSyntax == loss_JPEG2000 || pt->transferSyntax == loss_JPEG2000LosslessOnly)
{
FMJPEG2KDecoderRegistration::registerCodecs();
dataSet->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
FMJPEG2KDecoderRegistration::cleanup();
}
else
{
dataSet->chooseRepresentation(xfer, NULL);
}
}
return 0;
}
/*
function: 保存dicom图片数据为bmp图片
@bmppath: bmp文件路径
@numIndex: dcm第几张图片序列号
return: 0=正常返回0
*/
int saveBmpFile_t(const char *bmppath, int numIndex)
{
DicomImage *diImage = new DicomImage(dataSet, xfer, CIF_UsePartialAccessToPixelData, numIndex, 1);
if (!diImage)
{
printf("malloc DicomImage failed\n");
return -1;
}
FILE *bmpFile = fopen(bmppath, "wb");
if(!bmpFile)
{
printf("open bmp file failed\n");
return -2;
}
diImage->writeBMP(bmpFile, 24, 0);
if(diImage)
{
delete diImage;
diImage = nullptr;
}
if(bmpFile)
{
fclose(bmpFile);
}
return 0;
}
/*
function: 释放dcm文件内存
*/
int freeDcmFile_t()
{
if(fileFormat)
{
delete fileFormat;
fileFormat = nullptr;
}
return 0;
}
main.cpp
#include "dicom_open.h"
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <string.h>
int main(int argc, char *argv[])
{
//图片数量
int num = 0;
char dcm_filepath[128] = "";
char bmp_filepath[128] = "";
char tmp_buff[128] = "";
memcpy(dcm_filepath, argv[1], strlen(argv[1]));
DcmFileMessage *pt = (DcmFileMessage*)malloc(sizeof(DcmFileMessage));
if (!pt)
{
printf("malloc DcmFileMessage failed\n");
return -1;
}
//初始化读取dcm文件,获取dcm文件信息,解压dcm文件
if (initDcmFile_t(dcm_filepath, pt) < 0)
{
goto endX;
}
printf("patientID=%s\n", pt->patientID);
printf("patientName=%s\n", pt->patientName);
printf("patientSex=%s\n", pt->patientSex);
printf("patientAge=%s\n", pt->patientAge);
printf("imgWidth=%d\n", pt->mWidth);
printf("imgHeight=%d\n", pt->mHeight);
num = pt->numFrame;
sscanf(dcm_filepath, "%[^.]", tmp_buff);
for (int i = 0; i < num; i++)
{
// bmp文件名
memset(bmp_filepath, 0, sizeof(bmp_filepath));
sprintf(bmp_filepath, "%s_%03d.bmp", tmp_buff, i);
printf("bmp_filepath=[%s]\n", bmp_filepath);
// 保存dicom图片数据为bmp图片
if(saveBmpFile_t(bmp_filepath, i) < 0)
{
goto endX;
}
}
endX:
// 释放dcm文件内存
freeDcmFile_t();
if (pt)
{
free(pt);
pt = NULL;
}
return 0;
}
测试应用程序, 编译完终端执行 ./main ./file/DX_dcm1.dcm
结果:如下图 ,左边是jpeg2000编码dcm文件,右边是解码后的bmp文件