H&E β DAPI Virtual Staining (Pix2Pix UNet, HEMIT)
Virtual DAPI fluorescence prediction from brightfield H&E histopathology patches.
Model
- Architecture:
UNetGenerator(in_ch=3, out_ch=1, ngf=64)(paired Pix2Pix, 4-level UNet with skip connections). - Training: paired Pix2Pix (adversarial + L1), 512px patches, fp32 CPU.
- Dataset: HEMIT β split
3717 / 630 / 945(train/val/test). - Checkpoint: epoch 169 (best validation on the HEMIT split).
- File:
he2dapi_unet_epoch169.ptβ raw PyTorchstate_dictof the generator (46 tensors, ~24.7 MB).
Preprocessing / postprocessing
# H&E: uint8 RGB HWC -> float CHW in [-1, 1]
he_t = torch.from_numpy(he).permute(2, 0, 1).float() / 127.5 - 1.0
# Forward (model already outputs [0,1]):
with torch.no_grad():
dapi = model(he_t.unsqueeze(0)) # [1, 1, H, W] in [0, 1]
Usage
import torch, torch.nn as nn
from PIL import Image
import numpy as np
class UNetGenerator(nn.Module):
def __init__(self, in_ch=3, out_ch=1, ngf=64):
super().__init__()
self.enc1 = nn.Sequential(nn.Conv2d(in_ch, ngf, 4,2,1), nn.LeakyReLU(0.2))
self.enc2 = nn.Sequential(nn.Conv2d(ngf, ngf*2, 4,2,1), nn.BatchNorm2d(ngf*2), nn.LeakyReLU(0.2))
self.enc3 = nn.Sequential(nn.Conv2d(ngf*2, ngf*4, 4,2,1), nn.BatchNorm2d(ngf*4), nn.LeakyReLU(0.2))
self.enc4 = nn.Sequential(nn.Conv2d(ngf*4, ngf*8, 4,2,1), nn.BatchNorm2d(ngf*8), nn.LeakyReLU(0.2))
self.dec4 = nn.Sequential(nn.ConvTranspose2d(ngf*8, ngf*4, 4,2,1), nn.BatchNorm2d(ngf*4), nn.ReLU())
self.dec3 = nn.Sequential(nn.ConvTranspose2d(ngf*8, ngf*2, 4,2,1), nn.BatchNorm2d(ngf*2), nn.ReLU())
self.dec2 = nn.Sequential(nn.ConvTranspose2d(ngf*4, ngf, 4,2,1), nn.BatchNorm2d(ngf), nn.ReLU())
self.dec1 = nn.Sequential(nn.ConvTranspose2d(ngf*2, out_ch, 4,2,1), nn.Tanh())
def forward(self, x):
e1=self.enc1(x); e2=self.enc2(e1); e3=self.enc3(e2); e4=self.enc4(e3)
d4=self.dec4(e4); d3=self.dec3(torch.cat([d4,e3],1)); d2=self.dec2(torch.cat([d3,e2],1))
d1=self.dec1(torch.cat([d2,e1],1)); return (d1+1)/2
model = UNetGenerator()
model.load_state_dict(torch.load("he2dapi_unet_epoch169.pt", map_location="cpu"))
model.eval()
he = np.array(Image.open("he.png").convert("RGB"))
t = torch.from_numpy(he).permute(2,0,1).float()/127.5 - 1.0
with torch.no_grad():
dapi = model(t.unsqueeze(0))[0,0].clamp(0,1).numpy()
Image.fromarray((dapi*255).astype("uint8")).save("dapi_pred.png")
Source / training code
https://github.com/urbiologicalpa/he-dapi-translator
Research demonstration only. Not validated for clinical or diagnostic use.