diff --git a/FileFormat.docx b/FileFormat.docx
index a31fd8ffb333168cc1e291afa13d6bbfc421675e..a3ae876fcd1f9627bbf2425eb4651a0fe69d1586 100644
Binary files a/FileFormat.docx and b/FileFormat.docx differ
diff --git a/src/main/java/azgracompress/compression/IImageCompressor.java b/src/main/java/azgracompress/compression/IImageCompressor.java
index d5bb08f1aa054f26df2accb3dca80415bdfc1e91..be9d499576b1de60a8e24bfd86f0580a1d82d8bb 100644
--- a/src/main/java/azgracompress/compression/IImageCompressor.java
+++ b/src/main/java/azgracompress/compression/IImageCompressor.java
@@ -12,7 +12,7 @@ public interface IImageCompressor {
      * @param compressStream Compressed data stream.
      * @throws ImageCompressionException when compression fails.
      */
-    void compress(DataOutputStream compressStream) throws ImageCompressionException;
+    long[] compress(DataOutputStream compressStream) throws ImageCompressionException;
 
     /**
      * Train codebook from selected frames and save the learned codebook to cache file.
diff --git a/src/main/java/azgracompress/compression/ImageCompressor.java b/src/main/java/azgracompress/compression/ImageCompressor.java
index 0239a1b3488f9e9fa88f6c251bc0424be5d640ed..75ed35d8171b8e4a442aa4cd1bff784223736dcd 100644
--- a/src/main/java/azgracompress/compression/ImageCompressor.java
+++ b/src/main/java/azgracompress/compression/ImageCompressor.java
@@ -4,12 +4,11 @@ import azgracompress.cli.ParsedCliOptions;
 import azgracompress.compression.exception.ImageCompressionException;
 import azgracompress.fileformat.QCMPFileHeader;
 
-import java.io.BufferedOutputStream;
-import java.io.DataOutputStream;
-import java.io.FileOutputStream;
+import java.io.*;
 
 public class ImageCompressor extends CompressorDecompressorBase {
 
+    final int PLANE_DATA_SIZES_OFFSET = 23;
     private final int codebookSize;
 
     public ImageCompressor(ParsedCliOptions options) {
@@ -67,18 +66,19 @@ public class ImageCompressor extends CompressorDecompressorBase {
             return false;
         }
 
+        long[] planeDataSizes = null;
+
         try (FileOutputStream fos = new FileOutputStream(options.getOutputFile(), false);
              DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(fos, 8192))) {
 
             final QCMPFileHeader header = createHeader();
             header.writeHeader(compressStream);
 
-            imageCompressor.compress(compressStream);
+            planeDataSizes = imageCompressor.compress(compressStream);
 
             if (options.isVerbose()) {
                 reportCompressionRatio(header, compressStream.size());
             }
-
         } catch (ImageCompressionException ex) {
             System.err.println(ex.getMessage());
             return false;
@@ -86,9 +86,35 @@ public class ImageCompressor extends CompressorDecompressorBase {
             e.printStackTrace();
             return false;
         }
+
+        if (planeDataSizes == null) {
+            System.err.println("Plane data sizes are unknown!");
+            return false;
+        }
+
+        try (RandomAccessFile raf = new RandomAccessFile(options.getOutputFile(), "rw")) {
+            raf.seek(PLANE_DATA_SIZES_OFFSET);
+            writePlaneDataSizes(raf, planeDataSizes);
+        } catch (IOException ex) {
+            ex.printStackTrace();
+            return false;
+        }
+
         return true;
     }
 
+    /**
+     * Write plane data size to compressed file.
+     *
+     * @param outStream      Compressed file stream.
+     * @param planeDataSizes Written compressed plane sizes.
+     * @throws IOException when fails to write plane data size.
+     */
+    private void writePlaneDataSizes(RandomAccessFile outStream, final long[] planeDataSizes) throws IOException {
+        for (final long planeDataSize : planeDataSizes) {
+            outStream.writeInt((int) planeDataSize);
+        }
+    }
 
     /**
      * Create QCMPFile header for compressed file.
diff --git a/src/main/java/azgracompress/compression/SQImageCompressor.java b/src/main/java/azgracompress/compression/SQImageCompressor.java
index be249be3c668f042cbd0520e5f17133b04cdaca3..7c4258ec4a1c322517cba0dd415d1c8860af4da0 100644
--- a/src/main/java/azgracompress/compression/SQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/SQImageCompressor.java
@@ -88,8 +88,9 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
      * @param compressStream Stream to which compressed data will be written.
      * @throws ImageCompressionException When compress process fails.
      */
-    public void compress(DataOutputStream compressStream) throws ImageCompressionException {
+    public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
         Stopwatch stopwatch = new Stopwatch();
+        long[] planeDataSizes = new long[options.getImageDimension().getZ()];
         final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.hasReferencePlaneIndex();
 
         ScalarQuantizer quantizer = null;
@@ -161,10 +162,12 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
             } catch (Exception ex) {
                 throw new ImageCompressionException("Unable to write indices to OutBitStream.", ex);
             }
+            // TODO: Fill plane data size
             stopwatch.stop();
             Log("Plane time: " + stopwatch.getElapsedTimeString());
             Log(String.format("Finished processing of plane %d", planeIndex));
         }
