DICOM笔记-使用DCMTK读取DICOM文件保存DICOM文件

2023-11-07

记录之前写过一个读取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_WindowWidth
			dataset->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;
	}
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DICOM笔记-使用DCMTK读取DICOM文件保存DICOM文件 的相关文章

  • vtk.js怎么直接读取dicom文件

    vtk js 是一个 JavaScript 库 xff0c 用于创建三维计算机图形 它不能直接读取 DICOM 文件 你需要使用其他库 xff0c 如 CornerstoneJS 或 dcmjs xff0c 将 DICOM 文件转换为 vt
  • python resize dicom(dcm)代码

    from glob import glob import os import pydicom import numpy as np import cv2 dcm list 61 sorted os listdir 39 xxxx 39 fo
  • VTK安装与配置补充2

    本文参考自 https blog csdn net sinat 25923849 article details 78889674 1 原材料 1 1 VTK 最新 Release 文件包 https www vtk org downloa
  • centos 运行.exe文件 storescu.exe 上传 DICOM文件

    由于不会使用DCMTK源码在centos中进行运行 所以使用windows下的exe进行上传 windows 下 DICOM文件上传命令 storescu exe aec LINKINGMED IP 104 v D DICOMSOURCE
  • 数字图像处理 基于python读取DICOM、NIfTI格式医学图像文件

    一 医学图像 医学图像标注最重要的进步之一是应用机器学习来评估图像 以实现更精确 更快 更准确的医学诊断 在应用机器学习 ML 人工智能 AI 或任何其他诊断算法之前 您需要知道注释软件可以处理两种最常见的医疗和保健图像文件格式 包括 DI
  • 1.2 DICOM成像协议剖析

    以下链接是本系列文章 不足之处 可在评论区讨论 系列文章 1 1 DICOM协议简介及应用 1 2 DICOM成像协议剖析 1 3 DICOM成像协议实现思路 1 4 DICOM图像CT值计算 1 5 DICOM图像CT值转RGB 1 6
  • 如何将 dicom 文件转换为 jpg

    我们如何使用java将dicom文件 dcm 转换为jpeg图像 这是我的代码 import java io File import java io IOException import org dcm4che2 tool dcm2jpg
  • 了解 DICOM 图像内的 BPP

    几天以来我一直在使用 FO DICOM 处理 DICOM 文件 我使用一组 dicom 文件进行测试 并且打印了 光度解释 和 每像素样本 值 以便更好地了解我正在处理的图像类型 光度解释的结果是 MONOCHROME2 每像素样本的结果是
  • 正确显示 DICOM 图像 ITK-VTK(图像太暗)

    我使用 itk ImageSeriesReader 和 itk GDCMImageIO 读取 dicom 图像 然后使用 itk FlipImageFilter 翻转图像 以获得图像的正确方向 并使用 itk ImageToVTKImage
  • 如何解决python中pydicom的编码问题

    这是代码 import dicom ds dicom read file FILE PATH print ds Error LookupError unknown encoding ISO 2022 IR 100 当使用 pydicom 查
  • DICOM图像的窗宽和中心计算

    DICOM 图像 CT 中的 重新缩放截距 和 重新缩放斜率 是什么 如何计算窗口宽度和窗口中心 应用重新缩放截距和斜率将图像的像素值转换为对应用程序有意义的值 例如 原始像素值可以存储设备特定值 该值仅在由生成它的设备使用时才有意义 将重
  • 如何手动解码 JPEG 无损、非分层、一阶预测

    我正在尝试仅使用 JavaScript 和 HTML5 自己创建 DICOM 查看器 过去几天我一直在研究这个项目 现在我成功解析了我需要的所有文本信息 并且我还可以正确读取和显示未压缩的灰度和 RGB 图像 现在我正在尝试显示所谓的 JP
  • DICOM 图像中引用的图像序列中的[引用的 SOP 类/实例 UID] 是什么?

    我正在使用 fo dicom 库开发模态工作列表客户端 我不清楚以下与 有关的事情Referenced SOP Instance UID 0008 1155 什么是引用的 SOP 实例 UID 整个系列的参考 SOP 实例 UID 是否相同
  • Visual Studio:MSB3073 错误退出,代码为 1

    每个人 我正在 Visual Studio 2013 中编译 DCMTK 3 6 1 我的操作系统是 Windows 8 我还使用了 CMake 3 2 3 我已经为 ALL BUILD 项目成功编译了 x64 版本的调试版本和发布版本 但
  • matlab 数组中的 DICOM 维度(所有帧都以数组的最后一个维度结束)

    在我的 GUI 之一中 我加载 DICOM 图像 有时它们只是一个体积和另一个维度 当我将它们加载到 Matlab 中时 一切都会到达我想要的位置 handles inf dicominfo filepath filename handle
  • 在Matlab中选择图像上的像素时,索引指的是什么?

    当在Matlab中查看图像的单个像素时 该索引指的是什么 X Y 指的是像素的坐标 RGB 指的是颜色 但是关于索引是什么有什么想法吗 为了澄清一下 当我在 Matlab 中查看图形并使用数据光标选择一个点时 显示的三行是 X Y 指数 R
  • DICOM 和 DICOM 叠加问题

    我有一个 DICOM 图像 我正在使用 C 读取该图像并将其转换为 16 位位图 位图已创建 但图像具有 DICOM 覆盖 我想在创建最终的 dicom 位图时将叠加层刻录到位图中 我无法做到这一点 有什么帮助吗 一种方法是创建覆盖数据的位
  • 如何使用 pyDicom 替换同一 DICOM 文件中的像素数据,以便使用任何 DICOM 查看器再次读取它?

    我想读取一些 DICOM 文件 所以我正在测试pydicom对于我的工作来说 我认为这非常有用 现在我想加载现有的 DICOM 文件 用另一个像素数组替换像素数据数组 例如预处理或实际上另一个 DICOM 像素数组 最重要的是 我想使用任何
  • 如何使用 fo-DICOM 删除或更新私有标签?

    我有很多 DICOM 数据集 其中有一个私有标签 其中包含我不想保留在标头中的信息 每个数据集的此标签的值都会发生变化 因此我不知道该值 以下是我想要更新或删除的私人创建者和私人标签的示例 0033 0010 MITRA OBJECT UT
  • Dcm4che 从本地存档 (dicomdir) 中删除研究

    在对 dcm4che3 和 dicom 协议进行一些研究后 重新表述我原来的帖子 我正在使用 dcm4che3 工具包构建一个应用程序 该应用程序本质上是一个简单的图片档案 https github com dcm4che dcm4che

