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

记录之前写过一个读取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;
	}
};
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
使用 DCMTK读取 DICOM 文件需要进行以下步骤: 1. 引入 DCMTK 库 在 QT 项目中引入 DCMTK 库,可以通过在项目文件中添加以下代码引入静态库: ``` LIBS += -L/path/to/dcmtk/lib -ldcmdata -loflog -lofstd -li2d -ldcmimage -lzlib -lpng -ltiff ``` 注意:需要将 /path/to/dcmtk/lib 替换为 DCMTK 库的安装路径。 2. 初始化 DCMTK 库 在 QT 代码中,需要先初始化 DCMTK 库,可以在 main 函数中添加以下代码: ``` #include <dcmtk/config/osconfig.h> #include <dcmtk/dcmdata/dctk.h> int main(int argc, char *argv[]) { // 初始化 DCMTK 库 DcmInitialize(argc, argv); ... } ``` 3. 读取 DICOM 文件 使用 DCMTK读取 DICOM 文件可以使用 DcmFileFormat 类,以下是一个示例代码: ``` #include <dcmtk/config/osconfig.h> #include <dcmtk/dcmdata/dctk.h> void readDICOM(const char* filename) { // 创建 DcmFileFormat 对象 DcmFileFormat fileformat; // 读取 DICOM 文件到 DcmFileFormat 对象中 OFCondition status = fileformat.loadFile(filename); if (!status.good()) { qDebug() << "Failed to read DICOM file"; return; } // 获取 DICOM 数据集 DcmDataset* dataset = fileformat.getDataset(); // 获取 DICOM 图像数据 Uint16 *pixelData; dataset->findAndGetUint16Array(DCM_PixelData, pixelData); // 获取 DICOM 图像大小 Uint16 rows, cols; dataset->findAndGetUint16(DCM_Rows, rows); dataset->findAndGetUint16(DCM_Columns, cols); } ``` 上述代码中,首先创建 DcmFileFormat 对象,然后使用 loadFile() 方法读取 DICOM 文件,如果读取成功,则可以使用 getDataset() 方法获取 DICOM 数据集,使用 findAndGetUint16Array() 方法获取图像数据,使用 findAndGetUint16() 方法获取图像大小。 注意:DCMTK使用 C++98 标准,因此需要在 QT 项目中添加 -std=c++98 编译选项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黑山老妖的笔记本

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值