Android笔记——Bitmap自动取色(纯搬运)

2015/6/12更新:发现一个更好的,带demo

https://github.com/MichaelEvans/ColorArt

说明:
这个是一个老外写的自动自动从bitmap中取主色与第二主色的工具类,稍微研究了下用法,但感觉效果一般,记录下。
感兴趣的同学可以自行研究下,老外的代码没注释,这点给黄老师我造成的困惑不少。
顺便附上老外的github地址:https://gist.github.com/chrisbanes/ba8e7b9ec0e40f6949c6

大概的用法:

1 image = (ImageView)findViewById(R.id.image);
2 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
3 DominantColorCalculator colorCalculator = new DominantColorCalculator(bitmap);
4 ColorScheme scheme = colorCalculator.getColorScheme();
5 View main = findViewById(R.id.main);
6 View second = findViewById(R.id.second);
7 main.setBackgroundColor(scheme.primaryText);
8 second.setBackgroundColor(scheme.secondaryText);



老外的核心代码,及简单解释:
1、ColorScheme类,作用貌似是记录颜色,其中xxxtext都是Color对象

 1 /*
 2  * Copyright 2014 Chris Banes
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *     http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class ColorScheme {
18 
19     public final int primaryAccent;
20     public final int secondaryAccent;
21     public final int tertiaryAccent;
22 
23     public final int primaryText;
24     public final int secondaryText;
25 
26     public ColorScheme(int primaryAccent, int secondaryAccent, int tertiaryAccent,
27             int primaryText, int secondaryText) {
28         this.primaryAccent = primaryAccent;
29         this.secondaryAccent = secondaryAccent;
30         this.tertiaryAccent = tertiaryAccent;
31         this.primaryText = primaryText;
32         this.secondaryText = secondaryText;
33     }
34 }



2、ColorUtils类,有一些颜色操作的工具方法,比如颜色混合、亮暗调整、YIQ转换等等

 1 /*
 2  * Copyright 2014 Chris Banes
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *     http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import android.graphics.Color;
18 
19 public class ColorUtils {
20 
21     public static int darken(final int color, float fraction) {
22         return blendColors(Color.BLACK, color, fraction);
23     }
24 
25     public static int lighten(final int color, float fraction) {
26         return blendColors(Color.WHITE, color, fraction);
27     }
28 
29     /**
30      * @return luma value according to to YIQ color space.
31      */
32     public static final int calculateYiqLuma(int color) {
33         return Math.round((299 * Color.red(color) + 587 * Color.green(color) + 114 * Color.blue(color)) / 1000f);
34     }
35 
36     /**
37      * Blend {@code color1} and {@code color2} using the given ratio.
38      *
39      * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
40      *              0.0 will return {@code color2}.
41      */
42     public static int blendColors(int color1, int color2, float ratio) {
43         final float inverseRatio = 1f - ratio;
44         float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRatio);
45         float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRatio);
46         float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRatio);
47         return Color.rgb((int) r, (int) g, (int) b);
48     }
49 
50     public static final int changeBrightness(final int color, float fraction) {
51         return calculateYiqLuma(color) >= 128
52                 ? darken(color, fraction)
53                 : lighten(color, fraction);
54     }
55 
56     public static final int calculateContrast(MedianCutQuantizer.ColorNode color1,
57             MedianCutQuantizer.ColorNode color2) {
58         return Math.abs(ColorUtils.calculateYiqLuma(color1.getRgb())
59                 - ColorUtils.calculateYiqLuma(color2.getRgb()));
60     }
61 
62     public static final float calculateColorfulness(MedianCutQuantizer.ColorNode node) {
63         float[] hsv = node.getHsv();
64         return hsv[1] * hsv[2];
65     }
66 
67 }



