记录之前写过一个读取DICOM文件,修改对应Tag标签内容后,保存为新的DICOM文件的例子;
其中的对DICOM信息处理的过程为,将DICOM中的一系列连续DICOM图像,处理后生成一张多帧的DICOM文件;
主要步骤为:
1.读取DICOM文件;
2.对DICOM文件中的信息处理,修改;
3.保存为新的DICOM文件;

读取DICOM文件

读取DICOM文件的信息,使用一系列的findAndGetOFXXX方法;
可以根据DICOM标签中的VR标识来选择,DCMTK库中每个findAndGetOFXXX方法都有详细的注释;

读取DICOM文件的步骤为:

DcmFileFormat* nm_dcm_format = new DcmFileFormat;
OFCondition cond = nm_dcm_format->loadFile(nm_file.c_str());
// DICOM信息头
DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();
// DICOM信息
DcmDataset* dataset = nm_dcm_format->getDataset();
OFString new_instance_id;
if (meta_info->findAndGetOFString(DCM_MediaStorageSOPInstanceUID, new_instance_id).good())
{instance_id = new_instance_id.c_str();
}

保存DICOM文件

保存DICOM文件就很简单了。对应findAndGetOFXXX方法有putAndInsertXXX方法来设置DICOM标签对应的信息;

// 1.构建DICOM文件的信息;
DcmFileFormat* nm_dcm_format = new DcmFileFormat;
DcmDataset* dataset = nm_dcm_format->getDataset();
dataset->putAndInsertString(DCM_FrameOfReferenceUID, series_instance_id.c_str());
// 2.使用saveFile方法,保存DICOM文件;
nm_dcm_format->saveFile(mult_pet_file.c_str());

注意:DICOM标签内的数据长度必须是偶数,如果实际内容为奇数,就需要补足为偶数字节个数;

主要代码


