diff --git a/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java b/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java
index 82d9453091fcfd1024a1f3413ee5e6fd7811662f..4654ac8e280306ec577b0d584b3454d79181ce35 100644
--- a/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java
+++ b/src/main/java/cz/it4i/qcmp/compression/CompressorDecompressorBase.java
@@ -3,6 +3,7 @@ package cz.it4i.qcmp.compression;
 import cz.it4i.qcmp.compression.exception.ImageCompressionException;
 import cz.it4i.qcmp.compression.listeners.IProgressListener;
 import cz.it4i.qcmp.compression.listeners.IStatusListener;
+import cz.it4i.qcmp.huffman.HuffmanDecoder;
 import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
 import cz.it4i.qcmp.io.InputData;
 import cz.it4i.qcmp.io.OutBitStream;
@@ -106,6 +107,12 @@ public abstract class CompressorDecompressorBase {
         return huffman;
     }
 
+    protected HuffmanDecoder createHuffmanDecoder(final int[] symbols, final long[] frequencies) {
+        final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, frequencies);
+        huffman.buildHuffmanTree();
+        return new HuffmanDecoder(huffman.getRoot());
+    }
+
     protected int[] getPlaneIndicesForCompression(final InputData inputData) {
         if (inputData.isPlaneIndexSet()) {
             return new int[]{inputData.getPlaneIndex()};
diff --git a/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java b/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java
index 1e7c1ec7b2ac5a96f07c3280a28f109c8ec0f34e..e0f3451cfb2cddfe3e2a5c9de60bf8c2d6804302 100644
--- a/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java
+++ b/src/main/java/cz/it4i/qcmp/compression/SQImageCompressor.java
@@ -5,6 +5,7 @@ import cz.it4i.qcmp.cache.ICacheFile;
 import cz.it4i.qcmp.cache.QuantizationCacheManager;
 import cz.it4i.qcmp.cache.SQCacheFile;
 import cz.it4i.qcmp.compression.exception.ImageCompressionException;
+import cz.it4i.qcmp.huffman.HuffmanDecoder;
 import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
 import cz.it4i.qcmp.io.InputData;
 import cz.it4i.qcmp.io.loader.IPlaneLoader;
@@ -20,7 +21,7 @@ import java.io.IOException;
 public class SQImageCompressor extends CompressorDecompressorBase implements IImageCompressor {
 
     private ScalarQuantizer cachedQuantizer;
-    private HuffmanTreeBuilder cachedHuffman;
+    private HuffmanDecoder cachedHuffmanDecoder;
 
     public SQImageCompressor(final CompressionOptions options) {
         super(options);
@@ -45,7 +46,8 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
     public void preloadGlobalCodebook(final ICacheFile codebookCacheFile) {
         final SQCodebook cachedCodebook = ((SQCacheFile) codebookCacheFile).getCodebook();
         cachedQuantizer = new ScalarQuantizer(cachedCodebook);
-        cachedHuffman = createHuffmanCoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()), cachedCodebook.getSymbolFrequencies());
+        cachedHuffmanDecoder = createHuffmanDecoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
+                                                    cachedCodebook.getSymbolFrequencies());
     }
 
     /**
diff --git a/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java b/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java
index e72bfbb2baa95887536e68918ca3633bb681169c..b962be46a092acc017f0409a654961c40d3d8a9e 100644
--- a/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java
+++ b/src/main/java/cz/it4i/qcmp/compression/SQImageDecompressor.java
@@ -4,8 +4,7 @@ import cz.it4i.qcmp.cache.ICacheFile;
 import cz.it4i.qcmp.cache.SQCacheFile;
 import cz.it4i.qcmp.compression.exception.ImageDecompressionException;
 import cz.it4i.qcmp.fileformat.QCMPFileHeader;
-import cz.it4i.qcmp.huffman.HuffmanNode;
-import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
+import cz.it4i.qcmp.huffman.HuffmanDecoder;
 import cz.it4i.qcmp.io.InBitStream;
 import cz.it4i.qcmp.quantization.scalar.SQCodebook;
 import cz.it4i.qcmp.utilities.Stopwatch;
@@ -17,7 +16,7 @@ import java.io.IOException;
 
 public class SQImageDecompressor extends CompressorDecompressorBase implements IImageDecompressor {
     private SQCodebook cachedCodebook = null;
-    private HuffmanTreeBuilder cachedHuffman = null;
+    private HuffmanDecoder cachedHuffmanDecoder = null;
 
     public SQImageDecompressor(final CompressionOptions options) {
         super(options);
@@ -52,12 +51,12 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
         final int planePixelCount = header.getImageSizeX() * header.getImageSizeY();
 
         SQCodebook codebook = null;
-        HuffmanTreeBuilder huffman = null;
+        HuffmanDecoder huffmanDecoder = null;
         if (!header.isCodebookPerPlane()) {
             // There is only one codebook.
             reportStatusToListeners("Loading single codebook and huffman coder.");
             codebook = readScalarQuantizationValues(compressedStream, codebookSize);
-            huffman = createHuffmanCoder(huffmanSymbols, codebook.getSymbolFrequencies());
+            huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getSymbolFrequencies());
         }
 
         final Stopwatch stopwatch = new Stopwatch();
@@ -66,9 +65,9 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
             if (header.isCodebookPerPlane()) {
                 reportStatusToListeners("Loading plane codebook...");
                 codebook = readScalarQuantizationValues(compressedStream, codebookSize);
-                huffman = createHuffmanCoder(huffmanSymbols, codebook.getSymbolFrequencies());
+                huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getSymbolFrequencies());
             }
-            assert (codebook != null && huffman != null);
+            assert (codebook != null && huffmanDecoder != null);
 
             reportStatusToListeners(String.format("Decompressing plane %d...", planeIndex));
             byte[] decompressedPlaneData = null;
@@ -82,13 +81,8 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
                 final int[] decompressedValues = new int[planePixelCount];
                 final int[] quantizationValues = codebook.getCentroids();
                 for (int pixel = 0; pixel < planePixelCount; pixel++) {
-                    HuffmanNode currentHuffmanNode = huffman.getRoot();
-                    boolean bit;
-                    while (!currentHuffmanNode.isLeaf()) {
-                        bit = inBitStream.readBit();
-                        currentHuffmanNode = currentHuffmanNode.traverse(bit);
-                    }
-                    decompressedValues[pixel] = quantizationValues[currentHuffmanNode.getSymbol()];
+                    final int decodedSymbol = huffmanDecoder.decodeSymbol(inBitStream);
+                    decompressedValues[pixel] = quantizationValues[decodedSymbol];
                 }
 
                 decompressedPlaneData =
@@ -116,7 +110,8 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
         final SQCacheFile codebookCache = (SQCacheFile) codebookCacheFile;
 
         cachedCodebook = codebookCache.getCodebook();
-        cachedHuffman = createHuffmanCoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()), cachedCodebook.getSymbolFrequencies());
+        cachedHuffmanDecoder = createHuffmanDecoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
+                                                    cachedCodebook.getSymbolFrequencies());
     }
 
     @Override
@@ -130,20 +125,20 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
         final int planePixelCount = header.getImageSizeX() * header.getImageSizeY();
 
         SQCodebook codebook = null;
-        HuffmanTreeBuilder huffman = null;
+        HuffmanDecoder huffmanDecoder = null;
         if (!header.isCodebookPerPlane()) {
             // There is only one codebook.
             codebook = readScalarQuantizationValues(compressedStream, codebookSize);
-            huffman = createHuffmanCoder(huffmanSymbols, codebook.getSymbolFrequencies());
+            huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getSymbolFrequencies());
         }
 
         for (int planeIndex = 0; planeIndex < planeCountForDecompression; planeIndex++) {
             reportProgressToListeners(planeIndex, planeCountForDecompression, "Decompressing plane %d", planeIndex);
             if (header.isCodebookPerPlane()) {
                 codebook = readScalarQuantizationValues(compressedStream, codebookSize);
-                huffman = createHuffmanCoder(huffmanSymbols, codebook.getSymbolFrequencies());
+                huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getSymbolFrequencies());
             }
-            assert (codebook != null && huffman != null);
+            assert (codebook != null && huffmanDecoder != null);
 
             final int planeDataSize = (int) header.getPlaneDataSizes()[planeIndex];
             try (final InBitStream inBitStream = new InBitStream(compressedStream, header.getBitsPerCodebookIndex(), planeDataSize)) {
@@ -153,13 +148,8 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
                 final int[] decompressedValues = new int[planePixelCount];
                 final int[] quantizationValues = codebook.getCentroids();
                 for (int pixel = 0; pixel < planePixelCount; pixel++) {
-                    HuffmanNode currentHuffmanNode = huffman.getRoot();
-                    boolean bit;
-                    while (!currentHuffmanNode.isLeaf()) {
-                        bit = inBitStream.readBit();
-                        currentHuffmanNode = currentHuffmanNode.traverse(bit);
-                    }
-                    decompressedValues[pixel] = quantizationValues[currentHuffmanNode.getSymbol()];
+                    final int decodedSymbol = huffmanDecoder.decodeSymbol(inBitStream);
+                    decompressedValues[pixel] = quantizationValues[decodedSymbol];
                 }
 
                 buffer[planeIndex] = TypeConverter.intArrayToShortArray(decompressedValues);
diff --git a/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java b/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java
index 1da6828e3e2883ba57f761b676ea365929a005f4..6cff655804476201c0e01306a3a469a18a3c6431 100644
--- a/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java
+++ b/src/main/java/cz/it4i/qcmp/compression/VQImageDecompressor.java
@@ -6,8 +6,7 @@ import cz.it4i.qcmp.compression.exception.ImageDecompressionException;
 import cz.it4i.qcmp.data.*;
 import cz.it4i.qcmp.fileformat.QCMPFileHeader;
 import cz.it4i.qcmp.fileformat.QuantizationType;
-import cz.it4i.qcmp.huffman.HuffmanNode;
-import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
+import cz.it4i.qcmp.huffman.HuffmanDecoder;
 import cz.it4i.qcmp.io.InBitStream;
 import cz.it4i.qcmp.quantization.vector.VQCodebook;
 import cz.it4i.qcmp.utilities.Stopwatch;
@@ -20,7 +19,7 @@ import java.io.IOException;
 public class VQImageDecompressor extends CompressorDecompressorBase implements IImageDecompressor {
 
     private VQCodebook cachedCodebook = null;
-    private HuffmanTreeBuilder cachedHuffman = null;
+    private HuffmanDecoder cachedHuffmanDecoder = null;
 
     private interface DecompressCallback {
         void process(final Block imageBlock, final int planeIndex) throws ImageDecompressionException;
@@ -73,7 +72,8 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
         final VQCacheFile codebookCache = (VQCacheFile) codebookCacheFile;
 
         cachedCodebook = codebookCache.getCodebook();
-        cachedHuffman = createHuffmanCoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()), cachedCodebook.getVectorFrequencies());
+        cachedHuffmanDecoder = createHuffmanDecoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
+                                                    cachedCodebook.getVectorFrequencies());
     }
 
 
@@ -122,19 +122,19 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
 
 
         VQCodebook codebook = null;
-        HuffmanTreeBuilder huffman = null;
+        HuffmanDecoder huffmanDecoder = null;
         if (!header.isCodebookPerPlane()) {
             // There is only one codebook.
             codebook = readCodebook(compressedStream, codebookSize, vectorSize);
-            huffman = createHuffmanCoder(huffmanSymbols, codebook.getVectorFrequencies());
+            huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getVectorFrequencies());
         }
 
         for (int planeIndex = 0; planeIndex < planeCountForDecompression; planeIndex++) {
             if (header.isCodebookPerPlane()) {
                 codebook = readCodebook(compressedStream, codebookSize, vectorSize);
-                huffman = createHuffmanCoder(huffmanSymbols, codebook.getVectorFrequencies());
+                huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getVectorFrequencies());
             }
-            assert (codebook != null && huffman != null);
+            assert (codebook != null && huffmanDecoder != null);
 
 
             final int planeDataSize = (int) header.getPlaneDataSizes()[planeIndex];
@@ -146,14 +146,8 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
 
                 final int[][] decompressedVectors = new int[(int) planeVectorCount][vectorSize];
                 for (int vecIndex = 0; vecIndex < planeVectorCount; vecIndex++) {
-                    HuffmanNode currentHuffmanNode = huffman.getRoot();
-                    boolean bit;
-                    while (!currentHuffmanNode.isLeaf()) {
-                        bit = inBitStream.readBit();
-                        currentHuffmanNode = currentHuffmanNode.traverse(bit);
-                    }
-                    System.arraycopy(codebook.getVectors()[currentHuffmanNode.getSymbol()],
-                                     0, decompressedVectors[vecIndex], 0, vectorSize);
+                    final int decodedSymbol = huffmanDecoder.decodeSymbol(inBitStream);
+                    System.arraycopy(codebook.getVectors()[decodedSymbol], 0, decompressedVectors[vecIndex], 0, vectorSize);
                 }
 
 
@@ -177,7 +171,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                                           final QCMPFileHeader header,
                                           final DecompressCallback callback) throws ImageDecompressionException {
 
-        assert (cachedCodebook != null && cachedHuffman != null);
+        assert (cachedCodebook != null && cachedHuffmanDecoder != null);
         assert (header.getVectorSizeZ() == 1);
         final int planeCountForDecompression = header.getImageSizeZ();
         final long planeVectorCount = calculatePlaneVectorCount(header);
@@ -197,7 +191,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                 final int[][] decompressedVectors = new int[(int) planeVectorCount][vectorSize];
                 int huffmanIndex;
                 for (int vecIndex = 0; vecIndex < planeVectorCount; vecIndex++) {
-                    huffmanIndex = decodeHuffmanSymbol(cachedHuffman, inBitStream);
+                    huffmanIndex = cachedHuffmanDecoder.decodeSymbol(inBitStream);
                     System.arraycopy(cachedCodebook.getVectors()[huffmanIndex], 0, decompressedVectors[vecIndex], 0, vectorSize);
                 }
 
@@ -245,7 +239,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
 
 
         final VQCodebook codebook = readCodebook(compressedStream, codebookSize, vectorSize);
-        final HuffmanTreeBuilder huffman = createHuffmanCoder(huffmanSymbols, codebook.getVectorFrequencies());
+        final HuffmanDecoder huffmanDecoder = createHuffmanDecoder(huffmanSymbols, codebook.getVectorFrequencies());
 
         final int voxelLayerCount = VQImageCompressor.calculateVoxelLayerCount(header.getImageSizeZ(), header.getVectorSizeZ());
         final Stopwatch stopwatch = new Stopwatch();
@@ -267,7 +261,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                 inBitStream.setAllowReadFromUnderlyingStream(false);
 
                 for (int voxelIndex = 0; voxelIndex < voxelLayerVoxelCount; voxelIndex++) {
-                    final int huffmanSymbol = decodeHuffmanSymbol(huffman, inBitStream);
+                    final int huffmanSymbol = huffmanDecoder.decodeSymbol(inBitStream);
                     System.arraycopy(codebook.getVectors()[huffmanSymbol], 0, decompressedVoxels[voxelIndex], 0, vectorSize);
                 }
 
@@ -325,7 +319,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
                 inBitStream.setAllowReadFromUnderlyingStream(false);
 
                 for (int voxelIndex = 0; voxelIndex < voxelLayerVoxelCount; voxelIndex++) {
-                    final int huffmanSymbol = decodeHuffmanSymbol(cachedHuffman, inBitStream);
+                    final int huffmanSymbol = cachedHuffmanDecoder.decodeSymbol(inBitStream);
                     System.arraycopy(cachedCodebook.getVectors()[huffmanSymbol], 0, decompressedVoxels[voxelIndex], 0, vectorSize);
                 }
 
@@ -379,15 +373,6 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
         });
     }
 
-    private int decodeHuffmanSymbol(final HuffmanTreeBuilder huffman, final InBitStream inBitStream) throws IOException {
-        HuffmanNode currentHuffmanNode = huffman.getRoot();
-        while (!currentHuffmanNode.isLeaf()) {
-            currentHuffmanNode = currentHuffmanNode.traverse(inBitStream.readBit());
-        }
-        return currentHuffmanNode.getSymbol();
-    }
-
-
     @Override
     public short[] decompressStreamMode(final DataInputStream compressedStream,
                                         final QCMPFileHeader header) throws ImageDecompressionException {
diff --git a/src/main/java/cz/it4i/qcmp/huffman/HuffmanDecoder.java b/src/main/java/cz/it4i/qcmp/huffman/HuffmanDecoder.java
new file mode 100644
index 0000000000000000000000000000000000000000..00ff44c52200b2d8ba66620e15ced8b2c294c50f
--- /dev/null
+++ b/src/main/java/cz/it4i/qcmp/huffman/HuffmanDecoder.java
@@ -0,0 +1,36 @@
+package cz.it4i.qcmp.huffman;
+
+import cz.it4i.qcmp.io.InBitStream;
+
+import java.io.IOException;
+
+/**
+ * Simply wrapper around root huffman node to provide easy decode function.
+ */
+public class HuffmanDecoder {
+    private final HuffmanNode root;
+
+    /**
+     * Create huffman decoder from the root node.
+     *
+     * @param root Root huffman node.
+     */
+    public HuffmanDecoder(final HuffmanNode root) {
+        this.root = root;
+    }
+
+    /**
+     * Decode huffman symbol by reading binary code from stream.
+     *
+     * @param inBitStream Binary input stream.
+     * @return Decoded symbol.
+     * @throws IOException when fails to read from input stream.
+     */
+    public int decodeSymbol(final InBitStream inBitStream) throws IOException {
+        HuffmanNode currentNode = root;
+        while (!currentNode.isLeaf()) {
+            currentNode = currentNode.traverse(inBitStream.readBit());
+        }
+        return currentNode.getSymbol();
+    }
+}
diff --git a/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java b/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java
index 5daf0ae9782064ac07789aff49751d841f318535..91571ceb73e1d5411dddc4c7bbee07c920b7e6cd 100644
--- a/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java
+++ b/src/main/java/cz/it4i/qcmp/huffman/HuffmanTreeBuilder.java
@@ -23,9 +23,7 @@ public class HuffmanTreeBuilder {
             final HuffmanNode parentA = queue.poll();
             final HuffmanNode parentB = queue.poll();
             if (!(parentA.getProbability() <= parentB.getProbability())) {
-                System.err.println(String.format("Parent A prob: %.6f\nParent B prob: %.6f",
-                                                 parentA.getProbability(),
-                                                 parentB.getProbability()));
+                System.err.printf("Parent A prob: %.6f\nParent B prob: %.6f%n", parentA.getProbability(), parentB.getProbability());
                 assert (parentA.getProbability() <= parentB.getProbability());
             }