feat: working on adding abstractions
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.20.1" />
|
||||
<PackageReference Include="Serilog" Version="4.2.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageReference Include="Spectre.Console" Version="0.49.1" />
|
||||
<PackageReference Include="Spectre.Console.Cli" Version="0.49.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Files\**\*" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace BGR.Console.Common;
|
||||
|
||||
internal static class HostBuilderExtensions
|
||||
{
|
||||
public static CommandApp BuildApp(this IHostBuilder builder)
|
||||
{
|
||||
var registrar = new TypeRegistrar(builder);
|
||||
var app = new CommandApp(registrar);
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
namespace BGR.Console.Common;
|
||||
|
||||
internal class TypeRegistrar(IHostBuilder builder) : ITypeRegistrar
|
||||
{
|
||||
private readonly IHostBuilder _builder = builder;
|
||||
|
||||
public ITypeResolver Build()
|
||||
{
|
||||
return new TypeResolver(_builder.Build());
|
||||
}
|
||||
|
||||
public void Register(Type service, Type implementation)
|
||||
{
|
||||
_builder.ConfigureServices((_, services) => services.AddSingleton(service, implementation));
|
||||
}
|
||||
|
||||
public void RegisterInstance(Type service, object implementation)
|
||||
{
|
||||
_builder.ConfigureServices((_, services) => services.AddSingleton(service, implementation));
|
||||
}
|
||||
|
||||
public void RegisterLazy(Type service, Func<object> func)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(func);
|
||||
|
||||
_builder.ConfigureServices((_, services) => services.AddSingleton(service, _ => func()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace BGR.Console.Common;
|
||||
|
||||
internal class TypeResolver(IHost provider) : ITypeResolver, IDisposable
|
||||
{
|
||||
private readonly IHost _host = provider ?? throw new ArgumentNullException(nameof(provider));
|
||||
|
||||
public object? Resolve(Type? type)
|
||||
{
|
||||
return type is not null ? _host.Services.GetService(type) : null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_host.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.WriteTo.File(
|
||||
formatter: new CompactJsonFormatter(),
|
||||
path: Path.Combine(AppContext.BaseDirectory, "logs", "log.jsonl"),
|
||||
rollingInterval: RollingInterval.Day
|
||||
)
|
||||
.Enrich.FromLogContext()
|
||||
.MinimumLevel.Verbose()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Fatal)
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
var appName = Assembly.GetExecutingAssembly().GetName().Name;
|
||||
Log.Information("Starting {AppName}", appName);
|
||||
|
||||
await Host.CreateDefaultBuilder()
|
||||
.ConfigureLogging(static logging => logging.ClearProviders())
|
||||
.ConfigureServices(static (_, services) =>
|
||||
{
|
||||
services.AddSerilog();
|
||||
services.AddSingleton<IResourceManager, ResourceManager>();
|
||||
})
|
||||
.BuildApp()
|
||||
.RunAsync(args);
|
||||
|
||||
Log.Information("Stopping {AppName}", appName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Application terminated unexpectedly");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Log.CloseAndFlushAsync();
|
||||
}
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("Usage: BackgroundRemover <input_image_path>");
|
||||
return;
|
||||
}
|
||||
|
||||
var inputImagePath = args[0];
|
||||
var maskImagePath = Path.ChangeExtension(inputImagePath, null) + "_mask.png";
|
||||
var outputImagePath = Path.ChangeExtension(inputImagePath, null) + "_no_bg.png";
|
||||
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = "BGR.Console.Resources.Files.rmbg.onnx";
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName) ?? throw new FileNotFoundException("Model not found in embedded resources.");
|
||||
var modelBytes = new byte[stream.Length];
|
||||
stream.ReadExactly(modelBytes);
|
||||
|
||||
using var image = await Image.LoadAsync<Rgba32>(inputImagePath);
|
||||
var inputTensor = CreateTensorInput(image);
|
||||
|
||||
using var options = new SessionOptions() { LogSeverityLevel = OrtLoggingLevel.ORT_LOGGING_LEVEL_ERROR };
|
||||
using InferenceSession session = new(modelBytes, options);
|
||||
var inputs = new List<NamedOnnxValue>()
|
||||
{
|
||||
NamedOnnxValue.CreateFromTensor(session.InputNames[0], inputTensor),
|
||||
};
|
||||
|
||||
using var results = session.Run(inputs);
|
||||
var outputTensor = results[0].AsTensor<float>();
|
||||
|
||||
using var mask = GenerateMask(outputTensor, image.Width, image.Height);
|
||||
|
||||
using var bgRemoved = GetImageWithBackgroundRemoved(image, mask);
|
||||
|
||||
var encoder = new PngEncoder { CompressionLevel = PngCompressionLevel.BestCompression };
|
||||
|
||||
await mask.SaveAsync(maskImagePath, encoder);
|
||||
await bgRemoved.SaveAsync(outputImagePath, encoder);
|
||||
|
||||
Console.WriteLine($"Background removed and saved to {outputImagePath}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
static Tensor<float> CreateTensorInput(Image<Rgba32> image)
|
||||
{
|
||||
// U2Net expects input images to be 320x320. This is dependent on the model.
|
||||
const int targetWidth = 1024;
|
||||
const int targetHeight = 1024;
|
||||
|
||||
// ImageNet normalization parameters
|
||||
// source:
|
||||
// - https://www.image-net.org/
|
||||
// - https://pytorch.org calculated these values from the ImageNet dataset
|
||||
// and they are commonly used for models trained on ImageNet so we use them here
|
||||
// to normalize the input image to better match the distribution of the data the model was trained on
|
||||
// NOTE: These values are not universal and may vary for different models
|
||||
const float rMean = 0.485f; // Mean value for Red channel
|
||||
const float gMean = 0.456f; // Mean value for Green channel
|
||||
const float bMean = 0.406f; // Mean value for Blue channel
|
||||
const float rStd = 0.229f; // Standard deviation for Red channel
|
||||
const float gStd = 0.224f; // Standard deviation for Green channel
|
||||
const float bStd = 0.225f; // Standard deviation for Blue channel
|
||||
const float pixelMax = 255f; // Maximum pixel intensity for normalization
|
||||
|
||||
// Create a temporary image for preprocessing
|
||||
using var resized = image.Clone();
|
||||
resized.Mutate(x => x.Resize(targetWidth, targetHeight));
|
||||
|
||||
// Create tensor of shape (1, 3, 320, 320)
|
||||
// 1 for batch size, 3 for RGB channels, 320x320 for image dimensions
|
||||
DenseTensor<float> tensor = new([1, 3, targetHeight, targetWidth]);
|
||||
|
||||
// Normalize pixel values and copy to tensor
|
||||
WalkImage(resized.Height, resized.Width, (x, y) =>
|
||||
{
|
||||
var pixel = resized[x, y];
|
||||
|
||||
// u2net expects expect input images to be normalized using ImageNet mean and std
|
||||
// to better match the distribution of the data the model was trained on
|
||||
// Normalize to range [0, 1] and standardize using ImageNet mean/std
|
||||
// The tensor is filled with normalized pixel values
|
||||
tensor[0, 0, y, x] = ((pixel.R / pixelMax) - rMean) / rStd; // Red channel
|
||||
tensor[0, 1, y, x] = ((pixel.G / pixelMax) - gMean) / gStd; // Green channel
|
||||
tensor[0, 2, y, x] = ((pixel.B / pixelMax) - bMean) / bStd; // Blue channel
|
||||
});
|
||||
|
||||
return tensor;
|
||||
}
|
||||
|
||||
static Image<Rgba32> GenerateMask(Tensor<float> maskTensor, int width, int height)
|
||||
{
|
||||
var mask = new Image<Rgba32>(width, height);
|
||||
|
||||
var sourceHeight = maskTensor.Dimensions[2]; // Height of the original tensor mask
|
||||
var sourceWidth = maskTensor.Dimensions[3]; // Width of the original tensor mask
|
||||
|
||||
using Image<Rgba32> tempMask = new(sourceWidth, sourceHeight);
|
||||
|
||||
// Sigmoid function parameters
|
||||
const float sigmoidScale = 1f; // Scaling factor for sigmoid activation
|
||||
const float sigmoidShift = 1f; // Shift factor in the denominator of the sigmoid function
|
||||
const float sigmoidDivisor = -1f; // Multiplier for the exponent in the sigmoid function
|
||||
|
||||
static float CalculateSigmoid(float x)
|
||||
{
|
||||
return sigmoidScale / (sigmoidShift + MathF.Exp(sigmoidDivisor * x));
|
||||
}
|
||||
|
||||
const float binarizationThreshold = 0.5f; // Threshold to determine foreground vs. background
|
||||
const float normalizationFactor = 2f; // Scales the thresholded value to enhance contrast
|
||||
|
||||
// Pixel intensity values
|
||||
const byte maxIntensity = 255; // Maximum grayscale intensity
|
||||
const byte opaqueAlpha = 255; // Fully opaque alpha value
|
||||
|
||||
|
||||
WalkImage(sourceHeight, sourceWidth, (x, y) =>
|
||||
{
|
||||
// a sigmoid function is a function that produces an S-shaped curve
|
||||
// it is often used in machine learning and statistics to model probabilities
|
||||
// the sigmoid function is defined as:
|
||||
// f(x) = 1 / (1 + e^(-x))
|
||||
// where e is the base of the natural logarithm and x is the input value
|
||||
|
||||
// the raw tensor values for our mask are going to be real unbounded numbers
|
||||
// i.e. -1.5, 0.5, 2.0, etc.
|
||||
// the sigmoid function will map these values to a range between 0 and 1
|
||||
// this allows us to say that value closer to 0 is background and value
|
||||
// closer to 1 is foreground
|
||||
var sigmoidValue = CalculateSigmoid(maskTensor[0, 0, y, x]);
|
||||
|
||||
// now we want to threshold the sigmoid value to determine if it is foreground or background
|
||||
// we are arbitrarily choosing 0.5 as the threshold. so if the sigmoid value is greater than
|
||||
// 0.5 we will consider it foreground and if it is less than 0.5 we will consider it background
|
||||
|
||||
// when a sigmoid value is greater than 0.5 we will subtract the threshold from it
|
||||
// and multiply it by 2 this way the intensity value will be larger for values closer to 1
|
||||
// and create more contrast in the mask
|
||||
var normalizedValue = sigmoidValue > binarizationThreshold
|
||||
? (sigmoidValue - binarizationThreshold) * normalizationFactor
|
||||
: 0f;
|
||||
|
||||
// Convert to an 8-bit grayscale intensity
|
||||
var intensity = (byte)(normalizedValue * maxIntensity);
|
||||
|
||||
// Store the pixel with full opacity
|
||||
tempMask[x, y] = new Rgba32(intensity, intensity, intensity, opaqueAlpha);
|
||||
});
|
||||
|
||||
// Resize the mask to match the target dimensions
|
||||
tempMask.Mutate(x => x.Resize(width, height));
|
||||
|
||||
// Copy the resized mask to the final output image
|
||||
WalkImage(height, width, (x, y) => mask[x, y] = tempMask[x, y]);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static Image<Rgba32> GetImageWithBackgroundRemoved(Image<Rgba32> image, Image<Rgba32> mask)
|
||||
{
|
||||
Image<Rgba32> result = new(image.Width, image.Height);
|
||||
|
||||
const byte alphaThreshold = 20;
|
||||
Rgba32 transparentPixel = new(0, 0, 0, 0);
|
||||
|
||||
WalkImage(image.Height, image.Width, (x, y) =>
|
||||
{
|
||||
var sourcePixel = image[x, y];
|
||||
var maskPixel = mask[x, y];
|
||||
|
||||
var alpha = maskPixel.R;
|
||||
|
||||
result[x, y] = alpha > alphaThreshold
|
||||
? new Rgba32(sourcePixel.R, sourcePixel.G, sourcePixel.B, sourcePixel.A)
|
||||
: transparentPixel;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void WalkImage(int height, int width, Action<int, int> action)
|
||||
{
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
action(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace BGR.Console.Removal;
|
||||
|
||||
internal interface IImage
|
||||
{
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
void Resize(int width, int height);
|
||||
IPixel GetPixel(int x, int y);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace BGR.Console.Removal;
|
||||
|
||||
internal interface IPixel
|
||||
{
|
||||
float R { get; }
|
||||
float G { get; }
|
||||
float B { get; }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace BGR.Console.Removal;
|
||||
|
||||
internal interface ITensor<T>
|
||||
{
|
||||
int Height { get; }
|
||||
int Width { get; }
|
||||
void SetValue(int batch, int channel, int y, int x, T value);
|
||||
float GetValue(int batch, int channel, int y, int x);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace BGR.Console.Removal;
|
||||
|
||||
internal abstract class ImageProcessor
|
||||
{
|
||||
public abstract Task<ITensor<float>> CreateTensorInputAsync(Stream image, Model model);
|
||||
|
||||
public abstract Task<Stream> GenerateMaskAsync(OnnxTensor maskTensor, int width, int height);
|
||||
|
||||
public abstract Task<Stream> RemoveBackgroundAsync(Stream image, Stream mask);
|
||||
|
||||
protected static void WalkImage(int height, int width, Action<int, int> action)
|
||||
{
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
action(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
namespace BGR.Console.Removal.ImageSharp;
|
||||
|
||||
internal class ImageSharpProcessor : ImageProcessor
|
||||
{
|
||||
public override async Task<ITensor<float>> CreateTensorInputAsync(Stream image, Model model)
|
||||
{
|
||||
using var resized = await Image.LoadAsync<Rgba32>(image);
|
||||
resized.Mutate(x => x.Resize(model.InputWidth, model.InputHeight));
|
||||
|
||||
const int batchSize = 1;
|
||||
const int channels = 3;
|
||||
var tensor = new OnnxTensor(batchSize, channels, model.InputHeight, model.InputWidth);
|
||||
|
||||
WalkImage(resized.Height, resized.Width, (x, y) =>
|
||||
{
|
||||
var pixel = resized[x, y];
|
||||
tensor.SetValue(0, 0, y, x, model.NormalizeRed(pixel.R));
|
||||
tensor.SetValue(0, 1, y, x, model.NormalizeGreen(pixel.G));
|
||||
tensor.SetValue(0, 2, y, x, model.NormalizeBlue(pixel.B));
|
||||
});
|
||||
|
||||
return tensor;
|
||||
}
|
||||
|
||||
public override async Task<Stream> GenerateMaskAsync(OnnxTensor maskTensor, int width, int height)
|
||||
{
|
||||
using var mask = new Image<Rgba32>(width, height);
|
||||
|
||||
using Image<Rgba32> tempMask = new(maskTensor.Width, maskTensor.Height);
|
||||
|
||||
const byte opaqueAlpha = 255;
|
||||
|
||||
WalkImage(maskTensor.Height, maskTensor.Width, (x, y) =>
|
||||
{
|
||||
var sigmoidValue = CalculateSigmoid(maskTensor.GetValue(0, 0, y, x));
|
||||
var normalizedValue = Normalize(sigmoidValue);
|
||||
var intensity = ConvertToGreyscale(normalizedValue);
|
||||
|
||||
tempMask[x, y] = new Rgba32(intensity, intensity, intensity, opaqueAlpha);
|
||||
});
|
||||
|
||||
tempMask.Mutate(x => x.Resize(width, height));
|
||||
|
||||
WalkImage(height, width, (x, y) => mask[x, y] = tempMask[x, y]);
|
||||
|
||||
var stream = new MemoryStream();
|
||||
await mask.SaveAsync(stream, new PngEncoder());
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
public override async Task<Stream> RemoveBackgroundAsync(Stream image, Stream mask)
|
||||
{
|
||||
var imageWithBg = await Image.LoadAsync<Rgba32>(image);
|
||||
var maskImage = await Image.LoadAsync<Rgba32>(mask);
|
||||
using var imageWithBgRemoved = new Image<Rgba32>(imageWithBg.Width, imageWithBg.Height);
|
||||
|
||||
const byte alphaThreshold = 20;
|
||||
var transparentPixel = new Rgba32(0, 0, 0, 0);
|
||||
|
||||
WalkImage(imageWithBg.Height, imageWithBg.Width, (x, y) =>
|
||||
{
|
||||
var sourcePixel = imageWithBg[x, y];
|
||||
var maskPixel = maskImage[x, y];
|
||||
|
||||
var alpha = maskPixel.R;
|
||||
|
||||
imageWithBgRemoved[x, y] = alpha > alphaThreshold
|
||||
? new Rgba32(sourcePixel.R, sourcePixel.G, sourcePixel.B, sourcePixel.A)
|
||||
: transparentPixel;
|
||||
});
|
||||
|
||||
var result = new MemoryStream();
|
||||
await imageWithBgRemoved.SaveAsync(result, new PngEncoder());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static float Normalize(float value)
|
||||
{
|
||||
const float binarizationThreshold = 0.5f;
|
||||
const float normalizationFactor = 2f;
|
||||
|
||||
return value > binarizationThreshold
|
||||
? (value - binarizationThreshold) * normalizationFactor
|
||||
: 0f;
|
||||
}
|
||||
|
||||
private static byte ConvertToGreyscale(float value)
|
||||
{
|
||||
const float maxIntensity = 255f;
|
||||
return (byte)(value * maxIntensity);
|
||||
}
|
||||
|
||||
private static float CalculateSigmoid(float x)
|
||||
{
|
||||
const float sigmoidScale = 1f;
|
||||
const float sigmoidShift = 1f;
|
||||
const float sigmoidDivisor = -1f;
|
||||
|
||||
return sigmoidScale / (sigmoidShift + MathF.Exp(sigmoidDivisor * x));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace BGR.Console.Removal.ImageSharp;
|
||||
|
||||
internal class SharpImage(Image<Rgba32> image) : IImage
|
||||
{
|
||||
private readonly Image<Rgba32> _image = image;
|
||||
|
||||
public int Width => _image.Width;
|
||||
public int Height => _image.Height;
|
||||
|
||||
public void Resize(int width, int height)
|
||||
{
|
||||
_image.Mutate(x => x.Resize(width, height));
|
||||
}
|
||||
|
||||
public IPixel GetPixel(int x, int y)
|
||||
{
|
||||
return new SharpPixel(_image[x, y]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace BGR.Console.Removal.ImageSharp;
|
||||
|
||||
internal class SharpPixel(Rgba32 pixel) : IPixel
|
||||
{
|
||||
private readonly Rgba32 _pixel = pixel;
|
||||
|
||||
public float R => _pixel.R;
|
||||
public float G => _pixel.G;
|
||||
public float B => _pixel.B;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace BGR.Console.Removal.Models;
|
||||
|
||||
internal class ModNetModel : Model
|
||||
{
|
||||
public override int InputWidth => 512;
|
||||
public override int InputHeight => 512;
|
||||
public override float RedNormalizationMean => 0.485f;
|
||||
public override float GreenNormalizationMean => 0.456f;
|
||||
public override float BlueNormalizationMean => 0.406f;
|
||||
public override float RedNormalizationStd => 0.229f;
|
||||
public override float GreenNormalizationStd => 0.224f;
|
||||
public override float BlueNormalizationStd => 0.225f;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace BGR.Console.Removal.Models;
|
||||
|
||||
internal abstract class Model
|
||||
{
|
||||
private const float PixelMax = 255f;
|
||||
public abstract int InputWidth { get; }
|
||||
public abstract int InputHeight { get; }
|
||||
public abstract float RedNormalizationMean { get; }
|
||||
public abstract float GreenNormalizationMean { get; }
|
||||
public abstract float BlueNormalizationMean { get; }
|
||||
public abstract float RedNormalizationStd { get; }
|
||||
public abstract float GreenNormalizationStd { get; }
|
||||
public abstract float BlueNormalizationStd { get; }
|
||||
|
||||
public float NormalizeRed(float value)
|
||||
{
|
||||
return Normalize(value, RedNormalizationMean, RedNormalizationStd);
|
||||
}
|
||||
|
||||
public float NormalizeGreen(float value)
|
||||
{
|
||||
return Normalize(value, GreenNormalizationMean, GreenNormalizationStd);
|
||||
}
|
||||
|
||||
public float NormalizeBlue(float value)
|
||||
{
|
||||
return Normalize(value, BlueNormalizationMean, BlueNormalizationStd);
|
||||
}
|
||||
|
||||
private static float Normalize(float value, float mean, float std)
|
||||
{
|
||||
return ((value / PixelMax) - mean) / std;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
namespace BGR.Console.Removal.Models;
|
||||
|
||||
internal class RmbgModel : Model
|
||||
{
|
||||
public override int InputWidth => 1024;
|
||||
public override int InputHeight => 1024;
|
||||
public override float RedNormalizationMean => 0.485f;
|
||||
public override float GreenNormalizationMean => 0.456f;
|
||||
public override float BlueNormalizationMean => 0.406f;
|
||||
public override float RedNormalizationStd => 0.229f;
|
||||
public override float GreenNormalizationStd => 0.224f;
|
||||
public override float BlueNormalizationStd => 0.225f;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace BGR.Console.Removal.Models;
|
||||
|
||||
internal class U2NetModel : Model
|
||||
{
|
||||
public override int InputWidth => 320;
|
||||
public override int InputHeight => 320;
|
||||
public override float RedNormalizationMean => 0.485f;
|
||||
public override float GreenNormalizationMean => 0.456f;
|
||||
public override float BlueNormalizationMean => 0.406f;
|
||||
public override float RedNormalizationStd => 0.229f;
|
||||
public override float GreenNormalizationStd => 0.224f;
|
||||
public override float BlueNormalizationStd => 0.225f;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace BGR.Console.Removal.Onnx;
|
||||
|
||||
public class OnnxTensor(
|
||||
int batchSize,
|
||||
int channels,
|
||||
int height,
|
||||
int width
|
||||
) : ITensor<float>
|
||||
{
|
||||
private readonly DenseTensor<float> _tensor =
|
||||
new([batchSize, channels, height, width]);
|
||||
|
||||
public int Height => _tensor.Dimensions[2];
|
||||
|
||||
public int Width => _tensor.Dimensions[3];
|
||||
|
||||
public void SetValue(
|
||||
int batch,
|
||||
int channel,
|
||||
int y,
|
||||
int x,
|
||||
float value
|
||||
)
|
||||
{
|
||||
_tensor[batch, channel, y, x] = value;
|
||||
}
|
||||
|
||||
public float GetValue(
|
||||
int batch,
|
||||
int channel,
|
||||
int y,
|
||||
int x
|
||||
)
|
||||
{
|
||||
return _tensor[batch, channel, y, x];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:07c308cf0fc7e6e8b2065a12ed7fc07e1de8febb7dc7839d7b7f15dd66584df9
|
||||
size 25888640
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fcea23951a378f92634834888896cc1eec54655366ae6e949282646ce17c5420
|
||||
size 366087549
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8d10d2f3bb75ae3b6d527c77944fc5e7dcd94b29809d47a739a7a728a912b491
|
||||
size 175997641
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace BGR.Console.Resources;
|
||||
|
||||
internal interface IResourceManager
|
||||
{
|
||||
Stream GetResource(string resourceName);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
namespace BGR.Console.Resources;
|
||||
|
||||
internal sealed class ResourceManager : IResourceManager
|
||||
{
|
||||
public Stream GetResource(string resourceName)
|
||||
{
|
||||
var name = $"{nameof(BGR)}.{nameof(Console)}.{nameof(Resources)}.Files.{resourceName}";
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var names = assembly.GetManifestResourceNames();
|
||||
var stream = assembly.GetManifestResourceStream(name);
|
||||
return stream ?? throw new FileNotFoundException("Model not found in embedded resources.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
global using System.Reflection;
|
||||
|
||||
global using BGR.Console.Common;
|
||||
global using BGR.Console.Removal.Models;
|
||||
global using BGR.Console.Removal.Onnx;
|
||||
global using BGR.Console.Resources;
|
||||
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Microsoft.ML.OnnxRuntime;
|
||||
global using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
|
||||
global using Serilog;
|
||||
global using Serilog.Events;
|
||||
global using Serilog.Formatting.Compact;
|
||||
|
||||
global using SixLabors.ImageSharp;
|
||||
global using SixLabors.ImageSharp.Formats.Png;
|
||||
global using SixLabors.ImageSharp.PixelFormats;
|
||||
global using SixLabors.ImageSharp.Processing;
|
||||
|
||||
global using Spectre.Console.Cli;
|
||||
Reference in New Issue
Block a user