一般情况下,我们都是使用DCMTK库来读写DICOM文件,例如使用下面代码保存DICOM文件;

DcmFileFormat* nm_dcm_format = new DcmFileFormat;
OFCondition cond = nm_dcm_format->loadFile(nm_file.c_str());
cond = nm_dcm_format->saveFile(mult_pet_file.c_str());

主要使用了DcmFileFormat类的saveFile接口。

DcmFileFormat类的saveFile接口

/** save object to a DICOM file.
*  @param fileName name of the file to save (may contain wide chars if support enabled).
*    Since there are various constructors for the OFFilename class, a "char *", "OFString"
*    or "wchar_t *" can also be passed directly to this parameter.
*  @param writeXfer transfer syntax used to write the data (EXS_Unknown means use original)
*  @param encodingType flag, specifying the encoding with undefined or explicit length
*  @param groupLength flag, specifying how to handle the group length tags
*  @param padEncoding flag, specifying how to handle the padding tags
*  @param padLength number of bytes used for the dataset padding (has to be an even number)
*  @param subPadLength number of bytes used for the item padding (has to be an even number)
*  @param writeMode write file with or without meta header. Also allows for updating the
*    information in the file meta information header. The default behavior is to delete
*    all old meta information in order to create it from scratch.
*
*  @return status, EC_Normal if successful, an error code otherwise
*/
virtual OFCondition saveFile(const OFFilename &fileName,const E_TransferSyntax writeXfer = EXS_Unknown,const E_EncodingType encodingType = EET_UndefinedLength,const E_GrpLenEncoding groupLength = EGL_recalcGL,const E_PaddingEncoding padEncoding = EPD_noChange,const Uint32 padLength = 0,const Uint32 subPadLength = 0,const E_FileWriteMode writeMode = EWM_createNewMeta);

从上面的接口中得知,除了第一个参数是必需的外,其他参数都有默认值,可选项;
DcmFileFormat类除了saveFile接口,还提供了其他保存接口;

virtual OFCondition writeXML(STD_NAMESPACE ostream &out, const size_t flags = 0);
virtual OFCondition writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format);
virtual OFCondition write(DcmOutputStream &outStream,const E_TransferSyntax oxfer,const E_EncodingType enctype,DcmWriteCache *wcache,const E_GrpLenEncoding glenc,const E_PaddingEncoding padenc = EPD_noChange,const Uint32 padlen = 0,const Uint32 subPadlen = 0,Uint32 instanceLength = 0,const E_FileWriteMode writeMode = EWM_createNewMeta);
virtual OFCondition write(DcmOutputStream &outStream,const E_TransferSyntax oxfer,const E_EncodingType enctype,DcmWriteCache *wcache);

DcmFileFormat类的wrtie接口

wrtie接口就是将DICOM信息写入到输出流DcmOutputStream对象中,将DcmOutputStream对象的内存指针保存到磁盘上,就是一个可以使用的DICOM文件;
saveFile接口就是使用了wrtie接口;

OFCondition DcmFileFormat::saveFile(const OFFilename &fileName,const E_TransferSyntax writeXfer,const E_EncodingType encodingType,const E_GrpLenEncoding groupLength,const E_PaddingEncoding padEncoding,const Uint32 padLength,const Uint32 subPadLength,const E_FileWriteMode writeMode)
{if (writeMode == EWM_dataset){return getDataset()->saveFile(fileName, writeXfer, encodingType, groupLength,padEncoding, padLength, subPadLength);}OFCondition l_error = EC_InvalidFilename;/* check parameters first */if (!fileName.isEmpty()){DcmWriteCache wcache;/* open file for output */DcmOutputFileStream fileStream(fileName);/* check stream status */l_error = fileStream.status();if (l_error.good()){/* write data to file */transferInit();l_error = write(fileStream, writeXfer, encodingType, &wcache, groupLength,padEncoding, padLength, subPadLength, 0 /*instanceLength*/, writeMode);transferEnd();}}return l_error;
}

DcmFileFormat类

DcmFileFormat类的saveFile接口中使用的是DcmOutputFileStream类对象,用于向一个磁盘文件中写入DICOM文件内容;
DcmOutputStream类是一个抽象类,不可以被实例化,是一个接口类;

DcmOutputStream类中,派生出了DcmOutputBufferStream类和DcmOutputFileStream类;
下面是DcmOutputStream类提供的对外接口;

virtual ~DcmOutputStream ();
virtual OFBool good() const;
virtual OFCondition status() const;
virtual OFBool  isFlushed() const;
virtual offile_off_t avail() const;
virtual offile_off_t write(const void *buf, offile_off_t buflen);
virtual void flush ();
virtual offile_off_t tell () const;
virtual OFCondition installCompressionFilter(E_StreamCompression filterType);

DcmOutputBufferStream类

DcmOutputBufferStream类用于将DICOM信息写入调用者给定的固定长度缓冲区的输出流
DcmOutputBufferStream类的接口如下:

class DCMTK_DCMDATA_EXPORT DcmOutputBufferStream: public DcmOutputStream
{public:// buf必须是非空指针,buflen必须大于0。DcmOutputBufferStream(void *buf, offile_off_t bufLen);virtual ~DcmOutputBufferStream();virtual void flushBuffer(void *& buffer, offile_off_t& length);virtual offile_off_t filled();
private:  DcmOutputBufferStream(const DcmOutputBufferStream&);DcmOutputBufferStream& operator=(const DcmOutputBufferStream&);DcmBufferConsumer consumer_;
};

DcmOutputBufferStream类内部持有了一个DcmBufferConsumer类对象,用于对内存块的写操作;

DcmOutputFileStream类

DcmOutputFileStream类写入普通文件的输出流;

class DCMTK_DCMDATA_EXPORT DcmOutputFileStream: public DcmOutputStream
{public:DcmOutputFileStream(const OFFilename &filename);DcmOutputFileStream(FILE *file);virtual ~DcmOutputFileStream();
private:DcmOutputFileStream(const DcmOutputFileStream&);DcmOutputFileStream& operator=(const DcmOutputFileStream&);DcmFileConsumer consumer_;
};

DcmOutputBufferStream类不同之处在于,DcmOutputFileStream类持有的是一个DcmFileConsumer类对象;

将DICOM信息写入内存中

从Stack Overflow上看到一个提问DCMTK: Write DICOM file to memory,有人回答Orthanc中的Orthanc::FromDcmtkBridge::SaveToMemoryBuffer()可以实现这个功能;
下面是Orthanc::FromDcmtkBridge::SaveToMemoryBuffer()的代码;

bool FromDcmtkBridge::SaveToMemoryBuffer(std::string& buffer, DcmDataset& dataSet)
{// Determine the transfer syntax which shall be used to write the// information to the file. We always switch to the Little Endian// syntax, with explicit length.// http://support.dcmtk.org/docs/dcxfer_8h-source.html/*** Note that up to Orthanc 0.7.1 (inclusive), the* "EXS_LittleEndianExplicit" was always used to save the DICOM* dataset into memory. We now keep the original transfer syntax* (if available).**/E_TransferSyntax xfer = dataSet.getOriginalXfer();if (xfer == EXS_Unknown){// No information about the original transfer syntax: This is// most probably a DICOM dataset that was read from memory.xfer = EXS_LittleEndianExplicit;}E_EncodingType encodingType = /*opt_sequenceType*/ EET_ExplicitLength;// Create the meta-header informationDcmFileFormat ff(&dataSet);ff.validateMetaInfo(xfer);ff.removeInvalidGroups();// Create a memory buffer with the proper size{const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType);  // (*)buffer.resize(estimatedSize);}DcmOutputBufferStream ob(&buffer[0], buffer.size());// Fill the memory buffer with the meta-header and the datasetff.transferInit();OFCondition c = ff.write(ob, xfer, encodingType, NULL,/*opt_groupLength*/ EGL_recalcGL,/*opt_paddingType*/ EPD_withoutPadding);ff.transferEnd();if (c.good()){// The DICOM file is successfully written, truncate the target// buffer if its size was overestimated by (*)ob.flush();size_t effectiveSize = static_cast<size_t>(ob.tell());if (effectiveSize < buffer.size()){buffer.resize(effectiveSize);}return true;}else{// Errorbuffer.clear();return false;}
}

仿照的例子

参照这个例子,我写了一个demo;读取一个JPEG无损压缩的DICOM文件,然后保存到内存中的过程;

#include "dcmtk\config\osconfig.h"
#include "dcmtk\dcmdata\dctk.h"
#include "dcmtk\dcmdata\dcostrmb.h"
#include "dcmtk\dcmdata\dcwcache.h"#include <dcmtk/dcmjpeg/djdecode.h>  /* for dcmjpeg decoders */
#include <dcmtk/dcmjpeg/djencode.h>
#include <dcmtk/dcmjpls/djdecode.h>       //for JPEG-LS decode
#include <dcmtk/dcmjpls/djencode.h>       //for JPEG-LS encode
using namespace std;int main()
{DJDecoderRegistration::registerCodecs();DJLSEncoderRegistration::registerCodecs();     //JPEG-LS encoder registerCodecsDJLSDecoderRegistration::registerCodecs();      //JPEG-LS decoder registerCodecsDcmFileFormat *myFileFormat = new DcmFileFormat;OFCondition cond = myFileFormat->loadFile("G:\\Data\\1.dcm");E_TransferSyntax xfer = myFileFormat->getDataset()->getOriginalXfer();if (xfer == EXS_Unknown){// No information about the original transfer syntax: This is// most probably a DICOM dataset that was read from memory.xfer = EXS_LittleEndianExplicit;}E_EncodingType encodingType = EET_ExplicitLength;DcmFileFormat ff(myFileFormat->getDataset());ff.validateMetaInfo(xfer);ff.removeInvalidGroups();string buffer;const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType);  // (*)buffer.resize(estimatedSize);DcmOutputBufferStream ob(&buffer[0], buffer.size());ff.transferInit();OFCondition c = ff.write(ob, xfer, encodingType, NULL,/*opt_groupLength*/ EGL_recalcGL,/*opt_paddingType*/ EPD_withoutPadding);ff.transferEnd();if (c.good()){// The DICOM file is successfully written, truncate the target// buffer if its size was overestimated by (*)ob.flush();size_t effectiveSize = static_cast<size_t>(ob.tell());if (effectiveSize < buffer.size()){buffer.resize(effectiveSize);}FILE* pFile;pFile = fopen("G:\\Data\\3.dcm", "wb");fwrite(buffer.data(), 1, buffer.size(), pFile);fclose(pFile);return true;}else{// Errorbuffer.clear();return false;}
}

仿照将一个16位灰度数组保存JPEG无损压缩的DICOM文件

其实就是一个正常保存DICOM文件的过程,只是在最后使用saveFile的地方使用write
将二进制数组写入DICOM标签中

dset->putAndInsertUint16Array(DCM_PixelData, OFreinterpret_cast(Uint16*, pixData), length / 2);

给DICOM组装一些需要的tag信息:

DicomDataset *dataset_;
DcmFileFormat *dicom_file_;
std::string sopclassuid = "1.2.840.10008.5.1.4.1.1.2";
OFString str_sopinstanceuid;
DcmDataset *dataset = dicom_file_->getDataset();
dataset->findAndGetOFString(DCM_SOPInstanceUID, str_sopinstanceuid);
DcmMetaInfo *meta_info = dicom_file_->getMetaInfo();
meta_info_module_->SetMediaStorageSOPClassUID(sopclassuid);
meta_info_module_->SetMediaStorageSOPInstanceUID(str_sopinstanceuid.c_str());
meta_info_module_->Write(meta_info);
dataset->putAndInsertString(ToDcmTag(DCM_SOPClassUID), sopclassuid.c_str());
dataset->putAndInsertString(ToDcmTag(DCM_ConversionType), "SYN");
dataset->putAndInsertString(ToDcmTag(DCM_StationName), DW_STATION);
dataset->putAndInsertString(ToDcmTag(DCM_Manufacturer), DW_LOGO);OFCondition state = EC_IllegalParameter;// JPEG options
E_TransferSyntax opt_oxfer = EXS_JPEGProcess14SV1;
unsigned int opt_selection_value = 6;
unsigned int opt_point_transform = 0;
E_GrpLenEncoding opt_oglenc = EGL_recalcGL;
E_EncodingType opt_oenctype = EET_ExplicitLength;
E_PaddingEncoding opt_opadenc = EPD_noChange;
unsigned int opt_filepad = 0;
unsigned int opt_itempad = 0;DcmXfer opt_oxferSyn(opt_oxfer);
// create representation parameters for lossy and lossless
DJ_RPLossless rp_lossless(OFstatic_cast(int, opt_selection_value), OFstatic_cast(int, opt_point_transform));
const DcmRepresentationParameter *rp = &rp_lossless;
dataset->chooseRepresentation(opt_oxfer, rp);if (dataset->canWriteXfer(opt_oxfer)) {E_TransferSyntax xfer = opt_oxfer;               E_EncodingType encodingType = EET_ExplicitLength;DcmFileFormat ff(dataset);ff.validateMetaInfo(xfer);ff.removeInvalidGroups();             const uint32_t estimatedSize = ff.calcElementLength(xfer, encodingType);buffer.resize(estimatedSize);DcmOutputBufferStream ob(&buffer[0], buffer.size());ff.transferInit();OFCondition c = ff.write(ob, xfer, encodingType, NULL, EGL_recalcGL, EPD_noChange);ff.transferEnd();if (c.good()){ob.flush();size_t effectiveSize = static_cast<size_t>(ob.tell());if (effectiveSize < buffer.size()){buffer.resize(effectiveSize);}return 0;}else{                  buffer.clear();return 1;}
}

使用DJ_RPLossless给dataSet设置无损JPEG的参数;

DcmXfer opt_oxferSyn(opt_oxfer);
// create representation parameters for lossy and lossless
DJ_RPLossless rp_lossless(OFstatic_cast(int, opt_selection_value), OFstatic_cast(int, opt_point_transform));
const DcmRepresentationParameter *rp = &rp_lossless;
dataset->chooseRepresentation(opt_oxfer, rp);

参考资料

1.https://support.dcmtk.org/docs/annotated.html
2.https://support.dcmtk.org/docs/classDcmOutputStream.html
3.https://codesearch.isocpp.org/actcd19/main/o/orthanc/orthanc_1.5.6+dfsg-1/Core/DicomParsing/FromDcmtkBridge.cpp

