跳转到内容

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 坐标等,也可以表示抽象事物,如查找表索引。每个维度都有一个名称和一个长度,当与其他维度组合时,定义了变量的形状——变量图像由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 文件。打开的文件要么处于“define”模式,要么处于“data”模式。在 define 模式下,可以定义维度、变量和属性,但不能写入或读取变量数据。在 data 模式下,可以写入或读取变量值,但不能对维度或变量进行任何更改,并且属性只能在它们已存在的情况下写入,并且在写入时不会变得更大。新创建的文件会自动处于 define 模式。

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

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

MINC 格式

[编辑 | 编辑源代码]

可以使用仅 NetCDF 函数调用来构建 MINC 格式文件,但是提供了一个 C 包含文件来促进(并帮助确保)符合标准。这个文件定义了有用的常量、标准维度、变量和属性名称以及一些属性值。它还声明了在 minc 便利函数库中定义的函数,这些函数旨在使应用程序开发人员的生活更轻松。

MINC 标准

[编辑 | 编辑源代码]

已经提出了各种文件格式的要求。其中一项要求清单如下:协议应该:1) 简单,2) 自描述,3) 可维护,4) 可扩展,5) N 维,以及 6) 具有通用数据结构。NetCDF 格式满足所有这些要求,表明它是一个良好的起点。但是,我会在清单中添加更多要求。上述清单中隐含的要求是,应该有一个访问数据的标准(如何获取患者姓名)——NetCDF 没有提供。此外,一个有用的格式应该附带一个软件接口,使其易于使用,尤其是在开发环境中。最后,存储许多关联信息的格式还应该提供一些数据组织方式。

MINC 格式尝试在 NetCDF 格式中添加这些内容。

MINC 变量类型

[编辑 | 编辑源代码]

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

对应于维度的变量被称为维度变量,它们描述与维度相对应的坐标系。例如,MIxspace 是一个维度和变量,描述了其他变量的 x 坐标。

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

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

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

数据组织

[编辑 | 编辑源代码]

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

MINC 维度变量和属性名称

[编辑 | 编辑源代码]

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

图像维度

[编辑 | 编辑源代码]

MINC 标准赋予了图像概念一些特殊状态。NetCDF 中没有固有的内容表明特定维度有任何特殊状态,但在成像环境中,对哪些内容可以在哪些维度上变化施加限制可能很方便。例如,要求用于指定如何重新缩放图像的变量(参见后面的像素值部分)不随图像维度变化,这意味着我们可以将图像视为一个简单的单元。在最简单的情况下,图像维度仅仅是 MIimage 变量的两个变化最快的维度。

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

MINC 坐标系

[编辑 | 编辑源代码]

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

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

如果方向余弦不存在,则假定 MIxspace 为 (1,0,0),MIyspace 为 (0,1,0),MIzspace 为 (0,0,1)。方向余弦是单位向量,应该归一化。此外,step 属性应该包含关于轴翻转(负或正)的信息,而不是方向余弦。

像素值和真实值

[编辑 | 编辑源代码]

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

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

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

通用便利函数

[编辑 | 编辑源代码]

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) 本质上是程序希望图像在类型、比例和维度上看起来像什么。当 MINC 图像通过 icv 读取时,它将被转换为调用程序的标准格式,而不管数据如何在文件中存储。

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

ICV 例程

[编辑 | 编辑源代码]

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

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 为真,则使用 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_variable 用于 NC_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)查询体素间隔和位置,同样将维度编号添加到属性。

带有维度转换的示例读取

[编辑 | 编辑源代码]

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

  /* 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);
华夏公益教科书