PopularityPaletteWithThreshold.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. using Terminal.Gui;
  2. using Color = Terminal.Gui.Color;
  3. /// <summary>
  4. /// Simple fast palette building algorithm which uses the frequency that a color is seen
  5. /// to determine whether it will appear in the final palette. Includes a threshold where
  6. /// by colors will be considered 'the same'. This reduces the chance of under represented
  7. /// colors being missed completely.
  8. /// </summary>
  9. public class PopularityPaletteWithThreshold : IPaletteBuilder
  10. {
  11. private readonly IColorDistance _colorDistance;
  12. private readonly double _mergeThreshold;
  13. public PopularityPaletteWithThreshold (IColorDistance colorDistance, double mergeThreshold)
  14. {
  15. _colorDistance = colorDistance;
  16. _mergeThreshold = mergeThreshold; // Set the threshold for merging similar colors
  17. }
  18. public List<Color> BuildPalette (List<Color> colors, int maxColors)
  19. {
  20. if (colors == null || colors.Count == 0 || maxColors <= 0)
  21. {
  22. return new ();
  23. }
  24. // Step 1: Build the histogram of colors (count occurrences)
  25. Dictionary<Color, int> colorHistogram = new Dictionary<Color, int> ();
  26. foreach (Color color in colors)
  27. {
  28. if (colorHistogram.ContainsKey (color))
  29. {
  30. colorHistogram [color]++;
  31. }
  32. else
  33. {
  34. colorHistogram [color] = 1;
  35. }
  36. }
  37. // If we already have fewer or equal colors than the limit, no need to merge
  38. if (colorHistogram.Count <= maxColors)
  39. {
  40. return colorHistogram.Keys.ToList ();
  41. }
  42. // Step 2: Merge similar colors using the color distance threshold
  43. Dictionary<Color, int> mergedHistogram = MergeSimilarColors (colorHistogram, maxColors);
  44. // Step 3: Sort the histogram by frequency (most frequent colors first)
  45. List<Color> sortedColors = mergedHistogram.OrderByDescending (c => c.Value)
  46. .Take (maxColors) // Keep only the top `maxColors` colors
  47. .Select (c => c.Key)
  48. .ToList ();
  49. return sortedColors;
  50. }
  51. /// <summary>
  52. /// Merge colors in the histogram if they are within the threshold distance
  53. /// </summary>
  54. /// <param name="colorHistogram"></param>
  55. /// <returns></returns>
  56. private Dictionary<Color, int> MergeSimilarColors (Dictionary<Color, int> colorHistogram, int maxColors)
  57. {
  58. Dictionary<Color, int> mergedHistogram = new Dictionary<Color, int> ();
  59. foreach (KeyValuePair<Color, int> entry in colorHistogram)
  60. {
  61. Color currentColor = entry.Key;
  62. var merged = false;
  63. // Try to merge the current color with an existing entry in the merged histogram
  64. foreach (Color mergedEntry in mergedHistogram.Keys.ToList ())
  65. {
  66. double distance = _colorDistance.CalculateDistance (currentColor, mergedEntry);
  67. // If the colors are similar enough (within the threshold), merge them
  68. if (distance <= _mergeThreshold)
  69. {
  70. mergedHistogram [mergedEntry] += entry.Value; // Add the color frequency to the existing one
  71. merged = true;
  72. break;
  73. }
  74. }
  75. // If no similar color is found, add the current color as a new entry
  76. if (!merged)
  77. {
  78. mergedHistogram [currentColor] = entry.Value;
  79. }
  80. // Early exit if we've reduced the colors to the maxColors limit
  81. if (mergedHistogram.Count >= maxColors)
  82. {
  83. return mergedHistogram;
  84. }
  85. }
  86. return mergedHistogram;
  87. }
  88. }