DICOM笔记-使用DCMTK库的DcmOutputBufferStream类将DICOM信息序列化到内存中相关推荐

  1. java读取dcm影像文件_使用dcmtk库读取.dcm文件并获取信息+使用OpenCV显示图像

    借助VS2013和OpenCV的绘图功能,在工程DICOMReader.sln中实现了对单张.dcm图像的读取与显示,以下是详细步骤. 前期准备工作 编译器:VS2013 库:dcmtk-3.6.0( ...

  2. java动态编译类文件并加载到内存中

    如果你想在动态编译并加载了class后,能够用hibernate的数据访问接口以面向对象的方式来操作该class类,请参考这篇博文-http://www.cnblogs.com/anai/p/4270 ...

  3. C语言int类型和float浮点型数据在内存中的存储方式

    int 类型在内存中占4个字节,而一个字节是8个比特位,所以int类型占32个比特位. float类型在内存中同样也占4个字节,所以其也是占32个比特位. 一个比特位就是一个0或1,所以其在二进制位数 ...

  4. cuSPARSE库:(四)不同矩阵格式在内存中的存储方式

    (1)Dense Format,矩阵的全部元素以列优先格式(column-major format)存储在内存中, (2)Coordinate Format (COO),矩阵的非零元素以行优先格式(r ...

  5. python里面的类和对象_Python中类和对象在内存中是如何保存?

    类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图: 如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值 ...

  6. DCMTK:从命令行修改DICOM文件的类

    DCMTK:从命令行修改DICOM文件的类 从命令行修改DICOM文件的类 从命令行修改DICOM文件的类 #include "dcmtk/config/osconfig.h" # ...

  7. Python 数据分析与展示笔记3 -- Matplotlib 库基础

    Python 数据分析与展示笔记3 – Matplotlib 库基础 Python 数据分析与展示系列笔记是笔者学习.实践Python 数据分析与展示的相关笔记 课程链接: Python 数据分析与展 ...

  8. QT学习笔记(十一):QString类

    QT学习笔记(十一):QString类 1.概述 2.编辑操作 3.查询操作 3.转换操作 1.概述 1.1 QString 类是 Qt 中用于表示字符串的类,实现在 QtCore 共享库中.QStr ...

  9. JNI开发笔记(七)--aar库的生成和调用

    aar库的生成和调用 引 前言 1. 新建一个空工程 2. 新建一个Module 3. 移植JNI工程到Module中 4. 生成aar库与so库 5. 在另一个工程中调用aar库 引 JNI开发笔记 ...

  10. C++学习笔记-第4单元-对象和类(基础)

    C++学习笔记 文章目录 C++学习笔记 第4单元 对象和类(基础) 单元导读 4.1 用类创建对象 4.1.1 对象和类 4.1.2 创建对象并访问 4.2 对象拷贝.分离声明与实现 4.2.1 对 ...

最新文章

  1. 人工智能时代,教育如何做人工智能的“弄潮儿”?
  2. 每日一剂《适配器刷新报错》
  3. 双脑协同RSVP目标检测
  4. C++中变量使用前必须初始化,否则报错~
  5. 不同域名cookie共享_cookie在二级域名间共享完成sso
  6. Individual
  7. Android 可视化界面编辑器无法显示界面问题的终极解决方案
  8. Android自定义柱状图表效果
  9. 专有网络、云产品、路由器和交换机
  10. MaxScale 2.5
  11. Word符号,教你怎么在方框里打钩?
  12. Linux cat命令
  13. 基于VUE+H5PLUS 实现app交互蓝牙打印机得功能
  14. IC验证面试必考-跨时钟域
  15. 2074:【21CSPJ普及组】分糖果(candy)
  16. Chapter4.2:根轨迹法
  17. 基于tiny4412的Linux内核移植 -- 设备树的展开【转】
  18. VS2015 Winform 添加文件后 中文乱码
  19. 还在收集资料?我这里有个github汇总
  20. 【问题解决】KeyError: ‘profit‘ raise KeyError(key) from err

热门文章

  1. 15个常用excel函数公式_工作中常用的excel函数公式大全,拿来即用!
  2. XP框架开启debug模式_推荐一个兼容性强完美支持XP框架的安卓模拟器,一直在用!...
  3. 科沃斯扫地机器人阿尔法_科沃斯阿尔法智能清洁扫地机器人配置详解
  4. Manjaro/Arch 软件配置安装
  5. 卢菲菲数字编码表_学技树
  6. 如何将mp3文件转pcma格式或PCM格式的wav文件
  7. 测试远程服务器udp端口,测试远程udp端口
  8. Linux字体库ttc还是ttf,几种操作系统字体格式:otf/ttf/ttc格式字体的区别
  9. 大学高数常微分方程思维导图_思维导图_2016考研数学:高数中六种常见题型归纳_沪江英语...
  10. 简单聊聊嵌入式软件测试