跳转到内容

MINC/软件开发/MINC1-程序员指南

来自维基教科书,开放世界中的开放书籍


MINC 文件格式(医学图像 NetCDF)基于由 Unidata 项目中心发布的 NetCDF 文件格式(网络通用数据格式)。NetCDF 提供了一个软件接口,用于以机器无关的方式将命名、多维变量存储在文件中。此接口使应用程序免于担心可移植性和文件结构,并鼓励使用自描述形式的数据。

文件中每个 NetCDF 多维变量都由名称、维度和属性描述。例如,存储在文件中的图像可能以字节数据的形式存储在一个名为"image"的变量中,具有"x""y"维度(每个长度为 256),以及一个名为"long_name"的属性,该属性是一个字符串,用于描述图像的内容。一个文件中可以存储多个变量,每个变量可以有多个属性。维度独立于变量存在,并且可以对多个变量进行下标。

MINC 在 NetCDF 接口之上提供了三件事。它提供了一套适用于医学成像的维度、变量和属性名称标准,还提供了一些补充 NetCDF 接口的便利函数(不特定于 MINC 约定),以及一些用于使用 MINC 文件的便利函数。

NetCDF 简介

[编辑 | 编辑源代码]

(有关完整描述,请参阅NetCDF 用户指南)。

NetCDF 文件

[编辑 | 编辑源代码]

在考虑 NetCDF 接口时,查看示例文件很有用。幸运的是,NetCDF 包提供了实用程序(ncdump 和 ncgen)用于将二进制 NetCDF 文件转换为名为 CDL 的 ASCII 格式。下面给出了一个简单的 NetCDF 文件,由 ncdump 转换为 CDL 表示形式。

  netcdf test {
  dimensions:
  	ycoord = 3 ;
  	xcoord = 4 ;
  
  variables:
  	double image(ycoord, xcoord) ;
  		image:long_name = "My favorite tiny image" ;
  	double xcoord(xcoord) ;
  
  data:
  
   image =
    1, 2, 3, 4,
    5, 6, 7, 8,
    9, 10, 11, 12 ;
  
   xcoord = 100, 200, 300, 400 ;
  }

示例文件存储了一个 3 x 4 的双精度值图像。首先定义的是维度:xcoordycoord。维度可以代表物理维度,例如 x 坐标、y 坐标等,也可以代表抽象事物,例如查找表索引。每个维度都有一个名称和一个长度,当与其他维度组合时,它们会定义变量的形状——变量 image 由ycoordxcoord 下标。

维度也可以在变量之间使用,将它们关联起来。例如,如果文件中包含另一个也由ycoordxcoord 下标的图像,我们就会得到重要的信息,即这两个变量是在同一个网格上采样的。此外,可以创建与维度名称相同的变量来定义坐标系,例如上面的示例中的xcoord,它给出了图像中每个点的 x 坐标。

变量是在 cdl 文件中定义的下一项。每个变量都有一个名称、数据类型和由维度列表(每个变量最多有MAX_VAR_DIMS = 32 个维度)指定的形状。数据类型为NC_CHAR、NC_BYTE、NC_SHORT、NC_INT、NC_FLOATNC_DOUBLE每个变量的信息存储在属性中。属性 "long_name" 给出了描述变量 "image" 的字符字符串。属性可以是标量或上面列出的六种类型之一的向量(字符字符串是类型为NC_CHAR的向量)。

使用 NetCDF 进行编程

[编辑 | 编辑源代码]

