Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/BuiltinExtensions/ComfyUIBackend/WorkflowGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -834,7 +834,7 @@ public List<JArray> LoadPromptImagesForMainRef(List<Image> images)
public (JArray, JArray, JArray, JArray) BuildInputImageHandling(List<JArray> images, JArray pos, JArray neg, JArray latent)
{
JArray imgNeg = null;
if (IsKontext() || IsOmniGen() || IsQwenImage() || IsQwenImage21() || IsAnyFlux2() || IsBoogu() || IsMageFlow() || (IsKrea2() && UserInput.Get(ComfyUIBackendExtension.EnableReferenceLatents, "none") != "none"))
if (IsKontext() || IsOmniGen() || IsQwenImage() || IsQwenImage21() || IsAnyFlux2() || IsBoogu() || IsMageFlow() || IsMingImage() || (IsKrea2() && UserInput.Get(ComfyUIBackendExtension.EnableReferenceLatents, "none") != "none"))
{
if (IsOmniGen() || IsQwenImageEditPlus() || IsQwenImage21() || IsBoogu() || IsMageFlow())
{
Expand Down Expand Up @@ -1064,7 +1064,7 @@ public string CreateKSampler(JArray model, JArray pos, JArray neg, JArray latent
defscheduler ??= "simple";
}
// TODO: Registry of model default preferences instead of this
else if (IsFlux() || IsWanVideo() || IsWanVideo22() || IsOmniGen() || IsQwenImage() || IsQwenImage21() || IsZImage() || IsZetaChroma() || IsErnie() || IsHiDreamO1() || IsLens() || IsPixelDiT() || IsKrea2() || IsBoogu() || IsMageFlow() || IsMiniMaxMusic3() || IsSeedVR2())
else if (IsFlux() || IsWanVideo() || IsWanVideo22() || IsOmniGen() || IsQwenImage() || IsQwenImage21() || IsZImage() || IsMingImage() || IsZetaChroma() || IsErnie() || IsHiDreamO1() || IsLens() || IsPixelDiT() || IsKrea2() || IsBoogu() || IsMageFlow() || IsMiniMaxMusic3() || IsSeedVR2())
{
defscheduler ??= "simple";
}
Expand Down Expand Up @@ -1427,6 +1427,10 @@ public JArray GetPromptImage(bool fixSize, bool promptSize = false, int index =
target = 1024; // Qwen image targets 1328 for gen but wants 1024 inputs.
doesFit = Math.Abs(actual - target) <= 64;
}
else if (IsMingImage())
{
doesFit = Math.Abs(actual - target) <= 64;
}
if (fixSize && !doesFit)
{
(width, height) = Utilities.ResToModelFit(width, height, target * target, precision: promptSize ? 1 : 64);
Expand Down Expand Up @@ -2758,6 +2762,36 @@ public JArray CreateConditioningDirect(string prompt, JArray clip, T2IModel mode
["images"] = imageNode
}, id);
}
else if (IsMingImage() && isPositive)
{
JArray imageNode = GetPromptImage(true, true, 0);
for (int i = 1; i < 8; i++)
{
JArray image2 = GetPromptImage(true, true, i);
if (image2 is null)
{
break;
}
string batched = CreateNode("ImageBatch", new JObject()
{
["image1"] = imageNode,
["image2"] = image2
});
imageNode = [batched, 0];
}
node = CreateNode("SwarmTextEncodeAdvanced", new JObject()
{
["clip"] = clip,
["lora_hooks"] = CreateDynamicLoraHooks(),
["steps"] = steps,
["prompt"] = prompt,
["width"] = width,
["height"] = height,
["target_width"] = width,
["target_height"] = height,
["images"] = imageNode
}, id);
}
else if (IsHunyuanVideoI2V() && prompt.StartsWith("<image:"))
{
(string prefix, string content) = prompt.BeforeAndAfter('>');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ public bool HasFluxGuidance()
/// <summary>Returns true if the current model is a Z-Image model.</summary>
public bool IsZImage() => IsModelCompatClass(T2IModelClassSorter.CompatZImage);

/// <summary>Returns true if the current model is a Ming Image model.</summary>
public bool IsMingImage() => IsModelCompatClass(T2IModelClassSorter.CompatMingImage);

/// <summary>Returns true if the current model is a Zeta Chroma model.</summary>
public bool IsZetaChroma() => IsModelCompatClass(T2IModelClassSorter.CompatZetaChroma);

Expand Down Expand Up @@ -349,7 +352,7 @@ public WGNodeData EmptyImage(int width, int height, int batchSize, string id = n
["width"] = width
}, id));
}
else if (IsSD3() || IsFlux() || IsHiDream() || IsChroma() || IsOmniGen() || IsQwenImage() || IsZImage() || IsOvis() || IsKandinsky5ImgLite() || IsAnima() || IsLongcatImage() || IsKrea2())
else if (IsSD3() || IsFlux() || IsHiDream() || IsChroma() || IsOmniGen() || IsQwenImage() || IsZImage() || IsOvis() || IsKandinsky5ImgLite() || IsAnima() || IsLongcatImage() || IsKrea2() || IsMingImage())
{
return resultImage(CreateNode("EmptySD3LatentImage", new JObject()
{
Expand Down Expand Up @@ -708,6 +711,11 @@ public string GetQwenImage21TextEncoder()
return RequireClipModel("qwen3vl_8b_int8_convrot.safetensors", "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/text_encoders/qwen3vl_8b_int8_convrot.safetensors", "8bfd0f6e12abf2d2d697ecc888e5e90b0d6741d6708f05799f53afa560452e8f", T2IParamTypes.QwenModel);
}

public string GetMingImageTextEncoder()
{
return RequireClipModel("ming_image_0.1_ling_mini_2.0_int8_convrot.safetensors", "https://huggingface.co/Comfy-Org/Ming-Image/resolve/main/text_encoders/ming_image_0.1_ling_mini_2.0_int8_convrot.safetensors", "9d9f31cfce37c24ae1589287f5c33bd9dda80e14ddf773b5e17a0bb69c8cc4b1", null);
}

public string GetQwenImage25_7b_tenc()
{
return RequireClipModel("qwen_2.5_vl_7b_fp8_scaled.safetensors", "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/text_encoders/qwen_2.5_vl_7b_fp8_scaled.safetensors", "cb5636d852a0ea6a9075ab1bef496c0db7aef13c02350571e388aea959c5c0b4", T2IParamTypes.QwenModel);
Expand Down Expand Up @@ -1409,6 +1417,11 @@ public void LoadClip3(string type, string modelA, string modelB, string modelC)
});
LoadingModel = [samplingNode, 0];
}
else if (IsMingImage())
{
helpers.LoadClip("qwen_image", helpers.GetMingImageTextEncoder());
helpers.DoVaeLoader(null, T2IModelClassSorter.CompatMingImage, "ming-image-vae");
}
else if (IsHunyuanImage())
{
helpers.LoadClip2("hunyuan_image", helpers.GetQwenImage25_7b_tenc(), helpers.GetByT5SmallGlyphxl_tenc());
Expand Down Expand Up @@ -1616,7 +1629,7 @@ public void LoadClip3(string type, string modelA, string modelB, string modelC)
});
LoadingModel = [samplingNode, 0];
}
else if (IsZImage() || IsAceStep15() || IsAnima() || IsBoogu())
else if (IsZImage() || IsAceStep15() || IsAnima() || IsBoogu() || IsMingImage())
{
string samplingNode = CreateNode("ModelSamplingAuraFlow", new JObject()
{
Expand Down
1 change: 1 addition & 0 deletions src/Text2Image/CommonModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public static void RegisterCoreSet()
Register(new("ltx2-3-audio-vae", "LTX-2.3 Audio VAE", "The audio VAE for Lightricks LTX-2.3.", "https://huggingface.co/Kijai/LTX2.3_comfy/resolve/main/vae/LTX23_audio_vae_bf16.safetensors", "5bc10fa4adecf99dda132d916e23048cbd56797702c5fa50eb5d2079048a38c3", "VAE", "LTX-2/LTX23_audio_vae_bf16.safetensors"));
Register(new("ltx2-5-video-vae", "LTX-2.5 Video VAE", "The video VAE for Lightricks LTX-2.5.", "https://huggingface.co/mcmonkey/swarm-vaes/resolve/main/ltx-2.5-video-vae-conv-bf16.safetensors", "685b06ee3d9b2039647698fc4ea33175112462fc374e2777312c907897dfce8d", "VAE", "LTX-2/ltx-2.5-video-vae-conv-bf16.safetensors"));
Register(new("ltx2-5-audio-vae", "LTX-2.5 Audio VAE", "The audio VAE for Lightricks LTX-2.5.", "https://huggingface.co/mcmonkey/swarm-vaes/resolve/main/ltx-2.5-audio-vae-bf16.safetensors", "c52733d37f6a7fb7949c3dc0fb468c6cb2169e4d836983a73babb9f0d54837a5", "VAE", "LTX-2/ltx-2.5-audio-vae-bf16.safetensors"));
Register(new("ming-image-vae", "Ming Image VAE", "The VAE for Ming Image", "https://huggingface.co/Comfy-Org/Ming-Image/resolve/main/vae/ming_image_vae_bf16.safetensors", "7f5bed402dc8c77dc2e0ab1929a85d4df433b7cf7b599dfa8c353da98db0b90a", "VAE", "MingImage/ming_image_vae_bf16.safetensors"));
Register(new("qwen-image-2.1-vae", "Qwen Image 2.1 VAE", "The RGBA VAE for Qwen Image 2.1", "https://huggingface.co/Comfy-Org/Qwen-Image-2.1/resolve/main/vae/qwen_image_2.1_vae_bf16.safetensors", "bb21f7473051e1ac368515dd3f2e15cd44d7a11748ee8823e1ddca3e4876b7c9", "VAE", "QwenImage/qwen_image_2.1_vae_bf16.safetensors"));
Register(new("qwen-image-vae", "Qwen Image VAE", "The VAE for Qwen Image", "https://huggingface.co/Comfy-Org/Qwen-Image_ComfyUI/resolve/main/split_files/vae/qwen_image_vae.safetensors", "a70580f0213e67967ee9c95f05bb400e8fb08307e017a924bf3441223e023d1f", "VAE", "QwenImage/qwen_image_vae.safetensors"));
Register(new("mage-flow-vae", "Mage-VAE", "The VAE for Mage-Flow", "https://huggingface.co/microsoft/Mage-Flow/resolve/main/vae/diffusion_pytorch_model.safetensors", "34e076dc1e8a15321e1e07be5111d59cf16dd10b804b7c7e20b4de29013427e0", "VAE", "MageFlow/mage_flow_vae.safetensors"));
Expand Down
17 changes: 15 additions & 2 deletions src/Text2Image/T2IModelClassSorter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public static T2IVAEFamily RegisterVaeFamily(string id, string knownVaeId, strin
VaeSd3 = RegisterVaeFamily("sd3", "sd35-vae", "stable-diffusion-v3"),
VaeSdxl = RegisterVaeFamily("sdxl", "sdxl-vae", "stable-diffusion-xl-v1"),
VaeQwenImage21 = RegisterVaeFamily("qwenimage21", "qwen-image-2.1-vae", "qwen-image-2.1"),
VaeQwenImage = RegisterVaeFamily("qwenimage", "qwen-image-vae", "qwen-image");
VaeQwenImage = RegisterVaeFamily("qwenimage", "qwen-image-vae", "qwen-image"),
VaeMingImage = RegisterVaeFamily("mingimage", "ming-image-vae", "ming-image");

/// <summary>Core Compatibility classes.</summary>
public static T2IModelCompatClass
Expand Down Expand Up @@ -78,6 +79,7 @@ public static T2IModelCompatClass
CompatLumina2 = RegisterCompat(new() { ID = "lumina-2", ShortCode = "Lumi2", VaeFamily = VaeFlux1 }),
CompatQwenImage21 = RegisterCompat(new() { ID = "qwen-image-2.1", ShortCode = "Qwen21", LorasTargetTextEnc = false, VaeFamily = VaeQwenImage21, ResolutionPrecision = 32, SupportsAlpha = true }),
CompatQwenImage = RegisterCompat(new() { ID = "qwen-image", ShortCode = "Qwen", LorasTargetTextEnc = false, VaeFamily = VaeQwenImage }),
CompatMingImage = RegisterCompat(new() { ID = "ming-image", ShortCode = "Ming", LorasTargetTextEnc = false, VaeFamily = VaeMingImage, SupportsAlpha = true }),
CompatHunyuanImage2_1 = RegisterCompat(new() { ID = "hunyuan-image-2_1", ShortCode = "HyImg", LorasTargetTextEnc = false }),
CompatHunyuanImage2_1Refiner = RegisterCompat(new() { ID = "hunyuan-image-2_1-refiner", ShortCode = "HyImg", LorasTargetTextEnc = false }),
CompatHunyuanVideo1_5 = RegisterCompat(new() { ID = "hunyuan-video-1_5", ShortCode = "HyVid", LorasTargetTextEnc = false, IsText2Video = true, IsImage2Video = true }),
Expand Down Expand Up @@ -230,6 +232,8 @@ bool isLtxv2Lora(JObject h) => (hasLoraKey(h, "transformer_blocks.0.attn1.to_k")
bool isCosmosPredict2_14B(JObject h) => h.ContainsKey("net.blocks.0.adaln_modulation_cross_attn.1.weight") && h.ContainsKey("net.pos_embedder.dim_temporal_range") && h.ContainsKey("net.x_embedder.proj.1.weight") && h.ContainsKey("net.blocks.35.adaln_modulation_mlp.2.weight");
bool isLumina2(JObject h) => hasKey(h, "cap_embedder.0.weight");
bool isZImage(JObject h) => (hasKey(h, "context_refiner.0.attention.k_norm.weight") || hasKey(h, "context_refiner.0.attention.norm_k.weight")) && hasKey(h, "layers.0.adaLN_modulation.0.bias");
bool isMingImage(JObject h) => tryGetKey(h, "cap_embedder.1.weight", out JToken tok) && tok["shape"].ToArray()[0].Value<long>() == 3840 && hasKey(h, "noise_refiner.0.attention.k_norm.weight") && !hasKey(h, "cap_pad_token") && !hasKey(h, "dec_net.cond_embed.weight");
bool isMingImageVae(JObject h) => h.ContainsKey("decoder.upsamples.0.residual.2.weight") && h["decoder.head.2.weight"]?["shape"] is JArray shape && shape.Count == 5 && shape[0].Value<int>() == 4;
bool isZetaChroma(JObject h) => hasKey(h, "dec_net.input_embedder.embedder.0.bias") && hasKey(h, "__x0__");
bool isOvis(JObject h) => hasKey(h, "double_blocks.0.img_mlp.down_proj.weight");
bool isZImageLora(JObject h) => (hasLoraKey(h, "layers.0.adaLN_modulation.0") && hasLoraKey(h, "layers.9.feed_forward.w3"))
Expand Down Expand Up @@ -675,7 +679,7 @@ JToken GetEmbeddingKey(JObject h)
// ====================== Z-Image ======================
Register(new() { ID = "z-image", CompatClass = CompatZImage, Name = "Z-Image", StandardWidth = 1024, StandardHeight = 1024, IsThisModelOfClass = (m, h) =>
{
return isLumina2(h) && isZImage(h) && !isZetaChroma(h);
return isLumina2(h) && isZImage(h) && !isZetaChroma(h) && !isMingImage(h);
}});
Register(new() { ID = "z-image/lora", CompatClass = CompatZImage, Name = "Z-Image LoRA", StandardWidth = 1024, StandardHeight = 1024, IsThisModelOfClass = (m, h) =>
{
Expand All @@ -690,6 +694,15 @@ JToken GetEmbeddingKey(JObject h)
{
return isLumina2(h) && isZImage(h) && isZetaChroma(h);
}});
// ====================== Ming Image ======================
Register(new() { ID = "ming-image", CompatClass = CompatMingImage, Name = "Ming Image", StandardWidth = 2048, StandardHeight = 2048, IsThisModelOfClass = (m, h) =>
{
return isMingImage(h);
}});
Register(new() { ID = "ming-image/vae", CompatClass = CompatMingImage, Name = "Ming Image VAE", StandardWidth = 1024, StandardHeight = 1024, IsThisModelOfClass = (m, h) =>
{
return isMingImageVae(h);
}});
// ====================== Qwen Image 2.1 ======================
Register(new() { ID = "qwen-image-2.1", CompatClass = CompatQwenImage21, Name = "Qwen Image 2.1", StandardWidth = 1024, StandardHeight = 1024, IsThisModelOfClass = (m, h) =>
{
Expand Down
Loading