Merge changes I561ba44b,Icaebd98e,Ic0bd4afc,Ia0f98434 into sc-dev

* changes:
  Move Wu check for max colors earlier.
  Ignore transparent pixels during WSMeans
  Fix create colors in Wu quantizer
  Fix initializing clusters with random randoms
This commit is contained in:
Lucas Dupin
2021-03-11 23:15:44 +00:00
committed by Android (Google) Code Review
3 changed files with 33 additions and 14 deletions

View File

@@ -22,20 +22,19 @@ import java.util.Random;
* Represents a centroid in Kmeans algorithms.
*/
public class Mean {
private static final Random RANDOM = new Random(0);
public float[] center;
/**
* Constructor.
*
* @param upperBound maximum value of a dimension in the space Kmeans is optimizing in
* @param random used to generate a random center
*/
Mean(int upperBound) {
Mean(int upperBound, Random random) {
center =
new float[]{
RANDOM.nextInt(upperBound + 1), RANDOM.nextInt(upperBound + 1),
RANDOM.nextInt(upperBound + 1)
random.nextInt(upperBound + 1), random.nextInt(upperBound + 1),
random.nextInt(upperBound + 1)
};
}

View File

@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -57,13 +58,27 @@ public class WSMeansQuantizer implements Quantizer {
}
if (maxColors > means.length) {
// Always initialize Random with the same seed. Ensures the results of quantization
// are consistent, even when random centroids are required.
Random random = new Random(0x42688);
int randomMeansToCreate = maxColors - means.length;
for (int i = 0; i < randomMeansToCreate; i++) {
mMeans[means.length + i] = new Mean(100);
mMeans[means.length + i] = new Mean(100, random);
}
}
for (int pixel : pixels) {
// These are pixels from the bitmap that is being quantized.
// Depending on the bitmap & downscaling, it may have pixels that are less than opaque
// Ignore those pixels.
///
// Note: they don't _have_ to be ignored, for example, we could instead turn them
// opaque. Traditionally, including outside Android, quantizers ignore transparent
// pixels, so that strategy was chosen.
int alpha = (pixel >> 24);
if (alpha < 255) {
continue;
}
Integer currentCount = mCountByColor.get(pixel);
if (currentCount == null) {
currentCount = 0;
@@ -105,8 +120,12 @@ public class WSMeansQuantizer implements Quantizer {
/** Create random starting centroids for K-means. */
public static float[][] randomMeans(int maxColors, int upperBound) {
float[][] means = new float[maxColors][];
// Always initialize Random with the same seed. Ensures the results of quantization
// are consistent, even when random centroids are required.
Random random = new Random(0x42688);
for (int i = 0; i < maxColors; i++) {
means[i] = new Mean(upperBound).center;
means[i] = new Mean(upperBound, random).center;
}
return means;
}

View File

@@ -78,7 +78,12 @@ public class WuQuantizer implements Quantizer {
// All of the sample Wu implementations are reimplementations of a snippet of C code from
// the early 90s. They all cap the maximum # of colors at 256, and it is impossible to tell
// if this is a requirement, a consequence of QUANT_SIZE, or arbitrary.
this.mMaxColors = Math.min(MAX_COLORS, maxColorCount);
//
// Also, the number of maximum colors should be capped at the number of pixels - otherwise,
// If extraction is run on a set of pixels whose count is less than max colors,
// then colors.length < max colors, and accesses to colors[index] throw an
// ArrayOutOfBoundsException.
this.mMaxColors = Math.min(Math.min(MAX_COLORS, maxColorCount), colors.length);
Box[] cube = new Box[mMaxColors];
int red, green, blue;
@@ -119,17 +124,13 @@ public class WuQuantizer implements Quantizer {
}
}
// If extraction is run on a set of pixels whose count is less than the
// number of max colors, then colors.length < max colors, and accesses
// to colors[index] inside the for loop throw an ArrayOutOfBoundsException.
int numColorsToCreate = (int) Math.min(mMaxColors, colors.length);
for (k = 0; k < numColorsToCreate; ++k) {
for (k = 0; k < mMaxColors; ++k) {
weight = getVolume(cube[k], mWt);
if (weight > 0) {
red = (int) (getVolume(cube[k], mMr) / weight);
green = (int) (getVolume(cube[k], mMg) / weight);
blue = (int) (getVolume(cube[k], mMb) / weight);
colors[k] = ((red & 0x0ff) << 16) | ((green & 0x0ff) << 8) | (blue & 0x0ff);
colors[k] = (255 << 24) | (red << 16) | (green << 8) | blue;
} else {
colors[k] = 0;
}