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

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

Issue 7031078: Retry r88137: (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix for test failures Created 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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
5 #include "ui/gfx/color_analysis.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "ui/gfx/codec/png_codec.h"
11
12 namespace {
13
14 // RGBA KMean Constants
15 const uint32_t kNumberOfClusters = 4;
16 const int kNumberOfIterations = 50;
17 const uint32_t kMaxBrightness = 600;
18 const uint32_t kMinDarkness = 100;
19
20 // Background Color Modification Constants
21 const SkColor kDefaultBgColor = SK_ColorWHITE;
22
23 // Support class to hold information about each cluster of pixel data in
24 // the KMean algorithm. While this class does not contain all of the points
25 // that exist in the cluster, it keeps track of the aggregate sum so it can
26 // compute the new center appropriately.
27 class KMeanCluster {
28 public:
29 KMeanCluster() {
30 Reset();
31 }
32
33 void Reset() {
34 centroid[0] = centroid[1] = centroid[2] = 0;
35 aggregate[0] = aggregate[1] = aggregate[2] = 0;
36 counter = 0;
37 weight = 0;
38 }
39
40 inline void SetCentroid(uint8_t r, uint8_t g, uint8_t b) {
41 centroid[0] = r;
42 centroid[1] = g;
43 centroid[2] = b;
44 }
45
46 inline void GetCentroid(uint8_t* r, uint8_t* g, uint8_t* b) {
47 *r = centroid[0];
48 *g = centroid[1];
49 *b = centroid[2];
50 }
51
52 inline bool IsAtCentroid(uint8_t r, uint8_t g, uint8_t b) {
53 return r == centroid[0] && g == centroid[1] && b == centroid[2];
54 }
55
56 // Recomputes the centroid of the cluster based on the aggregate data. The
57 // number of points used to calculate this center is stored for weighting
58 // purposes. The aggregate and counter are then cleared to be ready for the
59 // next iteration.
60 inline void RecomputeCentroid() {
61 if (counter > 0) {
62 centroid[0] = aggregate[0] / counter;
63 centroid[1] = aggregate[1] / counter;
64 centroid[2] = aggregate[2] / counter;
65
66 aggregate[0] = aggregate[1] = aggregate[2] = 0;
67 weight = counter;
68 counter = 0;
69 }
70 }
71
72 inline void AddPoint(uint8_t r, uint8_t g, uint8_t b) {
73 aggregate[0] += r;
74 aggregate[1] += g;
75 aggregate[2] += b;
76 ++counter;
77 }
78
79 // Just returns the distance^2. Since we are comparing relative distances
80 // there is no need to perform the expensive sqrt() operation.
81 inline uint32_t GetDistanceSqr(uint8_t r, uint8_t g, uint8_t b) {
82 return (r - centroid[0]) * (r - centroid[0]) +
83 (g - centroid[1]) * (g - centroid[1]) +
84 (b - centroid[2]) * (b - centroid[2]);
85 }
86
87 // In order to determine if we have hit convergence or not we need to see
88 // if the centroid of the cluster has moved. This determines whether or
89 // not the centroid is the same as the aggregate sum of points that will be
90 // used to generate the next centroid.
91 inline bool CompareCentroidWithAggregate() {
92 if (counter == 0)
93 return false;
94
95 return aggregate[0] / counter == centroid[0] &&
96 aggregate[1] / counter == centroid[1] &&
97 aggregate[2] / counter == centroid[2];
98 }
99
100 // Returns the previous counter, which is used to determine the weight
101 // of the cluster for sorting.
102 inline uint32_t GetWeight() {
103 return weight;
104 }
105
106 static bool SortKMeanClusterByWeight(KMeanCluster a, KMeanCluster b) {
107 return a.GetWeight() > b.GetWeight();
108 }
109
110 private:
111 uint8_t centroid[3];
112
113 // Holds the sum of all the points that make up this cluster. Used to
114 // generate the next centroid as well as to check for convergence.
115 uint32_t aggregate[3];
116 uint32_t counter;
117
118 // The weight of the cluster, determined by how many points were used
119 // to generate the previous centroid.
120 uint32_t weight;
121 };
122
123 } // namespace
124
125 namespace color_utils {
126
127 KMeanImageSampler::KMeanImageSampler() {
128 }
129
130 KMeanImageSampler::~KMeanImageSampler() {
131 }
132
133 RandomSampler::RandomSampler() {
134 }
135
136 RandomSampler::~RandomSampler() {
137 }
138
139 int RandomSampler::GetSample(int width, int height) {
140 return rand();
141 }
142
143 GridSampler::GridSampler() : calls_(0) {
144 }
145
146 GridSampler::~GridSampler() {
147 }
148
149 int GridSampler::GetSample(int width, int height) {
150 calls_++;
151 // We may keep getting called after we've gone of the edge of the grid; in
152 // this case we offset future return values by the number of times we've gone
153 // off the grid.
154 return (width * height * calls_ / kNumberOfClusters) % (width * height) +
155 calls_ / kNumberOfClusters;
156 }
157
158 SkColor CalculateRecommendedBgColorForPNG(
159 scoped_refptr<RefCountedMemory> png) {
160 RandomSampler sampler;
161 return CalculateRecommendedBgColorForPNG(png, sampler);
162 }
163
164 SkColor CalculateKMeanColorOfPNG(scoped_refptr<RefCountedMemory> png,
165 uint32_t darkness_limit,
166 uint32_t brightness_limit) {
167 RandomSampler sampler;
168 return CalculateKMeanColorOfPNG(png, darkness_limit, brightness_limit,
169 sampler);
170 }
171
172 SkColor CalculateRecommendedBgColorForPNG(
173 scoped_refptr<RefCountedMemory> png,
174 KMeanImageSampler& sampler) {
175 return CalculateKMeanColorOfPNG(png,
176 kMinDarkness,
177 kMaxBrightness,
178 sampler);
179 }
180
181 SkColor CalculateKMeanColorOfPNG(scoped_refptr<RefCountedMemory> png,
182 uint32_t darkness_limit,
183 uint32_t brightness_limit,
184 KMeanImageSampler& sampler) {
185 int img_width, img_height;
186 std::vector<uint8_t> decoded_data;
187 SkColor color = kDefaultBgColor;
188
189 if (png.get() &&
190 png->size() &&
191 gfx::PNGCodec::Decode(png->front(),
192 png->size(),
193 gfx::PNGCodec::FORMAT_BGRA,
194 &decoded_data,
195 &img_width,
196 &img_height)) {
197 std::vector<KMeanCluster> clusters;
198 clusters.resize(kNumberOfClusters, KMeanCluster());
199
200 // Pick a starting point for each cluster
201 std::vector<KMeanCluster>::iterator cluster = clusters.begin();
202 while (cluster != clusters.end()) {
203 // Try up to 10 times to find a unique color. If no unique color can be
204 // found, destroy this cluster.
205 bool color_unique = false;
206 for (int i = 0; i < 10; ++i) {
207 int pixel_pos = sampler.GetSample(img_width, img_height) %
208 (img_width * img_height);
209
210 uint8_t b = decoded_data[pixel_pos * 4];
211 uint8_t g = decoded_data[pixel_pos * 4 + 1];
212 uint8_t r = decoded_data[pixel_pos * 4 + 2];
213
214 // Loop through the previous clusters and check to see if we have seen
215 // this color before.
216 color_unique = true;
217 for (std::vector<KMeanCluster>::iterator
218 cluster_check = clusters.begin();
219 cluster_check != cluster; ++cluster_check) {
220 if (cluster_check->IsAtCentroid(r, g, b)) {
221 color_unique = false;
222 break;
223 }
224 }
225
226 // If we have a unique color set the center of the cluster to
227 // that color.
228 if (color_unique) {
229 cluster->SetCentroid(r, g, b);
230 break;
231 }
232 }
233
234 // If we don't have a unique color erase this cluster.
235 if (!color_unique) {
236 cluster = clusters.erase(cluster);
237 } else {
238 // Have to increment the iterator here, otherwise the increment in the
239 // for loop will skip a cluster due to the erase if the color wasn't
240 // unique.
241 ++cluster;
242 }
243 }
244
245 bool convergence = false;
246 for (int iteration = 0;
247 iteration < kNumberOfIterations && !convergence && !clusters.empty();
248 ++iteration) {
249
250 // Loop through each pixel so we can place it in the appropriate cluster.
251 std::vector<uint8_t>::iterator pixel = decoded_data.begin();
252 while (pixel != decoded_data.end()) {
253 uint8_t b = *(pixel++);
254 if (pixel == decoded_data.end())
255 continue;
256 uint8_t g = *(pixel++);
257 if (pixel == decoded_data.end())
258 continue;
259 uint8_t r = *(pixel++);
260 if (pixel == decoded_data.end())
261 continue;
262 ++pixel; // Ignore the alpha channel.
263
264 uint32_t distance_sqr_to_closest_cluster = UINT_MAX;
265 std::vector<KMeanCluster>::iterator closest_cluster = clusters.begin();
266
267 // Figure out which cluster this color is closest to in RGB space.
268 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
269 cluster != clusters.end(); ++cluster) {
270 uint32_t distance_sqr = cluster->GetDistanceSqr(r, g, b);
271
272 if (distance_sqr < distance_sqr_to_closest_cluster) {
273 distance_sqr_to_closest_cluster = distance_sqr;
274 closest_cluster = cluster;
275 }
276 }
277
278 closest_cluster->AddPoint(r, g, b);
279 }
280
281 // Calculate the new cluster centers and see if we've converged or not.
282 convergence = true;
283 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
284 cluster != clusters.end(); ++cluster) {
285 convergence &= cluster->CompareCentroidWithAggregate();
286
287 cluster->RecomputeCentroid();
288 }
289 }
290
291 // Sort the clusters by population so we can tell what the most popular
292 // color is.
293 std::sort(clusters.begin(), clusters.end(),
294 KMeanCluster::SortKMeanClusterByWeight);
295
296 // Loop through the clusters to figure out which cluster has an appropriate
297 // color. Skip any that are too bright/dark and go in order of weight.
298 for (std::vector<KMeanCluster>::iterator cluster = clusters.begin();
299 cluster != clusters.end(); ++cluster) {
300 uint8_t r, g, b;
301 cluster->GetCentroid(&r, &g, &b);
302 // Sum the RGB components to determine if the color is too bright or too
303 // dark.
304 // TODO (dtrainor): Look into using HSV here instead. This approximation
305 // might be fine though.
306 uint32_t summed_color = r + g + b;
307
308 if (summed_color < brightness_limit && summed_color > darkness_limit) {
309 // If we found a valid color just set it and break. We don't want to
310 // check the other ones.
311 color = SkColorSetARGB(0xFF, r, g, b);
312 break;
313 } else if (cluster == clusters.begin()) {
314 // We haven't found a valid color, but we are at the first color so
315 // set the color anyway to make sure we at least have a value here.
316 color = SkColorSetARGB(0xFF, r, g, b);
317 }
318 }
319 }
320
321 return color;
322 }
323
324 } // color_utils
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698