作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
前言
Raw 格式是医学影像和科学计算中常见的原始数据格式,它仅包含像素值信息,不附带任何元数据(如尺寸、位深、间距等)。相比 DICOM、NIfTI 等标准格式,Raw 文件体积更小,但读取时需要手动配置所有参数。本文基于 ITK(Insight Segmentation and Registration Toolkit)库,详细讲解如何正确读取 Raw 文件并转换为 PNG 等可视化格式,附完整可运行代码和常见问题解决方案。
环境准备
参见:Windows下用CMake编译ITK及配置测试_itk配置-CSDN博客
Raw 文件的特点与读取难点
Raw 文件本质上是像素数据的二进制流,没有头部信息,这意味着:
- 必须已知图像的尺寸(宽、高、深度)才能正确解析;
- 需要明确像素类型(如 8 位无符号、16 位有符号);
- 需指定字节顺序(大端 / 小端);
- 缺乏空间信息(如像素间距),需手动设置。
读取 Raw 文件的核心挑战在于参数匹配—— 任何一个参数错误都会导致图像错乱(如拉伸、重叠、颜色异常)。
ITK 读取 Raw 文件的核心组件
ITK 提供了itk::RawImageIO
类专门处理 Raw 格式,配合图像读取器(ImageFileReader
)和过滤器,可完成从 Raw 到标准图像格式的转换。核心组件包括:
组件类 | 作用 |
---|---|
RawImageIO | 解析 Raw 二进制数据,需手动配置尺寸、像素类型等 |
ImageFileReader | 读取文件流,结合RawImageIO 生成 ITK 图像对象 |
RescaleIntensityImageFilter | 调整像素值范围(如将 0-4095 映射到 0-255),确保正确显示 |
ImageFileWriter | 将 ITK 图像写入为 PNG 等格式 |
使用说明
- 需安装ITK库及相关依赖。
- 将ITK官方项目中Examples\Data中的图像文件(文件名字与例程中名字一致),复制到你项目的路径下,并更改代码。
- 编译时需链接 ITK的库文件。
完整代码
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem> // 需要开启C++17
#include <itkImageFileWriter.h>
#include <itkImageFileReader.h>
#include <itkImage.h>
#include <itkRawImageIO.h>
#include <itkPNGImageIO.h>
#include <itkRescaleIntensityImageFilter.h>
namespace fs = std::filesystem;
int main()
{
try
{
// 定义图像维度和类型
constexpr unsigned int Dimension = 2;
using InputPixelType = unsigned char;
using InputImageType = itk::Image<InputPixelType, Dimension>;
using OutputPixelType = unsigned char;
using OutputImageType = itk::Image<OutputPixelType, Dimension>;
// 输入和输出文件路径
std::string inputFileName = "ExampleData/BrainProtonDensitySliceBorder20.raw";
std::string outputFileName = "Output/rawTest.png";
// 检查输入文件是否存在并可访问
std::cout << "检查输入文件: " << inputFileName << std::endl;
if (!fs::exists(inputFileName))
{
std::cerr << "错误: 文件不存在: " << inputFileName << std::endl;
return EXIT_FAILURE;
}
if (!fs::is_regular_file(inputFileName))
{
std::cerr << "错误: 不是有效的文件: " << inputFileName << std::endl;
return EXIT_FAILURE;
}
// 获取文件大小
size_t fileSize = fs::file_size(inputFileName);
std::cout << "文件大小: " << fileSize << " 字节" << std::endl;
// 检查输出目录是否存在,不存在则创建
fs::path outputDir = fs::path(outputFileName).parent_path();
if (!fs::exists(outputDir))
{
std::cout << "创建输出目录: " << outputDir << std::endl;
if (!fs::create_directories(outputDir))
{
std::cerr << "错误: 无法创建输出目录: " << outputDir << std::endl;
return EXIT_FAILURE;
}
}
// 创建RawImageIO对象
using RawImageIOType = itk::RawImageIO<InputPixelType, Dimension>;
RawImageIOType::Pointer rawImageIO = RawImageIOType::New();
// 设置图像尺寸
using SizeType = itk::Size<Dimension>;
SizeType size;
size[0] = 221;
size[1] = 257;
rawImageIO->SetDimensions(0, size[0]);
rawImageIO->SetDimensions(1, size[1]);
// 计算预期文件大小
size_t expectedSize = size[0] * size[1] * sizeof(InputPixelType);
std::cout << "预期文件大小: " << expectedSize << " 字节" << std::endl;
if (fileSize != expectedSize)
{
std::cerr << "警告: 文件大小不匹配!" << std::endl;
std::cerr << " 预期: " << expectedSize << " 字节" << std::endl;
std::cerr << " 实际: " << fileSize << " 字节" << std::endl;
std::cerr << "这可能会导致读取错误!" << std::endl;
}
// 设置像素间距和原点
rawImageIO->SetSpacing(0, 1.0);
rawImageIO->SetSpacing(1, 1.0);
rawImageIO->SetOrigin(0, 0.0);
rawImageIO->SetOrigin(1, 0.0);
// 设置像素类型和字节顺序
rawImageIO->SetPixelType(itk::ImageIOBase::SCALAR);
rawImageIO->SetComponentType(itk::ImageIOBase::IOComponentEnum::UCHAR);
rawImageIO->SetByteOrderToLittleEndian();
// 创建读取器并启用调试
using ReaderType = itk::ImageFileReader<InputImageType>;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(inputFileName);
reader->SetImageIO(rawImageIO);
// 创建强度重缩放过滤器
using RescaleFilterType = itk::RescaleIntensityImageFilter<InputImageType, OutputImageType>;
RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New();
rescaleFilter->SetInput(reader->GetOutput());
rescaleFilter->SetOutputMinimum(0);
rescaleFilter->SetOutputMaximum(255);
// 创建PNG写入器
using WriterType = itk::ImageFileWriter<OutputImageType>;
WriterType::Pointer writer = WriterType::New();
writer->SetFileName(outputFileName);
writer->SetInput(rescaleFilter->GetOutput());
writer->DebugOn();
// 设置PNG图像IO
using PNGImageIOType = itk::PNGImageIO;
PNGImageIOType::Pointer pngIO = PNGImageIOType::New();
writer->SetImageIO(pngIO);
// 执行读取和写入操作
std::cout << "开始处理..." << std::endl;
writer->Update();
std::cout << "成功将raw文件转换为: " << outputFileName << std::endl;
return EXIT_SUCCESS;
}
catch (itk::ExceptionObject& error)
{
std::cerr << "ITK错误: " << error << std::endl;
return EXIT_FAILURE;
}
catch (std::exception& error)
{
std::cerr << "标准库错误: " << error.what() << std::endl;
return EXIT_FAILURE;
}
catch (...)
{
std::cerr << "未知错误" << std::endl;
return EXIT_FAILURE;
}
}
测试效果
调试台输出结果如下。
将BrainProtonDensitySliceBorder20.raw文件以ImageJ打开,配置如下:
读取图像:
对比基于ITK读取Raw数据后生成的PNG图像:
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!