diff --git a/src/main/java/azgracompress/benchmark/BenchmarkBase.java b/src/main/java/azgracompress/benchmark/BenchmarkBase.java index 7a6b629990a55c19176dca4f2915294f193ad701..b1983a2d3ae664fe0eac5882d8686c04d3875166 100644 --- a/src/main/java/azgracompress/benchmark/BenchmarkBase.java +++ b/src/main/java/azgracompress/benchmark/BenchmarkBase.java @@ -1,5 +1,6 @@ package azgracompress.benchmark; +import azgracompress.compression.CompressionOptions; import azgracompress.io.InputData; import azgracompress.cli.ParsedCliOptions; import azgracompress.data.ImageU16; @@ -27,11 +28,10 @@ abstract class BenchmarkBase { protected final int[] planes; protected final V3i rawImageDims; - protected final boolean useMiddlePlane; protected final int codebookSize; - protected final boolean hasCacheFolder; protected final String cacheFolder; - protected final boolean hasGeneralQuantizer; + + protected CompressionOptions.CodebookType codebookType; protected final int workerCount; @@ -45,7 +45,7 @@ abstract class BenchmarkBase { this.outputDirectory = options.getOutputFilePath(); this.rawImageDims = ifi.getDimensions(); - this.useMiddlePlane = options.shouldUseMiddlePlane(); + this.codebookType = options.getCodebookType(); this.codebookSize = (int) Math.pow(2, options.getBitsPerCodebookIndex()); @@ -67,9 +67,7 @@ abstract class BenchmarkBase { } } - hasCacheFolder = options.hasCodebookCacheFolder(); cacheFolder = options.getCodebookCacheFolder(); - hasGeneralQuantizer = useMiddlePlane || hasCacheFolder; workerCount = options.getWorkerCount(); } diff --git a/src/main/java/azgracompress/benchmark/SQBenchmark.java b/src/main/java/azgracompress/benchmark/SQBenchmark.java index 1fc7d82de080e2e5b93978fc09818cfd57f3497a..29b19bbba675b91e91b27b356aed8242ce9cbd4c 100644 --- a/src/main/java/azgracompress/benchmark/SQBenchmark.java +++ b/src/main/java/azgracompress/benchmark/SQBenchmark.java @@ -3,6 +3,7 @@ package azgracompress.benchmark; import azgracompress.U16; import azgracompress.cache.QuantizationCacheManager; import azgracompress.cli.ParsedCliOptions; +import azgracompress.compression.CompressionOptions; import azgracompress.io.loader.IPlaneLoader; import azgracompress.io.loader.PlaneLoaderFactory; import azgracompress.quantization.QTrainIteration; @@ -40,7 +41,7 @@ public class SQBenchmark extends BenchmarkBase { System.out.println(String.format("|CODEBOOK| = %d", codebookSize)); ScalarQuantizer quantizer = null; - if (hasCacheFolder) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Global) { System.out.println("Loading codebook from cache"); QuantizationCacheManager cacheManager = new QuantizationCacheManager(cacheFolder); final SQCodebook codebook = cacheManager.loadSQCodebook(inputFile, codebookSize); @@ -52,7 +53,7 @@ public class SQBenchmark extends BenchmarkBase { quantizer = new ScalarQuantizer(codebook); System.out.println("Created quantizer from cache"); - } else if (useMiddlePlane) { + } else if (options.getCodebookType() == CompressionOptions.CodebookType.MiddlePlane) { final int middlePlaneIndex = rawImageDims.getZ() / 2; final int[] middlePlaneData; @@ -90,7 +91,7 @@ public class SQBenchmark extends BenchmarkBase { codebookSize); final String trainLogFile = String.format(TRAIN_FILE_TEMPLATE, planeIndex, codebookSize); - if (!hasGeneralQuantizer) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Individual) { quantizer = trainLloydMaxQuantizer(planeData, codebookSize, trainLogFile); System.out.println("Created plane quantizer"); } diff --git a/src/main/java/azgracompress/benchmark/VQBenchmark.java b/src/main/java/azgracompress/benchmark/VQBenchmark.java index c9095c4b4f51db17ac4550b4887e5d50911a69fb..d4847493d32ad617eae7fc2a51e6e6d842c6db4e 100644 --- a/src/main/java/azgracompress/benchmark/VQBenchmark.java +++ b/src/main/java/azgracompress/benchmark/VQBenchmark.java @@ -3,6 +3,7 @@ package azgracompress.benchmark; import azgracompress.U16; import azgracompress.cache.QuantizationCacheManager; import azgracompress.cli.ParsedCliOptions; +import azgracompress.compression.CompressionOptions; import azgracompress.data.*; import azgracompress.io.loader.IPlaneLoader; import azgracompress.io.loader.PlaneLoaderFactory; @@ -66,7 +67,7 @@ public class VQBenchmark extends BenchmarkBase { System.out.println(String.format("|CODEBOOK| = %d", codebookSize)); VectorQuantizer quantizer = null; - if (hasCacheFolder) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Global) { System.out.println("Loading codebook from cache"); QuantizationCacheManager cacheManager = new QuantizationCacheManager(cacheFolder); final VQCodebook codebook = cacheManager.loadVQCodebook(inputFile, codebookSize, qVector.toV3i()); @@ -77,7 +78,7 @@ public class VQBenchmark extends BenchmarkBase { quantizer = new VectorQuantizer(codebook); System.out.println("Created quantizer from cache"); - } else if (useMiddlePlane) { + } else if (options.getCodebookType() == CompressionOptions.CodebookType.MiddlePlane) { final int middlePlaneIndex = rawImageDims.getZ() / 2; final ImageU16 middlePlane; try { @@ -113,7 +114,7 @@ public class VQBenchmark extends BenchmarkBase { final int[][] planeData = getPlaneVectors(plane, qVector); - if (!hasGeneralQuantizer) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Individual) { LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeData, codebookSize, workerCount, diff --git a/src/main/java/azgracompress/cli/ParsedCliOptions.java b/src/main/java/azgracompress/cli/ParsedCliOptions.java index 524efef8db5091494dc287546b051994ed0a7fb8..1ff3dfe5242e1d5e517ac1f376d9b412674103d1 100644 --- a/src/main/java/azgracompress/cli/ParsedCliOptions.java +++ b/src/main/java/azgracompress/cli/ParsedCliOptions.java @@ -120,7 +120,17 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable { parseBitsPerPixel(cmd, errorBuilder); - setUseMiddlePlane(cmd.hasOption(CliConstants.USE_MIDDLE_PLANE_LONG)); + if (cmd.hasOption(CliConstants.USE_MIDDLE_PLANE_LONG)) + setCodebookType(CodebookType.MiddlePlane); + else if (cmd.hasOption(CliConstants.CODEBOOK_CACHE_FOLDER_LONG)) { + final String cbc = cmd.getOptionValue(CliConstants.CODEBOOK_CACHE_FOLDER_LONG, null); + assert (cbc != null); + setCodebookType(CodebookType.Global); + setCodebookCacheFolder(cbc); + } else { + setCodebookType(CodebookType.Individual); + } + final String[] fileInfo = cmd.getArgs(); parseInputFilePart(errorBuilder, fileInfo); @@ -137,7 +147,6 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable { errorBuilder.append("Unable to parse worker count. Expected int got: ").append(wcString).append('\n'); } } - setCodebookCacheFolder(cmd.getOptionValue(CliConstants.CODEBOOK_CACHE_FOLDER_LONG, null)); if (!parseErrorOccurred) { setOutputFilePath(cmd.getOptionValue(CliConstants.OUTPUT_LONG, getDefaultOutputFilePath(((FileInputData) getInputDataInfo()).getFilePath()))); @@ -192,7 +201,7 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable { getInputDataInfo().setDataLoaderType(InputData.DataLoaderType.SCIFIOLoader); Reader reader; try { - reader = ScifioWrapper.getReader(((FileInputData)getInputDataInfo()).getFilePath()); + reader = ScifioWrapper.getReader(((FileInputData) getInputDataInfo()).getFilePath()); } catch (IOException | FormatException e) { parseErrorOccurred = true; errorBuilder.append("Failed to get SCIFIO reader for file.\n"); @@ -544,11 +553,17 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable { } - sb.append("InputFile: ").append(((FileInputData)getInputDataInfo()).getFilePath()).append('\n'); + sb.append("InputFile: ").append(((FileInputData) getInputDataInfo()).getFilePath()).append('\n'); sb.append("Output: ").append(getOutputFilePath()).append('\n'); sb.append("BitsPerCodebookIndex: ").append(getBitsPerCodebookIndex()).append('\n'); - if (hasCodebookCacheFolder()) { - sb.append("CodebookCacheFolder: ").append(getCodebookCacheFolder()).append('\n'); + + switch (getCodebookType()) { + case MiddlePlane: + sb.append("Use middle plane for codebook training\n"); + break; + case Global: + sb.append("CodebookCacheFolder: ").append(getCodebookCacheFolder()).append('\n'); + break; } if (hasQuantizationType(method)) { @@ -559,9 +574,6 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable { sb.append("PlaneIndex: ").append(getInputDataInfo().getPlaneIndex()).append('\n'); } - if (shouldUseMiddlePlane()) { - sb.append("Use middle plane for codebook training\n"); - } if (getInputDataInfo().isPlaneRangeSet()) { sb.append("FromPlaneIndex: ").append(getInputDataInfo().getPlaneRange().getFrom()).append('\n'); diff --git a/src/main/java/azgracompress/compression/CompressionOptions.java b/src/main/java/azgracompress/compression/CompressionOptions.java index 53738f953bbd70974793abf64d0338864467e78f..da0384dde8d460a3736122dd4b2051d97ff3eba9 100644 --- a/src/main/java/azgracompress/compression/CompressionOptions.java +++ b/src/main/java/azgracompress/compression/CompressionOptions.java @@ -8,6 +8,14 @@ import azgracompress.fileformat.QuantizationType; * Options for the compressor/decompressor. */ public class CompressionOptions { + + public enum CodebookType { + Individual, + MiddlePlane, + Global, + Invalid + } + /** * Input image or compressed file. */ @@ -23,6 +31,11 @@ public class CompressionOptions { */ private QuantizationType quantizationType; + /** + * Type of the codebook. + */ + private CodebookType codebookType = CodebookType.Individual; + /** * Directory which contains codebook caches. */ @@ -39,11 +52,6 @@ public class CompressionOptions { */ private V2i vectorDimension = new V2i(0); - /** - * Flag, whether to use middle plane as reference plane for codebook creation. - */ - private boolean useMiddlePlane = false; - /** * Number of workers to be used for different operations. */ @@ -55,10 +63,6 @@ public class CompressionOptions { private boolean verbose; - public boolean hasCodebookCacheFolder() { - return codebookCacheFolder != null; - } - protected void setVerbose(boolean verbose) { this.verbose = verbose; } @@ -117,14 +121,6 @@ public class CompressionOptions { this.vectorDimension = vectorDimension; } - public boolean shouldUseMiddlePlane() { - return useMiddlePlane; - } - - public void setUseMiddlePlane(boolean useMiddlePlane) { - this.useMiddlePlane = useMiddlePlane; - } - public int getWorkerCount() { return workerCount; } @@ -132,4 +128,12 @@ public class CompressionOptions { public void setWorkerCount(int workerCount) { this.workerCount = workerCount; } + + public CodebookType getCodebookType() { + return codebookType; + } + + public void setCodebookType(CodebookType codebookType) { + this.codebookType = codebookType; + } } diff --git a/src/main/java/azgracompress/compression/ImageCompressor.java b/src/main/java/azgracompress/compression/ImageCompressor.java index 812e0c0c93e2aeb17592a9af509568ae8401f496..495b5890065cb09dbe2ed9c9ea48a7b1bfe8bffd 100644 --- a/src/main/java/azgracompress/compression/ImageCompressor.java +++ b/src/main/java/azgracompress/compression/ImageCompressor.java @@ -142,9 +142,7 @@ public class ImageCompressor extends CompressorDecompressorBase { header.setQuantizationType(options.getQuantizationType()); header.setBitsPerCodebookIndex((byte) options.getBitsPerCodebookIndex()); - // Codebook per plane is used only if middle plane isn't set nor is the cache folder. - final boolean oneCodebook = options.shouldUseMiddlePlane() || options.hasCodebookCacheFolder(); - header.setCodebookPerPlane(!oneCodebook); + header.setCodebookPerPlane(options.getCodebookType() == CompressionOptions.CodebookType.Individual); header.setImageSizeX(options.getInputDataInfo().getDimensions().getX()); header.setImageSizeY(options.getInputDataInfo().getDimensions().getY()); diff --git a/src/main/java/azgracompress/compression/SQImageCompressor.java b/src/main/java/azgracompress/compression/SQImageCompressor.java index c0cab1ec261360dd76c5e6fb500372d5ba9d08d6..598f231bbb93df1ddeebe29f3f190119f3d37924 100644 --- a/src/main/java/azgracompress/compression/SQImageCompressor.java +++ b/src/main/java/azgracompress/compression/SQImageCompressor.java @@ -90,7 +90,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm public long[] compress(DataOutputStream compressStream) throws ImageCompressionException { final InputData inputDataInfo = options.getInputDataInfo(); Stopwatch stopwatch = new Stopwatch(); - final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.shouldUseMiddlePlane(); + final boolean hasGeneralQuantizer = options.getCodebookType() != CompressionOptions.CodebookType.Individual; final IPlaneLoader planeLoader; try { @@ -102,7 +102,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm ScalarQuantizer quantizer = null; Huffman huffman = null; final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize()); - if (options.hasCodebookCacheFolder()) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Global) { Log("Loading codebook from cache file."); quantizer = loadQuantizerFromCache(); @@ -110,7 +110,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm Log("Cached quantizer with huffman coder created."); writeCodebookToOutputStream(quantizer, compressStream); - } else if (options.shouldUseMiddlePlane()) { + } else if (options.getCodebookType() == CompressionOptions.CodebookType.MiddlePlane) { stopwatch.restart(); ImageU16 middlePlane = null; final int middlePlaneIndex = getMiddlePlaneIndex(); diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java index 3979841e7d136da0f6c5b4a58421198477889e3e..e1f89b4768f84d88639252cbc14cb049da419599 100644 --- a/src/main/java/azgracompress/compression/VQImageCompressor.java +++ b/src/main/java/azgracompress/compression/VQImageCompressor.java @@ -94,7 +94,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm public long[] compress(DataOutputStream compressStream) throws ImageCompressionException { final InputData inputDataInfo = options.getInputDataInfo(); Stopwatch stopwatch = new Stopwatch(); - final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.shouldUseMiddlePlane(); + final boolean hasGeneralQuantizer = options.getCodebookType() != CompressionOptions.CodebookType.Individual; final IPlaneLoader planeLoader; final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize()); try { @@ -105,13 +105,13 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm VectorQuantizer quantizer = null; Huffman huffman = null; - if (options.hasCodebookCacheFolder()) { + if (options.getCodebookType() == CompressionOptions.CodebookType.Global) { Log("Loading codebook from cache file."); quantizer = loadQuantizerFromCache(); huffman = createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies()); Log("Cached quantizer with huffman coder created."); writeQuantizerToCompressStream(quantizer, compressStream); - } else if (options.shouldUseMiddlePlane()) { + } else if (options.getCodebookType() == CompressionOptions.CodebookType.MiddlePlane) { stopwatch.restart(); final int middlePlaneIndex = getMiddlePlaneIndex();