Bug 867249 - Improve getDominantColor algorithm to return more accurate colors. r=bnicholson
authorMargaret Leibovic <margaret.leibovic@gmail.com>
Thu, 02 May 2013 11:53:13 -0700
changeset 130640 b036d96c4c87c6c2c718c336431ea258594f4dd3
parent 130639 ebad4b72a6ea110261c5baa265e64d2e1efc5050
child 130641 27f8c1c2e09e9e0bb77c59fcf211efbb9df037cf
push id27468
push usermleibovic@mozilla.com
push dateThu, 02 May 2013 18:54:30 +0000
treeherdermozilla-inbound@b036d96c4c87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbnicholson
bugs867249
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 867249 - Improve getDominantColor algorithm to return more accurate colors. r=bnicholson
mobile/android/base/gfx/BitmapUtils.java
--- a/mobile/android/base/gfx/BitmapUtils.java
+++ b/mobile/android/base/gfx/BitmapUtils.java
@@ -50,58 +50,73 @@ public final class BitmapUtils {
         return bitmap;
     }
 
     public static int getDominantColor(Bitmap source) {
         return getDominantColor(source, true);
     }
 
     public static int getDominantColor(Bitmap source, boolean applyThreshold) {
-      int[] colors = new int[37];
-      int[] sat = new int[11];
-      int[] val = new int[11];
-      int maxH = 0;
-      int maxS = 0;
-      int maxV = 0;
       if (source == null)
         return Color.argb(255,255,255,255);
 
+      // Keep track of how many times a hue in a given bin appears in the image.
+      // Hue values range [0 .. 360), so dividing by 10, we get 36 bins.
+      int[] colorBins = new int[36];
+
+      // The bin with the most colors. Initialize to -1 to prevent accidentally
+      // thinking the first bin holds the dominant color.
+      int maxBin = -1;
+
+      // Keep track of sum hue/saturation/value per hue bin, which we'll use to
+      // compute an average to for the dominant color.
+      float[] sumHue = new float[36];
+      float[] sumSat = new float[36];
+      float[] sumVal = new float[36];
+
       for (int row = 0; row < source.getHeight(); row++) {
         for (int col = 0; col < source.getWidth(); col++) {
           int c = source.getPixel(col, row);
+          // Ignore pixels with a certain transparency.
           if (Color.alpha(c) < 128)
             continue;
 
           float[] hsv = new float[3];
           Color.colorToHSV(c, hsv);
 
-          // arbitrarily chosen values for "white" and "black"
-          if (applyThreshold && hsv[1] <= 0.35f && hsv[2] <= 0.35f)
+          // If a threshold is applied, ignore arbitrarily chosen values for "white" and "black".
+          if (applyThreshold && (hsv[1] <= 0.35f || hsv[2] <= 0.35f))
             continue;
 
-          int h = Math.round(hsv[0] / 10.0f);
-          int s = Math.round(hsv[1] * 10.0f);
-          int v = Math.round(hsv[2] * 10.0f);
-          colors[h]++;
-          sat[s]++;
-          val[v]++;
+          // We compute the dominant color by putting colors in bins based on their hue.
+          int bin = (int) Math.floor(hsv[0] / 10.0f);
 
-          // we only care about the most unique non white or black hue - if threshold is applied
-          // we also store its saturation and value params to match the color better
-          if (colors[h] > colors[maxH]) {
-            maxH = h;
-            maxS = s;
-            maxV = v;
-          }
+          // Update the sum hue/saturation/value for this bin.
+          sumHue[bin] = sumHue[bin] + hsv[0];
+          sumSat[bin] = sumSat[bin] + hsv[1];
+          sumVal[bin] = sumVal[bin] + hsv[2];
+
+          // Increment the number of colors in this bin.
+          colorBins[bin]++;
+
+          // Keep track of the bin that holds the most colors.
+          if (maxBin < 0 || colorBins[bin] > colorBins[maxBin])
+            maxBin = bin;
         }
       }
+
+      // maxBin may never get updated if the image holds only transparent and/or black/white pixels.
+      if (maxBin < 0)
+        return Color.argb(255,255,255,255);
+
+      // Return a color with the average hue/saturation/value of the bin with the most colors.
       float[] hsv = new float[3];
-      hsv[0] = maxH*10.0f;
-      hsv[1] = (float)maxS/10.0f;
-      hsv[2] = (float)maxV/10.0f;
+      hsv[0] = sumHue[maxBin]/colorBins[maxBin];
+      hsv[1] = sumSat[maxBin]/colorBins[maxBin];
+      hsv[2] = sumVal[maxBin]/colorBins[maxBin];
       return Color.HSVToColor(hsv);
     }
 
     /**
      * Decodes a bitmap from a Base64 data URI.
      *
      * @param dataURI a Base64-encoded data URI string
      * @return        the decoded bitmap, or null if the data URI is invalid