color_analysis.h 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef UI_GFX_COLOR_ANALYSIS_H_
  5. #define UI_GFX_COLOR_ANALYSIS_H_
  6. #include <stdint.h>
  7. #include "base/callback_forward.h"
  8. #include "base/memory/ref_counted.h"
  9. #include "base/memory/ref_counted_memory.h"
  10. #include "third_party/skia/include/core/SkColor.h"
  11. #include "ui/gfx/gfx_export.h"
  12. class SkBitmap;
  13. namespace gfx {
  14. class Rect;
  15. } // namespace gfx
  16. namespace color_utils {
  17. struct HSL;
  18. // This class exposes the sampling method to the caller, which allows
  19. // stubbing out for things like unit tests. Might be useful to pass more
  20. // arguments into the GetSample method in the future (such as which
  21. // cluster is being worked on, etc.).
  22. //
  23. // Note: Samplers should be deterministic, as the same image may be analyzed
  24. // twice with two sampler instances and the results displayed side-by-side
  25. // to the user.
  26. class GFX_EXPORT KMeanImageSampler {
  27. public:
  28. virtual int GetSample(int width, int height) = 0;
  29. protected:
  30. KMeanImageSampler();
  31. virtual ~KMeanImageSampler();
  32. };
  33. // This sampler will pick pixels from an evenly spaced grid.
  34. class GFX_EXPORT GridSampler : public KMeanImageSampler {
  35. public:
  36. GridSampler();
  37. ~GridSampler() override;
  38. int GetSample(int width, int height) override;
  39. private:
  40. // The number of times GetSample has been called.
  41. int calls_;
  42. };
  43. // Returns the color in an ARGB |image| that is closest in RGB-space to the
  44. // provided |color|. Exported for testing.
  45. GFX_EXPORT SkColor FindClosestColor(const uint8_t* image, int width, int height,
  46. SkColor color);
  47. // Returns an SkColor that represents the calculated dominant color in the
  48. // image. This uses a KMean clustering algorithm to find clusters of pixel
  49. // colors in RGB space.
  50. // |png|/|bitmap| represents the data of a png/bitmap encoded image.
  51. // |lower_bound| represents the minimum bound of HSL values to allow.
  52. // |upper_bound| represents the maximum bound of HSL values to allow.
  53. // See color_utils::IsWithinHSLRange() for description of these bounds.
  54. //
  55. // RGB KMean Algorithm (N clusters, M iterations):
  56. // 1.Pick N starting colors by randomly sampling the pixels. If you see a
  57. // color you already saw keep sampling. After a certain number of tries
  58. // just remove the cluster and continue with N = N-1 clusters (for an image
  59. // with just one color this should devolve to N=1). These colors are the
  60. // centers of your N clusters.
  61. // 2.For each pixel in the image find the cluster that it is closest to in RGB
  62. // space. Add that pixel's color to that cluster (we keep a sum and a count
  63. // of all of the pixels added to the space, so just add it to the sum and
  64. // increment count).
  65. // 3.Calculate the new cluster centroids by getting the average color of all of
  66. // the pixels in each cluster (dividing the sum by the count).
  67. // 4.See if the new centroids are the same as the old centroids.
  68. // a) If this is the case for all N clusters than we have converged and
  69. // can move on.
  70. // b) If any centroid moved, repeat step 2 with the new centroids for up
  71. // to M iterations.
  72. // 5.Once the clusters have converged or M iterations have been tried, sort
  73. // the clusters by weight (where weight is the number of pixels that make up
  74. // this cluster).
  75. // 6.Going through the sorted list of clusters, pick the first cluster with the
  76. // largest weight that's centroid falls between |lower_bound| and
  77. // |upper_bound|. Return that color.
  78. // If no color fulfills that requirement return the color with the largest
  79. // weight regardless of whether or not it fulfills the equation above.
  80. GFX_EXPORT SkColor
  81. CalculateKMeanColorOfPNG(scoped_refptr<base::RefCountedMemory> png,
  82. const HSL& lower_bound,
  83. const HSL& upper_bound,
  84. KMeanImageSampler* sampler);
  85. // Computes a dominant color using the above algorithm and reasonable defaults
  86. // for |lower_bound|, |upper_bound| and |sampler|.
  87. GFX_EXPORT SkColor CalculateKMeanColorOfPNG(
  88. scoped_refptr<base::RefCountedMemory> png);
  89. // Computes a dominant color for the first |height| rows of |bitmap| using the
  90. // above algorithm and a reasonable default sampler. If |find_closest| is true,
  91. // the returned color will be the closest color to the true K-mean color that
  92. // actually appears in the image; if false, the true color is returned
  93. // regardless of whether it actually appears.
  94. GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap,
  95. int height,
  96. const HSL& lower_bound,
  97. const HSL& upper_bound,
  98. bool find_closest);
  99. // Computes a dominant color using the above algorithm and reasonable defaults
  100. // for |lower_bound|, |upper_bound| and |sampler|.
  101. GFX_EXPORT SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap);
  102. // These enums specify general values to look for when calculating prominent
  103. // colors from an image. For example, a "light vibrant" prominent color would
  104. // tend to be brighter and more saturated. The best combination of color
  105. // attributes depends on how you plan to apply the color.
  106. enum class LumaRange {
  107. ANY,
  108. LIGHT,
  109. NORMAL,
  110. DARK,
  111. };
  112. enum class SaturationRange {
  113. ANY,
  114. VIBRANT,
  115. MUTED,
  116. };
  117. struct ColorProfile {
  118. ColorProfile() = default;
  119. ColorProfile(LumaRange l, SaturationRange s) : luma(l), saturation(s) {}
  120. LumaRange luma = LumaRange::DARK;
  121. SaturationRange saturation = SaturationRange::MUTED;
  122. };
  123. // A color value with an associated weight.
  124. struct Swatch {
  125. Swatch() : Swatch(SK_ColorTRANSPARENT, 0) {}
  126. Swatch(SkColor color, size_t population)
  127. : color(color), population(population) {}
  128. SkColor color;
  129. // The population correlates to a count, so it should be 1 or greater.
  130. size_t population;
  131. bool operator==(const Swatch& other) const {
  132. return color == other.color && population == other.population;
  133. }
  134. };
  135. // Used to filter colors from swatches. Called with the candidate color and will
  136. // return true if the color should be allowed.
  137. using ColorSwatchFilter = base::RepeatingCallback<bool(const SkColor&)>;
  138. // The maximum number of pixels to consider when generating swatches.
  139. GFX_EXPORT extern const int kMaxConsideredPixelsForSwatches;
  140. // Returns a vector of |Swatch| that represent the prominent colors of the
  141. // bitmap within |region|. The |max_swatches| is the maximum number of swatches.
  142. // For landscapes, good values are in the range 12-16. For images which are
  143. // largely made up of people's faces then this value should be increased to
  144. // 24-32. |filter| is an optional filter that can filter out unwanted colors.
  145. // This is an implementation of the Android Palette API:
  146. // https://developer.android.com/reference/android/support/v7/graphics/Palette
  147. GFX_EXPORT std::vector<Swatch> CalculateColorSwatches(
  148. const SkBitmap& bitmap,
  149. size_t max_swatches,
  150. const gfx::Rect& region,
  151. absl::optional<ColorSwatchFilter> filter);
  152. // Returns a vector of RGB colors that represents the bitmap based on the
  153. // |color_profiles| provided. For each value, if a value is succesfully
  154. // calculated, the calculated value is fully opaque. For failure, the calculated
  155. // value is transparent. |region| can be provided to select a specific area of
  156. // the bitmap. |filter| is an optional filter that can filter out unwanted
  157. // colors. If |filter| is not provided then we will filter out uninteresting
  158. // colors.
  159. GFX_EXPORT std::vector<Swatch> CalculateProminentColorsOfBitmap(
  160. const SkBitmap& bitmap,
  161. const std::vector<ColorProfile>& color_profiles,
  162. gfx::Rect* region,
  163. ColorSwatchFilter filter);
  164. } // namespace color_utils
  165. #endif // UI_GFX_COLOR_ANALYSIS_H_