如何用DCMTK读取3D Dicom并转OpenCV Mat?getOutputData读取切片方法
使用DCMTK读取3D DICOM并转换为OpenCV Mat对象
我来分享一下我实际项目中用过的解决方案,完美适配你这种[512,512,5]的16位无符号3D DICOM转OpenCV Mat的场景,核心是正确利用DCMTK的getOutputData方法读取所有切片,并保证数据类型和维度的一致性。
核心步骤与代码实现
首先确保你已经正确链接DCMTK的相关库(比如dcmimgle、dcmdata),然后看完整的代码:
#include <dcmtk/dcmimgle/dcmimage.h> #include <opencv2/opencv.hpp> #include <iostream> cv::Mat convertDicom3DToOpenCVMat(const std::string& dicomPath) { // 1. 加载DICOM文件/数据集 DcmFileFormat fileFormat; OFCondition loadStatus = fileFormat.loadFile(dicomPath.c_str()); if (!loadStatus.good()) { std::cerr << "加载DICOM失败: " << loadStatus.text() << std::endl; return cv::Mat(); } // 2. 初始化DicomImage对象,指定输出格式匹配原数据(16位无符号) DicomImage* dicomImage = new DicomImage( fileFormat.getDataset(), EXS_UndefinedPixelData, // 自动匹配原始像素编码 0, // 起始帧 0 // 读取所有帧 ); if (!dicomImage || dicomImage->getStatus() != EIS_Normal) { std::cerr << "创建DicomImage失败: " << DicomImage::getString(dicomImage->getStatus()) << std::endl; delete dicomImage; return cv::Mat(); } // 3. 获取图像维度信息 int rows = dicomImage->getHeight(); // 512 int cols = dicomImage->getWidth(); // 512 int sliceCount = dicomImage->getFrameCount(); // 5 // 校验维度是否符合预期 if (rows != 512 || cols != 512 || sliceCount != 5) { std::cerr << "DICOM维度与预期不符: 实际[" << rows << "," << cols << "," << sliceCount << "]" << std::endl; delete dicomImage; return cv::Mat(); } // 4. 创建OpenCV 3D Mat,数据类型为CV_16U(和原DICOM一致) // 这里先创建[slices, rows, cols]结构,后续可按需调整维度顺序 cv::Mat mat3D(sliceCount, rows, cols, CV_16U); // 5. 遍历每个切片,用getOutputData读取数据 for (int sliceIdx = 0; sliceIdx < sliceCount; ++sliceIdx) { // 切换到当前要读取的切片(帧) dicomImage->setCurrentFrame(sliceIdx); // 读取当前切片的16位无符号数据 // 第一个参数16:指定输出位深度为16位 // 第二个参数0:指定读取当前帧的第0个平面(单通道DICOM用0即可) const Uint16* sliceData = static_cast<const Uint16*>(dicomImage->getOutputData(16, 0)); if (!sliceData) { std::cerr << "读取第" << sliceIdx << "个切片数据失败" << std::endl; delete dicomImage; return cv::Mat(); } // 将切片数据复制到Mat对应层 cv::Mat singleSlice(rows, cols, CV_16U, const_cast<Uint16*>(sliceData)); singleSlice.copyTo(mat3D.row(sliceIdx)); } // 6. 按需调整Mat维度顺序(如果需要[rows, cols, slices]的结构) // mat3D = mat3D.reshape(1, rows).t(); // 释放资源 delete dicomImage; return mat3D; }
关键细节解释
getOutputData的正确用法:- 第一个参数传入
16,明确指定输出数据的位深度为16位,和你的DICOM原始数据类型(16位无符号)匹配,避免自动转换导致的数据失真。 - 第二个参数传入
0,对于单通道的3D DICOM,这个参数表示读取当前帧的第0个像素平面;如果是多通道DICOM,需要根据通道数调整。 - 必须先调用
setCurrentFrame(sliceIdx)切换到目标切片,再调用getOutputData才能获取对应切片的数据。
- 第一个参数传入
维度与数据类型一致性:
- OpenCV的
CV_16U类型完全匹配DICOM的16位无符号整数,确保数据没有截断或转换错误。 - 代码中默认创建的Mat是
[切片数, 行数, 列数]的结构,如果你的后续处理需要[行数, 列数, 切片数]的顺序,可以用注释里的reshape+转置代码调整。
- OpenCV的
额外注意事项
- 如果你的3D DICOM是由多个独立的切片文件组成(而非单文件多帧),需要先用DCMTK的
DcmDirectory遍历目录,按切片位置排序后逐个读取每个文件的像素数据,再组装成3D Mat。 - 编译时要确保DCMTK的头文件路径和库路径正确配置,链接必要的库文件(比如
dcmimgle.lib、dcmdata.lib等,Windows平台)或.so文件(Linux平台)。
内容的提问来源于stack exchange,提问作者rvimieiro
相关产品推荐
相关产品推荐

