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

Move symbol decoding to HuffmanDecoder class.

parent 8768db39
No related branches found
No related tags found
No related merge requests found
......@@ -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()};
......
......@@ -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());
}
/**
......
......@@ -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);
......
......@@ -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 {
......
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();
}
}
......@@ -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());
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment