From baeb1853ac039f1f6608f963334cf516d24db040 Mon Sep 17 00:00:00 2001
From: Vojtech Moravec <vojtech.moravec.st@vsb.cz>
Date: Tue, 8 Sep 2020 09:10:06 +0200
Subject: [PATCH] Remove CodebookEntry class, instead use raw vector.

This simplify the future work, where we need to access raw codebook
vector.
---
 .../java/azgracompress/cache/VQCacheFile.java | 25 ++++----
 .../compression/VQImageCompressor.java        |  9 ++-
 .../compression/VQImageDecompressor.java      | 12 ++--
 .../quantization/vector/CodebookEntry.java    | 58 -------------------
 .../quantization/vector/LBGResult.java        |  4 +-
 .../vector/LBGVectorQuantizer.java            | 31 +++++-----
 .../vector/LearningCodebookEntry.java         | 17 ++++--
 .../quantization/vector/VQCodebook.java       | 16 +----
 .../quantization/vector/VectorQuantizer.java  | 22 ++++---
 9 files changed, 63 insertions(+), 131 deletions(-)
 delete mode 100644 src/main/java/azgracompress/quantization/vector/CodebookEntry.java

diff --git a/src/main/java/azgracompress/cache/VQCacheFile.java b/src/main/java/azgracompress/cache/VQCacheFile.java
index 3710810..986a22a 100644
--- a/src/main/java/azgracompress/cache/VQCacheFile.java
+++ b/src/main/java/azgracompress/cache/VQCacheFile.java
@@ -1,6 +1,5 @@
 package azgracompress.cache;
 
-import azgracompress.quantization.vector.CodebookEntry;
 import azgracompress.quantization.vector.VQCodebook;
 
 import java.io.DataInputStream;
