From 28c691c5754f51796738ae7a3b837108d2c5fc7d Mon Sep 17 00:00:00 2001 From: Vojtech Moravec <vojtech.moravec.st@vsb.cz> Date: Thu, 3 Dec 2020 11:09:41 +0100 Subject: [PATCH] Implement conversion of QVC files. --- .../java/cz/it4i/qcmp/DataCompressor.java | 68 ++++++++++++++----- .../java/cz/it4i/qcmp/cli/CliConstants.java | 7 ++ .../qcmp/cli/CompressionOptionsCLIParser.java | 29 +------- .../java/cz/it4i/qcmp/cli/ProgramMethod.java | 3 +- .../cz/it4i/qcmp/fileformat/IQvcHeader.java | 2 + .../cz/it4i/qcmp/fileformat/QvcHeaderV1.java | 5 ++ .../cz/it4i/qcmp/fileformat/SqQvcFile.java | 15 ++-- .../cz/it4i/qcmp/fileformat/VqQvcFile.java | 15 ++-- 8 files changed, 88 insertions(+), 56 deletions(-) diff --git a/src/main/java/cz/it4i/qcmp/DataCompressor.java b/src/main/java/cz/it4i/qcmp/DataCompressor.java index 6061522..fa30b58 100644 --- a/src/main/java/cz/it4i/qcmp/DataCompressor.java +++ b/src/main/java/cz/it4i/qcmp/DataCompressor.java @@ -2,6 +2,7 @@ package cz.it4i.qcmp; import cz.it4i.qcmp.benchmark.CompressionBenchmark; import cz.it4i.qcmp.cache.QuantizationCacheManager; +import cz.it4i.qcmp.cache.QvcFileReader; import cz.it4i.qcmp.cli.CliConstants; import cz.it4i.qcmp.cli.CompressionOptionsCLIParser; import cz.it4i.qcmp.cli.CustomFunctionBase; @@ -9,6 +10,7 @@ import cz.it4i.qcmp.cli.functions.DebugFunction; import cz.it4i.qcmp.compression.ImageCompressor; import cz.it4i.qcmp.compression.ImageDecompressor; import cz.it4i.qcmp.fileformat.FileExtensions; +import cz.it4i.qcmp.fileformat.IQvcFile; import org.apache.commons.cli.*; import java.io.IOException; @@ -36,24 +38,24 @@ public class DataCompressor { return; } - final CompressionOptionsCLIParser compressionOptionsCLIParsed = new CompressionOptionsCLIParser(cmd); + final CompressionOptionsCLIParser parsedOptions = new CompressionOptionsCLIParser(cmd); // NOTE(Moravec): From this point we need to dispose of possible existing SCIFIO context. - if (compressionOptionsCLIParsed.parseError()) { - System.err.println(compressionOptionsCLIParsed.getParseError()); + if (parsedOptions.parseError()) { + System.err.println(parsedOptions.getParseError()); ScifioWrapper.dispose(); return; } - if (compressionOptionsCLIParsed.isVerbose()) { - System.out.println(compressionOptionsCLIParsed.report()); + if (parsedOptions.isVerbose()) { + System.out.println(parsedOptions.report()); } - switch (compressionOptionsCLIParsed.getMethod()) { + switch (parsedOptions.getMethod()) { case Compress: { final String label = - compressionOptionsCLIParsed.getQuantizationType().toString() + " " + compressionOptionsCLIParsed.getQuantizationVector().toString(); + parsedOptions.getQuantizationType().toString() + " " + parsedOptions.getQuantizationVector().toString(); // final Stopwatch stopwatch = Stopwatch.startNew(); - final ImageCompressor compressor = new ImageCompressor(compressionOptionsCLIParsed); + final ImageCompressor compressor = new ImageCompressor(parsedOptions); if (!compressor.compress()) { System.err.println("Errors occurred during compression."); } @@ -66,7 +68,7 @@ public class DataCompressor { break; case Decompress: { // final Stopwatch stopwatch = Stopwatch.startNew(); - final ImageDecompressor decompressor = new ImageDecompressor(compressionOptionsCLIParsed); + final ImageDecompressor decompressor = new ImageDecompressor(parsedOptions); if (!decompressor.decompressToFile()) { System.err.println("Errors occurred during decompression."); } @@ -78,11 +80,11 @@ public class DataCompressor { break; case Benchmark: { - CompressionBenchmark.runBenchmark(compressionOptionsCLIParsed); + CompressionBenchmark.runBenchmark(parsedOptions); } break; case TrainCodebook: { - final ImageCompressor compressor = new ImageCompressor(compressionOptionsCLIParsed); + final ImageCompressor compressor = new ImageCompressor(parsedOptions); if (!compressor.trainAndSaveCodebook()) { System.err.println("Errors occurred during training/saving of codebook."); } @@ -94,7 +96,7 @@ public class DataCompressor { // final CustomFunctionBase customFunction = new MeasurePlaneErrorFunction(parsedCliOptions); // final CustomFunctionBase customFunction = new EntropyCalculation(compressionOptionsCLIParsed); // final CustomFunctionBase cf = new CalculateDifference(compressionOptionsCLIParsed); - final CustomFunctionBase cf = new DebugFunction(compressionOptionsCLIParsed); + final CustomFunctionBase cf = new DebugFunction(parsedOptions); if (!cf.run()) { System.err.println("Errors occurred during custom function."); } @@ -106,11 +108,11 @@ public class DataCompressor { } break; case InspectFile: { - if (compressionOptionsCLIParsed.getInputDataInfo().getFilePath().endsWith(FileExtensions.CACHE_FILE_EXT)) { - QuantizationCacheManager.inspectCacheFile(compressionOptionsCLIParsed.getInputDataInfo().getFilePath(), - compressionOptionsCLIParsed.isVerbose()); + if (parsedOptions.getInputDataInfo().getFilePath().endsWith(FileExtensions.CACHE_FILE_EXT)) { + QuantizationCacheManager.inspectCacheFile(parsedOptions.getInputDataInfo().getFilePath(), + parsedOptions.isVerbose()); } else { - final ImageDecompressor decompressor = new ImageDecompressor(compressionOptionsCLIParsed); + final ImageDecompressor decompressor = new ImageDecompressor(parsedOptions); try { System.out.println(decompressor.inspectCompressedFile()); } catch (final IOException e) { @@ -121,7 +123,41 @@ public class DataCompressor { } } break; + case Convert: { + final boolean inPlace = parsedOptions.getOutputFilePath() == null; + // TODO(Moravec): Maybe replace with generic reader which can determine file type based on magic value and not an extension. + if (parsedOptions.getInputDataInfo().getFilePath().endsWith(FileExtensions.CACHE_FILE_EXT)) { + IQvcFile cacheFile = null; + try { + cacheFile = QvcFileReader.readCacheFile(parsedOptions.getInputDataInfo().getFilePath()); + } catch (final IOException e) { + System.err.println("Unable to read QVC file. Error: " + e.getMessage()); + exitApplication(1); + } + try { + assert (cacheFile != null); + cacheFile.convertToNewerVersion(inPlace, parsedOptions.getInputDataInfo().getFilePath(), + parsedOptions.getOutputFilePath()); + } catch (final IOException e) { + System.err.println("Unable to convert specified QVC file. Error: " + e.getMessage()); + exitApplication(1); + } + + if (parsedOptions.isVerbose()) { + System.err.println("Qvc file is converted."); + } + + } else { + System.err.println("Qcmp file conversion isn't supported yet"); + } + } + break; } ScifioWrapper.dispose(); } + + private static void exitApplication(final int exitCode) { + ScifioWrapper.dispose(); + System.exit(exitCode); + } } diff --git a/src/main/java/cz/it4i/qcmp/cli/CliConstants.java b/src/main/java/cz/it4i/qcmp/cli/CliConstants.java index 9d32749..02a9f91 100644 --- a/src/main/java/cz/it4i/qcmp/cli/CliConstants.java +++ b/src/main/java/cz/it4i/qcmp/cli/CliConstants.java @@ -30,6 +30,9 @@ public class CliConstants { public static final String CUSTOM_FUNCTION_SHORT = "cf"; public static final String CUSTOM_FUNCTION_LONG = "custom-function"; + public static final String CONVERT_SHORT = "conv"; + public static final String CONVERT_LONG = "convert"; + public static final String BITS_SHORT = "b"; public static final String BITS_LONG = "bits"; @@ -72,6 +75,10 @@ public class CliConstants { CliConstants.INSPECT_LONG, false, "Inspect the compressed file")); + methodGroup.addOption(new Option(CliConstants.CONVERT_SHORT, + CliConstants.CONVERT_LONG, + false, + "Convert input file to new version of custom file format")); methodGroup.addOption(new Option(CliConstants.BENCHMARK_SHORT, CliConstants.BENCHMARK_LONG, diff --git a/src/main/java/cz/it4i/qcmp/cli/CompressionOptionsCLIParser.java b/src/main/java/cz/it4i/qcmp/cli/CompressionOptionsCLIParser.java index 6cf1804..c30fb4d 100644 --- a/src/main/java/cz/it4i/qcmp/cli/CompressionOptionsCLIParser.java +++ b/src/main/java/cz/it4i/qcmp/cli/CompressionOptionsCLIParser.java @@ -390,6 +390,8 @@ public class CompressionOptionsCLIParser extends CompressionOptions implements C method = ProgramMethod.InspectFile; } else if (cmd.hasOption(CliConstants.CUSTOM_FUNCTION_LONG)) { method = ProgramMethod.CustomFunction; + } else if (cmd.hasOption(CliConstants.CONVERT_LONG)) { + method = ProgramMethod.Convert; } else { parseErrorOccurred = true; errorBuilder.append("No program method was matched\n"); @@ -412,31 +414,7 @@ public class CompressionOptionsCLIParser extends CompressionOptions implements C public String report() { final StringBuilder sb = new StringBuilder(); - sb.append("Method: "); - switch (method) { - case Compress: - sb.append("Compress\n"); - break; - case Decompress: - sb.append("Decompress\n"); - break; - case Benchmark: - sb.append("Benchmark\n"); - break; - case TrainCodebook: - sb.append("TrainCodebook\n"); - break; - case PrintHelp: - sb.append("PrintHelp\n"); - break; - case CustomFunction: - sb.append("CustomFunction\n"); - break; - case InspectFile: - sb.append("InspectFile\n"); - break; - } - + sb.append("Method: ").append(method).append('\n'); if (hasQuantizationType(method)) { sb.append("Quantization type: "); @@ -470,7 +448,6 @@ public class CompressionOptionsCLIParser extends CompressionOptions implements C } if (hasQuantizationType(method)) { - sb.append("Input image dims: ").append(getInputDataInfo().getDimensions().toString()).append('\n'); } if (getInputDataInfo().isPlaneIndexSet()) { diff --git a/src/main/java/cz/it4i/qcmp/cli/ProgramMethod.java b/src/main/java/cz/it4i/qcmp/cli/ProgramMethod.java index 73d3abd..fd83e49 100644 --- a/src/main/java/cz/it4i/qcmp/cli/ProgramMethod.java +++ b/src/main/java/cz/it4i/qcmp/cli/ProgramMethod.java @@ -7,5 +7,6 @@ public enum ProgramMethod { TrainCodebook, PrintHelp, CustomFunction, - InspectFile + InspectFile, + Convert } diff --git a/src/main/java/cz/it4i/qcmp/fileformat/IQvcHeader.java b/src/main/java/cz/it4i/qcmp/fileformat/IQvcHeader.java index b1a752f..fae999d 100644 --- a/src/main/java/cz/it4i/qcmp/fileformat/IQvcHeader.java +++ b/src/main/java/cz/it4i/qcmp/fileformat/IQvcHeader.java @@ -10,4 +10,6 @@ public interface IQvcHeader extends IFileHeader { int getCodebookSize(); V3i getVectorDim(); + + String getTrainFileName(); } diff --git a/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV1.java b/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV1.java index 4d0027b..34399f1 100644 --- a/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV1.java +++ b/src/main/java/cz/it4i/qcmp/fileformat/QvcHeaderV1.java @@ -155,18 +155,22 @@ public class QvcHeaderV1 implements IQvcHeader { this.trainFileNameSize = this.trainFileName.length(); } + @Override public QuantizationType getQuantizationType() { return quantizationType; } + @Override public int getCodebookSize() { return codebookSize; } + @Override public int getBitsPerCodebookIndex() { return (int) Utils.log2(codebookSize); } + @Override public String getTrainFileName() { return trainFileName; } @@ -183,6 +187,7 @@ public class QvcHeaderV1 implements IQvcHeader { return vectorSizeZ; } + @Override public V3i getVectorDim() { return new V3i(vectorSizeX, vectorSizeY, vectorSizeZ); } diff --git a/src/main/java/cz/it4i/qcmp/fileformat/SqQvcFile.java b/src/main/java/cz/it4i/qcmp/fileformat/SqQvcFile.java index df75dea..d90ec89 100644 --- a/src/main/java/cz/it4i/qcmp/fileformat/SqQvcFile.java +++ b/src/main/java/cz/it4i/qcmp/fileformat/SqQvcFile.java @@ -1,5 +1,6 @@ package cz.it4i.qcmp.fileformat; +import cz.it4i.qcmp.cache.QvcFileWriter; import cz.it4i.qcmp.huffman.HuffmanNode; import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InBitStream; @@ -88,22 +89,18 @@ public class SqQvcFile implements IQvcFile { codebook = new SQCodebook(centroids, huffmanRoot); } - private void convertQvcFromV1ToV2(final String outputFilePath) { - - } @Override - public void convertToNewerVersion(final boolean inPlace, final String inputPath, final String outputPath) { - final int headerVersion = header.getHeaderVersion(); + public void convertToNewerVersion(final boolean inPlace, final String inputPath, final String outputPath) throws IOException { if (!inPlace && (outputPath == null || outputPath.isEmpty())) { System.err.println("InPlace conversion wasn't specified nor the output file path."); return; } - if (headerVersion == 1) { - convertQvcFromV1ToV2(inPlace ? inputPath : outputPath); - } else { - System.err.printf("Version %d is already the newest version of QVC file.\n", headerVersion); + if (header.getHeaderVersion() == 2) { + System.err.print("Version 2 is already the newest version of QVC file.\n"); + return; } + QvcFileWriter.writeSqCacheFile(inPlace ? inputPath : outputPath, header.getTrainFileName(), codebook); } @Override diff --git a/src/main/java/cz/it4i/qcmp/fileformat/VqQvcFile.java b/src/main/java/cz/it4i/qcmp/fileformat/VqQvcFile.java index 83dcc6a..b7ad419 100644 --- a/src/main/java/cz/it4i/qcmp/fileformat/VqQvcFile.java +++ b/src/main/java/cz/it4i/qcmp/fileformat/VqQvcFile.java @@ -1,5 +1,6 @@ package cz.it4i.qcmp.fileformat; +import cz.it4i.qcmp.cache.QvcFileWriter; import cz.it4i.qcmp.huffman.HuffmanNode; import cz.it4i.qcmp.huffman.HuffmanTreeBuilder; import cz.it4i.qcmp.io.InBitStream; @@ -97,8 +98,16 @@ public class VqQvcFile implements IQvcFile { } @Override - public void convertToNewerVersion(final boolean inPlace, final String inputPath, final String outputPath) { - assert false : "NOT IMPLEMENTED YET"; + public void convertToNewerVersion(final boolean inPlace, final String inputPath, final String outputPath) throws IOException { + if (!inPlace && (outputPath == null || outputPath.isEmpty())) { + System.err.println("InPlace conversion wasn't specified nor the output file path."); + return; + } + if (header.getHeaderVersion() == 2) { + System.err.print("Version 2 is already the newest version of QVC file.\n"); + return; + } + QvcFileWriter.writeVqCacheFile(inPlace ? inputPath : outputPath, header.getTrainFileName(), codebook); } @Override @@ -111,7 +120,5 @@ public class VqQvcFile implements IQvcFile { } builder.append("\n- - - - - - - - - - - - - - - - - - - - - - - - -\n"); } - - } } -- GitLab