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.
5 #include "ui/gfx/color_analysis.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "third_party/skia/include/core/SkBitmap.h"
14 #include "third_party/skia/include/core/SkUnPreMultiply.h"
15 #include "ui/gfx/codec/png_codec.h"
19 // RGBA KMean Constants
20 const uint32_t kNumberOfClusters
= 4;
21 const int kNumberOfIterations
= 50;
22 const uint32_t kMaxBrightness
= 665;
23 const uint32_t kMinDarkness
= 100;
25 // Background Color Modification Constants
26 const SkColor kDefaultBgColor
= SK_ColorWHITE
;
28 // Support class to hold information about each cluster of pixel data in
29 // the KMean algorithm. While this class does not contain all of the points
30 // that exist in the cluster, it keeps track of the aggregate sum so it can
31 // compute the new center appropriately.
39 centroid
[0] = centroid
[1] = centroid
[2] = 0;
40 aggregate
[0] = aggregate
[1] = aggregate
[2] = 0;
45 inline void SetCentroid(uint8_t r
, uint8_t g
, uint8_t b
) {
51 inline void GetCentroid(uint8_t* r
, uint8_t* g
, uint8_t* b
) {
57 inline bool IsAtCentroid(uint8_t r
, uint8_t g
, uint8_t b
) {
58 return r
== centroid
[0] && g
== centroid
[1] && b
== centroid
[2];
61 // Recomputes the centroid of the cluster based on the aggregate data. The
62 // number of points used to calculate this center is stored for weighting
63 // purposes. The aggregate and counter are then cleared to be ready for the
65 inline void RecomputeCentroid() {
67 centroid
[0] = aggregate
[0] / counter
;
68 centroid
[1] = aggregate
[1] / counter
;
69 centroid
[2] = aggregate
[2] / counter
;
71 aggregate
[0] = aggregate
[1] = aggregate
[2] = 0;
77 inline void AddPoint(uint8_t r
, uint8_t g
, uint8_t b
) {
84 // Just returns the distance^2. Since we are comparing relative distances
85 // there is no need to perform the expensive sqrt() operation.
86 inline uint32_t GetDistanceSqr(uint8_t r
, uint8_t g
, uint8_t b
) {
87 return (r
- centroid
[0]) * (r
- centroid
[0]) +
88 (g
- centroid
[1]) * (g
- centroid
[1]) +
89 (b
- centroid
[2]) * (b
- centroid
[2]);
92 // In order to determine if we have hit convergence or not we need to see
93 // if the centroid of the cluster has moved. This determines whether or
94 // not the centroid is the same as the aggregate sum of points that will be
95 // used to generate the next centroid.
96 inline bool CompareCentroidWithAggregate() {
100 return aggregate
[0] / counter
== centroid
[0] &&
101 aggregate
[1] / counter
== centroid
[1] &&
102 aggregate
[2] / counter
== centroid
[2];
105 // Returns the previous counter, which is used to determine the weight
106 // of the cluster for sorting.
107 inline uint32_t GetWeight() const {
111 static bool SortKMeanClusterByWeight(const KMeanCluster
& a
,
112 const KMeanCluster
& b
) {
113 return a
.GetWeight() > b
.GetWeight();
119 // Holds the sum of all the points that make up this cluster. Used to
120 // generate the next centroid as well as to check for convergence.
121 uint32_t aggregate
[3];
124 // The weight of the cluster, determined by how many points were used
125 // to generate the previous centroid.
129 // Un-premultiplies each pixel in |bitmap| into an output |buffer|. Requires
130 // approximately 10 microseconds for a 16x16 icon on an Intel Core i5.
131 void UnPreMultiply(const SkBitmap
& bitmap
, uint32_t* buffer
, int buffer_size
) {
132 SkAutoLockPixels
auto_lock(bitmap
);
133 uint32_t* in
= static_cast<uint32_t*>(bitmap
.getPixels());
134 uint32_t* out
= buffer
;
135 int pixel_count
= std::min(bitmap
.width() * bitmap
.height(), buffer_size
);
136 for (int i
= 0; i
< pixel_count
; ++i
)
137 *out
++ = SkUnPreMultiply::PMColorToColor(*in
++);
142 namespace color_utils
{
144 KMeanImageSampler::KMeanImageSampler() {
147 KMeanImageSampler::~KMeanImageSampler() {
150 GridSampler::GridSampler() : calls_(0) {
153 GridSampler::~GridSampler() {
156 int GridSampler::GetSample(int width
, int height
) {
157 // Hand-drawn bitmaps often have special outlines or feathering at the edges.
158 // Start our sampling inset from the top and left edges. For example, a 10x10
159 // image with 4 clusters would be sampled like this:
172 (calls_
/ kNumberOfClusters
) * ((width
- 2 * kPadX
) / kNumberOfClusters
);
174 (calls_
% kNumberOfClusters
) * ((height
- 2 * kPadY
) / kNumberOfClusters
);
175 int index
= x
+ (y
* width
);
177 return index
% (width
* height
);
180 SkColor
FindClosestColor(const uint8_t* image
,
184 uint8_t in_r
= SkColorGetR(color
);
185 uint8_t in_g
= SkColorGetG(color
);
186 uint8_t in_b
= SkColorGetB(color
);
187 // Search using distance-squared to avoid expensive sqrt() operations.
188 int best_distance_squared
= kint32max
;
189 SkColor best_color
= color
;
190 const uint8_t* byte
= image
;
191 for (int i
= 0; i
< width
* height
; ++i
) {
192 uint8_t b
= *(byte
++);
193 uint8_t g
= *(byte
++);
194 uint8_t r
= *(byte
++);
195 uint8_t a
= *(byte
++);
196 // Ignore fully transparent pixels.
199 int distance_squared
=
200 (in_b
- b
) * (in_b
- b
) +
201 (in_g
- g
) * (in_g
- g
) +
202 (in_r
- r
) * (in_r
- r
);
203 if (distance_squared
< best_distance_squared
) {
204 best_distance_squared
= distance_squared
;
205 best_color
= SkColorSetRGB(r
, g
, b
);
211 // For a 16x16 icon on an Intel Core i5 this function takes approximately
213 // TODO(port): This code assumes the CPU architecture is little-endian.
214 SkColor
CalculateKMeanColorOfBuffer(uint8_t* decoded_data
,
217 uint32_t darkness_limit
,
218 uint32_t brightness_limit
,
219 KMeanImageSampler
* sampler
) {
220 SkColor color
= kDefaultBgColor
;
221 if (img_width
> 0 && img_height
> 0) {
222 std::vector
<KMeanCluster
> clusters
;
223 clusters
.resize(kNumberOfClusters
, KMeanCluster());
225 // Pick a starting point for each cluster
226 std::vector
<KMeanCluster
>::iterator cluster
= clusters
.begin();
227 while (cluster
!= clusters
.end()) {
228 // Try up to 10 times to find a unique color. If no unique color can be
229 // found, destroy this cluster.
230 bool color_unique
= false;
231 for (int i
= 0; i
< 10; ++i
) {
232 int pixel_pos
= sampler
->GetSample(img_width
, img_height
) %
233 (img_width
* img_height
);
235 uint8_t b
= decoded_data
[pixel_pos
* 4];
236 uint8_t g
= decoded_data
[pixel_pos
* 4 + 1];
237 uint8_t r
= decoded_data
[pixel_pos
* 4 + 2];
238 uint8_t a
= decoded_data
[pixel_pos
* 4 + 3];
239 // Skip fully transparent pixels as they usually contain black in their
240 // RGB channels but do not contribute to the visual image.
244 // Loop through the previous clusters and check to see if we have seen
245 // this color before.
247 for (std::vector
<KMeanCluster
>::iterator
248 cluster_check
= clusters
.begin();
249 cluster_check
!= cluster
; ++cluster_check
) {
250 if (cluster_check
->IsAtCentroid(r
, g
, b
)) {
251 color_unique
= false;
256 // If we have a unique color set the center of the cluster to
259 cluster
->SetCentroid(r
, g
, b
);
264 // If we don't have a unique color erase this cluster.
266 cluster
= clusters
.erase(cluster
);
268 // Have to increment the iterator here, otherwise the increment in the
269 // for loop will skip a cluster due to the erase if the color wasn't
275 // If all pixels in the image are transparent we will have no clusters.
276 if (clusters
.empty())
279 bool convergence
= false;
280 for (int iteration
= 0;
281 iteration
< kNumberOfIterations
&& !convergence
;
284 // Loop through each pixel so we can place it in the appropriate cluster.
285 uint8_t* pixel
= decoded_data
;
286 uint8_t* decoded_data_end
= decoded_data
+ (img_width
* img_height
* 4);
287 while (pixel
< decoded_data_end
) {
288 uint8_t b
= *(pixel
++);
289 uint8_t g
= *(pixel
++);
290 uint8_t r
= *(pixel
++);
291 uint8_t a
= *(pixel
++);
292 // Skip transparent pixels, see above.
296 uint32_t distance_sqr_to_closest_cluster
= UINT_MAX
;
297 std::vector
<KMeanCluster
>::iterator closest_cluster
= clusters
.begin();
299 // Figure out which cluster this color is closest to in RGB space.
300 for (std::vector
<KMeanCluster
>::iterator cluster
= clusters
.begin();
301 cluster
!= clusters
.end(); ++cluster
) {
302 uint32_t distance_sqr
= cluster
->GetDistanceSqr(r
, g
, b
);
304 if (distance_sqr
< distance_sqr_to_closest_cluster
) {
305 distance_sqr_to_closest_cluster
= distance_sqr
;
306 closest_cluster
= cluster
;
310 closest_cluster
->AddPoint(r
, g
, b
);
313 // Calculate the new cluster centers and see if we've converged or not.
315 for (std::vector
<KMeanCluster
>::iterator cluster
= clusters
.begin();
316 cluster
!= clusters
.end(); ++cluster
) {
317 convergence
&= cluster
->CompareCentroidWithAggregate();
319 cluster
->RecomputeCentroid();
323 // Sort the clusters by population so we can tell what the most popular
325 std::sort(clusters
.begin(), clusters
.end(),
326 KMeanCluster::SortKMeanClusterByWeight
);
328 // Loop through the clusters to figure out which cluster has an appropriate
329 // color. Skip any that are too bright/dark and go in order of weight.
330 for (std::vector
<KMeanCluster
>::iterator cluster
= clusters
.begin();
331 cluster
!= clusters
.end(); ++cluster
) {
333 cluster
->GetCentroid(&r
, &g
, &b
);
334 // Sum the RGB components to determine if the color is too bright or too
336 // TODO (dtrainor): Look into using HSV here instead. This approximation
337 // might be fine though.
338 uint32_t summed_color
= r
+ g
+ b
;
340 if (summed_color
< brightness_limit
&& summed_color
> darkness_limit
) {
341 // If we found a valid color just set it and break. We don't want to
342 // check the other ones.
343 color
= SkColorSetARGB(0xFF, r
, g
, b
);
345 } else if (cluster
== clusters
.begin()) {
346 // We haven't found a valid color, but we are at the first color so
347 // set the color anyway to make sure we at least have a value here.
348 color
= SkColorSetARGB(0xFF, r
, g
, b
);
353 // Find a color that actually appears in the image (the K-mean cluster center
354 // will not usually be a color that appears in the image).
355 return FindClosestColor(decoded_data
, img_width
, img_height
, color
);
358 SkColor
CalculateKMeanColorOfPNG(scoped_refptr
<base::RefCountedMemory
> png
,
359 uint32_t darkness_limit
,
360 uint32_t brightness_limit
,
361 KMeanImageSampler
* sampler
) {
364 std::vector
<uint8_t> decoded_data
;
365 SkColor color
= kDefaultBgColor
;
369 gfx::PNGCodec::Decode(png
->front(),
371 gfx::PNGCodec::FORMAT_BGRA
,
375 return CalculateKMeanColorOfBuffer(&decoded_data
[0],
385 SkColor
CalculateKMeanColorOfBitmap(const SkBitmap
& bitmap
) {
386 // SkBitmap uses pre-multiplied alpha but the KMean clustering function
387 // above uses non-pre-multiplied alpha. Transform the bitmap before we
388 // analyze it because the function reads each pixel multiple times.
389 int pixel_count
= bitmap
.width() * bitmap
.height();
390 scoped_ptr
<uint32_t[]> image(new uint32_t[pixel_count
]);
391 UnPreMultiply(bitmap
, image
.get(), pixel_count
);
394 SkColor color
= CalculateKMeanColorOfBuffer(
395 reinterpret_cast<uint8_t*>(image
.get()),
404 gfx::Matrix3F
ComputeColorCovariance(const SkBitmap
& bitmap
) {
405 // First need basic stats to normalize each channel separately.
406 SkAutoLockPixels
bitmap_lock(bitmap
);
407 gfx::Matrix3F covariance
= gfx::Matrix3F::Zeros();
408 if (!bitmap
.getPixels())
411 // Assume ARGB_8888 format.
412 DCHECK(bitmap
.config() == SkBitmap::kARGB_8888_Config
);
424 for (int y
= 0; y
< bitmap
.height(); ++y
) {
425 SkPMColor
* current_color
= static_cast<uint32_t*>(bitmap
.getAddr32(0, y
));
426 for (int x
= 0; x
< bitmap
.width(); ++x
, ++current_color
) {
427 SkColor c
= SkUnPreMultiply::PMColorToColor(*current_color
);
428 SkColor r
= SkColorGetR(c
);
429 SkColor g
= SkColorGetG(c
);
430 SkColor b
= SkColorGetB(c
);
444 // Covariance (not normalized) is E(X*X.t) - m * m.t and this is how it
445 // is calculated below.
446 // Each row below represents a row of the matrix describing (co)variances
447 // of R, G and B channels with (R, G, B)
448 int pixel_n
= bitmap
.width() * bitmap
.height();
450 (static_cast<double>(rr_sum
) / pixel_n
-
451 static_cast<double>(r_sum
* r_sum
) / pixel_n
/ pixel_n
),
452 (static_cast<double>(rg_sum
) / pixel_n
-
453 static_cast<double>(r_sum
* g_sum
) / pixel_n
/ pixel_n
),
454 (static_cast<double>(rb_sum
) / pixel_n
-
455 static_cast<double>(r_sum
* b_sum
) / pixel_n
/ pixel_n
),
456 (static_cast<double>(rg_sum
) / pixel_n
-
457 static_cast<double>(r_sum
* g_sum
) / pixel_n
/ pixel_n
),
458 (static_cast<double>(gg_sum
) / pixel_n
-
459 static_cast<double>(g_sum
* g_sum
) / pixel_n
/ pixel_n
),
460 (static_cast<double>(gb_sum
) / pixel_n
-
461 static_cast<double>(g_sum
* b_sum
) / pixel_n
/ pixel_n
),
462 (static_cast<double>(rb_sum
) / pixel_n
-
463 static_cast<double>(r_sum
* b_sum
) / pixel_n
/ pixel_n
),
464 (static_cast<double>(gb_sum
) / pixel_n
-
465 static_cast<double>(g_sum
* b_sum
) / pixel_n
/ pixel_n
),
466 (static_cast<double>(bb_sum
) / pixel_n
-
467 static_cast<double>(b_sum
* b_sum
) / pixel_n
/ pixel_n
));
471 bool ApplyColorReduction(const SkBitmap
& source_bitmap
,
472 const gfx::Vector3dF
& color_transform
,
474 SkBitmap
* target_bitmap
) {
475 DCHECK(target_bitmap
);
476 SkAutoLockPixels
source_lock(source_bitmap
);
477 SkAutoLockPixels
target_lock(*target_bitmap
);
479 DCHECK(source_bitmap
.getPixels());
480 DCHECK(target_bitmap
->getPixels());
481 DCHECK_EQ(SkBitmap::kARGB_8888_Config
, source_bitmap
.config());
482 DCHECK_EQ(SkBitmap::kA8_Config
, target_bitmap
->config());
483 DCHECK_EQ(source_bitmap
.height(), target_bitmap
->height());
484 DCHECK_EQ(source_bitmap
.width(), target_bitmap
->width());
485 DCHECK(!source_bitmap
.empty());
487 // Elements of color_transform are explicitly off-loaded to local values for
488 // efficiency reasons. Note that in practice images may correspond to entire
491 float tr
= color_transform
.x();
492 float tg
= color_transform
.y();
493 float tb
= color_transform
.z();
496 // We will figure out min/max in a preprocessing step and adjust
497 // actual_transform as required.
498 float max_val
= std::numeric_limits
<float>::min();
499 float min_val
= std::numeric_limits
<float>::max();
500 for (int y
= 0; y
< source_bitmap
.height(); ++y
) {
501 const SkPMColor
* source_color_row
= static_cast<SkPMColor
*>(
502 source_bitmap
.getAddr32(0, y
));
503 for (int x
= 0; x
< source_bitmap
.width(); ++x
) {
504 SkColor c
= SkUnPreMultiply::PMColorToColor(source_color_row
[x
]);
505 float r
= SkColorGetR(c
);
506 float g
= SkColorGetG(c
);
507 float b
= SkColorGetB(c
);
508 float gray_level
= tr
* r
+ tg
* g
+ tb
* b
;
509 max_val
= std::max(max_val
, gray_level
);
510 min_val
= std::min(min_val
, gray_level
);
514 // Adjust the transform so that the result is scaling.
517 if (max_val
> min_val
)
518 scale
= 255.0 / (max_val
- min_val
);
525 for (int y
= 0; y
< source_bitmap
.height(); ++y
) {
526 const SkPMColor
* source_color_row
= static_cast<SkPMColor
*>(
527 source_bitmap
.getAddr32(0, y
));
528 uint8_t* target_color_row
= target_bitmap
->getAddr8(0, y
);
529 for (int x
= 0; x
< source_bitmap
.width(); ++x
) {
530 SkColor c
= SkUnPreMultiply::PMColorToColor(source_color_row
[x
]);
531 float r
= SkColorGetR(c
);
532 float g
= SkColorGetG(c
);
533 float b
= SkColorGetB(c
);
535 float gl
= t0
+ tr
* r
+ tg
* g
+ tb
* b
;
540 target_color_row
[x
] = static_cast<uint8_t>(gl
);
547 bool ComputePrincipalComponentImage(const SkBitmap
& source_bitmap
,
548 SkBitmap
* target_bitmap
) {
549 if (!target_bitmap
) {
554 gfx::Matrix3F covariance
= ComputeColorCovariance(source_bitmap
);
555 gfx::Matrix3F eigenvectors
= gfx::Matrix3F::Zeros();
556 gfx::Vector3dF eigenvals
= covariance
.SolveEigenproblem(&eigenvectors
);
557 gfx::Vector3dF principal
= eigenvectors
.get_column(0);
558 if (eigenvals
== gfx::Vector3dF() || principal
== gfx::Vector3dF())
559 return false; // This may happen for some edge cases.
560 return ApplyColorReduction(source_bitmap
, principal
, true, target_bitmap
);