ITK-读取DICOM序列标签
作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
前言
在医学影像处理中,DICOM序列文件(如CT、MRI扫描的多切片数据)是存储3D医学影像的标准格式。这些序列文件的头信息中包含患者demographics、扫描参数、设备信息等关键信息,对于影像分析、数据归档和临床研究至关重要。Insight Segmentation and Registration Toolkit(ITK)提供了强大的 API,支持高效读取 DICOM 序列并解析其元数据标签。
本文将通过实例代码,详细介绍如何使用ITK读取DICOM序列文件、提取标签信息,并查询特定标签信息(如患者姓名),为医学影像开发者提供从文件读取到数据解析的完整技术方案。
环境准备
参见:Windows下用CMake编译ITK及配置测试_itk配置-CSDN博客
功能说明
本程序基于 ITK 实现以下核心功能:
- DICOM序列读取:自动识别目录中的DICOM序列文件,按顺序读取3D影像数据。
- 元数据全量解析:遍历DICOM头文件中的所有元数据标签,输出标签键值对。
- 特定标签查询:通过DICOM标签号(如
0010|0010
)精准提取患者姓名等关键信息。 - 异常处理:捕获读取过程中可能出现的格式错误或文件异常。
使用说明
- 需安装ITK库及相关依赖。
- 将
test data/1/Images
替换为你的DICOM序列所在路径。 - 编译时需链接 ITK的库文件。
完整代码
#include "itkImageSeriesReader.h"
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h" int main()
{// --------------------------- 定义基本类型 ---------------------------// 定义图像维度为3D(x,y,z)constexpr unsigned int Dimension = 3;// 定义像素类型为short(适用于医学影像常见的灰度值范围)using PixelType = short;// 定义ITK图像类型,结合像素类型和维度using ImageType = itk::Image<PixelType, Dimension>;// --------------------------- 创建图像读取器 ---------------------------// 定义图像序列读取器类型using ReaderType = itk::ImageSeriesReader<ImageType>;// 新建读取器实例(使用智能指针管理内存)auto reader = ReaderType::New();// --------------------------- 配置DICOM图像IO ---------------------------// 定义GDCM图像IO类型(GDCM是处理DICOM的开源库)using ImageIOType = itk::GDCMImageIO;// 新建GDCM图像IO实例auto dicomIO = ImageIOType::New();// 将图像IO设置到读取器中,以便处理DICOM格式reader->SetImageIO(dicomIO);// --------------------------- 生成DICOM文件列表 ---------------------------// 定义DICOM系列文件名生成器类型using NamesGeneratorType = itk::GDCMSeriesFileNames;// 新建文件名生成器实例auto nameGenerator = NamesGeneratorType::New();// 设置输入目录,包含DICOM系列文件nameGenerator->SetInputDirectory("test data/1/Images");// 定义文件名容器类型(字符串向量)using FileNamesContainer = std::vector<std::string>;// 获取目录中的DICOM文件列表FileNamesContainer fileNames = nameGenerator->GetInputFileNames();// 将文件列表设置到读取器中reader->SetFileNames(fileNames);// --------------------------- 执行图像读取操作 ---------------------------try{// 触发读取操作并更新图像数据reader->Update();}catch (const itk::ExceptionObject& ex){// 捕获并输出可能的ITK异常信息std::cout << ex << std::endl;return EXIT_FAILURE;}// --------------------------- 提取DICOM元数据 ---------------------------// 定义元数据字典类型using DictionaryType = itk::MetaDataDictionary;// 从图像IO中获取元数据字典(包含DICOM头信息)const DictionaryType& dictionary = dicomIO->GetMetaDataDictionary();// 定义元数据字符串类型(用于存储字符串类型的元数据)using MetaDataStringType = itk::MetaDataObject<std::string>;// 获取字典迭代器起始位置auto itr = dictionary.Begin();// 获取字典迭代器结束位置auto end = dictionary.End();// 遍历元数据字典并输出所有元数据std::cout << "===== 所有DICOM元数据 =====" << std::endl;while (itr != end){// 获取当前元数据项itk::MetaDataObjectBase::Pointer entry = itr->second;// 尝试将元数据项转换为字符串类型(DICOM标签通常存储为字符串)MetaDataStringType::Pointer entryvalue = dynamic_cast<MetaDataStringType*>(entry.GetPointer());if (entryvalue){// 获取标签键(格式为组号|元素号,如0010|0010)std::string tagkey = itr->first;// 获取标签对应的值std::string tagvalue = entryvalue->GetMetaDataObjectValue();// 输出标签键值对std::cout << tagkey << " = " << tagvalue << std::endl;}// 移动到下一个元数据项++itr;}// --------------------------- 查找特定DICOM标签 ---------------------------// 定义要查找的标签(患者姓名,DICOM标签0010|0010)std::string entryId = "0010|0010";// 在元数据字典中查找指定标签auto tagItr = dictionary.Find(entryId);if (tagItr == end){// 如果未找到标签,输出错误信息std::cerr << "Tag " << entryId;std::cerr << " not found in the DICOM header" << std::endl;return EXIT_FAILURE;}// 尝试将找到的元数据项转换为字符串类型(常量版本)MetaDataStringType::ConstPointer entryvalue = dynamic_cast<const MetaDataStringType*>(tagItr->second.GetPointer());if (entryvalue){// 获取标签对应的值std::string tagvalue = entryvalue->GetMetaDataObjectValue();// 输出患者姓名信息std::cout << "\n===== 特定DICOM标签信息 =====" << std::endl;std::cout << "Patient's Name (" << entryId << ") ";std::cout << " is: " << tagvalue << std::endl;}else{// 如果元数据项不是字符串类型,输出错误信息std::cerr << "Entry was not of string type" << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}
测试效果
输出:格式为ID-数值
对比:
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!