数据描述
一、引言
在乳腺癌精准诊断中,免疫组化(IHC)染色图像是分子分型的 “金标准”—— 通过检测 HER2、ER、PR 等蛋白表达,可直接指导靶向治疗(如 HER2 阳性患者使用曲妥珠单抗)。但传统 IHC 检测存在三大临床痛点:一是流程低效高耗,从切片制备到染色完成需 4-6 小时,单张切片试剂成本超 100 元,难以满足大规模筛查需求;二是操作依赖性强,IHC 对试剂批次、温度控制要求严苛,基层医院因操作差异导致的结果偏差率可达 15%;三是H&E 与 IHC 断层,H&E 作为常规染色(快速、低成本)仅能呈现细胞形态,无法提供蛋白表达信息,导致 “常规筛查与精准分型” 衔接断裂。
BCI 数据集(北京朝阳医院与首都医科大学联合构建)正是针对上述痛点设计:包含 4872 对结构级对齐的 H&E 与 IHC 染色图像,通过 “切片准备→扫描→elastix 注册→补丁选择” 的 6 步标准化流程构建,为 “从 H&E 图像生成 IHC 图像” 的深度学习算法提供高质量配对数据。其核心目标是通过 AI 技术替代部分传统 IHC 检测,实现 “常规 H&E 染色→AI 生成 IHC 结果” 的快速转化,将诊断耗时缩短至分钟级,同时为乳腺癌影像生成技术的临床落地提供基准数据。
二、内容主体
(一)数据集核心信息
通过表格直观呈现关键参数,覆盖 “数据属性、构建质控、临床适配” 等维度,未明确信息标注 “需咨询合作机构”,确保用户快速判断数据匹配度:
信息类别 |
具体内容(量化、明确) |
基础属性 |
数据集名称:BCI(Breast Cancer Immunohistochemistry)数据集;数据总量:4872 对配对图像(每对含 1 张 H&E 染色图 + 1 张 IHC 染色图);数据类型:乳腺癌组织病理数字化图像(RGB 三通道);创建方:北京朝阳医院、首都医科大学;核心用途:H&E→IHC 染色转换算法训练 / 验证、乳腺癌分子分型辅助诊断;文件格式:未公开(推测为 TIFF/PNG,临床病理图像无损格式) |
采集与构建信息 |
切片制备:临床手术切除的乳腺癌组织(FFPE 石蜡包埋),连续切片厚度 4-5μm(确保 H&E 与 IHC 切片来自同一组织层面);扫描设备:标准病理切片扫描仪(如 3DHISTECH Pannoramic,符合临床数字化规范);构建流程:6 步标准化质控 ——①FFPE 切片制备→②H&E/IHC 双染色→③20× 高分辨率扫描→④投影变换校正→⑤elastix 刚性 + 弹性注册→⑥图像精化(去模糊)+ 补丁选择 |
对齐与标注情况 |
对齐精度:基于 elastix 工具实现像素级结构对齐,通过互信息最大化优化,临床验证对齐误差<5 像素;IHC 染色类型:覆盖乳腺癌核心靶点(推测含 HER2、ER、PR,具体需咨询合作机构);数据完整性:4872 对图像均通过质控(无组织破损、染色不均),无缺失样本 |
格式与规格 |
图像分辨率:20× 放大下 0.5μm / 像素(单张全图约 10000×8000 像素);通道数:3 通道 RGB(H&E:苏木精蓝 + 伊红粉;IHC:DAB 棕染 + 苏木精蓝染);补丁规格:预处理后提取 256×256/512×512 像素补丁(适配 GPU 显存);适配工具:OpenCV/PIL(图像读取)、elastix/ITK(对齐验证)、PyTorch/TensorFlow(生成模型训练) |
数据划分 |
官方未划分;推荐策略:按8:1:1拆分(训练集 3898 对、验证集 487 对、测试集 487 对),划分时保持 “乳腺癌亚型分布一致”(如 HER2 阳性占比 15%、Luminal A 型占比 40%),避免样本偏倚影响算法泛化性 |
(二)数据集核心优势
本数据集的核心竞争力在于 “临床级数据质量 + 强任务适配性”,完美解决乳腺癌 IHC 生成算法的核心数据痛点,具体优势如下:
- elastix 精准对齐,消除算法训练偏差采用医学影像领域金标准的 elastix 工具链,通过 “刚性注册(校正平移 / 旋转)+ 弹性注册(校正组织形变)” 实现 H&E 与 IHC 的像素级对齐 —— 相比传统 “手动对齐”(误差>20 像素),其对齐误差<5 像素,可精准匹配同一细胞区域在两种染色中的位置(如 H&E 中的癌细胞膜对应 IHC 中的 HER2 棕染区域),避免模型学习 “虚假特征”(如将间质区域误映射为蛋白表达区),显著提升生成结果的临床可靠性。
- 大规模配对样本,覆盖临床多样化场景包含 4872 对高质量样本,覆盖乳腺癌主要病理类型(导管癌、小叶癌)与分子亚型(HER2 阳性、ER/PR 阳性、三阴性):
- 阳性样本:HER2 阳性病例含明确细胞膜棕染,ER/PR 阳性含细胞核棕染;
- 阴性样本:三阴性病例无棕染区域,仅细胞核呈蓝染;该规模可支撑 CycleGAN、Pix2Pix 等深度学习模型的充分训练,避免小样本导致的过拟合(如仅数百对样本的模型泛化误差超 20%)。
- 6 步标准化流程,数据无噪声冗余数据集构建全程遵循临床病理质控标准,每一步均有明确验收指标:
- 切片阶段:连续切片厚度误差≤0.5μm,确保组织层面一致性;
- 染色阶段:H&E 符合巴氏染色标准(细胞核蓝染清晰),IHC 通过阳性对照验证(正常乳腺组织呈阳性);
- 补丁阶段:仅保留组织占比≥30% 的补丁,剔除背景冗余 —— 标准化流程确保数据无 “染色异常”“组织破损” 等噪声,用户无需额外清洗即可直接用于模型训练,节省 60% 预处理时间。
- 任务导向设计,降低开发门槛直接提供 “输入(H&E)- 目标(IHC)” 的配对数据,无需用户手动匹配临床样本(传统研究中需花费数周匹配同组织的 H&E 与 IHC 切片);同时预处理阶段已完成 “补丁提取”,可直接输入生成模型(如 256×256 补丁适配 Batch=16 的训练),避免因 “全图分辨率过高(万级像素)” 导致的显存溢出问题。
(三)数据应用全流程指导(IHC 图像生成任务)
1. 数据加载与预处理(核心:配对验证 + 补丁提取)
功能目标:读取 H&E-IHC 配对图像,验证对齐精度,提取标准化补丁,为生成模型输入做准备。代码示例(Python):
# 1. 导入依赖库
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from elastix import Elastix # 需安装elastix-python(pip install elastix)
# 2. 配置路径与参数
DATA_ROOT = "path/to/BCI_dataset" # 数据集根目录(含sample_xxx_he.png/sample_xxx_ihc.png)
PATCH_SIZE = 256 # 补丁尺寸(适配生成模型)
ALIGN_TOLERANCE = 5 # 对齐误差容忍度(像素)
# 3. 读取单一样本的H&E与IHC配对图像
def load_paired_images(sample_id):
"""读取配对图像并转为RGB格式(OpenCV默认BGR)"""
he_path = os.path.join(DATA_ROOT, f"sample_{sample_id:04d}_he.png")
ihc_path = os.path.join(DATA_ROOT, f"sample_{sample_id:04d}_ihc.png")
he_img = cv2.imread(he_path)
ihc_img = cv2.imread(ihc_path)
# BGR→RGB转换
he_img = cv2.cvtColor(he_img, cv2.COLOR_BGR2RGB)
ihc_img = cv2.cvtColor(ihc_img, cv2.COLOR_BGR2RGB)
return he_img, ihc_img
# 4. 验证对齐精度(基于组织掩码中心偏移+elastix配准误差)
def verify_alignment(he_img, ihc_img):
"""双重验证对齐精度:1. 组织中心偏移;2. elastix配准误差"""
# 步骤1:提取组织掩码(排除白色背景)
def get_tissue_mask(img):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
_, mask = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV) # 背景阈值:240
return mask
he_mask = get_tissue_mask(he_img)
ihc_mask = get_tissue_mask(ihc_img)
# 步骤2:计算组织中心偏移
def get_centroid(mask):
moments = cv2.moments(mask)
cx = int(moments["m10"] / moments["m00"]) if moments["m00"] != 0 else 0
cy = int(moments["m01"] / moments["m00"]) if moments["m00"] != 0 else 0
return (cx, cy)
he_centroid = get_centroid(he_mask)
ihc_centroid = get_centroid(ihc_mask)
center_offset = np.sqrt((he_centroid[0]-ihc_centroid[0])**2 + (he_centroid[1]-ihc_centroid[1])**2)
# 步骤3:elastix配准误差(可选,需提前生成配准文件)
elastix_obj = Elastix()
elastix_obj.register(fixed_image=he_img, moving_image=ihc_img, parameter_file="elastix_rigid_affine.txt")
tre = elastix_obj.compute_transformed_points_error(landmark_file="tissue_landmarks.txt") # 手动标注10个组织标志点
return center_offset <= ALIGN_TOLERANCE and tre <= ALIGN_TOLERANCE, center_offset, tre
# 5. 提取有效补丁(过滤背景,保留组织区域)
def extract_valid_patches(he_img, ihc_img, patch_size=256, stride=128):
"""提取组织占比≥30%的补丁,避免背景干扰"""
patches_he, patches_ihc = [], []
h, w, _ = he_img.shape
# 遍历图像生成补丁
for y in range(0, h - patch_size + 1, stride):
for x in range(0, w - patch_size + 1, stride):
# 裁剪补丁
he_patch = he_img[y:y+patch_size, x:x+patch_size, :]
ihc_patch = ihc_img[y:y+patch_size, x:x+patch_size, :]
# 计算组织占比
he_mask = get_tissue_mask(he_patch)
tissue_ratio = np.sum(he_mask) / (patch_size * patch_size)
# 保留有效补丁
if tissue_ratio >= 0.3:
patches_he.append(he_patch / 255.0) # 归一化到[0,1]
patches_ihc.append(ihc_patch / 255.0)
return np.array(patches_he), np.array(patches_ihc)
# 6. 示例:加载样本并预处理
sample_id = 1001
he_img, ihc_img = load_paired_images(sample_id)
is_aligned, center_offset, tre = verify_alignment(he_img, ihc_img)
patches_he, patches_ihc = extract_valid_patches(he_img, ihc_img, PATCH_SIZE)
print(f"样本{sample_id}对齐验证:{'通过' if is_aligned else '失败'}")
print(f"组织中心偏移:{center_offset:.2f}像素,ELASTIX配准误差:{tre:.2f}像素")
print(f"提取有效补丁数量:H&E={len(patches_he)},IHC={len(patches_ihc)}")
# 可视化配对图像与补丁
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
axes[0].imshow(he_img)
axes[0].set_title("H&E染色图像(输入)")
axes[0].axis("off")
axes[1].imshow(ihc_img)
axes[1].set_title("IHC染色图像(目标)")
axes[1].axis("off")
axes[2].imshow(patches_he[0])
axes[2].set_title(f"H&E补丁({PATCH_SIZE}×{PATCH_SIZE},组织占比{np.sum(get_tissue_mask(patches_he[0]))/(PATCH_SIZE**2):.1%})")
axes[2].axis("off")
plt.tight_layout()
plt.show()
关键说明:elastix 验证需提前准备 “参数文件”(如elastix_rigid_affine.txt
,定义刚性 + 弹性注册规则)和 “标志点文件”(手动标注 10 个组织特征点,如血管交叉、腺体边缘);补丁提取的 “组织占比≥30%” 是基于临床经验设定,可过滤 90% 以上的背景冗余。
2. 核心任务:H&E→IHC 染色图像生成(基于 Pix2Pix 模型)
模型选择:Pix2Pix(条件生成对抗网络,专为 “成对图像转换” 设计,在医学影像染色转换中性能最优,可精准学习 H&E 到 IHC 的染色映射与结构一致性)代码示例(基于 PyTorch):
# 1. 导入依赖库
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchvision.transforms import Compose, ToTensor, Normalize
from skimage.metrics import peak_signal_noise_ratio, structural_similarity
# 2. 自定义BCI数据集类
class BCIDataset(Dataset):
def __init__(self, sample_ids, data_root, patch_size=256, transform=None):
self.sample_ids = sample_ids
self.data_root = data_root
self.patch_size = patch_size
self.transform = transform
def __len__(self):
return len(self.sample_ids)
def __getitem__(self, idx):
sample_id = self.sample_ids[idx]
he_img, ihc_img = load_paired_images(sample_id)
patches_he, patches_ihc = extract_valid_patches(he_img, ihc_img, self.patch_size)
# 随机选择1个补丁(批量训练可优化为多补丁)
patch_idx = np.random.randint(0, len(patches_he))
he_patch = patches_he[patch_idx]
ihc_patch = patches_ihc[patch_idx]
if self.transform:
he_patch = self.transform(he_patch)
ihc_patch = self.transform(ihc_patch)
return he_patch, ihc_patch
# 3. 数据变换与加载(适配GAN训练)
transform = Compose([
ToTensor(), # [H,W,C]→[C,H,W]
Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) # 归一化到[-1,1]
])
# 划分样本ID(4872对:训练3898+验证487+测试487)
all_sample_ids = list(range(1, 4873))
train_ids = all_sample_ids[:3898]
val_ids = all_sample_ids[3898:4385]
test_ids = all_sample_ids[4385:]
# 创建DataLoader
train_dataset = BCIDataset(train_ids, DATA_ROOT, transform=transform)
val_dataset = BCIDataset(val_ids, DATA_ROOT, transform=transform)
test_dataset = BCIDataset(test_ids, DATA_ROOT, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True, num_workers=4)
val_loader = DataLoader(val_dataset, batch_size=16, shuffle=False, num_workers=4)
test_loader = DataLoader(test_dataset, batch_size=16, shuffle=False, num_workers=4)
# 4. 定义Pix2Pix模型(生成器UNet+判别器PatchGAN)
class UNetGenerator(nn.Module):
"""UNet生成器:从H&E生成IHC,含跳跃连接保持结构一致性"""
def __init__(self, in_ch=3, out_ch=3):
super().__init__()
# 下采样(编码)
self.down1 = self._down_block(in_ch, 64)
self.down2 = self._down_block(64, 128)
self.down3 = self._down_block(128, 256)
self.down4 = self._down_block(256, 512)
# 上采样(解码)
self.up1 = self._up_block(512, 256)
self.up2 = self._up_block(256, 128)
self.up3 = self._up_block(128, 64)
# 输出层
self.out = nn.Conv2d(64, out_ch, kernel_size=1)
self.tanh = nn.Tanh() # 输出[-1,1]
def _down_block(self, in_ch, out_ch):
return nn.Sequential(
nn.Conv2d(in_ch, out_ch, 4, stride=2, padding=1),
nn.BatchNorm2d(out_ch),
nn.LeakyReLU(0.2, inplace=True)
)
def _up_block(self, in_ch, out_ch):
return nn.Sequential(
nn.ConvTranspose2d(in_ch, out_ch, 4, stride=2, padding=1),
nn.BatchNorm2d(out_ch),
nn.ReLU(inplace=True)
)
def forward(self, x):
d1 = self.down1(x)
d2 = self.down2(d1)
d3 = self.down3(d2)
d4 = self.down4(d3)
u1 = self.up1(d4)
u1 = torch.cat([u1, d3], dim=1) # 跳跃连接:融合编码层细节
u2 = self.up2(u1)
u2 = torch.cat([u2, d2], dim=1)
u3 = self.up3(u2)
u3 = torch.cat([u3, d1], dim=1)
return self.tanh(self.out(u3))
class PatchDiscriminator(nn.Module):
"""PatchGAN判别器:判断“ H&E+IHC ”是否为真实配对"""
def __init__(self, in_ch=6): # 输入:H&E(3ch)+IHC(3ch)
super().__init__()
self.model = nn.Sequential(
nn.Conv2d(in_ch, 64, 4, stride=2, padding=1),
nn.LeakyReLU(0.2),
nn.Conv2d(64, 128, 4, stride=2, padding=1),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2),
nn.Conv2d(128, 256, 4, stride=2, padding=1),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2),
nn.Conv2d(256, 1, 4, stride=1, padding=1),
nn.Sigmoid()
)
def forward(self, he, ihc):
x = torch.cat([he, ihc], dim=1) # 拼接输入
return self.model(x)
# 5. 模型训练(含对抗损失+L1内容损失)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
generator = UNetGenerator().to(device)
discriminator = PatchDiscriminator().to(device)
# 损失函数与优化器
adv_criterion = nn.BCELoss() # 对抗损失
l1_criterion = nn.L1Loss() # 内容损失(保证结构一致)
opt_g = optim.Adam(generator.parameters(), lr=2e-4, betas=(0.5, 0.999))
opt_d = optim.Adam(discriminator.parameters(), lr=2e-4, betas=(0.5, 0.999))
# 训练参数
num_epochs = 100
best_val_ssim = 0.0 # 最优指标:SSIM(结构相似性)
for epoch in range(num_epochs):
# 训练阶段
generator.train()
discriminator.train()
train_g_loss, train_d_loss = 0.0, 0.0
for he_imgs, ihc_imgs in train_loader:
he_imgs, ihc_imgs = he_imgs.to(device), ihc_imgs.to(device)
batch_size = he_imgs.size(0)
real_labels = torch.ones(batch_size, 1, 30, 30).to(device) # 判别器输出尺寸30×30
fake_labels = torch.zeros(batch_size, 1, 30, 30).to(device)
# 1. 优化生成器
opt_g.zero_grad()
fake_ihc = generator(he_imgs)
pred_fake = discriminator(he_imgs, fake_ihc)
# 损失计算:对抗损失 + L1内容损失(权重100,优先保证结构)
adv_loss = adv_criterion(pred_fake, real_labels)
l1_loss = l1_criterion(fake_ihc, ihc_imgs) * 100
g_loss = adv_loss + l1_loss
g_loss.backward()
opt_g.step()
train_g_loss += g_loss.item() * batch_size
# 2. 优化判别器
opt_d.zero_grad()
# 真实配对损失
pred_real = discriminator(he_imgs, ihc_imgs)
real_loss = adv_criterion(pred_real, real_labels)
# 虚假配对损失(冻结生成器)
with torch.no_grad():
fake_ihc = generator(he_imgs)
pred_fake = discriminator(he_imgs, fake_ihc)
fake_loss = adv_criterion(pred_fake, fake_labels)
d_loss = (real_loss + fake_loss) / 2
d_loss.backward()
opt_d.step()
train_d_loss += d_loss.item() * batch_size
# 验证阶段
generator.eval()
val_psnr, val_ssim = 0.0, 0.0
with torch.no_grad():
for he_imgs, ihc_imgs in val_loader:
he_imgs, ihc_imgs = he_imgs.to(device), ihc_imgs.to(device)
fake_ihc = generator(he_imgs)
# 反归一化到[0,1]
he_imgs = (he_imgs + 1) / 2
ihc_imgs = (ihc_imgs + 1) / 2
fake_ihc = (fake_ihc + 1) / 2
# 计算PSNR(峰值信噪比)和SSIM(结构相似性)
for i in range(batch_size):
real_np = ihc_imgs[i].permute(1, 2, 0).cpu().numpy()
fake_np = fake_ihc[i].permute(1, 2, 0).cpu().numpy()
val_psnr += peak_signal_noise_ratio(real_np, fake_np, data_range=1.0)
val_ssim += structural_similarity(
cv2.cvtColor((real_np*255).astype(np.uint8), cv2.COLOR_RGB2GRAY),
cv2.cvtColor((fake_np*255).astype(np.uint8), cv2.COLOR_RGB2GRAY),
data_range=255
)
# 平均指标
train_g_loss /= len(train_loader.dataset)
train_d_loss /= len(train_loader.dataset)
val_psnr /= len(val_loader.dataset)
val_ssim /= len(val_loader.dataset)
# 保存最优模型
if val_ssim > best_val_ssim:
best_val_ssim = val_ssim
torch.save(generator.state_dict(), "bci_pix2pix_best.pth")
print(f"Epoch {epoch+1}:保存最优模型(Val SSIM={val_ssim:.4f})")
# 打印日志
print(f"\nEpoch {epoch+1}/{num_epochs}")
print(f"训练:生成器损失={train_g_loss:.4f},判别器损失={train_d_loss:.4f}")
print(f"验证:PSNR={val_psnr:.2f},SSIM={val_ssim:.4f}")
# 6. 测试集评估(加载最优模型)
generator.load_state_dict(torch.load("bci_pix2pix_best.pth"))
generator.eval()
test_psnr, test_ssim = 0.0, 0.0
with torch.no_grad():
for he_imgs, ihc_imgs in test_loader:
he_imgs, ihc_imgs = he_imgs.to(device), ihc_imgs.to(device)
fake_ihc = generator(he_imgs)
# 反归一化与指标计算
ihc_imgs = (ihc_imgs + 1) / 2
fake_ihc = (fake_ihc + 1) / 2
for i in range(he_imgs.size(0)):
real_np = ihc_imgs[i].permute(1, 2, 0).cpu().numpy()
fake_np = fake_ihc[i].permute(1, 2, 0).cpu().numpy()
test_psnr += peak_signal_noise_ratio(real_np, fake_np, data_range=1.0)
test_ssim += structural_similarity(
cv2.cvtColor((real_np*255).astype(np.uint8), cv2.COLOR_RGB2GRAY),
cv2.cvtColor((fake_np*255).astype(np.uint8), cv2.COLOR_RGB2GRAY),
data_range=255
)
test_psnr /= len(test_loader.dataset)
test_ssim /= len(test_loader.dataset)
print(f"\n=== 测试集最终评估 ===")
print(f"平均PSNR:{test_psnr:.2f}(>30为优秀)")
print(f"平均SSIM:{test_ssim:.4f}(>0.8为优秀)")
关键说明:L1 损失权重设为 100 是核心 —— 生成模型需优先保证 “结构一致性”(如 IHC 棕染区域与 H&E 癌细胞位置匹配),避免 GAN 仅关注染色风格而生成 “非特异性棕染”;临床评估需额外加入 “病理医生盲评”(判断生成 IHC 的蛋白表达强度与真实值一致性),PSNR/SSIM 仅为工程指标,需结合临床标准验证。
3. 效果可视化与部署建议
- 效果可视化:
- 染色转换三列对比图:展示 “输入 H&E→生成 IHC→真实 IHC”,重点标注临床关键区域(如 HER2 阳性的细胞膜棕染、ER 阳性的细胞核棕染),直观验证生成精度;
验证报告
以下为卖家选择提供的数据验证报告:
