From 2a75b45bff598ca0e5df868fb2474365f2c883b4 Mon Sep 17 00:00:00 2001 From: James O'Leary Date: Thu, 11 Mar 2021 11:27:38 -0500 Subject: [PATCH] Fix initializing clusters with random randoms During the quantization process, an instance of a Quantizer is created. When quantization is run, the Quantizer may need to create starting points that are random. To keep results stable, meaning multiple runs of a quantizer output the same results, those starting points must be created using a Random with the same starting seed. Without specifying a starting seed, or if an instance of Random is reused across runs, the random starting points will vary across quantizer runs. With this patch, the Random used to create starting points is no longer used across multiple runs (i.e. it isn't a static variable anymore). Additionally, a starting seed is specified. Test: Add log dumps after the quantization process. Verify that results from Kmeans remain consistent as long as the input is consistent. Bug: 182333325 Change-Id: Ia0f984349b7885ae1b44536aaa1b286a28e50587 --- .../com/android/internal/graphics/palette/Mean.java | 9 ++++----- .../internal/graphics/palette/WSMeansQuantizer.java | 12 ++++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/core/java/com/android/internal/graphics/palette/Mean.java b/core/java/com/android/internal/graphics/palette/Mean.java index 894f91b6261c1..bde036349d3b9 100644 --- a/core/java/com/android/internal/graphics/palette/Mean.java +++ b/core/java/com/android/internal/graphics/palette/Mean.java @@ -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) }; } diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java index a87a34f4ae110..b7c03d6a0b0ab 100644 --- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java @@ -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,9 +58,12 @@ 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); } } @@ -105,8 +109,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; }