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;
+        }
+    }
 }