@@ -23,9 +22,9 @@ public class VQCacheFile implements ICacheFile {
     public void writeToStream(DataOutputStream outputStream) throws IOException {
         header.writeToStream(outputStream);
 
-        final CodebookEntry[] entries = codebook.getVectors();
-        for (final CodebookEntry entry : entries) {
-            for (final int vectorValue : entry.getVector()) {
+        final int[][] entries = codebook.getVectors();
+        for (final int[] entry : entries) {
+            for (final int vectorValue : entry) {
                 outputStream.writeShort(vectorValue);
             }
         }
@@ -45,16 +44,16 @@ public class VQCacheFile implements ICacheFile {
     @Override
     public void readFromStream(DataInputStream inputStream, CacheFileHeader header) throws IOException {
         final int codebookSize = header.getCodebookSize();
-        final CodebookEntry[] vectors = new CodebookEntry[codebookSize];
-        final long[] frequencies = new long[codebookSize];
 
         final int entrySize = header.getVectorSizeX() * header.getVectorSizeY() * header.getVectorSizeZ();
+        final int[][] vectors = new int[codebookSize][entrySize];
+        final long[] frequencies = new long[codebookSize];
+
         for (int i = 0; i < codebookSize; i++) {
-            int[] vector = new int[entrySize];
+            //int[] vector = new int[entrySize];
             for (int j = 0; j < entrySize; j++) {
-                vector[j] = inputStream.readUnsignedShort();
+                vectors[i][j] = inputStream.readUnsignedShort();
             }
-            vectors[i] = new CodebookEntry(vector);
         }
 
         for (int i = 0; i < codebookSize; i++) {
@@ -73,10 +72,12 @@ public class VQCacheFile implements ICacheFile {
 
     @Override
     public void report(StringBuilder builder) {
-        final CodebookEntry[] vectors = codebook.getVectors();
-        for (int i = 0; i < vectors.length; i++) {
+        final int[][] vectors = codebook.getVectors();
+        for (int[] vector : vectors) {
             builder.append("- - - - - - - - - - - - - - - - - - - - - - - - -\n");
-            vectors[i].getVectorString(builder);
+            for (final int x : vector) {
+                builder.append(x).append(';');
+            }
         }
     }
 }
diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java
index b32a93d..670a932 100644
--- a/src/main/java/azgracompress/compression/VQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/VQImageCompressor.java
@@ -47,11 +47,10 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
      */
     private void writeQuantizerToCompressStream(final VectorQuantizer quantizer,
                                                 DataOutputStream compressStream) throws ImageCompressionException {
-        final CodebookEntry[] codebook = quantizer.getCodebookVectors();
+        final int[][] codebook = quantizer.getCodebookVectors();
         try {
-            for (final CodebookEntry entry : codebook) {
-                final int[] entryVector = entry.getVector();
-                for (final int vecVal : entryVector) {
+            for (final int[] entry : codebook) {
+                for (final int vecVal : entry) {
                     compressStream.writeShort(vecVal);
                 }
             }
@@ -160,7 +159,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
             assert (quantizer != null);
 
             // Use BestBinFirst KDTree for codebook lookup.
-//             final int[] indices = quantizer.quantizeIntoIndicesUsingKDTree(planeVectors, options.getWorkerCount());
+            //             final int[] indices = quantizer.quantizeIntoIndicesUsingKDTree(planeVectors, options.getWorkerCount());
             // Use BruteForce for codebook lookup.
             final int[] indices = quantizer.quantizeIntoIndices(planeVectors, options.getWorkerCount());
 
diff --git a/src/main/java/azgracompress/compression/VQImageDecompressor.java b/src/main/java/azgracompress/compression/VQImageDecompressor.java
index 590fd41..383dea0 100644
--- a/src/main/java/azgracompress/compression/VQImageDecompressor.java
+++ b/src/main/java/azgracompress/compression/VQImageDecompressor.java
@@ -7,7 +7,6 @@ import azgracompress.fileformat.QuantizationType;
 import azgracompress.huffman.Huffman;
 import azgracompress.huffman.HuffmanNode;
 import azgracompress.io.InBitStream;
-import azgracompress.quantization.vector.CodebookEntry;
 import azgracompress.quantization.vector.VQCodebook;
 import azgracompress.utilities.Stopwatch;
 import azgracompress.utilities.TypeConverter;
@@ -48,15 +47,14 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                                     final int codebookSize,
                                     final int vectorSize) throws ImageDecompressionException {
 
-        final CodebookEntry[] codebookVectors = new CodebookEntry[codebookSize];
+        final int[][] codebookVectors = new int[codebookSize][vectorSize];
         final long[] frequencies = new long[codebookSize];
         try {
             for (int codebookIndex = 0; codebookIndex < codebookSize; codebookIndex++) {
-                final int[] vector = new int[vectorSize];
+                //                final int[] vector = new int[vectorSize];
                 for (int vecIndex = 0; vecIndex < vectorSize; vecIndex++) {
-                    vector[vecIndex] = compressedStream.readUnsignedShort();
+                    codebookVectors[codebookIndex][vecIndex] = compressedStream.readUnsignedShort();
                 }
-                codebookVectors[codebookIndex] = new CodebookEntry(vector);
             }
             for (int codebookIndex = 0; codebookIndex < codebookSize; codebookIndex++) {
                 frequencies[codebookIndex] = compressedStream.readLong();
@@ -166,7 +164,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                         bit = inBitStream.readBit();
                         currentHuffmanNode = currentHuffmanNode.traverse(bit);
                     }
-                    System.arraycopy(codebook.getVectors()[currentHuffmanNode.getSymbol()].getVector(),
+                    System.arraycopy(codebook.getVectors()[currentHuffmanNode.getSymbol()],
                                      0, decompressedVectors[vecIndex], 0, vectorSize);
                 }
 
@@ -239,7 +237,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
 
                 for (int voxelIndex = 0; voxelIndex < voxelLayerVoxelCount; voxelIndex++) {
                     final int huffmanSymbol = decodeHuffmanSymbol(huffman, inBitStream);
-                    System.arraycopy(codebook.getVectors()[huffmanSymbol].getVector(), 0, decompressedVoxels[voxelIndex], 0, vectorSize);
+                    System.arraycopy(codebook.getVectors()[huffmanSymbol], 0, decompressedVoxels[voxelIndex], 0, vectorSize);
                 }
 
             } catch (Exception e) {
diff --git a/src/main/java/azgracompress/quantization/vector/CodebookEntry.java b/src/main/java/azgracompress/quantization/vector/CodebookEntry.java
deleted file mode 100644
index 72cf864..0000000
--- a/src/main/java/azgracompress/quantization/vector/CodebookEntry.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package azgracompress.quantization.vector;
-
-public class CodebookEntry {
-
-    final int[] vector;
-    final int width;
-    final int height;
-
-    public CodebookEntry(final int[] codebook) {
-
-        this.vector = codebook;
-        this.width = codebook.length;
-        this.height = 1;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (obj instanceof CodebookEntry) {
-            final CodebookEntry ceObj = (CodebookEntry) obj;
-            if (vector.length != ceObj.vector.length) {
-                return false;
-            }
-            for (int i = 0; i < vector.length; i++) {
-                if (vector[i] != ceObj.vector[i]) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        return super.equals(obj);
-    }
-
-    public int getWidth() {
-        return width;
-    }
-
-    public int getHeight() {
-        return height;
-    }
-
-    public int[] getVector() {
-        return vector;
-    }
-
-    public String getVectorString(StringBuilder sb) {
-        for (int i = 0; i < vector.length; i++) {
-            sb.append(vector[i]);
-            if (i != (vector.length - 1))
-                sb.append(';');
-        }
-        sb.append('\n');
-        return sb.toString();
-    }
-
-    public String getVectorString() {
-        return getVectorString(new StringBuilder());
-    }
-}
diff --git a/src/main/java/azgracompress/quantization/vector/LBGResult.java b/src/main/java/azgracompress/quantization/vector/LBGResult.java
index da4a27c..4b72a79 100644
--- a/src/main/java/azgracompress/quantization/vector/LBGResult.java
+++ b/src/main/java/azgracompress/quantization/vector/LBGResult.java
@@ -4,14 +4,14 @@ import azgracompress.data.V3i;
 
 public class LBGResult {
 
-    private final CodebookEntry[] codebookVectors;
+    private final int[][] codebookVectors;
     private final long[] frequencies;
     private final double averageMse;
     private final double psnr;
     private final V3i vectorDims;
 
     public LBGResult(final V3i vectorDims,
-                     final CodebookEntry[] codebook,
+                     final int[][] codebook,
                      final long[] frequencies,
                      final double averageMse,
                      final double psnr) {
diff --git a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java
index b431e3f..0ad49cf 100644
--- a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java
+++ b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java
@@ -92,13 +92,12 @@ public class LBGVectorQuantizer {
         assert (uniqueTrainingVectors != null) : "uniqueTrainingVectors aren't initialized.";
         reportStatus("There is only %d unique vectors. Creating codebook from unique vectors...",
                      uniqueTrainingVectors.size());
-        CodebookEntry[] codebook = new CodebookEntry[codebookSize];
-        int[] zeros = new int[vectorSize];
-        Arrays.fill(zeros, 0);
-        CodebookEntry zeroEntry = new CodebookEntry(zeros);
+        final int[][] codebook = new int[codebookSize][vectorSize];
+        int[] zeroEntry = new int[vectorSize];
+        Arrays.fill(zeroEntry, 0);
         for (int i = 0; i < codebookSize; i++) {
             if (i < uniqueVectorCount) {
-                codebook[i] = new CodebookEntry(uniqueTrainingVectors.get(i).getVector());
+                codebook[i] = uniqueTrainingVectors.get(i).getVector();
             } else {
                 codebook[i] = zeroEntry;
             }
@@ -141,10 +140,10 @@ public class LBGVectorQuantizer {
      * @param learningCodebook Source array of LearningCodebookEntry.
      * @return Array of CodebookEntries.
      */
-    private CodebookEntry[] learningCodebookToCodebook(final LearningCodebookEntry[] learningCodebook) {
-        CodebookEntry[] codebook = new CodebookEntry[learningCodebook.length];
+    private int[][] learningCodebookToCodebook(final LearningCodebookEntry[] learningCodebook) {
+        final int[][] codebook = new int[learningCodebook.length][vectorSize];
         for (int i = 0; i < codebook.length; i++) {
-            codebook[i] = new CodebookEntry(learningCodebook[i].getVector());
+            codebook[i] = learningCodebook[i].getVector();
         }
         return codebook;
     }
@@ -168,14 +167,17 @@ public class LBGVectorQuantizer {
         }
     }
 
+    private double averageMse(final LearningCodebookEntry[] codebook) {
+        return averageMse(learningCodebookToCodebook(codebook));
+    }
+
     /**
      * Calculate the average mean square error of the codebook.
      *
      * @param codebook Codebook of vectors.
      * @return Mean square error.
      */
-    private double averageMse(final CodebookEntry[] codebook) {
-        Stopwatch s = Stopwatch.startNew("averageMse");
+    private double averageMse(final int[][] codebook) {
         double mse = 0.0;
         resetFrequencies();
         if (workerCount > 1) {
@@ -202,7 +204,8 @@ public class LBGVectorQuantizer {
                         qIndex = quantizer.quantizeToIndex(vector);
                         ++workerFrequencies[qIndex];
 
-                        qVector = quantizer.getCodebookVectors()[qIndex].getVector();
+
+                        qVector = quantizer.getCodebookVectors()[qIndex];
                         for (int vI = 0; vI < vectorSize; vI++) {
                             threadMse += Math.pow(((double) vector[vI] - (double) qVector[vI]), 2);
                         }
@@ -233,7 +236,7 @@ public class LBGVectorQuantizer {
             int[] qVector;
             for (final TrainingVector trV : trainingVectors) {
                 qIndex = quantizer.quantizeToIndex(trV.getVector());
-                qVector = quantizer.getCodebookVectors()[qIndex].getVector();
+                qVector = quantizer.getCodebookVectors()[qIndex];
                 ++frequencies[qIndex];
                 for (int i = 0; i < vectorSize; i++) {
                     mse += Math.pow(((double) trV.getVector()[i] - (double) qVector[i]), 2);
@@ -241,10 +244,6 @@ public class LBGVectorQuantizer {
             }
             mse /= (double) trainingVectors.length;
         }
-        s.stop();
-        //        if (this.verbose) {
-        ////            System.out.println(s);
-        //        }
         return mse;
     }
 
diff --git a/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java b/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java
index 30cbee3..4492e4c 100644
--- a/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java
+++ b/src/main/java/azgracompress/quantization/vector/LearningCodebookEntry.java
@@ -1,31 +1,36 @@
 package azgracompress.quantization.vector;
 
-public class LearningCodebookEntry extends CodebookEntry {
+public class LearningCodebookEntry {
 
+    private final int[] codebookVector;
     private int vectorCount = -1;
     private double averageDistortion = -1.0f;
     private double[] perturbationVector;
 
-    public LearningCodebookEntry(int[] codebook) {
-        super(codebook);
+    public LearningCodebookEntry(int[] codebookVector) {
+        this.codebookVector = codebookVector;
     }
 
     /**
      * Set codebook entry properties from helper object.
      *
-     * @param info Helper object with property informations.
+     * @param info Helper object with property information.
      */
     public void setInfo(final EntryInfo info) {
         this.vectorCount = info.vectorCount;
         this.averageDistortion = info.calculateAverageDistortion();
 
         final int[] newCentroid = info.calculateCentroid();
-        assert (newCentroid.length == vector.length);
-        System.arraycopy(newCentroid, 0, this.vector, 0, newCentroid.length);
+        assert (newCentroid.length == codebookVector.length);
+        System.arraycopy(newCentroid, 0, this.codebookVector, 0, newCentroid.length);
 
         this.perturbationVector = info.calculatePRTVector();
     }
 
+    public int[] getVector() {
+        return codebookVector;
+    }
+
     /**
      * Get perturbation vector for splitting this entry.
      *
diff --git a/src/main/java/azgracompress/quantization/vector/VQCodebook.java b/src/main/java/azgracompress/quantization/vector/VQCodebook.java
index 1afb8d2..d56fc6b 100644
--- a/src/main/java/azgracompress/quantization/vector/VQCodebook.java
+++ b/src/main/java/azgracompress/quantization/vector/VQCodebook.java
@@ -9,7 +9,7 @@ public class VQCodebook {
     /**
      * Quantization vectors.
      */
-    private final CodebookEntry[] vectors;
+    private final int[][] vectors;
 
     /**
      * Absolute frequencies of quantization vectors.
@@ -26,7 +26,7 @@ public class VQCodebook {
      */
     private final V3i vectorDims;
 
-    public VQCodebook(final V3i vectorDims, final CodebookEntry[] vectors, final long[] vectorFrequencies) {
+    public VQCodebook(final V3i vectorDims, final int[][] vectors, final long[] vectorFrequencies) {
         //assert (vectors.length == vectorFrequencies.length);
         this.vectorDims = vectorDims;
         this.vectors = vectors;
@@ -39,20 +39,10 @@ public class VQCodebook {
      *
      * @return Quantization vectors.
      */
-    public CodebookEntry[] getVectors() {
+    public int[][] getVectors() {
         return vectors;
     }
 
-    public int[][] getRawVectors() {
-        assert (codebookSize == vectors.length);
-        assert (vectors[0].getVector().length == (int) vectorDims.multiplyTogether());
-        final int[][] rawCodebook = new int[vectors.length][(int) vectorDims.multiplyTogether()];
-        for (int i = 0; i < codebookSize; i++) {
-            rawCodebook[i] = vectors[i].getVector();
-        }
-        return rawCodebook;
-    }
-
     /**
      * Get frequencies of codebook vectors at indices.
      *
diff --git a/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java
index 4fcc8ca..efca6eb 100644
--- a/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java
+++ b/src/main/java/azgracompress/quantization/vector/VectorQuantizer.java
@@ -11,7 +11,7 @@ public class VectorQuantizer {
     }
 
     private final VectorDistanceMetric metric = VectorDistanceMetric.Euclidean;
-    private final CodebookEntry[] codebookVectors;
+    private final int[][] codebookVectors;
     private final int vectorSize;
     private final long[] frequencies;
 
@@ -19,16 +19,15 @@ public class VectorQuantizer {
 
     public VectorQuantizer(final VQCodebook codebook) {
         this.codebookVectors = codebook.getVectors();
-        this.vectorSize = codebookVectors[0].getVector().length;
+        this.vectorSize = codebook.getVectors()[0].length;
         this.frequencies = codebook.getVectorFrequencies();
 
-        kdTree = new KDTreeBuilder(this.vectorSize, 8).buildTree(codebook.getRawVectors());
+        kdTree = new KDTreeBuilder(this.vectorSize, 8).buildTree(codebook.getVectors());
     }
 
     public int[] quantize(final int[] dataVector) {
         assert (dataVector.length > 0 && dataVector.length % vectorSize == 0) : "Wrong vector size";
-        final CodebookEntry closestEntry = findClosestCodebookEntry(dataVector, metric);
-        return closestEntry.getVector();
+        return findClosestCodebookEntry(dataVector, metric);
     }
 
     public int quantizeToIndex(final int[] dataVector) {
@@ -42,13 +41,12 @@ public class VectorQuantizer {
 
         if (workerCount == 1) {
             for (int vectorIndex = 0; vectorIndex < dataVectors.length; vectorIndex++) {
-                final CodebookEntry closestEntry = findClosestCodebookEntry(dataVectors[vectorIndex], metric);
-                result[vectorIndex] = closestEntry.getVector();
+                result[vectorIndex] = findClosestCodebookEntry(dataVectors[vectorIndex], metric);
             }
         } else {
             final int[] indices = quantizeIntoIndices(dataVectors, workerCount);
             for (int i = 0; i < dataVectors.length; i++) {
-                result[i] = codebookVectors[indices[i]].getVector();
+                result[i] = codebookVectors[indices[i]];
             }
         }
 
@@ -142,11 +140,11 @@ public class VectorQuantizer {
         return 0.0;
     }
 
-    private CodebookEntry findClosestCodebookEntry(final int[] dataVector) {
+    private int[] findClosestCodebookEntry(final int[] dataVector) {
         return findClosestCodebookEntry(dataVector, metric);
     }
 
-    private CodebookEntry findClosestCodebookEntry(final int[] dataVector, final VectorDistanceMetric metric) {
+    private int[] findClosestCodebookEntry(final int[] dataVector, final VectorDistanceMetric metric) {
         return codebookVectors[findClosestCodebookEntryIndex(dataVector, metric)];
     }
 
@@ -156,7 +154,7 @@ public class VectorQuantizer {
         for (int entryIndex = 0; entryIndex < codebookVectors.length; entryIndex++) {
 
 
-            final double dist = distanceBetweenVectors(dataVector, codebookVectors[entryIndex].getVector(), metric);
+            final double dist = distanceBetweenVectors(dataVector, codebookVectors[entryIndex], metric);
             if (dist < minDist) {
                 minDist = dist;
                 closestEntryIndex = entryIndex;
@@ -166,7 +164,7 @@ public class VectorQuantizer {
         return closestEntryIndex;
     }
 
-    public CodebookEntry[] getCodebookVectors() {
+    public int[][] getCodebookVectors() {
         return codebookVectors;
     }
 
-- 
GitLab