3、和Android的Bitmap对象的接口类,构造方法中传入bitmap对象即开始转换,然后通过getColorScheme获得抓取到的颜色

  1 /*
  2  * Copyright 2014 Chris Banes
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *     http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 import android.graphics.Bitmap;
 18 import android.graphics.Color;
 19 
 20 import java.util.Arrays;
 21 import java.util.Comparator;
 22 
 23 import org.apache.chrisbanes.colorscheme.MedianCutQuantizer.ColorNode;
 24 
 25 public class DominantColorCalculator {
 26 
 27     private static final int NUM_COLORS = 10;
 28 
 29     private static final int PRIMARY_TEXT_MIN_CONTRAST = 135;
 30 
 31     private static final int SECONDARY_MIN_DIFF_HUE_PRIMARY = 120;
 32 
 33     private static final int TERTIARY_MIN_CONTRAST_PRIMARY = 20;
 34     private static final int TERTIARY_MIN_CONTRAST_SECONDARY = 90;
 35 
 36     private final MedianCutQuantizer.ColorNode[] mPalette;
 37     private final MedianCutQuantizer.ColorNode[] mWeightedPalette;
 38     private ColorScheme mColorScheme;
 39 
 40     public DominantColorCalculator(Bitmap bitmap) {
 41         final int width = bitmap.getWidth();
 42         final int height = bitmap.getHeight();
 43 
 44         final int[] rgbPixels = new int[width * height];
 45         bitmap.getPixels(rgbPixels, 0, width, 0, 0, width, height);
 46 
 47         final MedianCutQuantizer mcq = new MedianCutQuantizer(rgbPixels, NUM_COLORS);
 48 
 49         mPalette = mcq.getQuantizedColors();
 50         mWeightedPalette = weight(mPalette);
 51 
 52         findColors();
 53     }
 54 
 55     public ColorScheme getColorScheme() {
 56         return mColorScheme;
 57     }
 58 
 59     private void findColors() {
 60         final ColorNode primaryAccentColor = findPrimaryAccentColor();
 61         final ColorNode secondaryAccentColor = findSecondaryAccentColor(primaryAccentColor);
 62 
 63         final int tertiaryAccentColor = findTertiaryAccentColor(
 64                 primaryAccentColor, secondaryAccentColor);
 65 
 66         final int primaryTextColor = findPrimaryTextColor(primaryAccentColor);
 67         final int secondaryTextColor = findSecondaryTextColor(primaryAccentColor);
 68 
 69         mColorScheme = new ColorScheme(
 70                 primaryAccentColor.getRgb(),
 71                 secondaryAccentColor.getRgb(),
 72                 tertiaryAccentColor,
 73                 primaryTextColor,
 74                 secondaryTextColor);
 75     }
 76 
 77     /**
 78      * @return the first color from our weighted palette.
 79      */
 80     private ColorNode findPrimaryAccentColor() {
 81         return mWeightedPalette[0];
 82     }
 83 
 84     /**
 85      * @return the next color in the weighted palette which ideally has enough difference in hue.
 86      */
 87     private ColorNode findSecondaryAccentColor(final ColorNode primary) {
 88         final float primaryHue = primary.getHsv()[0];
 89 
 90         // Find the first color which has sufficient difference in hue from the primary
 91         for (ColorNode candidate : mWeightedPalette) {
 92             final float candidateHue = candidate.getHsv()[0];
 93 
 94             // Calculate the difference in hue, if it's over the threshold return it
 95             if (Math.abs(primaryHue - candidateHue) >= SECONDARY_MIN_DIFF_HUE_PRIMARY) {
 96                 return candidate;
 97             }
 98         }
 99 
100         // If we get here, just return the second weighted color
101         return mWeightedPalette[1];
102     }
103 
104     /**
105      * @return the first color from our weighted palette which has sufficient contrast from the
106      *         primary and secondary colors.
107      */
108     private int findTertiaryAccentColor(final ColorNode primary, final ColorNode secondary) {
109         // Find the first color which has sufficient contrast from both the primary & secondary
110         for (ColorNode color : mWeightedPalette) {
111             if (ColorUtils.calculateContrast(color, primary) >= TERTIARY_MIN_CONTRAST_PRIMARY
112                     && ColorUtils.calculateContrast(color, secondary) >= TERTIARY_MIN_CONTRAST_SECONDARY) {
113                 return color.getRgb();
114             }
115         }
116 
117         // We couldn't find a colour. In that case use the primary colour, modifying it's brightness
118         // by 45%
119         return ColorUtils.changeBrightness(secondary.getRgb(), 0.45f);
120     }
121 
122     /**
123      * @return the first color which has sufficient contrast from the primary colors.
124      */
125     private int findPrimaryTextColor(final ColorNode primary) {
126         // Try and find a colour with sufficient contrast from the primary colour
127         for (ColorNode color : mPalette) {
128             if (ColorUtils.calculateContrast(color, primary) >= PRIMARY_TEXT_MIN_CONTRAST) {
129                 return color.getRgb();
130             }
131         }
132 
133         // We haven't found a colour, so return black/white depending on the primary colour's
134         // brightness
135         return ColorUtils.calculateYiqLuma(primary.getRgb()) >= 128 ? Color.BLACK : Color.WHITE;
136     }
137 
138     /**
139      * @return return black/white depending on the primary colour's brightness
140      */
141     private int findSecondaryTextColor(final ColorNode primary) {
142         return ColorUtils.calculateYiqLuma(primary.getRgb()) >= 128 ? Color.BLACK : Color.WHITE;
143     }
144 
145     private static ColorNode[] weight(ColorNode[] palette) {
146         final MedianCutQuantizer.ColorNode[] copy = Arrays.copyOf(palette, palette.length);
147         final float maxCount = palette[0].getCount();
148 
149         Arrays.sort(copy, new Comparator<ColorNode>() {
150             @Override
151             public int compare(ColorNode lhs, ColorNode rhs) {
152                 final float lhsWeight = calculateWeight(lhs, maxCount);
153                 final float rhsWeight = calculateWeight(rhs, maxCount);
154 
155                 if (lhsWeight < rhsWeight) {
156                     return 1;
157                 } else if (lhsWeight > rhsWeight) {
158                     return -1;
159                 }
160                 return 0;
161             }
162         });
163 
164         return copy;
165     }
166 
167     private static float calculateWeight(ColorNode node, final float maxCount) {
168         return FloatUtils.weightedAverage(
169                 ColorUtils.calculateColorfulness(node), 2f,
170                 (node.getCount() / maxCount), 1f
171         );
172     }
173 
174 }



