Skip to content
Snippets Groups Projects
Commit 34ae9aa4 authored by Vojtech Moravec's avatar Vojtech Moravec
Browse files

Finished CLI interface v1

parent 61e7eb8e
No related branches found
No related tags found
No related merge requests found
import cli.ImprovedOptionGroup; import cli.CliConstants;
import compression.tool.ImageCompressor; import cli.ParsedCliOptions;
import compression.tool.ImageDecompressor;
import org.apache.commons.cli.*; import org.apache.commons.cli.*;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths;
public class DataCompressor { public class DataCompressor {
private static final String COMPRESS_SHORT = "c";
private static final String COMPRESS_LONG = "compress";
private static final String DECOMPRESS_SHORT = "d";
private static final String DECOMPRESS_LONG = "decompress";
private static final String BITS_SHORT = "b";
private static final String BITS_LONG = "bits";
// private static final String INPUT_SHORT = "i";
// private static final String INPUT_LONG = "input";
private static final String OUTPUT_SHORT = "o";
private static final String OUTPUT_LONG = "output";
private static final String VERBOSE_SHORT = "v";
private static final String VERBOSE_LONG = "verbose";
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
/*
* Example of usage:
* ijava -ea -jar DataCompressor.jar -c -sq input.raw 1000x1000x500
* ijava -ea -jar DataCompressor.jar -c -sq -b 7 input.raw 1000x1000x500
* ijava -ea -jar DataCompressor.jar -c -sq -b 7 input.raw 1000x1000x500 10
* ijava -ea -jar DataCompressor.jar -c -sq -b 7 -rp 10 input.raw 1000x1000x500 10
* ijava -ea -jar DataCompressor.jar -c -sq -b 7 -rp 15 input.raw 1000x1000x500 10
* ijava -ea -jar DataCompressor.jar -c -sq -b 7 -rp 40 input.raw 1000x1000x500
* ijava -ea -jar DataCompressor.jar -c -vq 9 input.raw 1000x1000x500
* ijava -ea -jar DataCompressor.jar -c -vq 9x1 input.raw 1000x1000x500
* ijava -ea -jar DataCompressor.jar -c -vq 3x3 input.raw 1000x1000x500
*
* */
// TODO(Moravec): We have created nice API for single files, but ... // TODO(Moravec): We have created nice API for single files, but ...
// we are dealing with tiffs or RAW files, which means // we are dealing with tiffs or RAW files, which means
// that we have to pass image dimensions and plane index. // that we have to pass image dimensions and plane index.
Options options = new Options(); Options options = new Options();
OptionGroup methodGroup = new ImprovedOptionGroup(); OptionGroup methodGroup = new OptionGroup();
methodGroup.setRequired(true); methodGroup.setRequired(true);
methodGroup.addOption(new Option(COMPRESS_SHORT, COMPRESS_LONG, false, "Compress 16 bit raw image")); methodGroup.addOption(new Option(CliConstants.COMPRESS_SHORT,
methodGroup.addOption(new Option(DECOMPRESS_SHORT, DECOMPRESS_LONG, false, "Decompress 16 bit raw image")); CliConstants.COMPRESS_LONG,
methodGroup.addOption(new Option("h", "help", false, "Print help")); false,
methodGroup.addOption(new Option(VERBOSE_SHORT, VERBOSE_LONG, false, "Make program verbose")); "Compress 16 bit raw image"));
methodGroup.addOption(new Option(CliConstants.DECOMPRESS_SHORT,
CliConstants.DECOMPRESS_LONG,
false,
"Decompress 16 bit raw image"));
methodGroup.addOption(new Option(CliConstants.INSPECT_SHORT,
CliConstants.INSPECT_LONG,
false,
"Inspect the compressed file"));
methodGroup.addOption(new Option(CliConstants.HELP_SHORT, CliConstants.HELP_LONG, false, "Print help"));
methodGroup.addOption(new Option(CliConstants.VERBOSE_SHORT,
CliConstants.VERBOSE_LONG,
false,
"Make program verbose"));
OptionGroup compressionMethodGroup = new OptionGroup();
compressionMethodGroup.addOption(new Option(CliConstants.SCALAR_QUANTIZATION_SHORT,
CliConstants.SCALAR_QUANTIZATION_LONG,
false,
"Use scalar quantization."));
compressionMethodGroup.addOption(new Option(CliConstants.VECTOR_QUANTIZATION_SHORT,
CliConstants.VECTOR_QUANTIZATION_LONG,
true,
"Use vector quantization. Need to pass vector size eg. 9,9x1,3x3"));
options.addOptionGroup(methodGroup); options.addOptionGroup(methodGroup);
options.addOption(BITS_SHORT, BITS_LONG, true, "Bit count per pixel [Default 8]"); options.addOptionGroup(compressionMethodGroup);
options.addOption(CliConstants.BITS_SHORT, CliConstants.BITS_LONG, true, "Bit count per pixel [Default 8]");
options.addOption(CliConstants.REFERENCE_PLANE_SHORT,
CliConstants.REFERENCE_PLANE_LONG,
true,
"Reference plane index");
// options.addRequiredOption(INPUT_SHORT, INPUT_LONG, true, "Input file"); // options.addRequiredOption(INPUT_SHORT, INPUT_LONG, true, "Input file");
options.addOption(OUTPUT_SHORT, OUTPUT_LONG, true, "Custom output directory"); options.addOption(CliConstants.OUTPUT_SHORT, CliConstants.OUTPUT_LONG, true, "Custom output directory");
HelpFormatter formatter = new HelpFormatter(); HelpFormatter formatter = new HelpFormatter();
CommandLineParser parser = new DefaultParser(); CommandLineParser parser = new DefaultParser();
CommandLine cmd; CommandLine cmd;
try { try {
...@@ -63,41 +83,29 @@ public class DataCompressor { ...@@ -63,41 +83,29 @@ public class DataCompressor {
return; return;
} }
if (cmd.hasOption("help")) { ParsedCliOptions parsedCliOptions = new ParsedCliOptions(cmd);
formatter.printHelp("ijava -jar DataCompressor.jar", options); if (parsedCliOptions.hasErrorOccured()) {
System.err.println(parsedCliOptions.getError());
return; return;
} }
// Default output directory to current directory. System.out.println(parsedCliOptions.report());
final String currentOutputDirectory = cmd.getOptionValue(OUTPUT_LONG,
Paths.get("").toAbsolutePath().toString());
// Default bit count per pixel to 8. switch (parsedCliOptions.getMethod()) {
final int bits = Integer.parseInt(cmd.getOptionValue(BITS_LONG, "8"));
// Files to compress decompress case Compress:
final String[] files = cmd.getArgs(); System.out.println("Compress");
System.out.println("Output directory: " + currentOutputDirectory); break;
System.out.println("Bit count: " + bits); case Decompress:
System.out.println("Decompress");
final boolean verbose = cmd.hasOption(VERBOSE_LONG); break;
case PrintHelp:
if (cmd.hasOption(COMPRESS_SHORT) || cmd.hasOption(COMPRESS_LONG)) { formatter.printHelp("ijava -jar DataCompressor.jar", options);
System.out.println("Compressing: ..."); break;
ImageCompressor compressor = new ImageCompressor(currentOutputDirectory, bits, verbose); case InspectFile:
if (!compressor.compressRawImages(files)) { System.err.println("Not supported yet.");
// TODO(Moravec): Report error. break;
}
return;
}
if (cmd.hasOption(DECOMPRESS_SHORT) || cmd.hasOption(DECOMPRESS_LONG)) {
System.out.println("Decompressing: ...");
ImageDecompressor decompressor = new ImageDecompressor(currentOutputDirectory, bits, verbose);
if (!decompressor.decompressCompressedImages(files)) {
// TODO(Moravec): Report error.
}
} }
} }
} }
package cli;
public class CliConstants {
public static final String HELP_SHORT = "h";
public static final String HELP_LONG = "help";
public static final String COMPRESS_SHORT = "c";
public static final String COMPRESS_LONG = "compress";
public static final String DECOMPRESS_SHORT = "d";
public static final String DECOMPRESS_LONG = "decompress";
public static final String INSPECT_SHORT = "i";
public static final String INSPECT_LONG = "inspect";
public static final String BITS_SHORT = "b";
public static final String BITS_LONG = "bits";
public static final String OUTPUT_SHORT = "o";
public static final String OUTPUT_LONG = "output";
public static final String VERBOSE_SHORT = "v";
public static final String VERBOSE_LONG = "verbose";
public static final String SCALAR_QUANTIZATION_SHORT = "sq";
public static final String SCALAR_QUANTIZATION_LONG = "scalar-quantization";
public static final String VECTOR_QUANTIZATION_SHORT = "vq";
public static final String VECTOR_QUANTIZATION_LONG = "vector-quantization";
public static final String REFERENCE_PLANE_SHORT = "rp";
public static final String REFERENCE_PLANE_LONG = "reference-plane";
}
package cli;
public class ParseResult<T> {
private final boolean success;
private final T value;
private final String errorMessage;
public ParseResult(final String errorMessage) {
this.success = false;
this.value = null;
this.errorMessage = errorMessage;
}
public ParseResult(T value) {
this.success = true;
this.value = value;
this.errorMessage = null;
}
public boolean isSuccess() {
return success;
}
public T getValue() {
return value;
}
public String getErrorMessage() {
return errorMessage;
}
}
package cli;
import compression.data.V2i;
import compression.data.V3i;
import org.apache.commons.cli.CommandLine;
import java.nio.file.Paths;
public class ParsedCliOptions {
private static final int DEFAULT_BITS_PER_PIXEL = 8;
private ProgramMethod method;
private QuantizationType quantizationType;
private String inputFile;
private String outputDirectory;
private int bitsPerPixel;
private V2i vectorDimension;
private V3i imageDimension;
private boolean planeIndexSet = false;
private int planeIndex;
private boolean refPlaneIndexSet = false;
private int referencePlaneIndex;
private boolean verbose;
private boolean errorOccured;
private String error;
public ParsedCliOptions(CommandLine cmdInput) {
parseCLI(cmdInput);
}
private void parseCLI(final CommandLine cmd) {
StringBuilder errorBuilder = new StringBuilder("Errors:\n");
errorOccured = false;
parseProgramMethod(cmd, errorBuilder);
if (method == ProgramMethod.PrintHelp)
return;
parseCompressionType(cmd, errorBuilder);
parseBitsPerPixel(cmd, errorBuilder);
// Default output directory to current directory.
outputDirectory = cmd.getOptionValue(CliConstants.OUTPUT_LONG, Paths.get("").toAbsolutePath().toString());
parseReferencePlaneIndex(cmd, errorBuilder);
final String[] fileInfo = cmd.getArgs();
parseInputFilePart(errorBuilder, fileInfo);
verbose = cmd.hasOption(CliConstants.VERBOSE_LONG);
error = errorBuilder.toString();
}
private void parseInputFilePart(StringBuilder errorBuilder, final String[] fileInfo) {
if ((method == ProgramMethod.Decompress) || (method == ProgramMethod.InspectFile)) {
if (fileInfo.length > 0) {
inputFile = fileInfo[0];
} else {
errorOccured = true;
errorBuilder.append("Missing input file for decompression");
}
} else {
// Compression part.
// We require the file path and dimensions, like input.raw 1920x1080x5
if (fileInfo.length < 2) {
errorOccured = true;
errorBuilder.append("Both filepath and file dimensions are required arguments\n");
} else {
// The first string must be file path.
inputFile = fileInfo[0];
parseImageDims(fileInfo[1], errorBuilder);
if (fileInfo.length > 2) {
final var parseResult = tryParseInt(fileInfo[2]);
if (parseResult.isSuccess()) {
planeIndexSet = true;
planeIndex = parseResult.getValue();
} else {
errorOccured = true;
errorBuilder.append("The second argument after file name must be plane index\n");
}
}
}
}
}
private void parseImageDims(final String dimsString, StringBuilder errorBuilder) {
// We thing of 3x3x1 and 3x3 as the same thing
final int firstDelimIndex = dimsString.indexOf('x');
if (firstDelimIndex == -1) {
errorOccured = true;
errorBuilder.append("Error parsing image dimensions. We require DxDxD or DxD [=DxDx1]\n");
return;
}
final String num1String = dimsString.substring(0, firstDelimIndex);
final String secondPart = dimsString.substring(firstDelimIndex + 1);
final int secondDelimIndex = secondPart.indexOf('x');
if (secondDelimIndex == -1) {
final var n1Result = tryParseInt(num1String);
final var n2Result = tryParseInt(secondPart);
if (n1Result.isSuccess() && n2Result.isSuccess()) {
imageDimension = new V3i(n1Result.getValue(), n2Result.getValue(), 1);
} else {
errorOccured = true;
errorBuilder.append("Failed to parse image dimensions of format DxD, got: ");
errorBuilder.append(String.format("%sx%s\n", num1String, secondPart));
}
} else {
final String num2String = secondPart.substring(0, secondDelimIndex);
final String num3String = secondPart.substring(secondDelimIndex + 1);
final var n1Result = tryParseInt(num1String);
final var n2Result = tryParseInt(num2String);
final var n3Result = tryParseInt(num3String);
if (n1Result.isSuccess() && n2Result.isSuccess() && n3Result.isSuccess()) {
imageDimension = new V3i(n1Result.getValue(), n2Result.getValue(), n3Result.getValue());
} else {
errorOccured = true;
errorBuilder.append("Failed to parse image dimensions of format DxDxD, got: ");
errorBuilder.append(String.format("%sx%sx%s\n", num1String, num2String, num3String));
}
}
}
private void parseReferencePlaneIndex(CommandLine cmd, StringBuilder errorBuilder) {
if (cmd.hasOption(CliConstants.REFERENCE_PLANE_LONG)) {
final String rpString = cmd.getOptionValue(CliConstants.REFERENCE_PLANE_LONG);
final var parseResult = tryParseInt(rpString);
if (parseResult.isSuccess()) {
referencePlaneIndex = parseResult.getValue();
refPlaneIndexSet = true;
} else {
errorOccured = true;
errorBuilder.append("Failed to parse reference plane index").append('\n');
errorBuilder.append(parseResult.getErrorMessage()).append('\n');
}
} else {
refPlaneIndexSet = false;
}
}
private void parseBitsPerPixel(CommandLine cmd, StringBuilder errorBuilder) {
if (cmd.hasOption(CliConstants.BITS_LONG)) {
final String bitsString = cmd.getOptionValue(CliConstants.BITS_LONG);
final var parseResult = tryParseInt(bitsString);
if (parseResult.isSuccess()) {
bitsPerPixel = parseResult.getValue();
} else {
errorOccured = true;
errorBuilder.append("Failed to parse bits per pixel.").append('\n');
errorBuilder.append(parseResult.getErrorMessage()).append('\n');
}
} else {
bitsPerPixel = DEFAULT_BITS_PER_PIXEL;
}
}
private void parseCompressionType(CommandLine cmd, StringBuilder errorBuilder) {
if (method == ProgramMethod.Compress) {
if (cmd.hasOption(CliConstants.SCALAR_QUANTIZATION_LONG)) {
quantizationType = QuantizationType.Scalar;
} else if (cmd.hasOption(CliConstants.VECTOR_QUANTIZATION_LONG)) {
final String vectorDefinition = cmd.getOptionValue(CliConstants.VECTOR_QUANTIZATION_LONG);
final int delimiterIndex = vectorDefinition.indexOf('x');
if (delimiterIndex == -1) {
final var parseResult = tryParseInt(vectorDefinition);
if (parseResult.isSuccess()) {
quantizationType = QuantizationType.Vector1D;
vectorDimension = new V2i(parseResult.getValue(), 1);
} else {
errorOccured = true;
errorBuilder.append("1D vector quantization requires vector size").append('\n').append(
parseResult.getErrorMessage()).append('\n');
}
} else {
final String firstNumberString = vectorDefinition.substring(0, delimiterIndex);
final String secondNumberString = vectorDefinition.substring(delimiterIndex + 1);
final var firstNumberParseResult = tryParseInt(firstNumberString);
final var secondNumberParseResult = tryParseInt(secondNumberString);
if (firstNumberParseResult.isSuccess() && secondNumberParseResult.isSuccess()) {
vectorDimension = new V2i(firstNumberParseResult.getValue(),
secondNumberParseResult.getValue());
if ((vectorDimension.getX() <= 0) || (vectorDimension.getY() <= 0)) {
errorOccured = true;
errorBuilder.append("Wrong quantization vector: ").append(vectorDimension.toString());
} else {
if ((vectorDimension.getX() > 1) && (vectorDimension.getY() == 1)) {
quantizationType = QuantizationType.Vector1D;
} else if ((vectorDimension.getX() == 1) && (vectorDimension.getY() > 1)) {
errorOccured = true;
errorBuilder.append("There is nothing wrong with the vector ").
append(vectorDimension.toString()).append(
" but we do not support column vectors yet").append('\n');
} else {
quantizationType = QuantizationType.Vector2D;
}
}
} else {
errorOccured = true;
errorBuilder.append("Failed to parse vector dimension. Expected DxD, got: ").append(
vectorDefinition);
}
}
} else {
errorOccured = true;
errorBuilder.append("Quantization type wasn't set for compression").append('\n');
}
}
}
private void parseProgramMethod(CommandLine cmd, StringBuilder errorBuilder) {
if (cmd.hasOption(CliConstants.HELP_LONG)) {
method = ProgramMethod.PrintHelp;
} else if (cmd.hasOption(CliConstants.COMPRESS_LONG)) {
method = ProgramMethod.Compress;
} else if (cmd.hasOption(CliConstants.DECOMPRESS_LONG)) {
method = ProgramMethod.Decompress;
} else if (cmd.hasOption(CliConstants.INSPECT_LONG)) {
method = ProgramMethod.InspectFile;
} else {
errorOccured = true;
errorBuilder.append("No program method was matched\n");
}
}
private ParseResult<Integer> tryParseInt(final String string) {
try {
final int result = Integer.parseInt(string);
return new ParseResult<>(result);
} catch (NumberFormatException e) {
return new ParseResult<>(e.getMessage());
}
}
public ProgramMethod getMethod() {
return method;
}
public QuantizationType getQuantizationType() {
return quantizationType;
}
public String getInputFile() {
return inputFile;
}
public String getOutputDirectory() {
return outputDirectory;
}
public int getBitsPerPixel() {
return bitsPerPixel;
}
public V2i getVectorDimension() {
return vectorDimension;
}
public V3i getImageDimension() {
return imageDimension;
}
public int getPlaneIndex() {
return planeIndex;
}
public int getReferencePlaneIndex() {
return referencePlaneIndex;
}
public boolean isVerbose() {
return verbose;
}
public boolean hasReferencePlaneIndex() {
return refPlaneIndexSet;
}
public boolean hasPlaneIndexSet() {
return planeIndexSet;
}
public boolean hasErrorOccured() {
return errorOccured;
}
public String getError() {
return error;
}
public String report() {
StringBuilder sb = new StringBuilder();
sb.append("Method: ");
switch (method) {
case Compress:
sb.append("Compress\n");
break;
case Decompress:
sb.append("Decompress\n");
break;
case PrintHelp:
sb.append("PrintHelp\n");
break;
case InspectFile:
sb.append("InspectFile\n");
break;
}
if (method == ProgramMethod.Compress) {
sb.append("Quantization type: ");
switch (quantizationType) {
case Scalar:
sb.append("Scalar\n");
break;
case Vector1D:
sb.append(String.format("Vector1D %s\n", vectorDimension.toString()));
break;
case Vector2D:
sb.append(String.format("Vector2D %s\n", vectorDimension.toString()));
break;
}
}
sb.append("BitsPerPixel: ").append(bitsPerPixel).append('\n');
sb.append("OutputDirectory: ").append(outputDirectory).append('\n');
sb.append("InputFile: ").append(inputFile).append('\n');
if (method == ProgramMethod.Compress) {
sb.append("Input image dims: ").append(imageDimension.toString()).append('\n');
}
if (planeIndexSet) {
sb.append("PlaneIndex: ").append(planeIndex).append('\n');
}
if (refPlaneIndexSet) {
sb.append("ReferencePlaneIndex: ").append(referencePlaneIndex).append('\n');
}
return sb.toString();
}
}
package cli;
public enum ProgramMethod {
Compress,
Decompress,
PrintHelp, InspectFile
}
package cli;
public enum QuantizationType {
Scalar(0), Vector1D(1), Vector2D(2);
private final int value;
QuantizationType(int enumValue) {
value = enumValue;
}
public int getValue() {
return value;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment