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

避坑指南: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 的像素数据可能是有符号整数,也可能是无符号的。我遇到过像素值范围是 -1024 到 3071 的情况,直接显示会全黑。记得做窗宽窗位调整。

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']
我的习惯:做大型项目时,我会用 HDF5 作为数据中间格式。从设备导出的原始数据先转成 HDF5,后续所有分析都基于这个文件。这样既保留了原始信息,又方便管理。

3.4 专有格式处理:厂商的「黑盒子」

专有格式,说白了就是设备厂商自己定义的格式。比如 Zeiss 的 .czi、Leica 的 .lif、Olympus 的 .oib……每个厂商都有自己的「小算盘」。

我曾经被一个 .dm3 格式的文件折磨了两天——那是 Gatan 的电子显微镜格式。后来发现,每个厂商都会提供 SDK 或者开源库来解析自己的格式。

处理专有格式的通用策略

  1. 找官方 SDK:大厂一般会提供 Python/C++ 库,比如 Zeiss 的 czifile、Bio-Formats
  2. 用 Bio-Formats:这是一个开源库,支持 150+ 种格式,Java 写的,有 Python 接口
  3. 逆向工程:实在没办法了,用十六进制编辑器看文件头,自己写解析器
# 用 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)
小技巧:如果你经常处理各种格式,建议搭建一个统一的读取接口。把所有格式的解析逻辑封装成函数,返回统一的 NumPy 数组和元数据字典。这样后续分析代码就不用关心数据来源了。
微结构数据格式解析知识体系 数据格式解析 图像格式三兄弟 • TIFF:无损、多页、元数据丰富 • JPEG:有损压缩,慎用于分析 • PNG:无损、透明通道 • 选型:原始数据用TIFF DICOM 标准 • 医学影像通用标准 • 图像+元数据打包 • 多帧/压缩/私有标签 • 注意窗宽窗位调整 HDF5 层次格式 • 类文件系统结构 • Group + Dataset • 支持TB级大数据 • 并行I/O,灵活类型 专有格式处理 • 厂商SDK(czifile等) • Bio-Formats 通用方案 • 逆向工程(万不得已) 核心原则:保留原始信息,统一接口读取

嗯,格式解析这块内容确实不少,但掌握了这些,你处理微结构数据时就不会被文件格式卡住了。记住一点:数据格式只是载体,真正重要的是里面的微结构信息。别让格式问题成为你分析的绊脚石。