+        return planeDataSizes;
     }
 
     private int[] loadConfiguredPlanesData() throws ImageCompressionException {
diff --git a/src/main/java/azgracompress/compression/SQImageDecompressor.java b/src/main/java/azgracompress/compression/SQImageDecompressor.java
index 34adc34d9ca1f9e9e8761dba01661baa1265adc5..ead979a372e7c91939eacbe385fda805ed4655a5 100644
--- a/src/main/java/azgracompress/compression/SQImageDecompressor.java
+++ b/src/main/java/azgracompress/compression/SQImageDecompressor.java
@@ -3,7 +3,9 @@ package azgracompress.compression;
 import azgracompress.cli.ParsedCliOptions;
 import azgracompress.compression.exception.ImageDecompressionException;
 import azgracompress.fileformat.QCMPFileHeader;
+import azgracompress.huffman.Huffman;
 import azgracompress.io.InBitStream;
+import azgracompress.quantization.scalar.ScalarQuantizationCodebook;
 import azgracompress.utilities.Stopwatch;
 import azgracompress.utilities.TypeConverter;
 
@@ -16,17 +18,20 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
         super(options);
     }
 
-    private int[] readScalarQuantizationValues(DataInputStream compressedStream,
-                                               final int n) throws ImageDecompressionException {
-        int[] quantizationValues = new int[n];
+    private ScalarQuantizationCodebook readScalarQuantizationValues(DataInputStream compressedStream) throws ImageDecompressionException {
+        int[] quantizationValues = new int[codebookSize];
+        long[] symbolFrequencies = new long[codebookSize];
         try {
-            for (int i = 0; i < n; i++) {
+            for (int i = 0; i < codebookSize; i++) {
                 quantizationValues[i] = compressedStream.readUnsignedShort();
             }
+            for (int i = 0; i < codebookSize; i++) {
+                symbolFrequencies[i] = compressedStream.readLong();
+            }
         } catch (IOException ioEx) {
             throw new ImageDecompressionException("Unable to read quantization values from compressed stream.", ioEx);
         }
-        return quantizationValues;
+        return new ScalarQuantizationCodebook(quantizationValues, symbolFrequencies);
     }
 
     @Override
@@ -51,6 +56,8 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
     public void decompress(DataInputStream compressedStream,
                            DataOutputStream decompressStream,
                            QCMPFileHeader header) throws ImageDecompressionException {
+
+        final int[] huffmanSymbols = createHuffmanSymbols();
         final int codebookSize = (int) Math.pow(2, header.getBitsPerPixel());
         final int planeCountForDecompression = header.getImageSizeZ();
 
@@ -58,10 +65,13 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
         final int planeIndicesDataSize = (int) Math.ceil((planePixelCount * header.getBitsPerPixel()) / 8.0);
 
         int[] quantizationValues = null;
+        Huffman huffman = null;
         if (!header.isCodebookPerPlane()) {
             // There is only one codebook.
             Log("Loading reference codebook...");
-            quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
+            huffman = null;
+            // TODO(Moravec): Handle loading of Huffman.
+            //quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
         }
 
         Stopwatch stopwatch = new Stopwatch();
@@ -69,9 +79,13 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
             stopwatch.restart();
             if (header.isCodebookPerPlane()) {
                 Log("Loading plane codebook...");
-                quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
+                ScalarQuantizationCodebook codebook = readScalarQuantizationValues(compressedStream);
+                quantizationValues = codebook.getCentroids();
+                huffman = new Huffman(huffmanSymbols, codebook.getSymbolFrequencies());
+                huffman.buildHuffmanTree();
             }
             assert (quantizationValues != null);
+            assert (huffman != null);
 
             Log(String.format("Decompressing plane %d...", planeIndex));
             byte[] decompressedPlaneData = null;
diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java
index 695cd163a97122fd81733fec4f6e240919bdcf40..29413055f6933f39765df19ca311d16cf5f05cc5 100644
--- a/src/main/java/azgracompress/compression/VQImageCompressor.java
+++ b/src/main/java/azgracompress/compression/VQImageCompressor.java
@@ -85,7 +85,8 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
      * @param compressStream Stream to which compressed data will be written.
      * @throws ImageCompressionException When compress process fails.
      */
-    public void compress(DataOutputStream compressStream) throws ImageCompressionException {
+    public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
+        long[] planeDataSizes = new long[options.getImageDimension().getZ()];
         Stopwatch stopwatch = new Stopwatch();
         final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.hasReferencePlaneIndex();
         VectorQuantizer quantizer = null;
@@ -149,10 +150,12 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
             } catch (Exception ex) {
                 throw new ImageCompressionException("Unable to write indices to OutBitStream.", ex);
             }
+            // TODO: Fill plane data size
             stopwatch.stop();
             Log("Plane time: " + stopwatch.getElapsedTimeString());
             Log(String.format("Finished processing of plane %d.", planeIndex));
         }
+        return planeDataSizes;
     }
 
 
diff --git a/src/main/java/azgracompress/fileformat/QCMPFileHeader.java b/src/main/java/azgracompress/fileformat/QCMPFileHeader.java
index 80da0987432d8d2a7843661262d48c208aea97d7..0912f7e5dd85b4fc89e0f27dad29663c1d487bdd 100644
--- a/src/main/java/azgracompress/fileformat/QCMPFileHeader.java
+++ b/src/main/java/azgracompress/fileformat/QCMPFileHeader.java
@@ -69,6 +69,12 @@ public class QCMPFileHeader {
         outputStream.writeShort(vectorSizeX);
         outputStream.writeShort(vectorSizeY);
         outputStream.writeShort(vectorSizeZ);
+
+
+        // NOTE(Moravec): Allocate space for plane data sizes. Offset: 23.
+        for (int i = 0; i < imageSizeZ; i++) {
+            outputStream.writeInt(0x0);
+        }
     }
 
     public boolean readHeader(DataInputStream inputStream) throws IOException {