diff --git a/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java b/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java index 16562b64f6f06c7ced5a4b3207b7a584a55d94ee..7c01f3c2bb3e24007b3f7fe04235fd4926723d50 100644 --- a/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java +++ b/src/main/java/azgracompress/benchmark/VectorQuantizationBenchmark.java @@ -93,9 +93,7 @@ public class VectorQuantizationBenchmark extends BenchmarkBase { for (final int planeIndex : planes) { System.out.println(String.format("Loading plane %d ...", planeIndex)); - // NOTE(Moravec): Actual planeIndex is zero based. - - final ImageU16 plane = loadPlane(planeIndex - 1); + final ImageU16 plane = loadPlane(planeIndex); if (plane == null) { System.err.println(String.format("Failed to load plane %d data. Skipping plane.", planeIndex)); diff --git a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java index 98dd5aba4aa13bdeb96f8bc3b6c4e51200131592..6ccf86eabe1bfb1321be9bda8af44b48e30f4cde 100644 --- a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java +++ b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java @@ -26,7 +26,7 @@ public class LBGVectorQuantizer { this.trainingVectors = new TrainingVector[vectors.length]; for (int i = 0; i < vectors.length; i++) { - trainingVectors[i] = new TrainingVector(vectors[i]); + trainingVectors[i] = new TrainingVector(Arrays.copyOf(vectors[i], vectors[i].length)); } this.vectorSize = vectors[0].length; @@ -245,7 +245,7 @@ public class LBGVectorQuantizer { } // We always want to carry zero vector to next iteration. - if (isZeroVector(entryToSplit.getVector())) { + if (VectorQuantizer.isZeroVector(entryToSplit.getVector())) { // Use zero vector in next iteration. newCodebook[cbIndex++] = entryToSplit; @@ -261,7 +261,7 @@ public class LBGVectorQuantizer { continue; } - if (isZeroVector(prtV)) { + if (VectorQuantizer.isZeroVector(prtV)) { // Zero perturbation vector can't create two different entries. // The original entry is going to be moved to the next codebook with the new // random entry, which will get improved in the LBG algorithm. @@ -321,36 +321,6 @@ public class LBGVectorQuantizer { return randomVector; } - /** - * Check whether all vector elements are equal to 0.0 - * - * @param vector Vector array. - * @return True if all elements are zeros. - */ - private boolean isZeroVector(final double[] vector) { - for (final double value : vector) { - if (value != 0.0) { - return false; - } - } - return true; - } - - /** - * Check whether all vector elements are equal to 0 - * - * @param vector Vector array. - * @return True if all elements are zeros. - */ - private boolean isZeroVector(final int[] vector) { - for (final double value : vector) { - if (value != 0.0) { - return false; - } - } - return true; - } - /** * Execute the LBG algorithm with default epsilon value. @@ -657,7 +627,9 @@ public class LBGVectorQuantizer { int largestEntrySize = codebook[emptyEntryIndex].getVectorCount(); // NOTE(Moravec): We can't select random training vector, because zero vector would create another zero vector. for (int i = 0; i < codebook.length; i++) { - if ((codebook[i].getVectorCount() > largestEntrySize) && !isZeroVector(codebook[i].getVector())) { + if ((codebook[i].getVectorCount() > largestEntrySize) && + !VectorQuantizer.isZeroVector(codebook[i].getVector())) { + largestEntryIndex = i; largestEntrySize = codebook[i].getVectorCount(); } diff --git a/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java index 22ad150878129f640cdb7f9d1e4ba168d559472d..44318aa45c6055da8a5368cbbbe85c34159ae3ea 100644 --- a/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java +++ b/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java @@ -2,6 +2,7 @@ package azgracompress.quantization.vector; public class VectorQuantizer { + private final VectorDistanceMetric metric = VectorDistanceMetric.Euclidean; private final CodebookEntry[] codebook; private final int vectorSize; @@ -12,7 +13,7 @@ public class VectorQuantizer { public int[] quantize(final int[] dataVector) { assert (dataVector.length > 0 && dataVector.length % vectorSize == 0) : "Wrong vector size"; - final CodebookEntry closestEntry = findClosestCodebookEntry(dataVector, VectorDistanceMetric.Euclidean); + final CodebookEntry closestEntry = findClosestCodebookEntry(dataVector, metric); return closestEntry.getVector(); } @@ -22,8 +23,7 @@ public class VectorQuantizer { if (workerCount == 1) { for (int vectorIndex = 0; vectorIndex < dataVectors.length; vectorIndex++) { - final CodebookEntry closestEntry = findClosestCodebookEntry(dataVectors[vectorIndex], - VectorDistanceMetric.Euclidean); + final CodebookEntry closestEntry = findClosestCodebookEntry(dataVectors[vectorIndex], metric); result[vectorIndex] = closestEntry.getVector(); } } else { @@ -48,8 +48,7 @@ public class VectorQuantizer { if (maxWorkerCount == 1) { for (int vectorIndex = 0; vectorIndex < dataVectors.length; vectorIndex++) { - indices[vectorIndex] = findClosestCodebookEntryIndex(dataVectors[vectorIndex], - VectorDistanceMetric.Euclidean); + indices[vectorIndex] = findClosestCodebookEntryIndex(dataVectors[vectorIndex], metric); } } else { // Cap the worker count on 8 @@ -63,8 +62,7 @@ public class VectorQuantizer { workers[wId] = new Thread(() -> { for (int vectorIndex = fromIndex; vectorIndex < toIndex; vectorIndex++) { - indices[vectorIndex] = findClosestCodebookEntryIndex(dataVectors[vectorIndex], - VectorDistanceMetric.Euclidean); + indices[vectorIndex] = findClosestCodebookEntryIndex(dataVectors[vectorIndex], metric); } }); @@ -117,7 +115,7 @@ public class VectorQuantizer { } private CodebookEntry findClosestCodebookEntry(final int[] dataVector) { - return findClosestCodebookEntry(dataVector, VectorDistanceMetric.Euclidean); + return findClosestCodebookEntry(dataVector, metric); } private CodebookEntry findClosestCodebookEntry(final int[] dataVector, final VectorDistanceMetric metric) { @@ -125,21 +123,58 @@ public class VectorQuantizer { } private int findClosestCodebookEntryIndex(final int[] dataVector, final VectorDistanceMetric metric) { + boolean closesIsZero = false; double minDist = Double.MAX_VALUE; int closestEntryIndex = 0; - final int codebookSize = codebook.length; - for (int i = 0; i < codebookSize; i++) { - final double dist = distanceBetweenVectors(dataVector, codebook[i].getVector(), metric); + for (int entryIndex = 0; entryIndex < codebook.length; entryIndex++) { + + + final double dist = distanceBetweenVectors(dataVector, codebook[entryIndex].getVector(), metric); if (dist < minDist) { minDist = dist; - closestEntryIndex = i; + closestEntryIndex = entryIndex; + closesIsZero = isZeroVector(codebook[entryIndex].getVector()); } } + + if (closesIsZero) { +// System.out.println("One of zero vectors."); + } return closestEntryIndex; } public CodebookEntry[] getCodebook() { return codebook; } + + /** + * Check whether all vector elements are equal to 0.0 + * + * @param vector Vector array. + * @return True if all elements are zeros. + */ + public static boolean isZeroVector(final double[] vector) { + for (final double value : vector) { + if (value != 0.0) { + return false; + } + } + return true; + } + + /** + * Check whether all vector elements are equal to 0 + * + * @param vector Vector array. + * @return True if all elements are zeros. + */ + public static boolean isZeroVector(final int[] vector) { + for (final double value : vector) { + if (value != 0.0) { + return false; + } + } + return true; + } }