Commit:
b558b75Parent:
1cdfbf0Add "pixel filter" to only sample part of images
Colors/example/Program.cs
+1
-1
diff --git a/Colors/example/Program.cs b/Colors/example/Program.cs
index 5251dde..d501187 100644
@@ -7,7 +7,7 @@ using SixLabors.ImageSharp.PixelFormats;
using var image = Image.Load<Rgba32>(args.Length > 0 ? args[0] : @".\images\example1.jpg");
image.ConvertToScaledOklch();
var palette = image.GetPalette();
var palette = image.GetPalette(new(image.Width / 3, 2 * image.Width / 3, image.Height / 3, 2 * image.Height / 3));
Console.WriteLine(
" "
Colors/src/ColorAnalyzer.cs
+2
-2
diff --git a/Colors/src/ColorAnalyzer.cs b/Colors/src/ColorAnalyzer.cs
index 5064119..ee4a917 100644
@@ -8,9 +8,9 @@ public static class ColorAnalyzer
{
extension(Image<Rgba32> image)
{
public (Rgba32 color, float proportion)[] GetPalette()
public (Rgba32 color, float proportion)[] GetPalette(PixelFilter pixelFilter)
{
var quantized = image.Quantize(6);
var quantized = image.Quantize(pixelFilter, 6);
var total = quantized.Values.Aggregate(0.0f, (t, v) => t + v);
return [.. quantized.Select(kvp => (kvp.Key, kvp.Value / total)).OrderByDescending(p => p.Item2)];
}
Colors/src/HistogramBuilder.cs
+0
-28
diff --git a/Colors/src/HistogramBuilder.cs b/Colors/src/HistogramBuilder.cs
deleted file mode 100644
index 200568e..0000000
@@ -1,28 +0,0 @@
using System;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
namespace Mosaic.Colors;
internal class HistogramBuilder()
{
public readonly Memory<int> Histogram = new int[MmcqQuantizer.HistogramSize];
public void Populate(PixelAccessor<Rgba32> accessor)
{
var histogram = Histogram.Span;
for (var y = 0; y < accessor.Height; y++)
{
var row = accessor.GetRowSpan(y);
for (var x = 0; x < row.Length; x++)
{
var pixel = row[x];
var r = pixel.R >> MmcqQuantizer.Shift;
var g = pixel.G >> MmcqQuantizer.Shift;
var b = pixel.B >> MmcqQuantizer.Shift;
histogram[MmcqQuantizer.GetColorIndex(r, g, b)]++;
}
}
}
}
Colors/src/MmcqQuantizer.cs
+6
-14
diff --git a/Colors/src/MmcqQuantizer.cs b/Colors/src/MmcqQuantizer.cs
index 7e6f6aa..57b653f 100644
@@ -18,7 +18,7 @@ internal static class MmcqQuantizer
extension(Image<Rgba32> image)
{
internal Dictionary<Rgba32, int> Quantize(int maxColorCount)
internal Dictionary<Rgba32, int> Quantize(PixelFilter pixelFilter, int maxColorCount)
{
if (maxColorCount < 2 || 256 < maxColorCount)
{
@@ -28,9 +28,8 @@ internal static class MmcqQuantizer
);
}
var histogram = image.GetHistogram();
var vBox = image.GetVbox(histogram);
var vBox = image.GetVbox(pixelFilter);
var histogram = vBox.Histogram;
var queue = new PriorityQueue<VBox, VBox>(VBox.CountComparer);
queue.Enqueue(vBox, vBox);
@@ -42,18 +41,11 @@ internal static class MmcqQuantizer
return queue2.UnorderedItems.ToDictionary(p => p.Element.Average, p => p.Element.Count);
}
internal Memory<int> GetHistogram()
{
var builder = new HistogramBuilder();
image.ProcessPixelRows(builder.Populate);
return builder.Histogram;
}
internal VBox GetVbox(ReadOnlyMemory<int> histogram)
internal VBox GetVbox(PixelFilter pixelFilter)
{
var builder = new VboxBuilder();
var builder = new VboxBuilder(pixelFilter);
image.ProcessPixelRows(builder.Populate);
return builder.Build(histogram);
return builder.Build();
}
}
Colors/src/PixelFilter.cs
+13
-0
diff --git a/Colors/src/PixelFilter.cs b/Colors/src/PixelFilter.cs
new file mode 100644
index 0000000..6f6c307
@@ -0,0 +1,13 @@
namespace Mosaic.Colors;
public class PixelFilter(int minX, int maxX, int minY, int maxY)
{
public int MinX { get; } = minX;
private readonly int maxX = maxX;
public int MinY { get; } = minY;
private readonly int maxY = maxY;
public int MaxY(int height) => int.Min(maxY, height);
public int MaxX(int width) => int.Min(maxX, width);
}
Colors/src/VBox.cs
+13
-7
diff --git a/Colors/src/VBox.cs b/Colors/src/VBox.cs
index 5faf1d3..f01f980 100644
@@ -5,21 +5,25 @@ using SixLabors.ImageSharp.PixelFormats;
namespace Mosaic.Colors;
internal class VboxBuilder
internal class VboxBuilder(PixelFilter filter)
{
private int rMin = (int)MmcqQuantizer.MaxValue;
public readonly Memory<int> Histogram = new int[MmcqQuantizer.HistogramSize];
private int rMin = MmcqQuantizer.MaxValue;
private int rMax = 0;
private int gMin = (int)MmcqQuantizer.MaxValue;
private int gMin = MmcqQuantizer.MaxValue;
private int gMax = 0;
private int bMin = (int)MmcqQuantizer.MaxValue;
private int bMin = MmcqQuantizer.MaxValue;
private int bMax = 0;
public void Populate(PixelAccessor<Rgba32> accessor)
{
for (var y = 0; y < accessor.Height; y++)
var histogram = Histogram.Span;
for (var y = filter.MinY; y < filter.MaxY(accessor.Height); y++)
{
var row = accessor.GetRowSpan(y);
for (var x = 0; x < row.Length; x++)
for (var x = filter.MinX; x < filter.MaxX(row.Length); x++)
{
var pixel = row[x];
@@ -34,11 +38,13 @@ internal class VboxBuilder
var b = pixel.B >> MmcqQuantizer.Shift;
bMin = int.Min(bMin, b);
bMax = int.Max(bMax, b);
histogram[MmcqQuantizer.GetColorIndex(r, g, b)]++;
}
}
}
public VBox Build(ReadOnlyMemory<int> histogram) => new(rMin, rMax, gMin, gMax, bMin, bMax, histogram);
public VBox Build() => new(rMin, rMax, gMin, gMax, bMin, bMax, Histogram);
}
internal class VBox