使用 DCMTK 和 CxImage 将 DICOM 图像转换为通用图形格式(反之亦然)
将 DICOM 文件转换为 BMP/JPG 及反之

引言
本文介绍了一个最小的可运行的示例应用程序,作为一个起点,展示如何将 DICOM 图像转换为常见的图形格式(例如 BMP、JPG、TIF 等)以及反之。我们的示例应用程序基于两个开源库,它们是 DCMTK 和 CxImage
。
背景
DICOM 标准(医学数字成像和通信)是由美国电气制造商协会 (NEMA) 创建的标准,旨在简化医疗图像(如 CT 扫描、MRI 和超声)的分发和交换。在本文中,我们将重点关注文件格式转换。文件格式在 DICOM 标准的第 10 部分中进行了描述,您可以从这里下载[^]。这里还有一个对文件格式的简短介绍可用。
DCMTK 是一个广泛使用的 DICOM 标准开源实现;它是一组 C/C++ 库和应用程序,带有完整的源代码。要编译本文中的示例,您需要先下载 DCMTK。如果您在构建下载的 DCMTK 包时遇到问题,请参考DCMTK for Dummies。
本文中使用的另一个库是 CxImage
,它是一个 C++ 类,可以以一种非常简单快速的方式加载、保存、显示和转换图像。它支持几乎所有常见的图形类型,例如 BMP、JPG、TIF、PNG 等。在本文中,我们将扩展这个库,通过使用 DCMTK 提供的 DICOM 格式编码/解码功能来支持显示和转换 DICOM 图像。下载 CxImage并按照其使用指南确保它可以在您的机器上成功编译。
Using the Code
我们简单地从基类 CxImage
派生我们的 CxImageDCM
类。这样做使 CxImageDCM
类能够使用从基类继承的方法加载和解码通用图形。派生类中有三个额外的方法,LoadDCM(…)
、SaveAsDCM(…)
、SaveAsJPG(…)
,它们分别用于解码、编码和转换 DICOM 图像。
//
class CxImageDCM : public CxImage
{
public:
CxImageDCM();
virtual ~CxImageDCM();
bool LoadDCM(const TCHAR* filename);
bool SaveAsDCM(const TCHAR* filename);
bool SaveAsJPG(const TCHAR* fileName);
};//
加载 DCM
在示例应用程序中,使用 DCMTK 提供的类加载和解码 DICOM 图像,然后将其转换为临时位图文件以供以后操作
//
bool CxImageDCM::LoadDCM(const TCHAR* filename)
{
DcmFileFormat *dfile = new DcmFileFormat();
OFCondition cond = dfile->loadFile(filename, EXS_Unknown,
EGL_withoutGL,DCM_MaxReadLength,OFFalse);
if (cond.bad()) {
AfxMessageBox(cond.text());
}
E_TransferSyntax xfer =
dfile->getDataset()->getOriginalXfer();
DicomImage *di = new DicomImage(dfile, xfer,
CIF_AcrNemaCompatibility, 0, 1);
if (di->getStatus() != EIS_Normal)
AfxMessageBox(DicomImage::getString(di->getStatus()));
di->writeBMP("c:\\from_dicom.bmp",24);
return CxImage::Load("c:\\from_dicom.bmp",CXIMAGE_FORMAT_BMP);
}//
从 DCM 转换
在加载 DCM 文件后,您可以使用 CxImage
提供的编码功能将其保存为通用图形文件,或者您也可以使用 DCMTK 的编码插件进行转换(但是,CxImage
支持更多格式)
//
bool CxImageDCM::SaveAsJPG(const TCHAR* fileName)
{//you may also use DCMTK's JPG encoding plug-in
return CxImage::Save(fileName,CXIMAGE_FORMAT_JPG);
}//
转换为 DCM
要将通用图形文件转换为 DCM 文件,您需要先加载通用图形,然后设置必要的标签并将像素数据复制到目标 DCM 文件
//
bool CxImageDCM::SaveAsDCM(const TCHAR* filename)
{
CxImageDCM::IncreaseBpp(24);
char uid[100];
DcmFileFormat fileformat;
DcmDataset *dataset = fileformat.getDataset();
dataset->putAndInsertString(DCM_SOPClassUID,
UID_SecondaryCaptureImageStorage);
/* ... */
//dataset->putAndInsertUint32(DCM_MetaElementGroupLength,128);
dataset->putAndInsertUint16(DCM_FileMetaInformationVersion,
0x0001);
/* ... */
dataset->putAndInsertString(DCM_UID,
UID_MultiframeTrueColorSecondaryCaptureImageStorage);
dataset->putAndInsertString(DCM_PhotometricInterpretation,
"RGB");
//add more tags here
/* ... */
BYTE* pData=new BYTE[GetHeight()*info.dwEffWidth];
BYTE* pSrc=GetBits(head.biHeight-1);
BYTE* pDst=pData;
for(long y=0; y < head.biHeight; y++){
memcpy(pDst,pSrc,info.dwEffWidth);
pSrc-=info.dwEffWidth;
pDst+=info.dwEffWidth;
}
dataset->putAndInsertUint8Array(DCM_PixelData,
pData, GetHeight()*info.dwEffWidth);
delete[] pData;
OFCondition status = fileformat.saveFile(filename,
EXS_LittleEndianImplicit,
EET_UndefinedLength,EGL_withoutGL);
if (status.bad())
AfxMessageBox("Error: cannot write DICOM file ");
return true;
}//
关注点
在本文中,使用 CxImage
提供的编码功能将 DICOM 图像转换为 JPG 文件(或 CxImage
支持的其他格式)。实际上,DCMTK 已经有一个名为 dcmj2pnm 的成熟实用程序,用于将 DICOM 图像转换为 BMP、PNG、TIF 或 JPG 图像。对于 dcmj2pnm 不支持的其他格式,例如 GIF、TGA、PCX、WBMP 等,您可以使用 CxImage
的编码功能来编写自己的转换函数。这里需要澄清的一件事是,我们的示例应用程序只是一个提供入门点的玩具实用程序。要编写一个像样的 DICOM 图像转换器,您需要考虑更多与 DICOM 相关的选项。有关更多信息,您可以参考 dcmj2pnm 的实现。(它包含在 DCMTK 源代码包中。)
根据我的经验,CxImage
易于使用;它“可以以简单快速的方式加载、保存、显示和转换图像”。但是,当您必须从基类 CxImage
派生一个新的图像编码器/解码器时,我发现它很烦人。基类必须知道所有派生类才能提供多态行为。幸运的是,在我们的示例中,派生类 CxImageDCM
只需要它从基类继承的编码/解码函数,所以我懒得去动 CxImage
的源代码。