4、一个计算浮点数组平均权重平均值的工具方法类

 1 /*
 2  * Copyright 2014 Chris Banes
 3  *
 4  * Licensed under the Apache License, Version 2.0 (the "License");
 5  * you may not use this file except in compliance with the License.
 6  * You may obtain a copy of the License at
 7  *
 8  *     http://www.apache.org/licenses/LICENSE-2.0
 9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 public class FloatUtils {
18 
19     public static float weightedAverage(float... values) {
20         assert values.length % 2 == 0;
21 
22         float sum = 0;
23         float sumWeight = 0;
24 
25         for (int i = 0; i < values.length; i += 2) {
26             float value = values[i];
27             float weight = values[i + 1];
28 
29             sum += (value * weight);
30             sumWeight += weight;
31         }
32 
33         return sum / sumWeight;
34     }
35 
36 }



5、关键类,获取位图中的颜色

  1 /**
  2  * This sample code is made available as part of the book "Digital Image
  3  * Processing - An Algorithmic Introduction using Java" by Wilhelm Burger
  4  * and Mark J. Burge, Copyright (C) 2005-2008 Springer-Verlag Berlin,
  5  * Heidelberg, New York.
  6  * Note that this code comes with absolutely no warranty of any kind.
  7  * See http://www.imagingbook.com for details and licensing conditions.
  8  *
  9  * Modified by Chris Banes.
 10  */
 11 
 12 import android.graphics.Color;
 13 import android.util.Log;
 14 
 15 import java.util.ArrayList;
 16 import java.util.Arrays;
 17 import java.util.Comparator;
 18 import java.util.List;
 19 
 20 /*
 21  * This is an implementation of Heckbert's median-cut color quantization algorithm
 22  * (Heckbert P., "Color Image Quantization for Frame Buffer Display", ACM Transactions
 23  * on Computer Graphics (SIGGRAPH), pp. 297-307, 1982).
 24  * Unlike in the original algorithm, no initial uniform (scalar) quantization is used to
 25  * for reducing the number of image colors. Instead, all colors contained in the original
 26  * image are considered in the quantization process. After the set of representative
 27  * colors has been found, each image color is mapped to the closest representative
 28  * in RGB color space using the Euclidean distance.
 29  * The quantization process has two steps: first a ColorQuantizer object is created from
 30  * a given image using one of the constructor methods provided. Then this ColorQuantizer
 31  * can be used to quantize the original image or any other image using the same set of
 32  * representative colors (color table).
 33  */
 34 
 35 public class MedianCutQuantizer {
 36 
 37     private static final String LOG_TAG = MedianCutQuantizer.class
 38             .getSimpleName();
 39 
 40     private ColorNode[] imageColors = null; // original (unique) image colors
 41     private ColorNode[] quantColors = null; // quantized colors
 42 
 43     public MedianCutQuantizer(int[] pixels, int Kmax) {
 44         quantColors = findRepresentativeColors(pixels, Kmax);
 45     }
 46 
 47     public int countQuantizedColors() {
 48         return quantColors.length;
 49     }
 50 
 51     public ColorNode[] getQuantizedColors() {
 52         return quantColors;
 53     }
 54 
 55     ColorNode[] findRepresentativeColors(int[] pixels, int Kmax) {
 56         ColorHistogram colorHist = new ColorHistogram(pixels);
 57         int K = colorHist.getNumberOfColors();
 58         ColorNode[] rCols = null;
 59 
 60         imageColors = new ColorNode[K];
 61         for (int i = 0; i < K; i++) {
 62             int rgb = colorHist.getColor(i);
 63             int cnt = colorHist.getCount(i);
 64             imageColors[i] = new ColorNode(rgb, cnt);
 65         }
 66 
 67         if (K <= Kmax) {
 68             // image has fewer colors than Kmax
 69             rCols = imageColors;
 70         } else {
 71             ColorBox initialBox = new ColorBox(0, K - 1, 0);
 72             List<ColorBox> colorSet = new ArrayList<ColorBox>();
 73             colorSet.add(initialBox);
 74             int k = 1;
 75             boolean done = false;
 76             while (k < Kmax && !done) {
 77                 ColorBox nextBox = findBoxToSplit(colorSet);
 78                 if (nextBox != null) {
 79                     ColorBox newBox = nextBox.splitBox();
 80                     colorSet.add(newBox);
 81                     k = k + 1;
 82                 } else {
 83                     done = true;
 84                 }
 85             }
 86             rCols = averageColors(colorSet);
 87         }
 88         return rCols;
 89     }
 90 
 91     public void quantizeImage(int[] pixels) {
 92         for (int i = 0; i < pixels.length; i++) {
 93             ColorNode color = findClosestColor(pixels[i]);
 94             pixels[i] = Color.rgb(color.red, color.grn, color.blu);
 95         }
 96     }
 97 
 98     ColorNode findClosestColor(int rgb) {
 99         int idx = findClosestColorIndex(rgb);
100         return quantColors[idx];
101     }
102 
103     int findClosestColorIndex(int rgb) {
104         int red = Color.red(rgb);
105         int grn = Color.green(rgb);
106         int blu = Color.blue(rgb);
107         int minIdx = 0;
108         int minDistance = Integer.MAX_VALUE;
109         for (int i = 0; i < quantColors.length; i++) {
110             ColorNode color = quantColors[i];
111             int d2 = color.distance2(red, grn, blu);
112             if (d2 < minDistance) {
113                 minDistance = d2;
114                 minIdx = i;
115             }
116         }
117         return minIdx;
118     }
119 
120     private ColorBox findBoxToSplit(List<ColorBox> colorBoxes) {
121         ColorBox boxToSplit = null;
122         // from the set of splitable color boxes
123         // select the one with the minimum level
124         int minLevel = Integer.MAX_VALUE;
125         for (ColorBox box : colorBoxes) {
126             if (box.colorCount() >= 2) { // box can be split
127                 if (box.level < minLevel) {
128                     boxToSplit = box;
129                     minLevel = box.level;
130                 }
131             }
132         }
133         return boxToSplit;
134     }
135 
136     private ColorNode[] averageColors(List<ColorBox> colorBoxes) {
137         int n = colorBoxes.size();
138         ColorNode[] avgColors = new ColorNode[n];
139         int i = 0;
140         for (ColorBox box : colorBoxes) {
141             avgColors[i] = box.getAverageColor();
142             i = i + 1;
143         }
144         return avgColors;
145     }
146 
147     // -------------- class ColorNode
148     // -------------------------------------------
149 
150     public static class ColorNode {
151 
152         private final int red, grn, blu;
153         private final int cnt;
154 
155         private float[] hsv;
156 
157         ColorNode(int rgb, int cnt) {
158             this.red = Color.red(rgb);
159             this.grn = Color.green(rgb);
160             this.blu = Color.blue(rgb);
161             this.cnt = cnt;
162         }
163 
164         ColorNode(int red, int grn, int blu, int cnt) {
165             this.red = red;
166             this.grn = grn;
167             this.blu = blu;
168             this.cnt = cnt;
169         }
170 
171         public int getRgb() {
172             return Color.rgb(red, grn, blu);
173         }
174 
175         public float[] getHsv() {
176             if (hsv == null) {
177                 hsv = new float[3];
178                 Color.RGBToHSV(red, grn, blu, hsv);
179             }
180             return hsv;
181         }
182 
183         public int getCount() {
184             return cnt;
185         }
186 
187         int distance2(int red, int grn, int blu) {
188             // returns the squared distance between (red, grn, blu)
189             // and this this color
190             int dr = this.red - red;
191             int dg = this.grn - grn;
192             int db = this.blu - blu;
193             return dr * dr + dg * dg + db * db;
194         }
195 
196         public String toString() {
197             return new StringBuilder(getClass().getSimpleName()).append(" #")
198                     .append(Integer.toHexString(getRgb())).append(". count: ")
199                     .append(cnt).toString();
200         }
201     }
202 
203     // -------------- class ColorBox -------------------------------------------
204 
205     class ColorBox {
206 
207         int lower = 0; // lower index into 'imageColors'
208         int upper = -1; // upper index into 'imageColors'
209         int level; // split level o this color box
210         int count = 0; // number of pixels represented by thos color box
211         int rmin, rmax; // range of contained colors in red dimension
212         int gmin, gmax; // range of contained colors in green dimension
213         int bmin, bmax; // range of contained colors in blue dimension
214 
215         ColorBox(int lower, int upper, int level) {
216             this.lower = lower;
217             this.upper = upper;
218             this.level = level;
219             this.trim();
220         }
221 
222         int colorCount() {
223             return upper - lower;
224         }
225 
226         void trim() {
227             // recompute the boundaries of this color box
228             rmin = 255;
229             rmax = 0;
230             gmin = 255;
231             gmax = 0;
232             bmin = 255;
233             bmax = 0;
234             count = 0;
235             for (int i = lower; i <= upper; i++) {
236                 ColorNode color = imageColors[i];
237                 count = count + color.cnt;
238                 int r = color.red;
239                 int g = color.grn;
240                 int b = color.blu;
241                 if (r > rmax) {
242                     rmax = r;
243                 }
244                 if (r < rmin) {
245                     rmin = r;
246                 }
247                 if (g > gmax) {
248                     gmax = g;
249                 }
250                 if (g < gmin) {
251                     gmin = g;
252                 }
253                 if (b > bmax) {
254                     bmax = b;
255                 }
256                 if (b < bmin) {
257                     bmin = b;
258                 }
259             }
260         }
261 
262         // Split this color box at the median point along its
263         // longest color dimension
264         ColorBox splitBox() {
265             if (this.colorCount() < 2) // this box cannot be split
266             {
267                 return null;
268             } else {
269                 // find longest dimension of this box:
270                 ColorDimension dim = getLongestColorDimension();
271 
272                 // find median along dim
273                 int med = findMedian(dim);
274 
275                 // now split this box at the median return the resulting new
276                 // box.
277                 int nextLevel = level + 1;
278                 ColorBox newBox = new ColorBox(med + 1, upper, nextLevel);
279                 this.upper = med;
280                 this.level = nextLevel;
281                 this.trim();
282                 return newBox;
283             }
284         }
285 
286         // Find longest dimension of this color box (RED, GREEN, or BLUE)
287         ColorDimension getLongestColorDimension() {
288             int rLength = rmax - rmin;
289             int gLength = gmax - gmin;
290             int bLength = bmax - bmin;
291             if (bLength >= rLength && bLength >= gLength) {
292                 return ColorDimension.BLUE;
293             } else if (gLength >= rLength && gLength >= bLength) {
294                 return ColorDimension.GREEN;
295             } else {
296                 return ColorDimension.RED;
297             }
298         }
299 
300         // Find the position of the median in RGB space along
301         // the red, green or blue dimension, respectively.
302         int findMedian(ColorDimension dim) {
303             // sort color in this box along dimension dim:
304             Arrays.sort(imageColors, lower, upper + 1, dim.comparator);
305             // find the median point:
306             int half = count / 2;
307             int nPixels, median;
308             for (median = lower, nPixels = 0; median < upper; median++) {
309                 nPixels = nPixels + imageColors[median].cnt;
310                 if (nPixels >= half) {
311                     break;
312                 }
313             }
314             return median;
315         }
316 
317         ColorNode getAverageColor() {
318             int rSum = 0;
319             int gSum = 0;
320             int bSum = 0;
321             int n = 0;
322             for (int i = lower; i <= upper; i++) {
323                 ColorNode ci = imageColors[i];
324                 int cnt = ci.cnt;
325                 rSum = rSum + cnt * ci.red;
326                 gSum = gSum + cnt * ci.grn;
327                 bSum = bSum + cnt * ci.blu;
328                 n = n + cnt;
329             }
330             double nd = n;
331             int avgRed = (int) (0.5 + rSum / nd);
332             int avgGrn = (int) (0.5 + gSum / nd);
333             int avgBlu = (int) (0.5 + bSum / nd);
334             return new ColorNode(avgRed, avgGrn, avgBlu, n);
335         }
336 
337         public String toString() {
338             String s = this.getClass().getSimpleName();
339             s = s + " lower=" + lower + " upper=" + upper;
340             s = s + " count=" + count + " level=" + level;
341             s = s + " rmin=" + rmin + " rmax=" + rmax;
342             s = s + " gmin=" + gmin + " gmax=" + gmax;
343             s = s + " bmin=" + bmin + " bmax=" + bmax;
344             s = s + " bmin=" + bmin + " bmax=" + bmax;
345             return s;
346         }
347     }
348 
349     // --- color dimensions ------------------------
350 
351     // The main purpose of this enumeration class is associate
352     // the color dimensions with the corresponding comparators.
353     enum ColorDimension {
354         RED(new redComparator()), GREEN(new grnComparator()), BLUE(
355                 new bluComparator());
356 
357         public final Comparator<ColorNode> comparator;
358 
359         ColorDimension(Comparator<ColorNode> cmp) {
360             this.comparator = cmp;
361         }
362     }
363 
364     // --- color comparators used for sorting colors along different dimensions
365     // ---
366 
367     static class redComparator implements Comparator<ColorNode> {
368         public int compare(ColorNode colA, ColorNode colB) {
369             return colA.red - colB.red;
370         }
371     }
372 
373     static class grnComparator implements Comparator<ColorNode> {
374         public int compare(ColorNode colA, ColorNode colB) {
375             return colA.grn - colB.grn;
376         }
377     }
378 
379     static class bluComparator implements Comparator<ColorNode> {
380         public int compare(ColorNode colA, ColorNode colB) {
381             return colA.blu - colB.blu;
382         }
383     }
384 
385     // -------- utility methods -----------
386 
387     void listColorNodes(ColorNode[] nodes) {
388         int i = 0;
389         for (ColorNode color : nodes) {
390             Log.d(LOG_TAG, "Color Node #" + i + " " + color.toString());
391             i++;
392         }
393     }
394 
395     static class ColorHistogram {
396 
397         int colorArray[] = null;
398         int countArray[] = null;
399 
400         ColorHistogram(int[] color, int[] count) {
401             this.countArray = count;
402             this.colorArray = color;
403         }
404 
405         ColorHistogram(int[] pixelsOrig) {
406             int N = pixelsOrig.length;
407             int[] pixelsCpy = new int[N];
408             for (int i = 0; i < N; i++) {
409                 // remove possible alpha components
410                 pixelsCpy[i] = 0xFFFFFF & pixelsOrig[i];
411             }
412             Arrays.sort(pixelsCpy);
413 
414             // count unique colors:
415             int k = -1; // current color index
416             int curColor = -1;
417             for (int i = 0; i < pixelsCpy.length; i++) {
418                 if (pixelsCpy[i] != curColor) {
419                     k++;
420                     curColor = pixelsCpy[i];
421                 }
422             }
423             int nColors = k + 1;
424 
425             // tabulate and count unique colors:
426             colorArray = new int[nColors];
427             countArray = new int[nColors];
428             k = -1; // current color index
429             curColor = -1;
430             for (int i = 0; i < pixelsCpy.length; i++) {
431                 if (pixelsCpy[i] != curColor) { // new color
432                     k++;
433                     curColor = pixelsCpy[i];
434                     colorArray[k] = curColor;
435                     countArray[k] = 1;
436                 } else {
437                     countArray[k]++;
438                 }
439             }
440         }
441 
442         public int[] getColorArray() {
443             return colorArray;
444         }
445 
446         public int[] getCountArray() {
447             return countArray;
448         }
449 
450         public int getNumberOfColors() {
451             if (colorArray == null) {
452                 return 0;
453             } else {
454                 return colorArray.length;
455             }
456         }
457 
458         public int getColor(int index) {
459             return this.colorArray[index];
460         }
461 
462         public int getCount(int index) {
463             return this.countArray[index];
464         }
465     }
466 
467 } // class MedianCut



剩余的大家自行研究了,如果有研究出更详细的用法,记得联系黄老师哦!
QQ:811868948
E-Mail:halfmanhuang@gmail.com

原文地址:https://www.cnblogs.com/halfmanhuang/p/3848341.html