3、数据格式解析:TIFF/JPEG/PNG格式差异、DICOM标准、HDF5层次数据格式、专有格式处理
做微结构数据分析,第一关就是跟各种文件格式打交道。说实话,我刚入行那会儿也被这些格式搞得头大——同一个样品,不同设备导出的文件后缀五花八门,解析方式也完全不一样。今天我就把这几类常见格式掰开揉碎了讲清楚。
3.1 图像格式三兄弟:TIFF、JPEG、PNG
这三种格式,你平时看图可能觉得差不多,但在微结构数据里,差别大了去了。
TIFF:无损存储的老大哥
TIFF 格式,我个人最喜欢用它存原始数据。为什么?因为它支持无损压缩,而且能存多页图像。比如你拍了一组 Z-stack 的共聚焦图像,一个 TIFF 文件就能搞定。
我在项目中遇到过一件事:某次客户给的 SEM 图像是 TIFF 格式,但死活打不开。后来发现是 BigTIFF——普通库读不了超过 4GB 的文件。嗯,这里要注意,处理大文件时记得用支持 BigTIFF 的库。
- 无损压缩(LZW、ZIP、PackBits)
- 支持多页/多帧
- 可存储元数据(拍摄参数、标尺信息)
- 文件体积大,但信息完整
# Python 读取 TIFF 的两种方式
from PIL import Image
import tifffile
# 方式一:PIL 适合单页
img = Image.open('sample.tif')
# 方式二:tifffile 适合多页/大文件
with tifffile.TiffFile('big_sample.tif') as tif:
for page in tif.pages:
data = page.asarray() # 逐页读取
JPEG:有损压缩的陷阱
JPEG 格式,说白了就是「省空间但丢信息」。你想想看,一张 4096×4096 的 SEM 图像,存成 TIFF 可能要 50MB,JPEG 压缩后可能只有 2MB。但代价是什么?
我曾经用 JPEG 图像做晶粒尺寸分析,结果发现边缘检测出来的晶界全是锯齿状的。后来一查,JPEG 的块状压缩把微结构细节全毁了。所以我的建议是:定量分析永远不要用 JPEG。
PNG:网络时代的折中方案
PNG 格式,无损压缩,支持透明通道。在微结构领域,我常用它来存二值化后的分割结果。比如你做了晶粒分割,把每个晶粒标上不同颜色,存成 PNG 就很好——颜色信息不会丢失。
但 PNG 有个问题:它不支持多页。所以如果你有三维数据,还是老老实实用 TIFF 或者 HDF5。
| 特性 | TIFF | JPEG | PNG |
|---|---|---|---|
| 压缩方式 | 无损/有损可选 | 有损 | 无损 |
| 多页支持 | ✅ 支持 | ❌ 不支持 | ❌ 不支持 |
| 元数据 | 丰富 | 有限 | 有限 |
| 适用场景 | 原始数据、定量分析 | 预览、报告 | 分割结果、二值图 |
3.2 DICOM 标准:医学影像的通用语言
DICOM 标准,如果你做生物医学微结构分析,肯定绕不开。它不只是图像格式,更是一套完整的通信协议和数据管理标准。
我第一次接触 DICOM 时,觉得它太复杂了——一个文件里除了图像数据,还有患者信息、设备参数、扫描序列……说白了,它把元数据和图像数据打包在一起了。
DICOM 的核心结构
每个 DICOM 文件由多个 Data Element 组成。每个 Element 包含标签(Tag)、值表示法(VR)、值长度(VL)和值(Value)。
# 用 pydicom 读取 DICOM 文件
import pydicom
ds = pydicom.dcmread('ct_scan.dcm')
# 获取图像数据
pixel_array = ds.pixel_array
# 读取元数据
print(f"患者ID: {ds.PatientID}")
print(f"扫描层厚: {ds.SliceThickness} mm")
print(f"像素间距: {ds.PixelSpacing}")
DICOM 的常见陷阱
- 多帧 DICOM:有些设备把整个三维数据存成一个文件,需要用
ds.NumberOfFrames判断 - 压缩 DICOM:支持 JPEG 2000、RLE 等压缩,读取时需要解压
- 私有标签:不同厂商会加自己的私有信息,解析时容易报错
3.3 HDF5:层次数据格式的王者
HDF5 格式,我个人觉得它是微结构数据分析的瑞士军刀。为什么?因为它能存任意类型的数据,而且支持层次化组织。
你想想看,一个 HDF5 文件就像一个小型文件系统——里面有 Group(文件夹)和 Dataset(文件)。你可以把原始图像、分割结果、分析参数、统计表格全部塞进一个文件里。
HDF5 的优势
- 支持大数据:可以处理 TB 级别的数据,不会一次性加载到内存
- 灵活的数据类型:图像、表格、字符串、甚至自定义对象都能存
- 并行 I/O:多线程同时读写,适合高性能计算
# HDF5 读写示例
import h5py
import numpy as np
# 写入数据
with h5py.File('microstructure.h5', 'w') as f:
# 创建 Group
grp = f.create_group('experiment_001')
# 存储图像数据
grp.create_dataset('image', data=np.random.rand(1024, 1024))
# 存储属性(元数据)
grp.attrs['magnification'] = 5000
grp.attrs['voltage'] = 20.0
# 存储表格数据
grp.create_dataset('grain_stats', data=np.array([
[1, 45.2, 0.8],
[2, 32.1, 0.6]
]))
# 读取数据
with h5py.File('microstructure.h5', 'r') as f:
img = f['experiment_001/image'][:]
mag = f['experiment_001'].attrs['magnification']
3.4 专有格式处理:厂商的「黑盒子」
专有格式,说白了就是设备厂商自己定义的格式。比如 Zeiss 的 .czi、Leica 的 .lif、Olympus 的 .oib……每个厂商都有自己的「小算盘」。
我曾经被一个 .dm3 格式的文件折磨了两天——那是 Gatan 的电子显微镜格式。后来发现,每个厂商都会提供 SDK 或者开源库来解析自己的格式。
处理专有格式的通用策略
- 找官方 SDK:大厂一般会提供 Python/C++ 库,比如 Zeiss 的 czifile、Bio-Formats
- 用 Bio-Formats:这是一个开源库,支持 150+ 种格式,Java 写的,有 Python 接口
- 逆向工程:实在没办法了,用十六进制编辑器看文件头,自己写解析器
# 用 Bio-Formats 读取专有格式
import bioformats
import javabridge
# 启动 Java 虚拟机
javabridge.start_vm(class_path=bioformats.JARS)
# 读取 .czi 文件
with bioformats.ImageReader('sample.czi') as reader:
# 读取第 0 个系列、第 0 个通道、第 0 个 Z 层
img = reader.read(series=0, c=0, z=0)
javabridge.kill_vm()
3.5 格式选择与转换策略
说了这么多,到底该用哪种格式?我的建议很简单:
- 原始数据:用 TIFF 或 DICOM(保留所有信息)
- 中间处理结果:用 HDF5(方便管理)
- 最终输出:用 PNG 或 JPEG(节省空间)
格式转换时,一定要注意数据类型和位深。比如 16 位的 TIFF 转成 8 位的 PNG,信息就丢了。我一般会先检查像素值的范围,再做缩放。
# 安全的格式转换示例
from PIL import Image
import numpy as np
def safe_convert_tiff_to_png(tiff_path, png_path):
"""16位 TIFF 转 8位 PNG,保留对比度"""
img = Image.open(tiff_path)
arr = np.array(img)
# 自动调整对比度
p2, p98 = np.percentile(arr, [2, 98])
arr_scaled = np.clip((arr - p2) / (p98 - p2) * 255, 0, 255)
# 保存为 PNG
Image.fromarray(arr_scaled.astype(np.uint8)).save(png_path)
嗯,格式解析这块内容确实不少,但掌握了这些,你处理微结构数据时就不会被文件格式卡住了。记住一点:数据格式只是载体,真正重要的是里面的微结构信息。别让格式问题成为你分析的绊脚石。