使用 NetCDF 进行编程非常简单。上面的文件是由以下程序生成的

  #include <netcdf.h>
  
  #define THE_NAME "My favorite tiny image"
  static double vals[][4]={
     1.0, 2.0, 3.0, 4.0,
     5.0, 6.0, 7.0, 8.0,
     9.0,10.0,11.0,12.0
  };
  static int ysize=sizeof(vals)/sizeof(vals[0]);
  static int xsize=sizeof(vals[0])/sizeof(vals[0][0]);
  
  static double xcoord[]={100.,200.,300.,400.};
  
  main()
  {
     int cdf, img, xvar;
     int dim[MAX_VAR_DIMS];
     long count[MAX_VAR_DIMS], start[MAX_VAR_DIMS];
  
     /* Create the file */
     cdf=nccreate("test.cdf",NC_CLOBBER);
  
     /* Define the dimensions */
     dim[0]=ncdimdef(cdf, "ycoord", ysize);
     dim[1]=ncdimdef(cdf, "xcoord", xsize);
  
     /* Define the variables */
     img=ncvardef(cdf, "image", NC_DOUBLE, 2, dim);
     xvar=ncvardef(cdf,"xcoord", NC_DOUBLE, 1, &amp;dim[1]);
  
     /* Add an attribute */
     ncattput(cdf, img, "long_name", NC_CHAR, strlen(THE_NAME)+1, THE_NAME);
  
     /* End definition mode */
     ncendef(cdf);
  
     /* Write the variable values */
     start[0]=start[1]=0;
     count[0]=ysize; count[1]=xsize;
     ncvarput(cdf, img, start, count, vals);
     ncvarput(cdf, xvar, &amp;start[1], &amp;count[1], xcoord);
     ncclose(cdf);
  }

程序的第一行可执行语句创建了一个新的 NetCDF 文件。打开的文件要么处于“定义”模式,要么处于“数据”模式。在定义模式下,可以定义维度、变量和属性,但不能写入或读取变量数据。在数据模式下,可以写入或读取变量值,但不能对维度或变量进行更改,并且只有在属性已经存在的情况下才能写入属性,并且不会随着写入而变得更大。新创建的文件会自动处于定义模式。

调用 nccreate 后的行定义了文件中的维度和变量。请注意,NetCDF 文件、维度和变量都由创建它们时返回的整数标识。这些 ID 随后用于引用每个对象。图像变量的属性 "long_name" 仅由其名称标识。

一旦所有内容都定义好,ncendef 就会将文件置于数据模式,然后写入值。要写入的值由一个起始索引向量和一个每个维度中要写入的值数量向量定义。这在变量中定义了一个称为超片的、多维的矩形。在 C 接口中,向量的第一个元素引用变量变化最慢的索引,因此在此示例中,数组 vals 的xcoord 变化最快。在 FORTRAN 接口中,约定是第一个下标变化最快。这些约定遵循语言对多维数组的约定。

MINC 格式

[编辑 | 编辑源代码]

可以使用 NetCDF 函数调用来构建 MINC 格式文件,但提供了一个 C 包含文件以促进(并帮助确保)遵守标准。此文件定义了有用的常量、标准维度、变量和属性名称以及一些属性值。它还声明了在 MINC 便利函数库中定义的函数,这些函数旨在简化应用程序程序员的工作。

MINC 标准

[编辑 | 编辑源代码]

人们提出了各种关于文件格式的要求。其中一个列表如下:协议应该:1)简单,2)自描述,3)可维护,4)可扩展,5)N维,以及 6)拥有通用数据结构。NetCDF 格式满足所有这些要求,表明它是一个良好的起点。然而,我会在列表中添加一些额外的要求。以上列表中隐含着需要一个用于访问数据的标准(如何获取患者姓名)--- 这并非 NetCDF 提供的。此外,一个有用的格式应该附带一个软件接口,使其易于使用,特别是在开发环境中。最后,一个存储许多关联信息片段的格式也应该提供一些数据组织。

MINC 格式试图将这些内容添加到 NetCDF 格式中。

MINC 变量类型

[edit | edit source]

医学影像通常会生成包含大量辅助数据的文件(患者信息、图像信息、采集信息等)。为了以有用的方式组织这些信息,MINC 使用变量将相关的属性分组在一起。变量本身可能包含或不包含有用的数据。例如,变量MIimage 包含图像数据,并具有与该数据相关的属性。变量MIpatient 没有相关的变量数据,但用于将描述患者的所有属性(姓名、出生日期等)分组在一起。这种类型的变量称为分组变量。

与维度相对应的变量称为维度变量,用于描述与维度相对应的坐标系。例如 MIxspace --- 既是维度,也是描述其他变量的 x 坐标的变量。

NetCDF 约定允许这些维度变量指定每个点的坐标,但没有描述该点处样本的宽度。MINC 提供了维度宽度变量的约定,例如MIxspace_width,用于提供此信息。