随机推荐

  • Python raise用法(超级详细,看了无师自通)

    当程序出现错误时 系统会自动引发异常 除此之外 Python 也允许程序自行引发异常 自行引发异常使用 raise 语句来完成 异常是一种很 主观 的说法 以下雨为例 假设大家约好明天去爬山郊游 如果第二天下雨了 这种情况会打破既定计划 就
  • 软件测试测试环境搭建很难?一天学会这份测试环境搭建教程

    如何搭建测试环境 这既是一道高频面试题 又是困扰很多小伙伴的难题 因为你在网上找到的大多数教程 乃至在一些培训机构的课程 都不会有详细的说明 你能找到的大多数项目 是在本机电脑环境搭建环境 或是别人已经搭建好的环境 你很难上手体验在服务器上
  • IntersectionObserver实现滚动加载

    加载模板及样式 template div class lazy more div
  • WebSocket :用WebSocket实现推送你必须考虑的几个问题

    目录 目录 WebSocket简介 项目背景硬件环境及客户端支持 本文研究内容 基于javaxwebsocket服务端代码源码后续补充git连接 客户端代码 问题探索 8月3日补充 中间线路断网情况 如何做到支持几千个client同时在线人
  • WebSocket集群解决方案,不用MQ

    首先不了解WebSocket的可以先看看这篇文章 以及传统的WebSocket方案是怎么做的 https www cnblogs com jeremylai7 p 16875115 html 这是用MQ解决的版本 那么这种方案存在什么问题呢
  • 使用nginx配置二级域名

    最近想把三个项目配在一个服务器上 于是想使用nginx配置二级域名实现 1 域名添加解析 我的是阿里云的域名 所以首先给自己的域名添加解析 打算使用 www codeliu com test1 codeliu com test2 codel
  • elementUI表格行的点击事件,点击表格,拿到当前行的数据

    1 绑定事件 2 定义事件 3 点击表格某行的时候 拿到数据 转载于 https www cnblogs com wuhefeng p 11316215 html
  • STM32 PID调节输出电压

    一 简介 关于PID调节的这里不做详解 就简单说下 其实就是先设定好一个期望 通过反馈系统返回输出值 然后判断这个输出实际输出的值 和我们的期望值的误差 然后PID算法根据这个误差 去调整我们的输出值 直到输出达到我们的期望值 那么我们为啥
  • 为什么程序员做外包会被瞧不起?

    二哥 有个事想询问下您的意见 您觉得应届生值得去外包吗 公司虽然挺大的 中xx 但待遇感觉挺低 马上要报到 挺纠结的 以上是读者小 K 给我发的私信 除此之外 还有个读者 down 也问我关于外包的事情 担心外包学不到技术 但很不幸的是年前
  • 11种概率分布,你了解几个?

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 本文转自 视学算法 了解常见的概率分布十分必要 它是概率统计的基石 这是昨天推送的 从概率统计到深度学习 四大技术路线图谱 都在这里 文章中的第一大技术路线图谱如下所示
  • 【mega-nerf】调包失败&pip install失败解决方案

    Problem 1 调包失败 在这样的层级架构里调包 输出无法找到 mega nerf 直接用 sys path append 没有作用 import sys print sys path sys path append home pape
  • Java实现Excel文件生成和下载功能

    7 行代码优雅地实现 Excel 文件生成 下载功能 欢迎关注博主公众号 小哈学Java 专注于分享Java领域干货文章 关注回复 资源 免费领取全网最热的Java架构师学习PDF 转载请注明出处 https www exception s
  • stm32F103C8T6控制DHT11

    stm32F103C8T6控制DHT11串口打印 stm32F103C8T6控制DHT11串口打印学习经验总结 本人借鉴了许多大佬们的资料 这是个人学习的见解 如发现错误之处 麻烦指导指导 借鉴链接 https blog csdn net
  • 移动端-表头固定的表格组件

    UI原型 HTML代码 div class scroll table box div class scroll table head table class tb1 thead tr th style width 3em 序号 th th
  • Java二维码登录流程实现(包含短地址生成,含部分代码)

    近年来 二维码的使用越来越风生水起 笔者最近手头也遇到了一个需要使用二维码扫码登录网站的活 所以研究了一下这一套机制 并用代码实现了整个流程 接下来就和大家聊聊二维码登录及的那些事儿 二维码原理 二维码是微信搞起来的 当年微信扫码二维码登录
  • Web前端页面由哪三大层构成

    结构层为页面的骨架 由 HTML 或 XHTML 标记语言创建 用于搭建文档的结构 HTML 用来定义网页的内容 例如标题 正文 图像等 表示层为页面的样式 由 CSS 层叠样式表 负责创建 用于设置文档的呈现效果 CSS 用来控制网页的外
  • elk笔记10--filebeat使用

    elk笔记10 filebeat使用 1 filebeat 介绍 2 filebeat 使用案例 2 1 软件安装 2 2 采集数据到 kafka 2 3 采集数据到 es 3 使用技巧 3 1 filebeat 将日志按照类别发送到不同
  • git切换分支--Your local changes to the following files would be overwritten by checkout

    翻车现场 checkout切换分支报错 error Your Local changes to the following files would be overwritten by checkout xxxxxx某某文件 Please c
  • 联想笔记本e480恢复出厂设置_联想e480进入bios设置_thinkpade480进入bios的方法

    ThinkPad E480笔记本win10改win7如何修改BIOS设置 想要将预装win10系统换成win7系统 最重要的一步就是修改bios设置 这样才能在装机过程中不受到报错困扰 所以今天快启动小编为大家分享了详细图文教程 一起来看看
  • DICOM笔记-使用DCMTK读取DICOM文件保存DICOM文件

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