From 73ae5c378c03ba246ff13c24e8b1eed4470e45d8 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 15:14:38 -0400 Subject: [PATCH 01/17] Fix metadata reading to not use absolute header offset --- StabilityMatrix.Core/Helper/ImageMetadata.cs | 21 ++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/StabilityMatrix.Core/Helper/ImageMetadata.cs b/StabilityMatrix.Core/Helper/ImageMetadata.cs index 73557993..a10dae53 100644 --- a/StabilityMatrix.Core/Helper/ImageMetadata.cs +++ b/StabilityMatrix.Core/Helper/ImageMetadata.cs @@ -13,8 +13,9 @@ public class ImageMetadata { private IReadOnlyList? Directories { get; set; } - private static readonly byte[] Idat = { 0x49, 0x44, 0x41, 0x54 }; - private static readonly byte[] Text = { 0x74, 0x45, 0x58, 0x74 }; + private static readonly byte[] PngHeader = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; + private static readonly byte[] Idat = "IDAT"u8.ToArray(); + private static readonly byte[] Text = "tEXt"u8.ToArray(); public static ImageMetadata ParseFile(FilePath path) { @@ -139,12 +140,19 @@ public class ImageMetadata public static string ReadTextChunk(BinaryReader byteStream, string key) { - // skip to end of png header stuff - byteStream.BaseStream.Position = 0x21; + byteStream.BaseStream.Position = 0; + + // Read first 8 bytes and make sure they match the png header + if (!byteStream.ReadBytes(8).SequenceEqual(PngHeader)) + { + return string.Empty; + } + while (byteStream.BaseStream.Position < byteStream.BaseStream.Length - 4) { var chunkSize = BitConverter.ToInt32(byteStream.ReadBytes(4).Reverse().ToArray()); var chunkType = Encoding.UTF8.GetString(byteStream.ReadBytes(4)); + if (chunkType == Encoding.UTF8.GetString(Idat)) { return string.Empty; @@ -159,6 +167,11 @@ public class ImageMetadata return text[(key.Length + 1)..]; } } + else + { + // skip chunk data + byteStream.BaseStream.Position += chunkSize; + } // skip crc byteStream.BaseStream.Position += 4; From a2053fdcb364b04d1fdef78cb8a032e8b89259c9 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 15:24:31 -0400 Subject: [PATCH 02/17] Fix write directory for grid images --- .../ViewModels/Base/InferenceGenerationViewModelBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs index 2ddd9636..cad98800 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs @@ -235,6 +235,8 @@ public abstract partial class InferenceGenerationViewModelBase // Download all images to make grid, if multiple if (outputImages.Count > 1) { + var outputDir = outputImages[0].LocalFile!.Directory; + var loadedImages = outputImages .Select(i => i.LocalFile) .Where(f => f is { Exists: true }) @@ -255,7 +257,7 @@ public abstract partial class InferenceGenerationViewModelBase // Save to disk var lastName = outputImages.Last().LocalFile?.Info.Name; - var gridPath = args.Client.OutputImagesDir!.JoinFile($"grid-{lastName}"); + var gridPath = outputDir!.JoinFile($"grid-{lastName}"); await using (var fileStream = gridPath.Info.OpenWrite()) { From be0796808ac99f429b9bcab33fa299fea0c4182e Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 18:22:01 -0400 Subject: [PATCH 03/17] Cleanup imports --- StabilityMatrix.Avalonia/Controls/SeedCard.axaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/SeedCard.axaml b/StabilityMatrix.Avalonia/Controls/SeedCard.axaml index 3736348f..53773b86 100644 --- a/StabilityMatrix.Avalonia/Controls/SeedCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/SeedCard.axaml @@ -1,9 +1,7 @@  From 4dbdcb4290933e29f2be4dc721841e1782d9268b Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 18:22:46 -0400 Subject: [PATCH 04/17] Use conditional trace in LoadableViewModel serialization --- .../ViewModels/Base/LoadableViewModelBase.cs | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs index d5986fcb..53f205fb 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/LoadableViewModelBase.cs @@ -34,19 +34,19 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState && !typeof(IJsonLoadableState).IsAssignableFrom(property.PropertyType) ) { - Logger.Trace("Skipping {Property} - read-only", property.Name); + Logger.ConditionalTrace("Skipping {Property} - read-only", property.Name); return true; } // Check not JsonIgnore if (property.GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0) { - Logger.Trace("Skipping {Property} - has [JsonIgnore]", property.Name); + Logger.ConditionalTrace("Skipping {Property} - has [JsonIgnore]", property.Name); return true; } // Check not excluded type if (SerializerIgnoredTypes.Contains(property.PropertyType)) { - Logger.Trace( + Logger.ConditionalTrace( "Skipping {Property} - serializer ignored type {Type}", property.Name, property.PropertyType @@ -56,7 +56,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState // Check not ignored name if (SerializerIgnoredNames.Contains(property.Name, StringComparer.Ordinal)) { - Logger.Trace("Skipping {Property} - serializer ignored name", property.Name); + Logger.ConditionalTrace("Skipping {Property} - serializer ignored name", property.Name); return true; } @@ -71,7 +71,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState // Has JsonIncludeAttribute if (property.GetCustomAttributes(typeof(JsonIncludeAttribute), true).Length > 0) { - Logger.Trace("Including {Property} - has [JsonInclude]", property.Name); + Logger.ConditionalTrace("Including {Property} - has [JsonInclude]", property.Name); return true; } @@ -94,7 +94,11 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState { // Get all of our properties using reflection var properties = GetType().GetProperties(); - Logger.Trace("Serializing {Type} with {Count} properties", GetType(), properties.Length); + Logger.ConditionalTrace( + "Serializing {Type} with {Count} properties", + GetType(), + properties.Length + ); foreach (var property in properties) { @@ -108,7 +112,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState is JsonPropertyNameAttribute jsonPropertyName ) { - Logger.Trace( + Logger.ConditionalTrace( "Deserializing {Property} ({Type}) with JsonPropertyName {JsonPropertyName}", property.Name, property.PropertyType, @@ -120,7 +124,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState // Check if property is in the JSON object if (!state.TryGetPropertyValue(name, out var value)) { - Logger.Trace("Skipping {Property} - not in JSON object", property.Name); + Logger.ConditionalTrace("Skipping {Property} - not in JSON object", property.Name); continue; } @@ -133,7 +137,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState // For types that also implement IJsonLoadableState, defer to their load implementation if (typeof(IJsonLoadableState).IsAssignableFrom(property.PropertyType)) { - Logger.Trace( + Logger.ConditionalTrace( "Loading {Property} ({Type}) with IJsonLoadableState", property.Name, property.PropertyType @@ -171,7 +175,11 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState } else { - Logger.Trace("Loading {Property} ({Type})", property.Name, property.PropertyType); + Logger.ConditionalTrace( + "Loading {Property} ({Type})", + property.Name, + property.PropertyType + ); var propertyValue = value.Deserialize(property.PropertyType, SerializerOptions); property.SetValue(this, propertyValue); @@ -195,7 +203,11 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState { // Get all of our properties using reflection. var properties = GetType().GetProperties(); - Logger.Trace("Serializing {Type} with {Count} properties", GetType(), properties.Length); + Logger.ConditionalTrace( + "Serializing {Type} with {Count} properties", + GetType(), + properties.Length + ); // Create a JSON object to store the state. var state = new JsonObject(); @@ -218,7 +230,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState is JsonPropertyNameAttribute jsonPropertyName ) { - Logger.Trace( + Logger.ConditionalTrace( "Serializing {Property} ({Type}) with JsonPropertyName {JsonPropertyName}", property.Name, property.PropertyType, @@ -230,7 +242,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState // For types that also implement IJsonLoadableState, defer to their implementation. if (typeof(IJsonLoadableState).IsAssignableFrom(property.PropertyType)) { - Logger.Trace( + Logger.ConditionalTrace( "Serializing {Property} ({Type}) with IJsonLoadableState", property.Name, property.PropertyType @@ -245,7 +257,7 @@ public abstract class LoadableViewModelBase : ViewModelBase, IJsonLoadableState } else { - Logger.Trace( + Logger.ConditionalTrace( "Serializing {Property} ({Type})", property.Name, property.PropertyType From f4d287e2d10997c51b299c71fde194e4d78e5dc1 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 18:50:16 -0400 Subject: [PATCH 05/17] Add LatentFromBatch node --- .../Api/Comfy/Nodes/ComfyNodeBuilder.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs index 6e9d3092..b4c8d0b1 100644 --- a/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs +++ b/StabilityMatrix.Core/Models/Api/Comfy/Nodes/ComfyNodeBuilder.cs @@ -144,6 +144,25 @@ public class ComfyNodeBuilder }; } + public static NamedComfyNode LatentFromBatch( + string name, + LatentNodeConnection samples, + int batchIndex, + int length + ) + { + return new NamedComfyNode(name) + { + ClassType = "LatentFromBatch", + Inputs = new Dictionary + { + ["samples"] = samples.Data, + ["batch_index"] = batchIndex, + ["length"] = length, + } + }; + } + public static NamedComfyNode ImageUpscaleWithModel( string name, UpscaleModelNodeConnection upscaleModel, From dce7a5f593ba385d1f21291770d96b2a6c9a6643 Mon Sep 17 00:00:00 2001 From: Ionite Date: Sun, 8 Oct 2023 19:42:16 -0400 Subject: [PATCH 06/17] Add batch index selection and metadata saving --- .../Controls/BatchSizeCard.axaml | 45 +++++++++- .../DesignData/DesignData.cs | 6 ++ .../Extensions/ComfyNodeBuilderExtensions.cs | 16 ++++ .../Languages/Resources.Designer.cs | 9 ++ .../Languages/Resources.resx | 3 + .../Models/InferenceProjectDocument.cs | 88 ++++++++++++++++++- .../Base/InferenceGenerationViewModelBase.cs | 31 ++++++- .../Inference/BatchSizeCardViewModel.cs | 14 ++- 8 files changed, 201 insertions(+), 11 deletions(-) diff --git a/StabilityMatrix.Avalonia/Controls/BatchSizeCard.axaml b/StabilityMatrix.Avalonia/Controls/BatchSizeCard.axaml index b2cf9dc8..47ab4045 100644 --- a/StabilityMatrix.Avalonia/Controls/BatchSizeCard.axaml +++ b/StabilityMatrix.Avalonia/Controls/BatchSizeCard.axaml @@ -4,6 +4,8 @@ xmlns:vmInference="using:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:controls="using:StabilityMatrix.Avalonia.Controls" xmlns:icons="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" + xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" x:DataType="vmInference:BatchSizeCardViewModel"> @@ -12,16 +14,23 @@ DataContext="{x:Static mocks:DesignData.BatchSizeCardViewModel}"/> + DataContext="{x:Static mocks:DesignData.BatchSizeCardViewModelWithIndexOption}"/>