ITK-读取Raw原始图像数据

作者:翟天保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 等格式

使用说明

  1. 需安装ITK库及相关依赖。
  2. 将ITK官方项目中Examples\Data中的图像文件(文件名字与例程中名字一致),复制到你项目的路径下,并更改代码。
  3. 编译时需链接 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图像:

  

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

翟天保Steven

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

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

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

打赏作者

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

抵扣说明:

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

余额充值