Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: ui/gfx/color_analysis.cc

Issue 2943333003: Extracting more than one wallpaper prominent color (Closed)
Patch Set: feedback from ps4 Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/color_analysis.h" 5 #include "ui/gfx/color_analysis.h"
6 6
7 #include <limits.h> 7 #include <limits.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 (SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3.0f; 342 (SkColorGetR(color) + SkColorGetG(color) + SkColorGetB(color)) / 3.0f;
343 // If a color is too close to white or black, ignore it. 343 // If a color is too close to white or black, ignore it.
344 if (average_channel_value >= 237 || average_channel_value <= 22) 344 if (average_channel_value >= 237 || average_channel_value <= 22)
345 return false; 345 return false;
346 346
347 HSL hsl; 347 HSL hsl;
348 SkColorToHSL(color, &hsl); 348 SkColorToHSL(color, &hsl);
349 return !(hsl.h >= 0.028f && hsl.h <= 0.10f && hsl.s <= 0.82f); 349 return !(hsl.h >= 0.028f && hsl.h <= 0.10f && hsl.s <= 0.82f);
350 } 350 }
351 351
352 // Used to group lower_bound, upper_bound, goal HSL color together for prominent
353 // color calculation.
354 struct ColorBracket {
355 HSL lower_bound = {-1};
356 HSL upper_bound = {-1};
357 HSL goal = {-1};
358 };
359
352 // This algorithm is a port of Android's Palette API. Compare to package 360 // This algorithm is a port of Android's Palette API. Compare to package
353 // android.support.v7.graphics and see that code for additional high-level 361 // android.support.v7.graphics and see that code for additional high-level
354 // explanation of this algorithm. There are some minor differences: 362 // explanation of this algorithm. There are some minor differences:
355 // * This code doesn't exclude the same color from being used for 363 // * This code doesn't exclude the same color from being used for
356 // different color profiles. 364 // different color profiles.
357 // * This code doesn't try to heuristically derive missing colors from 365 // * This code doesn't try to heuristically derive missing colors from
358 // existing colors. 366 // existing colors.
359 SkColor CalculateProminentColor(const SkBitmap& bitmap, 367 std::vector<SkColor>
360 const HSL& lower_bound, 368 CalculateProminentColors(const SkBitmap& bitmap,
361 const HSL& upper_bound, 369 const std::vector<ColorBracket>& color_brackets) {
362 const HSL& goal) {
363 DCHECK(!bitmap.empty()); 370 DCHECK(!bitmap.empty());
364 DCHECK(!bitmap.isNull()); 371 DCHECK(!bitmap.isNull());
365 372
366 const uint32_t* pixels = static_cast<uint32_t*>(bitmap.getPixels()); 373 const uint32_t* pixels = static_cast<uint32_t*>(bitmap.getPixels());
367 const int pixel_count = bitmap.width() * bitmap.height(); 374 const int pixel_count = bitmap.width() * bitmap.height();
368 375
369 // For better performance, only consider at most 10k pixels (evenly 376 // For better performance, only consider at most 10k pixels (evenly
370 // distributed throughout the image). This has a very minor impact on the 377 // distributed throughout the image). This has a very minor impact on the
371 // outcome but improves runtime substantially for large images. 10,007 is a 378 // outcome but improves runtime substantially for large images. 10,007 is a
372 // prime number to reduce the chance of picking an unrepresentative sample. 379 // prime number to reduce the chance of picking an unrepresentative sample.
(...skipping 14 matching lines...) Expand all
387 394
388 // Now throw out some uninteresting colors. 395 // Now throw out some uninteresting colors.
389 std::vector<SkColor> interesting_colors; 396 std::vector<SkColor> interesting_colors;
390 interesting_colors.reserve(color_counts.size()); 397 interesting_colors.reserve(color_counts.size());
391 for (auto color_count : color_counts) { 398 for (auto color_count : color_counts) {
392 SkColor color = color_count.first; 399 SkColor color = color_count.first;
393 if (IsInterestingColor(color)) 400 if (IsInterestingColor(color))
394 interesting_colors.push_back(color); 401 interesting_colors.push_back(color);
395 } 402 }
396 403
404 std::vector<SkColor> best_colors(color_brackets.size(), SK_ColorTRANSPARENT);
397 if (interesting_colors.empty()) 405 if (interesting_colors.empty())
398 return SK_ColorTRANSPARENT; 406 return best_colors;
399 407
400 // Group the colors into "boxes" and repeatedly split the most voluminous box. 408 // Group the colors into "boxes" and repeatedly split the most voluminous box.
401 // We stop the process when a box can no longer be split (there's only one 409 // We stop the process when a box can no longer be split (there's only one
402 // color in it) or when the number of color boxes reaches 12. As per the 410 // color in it) or when the number of color boxes reaches 12. As per the
403 // Android docs, 411 // Android docs,
404 // 412 //
405 // For landscapes, good values are in the range 12-16. For images which 413 // For landscapes, good values are in the range 12-16. For images which
406 // are largely made up of people's faces then this value should be increased 414 // are largely made up of people's faces then this value should be increased
407 // to 24-32. 415 // to 24-32.
408 const int kMaxColors = 12; 416 const int kMaxColors = 12;
(...skipping 14 matching lines...) Expand all
423 // Now extract a single color to represent each box. This is the average color 431 // Now extract a single color to represent each box. This is the average color
424 // in the box, weighted by the frequency of that color in the source image. 432 // in the box, weighted by the frequency of that color in the source image.
425 std::vector<WeightedColor> box_colors; 433 std::vector<WeightedColor> box_colors;
426 uint64_t max_weight = 0; 434 uint64_t max_weight = 0;
427 while (!boxes.empty()) { 435 while (!boxes.empty()) {
428 box_colors.push_back(boxes.top().GetWeightedAverageColor(color_counts)); 436 box_colors.push_back(boxes.top().GetWeightedAverageColor(color_counts));
429 boxes.pop(); 437 boxes.pop();
430 max_weight = std::max(max_weight, box_colors.back().weight); 438 max_weight = std::max(max_weight, box_colors.back().weight);
431 } 439 }
432 440
433 // Given these box average colors, find the best one for the desired color 441 // Given these box average colors, find the best one for each desired color
434 // profile. "Best" in this case means the color which fits in the provided 442 // profile. "Best" in this case means the color which fits in the provided
435 // bounds and comes closest to |goal|. It's possible that no color will fit in 443 // bounds and comes closest to |goal|. It's possible that no color will fit in
436 // the provided bounds, in which case we'll return an empty color. 444 // the provided bounds, in which case we'll return an empty color.
437 double best_suitability = 0; 445 for (size_t i = 0; i < color_brackets.size(); ++i) {
438 SkColor best_color = SK_ColorTRANSPARENT; 446 double best_suitability = 0;
439 for (const auto& box_color : box_colors) { 447 for (const auto& box_color : box_colors) {
440 HSL hsl; 448 HSL hsl;
441 SkColorToHSL(box_color.color, &hsl); 449 SkColorToHSL(box_color.color, &hsl);
442 if (!IsWithinHSLRange(hsl, lower_bound, upper_bound)) 450 if (!IsWithinHSLRange(hsl, color_brackets[i].lower_bound,
443 continue; 451 color_brackets[i].upper_bound)) {
452 continue;
453 }
444 454
445 double suitability = 455 double suitability =
446 (1 - std::abs(hsl.s - goal.s)) * 3 + 456 (1 - std::abs(hsl.s - color_brackets[i].goal.s)) * 3 +
447 (1 - std::abs(hsl.l - goal.l)) * 6.5 + 457 (1 - std::abs(hsl.l - color_brackets[i].goal.l)) * 6.5 +
448 (box_color.weight / static_cast<float>(max_weight)) * 0.5; 458 (box_color.weight / static_cast<float>(max_weight)) * 0.5;
449 if (suitability > best_suitability) { 459 if (suitability > best_suitability) {
450 best_suitability = suitability; 460 best_suitability = suitability;
451 best_color = box_color.color; 461 best_colors[i] = box_color.color;
462 }
452 } 463 }
453 } 464 }
454 465
455 return best_color; 466 return best_colors;
456 } 467 }
457 468
458 } // namespace 469 } // namespace
459 470
460 KMeanImageSampler::KMeanImageSampler() { 471 KMeanImageSampler::KMeanImageSampler() {
461 } 472 }
462 473
463 KMeanImageSampler::~KMeanImageSampler() { 474 KMeanImageSampler::~KMeanImageSampler() {
464 } 475 }
465 476
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
716 bitmap.width(), bitmap.height(), 727 bitmap.width(), bitmap.height(),
717 lower_bound, upper_bound, sampler); 728 lower_bound, upper_bound, sampler);
718 } 729 }
719 730
720 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) { 731 SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap) {
721 GridSampler sampler; 732 GridSampler sampler;
722 return CalculateKMeanColorOfBitmap( 733 return CalculateKMeanColorOfBitmap(
723 bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler); 734 bitmap, kDefaultLowerHSLBound, kDefaultUpperHSLBound, &sampler);
724 } 735 }
725 736
726 SkColor CalculateProminentColorOfBitmap(const SkBitmap& bitmap, 737 std::vector<SkColor> CalculateProminentColorsOfBitmap(
727 LumaRange luma, 738 const SkBitmap& bitmap,
728 SaturationRange saturation) { 739 const std::vector<ColorProfile>& color_profiles) {
740 size_t size = color_profiles.size();
729 if (bitmap.empty() || bitmap.isNull()) 741 if (bitmap.empty() || bitmap.isNull())
bruthig 2017/06/21 15:54:17 This should probably return early if color_profile
Qiang(Joe) Xu 2017/06/21 22:35:27 Done.
730 return SK_ColorTRANSPARENT; 742 return std::vector<SkColor>(size, SK_ColorTRANSPARENT);
731 743
732 // The hue is not relevant to our bounds or goal colors. 744 // The hue is not relevant to our bounds or goal colors.
733 HSL lower_bound = { 745 std::vector<ColorBracket> color_brackets(size);
734 -1, 746 for (size_t i = 0; i < size; ++i) {
735 }; 747 switch (color_profiles[i].luma) {
736 HSL upper_bound = { 748 case LumaRange::LIGHT:
737 -1, 749 color_brackets[i].lower_bound.l = 0.55f;
738 }; 750 color_brackets[i].upper_bound.l = 1;
739 HSL goal = { 751 color_brackets[i].goal.l = 0.74f;
740 -1, 752 break;
741 }; 753 case LumaRange::NORMAL:
754 color_brackets[i].lower_bound.l = 0.3f;
755 color_brackets[i].upper_bound.l = 0.7f;
756 color_brackets[i].goal.l = 0.5f;
757 break;
758 case LumaRange::DARK:
759 color_brackets[i].lower_bound.l = 0;
760 color_brackets[i].upper_bound.l = 0.45f;
761 color_brackets[i].goal.l = 0.26f;
762 break;
763 }
742 764
743 switch (luma) { 765 switch (color_profiles[i].saturation) {
744 case LumaRange::LIGHT: 766 case SaturationRange::VIBRANT:
745 lower_bound.l = 0.55f; 767 color_brackets[i].lower_bound.s = 0.35f;
746 upper_bound.l = 1; 768 color_brackets[i].upper_bound.s = 1;
747 goal.l = 0.74f; 769 color_brackets[i].goal.s = 1;
748 break; 770 break;
749 case LumaRange::NORMAL: 771 case SaturationRange::MUTED:
750 lower_bound.l = 0.3f; 772 color_brackets[i].lower_bound.s = 0;
751 upper_bound.l = 0.7f; 773 color_brackets[i].upper_bound.s = 0.4f;
752 goal.l = 0.5f; 774 color_brackets[i].goal.s = 0.3f;
753 break; 775 break;
754 case LumaRange::DARK: 776 }
755 lower_bound.l = 0;
756 upper_bound.l = 0.45f;
757 goal.l = 0.26f;
758 break;
759 } 777 }
760 778
761 switch (saturation) { 779 return CalculateProminentColors(bitmap, color_brackets);
762 case SaturationRange::VIBRANT:
763 lower_bound.s = 0.35f;
764 upper_bound.s = 1;
765 goal.s = 1;
766 break;
767 case SaturationRange::MUTED:
768 lower_bound.s = 0;
769 upper_bound.s = 0.4f;
770 goal.s = 0.3f;
771 break;
772 }
773
774 return CalculateProminentColor(bitmap, lower_bound, upper_bound, goal);
775 } 780 }
776 781
777 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) { 782 gfx::Matrix3F ComputeColorCovariance(const SkBitmap& bitmap) {
778 // First need basic stats to normalize each channel separately. 783 // First need basic stats to normalize each channel separately.
779 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros(); 784 gfx::Matrix3F covariance = gfx::Matrix3F::Zeros();
780 if (!bitmap.getPixels()) 785 if (!bitmap.getPixels())
781 return covariance; 786 return covariance;
782 787
783 // Assume ARGB_8888 format. 788 // Assume ARGB_8888 format.
784 DCHECK(bitmap.colorType() == kN32_SkColorType); 789 DCHECK(bitmap.colorType() == kN32_SkColorType);
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after
932 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap); 937 gfx::Matrix3F covariance = ComputeColorCovariance(source_bitmap);
933 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros(); 938 gfx::Matrix3F eigenvectors = gfx::Matrix3F::Zeros();
934 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors); 939 gfx::Vector3dF eigenvals = covariance.SolveEigenproblem(&eigenvectors);
935 gfx::Vector3dF principal = eigenvectors.get_column(0); 940 gfx::Vector3dF principal = eigenvectors.get_column(0);
936 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF()) 941 if (eigenvals == gfx::Vector3dF() || principal == gfx::Vector3dF())
937 return false; // This may happen for some edge cases. 942 return false; // This may happen for some edge cases.
938 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap); 943 return ApplyColorReduction(source_bitmap, principal, true, target_bitmap);
939 } 944 }
940 945
941 } // color_utils 946 } // color_utils
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698