srikarym commited on
Commit
20b7c7c
·
verified ·
1 Parent(s): 89328c8

Add files using upload-large-folder tool

Browse files
README.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-nc-nd-4.0
3
+ library_name: diffusers
4
+ tags:
5
+ - histopathology
6
+ - whole-slide-image
7
+ - flow-matching
8
+ ---
9
+
10
+ # BigCell: Generating Gigapixel Whole-Slide Images
11
+
12
+ [[📄 arXiv]](https://arxiv.org/abs/2610.14261) [[GitHub]](https://github.com/cvlab-stonybrook/BigCell) [[💾 Sample data]](https://huggingface.co/datasets/StonyBrook-CVLab/BigCell-sample-data)
13
+
14
+ BigCell is a flow-matching transformer (SiT-XL/2, 845M parameters) that generates histopathology images at 20×.
15
+ It is conditioned on a dense grid of [UNI-2h](https://huggingface.co/MahmoodLab/UNI2-h) embeddings, with one
16
+ 1536-d token per 256×256 px. The model works in the latent space of the FLUX.2 VAE.
17
+
18
+ A native 2048×2048 tile generator, used with an inference-time sliding window, produces coherent images of up to
19
+ 32,768×32,768 px. A separate conditioning generation model (CGM) samples the UNI-2 grid from a pathology report,
20
+ for text-to-image generation at 8192×8192.
21
+
22
+ | Folder | Content | Native tile | UNI-2 tokens / tile |
23
+ |---|---|---|---|
24
+ | `bigcell-256` | BigCell tile generator, progressive-training stage 1 | 256 px | 1 |
25
+ | `bigcell-1024` | stage 3 | 1024 px | 4×4 |
26
+ | `bigcell-2048` | **final model** (used in the paper) | 2048 px | 8×8 |
27
+ | `cgm-8192` | CGM (DiT-DH-B): TITAN text → 32×32×1536 UNI-2 grid of an 8192 px region | – | – |
28
+
29
+ Each `bigcell-*` folder is a diffusers pipeline (`BigCellPipeline`).
30
+
31
+ ## Usage
32
+
33
+ The FLUX.2 VAE weights are not hosted here. Accept the licence of
34
+ [black-forest-labs/FLUX.2-dev](https://huggingface.co/black-forest-labs/FLUX.2-dev) and load the VAE from there.
35
+ Requires `diffusers>=0.36`.
36
+
37
+ ```python
38
+ import torch
39
+ from diffusers import AutoencoderKLFlux2, DiffusionPipeline
40
+ from huggingface_hub import snapshot_download
41
+
42
+ name = "bigcell-2048"
43
+ path = f"{snapshot_download('StonyBrook-CVLab/BigCell', allow_patterns=f'{name}/*')}/{name}"
44
+
45
+ vae = AutoencoderKLFlux2.from_pretrained("black-forest-labs/FLUX.2-dev", subfolder="vae", torch_dtype=torch.bfloat16)
46
+ pipe = DiffusionPipeline.from_pretrained(path, vae=vae, custom_pipeline=path, trust_remote_code=True,
47
+ torch_dtype=torch.bfloat16).to("cuda")
48
+ ```
49
+
50
+ ### Generate from a UNI-2 grid
51
+
52
+ The pipeline takes a UNI-2 grid of shape `(B, Hg, Wg, 1536)` and returns a `(256·Hg) × (256·Wg)` image.
53
+
54
+ - A grid of exactly one native tile (8×8 for `bigcell-2048`) is denoised directly. Tokens of shape
55
+ `(B, T, 1536)` in raster order are also accepted.
56
+ - Larger grids are generated with the sliding-window procedure of the paper. Set `top_r < 1` for adaptive window
57
+ scheduling: only the fraction `top_r` of windows with the largest classifier-free-guidance magnitude is
58
+ recomputed after the first step.
59
+
60
+ ```python
61
+ import numpy as np
62
+ from huggingface_hub import hf_hub_download
63
+
64
+ # 32×32 UNI-2 grid of a real 8192×8192 region from the sample data
65
+ f = hf_hub_download("StonyBrook-CVLab/BigCell-sample-data", repo_type="dataset",
66
+ filename="cptac/AML/C3L-00453-41_x0_y0/8192/0_uni.npy")
67
+ grid = torch.from_numpy(np.load(f)).float()[None] # (1, 32, 32, 1536)
68
+
69
+ image = pipe(grid, num_inference_steps=20, guidance_scale=2.0, top_r=0.25,
70
+ generator=torch.Generator("cuda").manual_seed(0)).images[0] # 8192×8192 PIL image
71
+ ```
72
+
73
+ `pipe.get_unconditional_embedding(B)` returns the learned null condition, for unconditional samples.
74
+
75
+ ### Extract a UNI-2 grid from your own image
76
+
77
+ ```python
78
+ import timm, einops
79
+ from PIL import Image
80
+ from timm.data import resolve_data_config
81
+ from timm.data.transforms_factory import create_transform
82
+
83
+ timm_kwargs = dict(img_size=224, patch_size=14, depth=24, num_heads=24, init_values=1e-5, embed_dim=1536,
84
+ mlp_ratio=2.66667 * 2, num_classes=0, no_embed_class=True, mlp_layer=timm.layers.SwiGLUPacked,
85
+ act_layer=torch.nn.SiLU, reg_tokens=8, dynamic_img_size=True)
86
+ uni = timm.create_model("hf-hub:MahmoodLab/UNI2-h", pretrained=True, **timm_kwargs).eval().cuda()
87
+ transform = create_transform(**resolve_data_config(uni.pretrained_cfg, model=uni))
88
+
89
+ img = np.array(Image.open("region_20x.png").convert("RGB")) # H×W, multiples of 256, at 20×
90
+ hg, wg = img.shape[0] // 256, img.shape[1] // 256
91
+ patches = einops.rearrange(img, "(hg h) (wg w) c -> (hg wg) h w c", h=256, w=256)
92
+ with torch.inference_mode():
93
+ feats = torch.cat([uni(torch.stack([transform(Image.fromarray(p)) for p in patches[i:i + 256]]).cuda())
94
+ for i in range(0, len(patches), 256)])
95
+ grid = feats.reshape(1, hg, wg, 1536).float().cpu()
96
+ ```
97
+
98
+ ### Multi-GPU 32k generation and text-to-image
99
+
100
+ Gigapixel generation with windows sharded across GPUs, text-to-image with the CGM, and CGM-based data
101
+ augmentation are in the [GitHub repository](https://github.com/cvlab-stonybrook/BigCell):
102
+
103
+ ```bash
104
+ torchrun --nproc_per_node 4 scripts/generate_from_grid.py --grid .../32768/0_uni.npy --top-r 0.25 --out 32k.jpg
105
+ python scripts/generate_from_text.py --prompt "Pancreatic ductal adenocarcinoma ..." --out out/
106
+ ```
107
+
108
+ ## Sampling settings used in the paper
109
+
110
+ - BigCell: 20 Euler steps, CFG 2.0, 2048 px windows with a 1024 px stride, and Gaussian window weighting.
111
+ - The time shift is stored in `transformer/config.json` (`alpha_shift`) and `scheduler/scheduler_config.json`
112
+ (`shift`). It is 3.0 for `bigcell-1024` / `bigcell-2048` and 1.0 for `bigcell-256`.
113
+ - CGM: 50-point Euler schedule with time shift √(32·32·1536/4096), CFG 2.0, and TITAN text tokens (unpooled,
114
+ 127×768) as the condition.
115
+
116
+ ## Citation
117
+
118
+ ```bibtex
119
+ @misc{yellapragada2026bigcell,
120
+ title={BigCell: Generating Gigapixel Whole-Slide Images},
121
+ author={Srikar Yellapragada and Alexandros Graikos and Zilinghan Li and Kostas Triaridis and Tarak Nath Nandi and Ji Dong K Bai and Beatrice Knudsen and Tahsin Kurc and Prateek Prasanna and Rajarsi R. Gupta and Ravi K Madduri and Joel Saltz and Dimitris Samaras},
122
+ year={2026},
123
+ eprint={2610.14261},
124
+ archivePrefix={arXiv},
125
+ primaryClass={cs.CV},
126
+ url={https://arxiv.org/abs/2610.14261},
127
+ }
128
+ ```
129
+
130
+ ## License & Usage
131
+
132
+ **License**: Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)
133
+
134
+ **Notice**: These models are derivative works conditioned on embeddings from the
135
+ [UNI-2h](https://huggingface.co/MahmoodLab/UNI2-h) foundation model and, for the CGM, the
136
+ [TITAN](https://huggingface.co/MahmoodLab/TITAN) text encoder. They are subject to the original terms of those
137
+ licences. The FLUX.2 VAE is governed by its own licence.
138
+
139
+ - Academic and research use only: you may use these weights for non-commercial research purposes.
140
+ - No commercial use, including product development or commercial services.
141
+ - Generated images must not be used for clinical diagnosis.
bigcell-1024/model_index.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": [
3
+ "pipeline",
4
+ "BigCellPipeline"
5
+ ],
6
+ "_diffusers_version": "0.37.1",
7
+ "transformer": [
8
+ "bigcell_transformer_2d",
9
+ "BigCellTransformer2DModel"
10
+ ],
11
+ "vae": [
12
+ "diffusers",
13
+ "AutoencoderKLFlux2"
14
+ ],
15
+ "scheduler": [
16
+ "diffusers",
17
+ "FlowMatchEulerDiscreteScheduler"
18
+ ]
19
+ }
bigcell-1024/pipeline.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell pipeline. https://github.com/cvlab-stonybrook/BigCell
2
+ #
3
+ # Generates histopathology images from a grid of UNI-2 embeddings (one 1536-d token per 256x256 px at 20x).
4
+ # A grid that matches the model's native tile (e.g. 8x8 for BigCell-2048) is denoised directly; larger grids
5
+ # are generated with the sliding-window procedure of the paper (BigCell-large), optionally with adaptive
6
+ # window scheduling (``top_r < 1``). For multi-GPU generation of 16k-32k images use the GitHub scripts.
7
+
8
+ import math
9
+ from typing import List, Optional, Union
10
+
11
+ import numpy as np
12
+ import torch
13
+ from diffusers import DiffusionPipeline
14
+ from diffusers.image_processor import VaeImageProcessor
15
+ from diffusers.pipelines.pipeline_utils import ImagePipelineOutput
16
+ from diffusers.utils.torch_utils import randn_tensor
17
+ from PIL import Image
18
+
19
+ LATENTS_PER_TOKEN = 32 # one UNI-2 token covers 256 px = 32 Flux.2 latents
20
+
21
+
22
+ def _gaussian_kernel(size, sigma, device):
23
+ x = torch.linspace(-1, 1, size)
24
+ x = torch.stack((x.tile(size, 1), x.tile(size, 1).T), dim=0)
25
+ k = torch.exp(-(torch.linalg.norm(x, dim=0) ** 2) / (2 * sigma**2))
26
+ return (k / k.max()).to(device)
27
+
28
+
29
+ class BigCellPipeline(DiffusionPipeline):
30
+ model_cpu_offload_seq = "transformer->vae"
31
+
32
+ def __init__(self, transformer, vae, scheduler):
33
+ super().__init__()
34
+ self.register_modules(transformer=transformer, vae=vae, scheduler=scheduler)
35
+ self.image_processor = VaeImageProcessor(vae_scale_factor=8)
36
+
37
+ @property
38
+ def tile_tokens(self):
39
+ """Side of the UNI-2 grid covered by one native tile (1, 4 or 8)."""
40
+ return int(round(math.sqrt(self.transformer.config.num_caption_tokens)))
41
+
42
+ def get_unconditional_embedding(self, batch_size=1):
43
+ return self.transformer.unconditional_embedding.tile(batch_size, 1, 1)
44
+
45
+ def _velocity(self, x, t, y, guidance_scale):
46
+ """Returns (cfg velocity, importance score |v_cond - v_uncond|)."""
47
+ null = self.get_unconditional_embedding(x.shape[0]).to(y.dtype)
48
+ v = self.transformer(torch.cat([x, x]), t.expand(2 * x.shape[0]), torch.cat([y, null])).float()
49
+ v_cond, v_uncond = v.chunk(2)
50
+ return v_uncond + guidance_scale * (v_cond - v_uncond), (v_cond - v_uncond).abs().mean().item()
51
+
52
+ def _sigmas(self, num_inference_steps, device):
53
+ self.scheduler.set_timesteps(sigmas=np.linspace(1.0, 0.0, num_inference_steps + 1)[:-1], device=device)
54
+ return self.scheduler.sigmas.to(device) # shifted, ends with 0
55
+
56
+ @torch.no_grad()
57
+ def _decode(self, latents, tile=256, sigma=0.5):
58
+ """Tiled decode for large canvases (Gaussian-blended, accumulated on CPU)."""
59
+ _, _, h, w = latents.shape
60
+ dtype = self.vae.dtype
61
+ if h <= tile and w <= tile:
62
+ return self.vae.decode(latents.to(dtype)).sample.float().cpu()
63
+ out = torch.zeros(latents.shape[0], 3, h * 8, w * 8)
64
+ cnt = torch.zeros(1, 1, h * 8, w * 8)
65
+ kernel = _gaussian_kernel(tile * 8, sigma, "cpu")[None, None]
66
+ stride = tile // 2
67
+ for i in range(0, h - tile + 1, stride):
68
+ for j in range(0, w - tile + 1, stride):
69
+ dec = self.vae.decode(latents[:, :, i:i + tile, j:j + tile].to(dtype)).sample.float().cpu()
70
+ out[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += dec * kernel
71
+ cnt[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += kernel
72
+ return out / cnt.clamp(min=1e-6)
73
+
74
+ @torch.no_grad()
75
+ def __call__(
76
+ self,
77
+ uni_embeds: torch.Tensor,
78
+ num_inference_steps: int = 20,
79
+ guidance_scale: float = 2.0,
80
+ top_r: float = 1.0,
81
+ stride: Optional[int] = None,
82
+ window_sigma: float = 0.5,
83
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
84
+ latents: Optional[torch.Tensor] = None,
85
+ output_type: str = "pil",
86
+ return_dict: bool = True,
87
+ progress: bool = True,
88
+ ):
89
+ """
90
+ Args:
91
+ uni_embeds: UNI-2 grid of shape (B, Hg, Wg, 1536), or tokens of one native tile (B, T, 1536).
92
+ The output image is (256 * Hg) x (256 * Wg) pixels.
93
+ num_inference_steps: Euler steps (paper: 20).
94
+ guidance_scale: classifier-free guidance scale (paper: 2.0).
95
+ top_r: fraction of sliding windows recomputed at every step after the first (paper Sec. 3.3);
96
+ the rest reuse their cached velocity. 1.0 = full computation.
97
+ stride: window stride in latents (default: half a window, e.g. 128 = 1024 px for BigCell-2048).
98
+ window_sigma: width of the Gaussian window weighting.
99
+ output_type: "pil", "np" (uint8 HxWx3) or "latent".
100
+ """
101
+ device = self._execution_device
102
+ dtype = self.transformer.dtype
103
+ tok = self.tile_tokens
104
+ if uni_embeds.ndim == 3: # (B, T, C) tokens of one tile -> grid
105
+ uni_embeds = uni_embeds.reshape(uni_embeds.shape[0], tok, tok, -1)
106
+ B, Hg, Wg, _ = uni_embeds.shape
107
+ if Hg < tok or Wg < tok:
108
+ raise ValueError(f"grid {Hg}x{Wg} is smaller than one tile ({tok}x{tok})")
109
+ uni_embeds = uni_embeds.to(device, dtype)
110
+
111
+ win = self.transformer.config.sample_size
112
+ H, W = Hg * LATENTS_PER_TOKEN, Wg * LATENTS_PER_TOKEN
113
+ stride = stride or win // 2
114
+ windows = [(h, w) for h in range(0, H - win + 1, stride) for w in range(0, W - win + 1, stride)]
115
+ kernel = _gaussian_kernel(win, window_sigma, device)
116
+
117
+ if latents is None:
118
+ latents = randn_tensor((B, self.transformer.config.in_channels, H, W), generator=generator,
119
+ device=device, dtype=torch.float32)
120
+ x = latents.to(device, torch.float32)
121
+ sigmas = self._sigmas(num_inference_steps, device)
122
+
123
+ n_select = len(windows) if top_r >= 1 else max(1, math.ceil(top_r * len(windows)))
124
+ importance = [dict() for _ in range(B)]
125
+ v_prev = torch.zeros_like(x)
126
+ with self.progress_bar(total=num_inference_steps) as pbar:
127
+ for step, (s_cur, s_next) in enumerate(zip(sigmas[:-1], sigmas[1:])):
128
+ t = s_cur.reshape(1).to(dtype)
129
+ v_new = v_prev.clone()
130
+ for b in range(B):
131
+ active = windows if step == 0 else sorted(windows, key=lambda w: -importance[b].get(w, 0.0))[:n_select]
132
+ v_sum = torch.zeros_like(x[b:b + 1])
133
+ w_sum = torch.zeros_like(x[b:b + 1])
134
+ for h, w in active:
135
+ y = uni_embeds[b:b + 1, h // 32:h // 32 + tok, w // 32:w // 32 + tok].reshape(1, tok * tok, -1)
136
+ v, score = self._velocity(x[b:b + 1, :, h:h + win, w:w + win].to(dtype), t, y, guidance_scale)
137
+ importance[b][(h, w)] = score
138
+ v_sum[:, :, h:h + win, w:w + win] += v * kernel
139
+ w_sum[:, :, h:h + win, w:w + win] += kernel
140
+ v_new[b:b + 1] = torch.where(w_sum > 0, v_sum / w_sum.clamp(min=1e-6), v_prev[b:b + 1])
141
+ v_prev = v_new
142
+ x = x + (s_next - s_cur) * v_new
143
+ if progress:
144
+ pbar.update()
145
+
146
+ if output_type == "latent":
147
+ return ImagePipelineOutput(images=x) if return_dict else (x,)
148
+ img = ((self._decode(x) + 1) / 2).clamp(0, 1)
149
+ img = (img.permute(0, 2, 3, 1) * 255).round().to(torch.uint8).numpy()
150
+ images = [Image.fromarray(i) for i in img] if output_type == "pil" else img
151
+ return ImagePipelineOutput(images=images) if return_dict else (images,)
bigcell-1024/scheduler/scheduler_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "FlowMatchEulerDiscreteScheduler",
3
+ "_diffusers_version": "0.37.1",
4
+ "base_image_seq_len": 256,
5
+ "base_shift": 0.5,
6
+ "invert_sigmas": false,
7
+ "max_image_seq_len": 4096,
8
+ "max_shift": 1.15,
9
+ "num_train_timesteps": 1000,
10
+ "shift": 3.0,
11
+ "shift_terminal": null,
12
+ "stochastic_sampling": false,
13
+ "time_shift_type": "exponential",
14
+ "use_beta_sigmas": false,
15
+ "use_dynamic_shifting": false,
16
+ "use_exponential_sigmas": false,
17
+ "use_karras_sigmas": false
18
+ }
bigcell-1024/transformer/bigcell_transformer_2d.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell tile generator: a SiT flow-matching transformer conditioned on a grid of UNI-2 embeddings
2
+ # through cross-attention. https://github.com/cvlab-stonybrook/BigCell
3
+ #
4
+ # Adapted from SiT (https://github.com/willisma/SiT, MIT) and REPA-E (https://github.com/End2End-Diffusion/REPA-E, MIT).
5
+
6
+ import math
7
+
8
+ import numpy as np
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
13
+ from diffusers.models.modeling_utils import ModelMixin
14
+
15
+
16
+ def modulate(x, shift, scale):
17
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
18
+
19
+
20
+ class PatchEmbed(nn.Module):
21
+ def __init__(self, patch_size, in_channels, hidden_size):
22
+ super().__init__()
23
+ self.proj = nn.Conv2d(in_channels, hidden_size, kernel_size=patch_size, stride=patch_size, bias=True)
24
+
25
+ def forward(self, x):
26
+ return self.proj(x).flatten(2).transpose(1, 2)
27
+
28
+
29
+ class Mlp(nn.Module):
30
+ def __init__(self, in_features, hidden_features, out_features=None, approximate="none"):
31
+ super().__init__()
32
+ self.fc1 = nn.Linear(in_features, hidden_features)
33
+ self.act = nn.GELU(approximate=approximate)
34
+ self.fc2 = nn.Linear(hidden_features, out_features or in_features)
35
+
36
+ def forward(self, x):
37
+ return self.fc2(self.act(self.fc1(x)))
38
+
39
+
40
+ class Attention(nn.Module):
41
+ def __init__(self, dim, num_heads):
42
+ super().__init__()
43
+ self.num_heads = num_heads
44
+ self.qkv = nn.Linear(dim, dim * 3, bias=True)
45
+ self.proj = nn.Linear(dim, dim)
46
+
47
+ def forward(self, x):
48
+ B, N, C = x.shape
49
+ q, k, v = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4).unbind(0)
50
+ x = F.scaled_dot_product_attention(q, k, v)
51
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
52
+
53
+
54
+ class CrossAttention(nn.Module):
55
+ def __init__(self, dim, num_heads):
56
+ super().__init__()
57
+ self.num_heads = num_heads
58
+ self.q_linear = nn.Linear(dim, dim)
59
+ self.kv_linear = nn.Linear(dim, dim * 2)
60
+ self.proj = nn.Linear(dim, dim)
61
+
62
+ def forward(self, x, cond):
63
+ B, N, C = x.shape
64
+ h = self.num_heads
65
+ q = self.q_linear(x).reshape(B, N, h, C // h).transpose(1, 2)
66
+ k, v = self.kv_linear(cond).reshape(B, cond.shape[1], 2, h, C // h).permute(2, 0, 3, 1, 4).unbind(0)
67
+ x = F.scaled_dot_product_attention(q, k, v)
68
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
69
+
70
+
71
+ class TimestepEmbedder(nn.Module):
72
+ def __init__(self, hidden_size, frequency_embedding_size=256):
73
+ super().__init__()
74
+ self.mlp = nn.Sequential(
75
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
76
+ nn.SiLU(),
77
+ nn.Linear(hidden_size, hidden_size, bias=True),
78
+ )
79
+ self.frequency_embedding_size = frequency_embedding_size
80
+
81
+ def forward(self, t):
82
+ half = self.frequency_embedding_size // 2
83
+ freqs = torch.exp(-math.log(10000) * torch.arange(half, dtype=torch.float32, device=t.device) / half)
84
+ args = t[:, None].float() * freqs[None]
85
+ return self.mlp(torch.cat([torch.cos(args), torch.sin(args)], dim=-1).to(t.dtype))
86
+
87
+
88
+ class CaptionEmbedder(nn.Module):
89
+ """Projects UNI-2 tokens; ``y_embedding`` is the learned unconditional embedding used for CFG."""
90
+
91
+ def __init__(self, in_channels, hidden_size, token_num):
92
+ super().__init__()
93
+ self.y_proj = Mlp(in_channels, hidden_size, hidden_size, approximate="tanh")
94
+ self.register_buffer("y_embedding", torch.randn(token_num, in_channels) / in_channels**0.5)
95
+
96
+ def forward(self, caption):
97
+ return self.y_proj(caption)
98
+
99
+
100
+ class BigCellBlock(nn.Module):
101
+ def __init__(self, hidden_size, num_heads, mlp_ratio=4.0):
102
+ super().__init__()
103
+ self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
104
+ self.attn = Attention(hidden_size, num_heads)
105
+ self.cross_attn = CrossAttention(hidden_size, num_heads)
106
+ self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
107
+ self.mlp = Mlp(hidden_size, int(hidden_size * mlp_ratio), approximate="tanh")
108
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size, bias=True))
109
+
110
+ def forward(self, x, cond, t):
111
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(t).chunk(6, dim=-1)
112
+ x = x + gate_msa.unsqueeze(1) * self.attn(modulate(self.norm1(x), shift_msa, scale_msa))
113
+ x = x + self.cross_attn(x, cond)
114
+ x = x + gate_mlp.unsqueeze(1) * self.mlp(modulate(self.norm2(x), shift_mlp, scale_mlp))
115
+ return x
116
+
117
+
118
+ class FinalLayer(nn.Module):
119
+ def __init__(self, hidden_size, patch_size, out_channels):
120
+ super().__init__()
121
+ self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
122
+ self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
123
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
124
+
125
+ def forward(self, x, c):
126
+ shift, scale = self.adaLN_modulation(c).chunk(2, dim=-1)
127
+ return self.linear(modulate(self.norm_final(x), shift, scale))
128
+
129
+
130
+ class BigCellTransformer2DModel(ModelMixin, ConfigMixin):
131
+ """SiT-XL/2 velocity predictor operating on Flux.2 VAE latents (32 channels, 8x downsampling).
132
+
133
+ Args:
134
+ sample_size: latent side length (tile resolution // 8).
135
+ num_caption_tokens: number of UNI-2 tokens per tile, (tile resolution // 256) ** 2.
136
+ alpha_shift: time shift used during training; the scheduler uses the same value.
137
+ """
138
+
139
+ @register_to_config
140
+ def __init__(
141
+ self,
142
+ sample_size: int = 256,
143
+ patch_size: int = 2,
144
+ in_channels: int = 32,
145
+ hidden_size: int = 1152,
146
+ num_layers: int = 28,
147
+ num_attention_heads: int = 16,
148
+ mlp_ratio: float = 4.0,
149
+ caption_channels: int = 1536,
150
+ num_caption_tokens: int = 64,
151
+ alpha_shift: float = 1.0,
152
+ ):
153
+ super().__init__()
154
+ self.x_embedder = PatchEmbed(patch_size, in_channels, hidden_size)
155
+ self.t_embedder = TimestepEmbedder(hidden_size)
156
+ self.y_embedder = CaptionEmbedder(caption_channels, hidden_size, num_caption_tokens)
157
+ num_patches = (sample_size // patch_size) ** 2
158
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size), requires_grad=False)
159
+ grid = int(num_caption_tokens**0.5)
160
+ if num_caption_tokens > 1 and grid**2 == num_caption_tokens:
161
+ self.y_pos_embed = nn.Parameter(torch.zeros(1, num_caption_tokens, hidden_size), requires_grad=False)
162
+ else:
163
+ self.y_pos_embed = None
164
+ self.blocks = nn.ModuleList(
165
+ [BigCellBlock(hidden_size, num_attention_heads, mlp_ratio) for _ in range(num_layers)]
166
+ )
167
+ self.final_layer = FinalLayer(hidden_size, patch_size, in_channels)
168
+ pos = _sincos_2d(hidden_size, np.stack(np.meshgrid(np.arange(sample_size // patch_size),
169
+ np.arange(sample_size // patch_size)), 0).reshape(2, -1))
170
+ self.pos_embed.data.copy_(torch.from_numpy(pos).float()[None])
171
+
172
+ @property
173
+ def unconditional_embedding(self):
174
+ """(1, T, 1536) learned null condition for classifier-free guidance."""
175
+ return self.y_embedder.y_embedding[None]
176
+
177
+ def unpatchify(self, x):
178
+ c, p = self.config.in_channels, self.config.patch_size
179
+ h = w = int(x.shape[1] ** 0.5)
180
+ x = torch.einsum("nhwpqc->nchpwq", x.reshape(x.shape[0], h, w, p, p, c))
181
+ return x.reshape(x.shape[0], c, h * p, w * p)
182
+
183
+ def forward(self, hidden_states, timestep, encoder_hidden_states):
184
+ """hidden_states: (B, 32, H, W) noisy latent; timestep: (B,) in [0, 1] (1 = pure noise);
185
+ encoder_hidden_states: (B, T, 1536) UNI-2 tokens in raster order. Returns the velocity (noise - data)."""
186
+ x = self.x_embedder(hidden_states) + self.pos_embed
187
+ t = self.t_embedder(timestep)
188
+ y = self.y_embedder(encoder_hidden_states)
189
+ if self.y_pos_embed is not None:
190
+ y = y + self.y_pos_embed
191
+ for block in self.blocks:
192
+ x = block(x, y, t)
193
+ return self.unpatchify(self.final_layer(x, t))
194
+
195
+
196
+ def _sincos_2d(embed_dim, grid):
197
+ def one_d(dim, pos):
198
+ omega = 1.0 / 10000 ** (np.arange(dim // 2, dtype=np.float64) / (dim / 2.0))
199
+ out = np.einsum("m,d->md", pos.reshape(-1), omega)
200
+ return np.concatenate([np.sin(out), np.cos(out)], axis=1)
201
+
202
+ return np.concatenate([one_d(embed_dim // 2, grid[0]), one_d(embed_dim // 2, grid[1])], axis=1)
bigcell-1024/transformer/config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BigCellTransformer2DModel",
3
+ "_diffusers_version": "0.37.1",
4
+ "alpha_shift": 3.0,
5
+ "caption_channels": 1536,
6
+ "hidden_size": 1152,
7
+ "in_channels": 32,
8
+ "mlp_ratio": 4.0,
9
+ "num_attention_heads": 16,
10
+ "num_caption_tokens": 16,
11
+ "num_layers": 28,
12
+ "patch_size": 2,
13
+ "sample_size": 128
14
+ }
bigcell-1024/transformer/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7e13cd2c48e9a4e7673e1cc9da626ee9842e5abbb22ba7b6036993d5015567b1
3
+ size 3322233744
bigcell-1024/vae/config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "AutoencoderKLFlux2",
3
+ "_diffusers_version": "0.36.0.dev0",
4
+ "act_fn": "silu",
5
+ "batch_norm_eps": 0.0001,
6
+ "batch_norm_momentum": 0.1,
7
+ "block_out_channels": [
8
+ 128,
9
+ 256,
10
+ 512,
11
+ 512
12
+ ],
13
+ "down_block_types": [
14
+ "DownEncoderBlock2D",
15
+ "DownEncoderBlock2D",
16
+ "DownEncoderBlock2D",
17
+ "DownEncoderBlock2D"
18
+ ],
19
+ "force_upcast": true,
20
+ "in_channels": 3,
21
+ "latent_channels": 32,
22
+ "layers_per_block": 2,
23
+ "mid_block_add_attention": true,
24
+ "norm_num_groups": 32,
25
+ "out_channels": 3,
26
+ "patch_size": [
27
+ 2,
28
+ 2
29
+ ],
30
+ "sample_size": 1024,
31
+ "up_block_types": [
32
+ "UpDecoderBlock2D",
33
+ "UpDecoderBlock2D",
34
+ "UpDecoderBlock2D",
35
+ "UpDecoderBlock2D"
36
+ ],
37
+ "use_post_quant_conv": true,
38
+ "use_quant_conv": true,
39
+ "_name_or_path": "black-forest-labs/FLUX.2-dev"
40
+ }
bigcell-2048/model_index.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": [
3
+ "pipeline",
4
+ "BigCellPipeline"
5
+ ],
6
+ "_diffusers_version": "0.37.1",
7
+ "transformer": [
8
+ "bigcell_transformer_2d",
9
+ "BigCellTransformer2DModel"
10
+ ],
11
+ "vae": [
12
+ "diffusers",
13
+ "AutoencoderKLFlux2"
14
+ ],
15
+ "scheduler": [
16
+ "diffusers",
17
+ "FlowMatchEulerDiscreteScheduler"
18
+ ]
19
+ }
bigcell-2048/pipeline.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell pipeline. https://github.com/cvlab-stonybrook/BigCell
2
+ #
3
+ # Generates histopathology images from a grid of UNI-2 embeddings (one 1536-d token per 256x256 px at 20x).
4
+ # A grid that matches the model's native tile (e.g. 8x8 for BigCell-2048) is denoised directly; larger grids
5
+ # are generated with the sliding-window procedure of the paper (BigCell-large), optionally with adaptive
6
+ # window scheduling (``top_r < 1``). For multi-GPU generation of 16k-32k images use the GitHub scripts.
7
+
8
+ import math
9
+ from typing import List, Optional, Union
10
+
11
+ import numpy as np
12
+ import torch
13
+ from diffusers import DiffusionPipeline
14
+ from diffusers.image_processor import VaeImageProcessor
15
+ from diffusers.pipelines.pipeline_utils import ImagePipelineOutput
16
+ from diffusers.utils.torch_utils import randn_tensor
17
+ from PIL import Image
18
+
19
+ LATENTS_PER_TOKEN = 32 # one UNI-2 token covers 256 px = 32 Flux.2 latents
20
+
21
+
22
+ def _gaussian_kernel(size, sigma, device):
23
+ x = torch.linspace(-1, 1, size)
24
+ x = torch.stack((x.tile(size, 1), x.tile(size, 1).T), dim=0)
25
+ k = torch.exp(-(torch.linalg.norm(x, dim=0) ** 2) / (2 * sigma**2))
26
+ return (k / k.max()).to(device)
27
+
28
+
29
+ class BigCellPipeline(DiffusionPipeline):
30
+ model_cpu_offload_seq = "transformer->vae"
31
+
32
+ def __init__(self, transformer, vae, scheduler):
33
+ super().__init__()
34
+ self.register_modules(transformer=transformer, vae=vae, scheduler=scheduler)
35
+ self.image_processor = VaeImageProcessor(vae_scale_factor=8)
36
+
37
+ @property
38
+ def tile_tokens(self):
39
+ """Side of the UNI-2 grid covered by one native tile (1, 4 or 8)."""
40
+ return int(round(math.sqrt(self.transformer.config.num_caption_tokens)))
41
+
42
+ def get_unconditional_embedding(self, batch_size=1):
43
+ return self.transformer.unconditional_embedding.tile(batch_size, 1, 1)
44
+
45
+ def _velocity(self, x, t, y, guidance_scale):
46
+ """Returns (cfg velocity, importance score |v_cond - v_uncond|)."""
47
+ null = self.get_unconditional_embedding(x.shape[0]).to(y.dtype)
48
+ v = self.transformer(torch.cat([x, x]), t.expand(2 * x.shape[0]), torch.cat([y, null])).float()
49
+ v_cond, v_uncond = v.chunk(2)
50
+ return v_uncond + guidance_scale * (v_cond - v_uncond), (v_cond - v_uncond).abs().mean().item()
51
+
52
+ def _sigmas(self, num_inference_steps, device):
53
+ self.scheduler.set_timesteps(sigmas=np.linspace(1.0, 0.0, num_inference_steps + 1)[:-1], device=device)
54
+ return self.scheduler.sigmas.to(device) # shifted, ends with 0
55
+
56
+ @torch.no_grad()
57
+ def _decode(self, latents, tile=256, sigma=0.5):
58
+ """Tiled decode for large canvases (Gaussian-blended, accumulated on CPU)."""
59
+ _, _, h, w = latents.shape
60
+ dtype = self.vae.dtype
61
+ if h <= tile and w <= tile:
62
+ return self.vae.decode(latents.to(dtype)).sample.float().cpu()
63
+ out = torch.zeros(latents.shape[0], 3, h * 8, w * 8)
64
+ cnt = torch.zeros(1, 1, h * 8, w * 8)
65
+ kernel = _gaussian_kernel(tile * 8, sigma, "cpu")[None, None]
66
+ stride = tile // 2
67
+ for i in range(0, h - tile + 1, stride):
68
+ for j in range(0, w - tile + 1, stride):
69
+ dec = self.vae.decode(latents[:, :, i:i + tile, j:j + tile].to(dtype)).sample.float().cpu()
70
+ out[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += dec * kernel
71
+ cnt[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += kernel
72
+ return out / cnt.clamp(min=1e-6)
73
+
74
+ @torch.no_grad()
75
+ def __call__(
76
+ self,
77
+ uni_embeds: torch.Tensor,
78
+ num_inference_steps: int = 20,
79
+ guidance_scale: float = 2.0,
80
+ top_r: float = 1.0,
81
+ stride: Optional[int] = None,
82
+ window_sigma: float = 0.5,
83
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
84
+ latents: Optional[torch.Tensor] = None,
85
+ output_type: str = "pil",
86
+ return_dict: bool = True,
87
+ progress: bool = True,
88
+ ):
89
+ """
90
+ Args:
91
+ uni_embeds: UNI-2 grid of shape (B, Hg, Wg, 1536), or tokens of one native tile (B, T, 1536).
92
+ The output image is (256 * Hg) x (256 * Wg) pixels.
93
+ num_inference_steps: Euler steps (paper: 20).
94
+ guidance_scale: classifier-free guidance scale (paper: 2.0).
95
+ top_r: fraction of sliding windows recomputed at every step after the first (paper Sec. 3.3);
96
+ the rest reuse their cached velocity. 1.0 = full computation.
97
+ stride: window stride in latents (default: half a window, e.g. 128 = 1024 px for BigCell-2048).
98
+ window_sigma: width of the Gaussian window weighting.
99
+ output_type: "pil", "np" (uint8 HxWx3) or "latent".
100
+ """
101
+ device = self._execution_device
102
+ dtype = self.transformer.dtype
103
+ tok = self.tile_tokens
104
+ if uni_embeds.ndim == 3: # (B, T, C) tokens of one tile -> grid
105
+ uni_embeds = uni_embeds.reshape(uni_embeds.shape[0], tok, tok, -1)
106
+ B, Hg, Wg, _ = uni_embeds.shape
107
+ if Hg < tok or Wg < tok:
108
+ raise ValueError(f"grid {Hg}x{Wg} is smaller than one tile ({tok}x{tok})")
109
+ uni_embeds = uni_embeds.to(device, dtype)
110
+
111
+ win = self.transformer.config.sample_size
112
+ H, W = Hg * LATENTS_PER_TOKEN, Wg * LATENTS_PER_TOKEN
113
+ stride = stride or win // 2
114
+ windows = [(h, w) for h in range(0, H - win + 1, stride) for w in range(0, W - win + 1, stride)]
115
+ kernel = _gaussian_kernel(win, window_sigma, device)
116
+
117
+ if latents is None:
118
+ latents = randn_tensor((B, self.transformer.config.in_channels, H, W), generator=generator,
119
+ device=device, dtype=torch.float32)
120
+ x = latents.to(device, torch.float32)
121
+ sigmas = self._sigmas(num_inference_steps, device)
122
+
123
+ n_select = len(windows) if top_r >= 1 else max(1, math.ceil(top_r * len(windows)))
124
+ importance = [dict() for _ in range(B)]
125
+ v_prev = torch.zeros_like(x)
126
+ with self.progress_bar(total=num_inference_steps) as pbar:
127
+ for step, (s_cur, s_next) in enumerate(zip(sigmas[:-1], sigmas[1:])):
128
+ t = s_cur.reshape(1).to(dtype)
129
+ v_new = v_prev.clone()
130
+ for b in range(B):
131
+ active = windows if step == 0 else sorted(windows, key=lambda w: -importance[b].get(w, 0.0))[:n_select]
132
+ v_sum = torch.zeros_like(x[b:b + 1])
133
+ w_sum = torch.zeros_like(x[b:b + 1])
134
+ for h, w in active:
135
+ y = uni_embeds[b:b + 1, h // 32:h // 32 + tok, w // 32:w // 32 + tok].reshape(1, tok * tok, -1)
136
+ v, score = self._velocity(x[b:b + 1, :, h:h + win, w:w + win].to(dtype), t, y, guidance_scale)
137
+ importance[b][(h, w)] = score
138
+ v_sum[:, :, h:h + win, w:w + win] += v * kernel
139
+ w_sum[:, :, h:h + win, w:w + win] += kernel
140
+ v_new[b:b + 1] = torch.where(w_sum > 0, v_sum / w_sum.clamp(min=1e-6), v_prev[b:b + 1])
141
+ v_prev = v_new
142
+ x = x + (s_next - s_cur) * v_new
143
+ if progress:
144
+ pbar.update()
145
+
146
+ if output_type == "latent":
147
+ return ImagePipelineOutput(images=x) if return_dict else (x,)
148
+ img = ((self._decode(x) + 1) / 2).clamp(0, 1)
149
+ img = (img.permute(0, 2, 3, 1) * 255).round().to(torch.uint8).numpy()
150
+ images = [Image.fromarray(i) for i in img] if output_type == "pil" else img
151
+ return ImagePipelineOutput(images=images) if return_dict else (images,)
bigcell-2048/scheduler/scheduler_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "FlowMatchEulerDiscreteScheduler",
3
+ "_diffusers_version": "0.37.1",
4
+ "base_image_seq_len": 256,
5
+ "base_shift": 0.5,
6
+ "invert_sigmas": false,
7
+ "max_image_seq_len": 4096,
8
+ "max_shift": 1.15,
9
+ "num_train_timesteps": 1000,
10
+ "shift": 3.0,
11
+ "shift_terminal": null,
12
+ "stochastic_sampling": false,
13
+ "time_shift_type": "exponential",
14
+ "use_beta_sigmas": false,
15
+ "use_dynamic_shifting": false,
16
+ "use_exponential_sigmas": false,
17
+ "use_karras_sigmas": false
18
+ }
bigcell-2048/transformer/bigcell_transformer_2d.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell tile generator: a SiT flow-matching transformer conditioned on a grid of UNI-2 embeddings
2
+ # through cross-attention. https://github.com/cvlab-stonybrook/BigCell
3
+ #
4
+ # Adapted from SiT (https://github.com/willisma/SiT, MIT) and REPA-E (https://github.com/End2End-Diffusion/REPA-E, MIT).
5
+
6
+ import math
7
+
8
+ import numpy as np
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
13
+ from diffusers.models.modeling_utils import ModelMixin
14
+
15
+
16
+ def modulate(x, shift, scale):
17
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
18
+
19
+
20
+ class PatchEmbed(nn.Module):
21
+ def __init__(self, patch_size, in_channels, hidden_size):
22
+ super().__init__()
23
+ self.proj = nn.Conv2d(in_channels, hidden_size, kernel_size=patch_size, stride=patch_size, bias=True)
24
+
25
+ def forward(self, x):
26
+ return self.proj(x).flatten(2).transpose(1, 2)
27
+
28
+
29
+ class Mlp(nn.Module):
30
+ def __init__(self, in_features, hidden_features, out_features=None, approximate="none"):
31
+ super().__init__()
32
+ self.fc1 = nn.Linear(in_features, hidden_features)
33
+ self.act = nn.GELU(approximate=approximate)
34
+ self.fc2 = nn.Linear(hidden_features, out_features or in_features)
35
+
36
+ def forward(self, x):
37
+ return self.fc2(self.act(self.fc1(x)))
38
+
39
+
40
+ class Attention(nn.Module):
41
+ def __init__(self, dim, num_heads):
42
+ super().__init__()
43
+ self.num_heads = num_heads
44
+ self.qkv = nn.Linear(dim, dim * 3, bias=True)
45
+ self.proj = nn.Linear(dim, dim)
46
+
47
+ def forward(self, x):
48
+ B, N, C = x.shape
49
+ q, k, v = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4).unbind(0)
50
+ x = F.scaled_dot_product_attention(q, k, v)
51
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
52
+
53
+
54
+ class CrossAttention(nn.Module):
55
+ def __init__(self, dim, num_heads):
56
+ super().__init__()
57
+ self.num_heads = num_heads
58
+ self.q_linear = nn.Linear(dim, dim)
59
+ self.kv_linear = nn.Linear(dim, dim * 2)
60
+ self.proj = nn.Linear(dim, dim)
61
+
62
+ def forward(self, x, cond):
63
+ B, N, C = x.shape
64
+ h = self.num_heads
65
+ q = self.q_linear(x).reshape(B, N, h, C // h).transpose(1, 2)
66
+ k, v = self.kv_linear(cond).reshape(B, cond.shape[1], 2, h, C // h).permute(2, 0, 3, 1, 4).unbind(0)
67
+ x = F.scaled_dot_product_attention(q, k, v)
68
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
69
+
70
+
71
+ class TimestepEmbedder(nn.Module):
72
+ def __init__(self, hidden_size, frequency_embedding_size=256):
73
+ super().__init__()
74
+ self.mlp = nn.Sequential(
75
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
76
+ nn.SiLU(),
77
+ nn.Linear(hidden_size, hidden_size, bias=True),
78
+ )
79
+ self.frequency_embedding_size = frequency_embedding_size
80
+
81
+ def forward(self, t):
82
+ half = self.frequency_embedding_size // 2
83
+ freqs = torch.exp(-math.log(10000) * torch.arange(half, dtype=torch.float32, device=t.device) / half)
84
+ args = t[:, None].float() * freqs[None]
85
+ return self.mlp(torch.cat([torch.cos(args), torch.sin(args)], dim=-1).to(t.dtype))
86
+
87
+
88
+ class CaptionEmbedder(nn.Module):
89
+ """Projects UNI-2 tokens; ``y_embedding`` is the learned unconditional embedding used for CFG."""
90
+
91
+ def __init__(self, in_channels, hidden_size, token_num):
92
+ super().__init__()
93
+ self.y_proj = Mlp(in_channels, hidden_size, hidden_size, approximate="tanh")
94
+ self.register_buffer("y_embedding", torch.randn(token_num, in_channels) / in_channels**0.5)
95
+
96
+ def forward(self, caption):
97
+ return self.y_proj(caption)
98
+
99
+
100
+ class BigCellBlock(nn.Module):
101
+ def __init__(self, hidden_size, num_heads, mlp_ratio=4.0):
102
+ super().__init__()
103
+ self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
104
+ self.attn = Attention(hidden_size, num_heads)
105
+ self.cross_attn = CrossAttention(hidden_size, num_heads)
106
+ self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
107
+ self.mlp = Mlp(hidden_size, int(hidden_size * mlp_ratio), approximate="tanh")
108
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size, bias=True))
109
+
110
+ def forward(self, x, cond, t):
111
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(t).chunk(6, dim=-1)
112
+ x = x + gate_msa.unsqueeze(1) * self.attn(modulate(self.norm1(x), shift_msa, scale_msa))
113
+ x = x + self.cross_attn(x, cond)
114
+ x = x + gate_mlp.unsqueeze(1) * self.mlp(modulate(self.norm2(x), shift_mlp, scale_mlp))
115
+ return x
116
+
117
+
118
+ class FinalLayer(nn.Module):
119
+ def __init__(self, hidden_size, patch_size, out_channels):
120
+ super().__init__()
121
+ self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
122
+ self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
123
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
124
+
125
+ def forward(self, x, c):
126
+ shift, scale = self.adaLN_modulation(c).chunk(2, dim=-1)
127
+ return self.linear(modulate(self.norm_final(x), shift, scale))
128
+
129
+
130
+ class BigCellTransformer2DModel(ModelMixin, ConfigMixin):
131
+ """SiT-XL/2 velocity predictor operating on Flux.2 VAE latents (32 channels, 8x downsampling).
132
+
133
+ Args:
134
+ sample_size: latent side length (tile resolution // 8).
135
+ num_caption_tokens: number of UNI-2 tokens per tile, (tile resolution // 256) ** 2.
136
+ alpha_shift: time shift used during training; the scheduler uses the same value.
137
+ """
138
+
139
+ @register_to_config
140
+ def __init__(
141
+ self,
142
+ sample_size: int = 256,
143
+ patch_size: int = 2,
144
+ in_channels: int = 32,
145
+ hidden_size: int = 1152,
146
+ num_layers: int = 28,
147
+ num_attention_heads: int = 16,
148
+ mlp_ratio: float = 4.0,
149
+ caption_channels: int = 1536,
150
+ num_caption_tokens: int = 64,
151
+ alpha_shift: float = 1.0,
152
+ ):
153
+ super().__init__()
154
+ self.x_embedder = PatchEmbed(patch_size, in_channels, hidden_size)
155
+ self.t_embedder = TimestepEmbedder(hidden_size)
156
+ self.y_embedder = CaptionEmbedder(caption_channels, hidden_size, num_caption_tokens)
157
+ num_patches = (sample_size // patch_size) ** 2
158
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size), requires_grad=False)
159
+ grid = int(num_caption_tokens**0.5)
160
+ if num_caption_tokens > 1 and grid**2 == num_caption_tokens:
161
+ self.y_pos_embed = nn.Parameter(torch.zeros(1, num_caption_tokens, hidden_size), requires_grad=False)
162
+ else:
163
+ self.y_pos_embed = None
164
+ self.blocks = nn.ModuleList(
165
+ [BigCellBlock(hidden_size, num_attention_heads, mlp_ratio) for _ in range(num_layers)]
166
+ )
167
+ self.final_layer = FinalLayer(hidden_size, patch_size, in_channels)
168
+ pos = _sincos_2d(hidden_size, np.stack(np.meshgrid(np.arange(sample_size // patch_size),
169
+ np.arange(sample_size // patch_size)), 0).reshape(2, -1))
170
+ self.pos_embed.data.copy_(torch.from_numpy(pos).float()[None])
171
+
172
+ @property
173
+ def unconditional_embedding(self):
174
+ """(1, T, 1536) learned null condition for classifier-free guidance."""
175
+ return self.y_embedder.y_embedding[None]
176
+
177
+ def unpatchify(self, x):
178
+ c, p = self.config.in_channels, self.config.patch_size
179
+ h = w = int(x.shape[1] ** 0.5)
180
+ x = torch.einsum("nhwpqc->nchpwq", x.reshape(x.shape[0], h, w, p, p, c))
181
+ return x.reshape(x.shape[0], c, h * p, w * p)
182
+
183
+ def forward(self, hidden_states, timestep, encoder_hidden_states):
184
+ """hidden_states: (B, 32, H, W) noisy latent; timestep: (B,) in [0, 1] (1 = pure noise);
185
+ encoder_hidden_states: (B, T, 1536) UNI-2 tokens in raster order. Returns the velocity (noise - data)."""
186
+ x = self.x_embedder(hidden_states) + self.pos_embed
187
+ t = self.t_embedder(timestep)
188
+ y = self.y_embedder(encoder_hidden_states)
189
+ if self.y_pos_embed is not None:
190
+ y = y + self.y_pos_embed
191
+ for block in self.blocks:
192
+ x = block(x, y, t)
193
+ return self.unpatchify(self.final_layer(x, t))
194
+
195
+
196
+ def _sincos_2d(embed_dim, grid):
197
+ def one_d(dim, pos):
198
+ omega = 1.0 / 10000 ** (np.arange(dim // 2, dtype=np.float64) / (dim / 2.0))
199
+ out = np.einsum("m,d->md", pos.reshape(-1), omega)
200
+ return np.concatenate([np.sin(out), np.cos(out)], axis=1)
201
+
202
+ return np.concatenate([one_d(embed_dim // 2, grid[0]), one_d(embed_dim // 2, grid[1])], axis=1)
bigcell-2048/transformer/config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BigCellTransformer2DModel",
3
+ "_diffusers_version": "0.37.1",
4
+ "alpha_shift": 3.0,
5
+ "caption_channels": 1536,
6
+ "hidden_size": 1152,
7
+ "in_channels": 32,
8
+ "mlp_ratio": 4.0,
9
+ "num_attention_heads": 16,
10
+ "num_caption_tokens": 64,
11
+ "num_layers": 28,
12
+ "patch_size": 2,
13
+ "sample_size": 256
14
+ }
bigcell-2048/transformer/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:17a3b7165206aa7d65140b685ae854a7c96098f2ebe98341371721d2855d030b
3
+ size 3379372944
bigcell-2048/vae/config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "AutoencoderKLFlux2",
3
+ "_diffusers_version": "0.36.0.dev0",
4
+ "act_fn": "silu",
5
+ "batch_norm_eps": 0.0001,
6
+ "batch_norm_momentum": 0.1,
7
+ "block_out_channels": [
8
+ 128,
9
+ 256,
10
+ 512,
11
+ 512
12
+ ],
13
+ "down_block_types": [
14
+ "DownEncoderBlock2D",
15
+ "DownEncoderBlock2D",
16
+ "DownEncoderBlock2D",
17
+ "DownEncoderBlock2D"
18
+ ],
19
+ "force_upcast": true,
20
+ "in_channels": 3,
21
+ "latent_channels": 32,
22
+ "layers_per_block": 2,
23
+ "mid_block_add_attention": true,
24
+ "norm_num_groups": 32,
25
+ "out_channels": 3,
26
+ "patch_size": [
27
+ 2,
28
+ 2
29
+ ],
30
+ "sample_size": 1024,
31
+ "up_block_types": [
32
+ "UpDecoderBlock2D",
33
+ "UpDecoderBlock2D",
34
+ "UpDecoderBlock2D",
35
+ "UpDecoderBlock2D"
36
+ ],
37
+ "use_post_quant_conv": true,
38
+ "use_quant_conv": true,
39
+ "_name_or_path": "black-forest-labs/FLUX.2-dev"
40
+ }
bigcell-256/model_index.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": [
3
+ "pipeline",
4
+ "BigCellPipeline"
5
+ ],
6
+ "_diffusers_version": "0.37.1",
7
+ "transformer": [
8
+ "bigcell_transformer_2d",
9
+ "BigCellTransformer2DModel"
10
+ ],
11
+ "vae": [
12
+ "diffusers",
13
+ "AutoencoderKLFlux2"
14
+ ],
15
+ "scheduler": [
16
+ "diffusers",
17
+ "FlowMatchEulerDiscreteScheduler"
18
+ ]
19
+ }
bigcell-256/pipeline.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell pipeline. https://github.com/cvlab-stonybrook/BigCell
2
+ #
3
+ # Generates histopathology images from a grid of UNI-2 embeddings (one 1536-d token per 256x256 px at 20x).
4
+ # A grid that matches the model's native tile (e.g. 8x8 for BigCell-2048) is denoised directly; larger grids
5
+ # are generated with the sliding-window procedure of the paper (BigCell-large), optionally with adaptive
6
+ # window scheduling (``top_r < 1``). For multi-GPU generation of 16k-32k images use the GitHub scripts.
7
+
8
+ import math
9
+ from typing import List, Optional, Union
10
+
11
+ import numpy as np
12
+ import torch
13
+ from diffusers import DiffusionPipeline
14
+ from diffusers.image_processor import VaeImageProcessor
15
+ from diffusers.pipelines.pipeline_utils import ImagePipelineOutput
16
+ from diffusers.utils.torch_utils import randn_tensor
17
+ from PIL import Image
18
+
19
+ LATENTS_PER_TOKEN = 32 # one UNI-2 token covers 256 px = 32 Flux.2 latents
20
+
21
+
22
+ def _gaussian_kernel(size, sigma, device):
23
+ x = torch.linspace(-1, 1, size)
24
+ x = torch.stack((x.tile(size, 1), x.tile(size, 1).T), dim=0)
25
+ k = torch.exp(-(torch.linalg.norm(x, dim=0) ** 2) / (2 * sigma**2))
26
+ return (k / k.max()).to(device)
27
+
28
+
29
+ class BigCellPipeline(DiffusionPipeline):
30
+ model_cpu_offload_seq = "transformer->vae"
31
+
32
+ def __init__(self, transformer, vae, scheduler):
33
+ super().__init__()
34
+ self.register_modules(transformer=transformer, vae=vae, scheduler=scheduler)
35
+ self.image_processor = VaeImageProcessor(vae_scale_factor=8)
36
+
37
+ @property
38
+ def tile_tokens(self):
39
+ """Side of the UNI-2 grid covered by one native tile (1, 4 or 8)."""
40
+ return int(round(math.sqrt(self.transformer.config.num_caption_tokens)))
41
+
42
+ def get_unconditional_embedding(self, batch_size=1):
43
+ return self.transformer.unconditional_embedding.tile(batch_size, 1, 1)
44
+
45
+ def _velocity(self, x, t, y, guidance_scale):
46
+ """Returns (cfg velocity, importance score |v_cond - v_uncond|)."""
47
+ null = self.get_unconditional_embedding(x.shape[0]).to(y.dtype)
48
+ v = self.transformer(torch.cat([x, x]), t.expand(2 * x.shape[0]), torch.cat([y, null])).float()
49
+ v_cond, v_uncond = v.chunk(2)
50
+ return v_uncond + guidance_scale * (v_cond - v_uncond), (v_cond - v_uncond).abs().mean().item()
51
+
52
+ def _sigmas(self, num_inference_steps, device):
53
+ self.scheduler.set_timesteps(sigmas=np.linspace(1.0, 0.0, num_inference_steps + 1)[:-1], device=device)
54
+ return self.scheduler.sigmas.to(device) # shifted, ends with 0
55
+
56
+ @torch.no_grad()
57
+ def _decode(self, latents, tile=256, sigma=0.5):
58
+ """Tiled decode for large canvases (Gaussian-blended, accumulated on CPU)."""
59
+ _, _, h, w = latents.shape
60
+ dtype = self.vae.dtype
61
+ if h <= tile and w <= tile:
62
+ return self.vae.decode(latents.to(dtype)).sample.float().cpu()
63
+ out = torch.zeros(latents.shape[0], 3, h * 8, w * 8)
64
+ cnt = torch.zeros(1, 1, h * 8, w * 8)
65
+ kernel = _gaussian_kernel(tile * 8, sigma, "cpu")[None, None]
66
+ stride = tile // 2
67
+ for i in range(0, h - tile + 1, stride):
68
+ for j in range(0, w - tile + 1, stride):
69
+ dec = self.vae.decode(latents[:, :, i:i + tile, j:j + tile].to(dtype)).sample.float().cpu()
70
+ out[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += dec * kernel
71
+ cnt[:, :, i * 8:(i + tile) * 8, j * 8:(j + tile) * 8] += kernel
72
+ return out / cnt.clamp(min=1e-6)
73
+
74
+ @torch.no_grad()
75
+ def __call__(
76
+ self,
77
+ uni_embeds: torch.Tensor,
78
+ num_inference_steps: int = 20,
79
+ guidance_scale: float = 2.0,
80
+ top_r: float = 1.0,
81
+ stride: Optional[int] = None,
82
+ window_sigma: float = 0.5,
83
+ generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None,
84
+ latents: Optional[torch.Tensor] = None,
85
+ output_type: str = "pil",
86
+ return_dict: bool = True,
87
+ progress: bool = True,
88
+ ):
89
+ """
90
+ Args:
91
+ uni_embeds: UNI-2 grid of shape (B, Hg, Wg, 1536), or tokens of one native tile (B, T, 1536).
92
+ The output image is (256 * Hg) x (256 * Wg) pixels.
93
+ num_inference_steps: Euler steps (paper: 20).
94
+ guidance_scale: classifier-free guidance scale (paper: 2.0).
95
+ top_r: fraction of sliding windows recomputed at every step after the first (paper Sec. 3.3);
96
+ the rest reuse their cached velocity. 1.0 = full computation.
97
+ stride: window stride in latents (default: half a window, e.g. 128 = 1024 px for BigCell-2048).
98
+ window_sigma: width of the Gaussian window weighting.
99
+ output_type: "pil", "np" (uint8 HxWx3) or "latent".
100
+ """
101
+ device = self._execution_device
102
+ dtype = self.transformer.dtype
103
+ tok = self.tile_tokens
104
+ if uni_embeds.ndim == 3: # (B, T, C) tokens of one tile -> grid
105
+ uni_embeds = uni_embeds.reshape(uni_embeds.shape[0], tok, tok, -1)
106
+ B, Hg, Wg, _ = uni_embeds.shape
107
+ if Hg < tok or Wg < tok:
108
+ raise ValueError(f"grid {Hg}x{Wg} is smaller than one tile ({tok}x{tok})")
109
+ uni_embeds = uni_embeds.to(device, dtype)
110
+
111
+ win = self.transformer.config.sample_size
112
+ H, W = Hg * LATENTS_PER_TOKEN, Wg * LATENTS_PER_TOKEN
113
+ stride = stride or win // 2
114
+ windows = [(h, w) for h in range(0, H - win + 1, stride) for w in range(0, W - win + 1, stride)]
115
+ kernel = _gaussian_kernel(win, window_sigma, device)
116
+
117
+ if latents is None:
118
+ latents = randn_tensor((B, self.transformer.config.in_channels, H, W), generator=generator,
119
+ device=device, dtype=torch.float32)
120
+ x = latents.to(device, torch.float32)
121
+ sigmas = self._sigmas(num_inference_steps, device)
122
+
123
+ n_select = len(windows) if top_r >= 1 else max(1, math.ceil(top_r * len(windows)))
124
+ importance = [dict() for _ in range(B)]
125
+ v_prev = torch.zeros_like(x)
126
+ with self.progress_bar(total=num_inference_steps) as pbar:
127
+ for step, (s_cur, s_next) in enumerate(zip(sigmas[:-1], sigmas[1:])):
128
+ t = s_cur.reshape(1).to(dtype)
129
+ v_new = v_prev.clone()
130
+ for b in range(B):
131
+ active = windows if step == 0 else sorted(windows, key=lambda w: -importance[b].get(w, 0.0))[:n_select]
132
+ v_sum = torch.zeros_like(x[b:b + 1])
133
+ w_sum = torch.zeros_like(x[b:b + 1])
134
+ for h, w in active:
135
+ y = uni_embeds[b:b + 1, h // 32:h // 32 + tok, w // 32:w // 32 + tok].reshape(1, tok * tok, -1)
136
+ v, score = self._velocity(x[b:b + 1, :, h:h + win, w:w + win].to(dtype), t, y, guidance_scale)
137
+ importance[b][(h, w)] = score
138
+ v_sum[:, :, h:h + win, w:w + win] += v * kernel
139
+ w_sum[:, :, h:h + win, w:w + win] += kernel
140
+ v_new[b:b + 1] = torch.where(w_sum > 0, v_sum / w_sum.clamp(min=1e-6), v_prev[b:b + 1])
141
+ v_prev = v_new
142
+ x = x + (s_next - s_cur) * v_new
143
+ if progress:
144
+ pbar.update()
145
+
146
+ if output_type == "latent":
147
+ return ImagePipelineOutput(images=x) if return_dict else (x,)
148
+ img = ((self._decode(x) + 1) / 2).clamp(0, 1)
149
+ img = (img.permute(0, 2, 3, 1) * 255).round().to(torch.uint8).numpy()
150
+ images = [Image.fromarray(i) for i in img] if output_type == "pil" else img
151
+ return ImagePipelineOutput(images=images) if return_dict else (images,)
bigcell-256/scheduler/scheduler_config.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "FlowMatchEulerDiscreteScheduler",
3
+ "_diffusers_version": "0.37.1",
4
+ "base_image_seq_len": 256,
5
+ "base_shift": 0.5,
6
+ "invert_sigmas": false,
7
+ "max_image_seq_len": 4096,
8
+ "max_shift": 1.15,
9
+ "num_train_timesteps": 1000,
10
+ "shift": 1.0,
11
+ "shift_terminal": null,
12
+ "stochastic_sampling": false,
13
+ "time_shift_type": "exponential",
14
+ "use_beta_sigmas": false,
15
+ "use_dynamic_shifting": false,
16
+ "use_exponential_sigmas": false,
17
+ "use_karras_sigmas": false
18
+ }
bigcell-256/transformer/bigcell_transformer_2d.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BigCell tile generator: a SiT flow-matching transformer conditioned on a grid of UNI-2 embeddings
2
+ # through cross-attention. https://github.com/cvlab-stonybrook/BigCell
3
+ #
4
+ # Adapted from SiT (https://github.com/willisma/SiT, MIT) and REPA-E (https://github.com/End2End-Diffusion/REPA-E, MIT).
5
+
6
+ import math
7
+
8
+ import numpy as np
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
13
+ from diffusers.models.modeling_utils import ModelMixin
14
+
15
+
16
+ def modulate(x, shift, scale):
17
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
18
+
19
+
20
+ class PatchEmbed(nn.Module):
21
+ def __init__(self, patch_size, in_channels, hidden_size):
22
+ super().__init__()
23
+ self.proj = nn.Conv2d(in_channels, hidden_size, kernel_size=patch_size, stride=patch_size, bias=True)
24
+
25
+ def forward(self, x):
26
+ return self.proj(x).flatten(2).transpose(1, 2)
27
+
28
+
29
+ class Mlp(nn.Module):
30
+ def __init__(self, in_features, hidden_features, out_features=None, approximate="none"):
31
+ super().__init__()
32
+ self.fc1 = nn.Linear(in_features, hidden_features)
33
+ self.act = nn.GELU(approximate=approximate)
34
+ self.fc2 = nn.Linear(hidden_features, out_features or in_features)
35
+
36
+ def forward(self, x):
37
+ return self.fc2(self.act(self.fc1(x)))
38
+
39
+
40
+ class Attention(nn.Module):
41
+ def __init__(self, dim, num_heads):
42
+ super().__init__()
43
+ self.num_heads = num_heads
44
+ self.qkv = nn.Linear(dim, dim * 3, bias=True)
45
+ self.proj = nn.Linear(dim, dim)
46
+
47
+ def forward(self, x):
48
+ B, N, C = x.shape
49
+ q, k, v = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4).unbind(0)
50
+ x = F.scaled_dot_product_attention(q, k, v)
51
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
52
+
53
+
54
+ class CrossAttention(nn.Module):
55
+ def __init__(self, dim, num_heads):
56
+ super().__init__()
57
+ self.num_heads = num_heads
58
+ self.q_linear = nn.Linear(dim, dim)
59
+ self.kv_linear = nn.Linear(dim, dim * 2)
60
+ self.proj = nn.Linear(dim, dim)
61
+
62
+ def forward(self, x, cond):
63
+ B, N, C = x.shape
64
+ h = self.num_heads
65
+ q = self.q_linear(x).reshape(B, N, h, C // h).transpose(1, 2)
66
+ k, v = self.kv_linear(cond).reshape(B, cond.shape[1], 2, h, C // h).permute(2, 0, 3, 1, 4).unbind(0)
67
+ x = F.scaled_dot_product_attention(q, k, v)
68
+ return self.proj(x.transpose(1, 2).reshape(B, N, C))
69
+
70
+
71
+ class TimestepEmbedder(nn.Module):
72
+ def __init__(self, hidden_size, frequency_embedding_size=256):
73
+ super().__init__()
74
+ self.mlp = nn.Sequential(
75
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
76
+ nn.SiLU(),
77
+ nn.Linear(hidden_size, hidden_size, bias=True),
78
+ )
79
+ self.frequency_embedding_size = frequency_embedding_size
80
+
81
+ def forward(self, t):
82
+ half = self.frequency_embedding_size // 2
83
+ freqs = torch.exp(-math.log(10000) * torch.arange(half, dtype=torch.float32, device=t.device) / half)
84
+ args = t[:, None].float() * freqs[None]
85
+ return self.mlp(torch.cat([torch.cos(args), torch.sin(args)], dim=-1).to(t.dtype))
86
+
87
+
88
+ class CaptionEmbedder(nn.Module):
89
+ """Projects UNI-2 tokens; ``y_embedding`` is the learned unconditional embedding used for CFG."""
90
+
91
+ def __init__(self, in_channels, hidden_size, token_num):
92
+ super().__init__()
93
+ self.y_proj = Mlp(in_channels, hidden_size, hidden_size, approximate="tanh")
94
+ self.register_buffer("y_embedding", torch.randn(token_num, in_channels) / in_channels**0.5)
95
+
96
+ def forward(self, caption):
97
+ return self.y_proj(caption)
98
+
99
+
100
+ class BigCellBlock(nn.Module):
101
+ def __init__(self, hidden_size, num_heads, mlp_ratio=4.0):
102
+ super().__init__()
103
+ self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
104
+ self.attn = Attention(hidden_size, num_heads)
105
+ self.cross_attn = CrossAttention(hidden_size, num_heads)
106
+ self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
107
+ self.mlp = Mlp(hidden_size, int(hidden_size * mlp_ratio), approximate="tanh")
108
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size, bias=True))
109
+
110
+ def forward(self, x, cond, t):
111
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.adaLN_modulation(t).chunk(6, dim=-1)
112
+ x = x + gate_msa.unsqueeze(1) * self.attn(modulate(self.norm1(x), shift_msa, scale_msa))
113
+ x = x + self.cross_attn(x, cond)
114
+ x = x + gate_mlp.unsqueeze(1) * self.mlp(modulate(self.norm2(x), shift_mlp, scale_mlp))
115
+ return x
116
+
117
+
118
+ class FinalLayer(nn.Module):
119
+ def __init__(self, hidden_size, patch_size, out_channels):
120
+ super().__init__()
121
+ self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
122
+ self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
123
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
124
+
125
+ def forward(self, x, c):
126
+ shift, scale = self.adaLN_modulation(c).chunk(2, dim=-1)
127
+ return self.linear(modulate(self.norm_final(x), shift, scale))
128
+
129
+
130
+ class BigCellTransformer2DModel(ModelMixin, ConfigMixin):
131
+ """SiT-XL/2 velocity predictor operating on Flux.2 VAE latents (32 channels, 8x downsampling).
132
+
133
+ Args:
134
+ sample_size: latent side length (tile resolution // 8).
135
+ num_caption_tokens: number of UNI-2 tokens per tile, (tile resolution // 256) ** 2.
136
+ alpha_shift: time shift used during training; the scheduler uses the same value.
137
+ """
138
+
139
+ @register_to_config
140
+ def __init__(
141
+ self,
142
+ sample_size: int = 256,
143
+ patch_size: int = 2,
144
+ in_channels: int = 32,
145
+ hidden_size: int = 1152,
146
+ num_layers: int = 28,
147
+ num_attention_heads: int = 16,
148
+ mlp_ratio: float = 4.0,
149
+ caption_channels: int = 1536,
150
+ num_caption_tokens: int = 64,
151
+ alpha_shift: float = 1.0,
152
+ ):
153
+ super().__init__()
154
+ self.x_embedder = PatchEmbed(patch_size, in_channels, hidden_size)
155
+ self.t_embedder = TimestepEmbedder(hidden_size)
156
+ self.y_embedder = CaptionEmbedder(caption_channels, hidden_size, num_caption_tokens)
157
+ num_patches = (sample_size // patch_size) ** 2
158
+ self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size), requires_grad=False)
159
+ grid = int(num_caption_tokens**0.5)
160
+ if num_caption_tokens > 1 and grid**2 == num_caption_tokens:
161
+ self.y_pos_embed = nn.Parameter(torch.zeros(1, num_caption_tokens, hidden_size), requires_grad=False)
162
+ else:
163
+ self.y_pos_embed = None
164
+ self.blocks = nn.ModuleList(
165
+ [BigCellBlock(hidden_size, num_attention_heads, mlp_ratio) for _ in range(num_layers)]
166
+ )
167
+ self.final_layer = FinalLayer(hidden_size, patch_size, in_channels)
168
+ pos = _sincos_2d(hidden_size, np.stack(np.meshgrid(np.arange(sample_size // patch_size),
169
+ np.arange(sample_size // patch_size)), 0).reshape(2, -1))
170
+ self.pos_embed.data.copy_(torch.from_numpy(pos).float()[None])
171
+
172
+ @property
173
+ def unconditional_embedding(self):
174
+ """(1, T, 1536) learned null condition for classifier-free guidance."""
175
+ return self.y_embedder.y_embedding[None]
176
+
177
+ def unpatchify(self, x):
178
+ c, p = self.config.in_channels, self.config.patch_size
179
+ h = w = int(x.shape[1] ** 0.5)
180
+ x = torch.einsum("nhwpqc->nchpwq", x.reshape(x.shape[0], h, w, p, p, c))
181
+ return x.reshape(x.shape[0], c, h * p, w * p)
182
+
183
+ def forward(self, hidden_states, timestep, encoder_hidden_states):
184
+ """hidden_states: (B, 32, H, W) noisy latent; timestep: (B,) in [0, 1] (1 = pure noise);
185
+ encoder_hidden_states: (B, T, 1536) UNI-2 tokens in raster order. Returns the velocity (noise - data)."""
186
+ x = self.x_embedder(hidden_states) + self.pos_embed
187
+ t = self.t_embedder(timestep)
188
+ y = self.y_embedder(encoder_hidden_states)
189
+ if self.y_pos_embed is not None:
190
+ y = y + self.y_pos_embed
191
+ for block in self.blocks:
192
+ x = block(x, y, t)
193
+ return self.unpatchify(self.final_layer(x, t))
194
+
195
+
196
+ def _sincos_2d(embed_dim, grid):
197
+ def one_d(dim, pos):
198
+ omega = 1.0 / 10000 ** (np.arange(dim // 2, dtype=np.float64) / (dim / 2.0))
199
+ out = np.einsum("m,d->md", pos.reshape(-1), omega)
200
+ return np.concatenate([np.sin(out), np.cos(out)], axis=1)
201
+
202
+ return np.concatenate([one_d(embed_dim // 2, grid[0]), one_d(embed_dim // 2, grid[1])], axis=1)
bigcell-256/transformer/config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "BigCellTransformer2DModel",
3
+ "_diffusers_version": "0.37.1",
4
+ "alpha_shift": 1.0,
5
+ "caption_channels": 1536,
6
+ "hidden_size": 1152,
7
+ "in_channels": 32,
8
+ "mlp_ratio": 4.0,
9
+ "num_attention_heads": 16,
10
+ "num_caption_tokens": 1,
11
+ "num_layers": 28,
12
+ "patch_size": 2,
13
+ "sample_size": 32
14
+ }
bigcell-256/transformer/diffusion_pytorch_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:091870c776997dd5314f8ccb69fdac97be13e54813eb8af2e56104cc437d0c78
3
+ size 3304373040
bigcell-256/vae/config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_class_name": "AutoencoderKLFlux2",
3
+ "_diffusers_version": "0.36.0.dev0",
4
+ "act_fn": "silu",
5
+ "batch_norm_eps": 0.0001,
6
+ "batch_norm_momentum": 0.1,
7
+ "block_out_channels": [
8
+ 128,
9
+ 256,
10
+ 512,
11
+ 512
12
+ ],
13
+ "down_block_types": [
14
+ "DownEncoderBlock2D",
15
+ "DownEncoderBlock2D",
16
+ "DownEncoderBlock2D",
17
+ "DownEncoderBlock2D"
18
+ ],
19
+ "force_upcast": true,
20
+ "in_channels": 3,
21
+ "latent_channels": 32,
22
+ "layers_per_block": 2,
23
+ "mid_block_add_attention": true,
24
+ "norm_num_groups": 32,
25
+ "out_channels": 3,
26
+ "patch_size": [
27
+ 2,
28
+ 2
29
+ ],
30
+ "sample_size": 1024,
31
+ "up_block_types": [
32
+ "UpDecoderBlock2D",
33
+ "UpDecoderBlock2D",
34
+ "UpDecoderBlock2D",
35
+ "UpDecoderBlock2D"
36
+ ],
37
+ "use_post_quant_conv": true,
38
+ "use_quant_conv": true,
39
+ "_name_or_path": "black-forest-labs/FLUX.2-dev"
40
+ }
cgm-8192/config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "CGM-B",
3
+ "grid_size": 32,
4
+ "region_size": 8192,
5
+ "time_shift": 19.595917942265423,
6
+ "text_encoder": "MahmoodLab/TITAN",
7
+ "num_inference_steps": 50,
8
+ "guidance_scale": 2.0
9
+ }
cgm-8192/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a3ae9348c830c1b0f8df002341ebd919e8ae76c03f5706d1971cafdcc7c22a78
3
+ size 1308806832
cgm-8192/normalization_stats.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:64d6310d55153ff074537018252b4809b80cfd3044354793fff554ad945137b9
3
+ size 12583072