Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

310 lines
9.8 KiB

using System.Diagnostics.CodeAnalysis;
using StabilityMatrix.Core.Models.Api.Comfy.NodeTypes;
namespace StabilityMatrix.Core.Models.Api.Comfy.Nodes;
/// <summary>
/// Builder functions for comfy nodes
/// </summary>
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
public class ComfyNodeBuilder
{
private readonly NodeDictionary nodes;
public ComfyNodeBuilder(NodeDictionary nodes)
{
this.nodes = nodes;
}
private static string GetRandomPrefix() => Guid.NewGuid().ToString()[..8];
public static NamedComfyNode<LatentNodeConnection> VAEEncode(
string name,
ImageNodeConnection pixels,
VAENodeConnection vae
)
{
return new NamedComfyNode<LatentNodeConnection>(name)
{
ClassType = "VAEEncode",
Inputs = new Dictionary<string, object?> { ["pixels"] = pixels.Data, ["vae"] = vae.Data }
};
}
public static NamedComfyNode<ImageNodeConnection> VAEDecode(
string name,
LatentNodeConnection samples,
VAENodeConnection vae
)
{
return new NamedComfyNode<ImageNodeConnection>(name)
{
ClassType = "VAEDecode",
Inputs = new Dictionary<string, object?> { ["samples"] = samples.Data, ["vae"] = vae.Data }
};
}
public static NamedComfyNode<LatentNodeConnection> KSampler(
string name,
ModelNodeConnection model,
ulong seed,
int steps,
double cfg,
string samplerName,
string scheduler,
ConditioningNodeConnection positive,
ConditioningNodeConnection negative,
LatentNodeConnection latentImage,
double denoise
)
{
return new NamedComfyNode<LatentNodeConnection>(name)
{
ClassType = "KSampler",
Inputs = new Dictionary<string, object?>
{
["model"] = model.Data,
["seed"] = seed,
["steps"] = steps,
["cfg"] = cfg,
["sampler_name"] = samplerName,
["scheduler"] = scheduler,
["positive"] = positive.Data,
["negative"] = negative.Data,
["latent_image"] = latentImage.Data,
["denoise"] = denoise
}
};
}
public static NamedComfyNode<ImageNodeConnection> ImageUpscaleWithModel(
string name,
UpscaleModelNodeConnection upscaleModel,
ImageNodeConnection image
)
{
return new NamedComfyNode<ImageNodeConnection>(name)
{
ClassType = "ImageUpscaleWithModel",
Inputs = new Dictionary<string, object?>
{
["upscale_model"] = upscaleModel.Data,
["image"] = image.Data
}
};
}
public static NamedComfyNode<UpscaleModelNodeConnection> UpscaleModelLoader(
string name,
string modelName)
{
return new NamedComfyNode<UpscaleModelNodeConnection>(name)
{
ClassType = "UpscaleModelLoader",
Inputs = new Dictionary<string, object?> { ["model_name"] = modelName }
};
}
public static NamedComfyNode<ImageNodeConnection> ImageScale(
string name,
ImageNodeConnection image,
string method,
int height,
int width,
bool crop)
{
return new NamedComfyNode<ImageNodeConnection>(name)
{
ClassType = "ImageScale",
Inputs = new Dictionary<string, object?>
{
["image"] = image.Data,
["upscale_method"] = method,
["height"] = height,
["width"] = width,
["crop"] = crop ? "center" : "disabled"
}
};
}
public static NamedComfyNode<VAENodeConnection> VAELoader(
string name,
string vaeModelName)
{
return new NamedComfyNode<VAENodeConnection>(name)
{
ClassType = "VAELoader",
Inputs = new Dictionary<string, object?>
{
["vae_name"] = vaeModelName
}
};
}
public ImageNodeConnection Lambda_LatentToImage(LatentNodeConnection latent, VAENodeConnection vae)
{
return nodes.AddNamedNode(VAEDecode($"{GetRandomPrefix()}_VAEDecode", latent, vae)).Output;
}
public LatentNodeConnection Lambda_ImageToLatent(ImageNodeConnection pixels, VAENodeConnection vae)
{
return nodes.AddNamedNode(VAEEncode($"{GetRandomPrefix()}_VAEEncode", pixels, vae)).Output;
}
/// <summary>
/// Create a group node that upscales a given image with a given model
/// </summary>
public NamedComfyNode<ImageNodeConnection> Group_UpscaleWithModel(string name, string modelName, ImageNodeConnection image)
{
var modelLoader = nodes.AddNamedNode(
UpscaleModelLoader($"{name}_UpscaleModelLoader", modelName));
var upscaler = nodes.AddNamedNode(
ImageUpscaleWithModel($"{name}_ImageUpscaleWithModel", modelLoader.Output, image));
return upscaler;
}
/// <summary>
/// Create a group node that scales a given image to a given size
/// </summary>
public NamedComfyNode<LatentNodeConnection> Group_UpscaleToLatent(string name,
LatentNodeConnection latent, VAENodeConnection vae,
ComfyUpscaler upscaleInfo, int width, int height)
{
if (upscaleInfo.Type == ComfyUpscalerType.Latent)
{
return nodes
.AddNamedNode(
new NamedComfyNode<LatentNodeConnection>($"{name}_LatentUpscale")
{
ClassType = "LatentUpscale",
Inputs = new Dictionary<string, object?>
{
["upscale_method"] = upscaleInfo.Name,
["width"] = width,
["height"] = height,
["crop"] = "disabled",
["samples"] = latent.Data,
}
}
);
}
if (upscaleInfo.Type == ComfyUpscalerType.ESRGAN)
{
// Convert to image space
var samplerImage = nodes.AddNamedNode(
VAEDecode(
$"{name}_VAEDecode",
latent,
vae
)
);
// Do group upscale
var modelUpscaler = Group_UpscaleWithModel(
$"{name}_ModelUpscale",
upscaleInfo.Name,
samplerImage.Output
);
// Since the model upscale is fixed to model (2x/4x), scale it again to the requested size
var resizedScaled = nodes.AddNamedNode(
ImageScale(
$"{name}_ImageScale",
modelUpscaler.Output,
"bilinear",
height,
width,
false
)
);
// Convert back to latent space
return nodes
.AddNamedNode(
VAEEncode(
$"{name}_VAEEncode",
resizedScaled.Output,
vae
)
);
}
throw new InvalidOperationException($"Unknown upscaler type: {upscaleInfo.Type}");
}
/// <summary>
/// Create a group node that scales a given image to image output
/// </summary>
public NamedComfyNode<ImageNodeConnection> Group_UpscaleToImage(string name,
LatentNodeConnection latent, VAENodeConnection vae,
ComfyUpscaler upscaleInfo, int width, int height)
{
if (upscaleInfo.Type == ComfyUpscalerType.Latent)
{
var latentUpscale = nodes
.AddNamedNode(
new NamedComfyNode<LatentNodeConnection>($"{name}_LatentUpscale")
{
ClassType = "LatentUpscale",
Inputs = new Dictionary<string, object?>
{
["upscale_method"] = upscaleInfo.Name,
["width"] = width,
["height"] = height,
["crop"] = "disabled",
["samples"] = latent.Data,
}
}
);
// Convert to image space
return nodes.AddNamedNode(
VAEDecode(
$"{name}_VAEDecode",
latentUpscale.Output,
vae
)
);
}
if (upscaleInfo.Type == ComfyUpscalerType.ESRGAN)
{
// Convert to image space
var samplerImage = nodes.AddNamedNode(
VAEDecode(
$"{name}_VAEDecode",
latent,
vae
)
);
// Do group upscale
var modelUpscaler = Group_UpscaleWithModel(
$"{name}_ModelUpscale",
upscaleInfo.Name,
samplerImage.Output
);
// Since the model upscale is fixed to model (2x/4x), scale it again to the requested size
var resizedScaled = nodes.AddNamedNode(
ImageScale(
$"{name}_ImageScale",
modelUpscaler.Output,
"bilinear",
height,
width,
false
)
);
// No need to convert back to latent space
return resizedScaled;
}
throw new InvalidOperationException($"Unknown upscaler type: {upscaleInfo.Type}");
}
}