最后,可以拥有在变量的某些维度上变化的属性。例如,如果我们有一个图像数据的体积,在MIxspaceMIyspaceMIzspace 上变化,我们可能想要一个属性来提供每个图像的最大值,在MIzspace 上变化。为了实现这一点,我们使用一个变量,称为变量属性,由图像变量的属性指向。

因此,MINC 引入了几种类型的变量:分组变量、维度变量、维度宽度变量和变量属性。

数据组织

[edit | edit source]

MINC 试图通过分组变量的层次结构来提供一定程度的数据组织。如上所述,属性根据类型在分组变量中进行分组。每个分组变量都可以具有一个MIparent 和一个MIchildren 属性 --- 前者指定层次结构中位于该变量之上的另一个变量的名称,后者指定层次结构中位于该变量之下的变量的换行符分隔列表。层次结构的根是MIrootvariable 变量,没有父级。虽然不需要使用这种结构,但它可以提供一种机制来组织大量信息。

MINC 维度变量和属性名称

[edit | edit source]

NetCDF 格式对变量和维度命名约定没有说明,对属性名称也没有说明太多。它确实提供了一些标准,例如用于描述变量的属性“long_name”,这些标准已被 MINC 标准采用。MINC 定义了一组用于常用实体的标准名称,包含文件定义了指定这些名称的常量。这些在 MINC 参考手册中有详细说明。其中最有趣的是MIimage,这是用于在文件中存储实际图像数据的变量的名称。

图像维度

[edit | edit source]

MINC 标准赋予了图像概念一些特殊的地位。NetCDF 本身并没有任何内容表明特定维度具有任何特殊地位,但在成像上下文中,对哪些维度可以变化以及如何变化进行限制可能很方便。例如,要求指定如何重新缩放图像的变量(参见后面关于像素值的章节)不随图像维度变化,这意味着我们可以将图像视为一个简单的单元。在最简单的情况下,图像维度只是MIimage 变量的两个变化最快的维度。

允许向量场 --- 在每个点上具有向量值的图像或图像体积 --- 也很有帮助。RGB 图像是一个向量场的简单示例。在空间中的每个点,都有三个值:红色、绿色和蓝色。维度MIvector_dimension 用于向量的分量,它应该是MIimage 变量中变化最快的维度。如果它存在,那么MIimage 的三个变化最快的维度就是图像维度。

MINC 坐标系

[edit | edit source]

MINC 标准定义了空间坐标如何相对于患者进行定向。文件可以自由地以所需的方向存储数据,但在医学影像上下文中,正的世界坐标具有明确的含义。标准是正 x 轴从患者的左指向右,正 y 轴从后指向前,正 z 轴从下指向上。

元素索引到世界坐标的转换是使用维度变量属性MIdirection_cosinesMIstepMIstart 完成的。如果方向余弦为c=(c_x, c_y, c_z),则沿轴的相邻元素之间的向量为 step x c。如果 start(i) 和c(i) 是维度 i 的MIstartMIdirection_cosines 属性(MIxspaceMIyspaceMIzspace 之一),则图像变量的第一个元素位于世界坐标 \sum_i start(i) c(i) 处。

如果方向余弦不存在,则假设它们对于MIxspace 为 (1,0,0),对于MIyspace 为 (0,1,0),对于MIzspace 为 (0,0,1)。方向余弦是单位向量,应该归一化。同样,step 属性应该携带有关轴翻转(负或正)的信息,而不是方向余弦。

像素值和真实值

[edit | edit source]

在医学影像中,像素值通常存储为字节或短整型,但通常与每个像素也关联一个真实值。该真实值是通过与每个图像或图像体积相关的比例因子和偏移获得的。MINC 标准指示了如何解释像素值。

MIimage 变量中的图像数据可以存储为字节、短整型、整型(32 位)、浮点型或双精度型。NetCDF 约定使用属性MIvalid_rangeMIvalid_maxMIvalid_min 来指示变量中可以找到的值范围。例如,对于短整型值,我们可能有一个有效的范围为 0 到 32000。为了将这些整数转换为真实值,我们可以使用比例因子和偏移。但是,如果数据转换为范围为 23 到 228 的字节,则这些值必须更改。

