返回系列

NumPy 影像计算:shape、axis、dtype 与体数据

用 DicomVision 的后端处理链路解释 NumPy 在医学影像中的核心角色:数组形状、轴顺序、窗宽窗位、dtype 和内存。

问题引入

医学影像后端离不开 NumPy。pydicom 负责把 DICOM 像素解码出来,但真正的计算从 NumPy 数组开始:切片要排序和堆叠,灰度要裁剪和归一化,MPR 要重排轴和重采样,测量要做向量和距离计算,VTK 也需要从连续内存的数组接收体数据。

如果不理解 shape、axis 和 dtype,影像代码很容易出现三类错误:图像方向反了,MPR 切面比例错了,内存突然爆了。DicomVision 中大量看似细小的 np.asarray、np.stack、np.flip、np.clip,其实都在处理这些基础问题。

背景概念

DICOM 单张切片解码后通常是二维数组,shape 为 (rows, columns),也就是 (y, x)。多张切片组成体数据后,后端常用 (z, y, x) 表示,其中 z 是切片方向,y 是行,x 是列。这个顺序适合按照切片索引取图,也适合把 axial/coronal/sagittal 的关系说清楚。

但是数组坐标不等于物理坐标。数组的 x/y/z 是索引,单位是像素或切片序号;物理坐标要乘上 spacing,还要考虑 DICOM 中的方向矩阵。PixelSpacing 决定行列方向的毫米间距,切片间距可以来自相邻切片位置,也可以退化使用 SliceThickness。MPR、测量和 3D 都不能只看数组索引。

dtype 同样关键。CT 原始像素可能是 int16,应用 slope/intercept 后通常转为 float32。显示图像最终要转成 uint8。3D 渲染为了给 VTK,常会保持 float32 并保证连续内存。一个 512x512x500 的 volume,如果是 int16,大约 250 MB;如果转 float32,就是约 500 MB。再加缓存、重采样和 VTK session,低配机器很快到内存边界。

项目实现

项目在 DicomCache 中把 dataset.pixel_array 转为 NumPy 数组。如果是灰度图,会转成 float32,再应用 slope/intercept;如果是彩色图,会处理 YBR 到 RGB 的转换,并把值规整到 uint8。对于 MONOCHROME1,项目通过取负值统一灰度方向。

窗宽窗位的核心计算可以简化为:

low = center - width / 2
high = center + width / 2
display = np.clip(pixels, low, high)
display = (display - low) / (high - low) * 255
display = display.astype(np.uint8)

这段逻辑的意义是把医学值域映射到显示值域。np.clip 不改变原始 Dataset,它只服务于当前视口显示。MPR 和 3D 则会进一步把多个切片通过 np.stack 组织成 volume。轴方向不一致时,项目会用 np.transpose、np.flip 和方向信息把数据变成可稳定重采样的体。

关键函数表

NumPy 函数在影像中的用途风险点
np.asarray把 pydicom 或 PIL 结果变成数组可能共享内存,修改前要确认语义
np.stack多张切片堆成 volume切片 shape 必须一致
np.transpose调整 axis 顺序很容易把 x/y/z 语义写反
np.flip修正方向或显示翻转要区分显示翻转和物理方向修正
np.clip窗宽窗位裁剪、颜色范围限制裁剪只用于显示,不代表修改原始像素
np.ascontiguousarray给 VTK 或编码路径提供连续内存会复制数据,注意内存峰值
np.linalg.norm距离和向量长度要使用物理坐标而不是屏幕坐标
np.cross / np.dot方向向量、角度、平面计算方向向量必须归一化

自绘图表

x: columns z: slices y: rows 后端体数据常按 (z, y, x) 组织 数组索引是离散网格,真实距离还要乘 spacing 并考虑方向。
shape 只是数组布局,物理空间还需要 spacing 和 orientation。
pixel_array (rows, columns) np.stack (z, y, x) transpose / flip 修正轴顺序和方向 MPR 重采样 axis 变换要服务于真实空间解释 如果只让图像“看起来正”,测量和 MPR 仍可能是错的。
NumPy 轴变换必须同时考虑显示方向和物理方向。
数据shape 示例dtype约占内存
单张 CT 原始切片512 x 512int160.5 MB
500 张 CT 原始体500 x 512 x 512int16250 MB
500 张 CT 计算体500 x 512 x 512float32500 MB
500 张显示缓存500 x 512 x 512uint8125 MB
RGB 彩色显示体500 x 512 x 512 x 3uint8375 MB

小结

NumPy 在 DicomVision 中不是辅助库,而是影像计算的共同语言。pydicom 解码出来的是数组,MPR 重采样操作的是数组,测量统计依赖数组,VTK 也从数组接收 volume。写医学影像代码时,最该先确认的不是某个算法名,而是当前数组的 shape、axis、dtype、spacing 和方向语义。