Quiet
  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我

bajiu

  • 主页
  • 归档
  • 分类
  • 标签
  • 链接
  • 关于我
Quiet主题
  • C#
  • VTK

使用 ActiViZ 实现vtkImageData的体积创建(一)

bajiu
.Net客户端

2024-02-27 18:14:00

什么是vtkImageData

vtkImageData是一个表示规则的、均匀间隔的三维网格的数据结构。它是VTK中用于图像处理和体积渲染的基础类,可以用来表示从简单的二维图像到复杂的三维数据集。vtkImageData通过定义每个网格点(或体素)上的标量或向量值来存储信息,使得它适用于各种科学计算和可视化应用。

关键概念和方法

维度、间距和原点

在使用vtkImageData之前,理解其三个基本属性——维度、间距和原点——是非常重要的。

  • 维度:指定了图像或体积数据在三个方向(x、y、z)上的大小。
  • 间距:每个体素在各个方向上的实际物理距离,允许数据在不同的比例尺上被表示。
  • 原点:数据集在空间中的起始点,通常是左下角的点。

Extent

Extent由六个整数组成,格式为 [xMin, xMax, yMin, yMax, zMin, zMax],这六个值分别定义了数据集在X、Y、Z轴方向上的最小和最大索引。这意味着Extent定义了一个立方体(或者对于二维数据是一个矩形),包含了数据集中所有的数据点。

vtkImageData imageData = vtkImageData.New();
imageData.SetExtent(0, 9, 0, 9, 0, 0); // 对于二维图像,Z轴的范围通常是0到0

这段代码创建了一个10x10像素的二维图像,其中Extent在X和Y方向上从0到9,Z方向上是0(因为它是二维的)。

虽然Extent定义了数据点的索引范围,但Spacing和Origin则定义了这些点在实际物理空间中的位置和间隔。

数据分配与填充(AllocateScalars)

AllocateScalars是vtkImageData中的一个关键方法,用于在内存中为图像数据分配空间。这个方法的调用是在图像数据对象的初始化阶段的一个重要步骤,因为它为后续的数据填充和图像处理操作建立了基础。

AllocateScalars的主要作用是为vtkImageData对象预留足够的内存空间,以存储图像的标量值或颜色信息。这一步骤确保了当我们向图像数据中填充值或对其进行操作时,有一个已经定义好大小和类型的数据结构等待我们的操作。

AllocateScalars方法通常接受两个参数:

  • 数据类型:指定存储图像标量值的数据类型。VTK提供了一系列的数据类型常量,如VTK_UNSIGNED_CHAR、VTK_FLOAT等,以支持不同的数值范围和精度要求。选择合适的数据类型对于图像数据的有效表示非常重要。

  • 组件数:指定每个数据点可以有多少个分量。对于灰度图像,每个像素点通常只有一个分量;而对于彩色图像,可能有三个分量(红、绿、蓝)或更多,如果包含了透明度信息,则可能有四个分量。

调用时机和上下文:

在创建vtkImageData对象并通过SetDimensions、SetSpacing和SetOrigin方法设置好基本属性后,应该调用AllocateScalars方法为该对象分配内存。这个步骤是在填充数据(例如,通过SetScalarComponentFromDouble方法设置每个体素的值)之前必须完成的。

// 初始化vtkImageData
var imageData = vtkImageData.New();
imageData.SetDimensions(50, 50, 50);
imageData.SetSpacing(1.0, 1.0, 1.0);
imageData.SetOrigin(0.0, 0.0, 0.0);
imageData.AllocateScalars(3, 1);

// 填充数据
for (int z = 0; z < 50; z++)
{
    for (int y = 0; y < 50; y++)
    {
        for (int x = 0; x < 50; x++)
        {
            var value = (x * y * z) % 255;
            imageData.SetScalarComponentFromDouble(x, y, z, 0, value);
        }
    }
}

举个栗子:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // 获取 RenderWindowControl 的渲染器
    var renderer = renderWindowControl.RenderWindow.GetRenderers().GetFirstRenderer();

    renderer.RemoveAllViewProps();
    //var renderWindowInteractor = vtkRenderWindowInteractor.New();
    //renderWindowInteractor.SetRenderWindow(renderWindow);

    // 创建vtkImageData
    var imageData = vtkImageData.New();
    imageData.SetDimensions(50, 50, 50);
    imageData.SetSpacing(1.0, 1.0, 1.0);
    imageData.SetOrigin(0.0, 0.0, 0.0);
    imageData.AllocateScalars(3, 3);

    // 填充数据
    int[] dims = imageData.GetDimensions();
    for (int z = 0; z < dims[2]; z++)
    {
        for (int y = 0; y < dims[1]; y++)
        {
            for (int x = 0; x < dims[0]; x++)
            {
                byte[] pixel = { (byte)(x * 5), (byte)(y * 5), (byte)(z * 5) };
                IntPtr ptr = imageData.GetScalarPointer(x, y, z);
                System.Runtime.InteropServices.Marshal.Copy(pixel, 0, ptr, 3);
            }
        }
    }
    // 创建映射器和Actor来渲染图像数据
    var mapper = vtkDataSetMapper.New();
    mapper.SetInputData(imageData);

    // 创建Actor
    var actor = vtkActor.New();
    actor.SetMapper(mapper);

    // 将Actor添加到Renderer中
    renderer.AddActor(actor);
    renderer.SetBackground(0.1, 0.2, 0.4); // 设置背景颜色
}

出来的图就是酱紫的:

vtkImageData_1

System.Runtime.InteropServices.Marshal.Copy方法是.NET Framework中一个非常重要的函数,用于在托管代码和非托管代码之间复制数据。这个方法属于System.Runtime.InteropServices命名空间,主要用于高级内存操作,尤其是在需要直接与操作系统的内存进行交互时。Marshal.Copy提供了一种安全的方式来复制字节数据,避免了直接使用指针可能引发的安全问题和稳定性问题。

Marshal.Copy有几个重载版本,允许从托管数组到非托管内存、从非托管内存到托管数组的数据复制,以及在托管数组之间复制数据。以下是几个常见重载版本的简介:

1.从托管数组到非托管内存:

public static void Copy(byte[] source, int startIndex, IntPtr destination, int length);
public static void Copy(char[] source, int startIndex, IntPtr destination, int length);
public static void Copy(short[] source, int startIndex, IntPtr destination, int length);
public static void Copy(int[] source, int startIndex, IntPtr destination, int length);

这些重载允许将不同类型的托管数组(如byte[], char[], short[], **int[]**等)从指定的起始索引开始,复制指定长度的数据到非托管内存的指定位置。

2.从非托管内存到托管数组:

public static void Copy(IntPtr source, byte[] destination, int startIndex, int length);
public static void Copy(IntPtr source, char[] destination, int startIndex, int length);
public static void Copy(IntPtr source, short[] destination, int startIndex, int length);
public static void Copy(IntPtr source, int[] destination, int startIndex, int length);

这些重载允许从非托管内存的指定位置开始,复制指定长度的数据到托管数组的指定起始索引位置。

例如,当你使用VTK处理图像数据,并需要将颜色值直接填充到图像的像素中时,Marshal.Copy可以用来将包含颜色值的托管数组(如RGB值)安全地复制到VTK图像数据结构的内存位置。这样的操作既高效又安全,是.NET中处理高级内存操作的首选方法。

支持输入/输出类型为vtkImageData的读写器类

类名 功能
vtkBMPReader 读BMP图像
vtkBMPWriter 写BMP图像
vtkJPEGReader 读JPEG图像
vtkJPEGWriter 写JPEG图像
vtkPNGReader 读PNG图像
vtkPNGWriter 写PNG图像
vtkTIFFReader 读TIFF图像
vtkTIFFWriter 写TIFF图像
vtkMetaImageReader 读MHA|MHD图像
vtkMetaImageWriter 写MHA|MHD图像
vtkDicomImageReader 读DICOM图像
vtkXMLImageDataReader 读基于XML文件格式图像
vtkXMLImageDataWriter 写基于XML文件格式图像
vtkImageReader 读RAW格式图像
vtkImageWriter 写RAW格式图像
上一篇

使用 ActiViZ 实现基本图形处理(二)

下一篇

NodeJs定时任务node-schedule模块的使用

©2024 By bajiu.