From 489c23395c9deb73ce92bd5f9318c09089455e5a Mon Sep 17 00:00:00 2001 From: DarthAffe Date: Sun, 5 Jan 2025 15:02:26 +0100 Subject: [PATCH] Fixed Image2Image and added Inpainting support --- StableDiffusion.NET/Models/DiffusionModel.cs | 67 ++++++++++++++++++- .../Extensions/DiffusionParameterExtension.cs | 7 ++ StableDiffusion.NET/Native/Native.cs | 1 + 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/StableDiffusion.NET/Models/DiffusionModel.cs b/StableDiffusion.NET/Models/DiffusionModel.cs index e53b698..12b11b9 100644 --- a/StableDiffusion.NET/Models/DiffusionModel.cs +++ b/StableDiffusion.NET/Models/DiffusionModel.cs @@ -202,17 +202,75 @@ public sealed unsafe class DiffusionModel : IDisposable ObjectDisposedException.ThrowIf(_disposed, this); ArgumentNullException.ThrowIfNull(prompt); + ArgumentNullException.ThrowIfNull(image); parameter.Validate(); if (image is not IImage refImage) refImage = image.ConvertTo(); - fixed (byte* imagePtr = refImage.AsRefImage()) - return ImageToImage(prompt, refImage.ToSdImage(imagePtr), parameter); + // DarthAffe 10.08.2024: Mask needs to be a 1 channel all max value image when it's not used - I really don't like this concept as it adds unnecessary allocations, but that's how it is :( + Span maskBuffer = new byte[image.Width * image.Height]; + maskBuffer.Fill(byte.MaxValue); + + fixed (byte* maskPtr = maskBuffer) + { + Native.sd_image_t maskImage = new() + { + width = (uint)image.Width, + height = (uint)image.Height, + channel = 1, + data = maskPtr + }; + + fixed (byte* imagePtr = refImage.AsRefImage()) + return ImageToImage(prompt, refImage.ToSdImage(imagePtr), maskImage, parameter); + } } - private IImage ImageToImage(string prompt, Native.sd_image_t image, DiffusionParameter parameter) + public IImage Inpaint(string prompt, IImage image, IImage mask, DiffusionParameter? parameter = null) + { + parameter ??= GetDefaultParameter(); + + ObjectDisposedException.ThrowIf(_disposed, this); + ArgumentNullException.ThrowIfNull(prompt); + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(mask); + + if (image.Width != mask.Width) throw new ArgumentException("The mask needs to have the same with as the image.", nameof(mask)); + if (image.Height != mask.Height) throw new ArgumentException("The mask needs to have the same height as the image.", nameof(mask)); + + parameter.Validate(); + + if (image is not IImage refImage) + refImage = image.ConvertTo(); + + // DarthAffe 10.08.2024: HPPH does currently not support monochrome images, that's why we need to convert it here. We're going for the simple conversion as the source image is supposed to be monochrome anyway. + Span maskBuffer = new byte[image.Width * image.Height]; + for (int y = 0; y < image.Height; y++) + for (int x = 0; x < image.Width; x++) + { + IColor color = mask[x, y]; + maskBuffer[(image.Width * y) + x] = (byte)Math.Round((color.R + color.G + color.B) / 3.0); + } + + fixed (byte* maskPtr = maskBuffer) + { + Native.sd_image_t maskImage = new() + { + width = (uint)image.Width, + height = (uint)image.Height, + channel = 1, + data = maskPtr + }; + + fixed (byte* imagePtr = refImage.AsRefImage()) + return ImageToImage(prompt, refImage.ToSdImage(imagePtr), maskImage, parameter); + } + + } + + private IImage ImageToImage(string prompt, Native.sd_image_t image, Native.sd_image_t mask, DiffusionParameter parameter) { ObjectDisposedException.ThrowIf(_disposed, this); ArgumentNullException.ThrowIfNull(prompt); @@ -246,6 +304,7 @@ public sealed unsafe class DiffusionModel : IDisposable result = Native.img2img(_ctx, image, + mask, prompt, parameter.NegativePrompt, parameter.ClipSkip, @@ -283,6 +342,7 @@ public sealed unsafe class DiffusionModel : IDisposable result = Native.img2img(_ctx, image, + mask, prompt, parameter.NegativePrompt, parameter.ClipSkip, @@ -312,6 +372,7 @@ public sealed unsafe class DiffusionModel : IDisposable { result = Native.img2img(_ctx, image, + mask, prompt, parameter.NegativePrompt, parameter.ClipSkip, diff --git a/StableDiffusion.NET/Models/Parameter/Extensions/DiffusionParameterExtension.cs b/StableDiffusion.NET/Models/Parameter/Extensions/DiffusionParameterExtension.cs index 53f4b3e..1372aed 100644 --- a/StableDiffusion.NET/Models/Parameter/Extensions/DiffusionParameterExtension.cs +++ b/StableDiffusion.NET/Models/Parameter/Extensions/DiffusionParameterExtension.cs @@ -64,6 +64,13 @@ public static class DiffusionParameterExtension return parameter; } + public static DiffusionParameter WithStrength(this DiffusionParameter parameter, float strength) + { + parameter.Strength = strength; + + return parameter; + } + public static DiffusionParameter WithSlgScale(this DiffusionParameter parameter, float slgScale) { parameter.SlgScale = slgScale; diff --git a/StableDiffusion.NET/Native/Native.cs b/StableDiffusion.NET/Native/Native.cs index a532b48..6042aee 100644 --- a/StableDiffusion.NET/Native/Native.cs +++ b/StableDiffusion.NET/Native/Native.cs @@ -102,6 +102,7 @@ internal unsafe partial class Native [LibraryImport(LIB_NAME, EntryPoint = "img2img")] internal static partial sd_image_t* img2img(sd_ctx_t* sd_ctx, sd_image_t init_image, + sd_image_t mask_image, [MarshalAs(UnmanagedType.LPStr)] string prompt, [MarshalAs(UnmanagedType.LPStr)] string negative_prompt, int clip_skip,