diff --git a/DataCompressor.iml b/DataCompressor.iml
index add71798f4dc0699f9f3ae1cd18003ffe51acc6a..3b89bcd618d9e5144d76bb3f650bc05ce9f93f48 100644
--- a/DataCompressor.iml
+++ b/DataCompressor.iml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<module type="JAVA_MODULE" version="4">
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_9">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
@@ -10,10 +10,29 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: org.jetbrains:annotations:18.0.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-math3:3.6.1" level="project" />
- <orderEntry type="library" name="Maven: commons-io:commons-io:1.3.2" level="project" />
<orderEntry type="library" name="Maven: commons-cli:commons-cli:1.4" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:19.0.0" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.6" level="project" />
+ <orderEntry type="library" name="Maven: io.scif:scifio:0.37.3" level="project" />
+ <orderEntry type="library" name="Maven: io.scif:scifio-jai-imageio:1.1.1" level="project" />
+ <orderEntry type="library" name="Maven: net.imagej:imagej-common:0.28.2" level="project" />
+ <orderEntry type="library" name="Maven: net.imglib2:imglib2-roi:0.7.0" level="project" />
+ <orderEntry type="library" name="Maven: net.imglib2:imglib2-realtransform:2.2.1" level="project" />
+ <orderEntry type="library" name="Maven: gov.nist.math:jama:1.0.3" level="project" />
+ <orderEntry type="library" name="Maven: jitk:jitk-tps:3.0.1" level="project" />
+ <orderEntry type="library" name="Maven: com.googlecode.efficient-java-matrix-library:ejml:0.24" level="project" />
+ <orderEntry type="library" name="Maven: log4j:log4j:1.2.17" level="project" />
+ <orderEntry type="library" name="Maven: net.sf.trove4j:trove4j:3.0.3" level="project" />
+ <orderEntry type="library" name="Maven: org.scijava:scijava-table:0.4.0" level="project" />
+ <orderEntry type="library" name="Maven: edu.ucar:udunits:4.3.18" level="project" />
+ <orderEntry type="library" name="Maven: net.imglib2:imglib2:5.6.3" level="project" />
+ <orderEntry type="library" name="Maven: net.imglib2:imglib2-cache:1.0.0-beta-11" level="project" />
+ <orderEntry type="library" name="Maven: com.github.ben-manes.caffeine:caffeine:2.4.0" level="project" />
+ <orderEntry type="library" name="Maven: org.scijava:scijava-common:2.77.0" level="project" />
+ <orderEntry type="library" name="Maven: org.scijava:parsington:1.0.4" level="project" />
+ <orderEntry type="library" name="Maven: org.bushe:eventbus:1.4" level="project" />
+ <orderEntry type="library" name="Maven: commons-io:commons-io:2.6" level="project" />
</component>
</module>
\ No newline at end of file
diff --git a/README.md b/README.md
index bf49431a6d48dfbe18393205df04363c2a3e6114..878298021f212468f381f2f5a5d82847820d4895 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# DataCompressor usage
-**Right now only RAW 16-bit files are supported. Other types can be converted to 16-bit RAW file using [FIJI](https://imagej.net/Fiji).**
+**This branch support loading of 16-bit input files via SCIFIO readers. RAW files will be loaded as before,
+anything else will be loaded by SCIFIO.**
Help output:
```
diff --git a/pom.xml b/pom.xml
index 60740bc5d43e947600887169ed75e137133c5aad..61d82b0931b75145445e0769facdf39fb55e7ccd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,8 +5,14 @@
<modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.scijava</groupId>
+ <artifactId>pom-scijava</artifactId>
+ <version>27.0.1</version>
+ <relativePath/>
+ </parent>
-<!-- <groupId>org.scijava</groupId>-->
+ <!-- <groupId>org.scijava</groupId>-->
<groupId>org.azgra</groupId>
<artifactId>DataCompressor</artifactId>
<version>1.0-SNAPSHOT</version>
@@ -23,6 +29,13 @@
</plugins>
</build>
+ <repositories>
+ <repository>
+ <id>scijava.public</id>
+ <url>https://maven.scijava.org/content/groups/public</url>
+ </repository>
+ </repositories>
+
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -50,5 +63,14 @@
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
+ <dependency>
+ <groupId>io.scif</groupId>
+ <artifactId>scifio</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.6</version>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/src/main/java/azgracompress/DataCompressor.java b/src/main/java/azgracompress/DataCompressor.java
index 0d9e0b48b34702dc238732c09593b5de2f10bc20..8203c7be7eeb6f8f0fd6a8e3f83b53dc639f02c6 100644
--- a/src/main/java/azgracompress/DataCompressor.java
+++ b/src/main/java/azgracompress/DataCompressor.java
@@ -30,8 +30,10 @@ public class DataCompressor {
}
ParsedCliOptions parsedCliOptions = new ParsedCliOptions(cmd);
+ // NOTE(Moravec): From this point we need to dispose of possible existing SCIFIO context.
if (parsedCliOptions.parseError()) {
System.err.println(parsedCliOptions.getParseError());
+ ScifioWrapper.dispose();
return;
}
@@ -46,26 +48,27 @@ public class DataCompressor {
if (!compressor.compress()) {
System.err.println("Errors occurred during compression.");
}
- return;
}
+ break;
case Decompress: {
ImageDecompressor decompressor = new ImageDecompressor(parsedCliOptions);
if (!decompressor.decompress()) {
System.err.println("Errors occurred during decompression.");
}
- return;
}
+ break;
+
case Benchmark: {
CompressionBenchmark.runBenchmark(parsedCliOptions);
- return;
}
+ break;
case TrainCodebook: {
ImageCompressor compressor = new ImageCompressor(parsedCliOptions);
if (!compressor.trainAndSaveCodebook()) {
System.err.println("Errors occurred during training/saving of codebook.");
}
- return;
}
+ break;
case CustomFunction: {
// NOTE(Moravec): Custom function class here |
// V
@@ -74,9 +77,8 @@ public class DataCompressor {
if (!customFunction.run()) {
System.err.println("Errors occurred during custom function.");
}
- return;
-
}
+ break;
case PrintHelp: {
formatter.printHelp(CliConstants.MAIN_HELP, options);
@@ -91,9 +93,9 @@ public class DataCompressor {
System.err.println(e.getMessage());
e.printStackTrace();
}
- return;
}
+ break;
}
- return;
+ ScifioWrapper.dispose();
}
}
diff --git a/src/main/java/azgracompress/ScifioWrapper.java b/src/main/java/azgracompress/ScifioWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..90041087c4ae02fcea557f1a6435f03122c9a8a4
--- /dev/null
+++ b/src/main/java/azgracompress/ScifioWrapper.java
@@ -0,0 +1,52 @@
+package azgracompress;
+
+import io.scif.FormatException;
+import io.scif.Reader;
+import io.scif.SCIFIO;
+
+import java.io.IOException;
+
+public class ScifioWrapper {
+
+ private static ScifioWrapper instance = null;
+ private SCIFIO scifioInstance = null;
+
+ private ScifioWrapper() {
+ scifioInstance = new SCIFIO();
+ }
+
+ public static SCIFIO getScifio() {
+ if (instance == null) {
+ synchronized (ScifioWrapper.class) {
+ if (instance == null) {
+ instance = new ScifioWrapper();
+ }
+ }
+ }
+
+ return instance.scifioInstance;
+ }
+
+ /**
+ * Get image file reader.
+ *
+ * @param path Path of image file.
+ * @return Scifio reader.
+ * @throws IOException
+ * @throws FormatException
+ */
+ public static Reader getReader(final String path) throws IOException, FormatException {
+ SCIFIO scifio = getScifio();
+ return scifio.initializer().initializeReader(path);
+ }
+
+ public synchronized static void dispose() {
+ if (instance != null) {
+ if (instance.scifioInstance != null) {
+ instance.scifioInstance.context().dispose();
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/azgracompress/benchmark/Benchmark.java b/src/main/java/azgracompress/benchmark/Benchmark.java
index 21af7c41cb2158b9d90d250428acd0a06978d550..e4ba0f258da6235af7c50178b3b9ea33bd506a2a 100644
--- a/src/main/java/azgracompress/benchmark/Benchmark.java
+++ b/src/main/java/azgracompress/benchmark/Benchmark.java
@@ -1,15 +1,6 @@
package azgracompress.benchmark;
-import azgracompress.U16;
import azgracompress.cli.ParsedCliOptions;
-import azgracompress.compression.ImageCompressor;
-import azgracompress.compression.ImageDecompressor;
-import azgracompress.data.ImageU16;
-import azgracompress.io.RawDataIO;
-import azgracompress.utilities.Utils;
-
-import java.io.File;
-import java.io.IOException;
public class Benchmark extends BenchmarkBase {
@@ -21,88 +12,89 @@ public class Benchmark extends BenchmarkBase {
@Override
public void startBenchmark() {
- ParsedCliOptions compressOps;
- ParsedCliOptions decompressOps;
- try {
- compressOps = (ParsedCliOptions) options.clone();
- decompressOps = (ParsedCliOptions) options.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- return;
- }
-
- boolean dirCreated = new File(options.getOutputFilePath()).mkdirs();
- //"%d_cb%d.raw.qcmp"
- final String qcmpFilePath = getFileNamePathIntoOutDir(String.format(COMPRESSED_FILE_TEMPLATE,
- options.getPlaneIndex(),
- codebookSize));
- compressOps.setOutputFilePath(qcmpFilePath);
- ImageCompressor compressor = new ImageCompressor(compressOps);
- if (!compressor.compress()) {
- System.err.println("Errors occurred during compression.");
- return;
- }
-
- decompressOps.setInputFilePath(qcmpFilePath);
-
-
- final String decompressedFile = getFileNamePathIntoOutDir(String.format(QUANTIZED_FILE_TEMPLATE,
- options.getPlaneIndex(),
- codebookSize));
-
- decompressOps.setOutputFilePath(decompressedFile);
- ImageDecompressor decompressor = new ImageDecompressor(decompressOps);
- if (!decompressor.decompress()) {
- System.err.println("Errors occurred during decompression.");
- }
-
- final int[] originalData;
- try {
- originalData = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- options.getPlaneIndex()).getData();
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
- final int[] quantizedData;
- try {
- quantizedData = RawDataIO.loadImageU16(decompressedFile,
- options.getImageDimension().toV2i().toV3i(), 0).getData();
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
-
- final int[] diffArray = Utils.getDifference(originalData, quantizedData);
- final double mse = Utils.calculateMse(diffArray);
- final double PSNR = Utils.calculatePsnr(mse, U16.Max);
- System.out.println(String.format("MSE: %.4f\tPSNR: %.4f(dB)", mse, PSNR));
-
- final int[] absDifferenceData = Utils.asAbsoluteValues(diffArray);
-
- final String diffFilePath = getFileNamePathIntoOutDir(String.format(DIFFERENCE_FILE_TEMPLATE,
- options.getPlaneIndex(),
- codebookSize));
-
- final String absDiffFilePath = getFileNamePathIntoOutDir(String.format(ABSOLUTE_DIFFERENCE_FILE_TEMPLATE,
- options.getPlaneIndex(),
- codebookSize));
-
- ImageU16 img = new ImageU16(rawImageDims.getX(),
- rawImageDims.getY(),
- absDifferenceData);
- try {
- // NOTE(Moravec): Use little endian so that gnuplot can read the array.
- RawDataIO.writeImageU16(absDiffFilePath, img, true);
- System.out.println("Saved absolute difference to: " + absDiffFilePath);
-
- RawDataIO.writeDataI32(diffFilePath, diffArray, true);
- System.out.println("Saved difference to: " + absDiffFilePath);
- } catch (Exception e) {
- e.printStackTrace();
- System.err.println("Failed to save difference.");
- return;
- }
+ // TODO(Moravec): Support PlaneLoader API.
+// ParsedCliOptions compressOps;
+// ParsedCliOptions decompressOps;
+// try {
+// compressOps = (ParsedCliOptions) options.clone();
+// decompressOps = (ParsedCliOptions) options.clone();
+// } catch (CloneNotSupportedException e) {
+// e.printStackTrace();
+// return;
+// }
+//
+// boolean dirCreated = new File(options.getOutputFilePath()).mkdirs();
+// //"%d_cb%d.raw.qcmp"
+// final String qcmpFilePath = getFileNamePathIntoOutDir(String.format(COMPRESSED_FILE_TEMPLATE,
+// options.getPlaneIndex(),
+// codebookSize));
+// compressOps.setOutputFilePath(qcmpFilePath);
+// ImageCompressor compressor = new ImageCompressor(compressOps);
+// if (!compressor.compress()) {
+// System.err.println("Errors occurred during compression.");
+// return;
+// }
+//
+// decompressOps.setInputFileInfo(new InputFileInfo(qcmpFilePath));
+//
+//
+// final String decompressedFile = getFileNamePathIntoOutDir(String.format(QUANTIZED_FILE_TEMPLATE,
+// options.getPlaneIndex(),
+// codebookSize));
+//
+// decompressOps.setOutputFilePath(decompressedFile);
+// ImageDecompressor decompressor = new ImageDecompressor(decompressOps);
+// if (!decompressor.decompress()) {
+// System.err.println("Errors occurred during decompression.");
+// }
+//
+// final int[] originalData;
+// try {
+// originalData = RawDataIO.loadImageU16(options.getInputFilePath(),
+// options.getImageDimension(),
+// options.getPlaneIndex()).getData();
+// } catch (IOException e) {
+// e.printStackTrace();
+// return;
+// }
+// final int[] quantizedData;
+// try {
+// quantizedData = RawDataIO.loadImageU16(decompressedFile,
+// options.getImageDimension().toV2i().toV3i(), 0).getData();
+// } catch (IOException e) {
+// e.printStackTrace();
+// return;
+// }
+//
+// final int[] diffArray = Utils.getDifference(originalData, quantizedData);
+// final double mse = Utils.calculateMse(diffArray);
+// final double PSNR = Utils.calculatePsnr(mse, U16.Max);
+// System.out.println(String.format("MSE: %.4f\tPSNR: %.4f(dB)", mse, PSNR));
+//
+// final int[] absDifferenceData = Utils.asAbsoluteValues(diffArray);
+//
+// final String diffFilePath = getFileNamePathIntoOutDir(String.format(DIFFERENCE_FILE_TEMPLATE,
+// options.getPlaneIndex(),
+// codebookSize));
+//
+// final String absDiffFilePath = getFileNamePathIntoOutDir(String.format(ABSOLUTE_DIFFERENCE_FILE_TEMPLATE,
+// options.getPlaneIndex(),
+// codebookSize));
+//
+// ImageU16 img = new ImageU16(rawImageDims.getX(),
+// rawImageDims.getY(),
+// absDifferenceData);
+// try {
+// // NOTE(Moravec): Use little endian so that gnuplot can read the array.
+// RawDataIO.writeImageU16(absDiffFilePath, img, true);
+// System.out.println("Saved absolute difference to: " + absDiffFilePath);
+//
+// RawDataIO.writeDataI32(diffFilePath, diffArray, true);
+// System.out.println("Saved difference to: " + absDiffFilePath);
+// } catch (Exception e) {
+// e.printStackTrace();
+// System.err.println("Failed to save difference.");
+// return;
+// }
}
}
diff --git a/src/main/java/azgracompress/benchmark/BenchmarkBase.java b/src/main/java/azgracompress/benchmark/BenchmarkBase.java
index 5d272e2f7bfe7c6b77c8421bf0757bcfeed995c9..5cc054afbcf829ee35ea96bd67223996531db75f 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.cli.InputFileInfo;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.Interval;
import azgracompress.data.ImageU16;
@@ -39,25 +40,29 @@ abstract class BenchmarkBase {
protected BenchmarkBase(final ParsedCliOptions options) {
this.options = options;
- this.inputFile = options.getInputFilePath();
+
+ final InputFileInfo ifi = options.getInputFileInfo();
+ this.inputFile = ifi.getFilePath();
this.outputDirectory = options.getOutputFilePath();
- this.rawImageDims = options.getImageDimension();
+
+ this.rawImageDims = ifi.getDimensions();
this.useMiddlePlane = options.shouldUseMiddlePlane();
+
this.codebookSize = (int) Math.pow(2, options.getBitsPerCodebookIndex());
- if (options.isPlaneIndexSet()) {
- this.planes = new int[]{options.getPlaneIndex()};
- } else if (options.isPlaneRangeSet()) {
- final Interval<Integer> planeRange = options.getPlaneRange();
- final int from = planeRange.getFrom();
- final int count = planeRange.getInclusiveTo() - from;
+ if (ifi.isPlaneIndexSet()) {
+ this.planes = new int[]{ifi.getPlaneIndex()};
+ } else if (ifi.isPlaneRangeSet()) {
+ final int from = ifi.getPlaneRange().getX();
+ final int to = ifi.getPlaneRange().getY();
+ final int count = to - from;
this.planes = new int[count + 1];
for (int i = 0; i <= count; i++) {
this.planes[i] = from + i;
}
} else {
- final int planeCount = options.getImageDimension().getZ();
+ final int planeCount = ifi.getDimensions().getZ();
this.planes = new int[planeCount];
for (int i = 0; i < planeCount; i++) {
this.planes[i] = i;
@@ -81,32 +86,32 @@ abstract class BenchmarkBase {
return file.getAbsolutePath();
}
- /**
- * Load u16 plane from RAW file.
- *
- * @param planeIndex Zero based plane index.
- * @return u16 plane.
- */
- protected ImageU16 loadPlane(final int planeIndex) {
- try {
- return RawDataIO.loadImageU16(inputFile, rawImageDims, planeIndex);
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- return null;
- }
-
- /**
- * Load U16 plane data from RAW file.
- *
- * @param planeIndex Zero based plane index.
- * @return U16 array of image plane data.
- */
- protected int[] loadPlaneData(final int planeIndex) {
- ImageU16 plane = loadPlane(planeIndex);
-
- return (plane != null) ? plane.getData() : new int[0];
- }
+ // /**
+ // * Load u16 plane from RAW file.
+ // *
+ // * @param planeIndex Zero based plane index.
+ // * @return u16 plane.
+ // */
+ // protected ImageU16 loadPlane(final int planeIndex) {
+ // try {
+ // return RawDataIO.loadImageU16(inputFile, rawImageDims, planeIndex);
+ // } catch (Exception ex) {
+ // ex.printStackTrace();
+ // }
+ // return null;
+ // }
+
+ // /**
+ // * Load U16 plane data from RAW file.
+ // *
+ // * @param planeIndex Zero based plane index.
+ // * @return U16 array of image plane data.
+ // */
+ // protected int[] loadPlaneData(final int planeIndex) {
+ // ImageU16 plane = loadPlane(planeIndex);
+ //
+ // return (plane != null) ? plane.getData() : new int[0];
+ // }
/**
diff --git a/src/main/java/azgracompress/benchmark/SQBenchmark.java b/src/main/java/azgracompress/benchmark/SQBenchmark.java
index 4850b367805f606519d3670898b0c31058a3c1cd..9a61ecdfaf8cc4571cfa87d1af2641109ae9dc7b 100644
--- a/src/main/java/azgracompress/benchmark/SQBenchmark.java
+++ b/src/main/java/azgracompress/benchmark/SQBenchmark.java
@@ -3,6 +3,8 @@ package azgracompress.benchmark;
import azgracompress.U16;
import azgracompress.cache.QuantizationCacheManager;
import azgracompress.cli.ParsedCliOptions;
+import azgracompress.io.IPlaneLoader;
+import azgracompress.io.PlaneLoaderFactory;
import azgracompress.quantization.QTrainIteration;
import azgracompress.quantization.scalar.LloydMaxU16ScalarQuantization;
import azgracompress.quantization.scalar.SQCodebook;
@@ -22,12 +24,22 @@ public class SQBenchmark extends BenchmarkBase {
@Override
public void startBenchmark() {
+ IPlaneLoader planeLoader;
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(options.getInputFileInfo());
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.err.println("Unable to create SCIFIO reader.");
+ return;
+ }
+
if (planes.length < 1) {
return;
}
boolean dirCreated = new File(this.outputDirectory).mkdirs();
System.out.println(String.format("|CODEBOOK| = %d", codebookSize));
ScalarQuantizer quantizer = null;
+
if (hasCacheFolder) {
System.out.println("Loading codebook from cache");
QuantizationCacheManager cacheManager = new QuantizationCacheManager(cacheFolder);
@@ -42,8 +54,12 @@ public class SQBenchmark extends BenchmarkBase {
System.out.println("Created quantizer from cache");
} else if (useMiddlePlane) {
final int middlePlaneIndex = rawImageDims.getZ() / 2;
- final int[] middlePlaneData = loadPlaneData(middlePlaneIndex);
- if (middlePlaneData.length == 0) {
+
+ final int[] middlePlaneData;
+ try {
+ middlePlaneData = planeLoader.loadPlaneU16(middlePlaneIndex).getData();
+ } catch (IOException e) {
+ e.printStackTrace();
System.err.println("Failed to load middle plane data.");
return;
}
@@ -54,7 +70,14 @@ public class SQBenchmark extends BenchmarkBase {
for (final int planeIndex : planes) {
System.out.println(String.format("Loading plane %d ...", planeIndex));
// NOTE(Moravec): Actual planeIndex is zero based.
- final int[] planeData = loadPlaneData(planeIndex);
+ final int[] planeData;
+ try {
+ planeData = planeLoader.loadPlaneU16(planeIndex).getData();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.err.println("Failed to load plane data.");
+ return;
+ }
if (planeData.length == 0) {
System.err.println(String.format("Failed to load plane %d data. Skipping plane.", planeIndex));
return;
@@ -63,8 +86,8 @@ public class SQBenchmark extends BenchmarkBase {
final String quantizedFile = String.format(QUANTIZED_FILE_TEMPLATE, planeIndex, codebookSize);
final String diffFile = String.format(DIFFERENCE_FILE_TEMPLATE, planeIndex, codebookSize);
final String absoluteDiffFile = String.format(ABSOLUTE_DIFFERENCE_FILE_TEMPLATE,
- planeIndex,
- codebookSize);
+ planeIndex,
+ codebookSize);
final String trainLogFile = String.format(TRAIN_FILE_TEMPLATE, planeIndex, codebookSize);
if (!hasGeneralQuantizer) {
diff --git a/src/main/java/azgracompress/benchmark/VQBenchmark.java b/src/main/java/azgracompress/benchmark/VQBenchmark.java
index 463a90878cf3a472e8f98a436d8e7d6d9321d4a1..e827d6ef21268f92a058973e7d3a5fca33aab556 100644
--- a/src/main/java/azgracompress/benchmark/VQBenchmark.java
+++ b/src/main/java/azgracompress/benchmark/VQBenchmark.java
@@ -4,6 +4,8 @@ import azgracompress.U16;
import azgracompress.cache.QuantizationCacheManager;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.data.*;
+import azgracompress.io.IPlaneLoader;
+import azgracompress.io.PlaneLoaderFactory;
import azgracompress.quantization.vector.LBGResult;
import azgracompress.quantization.vector.LBGVectorQuantizer;
import azgracompress.quantization.vector.VQCodebook;
@@ -11,6 +13,7 @@ import azgracompress.quantization.vector.VectorQuantizer;
import azgracompress.utilities.Utils;
import java.io.File;
+import java.io.IOException;
public class VQBenchmark extends BenchmarkBase {
@@ -46,6 +49,14 @@ public class VQBenchmark extends BenchmarkBase {
if (planes.length < 1) {
return;
}
+ IPlaneLoader planeLoader;
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(options.getInputFileInfo());
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.err.println("Unable to create SCIFIO reader.");
+ return;
+ }
if (qVector.getY() > 1) {
System.out.println("2D qVector");
} else {
@@ -65,20 +76,23 @@ public class VQBenchmark extends BenchmarkBase {
}
quantizer = new VectorQuantizer(codebook);
System.out.println("Created quantizer from cache");
+
} else if (useMiddlePlane) {
final int middlePlaneIndex = rawImageDims.getZ() / 2;
- final ImageU16 middlePlane = loadPlane(middlePlaneIndex);
-
- if (middlePlane == null) {
+ final ImageU16 middlePlane;
+ try {
+ middlePlane = planeLoader.loadPlaneU16(middlePlaneIndex);
+ } catch (IOException e) {
+ e.printStackTrace();
System.err.println("Failed to load middle plane data.");
return;
}
final int[][] refPlaneData = getPlaneVectors(middlePlane, qVector);
LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(refPlaneData,
- codebookSize,
- workerCount,
- qVector.toV3i());
+ codebookSize,
+ workerCount,
+ qVector.toV3i());
final LBGResult vqResult = vqInitializer.findOptimalCodebook();
quantizer = new VectorQuantizer(vqResult.getCodebook());
System.out.println("Created quantizer from middle plane.");
@@ -86,9 +100,12 @@ public class VQBenchmark extends BenchmarkBase {
for (final int planeIndex : planes) {
System.out.println(String.format("Loading plane %d ...", planeIndex));
- final ImageU16 plane = loadPlane(planeIndex);
- if (plane == null) {
+ final ImageU16 plane;
+ try {
+ plane = planeLoader.loadPlaneU16(planeIndex);
+ } catch (IOException e) {
+ e.printStackTrace();
System.err.println(String.format("Failed to load plane %d data. Skipping plane.", planeIndex));
return;
}
@@ -98,9 +115,9 @@ public class VQBenchmark extends BenchmarkBase {
if (!hasGeneralQuantizer) {
LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeData,
- codebookSize,
- workerCount,
- qVector.toV3i());
+ codebookSize,
+ workerCount,
+ qVector.toV3i());
LBGResult vqResult = vqInitializer.findOptimalCodebook();
quantizer = new VectorQuantizer(vqResult.getCodebook());
System.out.println("Created plane quantizer.");
@@ -109,8 +126,8 @@ public class VQBenchmark extends BenchmarkBase {
final String quantizedFile = String.format(QUANTIZED_FILE_TEMPLATE, planeIndex, codebookSize);
final String diffFile = String.format(DIFFERENCE_FILE_TEMPLATE, planeIndex, codebookSize);
final String absoluteDiffFile = String.format(ABSOLUTE_DIFFERENCE_FILE_TEMPLATE,
- planeIndex,
- codebookSize);
+ planeIndex,
+ codebookSize);
final int[][] quantizedData = quantizer.quantize(planeData, workerCount);
diff --git a/src/main/java/azgracompress/cli/InputFileInfo.java b/src/main/java/azgracompress/cli/InputFileInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8a6f073bff769397fa234a23974069e698f1658
--- /dev/null
+++ b/src/main/java/azgracompress/cli/InputFileInfo.java
@@ -0,0 +1,89 @@
+package azgracompress.cli;
+
+import azgracompress.data.V2i;
+import azgracompress.data.V3i;
+
+/**
+ * Information about the input file.
+ */
+public class InputFileInfo {
+ /**
+ * Input file path.
+ */
+ private final String filePath;
+
+ private boolean isRAW = true;
+
+ private V3i dimension;
+
+ private boolean planeIndexSet = false;
+ private int planeIndex;
+
+ private boolean planeRangeSet = false;
+ private V2i planeRange;
+
+ public InputFileInfo(final String filePath) {
+ this.filePath = filePath;
+ }
+
+ /**
+ * Get number of selected planes to be compressed.
+ *
+ * @return Number of planes for compression.
+ */
+ public int getNumberOfPlanes() {
+ if (planeIndexSet) {
+ return 1;
+ } else if (planeRangeSet) {
+ return ((planeRange.getY() + 1) - planeRange.getX());
+ } else {
+ return dimension.getZ();
+ }
+ }
+
+ public void setDimension(final V3i dimension) {
+ this.dimension = dimension;
+ }
+
+ public void setPlaneIndex(final int planeIndex) {
+ this.planeIndexSet = true;
+ this.planeIndex = planeIndex;
+ }
+
+ public void setPlaneRange(final V2i planeRange) {
+ this.planeRangeSet = true;
+ this.planeRange = planeRange;
+ }
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public V3i getDimensions() {
+ return dimension;
+ }
+
+ public boolean isPlaneIndexSet() {
+ return planeIndexSet;
+ }
+
+ public int getPlaneIndex() {
+ return planeIndex;
+ }
+
+ public boolean isPlaneRangeSet() {
+ return planeRangeSet;
+ }
+
+ public V2i getPlaneRange() {
+ return planeRange;
+ }
+
+ public boolean isRAW() {
+ return isRAW;
+ }
+
+ public void setIsRaw(boolean RAW) {
+ isRAW = RAW;
+ }
+}
diff --git a/src/main/java/azgracompress/cli/ParsedCliOptions.java b/src/main/java/azgracompress/cli/ParsedCliOptions.java
index 28a3bdeebfbba1f43f621fc8649a75c32c4f830a..ece73127c01c4a043fcf6352fcb9bdb2da9df2e5 100644
--- a/src/main/java/azgracompress/cli/ParsedCliOptions.java
+++ b/src/main/java/azgracompress/cli/ParsedCliOptions.java
@@ -1,14 +1,20 @@
package azgracompress.cli;
import azgracompress.compression.CompressionOptions;
+import azgracompress.ScifioWrapper;
import azgracompress.compression.CompressorDecompressorBase;
-import azgracompress.compression.Interval;
import azgracompress.data.V2i;
import azgracompress.data.V3i;
+import azgracompress.fileformat.FileExtensions;
import azgracompress.fileformat.QuantizationType;
+import io.scif.FormatException;
+import io.scif.Plane;
+import io.scif.Reader;
import org.apache.commons.cli.CommandLine;
+import org.apache.commons.io.FilenameUtils;
import java.io.File;
+import java.io.IOException;
import java.nio.file.Paths;
public class ParsedCliOptions extends CompressionOptions implements Cloneable {
@@ -38,6 +44,13 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
parseCLI(cmdInput);
}
+ private String removeQCMPFileExtension(final String originalPath) {
+ if (originalPath.toUpperCase().endsWith(CompressorDecompressorBase.EXTENSION)) {
+ return originalPath.substring(0, originalPath.length() - CompressorDecompressorBase.EXTENSION.length());
+ }
+ return originalPath;
+ }
+
/**
* Creates default output file path depending on the chosen program method.
*
@@ -45,6 +58,7 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
* @return Default ouput file path.
*/
private String getDefaultOutputFilePath(final String inputPath) {
+ // No default output file for custom function.
if (method == ProgramMethod.CustomFunction)
return "";
@@ -52,29 +66,35 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
final File outputFile = new File(Paths.get("").toAbsolutePath().toString(), inputFile.getName());
+ // Default value is current directory with input file name.
String defaultValue = outputFile.getAbsolutePath();
switch (method) {
case Compress: {
+ // Add compressed file extension.
defaultValue += CompressorDecompressorBase.EXTENSION;
}
break;
case Decompress: {
- if (defaultValue.toUpperCase().endsWith(CompressorDecompressorBase.EXTENSION)) {
- defaultValue = defaultValue.substring(0,
- defaultValue.length() - CompressorDecompressorBase.EXTENSION.length());
- }
+ // If it ends with QCMP file extension remove the extension.
+ defaultValue = removeQCMPFileExtension(defaultValue);
+ // Remove the old extension and add RAW extension
+ defaultValue = defaultValue.replace(FilenameUtils.getExtension(defaultValue),
+ CompressorDecompressorBase.RAW_EXTENSION_NO_DOT);
+
}
break;
case Benchmark: {
defaultValue = new File(inputFile.getParent(), "benchmark").getAbsolutePath();
}
break;
- case PrintHelp:
- break;
case InspectFile:
defaultValue += ".txt";
break;
+ case TrainCodebook:
+ case PrintHelp:
+ case CustomFunction:
+ break;
}
return defaultValue;
@@ -117,68 +137,167 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
setCodebookCacheFolder(cmd.getOptionValue(CliConstants.CODEBOOK_CACHE_FOLDER_LONG, null));
if (!parseErrorOccurred) {
- setOutputFilePath(cmd.getOptionValue(CliConstants.OUTPUT_LONG, getDefaultOutputFilePath(getInputFilePath())));
+ setOutputFilePath(cmd.getOptionValue(CliConstants.OUTPUT_LONG, getDefaultOutputFilePath(getInputFileInfo().getFilePath())));
+ setCodebookCacheFolder(cmd.getOptionValue(CliConstants.CODEBOOK_CACHE_FOLDER_LONG, null));
}
parseError = errorBuilder.toString();
}
+
/**
* Parse input file info from command line arguments.
*
- * @param errorBuilder String error builder.
- * @param fileInfo Input file info strings.
+ * @param errorBuilder String error builder.
+ * @param inputFileArguments Input file info strings.
*/
- private void parseInputFilePart(StringBuilder errorBuilder, final String[] fileInfo) {
+
+ private void parseInputFilePart(StringBuilder errorBuilder, final String[] inputFileArguments) {
+
+ if (inputFileArguments.length < 1) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Missing input file option");
+ return;
+ }
+
+ setInputFileInfo(new InputFileInfo(inputFileArguments[0]));
+
+ // Decompress and Inspect methods doesn't require additional file information.
if ((method == ProgramMethod.Decompress) || (method == ProgramMethod.InspectFile)) {
- if (fileInfo.length > 0) {
- setInputFilePath(fileInfo[0]);
- } else {
- parseErrorOccurred = true;
- errorBuilder.append("Missing input file for decompression");
- }
+ return;
+ }
+
+ // Check if input file exists.
+ if (!new File(getInputFileInfo().getFilePath()).exists()) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Input file doesn't exist.\n");
+ return;
+ }
+
+ final String extension = FilenameUtils.getExtension(inputFileArguments[0]).toLowerCase();
+ if (FileExtensions.RAW.equals(extension)) {
+ parseRawFileArguments(errorBuilder, inputFileArguments);
} else {
- // Compression part.
+ // Default loading through SCIFIO.
+ parseSCIFIOFileArguments(errorBuilder, inputFileArguments);
+ }
+ }
- // We require the file path and dimensions, like input.raw 1920x1080x5
- if (fileInfo.length < 2) {
- if (method == ProgramMethod.CustomFunction) {
- return;
- }
+
+ private void parseSCIFIOFileArguments(StringBuilder errorBuilder, final String[] inputFileArguments) {
+
+ getInputFileInfo().setIsRaw(false);
+ Reader reader;
+ try {
+ reader = ScifioWrapper.getReader(getInputFileInfo().getFilePath());
+ } catch (IOException | FormatException e) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Failed to get SCIFIO reader for file.\n");
+ errorBuilder.append(e.getMessage());
+ return;
+ }
+
+ final int imageCount = reader.getImageCount();
+ if (imageCount != 1) {
+ parseErrorOccurred = true;
+ errorBuilder.append("We are currently not supporting files with multiple images.\n");
+ return;
+ }
+
+ final long planeCount = reader.getPlaneCount(0);
+ if (planeCount > (long) Integer.MAX_VALUE) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Too many planes.\n");
+ }
+
+ long planeWidth, planeHeight;
+ try {
+ Plane plane = reader.openPlane(0, 0);
+ planeWidth = plane.getLengths()[0];
+ planeHeight = plane.getLengths()[1];
+
+ if ((planeWidth > (long) Integer.MAX_VALUE) ||
+ (planeHeight > (long) Integer.MAX_VALUE)) {
parseErrorOccurred = true;
- errorBuilder.append("Both filepath and file dimensions are required arguments\n");
- } else {
- // The first string must be file path.
- setInputFilePath(fileInfo[0]);
+ errorBuilder.append("We are currently supporting planes with maximum size of Integer.MAX_VALUE x Integer.MAX_VALUE");
+ }
- parseImageDims(fileInfo[1], errorBuilder);
- if (fileInfo.length > 2) {
+ } catch (FormatException | IOException e) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Unable to open first plane of the first image.\n")
+ .append(e.getMessage());
+ return;
+ }
- int rangeSepIndex = fileInfo[2].indexOf("-");
- if (rangeSepIndex != -1) {
- final String fromIndexString = fileInfo[2].substring(0, rangeSepIndex);
- final String toIndexString = fileInfo[2].substring(rangeSepIndex + 1);
- final ParseResult<Integer> indexFromResult = tryParseInt(fromIndexString);
- final ParseResult<Integer> indexToResult = tryParseInt(toIndexString);
+ getInputFileInfo().setDimension(new V3i(
+ (int) planeWidth,
+ (int) planeHeight,
+ (int) planeCount
+ ));
- if (indexFromResult.isSuccess() && indexToResult.isSuccess()) {
- setPlaneRange(new Interval<>(indexFromResult.getValue(), indexToResult.getValue()));
- } else {
- parseErrorOccurred = true;
- errorBuilder.append("Plane range index is wrong. Expected format D-D, got: ").append(
- fileInfo[2]).append('\n');
- }
- } else {
- final ParseResult<Integer> parseResult = tryParseInt(fileInfo[2]);
- if (parseResult.isSuccess()) {
- setPlaneIndex(parseResult.getValue());
- } else {
- parseErrorOccurred = true;
- errorBuilder.append("The second argument after file name must be plane index\n");
- }
- }
- }
+ if (inputFileArguments.length > 1) {
+ parseInputFilePlaneOptions(errorBuilder, inputFileArguments, 1);
+ }
+ }
+
+ private void parseRawFileArguments(StringBuilder errorBuilder, String[] inputFileArguments) {
+ // We require the file path and dimensions, like input.raw 1920x1080x5
+ // First argument is input file name.
+ if (inputFileArguments.length < 2) {
+ parseErrorOccurred = true;
+ errorBuilder.append("Raw file requires its dimension as additional information.")
+ .append("e.g.: 1920x1080x1\n");
+ return;
+ }
+
+ getInputFileInfo().setIsRaw(true);
+ parseImageDims(inputFileArguments[1], errorBuilder);
+
+ // User specified plane index or plane range.
+ if (inputFileArguments.length > 2) {
+ parseInputFilePlaneOptions(errorBuilder, inputFileArguments, 2);
+ }
+ }
+
+ /**
+ * Parse optional user specified plane index or plane range. (e.g. 5 or 5-50)
+ *
+ * @param errorBuilder String builder for the error message.
+ * @param inputFileArguments Input file arguments.
+ * @param inputFileArgumentsOffset Offset of the plane argument.
+ */
+ private void parseInputFilePlaneOptions(StringBuilder errorBuilder,
+ final String[] inputFileArguments,
+ final int inputFileArgumentsOffset) {
+ int rangeSeparatorIndex = inputFileArguments[inputFileArgumentsOffset].indexOf("-");
+ if (rangeSeparatorIndex != -1) {
+// Here we parse the plane range option.
+ final String fromIndexString =
+ inputFileArguments[inputFileArgumentsOffset].substring(0, rangeSeparatorIndex);
+ final String toIndexString =
+ inputFileArguments[inputFileArgumentsOffset].substring(rangeSeparatorIndex + 1);
+
+ final ParseResult<Integer> indexFromResult = tryParseInt(fromIndexString);
+ final ParseResult<Integer> indexToResult = tryParseInt(toIndexString);
+
+ if (indexFromResult.isSuccess() && indexToResult.isSuccess()) {
+ getInputFileInfo().setPlaneRange(new V2i(indexFromResult.getValue(), indexToResult.getValue()));
+ } else {
+ parseErrorOccurred = true;
+ errorBuilder.append("Plane range index is wrong. Expected format D-D, got: ").append(
+ inputFileArguments[inputFileArgumentsOffset]).append('\n');
+ }
+ } else {
+// Here we parse single plane index option.
+ final ParseResult<Integer> parseResult = tryParseInt(inputFileArguments[inputFileArgumentsOffset]);
+ if (parseResult.isSuccess()) {
+ getInputFileInfo().setPlaneIndex(parseResult.getValue());
+ } else {
+ parseErrorOccurred = true;
+ errorBuilder.append("Failed to parse plane index option, expected integer, got: ")
+ .append(inputFileArguments[inputFileArgumentsOffset])
+ .append('\n');
}
}
}
@@ -192,43 +311,42 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
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) {
+ final int firstDelimiterIndex = dimsString.indexOf('x');
+ if (firstDelimiterIndex == -1) {
parseErrorOccurred = 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 String num1String = dimsString.substring(0, firstDelimiterIndex);
+ final String secondPart = dimsString.substring(firstDelimiterIndex + 1);
- final int secondDelimIndex = secondPart.indexOf('x');
- if (secondDelimIndex == -1) {
+ final int secondDelimiterIndex = secondPart.indexOf('x');
+ if (secondDelimiterIndex == -1) {
final ParseResult<Integer> n1Result = tryParseInt(num1String);
final ParseResult<Integer> n2Result = tryParseInt(secondPart);
if (n1Result.isSuccess() && n2Result.isSuccess()) {
- setImageDimension(new V3i(n1Result.getValue(), n2Result.getValue(), 1));
+ getInputFileInfo().setDimension(new V3i(n1Result.getValue(), n2Result.getValue(), 1));
} else {
parseErrorOccurred = 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 String num2String = secondPart.substring(0, secondDelimiterIndex);
+ final String num3String = secondPart.substring(secondDelimiterIndex + 1);
final ParseResult<Integer> n1Result = tryParseInt(num1String);
final ParseResult<Integer> n2Result = tryParseInt(num2String);
final ParseResult<Integer> n3Result = tryParseInt(num3String);
if (n1Result.isSuccess() && n2Result.isSuccess() && n3Result.isSuccess()) {
- setImageDimension(new V3i(n1Result.getValue(), n2Result.getValue(), n3Result.getValue()));
+ getInputFileInfo().setDimension(new V3i(n1Result.getValue(), n2Result.getValue(), n3Result.getValue()));
} else {
parseErrorOccurred = true;
errorBuilder.append("Failed to parse image dimensions of format DxDxD, got: ");
errorBuilder.append(String.format("%sx%sx%s\n", num1String, num2String, num3String));
}
}
-
}
/**
@@ -267,7 +385,8 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
/**
* Parse compression type and vector dimensions for VQ.
- * @param cmd Command line arguments.
+ *
+ * @param cmd Command line arguments.
* @param errorBuilder String error builder.
*/
private void parseCompressionType(CommandLine cmd, StringBuilder errorBuilder) {
@@ -326,7 +445,8 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
/**
* Parse chosen program method.
- * @param cmd Command line arguments.
+ *
+ * @param cmd Command line arguments.
* @param errorBuilder String error builder.
*/
private void parseProgramMethod(CommandLine cmd, StringBuilder errorBuilder) {
@@ -352,6 +472,7 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
/**
* Try to parse int from string.
+ *
* @param string Possible integer value.
* @return Parse result.
*/
@@ -376,10 +497,6 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
return parseError;
}
- public boolean hasCodebookCacheFolder() {
- return (getCodebookCacheFolder() != null);
- }
-
public String report() {
StringBuilder sb = new StringBuilder();
@@ -425,7 +542,7 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
}
- sb.append("InputFile: ").append(getInputFilePath()).append('\n');
+ sb.append("InputFile: ").append(getInputFileInfo().getFilePath()).append('\n');
sb.append("Output: ").append(getOutputFilePath()).append('\n');
sb.append("BitsPerCodebookIndex: ").append(getBitsPerCodebookIndex()).append('\n');
if (hasCodebookCacheFolder()) {
@@ -433,18 +550,20 @@ public class ParsedCliOptions extends CompressionOptions implements Cloneable {
}
if (hasQuantizationType(method)) {
- sb.append("Input image dims: ").append(getImageDimension().toString()).append('\n');
- }
- if (getPlaneIndex() != null) {
- sb.append("PlaneIndex: ").append(getPlaneIndex()).append('\n');
+ sb.append("Input image dims: ").append(getInputFileInfo().getDimensions().toString()).append('\n');
}
+ if (getInputFileInfo().isPlaneIndexSet()) {
+ sb.append("PlaneIndex: ").append(getInputFileInfo().getPlaneIndex()).append('\n');
+ }
+
if (shouldUseMiddlePlane()) {
sb.append("Use middle plane for codebook training\n");
}
- if (isPlaneRangeSet()) {
- sb.append("FromPlaneIndex: ").append(getPlaneRange().getFrom()).append('\n');
- sb.append("ToPlaneIndex: ").append(getPlaneRange().getInclusiveTo()).append('\n');
+
+ if (getInputFileInfo().isPlaneRangeSet()) {
+ sb.append("FromPlaneIndex: ").append(getInputFileInfo().getPlaneRange().getX()).append('\n');
+ sb.append("ToPlaneIndex: ").append(getInputFileInfo().getPlaneRange().getY()).append('\n');
}
sb.append("Verbose: ").append(isVerbose()).append('\n');
diff --git a/src/main/java/azgracompress/cli/functions/EntropyCalculation.java b/src/main/java/azgracompress/cli/functions/EntropyCalculation.java
index c7285b01a2a9f6e5cdf200f67fb4b6bf67cedcb7..1528a43e0db4d98abf72c56671ed7adec9b298fe 100644
--- a/src/main/java/azgracompress/cli/functions/EntropyCalculation.java
+++ b/src/main/java/azgracompress/cli/functions/EntropyCalculation.java
@@ -20,22 +20,24 @@ public class EntropyCalculation extends CustomFunctionBase {
@Override
public boolean run() {
- ImageU16 plane = null;
- System.out.println(String.format("Input file: %s", options.getInputFilePath()));
-
- for (int planeIndex = 0; planeIndex < options.getImageDimension().getZ(); planeIndex++) {
- try {
- plane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- planeIndex);
- } catch (IOException e) {
- e.printStackTrace();
- return false;
- }
- final double planeEntropy = Utils.calculateEntropy(plane.getData());
-
- System.out.println(String.format("%d\t%.4f", planeIndex, planeEntropy));
- }
- return true;
+ // TODO(Moravec): Support PlaneLoader API.
+ return false;
+// ImageU16 plane = null;
+// System.out.println(String.format("Input file: %s", options.getInputFilePath()));
+//
+// for (int planeIndex = 0; planeIndex < options.getImageDimension().getZ(); planeIndex++) {
+// try {
+// plane = RawDataIO.loadImageU16(options.getInputFilePath(),
+// options.getImageDimension(),
+// planeIndex);
+// } catch (IOException e) {
+// e.printStackTrace();
+// return false;
+// }
+// final double planeEntropy = Utils.calculateEntropy(plane.getData());
+//
+// System.out.println(String.format("%d\t%.4f", planeIndex, planeEntropy));
+// }
+// return true;
}
}
diff --git a/src/main/java/azgracompress/cli/functions/MeasurePlaneErrorFunction.java b/src/main/java/azgracompress/cli/functions/MeasurePlaneErrorFunction.java
index cdc809747db281d71d29ba667c271752fc479a2d..6178a818d1ee266ab80b642bd07397a9e29510c2 100644
--- a/src/main/java/azgracompress/cli/functions/MeasurePlaneErrorFunction.java
+++ b/src/main/java/azgracompress/cli/functions/MeasurePlaneErrorFunction.java
@@ -1,10 +1,11 @@
package azgracompress.cli.functions;
import azgracompress.cli.CustomFunctionBase;
+import azgracompress.cli.InputFileInfo;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.data.ImageU16;
import azgracompress.data.V3i;
-import azgracompress.io.RawDataIO;
+import azgracompress.io.RawDataLoader;
import azgracompress.utilities.Utils;
import java.io.FileOutputStream;
@@ -26,13 +27,13 @@ public class MeasurePlaneErrorFunction extends CustomFunctionBase {
boolean result = true;
- result &= runPlaneDifferenceForAllBits(0, "sq", "middle_frame", "D:\\biology\\tiff_data\\quantized");
- result &= runPlaneDifferenceForAllBits(0, "vq3x3", "middle_frame", "D:\\biology\\tiff_data\\quantized");
- result &= runPlaneDifferenceForAllBits(0, "vq9x1", "middle_frame", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(0, "sq", "file_codebook", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(0, "vq3x3", "file_codebook", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(0, "vq9x1", "file_codebook", "D:\\biology\\tiff_data\\quantized");
-// result &= runPlaneDifferenceForAllBits(1, "sq", "middle_frame", "D:\\biology\\tiff_data\\quantized");
- result &= runPlaneDifferenceForAllBits(1, "vq3x3", "middle_frame", "D:\\biology\\tiff_data\\quantized");
- result &= runPlaneDifferenceForAllBits(1, "vq9x1", "middle_frame", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(1, "sq", "file_codebook", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(1, "vq3x3", "file_codebook", "D:\\biology\\tiff_data\\quantized");
+ result &= runPlaneDifferenceForAllBits(1, "vq9x1", "file_codebook", "D:\\biology\\tiff_data\\quantized");
// result &= reportPlaneDifference(
// String.format("%s\\%s\\fused_tp_10_ch_%d_16bit_%s_cb4.raw",
@@ -118,13 +119,20 @@ public class MeasurePlaneErrorFunction extends CustomFunctionBase {
}
private boolean reportPlaneDifference(final String compressedFile, final String reportFile, final String compFile) {
- final String referenceFile = compFile;
final int workerCount = 8;
final V3i dims = new V3i(1041, 996, 946);
final int planePixelCount = dims.getX() * dims.getY();
PlaneError[] planeErrors = new PlaneError[dims.getZ()];
+ InputFileInfo refFileInfo = new InputFileInfo(compFile);
+ refFileInfo.setDimension(dims);
+ InputFileInfo compFileInfo = new InputFileInfo(compressedFile);
+ compFileInfo.setDimension(dims);
+
+ final RawDataLoader refPlaneloader = new RawDataLoader(refFileInfo);
+ final RawDataLoader compPlaneloader = new RawDataLoader(compFileInfo);
+
Thread[] workers = new Thread[workerCount];
final int workSize = dims.getZ() / workerCount;
@@ -137,8 +145,8 @@ public class MeasurePlaneErrorFunction extends CustomFunctionBase {
ImageU16 originalPlane, compressedPlane, differencePlane;
for (int planeIndex = fromIndex; planeIndex < toIndex; planeIndex++) {
try {
- originalPlane = RawDataIO.loadImageU16(referenceFile, dims, planeIndex);
- compressedPlane = RawDataIO.loadImageU16(compressedFile, dims, planeIndex);
+ originalPlane = refPlaneloader.loadPlaneU16(planeIndex);
+ compressedPlane = compPlaneloader.loadPlaneU16(planeIndex);
} catch (IOException e) {
e.printStackTrace();
break;
@@ -183,4 +191,47 @@ public class MeasurePlaneErrorFunction extends CustomFunctionBase {
System.out.println("Finished reportPlaneDifference");
return true;
}
+
+ /*
+ final String[] templates = new String[]{
+ "D:\\biology\\benchmark\\jpeg_comp\\jpeg2000\\ch0_400_cr%d.%s",
+ "D:\\biology\\benchmark\\jpeg_comp\\jpeg2000\\ch1_683_cr%d.%s"
+ };
+ final String[] referenceFiles = new String[]{
+ "D:\\biology\\benchmark\\jpeg_comp\\ch0_400.raw",
+ "D:\\biology\\benchmark\\jpeg_comp\\ch1_683.raw"
+ };
+ final int[] CRS = new int[]{1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30};
+ try {
+
+ for (int i = 0; i < templates.length; i++) {
+ final String refFile = referenceFiles[i];
+ final int[] refData = loadImageData(refFile);
+
+ for (final int cr : CRS) {
+ final String inputFile = String.format(templates[i], cr, "raw");
+
+ final int[] imageData = loadImageData(inputFile);
+
+ final int[] diff = Utils.getDifference(refData, imageData);
+ final double mse = Utils.calculateMse(diff);
+
+ final double psnr = Utils.calculatePsnr(mse, U16.Max);
+
+ final String channel = new File(inputFile).getName().substring(0, 3);
+ DecimalFormat df = new DecimalFormat("#.####");
+
+ System.out.println(String.format("%s CR: %d\n\tMSE: %s\n\tPSNR: %s\n",
+ channel, cr, df.format(mse), df.format(psnr)));
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ ScifioWrapper.dispose();
+ }
+ * */
+
+
}
diff --git a/src/main/java/azgracompress/compression/CompressionOptions.java b/src/main/java/azgracompress/compression/CompressionOptions.java
index ac772956d1c576ac70877c252ce6a5e9d716f6f3..48d502b72c1a268daf3bd23c9f418d2a22a9e70f 100644
--- a/src/main/java/azgracompress/compression/CompressionOptions.java
+++ b/src/main/java/azgracompress/compression/CompressionOptions.java
@@ -1,5 +1,6 @@
package azgracompress.compression;
+import azgracompress.cli.InputFileInfo;
import azgracompress.data.V2i;
import azgracompress.data.V3i;
import azgracompress.fileformat.QuantizationType;
@@ -11,7 +12,7 @@ public class CompressionOptions {
/**
* Input image or compressed file.
*/
- private String inputFilePath;
+ private InputFileInfo inputFileInfo;
/**
* Output image or compressed file.
@@ -91,12 +92,12 @@ public class CompressionOptions {
return verbose;
}
- public String getInputFilePath() {
- return inputFilePath;
+ public InputFileInfo getInputFileInfo() {
+ return inputFileInfo;
}
- public void setInputFilePath(String inputFilePath) {
- this.inputFilePath = inputFilePath;
+ public void setInputFileInfo(InputFileInfo ifi) {
+ this.inputFileInfo = ifi;
}
public String getOutputFilePath() {
diff --git a/src/main/java/azgracompress/compression/CompressorDecompressorBase.java b/src/main/java/azgracompress/compression/CompressorDecompressorBase.java
index 896222dd24305a7b36f70e7baa27df5756d99bb1..0aab879ba9e82680abaa750efa5467d7d462b51e 100644
--- a/src/main/java/azgracompress/compression/CompressorDecompressorBase.java
+++ b/src/main/java/azgracompress/compression/CompressorDecompressorBase.java
@@ -1,5 +1,6 @@
package azgracompress.compression;
+import azgracompress.cli.InputFileInfo;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.exception.ImageCompressionException;
import azgracompress.huffman.Huffman;
@@ -10,6 +11,7 @@ import java.io.DataOutputStream;
public abstract class CompressorDecompressorBase {
public static final int LONG_BYTES = 8;
public static final String EXTENSION = ".QCMP";
+ public static final String RAW_EXTENSION_NO_DOT = "raw";
protected final CompressionOptions options;
private final int codebookSize;
@@ -48,11 +50,14 @@ public abstract class CompressorDecompressorBase {
}
protected int[] getPlaneIndicesForCompression() {
- if (options.isPlaneIndexSet()) {
- return new int[]{options.getPlaneIndex()};
- } else if (options.isPlaneRangeSet()) {
- final int from = options.getPlaneRange().getFrom();
- final int count = options.getPlaneRange().getInclusiveTo() - from;
+
+ final InputFileInfo ifi = options.getInputFileInfo();
+ if (ifi.isPlaneIndexSet()) {
+ return new int[]{ifi.getPlaneIndex()};
+ } else if (ifi.isPlaneRangeSet()) {
+ final int from = ifi.getPlaneRange().getX();
+ final int to = ifi.getPlaneRange().getY();
+ final int count = to - from;
int[] indices = new int[count + 1];
for (int i = 0; i <= count; i++) {
@@ -60,7 +65,7 @@ public abstract class CompressorDecompressorBase {
}
return indices;
} else {
- return generateAllPlaneIndices(options.getImageDimension().getZ());
+ return generateAllPlaneIndices(ifi.getDimensions().getZ());
}
}
@@ -101,7 +106,7 @@ public abstract class CompressorDecompressorBase {
* @return Index of the middle plane.
*/
protected int getMiddlePlaneIndex() {
- return (options.getImageDimension().getZ() / 2);
+ return (options.getInputFileInfo().getDimensions().getZ() / 2);
}
/**
diff --git a/src/main/java/azgracompress/compression/ImageCompressor.java b/src/main/java/azgracompress/compression/ImageCompressor.java
index 82676671154df6f810a8313c721c01c8e710b9d3..bff5a843fa0b7dc672777fe62c2dc0ce2a63e27f 100644
--- a/src/main/java/azgracompress/compression/ImageCompressor.java
+++ b/src/main/java/azgracompress/compression/ImageCompressor.java
@@ -145,9 +145,9 @@ public class ImageCompressor extends CompressorDecompressorBase {
final boolean oneCodebook = options.shouldUseMiddlePlane() || options.hasCodebookCacheFolder();
header.setCodebookPerPlane(!oneCodebook);
- header.setImageSizeX(options.getImageDimension().getX());
- header.setImageSizeY(options.getImageDimension().getY());
- header.setImageSizeZ(getNumberOfPlanes());
+ header.setImageSizeX(options.getInputFileInfo().getDimensions().getX());
+ header.setImageSizeY(options.getInputFileInfo().getDimensions().getY());
+ header.setImageSizeZ(options.getInputFileInfo().getNumberOfPlanes());
header.setVectorDimension(options.getVectorDimension());
diff --git a/src/main/java/azgracompress/compression/ImageDecompressor.java b/src/main/java/azgracompress/compression/ImageDecompressor.java
index ded4d46557e7069933cdf0339f9693d2e0886450..359679a07c8bd391e9d4e7fe4f5b3d94c9b3e9e6 100644
--- a/src/main/java/azgracompress/compression/ImageDecompressor.java
+++ b/src/main/java/azgracompress/compression/ImageDecompressor.java
@@ -61,7 +61,7 @@ public class ImageDecompressor extends CompressorDecompressorBase {
boolean validFile = true;
QCMPFileHeader header = null;
- try (FileInputStream fileInputStream = new FileInputStream(options.getInputFilePath());
+ try (FileInputStream fileInputStream = new FileInputStream(options.getInputFileInfo().getFilePath());
DataInputStream dataInputStream = new DataInputStream(fileInputStream)) {
header = readQCMPFileHeader(dataInputStream);
} catch (IOException ioEx) {
@@ -113,10 +113,9 @@ public class ImageDecompressor extends CompressorDecompressorBase {
logBuilder.append("Vector size Y:\t\t").append(header.getVectorSizeY()).append('\n');
logBuilder.append("Vector size Z:\t\t").append(header.getVectorSizeZ()).append('\n');
- final long fileSize = new File(options.getInputFilePath()).length();
final long headerSize = header.getHeaderSize();
- final long dataSize = fileSize - headerSize;
-
+ final long fileSize = new File(options.getInputFileInfo().getFilePath()).length();
+ final long dataSize = fileSize - header.getHeaderSize();
final IImageDecompressor decompressor = getImageDecompressor(header);
@@ -164,10 +163,9 @@ public class ImageDecompressor extends CompressorDecompressorBase {
}
public boolean decompress() {
-
final Stopwatch decompressionStopwatch = Stopwatch.startNew();
final long decompressedFileSize;
- try (FileInputStream fileInputStream = new FileInputStream(options.getInputFilePath());
+ try (FileInputStream fileInputStream = new FileInputStream(options.getInputFileInfo().getFilePath());
DataInputStream dataInputStream = new DataInputStream(fileInputStream)) {
final QCMPFileHeader header = readQCMPFileHeader(dataInputStream);
@@ -187,7 +185,7 @@ public class ImageDecompressor extends CompressorDecompressorBase {
return false;
}
- final long fileSize = new File(options.getInputFilePath()).length();
+ final long fileSize = new File(options.getInputFileInfo().getFilePath()).length();
final long dataSize = fileSize - header.getHeaderSize();
final long expectedDataSize = imageDecompressor.getExpectedDataSize(header);
if (dataSize != expectedDataSize) {
diff --git a/src/main/java/azgracompress/compression/SQImageCompressor.java b/src/main/java/azgracompress/compression/SQImageCompressor.java
index a73f3a9542431905076deb24bd918c8b84df77fe..2ce6e59b4798d0b1fa7008e9ae0c397f4fb73c30 100644
--- a/src/main/java/azgracompress/compression/SQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/SQImageCompressor.java
@@ -2,10 +2,13 @@ package azgracompress.compression;
import azgracompress.U16;
import azgracompress.cache.QuantizationCacheManager;
+import azgracompress.cli.InputFileInfo;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.exception.ImageCompressionException;
import azgracompress.data.ImageU16;
import azgracompress.huffman.Huffman;
+import azgracompress.io.IPlaneLoader;
+import azgracompress.io.PlaneLoaderFactory;
import azgracompress.io.RawDataIO;
import azgracompress.quantization.scalar.LloydMaxU16ScalarQuantization;
import azgracompress.quantization.scalar.SQCodebook;
@@ -72,7 +75,8 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
private ScalarQuantizer loadQuantizerFromCache() throws ImageCompressionException {
QuantizationCacheManager cacheManager = new QuantizationCacheManager(options.getCodebookCacheFolder());
- final SQCodebook codebook = cacheManager.loadSQCodebook(options.getInputFilePath(), getCodebookSize());
+ final SQCodebook codebook = cacheManager.loadSQCodebook(options.getInputFileInfo().getFilePath(),
+ getCodebookSize());
if (codebook == null) {
throw new ImageCompressionException("Failed to read quantization values from cache file.");
}
@@ -86,8 +90,17 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
* @throws ImageCompressionException When compress process fails.
*/
public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
+ final InputFileInfo inputFileInfo = options.getInputFileInfo();
Stopwatch stopwatch = new Stopwatch();
final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.shouldUseMiddlePlane();
+
+ final IPlaneLoader planeLoader;
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputFileInfo);
+ } catch (Exception e) {
+ throw new ImageCompressionException("Unable to create SCIFIO reader. " + e.getMessage());
+ }
+
ScalarQuantizer quantizer = null;
Huffman huffman = null;
final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize());
@@ -104,11 +117,10 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
ImageU16 middlePlane = null;
final int middlePlaneIndex = getMiddlePlaneIndex();
try {
- middlePlane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- getMiddlePlaneIndex());
- } catch (Exception ex) {
- throw new ImageCompressionException("Unable to load plane data.", ex);
+
+ middlePlane = planeLoader.loadPlaneU16(middlePlaneIndex);
+ } catch (IOException ex) {
+ throw new ImageCompressionException("Unable to load middle plane data.", ex);
}
Log(String.format("Training scalar quantizer from middle plane %d.", middlePlaneIndex));
@@ -130,10 +142,9 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
ImageU16 plane = null;
try {
- plane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- planeIndex);
- } catch (Exception ex) {
+
+ plane = planeLoader.loadPlaneU16(planeIndex);
+ } catch (IOException ex) {
throw new ImageCompressionException("Unable to load plane data.", ex);
}
@@ -162,21 +173,27 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
}
private int[] loadConfiguredPlanesData() throws ImageCompressionException {
+ final InputFileInfo inputFileInfo = options.getInputFileInfo();
+ final IPlaneLoader planeLoader;
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputFileInfo);
+ } catch (Exception e) {
+ throw new ImageCompressionException("Unable to create SCIFIO reader. " + e.getMessage());
+ }
int[] trainData = null;
- if (options.isPlaneIndexSet()) {
+
+ if (inputFileInfo.isPlaneIndexSet()) {
try {
Log("Loading single plane data.");
- trainData = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- options.getPlaneIndex()).getData();
+ trainData = planeLoader.loadPlaneU16(inputFileInfo.getPlaneIndex()).getData();
} catch (IOException e) {
throw new ImageCompressionException("Failed to load plane data.", e);
}
- } else if (options.isPlaneRangeSet()) {
+ } else if (inputFileInfo.isPlaneRangeSet()) {
Log("Loading plane range data.");
final int[] planes = getPlaneIndicesForCompression();
try {
- trainData = RawDataIO.loadPlanesData(options.getInputFilePath(), options.getImageDimension(), planes);
+ trainData = planeLoader.loadPlanesU16Data(planes);
} catch (IOException e) {
e.printStackTrace();
throw new ImageCompressionException("Failed to load plane range data.", e);
@@ -184,7 +201,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
} else {
Log("Loading all planes data.");
try {
- trainData = RawDataIO.loadAllPlanesData(options.getInputFilePath(), options.getImageDimension());
+ trainData = planeLoader.loadAllPlanesU16Data();
} catch (IOException e) {
throw new ImageCompressionException("Failed to load all planes data.", e);
}
@@ -202,13 +219,12 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
Log("Starting LloydMax training.");
lloydMax.train(options.isVerbose());
final SQCodebook codebook = lloydMax.getCodebook();
- final int[] qValues = codebook.getCentroids();
Log("Finished LloydMax training.");
Log(String.format("Saving cache file to %s", options.getOutputFilePath()));
QuantizationCacheManager cacheManager = new QuantizationCacheManager(options.getOutputFilePath());
try {
- cacheManager.saveCodebook(options.getInputFilePath(), codebook);
+ cacheManager.saveCodebook(options.getInputFileInfo().getFilePath(), codebook);
} catch (IOException e) {
throw new ImageCompressionException("Unable to write cache.", e);
}
diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java
index 7365bf2eecb11935696f3e392047d74385de3483..14f0dc91801f6e4db1ee2bf1447a35ba84c675e1 100644
--- a/src/main/java/azgracompress/compression/VQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/VQImageCompressor.java
@@ -1,12 +1,14 @@
package azgracompress.compression;
import azgracompress.cache.QuantizationCacheManager;
+import azgracompress.cli.InputFileInfo;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.exception.ImageCompressionException;
import azgracompress.data.Chunk2D;
import azgracompress.data.ImageU16;
import azgracompress.huffman.Huffman;
-import azgracompress.io.RawDataIO;
+import azgracompress.io.IPlaneLoader;
+import azgracompress.io.PlaneLoaderFactory;
import azgracompress.quantization.vector.*;
import azgracompress.utilities.Stopwatch;
@@ -71,11 +73,13 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
* @throws ImageCompressionException when fails to read cached codebook.
*/
private VectorQuantizer loadQuantizerFromCache() throws ImageCompressionException {
+
QuantizationCacheManager cacheManager = new QuantizationCacheManager(options.getCodebookCacheFolder());
- final VQCodebook codebook = cacheManager.loadVQCodebook(options.getInputFilePath(),
- getCodebookSize(),
- options.getVectorDimension().toV3i());
+
+ final VQCodebook codebook = cacheManager.loadVQCodebook(options.getInputFileInfo().getFilePath(),
+ getCodebookSize(),
+ options.getVectorDimension().toV3i());
if (codebook == null) {
throw new ImageCompressionException("Failed to read quantization vectors from cache.");
}
@@ -89,12 +93,16 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
* @throws ImageCompressionException When compress process fails.
*/
public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
-
+ final InputFileInfo inputFileInfo = options.getInputFileInfo();
Stopwatch stopwatch = new Stopwatch();
final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.shouldUseMiddlePlane();
-
-
+ final IPlaneLoader planeLoader;
final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize());
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputFileInfo);
+ } catch (Exception e) {
+ throw new ImageCompressionException("Unable to create SCIFIO reader. " + e.getMessage());
+ }
VectorQuantizer quantizer = null;
Huffman huffman = null;
@@ -110,11 +118,10 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
final int middlePlaneIndex = getMiddlePlaneIndex();
ImageU16 middlePlane = null;
try {
- middlePlane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- middlePlaneIndex);
- } catch (Exception ex) {
- throw new ImageCompressionException("Unable to load plane data.", ex);
+
+ middlePlane = planeLoader.loadPlaneU16(middlePlaneIndex);
+ } catch (IOException ex) {
+ throw new ImageCompressionException("Unable to load reference plane data.", ex);
}
Log(String.format("Training vector quantizer from middle plane %d.", middlePlaneIndex));
@@ -136,10 +143,9 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
ImageU16 plane = null;
try {
- plane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- planeIndex);
- } catch (Exception ex) {
+
+ plane = planeLoader.loadPlaneU16(planeIndex);
+ } catch (IOException ex) {
throw new ImageCompressionException("Unable to load plane data.", ex);
}
@@ -175,33 +181,39 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
* @return Quantization vectors of configured quantization.
* @throws IOException When reading fails.
*/
- private int[][] loadPlaneQuantizationVectors(final int planeIndex) throws IOException {
- ImageU16 refPlane = RawDataIO.loadImageU16(options.getInputFilePath(),
- options.getImageDimension(),
- planeIndex);
+ private int[][] loadPlaneQuantizationVectors(final IPlaneLoader planeLoader,
+ final int planeIndex) throws IOException {
+ ImageU16 refPlane = planeLoader.loadPlaneU16(planeIndex);
return refPlane.toQuantizationVectors(options.getVectorDimension());
}
private int[][] loadConfiguredPlanesData() throws ImageCompressionException {
final int vectorSize = options.getVectorDimension().getX() * options.getVectorDimension().getY();
+ final InputFileInfo inputFileInfo = options.getInputFileInfo();
+ final IPlaneLoader planeLoader;
+ try {
+ planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputFileInfo);
+ } catch (Exception e) {
+ throw new ImageCompressionException("Unable to create SCIFIO reader. " + e.getMessage());
+ }
int[][] trainData = null;
Stopwatch s = new Stopwatch();
s.start();
- if (options.isPlaneIndexSet()) {
+ if (inputFileInfo.isPlaneIndexSet()) {
Log("VQ: Loading single plane data.");
try {
- trainData = loadPlaneQuantizationVectors(options.getPlaneIndex());
+
+ trainData = loadPlaneQuantizationVectors(planeLoader, inputFileInfo.getPlaneIndex());
} catch (IOException e) {
throw new ImageCompressionException("Failed to load plane data.", e);
}
} else {
-
- Log((options.isPlaneRangeSet()) ? "VQ: Loading plane range data." : "VQ: Loading all planes data.");
+ Log(inputFileInfo.isPlaneRangeSet() ? "VQ: Loading plane range data." : "VQ: Loading all planes data.");
final int[] planeIndices = getPlaneIndicesForCompression();
final int chunkCountPerPlane = Chunk2D.calculateRequiredChunkCountPerPlane(
- options.getImageDimension().toV2i(),
+ inputFileInfo.getDimensions().toV2i(),
options.getVectorDimension());
final int totalChunkCount = chunkCountPerPlane * planeIndices.length;
@@ -211,14 +223,18 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
int planeCounter = 0;
for (final int planeIndex : planeIndices) {
try {
- planeVectors = loadPlaneQuantizationVectors(planeIndex);
+ planeVectors = loadPlaneQuantizationVectors(planeLoader, planeIndex);
assert (planeVectors.length == chunkCountPerPlane) : "Wrong chunk count per plane";
} catch (IOException e) {
throw new ImageCompressionException(String.format("Failed to load plane %d image data.",
planeIndex), e);
}
- System.arraycopy(planeVectors, 0, trainData, (planeCounter * chunkCountPerPlane), chunkCountPerPlane);
+ System.arraycopy(planeVectors,
+ 0,
+ trainData,
+ (planeCounter * chunkCountPerPlane),
+ chunkCountPerPlane);
++planeCounter;
}
}
@@ -243,7 +259,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
Log("Saving cache file to %s", options.getOutputFilePath());
QuantizationCacheManager cacheManager = new QuantizationCacheManager(options.getOutputFilePath());
try {
- cacheManager.saveCodebook(options.getInputFilePath(), lbgResult.getCodebook());
+ cacheManager.saveCodebook(options.getInputFileInfo().getFilePath(), lbgResult.getCodebook());
} catch (IOException e) {
throw new ImageCompressionException("Unable to write VQ cache.", e);
}
diff --git a/src/main/java/azgracompress/data/ImageU16.java b/src/main/java/azgracompress/data/ImageU16.java
index ddd7b4e1dd34ffeb8ad2df6e89130aa836ea53ca..8c5f7a53020ebcab62013f79f6e8e7e6608d9b5a 100644
--- a/src/main/java/azgracompress/data/ImageU16.java
+++ b/src/main/java/azgracompress/data/ImageU16.java
@@ -8,13 +8,17 @@ public class ImageU16 {
private final int height;
private int[] data;
- public ImageU16(int width, int height, int[] data) {
+ public ImageU16(final int width, final int height, final int[] data) {
assert ((width * height) == data.length) : "Wrong data size in ImageU16 constructor.";
this.width = width;
this.height = height;
this.data = data;
}
+ public ImageU16(final V2i dims, final int[] data) {
+ this(dims.getX(), dims.getY(), data);
+ }
+
private int index(final int x, final int y) {
assert ((x >= 0 && x < height) && (y >= 0 && y < width)) : "Index out of bounds";
return (x * width) + y;
diff --git a/src/main/java/azgracompress/data/V2i.java b/src/main/java/azgracompress/data/V2i.java
index 9df55d73cac1579deff4411b82a497f0b5c583ba..4e888935ec9ba8bac6cd3ddd54e661f3e8bac203 100644
--- a/src/main/java/azgracompress/data/V2i.java
+++ b/src/main/java/azgracompress/data/V2i.java
@@ -13,9 +13,7 @@ public class V2i {
this(universalValue, universalValue);
}
- public int getX() {
- return x;
- }
+ public int getX() { return x; }
public int getY() {
return y;
diff --git a/src/main/java/azgracompress/fileformat/FileExtensions.java b/src/main/java/azgracompress/fileformat/FileExtensions.java
new file mode 100644
index 0000000000000000000000000000000000000000..07f171f6914e3f9a36c7671cbd2f81df1a592485
--- /dev/null
+++ b/src/main/java/azgracompress/fileformat/FileExtensions.java
@@ -0,0 +1,7 @@
+package azgracompress.fileformat;
+
+public class FileExtensions {
+ public static final String RAW = "raw";
+ public static final String QCMP = "qcmp";
+
+}
diff --git a/src/main/java/azgracompress/io/IPlaneLoader.java b/src/main/java/azgracompress/io/IPlaneLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..45026b5e04b3f62f24b9403b438e9e7ce9e056a9
--- /dev/null
+++ b/src/main/java/azgracompress/io/IPlaneLoader.java
@@ -0,0 +1,14 @@
+package azgracompress.io;
+
+import azgracompress.cli.InputFileInfo;
+import azgracompress.data.ImageU16;
+
+import java.io.IOException;
+
+public interface IPlaneLoader {
+ ImageU16 loadPlaneU16(final int plane) throws IOException;
+
+ int[] loadPlanesU16Data(int[] planes) throws IOException;
+
+ int[] loadAllPlanesU16Data() throws IOException;
+}
diff --git a/src/main/java/azgracompress/io/PlaneLoaderFactory.java b/src/main/java/azgracompress/io/PlaneLoaderFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb9b77b52b5e56db8f7466ab6048ee286261b168
--- /dev/null
+++ b/src/main/java/azgracompress/io/PlaneLoaderFactory.java
@@ -0,0 +1,21 @@
+package azgracompress.io;
+
+import azgracompress.cli.InputFileInfo;
+
+public final class PlaneLoaderFactory {
+
+ /**
+ * Create concrete plane loader for the input file.
+ *
+ * @param inputFileInfo Input file information.
+ * @return Concrete plane loader.
+ * @throws Exception When fails to create plane loader.
+ */
+ public static IPlaneLoader getPlaneLoaderForInputFile(final InputFileInfo inputFileInfo) throws Exception {
+ if (inputFileInfo.isRAW()) {
+ return new RawDataLoader(inputFileInfo);
+ } else {
+ return new SCIFIOLoader(inputFileInfo);
+ }
+ }
+}
diff --git a/src/main/java/azgracompress/io/RawDataIO.java b/src/main/java/azgracompress/io/RawDataIO.java
index 5cf711f0271b8642282e2343d0eef24533218d9f..402df89490ae1281c60b4814598821c3f4684e18 100644
--- a/src/main/java/azgracompress/io/RawDataIO.java
+++ b/src/main/java/azgracompress/io/RawDataIO.java
@@ -1,119 +1,12 @@
package azgracompress.io;
import azgracompress.data.ImageU16;
-import azgracompress.data.V3i;
import azgracompress.utilities.TypeConverter;
-import java.io.*;
-import java.util.Arrays;
+import java.io.FileOutputStream;
+import java.io.IOException;
public class RawDataIO {
- /**
- * Load single U16 image from RAW data file.
- *
- * @param rawFile Path to the raw file.
- * @param rawDataDimension X (Width), Y (Height) of plane and Z(Number of planes)
- * @param plane Plane index.
- * @return U16 image specified by the plane
- */
- public static ImageU16 loadImageU16(final String rawFile,
- final V3i rawDataDimension,
- final int plane) throws IOException {
-
- byte[] buffer;
- try (FileInputStream fileStream = new FileInputStream(rawFile)) {
- final long planeSize = (long) rawDataDimension.getX() * (long) rawDataDimension.getY() * 2;
- final long expectedFileSize = planeSize * rawDataDimension.getZ();
- final long fileSize = fileStream.getChannel().size();
-
-
- if (expectedFileSize != fileSize) {
- throw new IOException(
- "File specified by `rawFile` doesn't contains raw data for image of dimensions " +
- "`rawDataDimension`");
- }
-
- final long planeOffset = plane * planeSize;
-
- buffer = new byte[(int) planeSize];
- if (fileStream.skip(planeOffset) != planeOffset) {
- throw new IOException("Failed to skip.");
- }
- if (fileStream.read(buffer, 0, (int) planeSize) != planeSize) {
- throw new IOException("Read wrong number of bytes.");
- }
- }
-
- return new ImageU16(rawDataDimension.getX(),
- rawDataDimension.getY(),
- TypeConverter.unsignedShortBytesToIntArray(buffer));
- }
-
- public static int[] loadPlanesData(final String rawFile,
- final V3i rawDataDims,
- int[] planes) throws IOException {
-
- if (planes.length < 1)
- return new int[0];
-
- final int planeValueCount = rawDataDims.getX() * rawDataDims.getY();
- final long planeDataSize = 2 * (long) planeValueCount;
-
- final long totalValueCount = (long) planeValueCount * planes.length;
- int[] values = new int[(int) totalValueCount];
-
-
- if (totalValueCount > (long) Integer.MAX_VALUE) {
- throw new IOException("Integer count is too big.");
- }
-
- Arrays.sort(planes);
-
- try (FileInputStream fileStream = new FileInputStream(rawFile);
- DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) {
-
- int lastIndex = 0;
- int valIndex = 0;
-
- for (final int planeIndex : planes) {
- // Skip specific number of bytes to get to the next plane.
- final int requestedSkip = (planeIndex == 0) ? 0 : ((planeIndex - lastIndex) - 1) * (int) planeDataSize;
- lastIndex = planeIndex;
-
- final int actualSkip = dis.skipBytes(requestedSkip);
- if (requestedSkip != actualSkip) {
- throw new IOException("Skip operation failed.");
- }
-
- for (int i = 0; i < planeValueCount; i++) {
- values[valIndex++] = dis.readUnsignedShort();
- }
-
- }
- }
-
- return values;
- }
-
- public static int[] loadAllPlanesData(final String rawFile, final V3i imageDims) throws IOException {
-
- final long dataSize = (long) imageDims.getX() * (long) imageDims.getY() * (long) imageDims.getZ();
- int[] values = new int[(int) dataSize];
-
- if (dataSize > (long) Integer.MAX_VALUE) {
- throw new IOException("RawFile size is too big.");
- }
-
- try (FileInputStream fileStream = new FileInputStream(rawFile);
- DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) {
-
- for (int i = 0; i < (int) dataSize; i++) {
- values[i] = dis.readUnsignedShort();
- }
- }
-
- return values;
- }
public static void writeImageU16(final String rawFile,
final ImageU16 image,
diff --git a/src/main/java/azgracompress/io/RawDataLoader.java b/src/main/java/azgracompress/io/RawDataLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..69e7ca33f34a4650c03fc33906ae47c3bf7342a9
--- /dev/null
+++ b/src/main/java/azgracompress/io/RawDataLoader.java
@@ -0,0 +1,119 @@
+package azgracompress.io;
+
+import azgracompress.cli.InputFileInfo;
+import azgracompress.data.ImageU16;
+import azgracompress.data.V3i;
+import azgracompress.utilities.TypeConverter;
+
+import java.io.*;
+import java.util.Arrays;
+
+public class RawDataLoader implements IPlaneLoader {
+ private final InputFileInfo inputFileInfo;
+
+ public RawDataLoader(final InputFileInfo inputFileInfo) {
+ this.inputFileInfo = inputFileInfo;
+ }
+
+ @Override
+ public ImageU16 loadPlaneU16(int plane) throws IOException {
+ byte[] buffer;
+ final V3i rawDataDimension = inputFileInfo.getDimensions();
+
+ try (FileInputStream fileStream = new FileInputStream(inputFileInfo.getFilePath())) {
+ final long planeSize = (long) rawDataDimension.getX() * (long) rawDataDimension.getY() * 2;
+ final long expectedFileSize = planeSize * rawDataDimension.getZ();
+ final long fileSize = fileStream.getChannel().size();
+
+
+ if (expectedFileSize != fileSize) {
+ throw new IOException(
+ "File specified by `rawFile` doesn't contains raw data for image of dimensions " +
+ "`rawDataDimension`");
+ }
+
+ final long planeOffset = plane * planeSize;
+
+ buffer = new byte[(int) planeSize];
+ if (fileStream.skip(planeOffset) != planeOffset) {
+ throw new IOException("Failed to skip.");
+ }
+ if (fileStream.read(buffer, 0, (int) planeSize) != planeSize) {
+ throw new IOException("Read wrong number of bytes.");
+ }
+ }
+
+ return new ImageU16(rawDataDimension.getX(),
+ rawDataDimension.getY(),
+ TypeConverter.unsignedShortBytesToIntArray(buffer));
+ }
+
+ @Override
+ public int[] loadPlanesU16Data(int[] planes) throws IOException {
+ if (planes.length < 1) {
+ return new int[0];
+ } else if (planes.length == 1) {
+ return loadPlaneU16(planes[0]).getData();
+ }
+
+ final int planeValueCount = inputFileInfo.getDimensions().getX() * inputFileInfo.getDimensions().getY();
+ final long planeDataSize = 2 * (long) planeValueCount;
+
+ final long totalValueCount = (long) planeValueCount * planes.length;
+
+ if (totalValueCount > (long) Integer.MAX_VALUE) {
+ throw new IOException("Integer count is too big.");
+ }
+
+ int[] values = new int[(int) totalValueCount];
+
+ Arrays.sort(planes);
+
+ try (FileInputStream fileStream = new FileInputStream(inputFileInfo.getFilePath());
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) {
+
+ int lastIndex = 0;
+ int valIndex = 0;
+
+ for (final int planeIndex : planes) {
+ // Skip specific number of bytes to get to the next plane.
+ final int requestedSkip = (planeIndex == 0) ? 0 : ((planeIndex - lastIndex) - 1) * (int) planeDataSize;
+ lastIndex = planeIndex;
+
+ final int actualSkip = dis.skipBytes(requestedSkip);
+ if (requestedSkip != actualSkip) {
+ throw new IOException("Skip operation failed.");
+ }
+
+ for (int i = 0; i < planeValueCount; i++) {
+ values[valIndex++] = dis.readUnsignedShort();
+ }
+
+ }
+ }
+
+ return values;
+ }
+
+ @Override
+ public int[] loadAllPlanesU16Data() throws IOException {
+ final V3i imageDims = inputFileInfo.getDimensions();
+ final long dataSize = (long) imageDims.getX() * (long) imageDims.getY() * (long) imageDims.getZ();
+
+ if (dataSize > (long) Integer.MAX_VALUE) {
+ throw new IOException("RawFile size is too big.");
+ }
+
+ int[] values = new int[(int) dataSize];
+
+ try (FileInputStream fileStream = new FileInputStream(inputFileInfo.getFilePath());
+ DataInputStream dis = new DataInputStream(new BufferedInputStream(fileStream, 8192))) {
+
+ for (int i = 0; i < (int) dataSize; i++) {
+ values[i] = dis.readUnsignedShort();
+ }
+ }
+
+ return values;
+ }
+}
diff --git a/src/main/java/azgracompress/io/SCIFIOLoader.java b/src/main/java/azgracompress/io/SCIFIOLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ca3d1c36a9f058d4936da990eb4a409ce0b379e
--- /dev/null
+++ b/src/main/java/azgracompress/io/SCIFIOLoader.java
@@ -0,0 +1,108 @@
+package azgracompress.io;
+
+import azgracompress.ScifioWrapper;
+import azgracompress.cli.InputFileInfo;
+import azgracompress.data.ImageU16;
+import azgracompress.data.V3i;
+import azgracompress.utilities.TypeConverter;
+import io.scif.FormatException;
+import io.scif.Reader;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class SCIFIOLoader implements IPlaneLoader {
+
+ private final InputFileInfo inputFileInfo;
+ private final Reader reader;
+
+ /**
+ * Create SCIFIO reader from input file.
+ *
+ * @param inputFileInfo Input file info.
+ * @throws IOException When fails to create SCIFIO reader.
+ * @throws FormatException When fails to create SCIFIO reader.
+ */
+ public SCIFIOLoader(final InputFileInfo inputFileInfo) throws IOException, FormatException {
+ this.inputFileInfo = inputFileInfo;
+ this.reader = ScifioWrapper.getReader(this.inputFileInfo.getFilePath());
+ }
+
+ @Override
+ public ImageU16 loadPlaneU16(int plane) throws IOException {
+ byte[] planeBytes;
+ try {
+ planeBytes = reader.openPlane(0, plane).getBytes();
+ } catch (FormatException e) {
+ throw new IOException("Unable to open plane with the reader. " + e.getMessage());
+ }
+ final int[] data = TypeConverter.unsignedShortBytesToIntArray(planeBytes);
+ return new ImageU16(inputFileInfo.getDimensions().toV2i(), data);
+ }
+
+ @SuppressWarnings("DuplicatedCode")
+ @Override
+ public int[] loadPlanesU16Data(int[] planes) throws IOException {
+ if (planes.length < 1) {
+ return new int[0];
+ } else if (planes.length == 1) {
+ return loadPlaneU16(planes[0]).getData();
+ }
+
+ final int planeValueCount = inputFileInfo.getDimensions().getX() * inputFileInfo.getDimensions().getY();
+ final long planeDataSize = 2 * (long) planeValueCount;
+
+ final long totalValueCount = (long) planeValueCount * planes.length;
+
+ if (totalValueCount > (long) Integer.MAX_VALUE) {
+ throw new IOException("Integer count is too big.");
+ }
+
+ int[] values = new int[(int) totalValueCount];
+ Arrays.sort(planes);
+
+ byte[] planeBytes;
+ for (int i = 0; i < planes.length; i++) {
+ final int plane = planes[i];
+
+ try {
+ planeBytes = reader.openPlane(0, plane).getBytes();
+ } catch (FormatException e) {
+ throw new IOException("Unable to open plane.");
+ }
+ if (planeBytes.length != planeDataSize) {
+ throw new IOException("Bad byte count read from plane.");
+ }
+
+ TypeConverter.unsignedShortBytesToIntArray(planeBytes, values, (i * planeValueCount));
+ }
+ return values;
+ }
+
+ @Override
+ public int[] loadAllPlanesU16Data() throws IOException {
+ final V3i imageDims = inputFileInfo.getDimensions();
+ final long planePixelCount = (long) imageDims.getX() * (long) imageDims.getY();
+ final long dataSize = planePixelCount * (long) imageDims.getZ();
+
+ if (dataSize > (long) Integer.MAX_VALUE) {
+ throw new IOException("FileSize is too big.");
+ }
+
+ int[] values = new int[(int) dataSize];
+ byte[] planeBytes;
+ for (int plane = 0; plane < imageDims.getZ(); plane++) {
+ try {
+ planeBytes = reader.openPlane(0, plane).getBytes();
+ } catch (FormatException e) {
+ throw new IOException("Unable to open plane.");
+ }
+ if (planeBytes.length != 2 * planePixelCount) {
+ throw new IOException("Bad byte count read from plane.");
+ }
+ TypeConverter.unsignedShortBytesToIntArray(planeBytes, values, (int) (plane * planePixelCount));
+ }
+
+ return values;
+ }
+}
diff --git a/src/main/java/azgracompress/utilities/TypeConverter.java b/src/main/java/azgracompress/utilities/TypeConverter.java
index 09f8afe7cc506459c7e687bbb7fc9d11e914c67a..4f8ec2d16f5551907e093b0f38b183b9d4938c69 100644
--- a/src/main/java/azgracompress/utilities/TypeConverter.java
+++ b/src/main/java/azgracompress/utilities/TypeConverter.java
@@ -80,4 +80,20 @@ public class TypeConverter {
}
return buffer;
}
+
+ /**
+ * Convert unsigned short bytes to integers. Place integers into values array from offset position.
+ *
+ * @param bytes Unsigned short bytes.
+ * @param values Integer value buffer.
+ * @param offset Offset into integer array.
+ */
+ public static void unsignedShortBytesToIntArray(final byte[] bytes, final int[] values, final int offset) {
+ assert (bytes.length % 2 == 0);
+ int valuesIndex = offset;
+ for (int i = 0; i < bytes.length; i += 2) {
+ final int value = (int) (((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff));
+ values[valuesIndex++] = value;
+ }
+ }
}