如果我们指定一个图像最大值和最小值,MIvalid_maxMIvalid_min 应该通过适当的比例因子和偏移映射到该值,那么我们就可以转换类型和有效范围,而无需更改真实最大值和最小值。为了允许图像中的最大动态范围,我们使用变量MIimagemaxMIimagemin 来存储真实最大值和最小值 --- 这些值可以在MIimage 的任何非图像维度上变化。

通用便利函数

[edit | edit source]

MINC 提供了许多便利函数,这些函数与医学影像无关,但使 NetCDF 文件的使用更加容易。NetCDF 格式的一个缺点是数据可以以任何形式(字节、短整型、整型、浮点型、双精度型)出现,并且调用程序必须处理一般情况。与其限制这一点,MINC 提供了用于转换类型的函数。

第一组便利函数用于类型转换。

  • miattget - 读取属性向量,指定所需的数字类型和要读取的最大值数量。
  • miattget1 - 读取一个指定类型的属性值。
  • miattgetstr - 读取指定最大长度的字符属性。
  • miattputdbl - 写入一个双精度属性。
  • miattputstr - 写入一个字符串属性。
  • mivarget - 获取指定类型的超立方体值。
  • mivarget1 - 获取一个指定类型的单个值。
  • mivarput - 放置一个指定类型的超立方体值。
  • mivarput1 - 放置指定类型的一个单一值。

接下来我们有一些处理坐标向量的函数。

  • miset_coords - 将坐标向量设置为一个单一值。
  • mitranslate_coords - 将一个变量的坐标转换为另一个变量的索引向量。

最后,有一些函数可以将变量作为一组属性进行处理,使修改文件更容易,同时保留辅助信息。

  • micopy_all_atts - 将一个变量的所有属性复制到另一个变量(可能跨文件)。
  • micopy_var_def - 将一个变量定义(包括属性)从一个文件复制到另一个文件。
  • micopy_var_vals - 将一个变量的值从一个变量复制到另一个变量(可能跨文件)。
  • micopy_all_var_defs - 将所有变量定义从一个文件复制到另一个文件,排除一个变量列表。
  • micopy_all_var_vals - 将所有变量值从一个文件复制到另一个文件,排除一个变量列表。

MINC 特定的便利函数

[编辑 | 编辑源代码]

提供了一些例程来处理一些 minc 结构。miattput_pointermiattget_pointer 放置/获取指向变量属性的指针。miadd_child 通过处理两个变量的 MIparentMIchildren 属性来帮助维护变量的层次结构。最后,micreate_std_variablemicreate_group_variable 创建一些标准变量并填写一些默认属性。

图像转换变量

[编辑 | 编辑源代码]

前面提到的文件格式要求之一是软件接口,使其易于使用。使用灵活格式的最大困难在于应用程序必须处理许多可能性。在图像方面,这意味着各种数据类型和比例因子,以及不同大小的图像。MINC 的图像转换变量函数试图为程序员消除这种复杂性。

图像转换变量 (icv) 本质上是对程序希望图像在类型、比例和维度上呈现方式的规范。当通过 icv 读取 MINC 图像时,无论数据如何在文件中存储,它都会被转换为调用程序的标准格式。

转换分为两类:类型和范围转换改变图像值的 数据类型(和符号),并选择性地对其进行缩放以进行适当的规范化。维度转换允许程序指定图像维度的尺寸和图像轴的方向(MIxspace 坐标应该增加还是减少?患者的左侧应该出现在图像的左侧还是右侧?)。

ICV 例程

[编辑 | 编辑源代码]

通过 icv 访问文件是一个简单的过程。使用 miicv_create 创建 icv,使用 miicv_set 例程设置属性(如所需的 数据类型),使用 miicv_attach 将 icv 附加到 NetCDF 变量,并使用 miicv_getmiicv_put 访问数据。可以使用 miicv_detach 将 icv 从 NetCDF 变量中分离,并可以使用 miicv_free 释放它。

Icv 属性是字符串、整数、长整数或双精度数。例如,MI_ICV_SIGN(变量值的符号)是一个字符串,而 MI_ICV_IMAGE_MAX(图像最大值)是一个双精度值。提供了四个函数——miicv_setint、miicv_setlong、miicv_setdblmiicv_setstr——来简化属性值的设置。程序可以使用 miicv_inqint、miicv_inqlong、miicv_inqdblmiicv_inqstr 查询属性值。

