diff --git a/src/main/java/azgracompress/compression/CompressionOptions.java b/src/main/java/azgracompress/compression/CompressionOptions.java index 82e7c5c61bf4d1aacf946f74f49d6ef3d41c4113..09c83fb5d3395150cd175f0d1de813dd28163321 100644 --- a/src/main/java/azgracompress/compression/CompressionOptions.java +++ b/src/main/java/azgracompress/compression/CompressionOptions.java @@ -67,7 +67,7 @@ public class CompressionOptions { this.workerCount = (cores / 2); } - protected void setVerbose(boolean verbose) { + public void setVerbose(boolean verbose) { this.verbose = verbose; } diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java index 05539c6f994ba04f0ce8ca5327701d2607b7187c..4543ed9b2e6702f01f81d612dbea4bafe76317d1 100644 --- a/src/main/java/azgracompress/compression/VQImageCompressor.java +++ b/src/main/java/azgracompress/compression/VQImageCompressor.java @@ -32,7 +32,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm getCodebookSize(), options.getWorkerCount(), options.getVectorDimension().toV3i()); - LBGResult vqResult = vqInitializer.findOptimalCodebook(false); + LBGResult vqResult = vqInitializer.findOptimalCodebook(); return new VectorQuantizer(vqResult.getCodebook()); } @@ -255,7 +255,8 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm options.getWorkerCount(), options.getVectorDimension().toV3i()); reportStatusToListeners("Starting LBG optimization."); - LBGResult lbgResult = vqInitializer.findOptimalCodebook(options.isVerbose()); + vqInitializer.setStatusListener(this::reportStatusToListeners); + LBGResult lbgResult = vqInitializer.findOptimalCodebook(); reportStatusToListeners("Learned the optimal codebook."); diff --git a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java index 6943a3edd8411a09c2eea8e1a683f812c33e063f..b431e3f5659e745850e55dfdee8ea8e3c5cfa786 100644 --- a/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java +++ b/src/main/java/azgracompress/quantization/vector/LBGVectorQuantizer.java @@ -1,6 +1,7 @@ package azgracompress.quantization.vector; import azgracompress.U16; +import azgracompress.compression.listeners.IStatusListener; import azgracompress.data.V3i; import azgracompress.utilities.Stopwatch; import azgracompress.utilities.Utils; @@ -24,7 +25,7 @@ public class LBGVectorQuantizer { private long[] frequencies; - boolean verbose = false; + private IStatusListener statusListener = null; private double _mse = 0.0; public LBGVectorQuantizer(final int[][] vectors, @@ -49,6 +50,19 @@ public class LBGVectorQuantizer { findUniqueVectors(); } + public void setStatusListener(final IStatusListener statusListener) { + this.statusListener = statusListener; + } + + private void reportStatus(final String message) { + if (statusListener != null) + statusListener.sendMessage(message); + } + + private void reportStatus(final String format, final Object... arg) { + reportStatus(String.format(format, arg)); + } + private void findUniqueVectors() { uniqueVectorCount = 0; uniqueTrainingVectors = new ArrayList<>(codebookSize); @@ -74,10 +88,10 @@ public class LBGVectorQuantizer { } private LBGResult createCodebookFromUniqueVectors() { + assert (uniqueTrainingVectors != null) : "uniqueTrainingVectors aren't initialized."; - if (verbose) { - System.out.println("Creating codebook from unique vectors."); - } + 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); @@ -91,9 +105,7 @@ public class LBGVectorQuantizer { } final double mse = averageMse(codebook); final double psnr = Utils.calculatePsnr(mse, U16.Max); - if (verbose) { - System.out.println(String.format("Final MSE: %.4f\nFinal PSNR: %.4f (dB)", mse, psnr)); - } + reportStatus("Unique vector codebook, MSE: %f PSNR: %f(dB)", mse, psnr); return new LBGResult(vectorDimensions, codebook, frequencies, mse, psnr); } @@ -103,40 +115,23 @@ public class LBGVectorQuantizer { * @return Result of the search. */ public LBGResult findOptimalCodebook() { - return findOptimalCodebook(false); - } - - - /** - * Find the optimal codebook of vectors, used for vector quantization. - * - * @param isVerbose True if program algorithm should be verbose. - * @return Result of the search. - */ - public LBGResult findOptimalCodebook(boolean isVerbose) { - Stopwatch stopwatch = Stopwatch.startNew("findOptimalCodebook"); - this.verbose = isVerbose; + Stopwatch stopwatch = Stopwatch.startNew("LBG::findOptimalCodebook()"); if (uniqueVectorCount < codebookSize) { return createCodebookFromUniqueVectors(); } LearningCodebookEntry[] codebook = initializeCodebook(); - if (verbose) { - System.out.println("Got initial codebook. Improving codebook..."); - } + reportStatus("LBG::findOptimalCodebook() - Got initial codebook. Improving it..."); + LBG(codebook, EPSILON * 0.1); final double finalMse = averageMse(codebook); final double psnr = Utils.calculatePsnr(finalMse, U16.Max); - if (verbose) { - System.out.println(String.format("Improved codebook, final average MSE: %.4f PSNR: %.4f (dB)", - finalMse, - psnr)); - } + reportStatus("LBG::findOptimalCodebook() - Improved the codebook. Final MSE: %f PSNR: %f (dB)", + finalMse, + psnr); stopwatch.stop(); - if (verbose) { - System.out.println(stopwatch); - } + reportStatus(stopwatch.toString()); return new LBGResult(vectorDimensions, learningCodebookToCodebook(codebook), frequencies, finalMse, psnr); } @@ -384,22 +379,17 @@ public class LBGVectorQuantizer { } codebook = newCodebook; assert (codebook.length == (currentCodebookSize * 2)); - if (verbose) { - System.out.println(String.format("Split from %d -> %d", currentCodebookSize, currentCodebookSize * 2)); - } + reportStatus("LBG::initializeCodebook() - Dividing codebook from %d --> %d", + currentCodebookSize, + 2 * currentCodebookSize); currentCodebookSize *= 2; // Execute LBG Algorithm on current codebook to improve it. - if (verbose) { - System.out.println("Improving current codebook..."); - } LBG(codebook); final double avgMse = averageMse(codebook); - if (verbose) { - System.out.println(String.format("Average MSE: %.4f", avgMse)); - } + reportStatus("MSE of improved divided codebook: %f", avgMse); } return codebook; } @@ -463,32 +453,26 @@ public class LBGVectorQuantizer { avgDistortion /= codebook.length; // Calculate distortion - double dist = (previousDistortion - avgDistortion) / avgDistortion; - if (verbose) { - System.out.println(String.format("---- It: %d Distortion: %.5f", iteration++, dist)); - System.out.println(String.format("Last Dist: %.5f Current dist: %.5f", lastDist, dist)); - } + double distortion = (previousDistortion - avgDistortion) / avgDistortion; + reportStatus("LBG::LBG() - Iteration: %d Distortion: %.5f", iteration++, distortion); - if (Double.isNaN(dist)) { - if (verbose) { - System.out.println("Distortion is NaN."); - } + + if (Double.isNaN(distortion)) { + reportStatus("Distortion is NaN. Stopping LBG::LBG()."); break; } - if (dist > lastDist) { - if (verbose) { - System.out.println("Previous distortion was better. Ending LBG..."); - } + if (distortion > lastDist) { + reportStatus("Previous distortion was better. Stopping LBG::LBG()."); break; } // Check distortion against epsilon - if (dist < epsilon) { + if (distortion < epsilon) { break; } else { previousDistortion = avgDistortion; - lastDist = dist; + lastDist = distortion; } } } @@ -665,7 +649,6 @@ public class LBGVectorQuantizer { */ private void calculateEntryProperties(LearningCodebookEntry[] codebook) { - Stopwatch stopwatch = Stopwatch.startNew("calculateEntryProperties"); int value; EntryInfo[] entryInfos = new EntryInfo[codebook.length]; for (int i = 0; i < entryInfos.length; i++) { @@ -695,10 +678,6 @@ public class LBGVectorQuantizer { for (int i = 0; i < codebook.length; i++) { codebook[i].setInfo(entryInfos[i]); } - stopwatch.stop(); - if (this.verbose) { - System.out.println(stopwatch); - } }