第四章 公开数据集介绍:Cell Image Library、ISBI 等
做细胞分类,最头疼的往往不是模型怎么搭,而是数据从哪来。我刚入行那会儿,自己拍了几百张显微图像,结果标签质量参差不齐,折腾一个月效果还不如人家公开数据集跑出来的 baseline。从那以后,我养成了习惯:先看看有没有成熟的数据集能用。
这一章我挑几个在学界和工业界都常用的公开数据集,结合我踩过的坑,带你快速上手。你想想看,与其自己从头标注几千张细胞图,不如站在前人的肩膀上——但前提是,你得知道哪个肩膀更靠谱。
4.1 Cell Image Library (CIL)
CIL 是美国细胞生物学学会维护的在线资源库,收录了超过 30 万张显微图像,涵盖各种细胞类型、染色方式和成像模态。它的最大优点是数据集结构统一,每张图都附有元数据(细胞系、荧光标记、缩放比例等)。
我比较常用的一个子集是 CIL 的“Cell Classification Benchmark”,大概 2000 张 HeLa 细胞的明场 + 荧光双通道图像,分辨率 1024×1024,标注了细胞核和细胞质的分割掩膜。
如果你想用 Python 快速下载,可以参考下面这段脚本。注意 URL 我用了占位符,实际使用时替换成 CIL 的 API endpoint 就行。
import requests
import tifffile as tiff
from io import BytesIO
# 假设已获取 image_id 列表
image_ids = ['CIL_10001', 'CIL_10002']
base_url = 'https://api.cellimagelibrary.org/image/'
for img_id in image_ids:
resp = requests.get(f'{base_url}{img_id}/download')
if resp.status_code == 200:
img_data = tiff.imread(BytesIO(resp.content))
# 归一化到 [0,1]
img_norm = (img_data - img_data.min()) / (img_data.max() - img_data.min())
print(f'{img_id} downloaded, shape: {img_norm.shape}')
4.2 ISBI 细胞分割挑战数据集
ISBI(国际生物医学成像研讨会)每年都会组织分割挑战,留下的数据集是实打实的“黄金标准”。其中最经典的是 DIC-C2DH-HeLa —— 用微分干涉差显微镜拍的 HeLa 细胞序列,每帧都有像素级标注。
这个数据集的特点是:
- 图像尺寸 1024×1024,时序长度 100 帧左右
- 细胞形态多变,分裂和凋亡案例丰富
- 标注仅包含细胞边界,不区分细胞质与细胞核
我曾经用它训练分割模型时发现一件怪事:验证集 Dice 很高,但预测结果里总有细碎的假阳性。折腾两天才意识到——原图的对比度在空间上分布不均,边缘细胞偏暗。后来加了局部对比度归一化,问题才解决。
import tifffile as tiff
import numpy as np
# 读取 100 帧序列
stack = tiff.imread('DIC-C2DH-HeLa.tif')
print(f'Volume shape: {stack.shape}') # (100, 1024, 1024)
# 局部对比度归一化
from skimage.exposure import equalize_adapthist
stack_norm = np.array([equalize_adapthist(frame) for frame in stack])
4.3 其他常用数据集一览
除了上面两个,还有几个数据集我经常拿来当补充测试集。下面这个表帮你快速对比:
| 数据集 | 细胞类型 | 图像数量 | 分辨率 | 标注内容 |
|---|---|---|---|---|
| BBBC (Broad Bioimage Benchmark Collection) | 多种(U2OS, HeLa) | 1000+ | 512×512 ~ 2048×2048 | 细胞核、细胞质轮廓 |
| MoNuSeg | 人组织切片细胞核 | 44 张全切片 | 1000×1000 左右 | 核分割多边形 |
| Cell Tracking Challenge | 多种(荧光、明场) | 50+ 序列 | VGA ~ 4K | 分割 + 轨迹 |
| LIVECell | 活细胞相位对比 | 5000+ 已分割 | 1280×1024 | 细胞实例分割掩膜 |
BBBC 的下载地址在 https://bbbc.broadinstitute.org/,每个子集都有独立的 DOI,引用写论文很方便。
4.4 数据下载与预处理最佳实践
拿到公开数据集之后,第一件事不是直接训练,而是按以下步骤处理:
- 统一格式:所有图像转成 .png 或 .npy,避免 TIFF 的压缩差异。
- 尺寸对齐:用插值或 padding 缩放到 512×512,节省显存。
- 归一化:强度到 [0,1] 或 z-score,保持输入分布一致。
- 划分策略:按细胞系或实验批次划分,不要所有数据混洗——否则你会在验证集上“作弊”。
- 存储优化:打成 HDF5 或 TFRecord,减少小文件 IO 时间。
下面是一个统一的预处理脚本骨架,我把常用的参数都写在配置里:
# config.yaml
dataset:
name: "BBBC002"
image_dir: "./raw"
output_dir: "./processed"
target_size: [512, 512]
normalization: "minmax" # minmax / zscore
train_ratio: 0.7
val_ratio: 0.15
test_ratio: 0.15
# preprocess.py
import cv2, yaml, numpy as np, h5py
from glob import glob
from sklearn.model_selection import train_test_split
with open('config.yaml') as f:
cfg = yaml.safe_load(f)
imgs = sorted(glob(f"{cfg['image_dir']}/*.tif"))
labels = sorted(glob(f"{cfg['image_dir']}/*_mask.tif"))
# 按文件名分组,确保图像-掩膜对应
pairs = list(zip(imgs, labels))
train_pairs, val_pairs = train_test_split(
pairs, test_size=cfg['val_ratio'] + cfg['test_ratio'], random_state=42
)
# 再分 val 和 test
val_pairs, test_pairs = train_test_split(val_pairs, test_size=0.5, random_state=42)
def process(pair_list, out_h5):
with h5py.File(out_h5, 'w') as f:
for i, (img_path, mask_path) in enumerate(pair_list):
img = cv2.imread(img_path, cv2.IMREAD_UNCHANGED)
mask = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, cfg['target_size'])
mask = cv2.resize(mask, cfg['target_size'], interpolation=cv2.INTER_NEAREST)
if cfg['normalization'] == 'minmax':
img = (img - img.min()) / (img.max() - img.min() + 1e-8)
f.create_dataset(f'img_{i}', data=img)
f.create_dataset(f'mask_{i}', data=mask)
process(train_pairs, 'train.h5')
process(val_pairs, 'val.h5')
process(test_pairs, 'test.h5')
4.5 本章核心框架:从数据集到分类模型
为了让你对整个流程有个宏观认识,我画了一张图。从数据集选择到特征提取,再到分类训练——每一步的输入输出都标清楚了。
图里我用三种颜色代表不同数据源,中间两条虚线表示你可以选择直接从 CIL 下载并一路走到预处理。实际操作时,我建议先在 BBBC 上跑通完整 pipeline,再去碰 ISBI 的多时序数据——因为时序数据多了时间维,踩坑概率翻倍。
嗯,数据集这块就聊这么多。下面前人的坑我已经替你们填平了一部分,具体怎么用,还要看你的任务需求。记住:数据质量永远比模型结构重要。