类型和范围转换

[编辑 | 编辑源代码]

通过指定 MI_ICV_TYPEMI_ICV_SIGN 属性的值来转换像素值的类型和符号(它们默认为 NC_SHORTMI_SIGNED)。还可以将值转换为有效范围并进行规范化。通过将 MI_ICV_DO_RANGE 设置为 TRUE(默认值)来启用这些转换。

如果 MI_ICV_DO_NORMFALSE(默认值),则仅进行有效范围的转换。这意味着,如果输入文件具有 0 到 4095 范围内的短整型,则可以将其转换为 64 到 248 范围内的字节(例如)。实际图像的最大值和最小值(MIimagemaxMIimagemin)将被忽略。有效范围由 MI_ICV_VALID_MAXMI_ICV_VALID_MIN 属性指定,它们默认为类型和符号的合法范围。

我们可能希望缩放值,以便它们规范化为 MIimage 变量中的所有值,或者规范化为某个用户定义的范围。要进行规范化,请将 MI_ICV_DO_NORM 设置为 TRUE。将 MI_ICV_USER_NORM 设置为 FALSE(默认值)会导致规范化为变量的实际最大值和最小值(MIimagemax 的最大值和 MIimagemin 的最小值)。如果 MI_ICV_USER_NORM 为 true,则使用 MI_ICV_IMAGE_MAXMI_ICV_IMAGE_MIN 的值(默认为 1.0 和 0.0)。

MI_ICV_TYPE 或文件类型为浮点数时,从实数到实数的转换始终使用实际图像的最大值和最小值信息进行。如果内部类型为整型且 MI_ICV_DO_NORMFALSE,则进行重新缩放,以便切片最大值映射到内部值的有效范围。

请注意,在转换为整型时,值将四舍五入到最接近的整数,并限制在数据类型的合法范围内。

上述转换很简单,但使用浮点数增加了复杂性,因为一般情况下,我们不想重新缩放这些值以获得实际值。下面更详细地描述了各种可能性。

像素值转换的详细信息

[编辑 | 编辑源代码]

思考重新缩放的最简单方法是通过四个范围(最大值-最小值对)。在文件变量中,值具有有效范围 var_vrange,这些值对应于实际值 var_imgrange。用户/应用程序希望将实际值 usr_imgrange 转换为有用的有效范围 usr_vrange。从 var_vrangevar_imgrangeusr_imgrangeusr_vrange,我们可以确定用于转换像素值的比例和偏移量:输入值按 var_vrangevar_imgrange 的比例缩放为实际值,然后再次按 usr_imgrangeusr_vrange 的比例缩放为用户值。

如果两个 vrange 变量都没有指定,则它们默认为整型类型的最大可能范围。对于浮点类型,usr_vrange 设置为等于 usr_imgrange,因此不进行实际值的转换。

如果没有进行规范化,则对于整型,var_imgrangeusr_imgrange 设置为 [0,1](缩放到 [0,1] 并再次缩放)。在规范化时,usr_imgrange 设置为变量的整个范围(如果找不到则为 [0,1])或用户请求的范围。如果变量值为浮点数,则 var_imgrange 设置为 var_vrange(不缩放到实际值),否则 var_imgrange 为每个图像读取(同样,如果找不到,则为 [0,1])。

这对于读取和写入图像意味着什么将在下面讨论。

使用像素转换读取

[编辑 | 编辑源代码]

当读取到内部浮点数时,规范化没有影响。当读取没有规范化的整数时,每个图像都被缩放到整个范围。使用规范化,它们被缩放到指定的范围,并且可以比较切片。

当输入文件缺少 MIimagemax/MIimageminvar_imgrange 信息)或 MIvalid_range 时,例程会尝试提供合理的默认值,但仍然会发生奇怪的事情。最大的问题是,如果默认值不正确(整型值的整个范围,浮点数的 [0,1]),则缺少 MIvalid_range。在将浮点数转换为整型时,如果值超出了 [0,1] 范围,将会发生溢出。

使用像素转换写入

[编辑 | 编辑源代码]

转换例程可用于写入值。这对于数据压缩很有用——例如,将内部浮点数转换为文件中的字节值,或将内部短整型转换为字节。在使用规范化进行此操作时(例如,将字节重新缩放到切片最大值),重要的是在写入切片之前在 MIimagemaxMIimagemin 中写入切片最大值和最小值。

另一个问题是,MIvalid_rangeMIvalid_maxMIvalid_min 应该正确写入(尤其是当默认值不正确时)。在写入浮点数时,MIvalid_range 应该设置为变量中所有值的整个范围。在这种情况下,属性不必在写入变量之前正确设置,但如果它存在,则值应该合理(最大值大于最小值,并且值不太可能导致溢出)。如果例程 micreate_std_variableNC_FILL 模式下使用(默认值),则这些值将被自动设置。

示例 读取值

[编辑 | 编辑源代码]

在没有规范化的情况下读取图像

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  
  /* Open the file, attach the image variable */
  cdfid=ncopen(filename, NC_NOWRITE);
  
  /* Attach image variable */
  img=ncvarid(cdfid, MIimage);
  (void) miicv_attach(icv, cdfid, img);
  
  /* Get the data - we assume that coord and count are set properly */
  (void) miicv_get(icv, coord, count, image);
  
  /* Close the file and free the icv */
  (void) ncclose(cdfid);
  (void) miicv_free(icv);

使用规范化读取图像

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
  (void) miicv_setdbl(icv, MI_ICV_IMAGE_MAX, 1.83);
  (void) miicv_setdbl(icv, MI_ICV_IMAGE_MIN, -0.57);
  ...

读取浮点数图像

  /* Create the icv. We don't have to set MI_ICV_USER_NORM to TRUE,
     but doing so ensures that the conversion is done properly
     without looking at file values (the defaults for
     MI_ICV_IMAGE_MAX and MI_ICV_IMAGE_MIN are 1 and 0) */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  (void) miicv_setint(icv, MI_ICV_USER_NORM, TRUE);
  ...

示例 写入值

[编辑 | 编辑源代码]

从浮点数写入字节值

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_FLOAT);
  (void) miicv_setint(icv, MI_ICV_DO_NORM, TRUE);
  
  /* Create the file */
  cdf=nccreate(filename, NC_CLOBBER);
  
  /* Define the dimensions */
  dim[0]=ncdimdef(cdf, MIyspace, ysize);
  dim[1]=ncdimdef(cdf, MIxspace, xsize);
  
  /* Define the variables */
  img=micreate_std_variable(cdf, MIimage, NC_BYTE, 2, dim);
  (void) miattputstr(cdf, img, MIsigntype, MI_UNSIGNED);
  vrange[0]=0; vrange[1]=200;
  (void) ncattput(cdf, img, MIvalid_range, NC_DOUBLE, 2, vrange);
  max=micreate_std_variable(cdf, MIimagemax, NC_DOUBLE, 0, NULL);
  min=micreate_std_variable(cdf, MIimagemin, NC_DOUBLE, 0, NULL);
  
  /* End definition mode */
  ncendef(cdf);
  
  /* Attach image variable */
  (void) miicv_attach(icv, cdf, img);
  
  /* Write the image max and min */
  ncvarput1(cdf, max, NULL, &image_maximum);
  ncvarput1(cdf, min, NULL, &image_minimum);
  
  /* Write the image */
  start[0]=start[1]=0;
  count[0]=ysize; count[1]=xsize;
  miicv_put(icv, start, count, vals);
  
  /* Close the file and free the icv */
  (void) ncclose(cdf);
  (void) miicv_free(icv);

如果我们正在写入浮点图像,唯一的区别(除了将NC_BYTE更改为NC_FLOAT之外)是,我们将在文件末尾用浮点值的完整范围重写MIvalid_range

维度转换

[编辑 | 编辑源代码]

任意维图像的一个问题是,软件需要处理一般情况。如果事先知道所有图像都具有特定大小(例如 256 x 256)和特定方向(例如,第一个像素位于患者的前面右侧),则编写应用程序软件会更容易。

通过将 icv 属性 MI_ICV_DO_DIM_CONV 设置为 TRUE,可以自动完成这些转换。空间轴的方向由属性 MI_ICV_XDIM_DIRMI_ICV_YDIM_DIRMI_ICV_ZDIM_DIR 确定。这些影响任何是 MI?spaceMI?frequency 的图像维度,其中 ? 对应于 xyz。这些属性可以具有值 MI_ICV_POSITIVEMI_ICV_NEGATIVEMI_ICV_ANYDIR。最后一个将防止维度翻转。前两个将在必要时翻转维度,以便维度变量的属性 MIstep 具有正确的符号。

这两个图像维度被称为维度 A 和 B。维度 A 是这两个维度中变化最快的维度。设置属性 MI_ICV_ADIM_SIZEMI_ICV_BDIM_SIZE 指定图像维度的期望大小。调整维度大小,以便文件图像完全适合调用程序的图像,并且在图像中居中。大小 MI_ICV_ANYSIZE 允许其中一个维度具有可变大小。如果属性 MI_ICV_KEEP_ASPECT 设置为 TRUE,则两个维度将按相同量重新缩放。可以查询与属性 MIstepMIstart 相对应的新的步长和起点(其中像素位置 = ipixel*step+start,ipixel 从零开始计数)。属性 MI_ICV_?DIM_STEPMI_ICV_?DIM_START? = AB)会自动设置,可以查询但不能设置。

虽然允许矢量图像,但许多应用程序宁愿只处理标量图像(每个点一个强度值)。将 MI_ICV_DO_SCALAR 设置为 TRUE(默认值)将导致矢量图像通过对分量求平均值转换为标量图像。(因此,RGB 图像以这种简单的方式自动转换为灰度图像)。

对于程序来说,对三个(或者更多)维度(而不仅仅是两个标准图像维度)执行维度转换有时可能很有用。为了对超出通常两个的维度执行维度翻转和/或调整大小,可以将属性 MI_ICV_NUM_IMGDIMS 设置为 1 到 MI_MAX_IMGDIMS 之间的整数值。要设置维度的尺寸,请设置属性 MI_ICV_DIM_SIZE(类似于 MI_ICV_ADIM_SIZE)。要指定要设置的维度,请将维度添加到属性中(添加零对应于变化最快的维度——对“A”维度添加零,对“B”维度添加一,等等)。可以通过属性 MI_ICV_DIM_STEPMI_ICV_DIM_START 查询体素间距和位置(类似于 MI_ICV_ADIM_STEPMI_ICV_ADIM_START),同样将维度号添加到属性中。

带维度转换的读取示例

[编辑 | 编辑源代码]

读取一个 256 x 256 的图像,第一个像素位于患者的下方、后面、左侧,作为 0 到 32000 之间的短整数值

  /* Create the icv */
  icv=miicv_create();
  (void) miicv_setint(icv, MI_ICV_TYPE, NC_SHORT);
  (void) miicv_setstr(icv, MI_ICV_SIGN, MI_UNSIGNED);
  (void) miicv_setint(icv, MI_ICV_VALID_MAX, 32000);
  (void) miicv_setint(icv, MI_ICV_VALID_MIN, 0);
  (void) miicv_setint(icv, MI_ICV_DO_DIM_CONV, TRUE);
  (void) miicv_setint(icv, MI_ICV_ADIM_SIZE, 256);
  (void) miicv_setint(icv, MI_ICV_BDIM_SIZE, 256);
  (void) miicv_setint(icv, MI_ICV_KEEP_ASPECT, TRUE);
  (void) miicv_setint(icv, MI_ICV_XDIM_DIR, MI_POSITIVE);
  (void) miicv_setint(icv, MI_ICV_YDIM_DIR, MI_POSITIVE);
  (void) miicv_setint(icv, MI_ICV_ZDIM_DIR, MI_POSITIVE);
  
  /* Open the file, attach the image variable */
  cdfid=ncopen(filename, NC_NOWRITE);
  
  /* Attach image variable */
  img=ncvarid(cdfid, MIimage);
  (void) miicv_attach(icv, cdfid, img);
  
  /* Get the data - we assume that coord and count are set properly */
  (void) miicv_get(icv, coord, count, image);
  
  /* Close the file and free the icv */
  (void) ncclose(cdfid);
  (void) miicv_free(icv);
华夏公益教科书