Browse Source

Add ColorQuantizer

tznind 11 months ago
parent
commit
f0960624c1
2 changed files with 96 additions and 9 deletions
  1. 78 0
      Terminal.Gui/Drawing/ColorQuantizer.cs
  2. 18 9
      Terminal.Gui/Drawing/SixelEncoder.cs

+ 78 - 0
Terminal.Gui/Drawing/ColorQuantizer.cs

@@ -0,0 +1,78 @@
+namespace Terminal.Gui;
+
+/// <summary>
+/// Translates colors in an image into a Palette of up to 256 colors.
+/// </summary>
+public class ColorQuantizer
+{
+    private Dictionary<Color, int> colorFrequency;
+    public List<Color> Palette;
+    private const int MaxColors = 256;
+
+    public ColorQuantizer ()
+    {
+        colorFrequency = new Dictionary<Color, int> ();
+        Palette = new List<Color> ();
+    }
+
+    public void BuildColorPalette (Color [,] pixels)
+    {
+        int width = pixels.GetLength (0);
+        int height = pixels.GetLength (1);
+
+        // Count the frequency of each color
+        for (int x = 0; x < width; x++)
+        {
+            for (int y = 0; y < height; y++)
+            {
+                Color color = pixels [x, y];
+                if (colorFrequency.ContainsKey (color))
+                {
+                    colorFrequency [color]++;
+                }
+                else
+                {
+                    colorFrequency [color] = 1;
+                }
+            }
+        }
+
+        // Create a sorted list of colors based on frequency
+        var sortedColors = colorFrequency.OrderByDescending (kvp => kvp.Value).ToList ();
+
+        // Build the Palette with the most frequent colors up to MaxColors
+        Palette = sortedColors.Take (MaxColors).Select (kvp => kvp.Key).ToList ();
+
+
+    }
+
+    public int GetNearestColor (Color toTranslate)
+    {
+        // Simple nearest color matching based on Euclidean distance in RGB space
+        double minDistance = double.MaxValue;
+        int nearestIndex = 0;
+
+        for (var index = 0; index < Palette.Count; index++)
+        {
+            Color color = Palette [index];
+            double distance = ColorDistance (color, toTranslate);
+
+            if (distance < minDistance)
+            {
+                minDistance = distance;
+                nearestIndex = index;
+            }
+        }
+
+        return nearestIndex;
+    }
+
+    private double ColorDistance (Color c1, Color c2)
+    {
+        // Euclidean distance in RGB space
+        int rDiff = c1.R - c2.R;
+        int gDiff = c1.G - c2.G;
+        int bDiff = c1.B - c2.B;
+        return Math.Sqrt (rDiff * rDiff + gDiff * gDiff + bDiff * bDiff);
+    }
+}

+ 18 - 9
Terminal.Gui/Drawing/SixelEncoder.cs

@@ -7,8 +7,6 @@ namespace Terminal.Gui;
 /// </summary>
 public class SixelEncoder
 {
-
-
     /// <summary>
     /// Encode the given bitmap into sixel encoding
     /// </summary>
@@ -37,16 +35,27 @@ public class SixelEncoder
         return start + defaultRatios + completeStartSequence + noScaling + fillArea + pallette + pixelData + terminator;
     }
 
-    private string GetColorPallette (Color [,] pixels, out Dictionary<Color, int> dictionary)
+    private string GetColorPallette (Color [,] pixels, out ColorQuantizer quantizer)
     {
+        quantizer = new ColorQuantizer ();
+        quantizer.BuildColorPalette (pixels);
 
-        dictionary = new Dictionary<Color, int>
-        {
-            {new Color(255,0,0),0}
-        };
 
-        // Red color definition in the format "#<index>;<type>;<R>;<G>;<B>" - 2 means RGB.  The values range 0 to 100
-        return "#0;2;100;0;0";
+        // Color definitions in the format "#<index>;<type>;<R>;<G>;<B>" - For type the 2 means RGB.  The values range 0 to 100
+
+        StringBuilder paletteSb = new StringBuilder ();
+
+        for (int i = 0; i < quantizer.Palette.Count; i++)
+        {
+            var color = quantizer.Palette [i];
+            paletteSb.AppendFormat ("#{0};2;{1};{2};{3}",
+                                    i,
+                                    color.R * 100 / 255,
+                                    color.G * 100 / 255,
+                                    color.B * 100 / 255);
+        }
+
+        return paletteSb.ToString ();
     }
 
     private string GetFillArea (Color [,] pixels)