Spaces:
Paused
Paused
use pre-quantized INT8 model (no FP8 casting, no LoRA merge at runtime)
Browse files
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""FE2E: Depth + Normal estimation from a single image (CPU)"""
|
| 2 |
from __future__ import annotations
|
| 3 |
|
| 4 |
import gc
|
|
@@ -35,64 +35,42 @@ def _download(repo, filename, token=None):
|
|
| 35 |
|
| 36 |
|
| 37 |
def _load_generator():
|
| 38 |
-
"""Load
|
| 39 |
-
from safetensors.torch import load_file
|
| 40 |
from modules.model_edit import Step1XParams, Step1XEdit
|
| 41 |
from modules.autoencoder import AutoEncoder
|
| 42 |
-
from infer.inference import ImageGenerator
|
| 43 |
-
import numpy as np
|
| 44 |
|
| 45 |
token = os.environ.get("HF_TOKEN")
|
| 46 |
-
dit_path = _download("
|
| 47 |
-
vae_path = _download("
|
| 48 |
-
lora_path = _download("exander/FE2E", "LDRN.safetensors", token)
|
| 49 |
|
| 50 |
args = Args()
|
| 51 |
-
|
| 52 |
-
print("[init] Building model on meta device...")
|
| 53 |
-
with torch.device("meta"):
|
| 54 |
-
ae = AutoEncoder(
|
| 55 |
-
resolution=256, in_channels=3, ch=128, out_ch=3,
|
| 56 |
-
ch_mult=[1, 2, 4, 4], num_res_blocks=2, z_channels=16,
|
| 57 |
-
scale_factor=0.3611, shift_factor=0.1159,
|
| 58 |
-
)
|
| 59 |
-
step1x_params = Step1XParams(
|
| 60 |
-
in_channels=64, out_channels=64, vec_in_dim=768, context_in_dim=4096,
|
| 61 |
-
hidden_size=3072, mlp_ratio=4.0, num_heads=24, depth=19,
|
| 62 |
-
depth_single_blocks=38, axes_dim=[16, 56, 56], theta=10_000, qkv_bias=True,
|
| 63 |
-
)
|
| 64 |
-
dit = Step1XEdit(step1x_params)
|
| 65 |
-
|
| 66 |
-
# Load weights as FP8, then cast to FP32 layer by layer to avoid 46 GB peak
|
| 67 |
-
print("[init] Loading FP8 weights and casting to FP32 (layer by layer)...")
|
| 68 |
t0 = time.time()
|
| 69 |
-
fp8_sd = load_file(dit_path, device="cpu")
|
| 70 |
-
dit_sd = {}
|
| 71 |
-
for k, v in fp8_sd.items():
|
| 72 |
-
dit_sd[k] = v.float()
|
| 73 |
-
del fp8_sd
|
| 74 |
-
gc.collect()
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
# Load VAE
|
| 83 |
-
|
| 84 |
-
ae =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
# Merge LoRA in FP32 (full precision merge)
|
| 87 |
-
print("[init] Merging LoRA...")
|
| 88 |
-
equip_dit_with_lora_sd_scripts(ae, [None], dit, lora_path, device="cpu")
|
| 89 |
-
|
| 90 |
-
# Dynamic INT8 quantization
|
| 91 |
-
print("[init] Applying dynamic INT8 quantization...")
|
| 92 |
-
dit = torch.quantization.quantize_dynamic(dit, {torch.nn.Linear}, dtype=torch.qint8)
|
| 93 |
gc.collect()
|
| 94 |
|
| 95 |
-
# Build generator wrapper
|
| 96 |
generator = ImageGenerator.__new__(ImageGenerator)
|
| 97 |
generator.device = torch.device("cpu")
|
| 98 |
generator.args = args
|
|
@@ -103,7 +81,7 @@ def _load_generator():
|
|
| 103 |
generator.offload = False
|
| 104 |
generator.lora_module = None
|
| 105 |
|
| 106 |
-
print("[init]
|
| 107 |
return generator
|
| 108 |
|
| 109 |
|
|
@@ -139,7 +117,7 @@ def generate(image):
|
|
| 139 |
print(f"[gen] Input: {image.size}")
|
| 140 |
t0 = time.time()
|
| 141 |
|
| 142 |
-
with torch.inference_mode()
|
| 143 |
images, Lpred, Rpred = GENERATOR.generate_image(
|
| 144 |
prompt="",
|
| 145 |
negative_prompt="",
|
|
@@ -186,7 +164,7 @@ def main():
|
|
| 186 |
with gr.Blocks(title="FE2E: Depth + Normal (CPU)") as demo:
|
| 187 |
gr.Markdown(
|
| 188 |
"**[FE2E](https://github.com/AMAP-ML/FE2E)** Depth + Normal from a single image (CVPR 2026). "
|
| 189 |
-
"Single denoise step, Step1X-Edit DiT + LDRN LoRA,
|
| 190 |
)
|
| 191 |
with gr.Row():
|
| 192 |
with gr.Column():
|
|
|
|
| 1 |
+
"""FE2E: Depth + Normal estimation from a single image (CPU, pre-quantized INT8)"""
|
| 2 |
from __future__ import annotations
|
| 3 |
|
| 4 |
import gc
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
def _load_generator():
|
| 38 |
+
"""Load pre-quantized INT8 DiT + FP32 VAE. No FP8 casting, no LoRA merge at runtime."""
|
|
|
|
| 39 |
from modules.model_edit import Step1XParams, Step1XEdit
|
| 40 |
from modules.autoencoder import AutoEncoder
|
| 41 |
+
from infer.inference import ImageGenerator
|
|
|
|
| 42 |
|
| 43 |
token = os.environ.get("HF_TOKEN")
|
| 44 |
+
dit_path = _download("WeReCooking2/FE2E-INT8", "dit_int8.pt", token)
|
| 45 |
+
vae_path = _download("WeReCooking2/FE2E-INT8", "vae_fp32.pt", token)
|
|
|
|
| 46 |
|
| 47 |
args = Args()
|
| 48 |
+
print("[init] Loading pre-quantized INT8 DiT...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
t0 = time.time()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
# Build model structure, then quantize empty model, then load pre-quantized weights
|
| 52 |
+
step1x_params = Step1XParams(
|
| 53 |
+
in_channels=64, out_channels=64, vec_in_dim=768, context_in_dim=4096,
|
| 54 |
+
hidden_size=3072, mlp_ratio=4.0, num_heads=24, depth=19,
|
| 55 |
+
depth_single_blocks=38, axes_dim=[16, 56, 56], theta=10_000, qkv_bias=True,
|
| 56 |
+
)
|
| 57 |
+
dit = Step1XEdit(step1x_params).float()
|
| 58 |
+
dit = torch.quantization.quantize_dynamic(dit, {torch.nn.Linear}, dtype=torch.qint8)
|
| 59 |
+
dit.load_state_dict(torch.load(dit_path, map_location="cpu", weights_only=False))
|
| 60 |
+
print(f"[init] DiT loaded in {time.time()-t0:.0f}s")
|
| 61 |
|
| 62 |
# Load VAE
|
| 63 |
+
print("[init] Loading VAE (FP32)...")
|
| 64 |
+
ae = AutoEncoder(
|
| 65 |
+
resolution=256, in_channels=3, ch=128, out_ch=3,
|
| 66 |
+
ch_mult=[1, 2, 4, 4], num_res_blocks=2, z_channels=16,
|
| 67 |
+
scale_factor=0.3611, shift_factor=0.1159,
|
| 68 |
+
).float()
|
| 69 |
+
ae.load_state_dict(torch.load(vae_path, map_location="cpu", weights_only=False))
|
| 70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
gc.collect()
|
| 72 |
|
| 73 |
+
# Build generator wrapper (no LoRA needed, already merged)
|
| 74 |
generator = ImageGenerator.__new__(ImageGenerator)
|
| 75 |
generator.device = torch.device("cpu")
|
| 76 |
generator.args = args
|
|
|
|
| 81 |
generator.offload = False
|
| 82 |
generator.lora_module = None
|
| 83 |
|
| 84 |
+
print(f"[init] Ready. Total load: {time.time()-t0:.0f}s")
|
| 85 |
return generator
|
| 86 |
|
| 87 |
|
|
|
|
| 117 |
print(f"[gen] Input: {image.size}")
|
| 118 |
t0 = time.time()
|
| 119 |
|
| 120 |
+
with torch.inference_mode():
|
| 121 |
images, Lpred, Rpred = GENERATOR.generate_image(
|
| 122 |
prompt="",
|
| 123 |
negative_prompt="",
|
|
|
|
| 164 |
with gr.Blocks(title="FE2E: Depth + Normal (CPU)") as demo:
|
| 165 |
gr.Markdown(
|
| 166 |
"**[FE2E](https://github.com/AMAP-ML/FE2E)** Depth + Normal from a single image (CVPR 2026). "
|
| 167 |
+
"Single denoise step, Step1X-Edit DiT + LDRN LoRA (pre-merged), INT8 quantized on CPU."
|
| 168 |
)
|
| 169 |
with gr.Row():
|
| 170 |
with gr.Column():
|