std::string getTime()
{time_t timep;time(&timep);struct tm* p = localtime(&timep);char tmp[64] = { 0 };sprintf(tmp, "%02d%02d%02d.0000 ", p->tm_hour, p->tm_min, p->tm_sec);return tmp;
}class Dicom_info
{public:Dicom_info(DcmFileFormat* nm_dcm_format):number_of_frames(0){DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();DcmDataset* dataset = nm_dcm_format->getDataset();// 0002,0003序列ID;OFString new_instance_id;if (meta_info->findAndGetOFString(DCM_MediaStorageSOPInstanceUID, new_instance_id).good()){instance_id = new_instance_id.c_str();}OFString of_series_instance_id;if (dataset->findAndGetOFString(DCM_SeriesInstanceUID, of_series_instance_id).good()){series_instance_id = of_series_instance_id.c_str();}OFString of_frame_referenceUID;if (dataset->findAndGetOFString(DCM_FrameOfReferenceUID, of_frame_referenceUID).good()){frame_referenceUID = of_frame_referenceUID.c_str();}const char* pbuf = NULL;if (dataset->findAndGetString(DCM_PixelSpacing, pbuf).good()){pixel_spacings = Tool::DealString::SplitString(pbuf, "\\");}if (!dataset->findAndGetUint16(DCM_Rows, rows).good()){rows = 0;}if (!dataset->findAndGetUint16(DCM_Columns, columns).good()){columns = 0;}const Uint16* pix_inbuf = nullptr;unsigned long size = 0;if (dataset->findAndGetUint16Array(DCM_PixelData, pix_inbuf, &size).good()){pix_buf = new float[size];for (size_t i = 0; i < size; i++){pix_buf[i] = pix_inbuf[i];}}OFString numberOfFrames;if (dataset->findAndGetOFString(DCM_NumberOfFrames, numberOfFrames).good()){number_of_frames = atoi(numberOfFrames.c_str());}}~Dicom_info(){if (pix_buf != NULL){delete[]pix_buf;}}int ChangeDcmFileFormat(DcmFileFormat* nm_dcm_format, float* pinbuf, int rows, int columns, int num){try{DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();DcmDataset* dataset = nm_dcm_format->getDataset();instance_id += Superaddition;CheckStringLengthIsEvenSize(instance_id);meta_info->putAndInsertString(DCM_MediaStorageSOPInstanceUID, instance_id.c_str());dataset->putAndInsertString(DCM_SOPInstanceUID, instance_id.c_str());series_instance_id += Superaddition;CheckStringLengthIsEvenSize(series_instance_id);dataset->putAndInsertString(DCM_SeriesInstanceUID, series_instance_id.c_str());frame_referenceUID += Superaddition;CheckStringLengthIsEvenSize(frame_referenceUID);dataset->putAndInsertString(DCM_FrameOfReferenceUID, series_instance_id.c_str());std::string cur_time = getTime();dataset->putAndInsertString(DCM_InstanceCreationTime, cur_time.c_str());dataset->putAndInsertString(DCM_ImageType, "RECON TOMO");//dataset->putAndInsertString(DCM_SeriesDescription, "MEMRS RECON RESULTS ");std::string org_value = pixel_spacings.at(0);std::string neg_value = "-" + org_value;CheckStringLengthIsEvenSize(org_value);CheckStringLengthIsEvenSize(neg_value);dataset->putAndInsertString(DCM_SliceThickness, org_value.c_str());dataset->putAndInsertString(DCM_SpacingBetweenSlices, neg_value.c_str());dataset->putAndInsertString(DCM_AcquisitionTerminationCondition, "MANU");std::string num_str = std::to_string(num);CheckStringLengthIsEvenSize(num_str);dataset->putAndInsertString(DCM_NumberOfFrames, num_str.c_str());dataset->putAndInsertUint16(DCM_NumberOfSlices, num);dataset->putAndInsertUint16(DCM_Rows, rows);dataset->putAndInsertUint16(DCM_Columns, columns);dataset->putAndInsertString(DCM_CorrectedImage, "ATTN");std::string image_orientation = "1.000000\\0.000000\\0.000000\\0.000000\\1.000000\\0.000000";CheckStringLengthIsEvenSize(image_orientation);DcmItem* sq_item;if (dataset->findAndGetSequenceItem(DCM_DetectorInformationSequence, sq_item).good()){sq_item->putAndInsertString(DCM_ImageOrientationPatient, image_orientation.c_str());}dataset->putAndInsertString(DCM_ImageID, "TOMO_IRAC ");DcmElement* element;int out_size = rows * columns * num;Uint16* pix_buf = new Uint16[out_size];int max_pixel = INT_MIN;int min_pixel = INT_MAX;for (size_t i = 0; i < out_size; i++){pix_buf[i] = pinbuf[i] * 1000;if (max_pixel < pix_buf[i]){max_pixel = pix_buf[i];}if (min_pixel > pix_buf[i]){min_pixel = pix_buf[i];}}dataset->putAndInsertUint16(DCM_SmallestImagePixelValue, min_pixel);dataset->putAndInsertUint16(DCM_LargestImagePixelValue, max_pixel);std::pair<double, double> center_width = CalculateWindowCenterAndWidth(max_pixel, min_pixel);std::string window_center_str = std::to_string(center_width.first);std::string window_width_str = std::to_string(center_width.second);CheckStringLengthIsEvenSize(window_center_str);CheckStringLengthIsEvenSize(window_width_str);//DCM_WindowCenter DCM_WindowWidthdataset->putAndInsertString(DCM_WindowCenter, window_center_str.c_str());dataset->putAndInsertString(DCM_WindowWidth, window_width_str.c_str());Uint16* deal_buf = new Uint16[out_size];int size_one_image = num * columns;int size_org = rows * columns;// 一共多少张横断图for (size_t i = 0; i < columns; i++){Uint16* one_image = deal_buf + i * size_one_image;// 对每张图的行数据进行拼接;for (size_t j = 0; j < rows; j++){memcpy(one_image + (num - 1 - j) * num, pix_buf + j * size_org + i * rows, columns * sizeof(Uint16));}}if (dataset->findAndGetElement(DCM_PixelData, element).good()){element->putUint16Array(deal_buf, out_size);}delete[] deal_buf;delete[] pix_buf;return 0;}catch (...){return 1;}}
private:void CheckStringLengthIsEvenSize(std::string content){if (content.size() / 2 != 0){content += " ";}}std::pair<double, double> CalculateWindowCenterAndWidth(int max_pixel, int min_pixel){double windowWidth = max_pixel - min_pixel;double windowCenter = min_pixel + windowWidth / 2.0;return std::make_pair(windowCenter, windowWidth);}
public:std::string instance_id;std::string series_instance_id;std::string frame_referenceUID;std::vector<std::string> pixel_spacings;float* pix_buf;Uint16 rows;Uint16 columns;double window_center;double window_width;Uint16 number_of_frames;
};class ReconDicom
{public:static int GenerateMultPET(std::string nm_file, std::string mult_pet_file){ODI("GenerateMultPET begin");if (nm_file.empty() || mult_pet_file.empty()){ODI("文件名为空");return 1;}DcmFileFormat* nm_dcm_format = new DcmFileFormat;      OFCondition cond = nm_dcm_format->loadFile(nm_file.c_str());if (cond.good()){Dicom_info decode_dicom(nm_dcm_format);DcmRawData data_out = { 0 };DcmRawData raw_data;ret = decode_dicom.ChangeDcmFileFormat(nm_dcm_format, data_out._img_data, data_out._size_x, data_out._size_y, data_out._series_num);if (ret == 1){return -1;}cond = nm_dcm_format->saveFile(mult_pet_file.c_str());if (cond.bad()){return -1;}}return 0;}
};

DICOM笔记-使用DCMTK读取DICOM文件保存DICOM文件相关推荐

  1. DCMTK读取压缩格式的DICOM文件并使用Vtk显示

    想必看这篇文章的读者都对vtk有或多或少的认识,vtk中的vtkDICOMImageReader是用来读取DICOM文件的类,但是其只能读取未压缩格式的DICOM文件,本人手头上需要显示和处理的DIC ...

  2. c语言打开文件保存文件格式,文件的打开与保存(C语言)

    最近在弄文件的传输,基本流程就是: 打开文件->读取文件->传输(scoket模块) ->写入文件->保存. 现在单单看文件操作的部分,弄了我3天时间没想明白,看下面的代码. ...

  3. 十三、PyQt5的QFileDialog文件打开、文件保存、文件夹选择对话框

    import PyQt5.QtCore,PyQt5.QtGui # 获取文件路径对话框 file_name = QFileDialog.getOpenFileName(self,"打开文件& ...

  4. c语言保存后怎么打开文件,保存打开文件之后,怎么也不能在显示函数中出来。。...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 抑郁了..各位指点一下.. void WritetoText(Person per[],int n) { int i=0; FILE *fp;       ...

  5. c语言怎么打开已经保存的文件,保存打开文件之后,怎么也不能在显示函数中出来。。...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 抑郁了..各位指点一下.. void WritetoText(Person per[],int n) { int i=0; FILE *fp;       ...

  6. iphone 文件保存策略

    iphone 文件保存策略 文件保存策略: 一般有三中方法:1,属性列表,2,对象归档,3,iphone的嵌入式数据库库(sqLite3) 1,属性列表 存储文件: //获取文档目录,NSDocume ...

  7. 将文件保存到数据库中

    在编程中我们常常会遇到"将文件保存到数据库中"这样一个问题,虽然这已不是什么高难度的问题,但对于一些刚刚开始编程的朋友来说可能是有一点困难.其实,方法非常的简单,只是可能由于这些朋 ...

  8. 计算机基础(四):C语言字符串处理文件保存

    一.字符串函数 1.strstr(str1,str2)  用于判断字符串str2是否是str1的子串.如果是,则该函数返回str2在str1中首次出现的地址:否则,返回NULL. 2.char *st ...

  9. C语言动态内存使用多文件实现通讯录,并可以保存在文件中

    一.使用多文件实现通讯录 1.头文件"Contact.h" 自己写的头文件:只要写包含库函数的头文件,和自己写的函数的声明,个人信息结构体,通讯录结构体,以及枚举,和宏定义. #p ...

  10. GDCM:读取两个DICOM文件保存在另外dicom文件中的测试程序

    GDCM:读取两个DICOM文件保存在另外dicom文件中 GDCM:读取两个DICOM文件保存在另外dicom文件中 GDCM:读取两个DICOM文件保存在另外dicom文件中 #include & ...

最新文章

  1. kubelet配置cni插件_从零开始入门 K8s | 理解 CNI 和 CNI 插件
  2. Android Studio不安装opencv manager配置
  3. SQL语法练习 - 使用WITH AS提高性能简化嵌套SQL
  4. 【LightOJ - 1038】Race to 1 Again(概率dp,数学期望)
  5. Java 判断目录是否为空
  6. springboot项目打war包发布到外置tomcat
  7. Dockerfile 中的 CMD 与 ENTRYPOINT
  8. 136_原始套接字_链路层MAC包_模仿他人飞秋,给自己主机上的飞秋【发送UDP数据】【只需要修改包含用户名、头像信息的数组】
  9. win7旗舰版上装VS2010错误(提示:miicrosoft 应用程序报告[安装失败])
  10. .net RestSharp使用
  11. spring html导出excel文件,Spring 导出 Excel-Fun言
  12. 高级程序员解决问题的思维模式和普通程序员的区别在哪里?
  13. 服务器日志修改保存时间,日志服务保留时间
  14. LVGL系列(四)概述 之 位置、尺寸和布局
  15. java 编码app_智慧职教mooc的APPJava编码技术(四川交通职业技术学院)答案搜题公众号...
  16. java公路车组装教程_自行车DIY入门教程,图文展示自行车组装全过程。(原创图文,转载请注明出处)...
  17. 任天堂服务器维护12月1,《怪物猎人:崛起》太火爆!任天堂服务器紧急维护!会是独占游戏么?...
  18. 高通常用缩写 --不错
  19. 关于使用通用mapper出现的错误
  20. 从GMT时间转换到当地时间(北京时间)

热门文章

  1. Javashop连锁门店管理系统带您玩转获客
  2. 苹果ipad怎么录屏_追剧,玩游戏必备,这才是苹果手机正确的投屏操作,网友:没白拿...
  3. java ognl表达式_常用的OGNL表达式
  4. 2021-2027全球与中国DJ设备市场现状及未来发展趋势
  5. 金山pdf阅读器 独立版V10.1.0.6683
  6. java 连线题_java练习题
  7. Proteus与emu8086实现流水灯联调
  8. 51单片机的超声波测距仪制作教程
  9. (二)以太网与WiFi协议
  10. SPSS使用命令语法之复制与粘贴讲解