multimodalart HF Staff commited on
Commit
66e6d09
·
verified ·
1 Parent(s): 406e0b0

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +103 -6
app.py CHANGED
@@ -11,18 +11,115 @@ TURBO_REPO = "krea/Krea-2-Turbo"
11
  HD_VAE_REPO = "wikeeyang/Krea2-Turbo-HD-V1"
12
  MAX_SEED = 2**31 - 1
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # Load the Krea-2-Turbo pipeline (full diffusers format) then swap in the
15
  # HD-optimized VAE from wikeeyang/Krea2-Turbo-HD-V1 for enhanced detail,
16
- # clarity, and contrast. The HD VAE is a single safetensors checkpoint of the
17
- # same AutoencoderKLQwenImage architecture, so we load the base pipeline's VAE
18
- # (which has the proper config) and overwrite its weights.
19
  pipe = Krea2Pipeline.from_pretrained(TURBO_REPO, torch_dtype=torch.bfloat16)
20
 
21
- # Load the HD VAE weights and replace the pipeline's VAE state dict
22
  hd_vae_path = hf_hub_download(HD_VAE_REPO, "Krea2-HD-vae.safetensors")
23
  hd_vae_state = load_safetensors(hd_vae_path)
24
- pipe.vae.load_state_dict(hd_vae_state, strict=True)
25
- print("HD VAE weights loaded successfully.")
 
26
 
27
  pipe.to("cuda")
28
 
 
11
  HD_VAE_REPO = "wikeeyang/Krea2-Turbo-HD-V1"
12
  MAX_SEED = 2**31 - 1
13
 
14
+
15
+ def _remap_vae_keys(state_dict):
16
+ """Remap ComfyUI-format VAE state dict keys to diffusers AutoencoderKLQwenImage keys.
17
+
18
+ The ComfyUI checkpoint uses a flat sequential naming convention (residual.0,
19
+ residual.2, etc.) while diffusers uses semantic names (norm1, conv1, norm2,
20
+ conv2). This function translates between the two.
21
+ """
22
+ # Mapping from ComfyUI decoder upsamples index to diffusers up_blocks path.
23
+ # Derived from the AutoencoderKLQwenImage architecture with dim_mult=[1,2,4,4]
24
+ # and num_res_blocks=2: each level has 2 resnets, with upsamplers between levels.
25
+ up_map = {
26
+ 0: "up_blocks.0.resnets.0",
27
+ 1: "up_blocks.0.resnets.1",
28
+ 2: "up_blocks.0.resnets.2",
29
+ 3: "up_blocks.0.upsamplers.0",
30
+ 4: "up_blocks.1.resnets.0",
31
+ 5: "up_blocks.1.resnets.1",
32
+ 6: "up_blocks.1.resnets.2",
33
+ 7: "up_blocks.1.upsamplers.0",
34
+ 8: "up_blocks.2.resnets.0",
35
+ 9: "up_blocks.2.resnets.1",
36
+ 10: "up_blocks.2.resnets.2",
37
+ 11: "up_blocks.2.upsamplers.0",
38
+ 12: "up_blocks.3.resnets.0",
39
+ 13: "up_blocks.3.resnets.1",
40
+ 14: "up_blocks.3.resnets.2",
41
+ }
42
+
43
+ def _fix_resnet(rest):
44
+ """Map sequential residual indices to semantic names."""
45
+ rest = rest.replace("residual.0.", "norm1.")
46
+ rest = rest.replace("residual.2.", "conv1.")
47
+ rest = rest.replace("residual.3.", "norm2.")
48
+ rest = rest.replace("residual.6.", "conv2.")
49
+ rest = rest.replace("shortcut.", "conv_shortcut.")
50
+ return rest
51
+
52
+ def _fix_middle(key, side):
53
+ """Map encoder/decoder middle.X to mid_block structure."""
54
+ parts = key.split(".")
55
+ idx = int(parts[2])
56
+ rest = ".".join(parts[3:])
57
+ if idx == 0: # first resnet
58
+ return f"{side}.mid_block.resnets.0.{_fix_resnet(rest)}"
59
+ elif idx == 1: # attention
60
+ return f"{side}.mid_block.attentions.0.{rest}"
61
+ elif idx == 2: # second resnet
62
+ return f"{side}.mid_block.resnets.1.{_fix_resnet(rest)}"
63
+
64
+ new_state = {}
65
+ for key, val in state_dict.items():
66
+ new_key = key
67
+
68
+ # Top-level convs: quant_conv and post_quant_conv
69
+ if key.startswith("conv1."):
70
+ new_key = "quant_conv." + key[len("conv1."):]
71
+ elif key.startswith("conv2."):
72
+ new_key = "post_quant_conv." + key[len("conv2."):]
73
+
74
+ # Encoder mapping
75
+ elif key.startswith("encoder.conv1."):
76
+ new_key = "encoder.conv_in." + key[len("encoder.conv1."):]
77
+ elif key.startswith("encoder.head.0."):
78
+ new_key = "encoder.norm_out." + key[len("encoder.head.0."):]
79
+ elif key.startswith("encoder.head.2."):
80
+ new_key = "encoder.conv_out." + key[len("encoder.head.2."):]
81
+ elif key.startswith("encoder.downsamples."):
82
+ parts = key.split(".")
83
+ idx = int(parts[2])
84
+ rest = ".".join(parts[3:])
85
+ rest = _fix_resnet(rest)
86
+ new_key = f"encoder.down_blocks.{idx}.{rest}"
87
+ elif key.startswith("encoder.middle."):
88
+ new_key = _fix_middle(key, "encoder")
89
+
90
+ # Decoder mapping
91
+ elif key.startswith("decoder.conv1."):
92
+ new_key = "decoder.conv_in." + key[len("decoder.conv1."):]
93
+ elif key.startswith("decoder.head.0."):
94
+ new_key = "decoder.norm_out." + key[len("decoder.head.0."):]
95
+ elif key.startswith("decoder.head.2."):
96
+ new_key = "decoder.conv_out." + key[len("decoder.head.2."):]
97
+ elif key.startswith("decoder.upsamples."):
98
+ parts = key.split(".")
99
+ idx = int(parts[2])
100
+ rest = ".".join(parts[3:])
101
+ rest = _fix_resnet(rest)
102
+ new_key = f"decoder.{up_map[idx]}.{rest}"
103
+ elif key.startswith("decoder.middle."):
104
+ new_key = _fix_middle(key, "decoder")
105
+
106
+ new_state[new_key] = val
107
+
108
+ return new_state
109
+
110
+
111
  # Load the Krea-2-Turbo pipeline (full diffusers format) then swap in the
112
  # HD-optimized VAE from wikeeyang/Krea2-Turbo-HD-V1 for enhanced detail,
113
+ # clarity, and contrast. The HD VAE is a ComfyUI-format single safetensors
114
+ # checkpoint; we remap its keys to the diffusers AutoencoderKLQwenImage layout.
 
115
  pipe = Krea2Pipeline.from_pretrained(TURBO_REPO, torch_dtype=torch.bfloat16)
116
 
117
+ # Load the HD VAE weights, remap keys from ComfyUI format, and replace the VAE
118
  hd_vae_path = hf_hub_download(HD_VAE_REPO, "Krea2-HD-vae.safetensors")
119
  hd_vae_state = load_safetensors(hd_vae_path)
120
+ remapped = _remap_vae_keys(hd_vae_state)
121
+ pipe.vae.load_state_dict(remapped, strict=True)
122
+ print("HD VAE weights loaded and remapped successfully.")
123
 
124
  pipe.to("cuda")
125