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

Move encode functionality to HuffmanEncoder class.

parent eafefe04
No related branches found
No related tags found
No related merge requests found
......@@ -36,14 +36,14 @@ public class DebugFunction extends CustomFunctionBase {
symbols[i] = i;
}
final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, codebook.getVectorFrequencies());
huffman.buildHuffmanTree();
final HuffmanTreeBuilder huffmanBuilder = new HuffmanTreeBuilder(symbols, codebook.getVectorFrequencies());
huffmanBuilder.buildHuffmanTree();
final int bitsPerSymbol = (int) Utils.log2(codebook.getCodebookSize());
try (final OutBitStream bitStream = new OutBitStream(new FileOutputStream("D:\\tmp\\huffman_tree.data", false),
bitsPerSymbol,
64)) {
huffman.getRoot().writeToBinaryStream(bitStream);
huffmanBuilder.createEncoder().getRoot().writeToBinaryStream(bitStream);
} catch (final IOException e) {
e.printStackTrace();
}
......@@ -56,7 +56,7 @@ public class DebugFunction extends CustomFunctionBase {
ex.printStackTrace();
}
final boolean equal = huffman.getRoot().treeEqual(readRoot);
final boolean equal = huffmanBuilder.createEncoder().getRoot().treeEqual(readRoot);
System.out.println(readRoot != null);
......
......@@ -4,6 +4,7 @@ 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.HuffmanEncoder;
import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
import cz.it4i.qcmp.io.InputData;
import cz.it4i.qcmp.io.OutBitStream;
......@@ -101,16 +102,16 @@ public abstract class CompressorDecompressorBase {
return symbols;
}
protected HuffmanTreeBuilder createHuffmanCoder(final int[] symbols, final long[] frequencies) {
protected HuffmanEncoder createHuffmanEncoder(final int[] symbols, final long[] frequencies) {
final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, frequencies);
huffman.buildHuffmanTree();
return huffman;
return huffman.createEncoder();
}
protected HuffmanDecoder createHuffmanDecoder(final int[] symbols, final long[] frequencies) {
final HuffmanTreeBuilder huffman = new HuffmanTreeBuilder(symbols, frequencies);
huffman.buildHuffmanTree();
return new HuffmanDecoder(huffman.getRoot());
return huffman.createDecoder();
}
protected int[] getPlaneIndicesForCompression(final InputData inputData) {
......@@ -158,17 +159,17 @@ public abstract class CompressorDecompressorBase {
* Write huffman encoded indices to the compress stream.
*
* @param compressStream Compress stream.
* @param huffman Huffman encoder.
* @param huffmanEncoder Huffman encoder.
* @param indices Indices to write.
* @return Number of bytes written.
* @throws ImageCompressionException when fails to write to compress stream.
*/
protected long writeHuffmanEncodedIndices(final DataOutputStream compressStream,
final HuffmanTreeBuilder huffman,
final HuffmanEncoder huffmanEncoder,
final int[] indices) throws ImageCompressionException {
try (final OutBitStream outBitStream = new OutBitStream(compressStream, options.getBitsPerCodebookIndex(), 2048)) {
for (final int index : indices) {
outBitStream.write(huffman.getCode(index));
outBitStream.write(huffmanEncoder.getSymbolCode(index));
}
return outBitStream.getBytesWritten();
} catch (final Exception ex) {
......
......@@ -5,8 +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.huffman.HuffmanEncoder;
import cz.it4i.qcmp.io.InputData;
import cz.it4i.qcmp.io.loader.IPlaneLoader;
import cz.it4i.qcmp.io.loader.PlaneLoaderFactory;
......@@ -21,7 +20,7 @@ import java.io.IOException;
public class SQImageCompressor extends CompressorDecompressorBase implements IImageCompressor {
private ScalarQuantizer cachedQuantizer;
private HuffmanDecoder cachedHuffmanDecoder;
private HuffmanEncoder cachedHuffmanEncoder;
public SQImageCompressor(final CompressionOptions options) {
super(options);
......@@ -46,7 +45,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
public void preloadGlobalCodebook(final ICacheFile codebookCacheFile) {
final SQCodebook cachedCodebook = ((SQCacheFile) codebookCacheFile).getCodebook();
cachedQuantizer = new ScalarQuantizer(cachedCodebook);
cachedHuffmanDecoder = createHuffmanDecoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
cachedHuffmanEncoder = createHuffmanEncoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
cachedCodebook.getSymbolFrequencies());
}
......@@ -118,13 +117,13 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
}
ScalarQuantizer quantizer = null;
HuffmanTreeBuilder huffman = null;
HuffmanEncoder huffmanEncoder = null;
final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize());
if (options.getCodebookType() == CompressionOptions.CodebookType.Global) {
reportStatusToListeners("Loading codebook from cache file.");
quantizer = loadQuantizerFromCache();
huffman = createHuffmanCoder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
reportStatusToListeners("Cached quantizer with huffman coder created.");
writeCodebookToOutputStream(quantizer, compressStream);
......@@ -140,7 +139,7 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
reportStatusToListeners(String.format("Training scalar quantizer from middle plane %d.", middlePlaneIndex));
quantizer = trainScalarQuantizerFromData(middlePlaneData);
huffman = createHuffmanCoder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
stopwatch.stop();
writeCodebookToOutputStream(quantizer, compressStream);
......@@ -176,16 +175,15 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
quantizer = trainScalarQuantizerFromData(planeData);
writeCodebookToOutputStream(quantizer, compressStream);
huffman = new HuffmanTreeBuilder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
huffman.buildHuffmanTree();
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getCodebook().getSymbolFrequencies());
}
assert (quantizer != null) : "Scalar Quantizer wasn't initialized.";
assert (huffman != null) : "Huffman wasn't initialized.";
assert (huffmanEncoder != null) : "Huffman wasn't initialized.";
final int[] indices = quantizer.quantizeIntoIndices(planeData, 1);
planeDataSizes[planeCounter++] = writeHuffmanEncodedIndices(compressStream, huffman, indices);
planeDataSizes[planeCounter++] = writeHuffmanEncodedIndices(compressStream, huffmanEncoder, indices);
stopwatch.stop();
reportProgressToListeners(planeIndex, planeIndices.length,
......
......@@ -6,7 +6,7 @@ import cz.it4i.qcmp.cache.VQCacheFile;
import cz.it4i.qcmp.compression.exception.ImageCompressionException;
import cz.it4i.qcmp.data.Range;
import cz.it4i.qcmp.fileformat.QuantizationType;
import cz.it4i.qcmp.huffman.HuffmanTreeBuilder;
import cz.it4i.qcmp.huffman.HuffmanEncoder;
import cz.it4i.qcmp.io.InputData;
import cz.it4i.qcmp.io.loader.IPlaneLoader;
import cz.it4i.qcmp.io.loader.PlaneLoaderFactory;
......@@ -24,7 +24,7 @@ import java.io.IOException;
public class VQImageCompressor extends CompressorDecompressorBase implements IImageCompressor {
private VectorQuantizer cachedQuantizer = null;
private HuffmanTreeBuilder cachedHuffman = null;
private HuffmanEncoder cachedHuffmanEncoder = null;
private boolean useKdTree = false;
......@@ -36,7 +36,8 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
public void preloadGlobalCodebook(final ICacheFile codebookCacheFile) {
final VQCodebook cachedCodebook = ((VQCacheFile) codebookCacheFile).getCodebook();
cachedQuantizer = new VectorQuantizer(cachedCodebook);
cachedHuffman = createHuffmanCoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()), cachedCodebook.getVectorFrequencies());
cachedHuffmanEncoder = createHuffmanEncoder(createHuffmanSymbols(cachedCodebook.getCodebookSize()),
cachedCodebook.getVectorFrequencies());
}
public boolean shouldUseKdTree() {
......@@ -158,14 +159,14 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
}
VectorQuantizer quantizer = cachedQuantizer;
HuffmanTreeBuilder huffman = cachedHuffman;
assert (!streamMode || ((quantizer != null) && (huffman != null)));
HuffmanEncoder huffmanEncoder = cachedHuffmanEncoder;
assert (!streamMode || ((quantizer != null) && (huffmanEncoder != null)));
if (!streamMode) {
if (options.getCodebookType() == CompressionOptions.CodebookType.Global) {
reportStatusToListeners("Loading codebook from cache file.");
quantizer = loadQuantizerFromCache();
huffman = createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies());
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getFrequencies());
reportStatusToListeners("Cached quantizer with huffman coder created.");
writeQuantizerToCompressStream(quantizer, compressStream);
} else if (options.getCodebookType() == CompressionOptions.CodebookType.MiddlePlane) {
......@@ -174,7 +175,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
final int[][] refPlaneVectors = planeLoader.loadVectorsFromPlaneRange(0, options,
Utils.singlePlaneRange(getMiddlePlaneIndex()));
quantizer = trainVectorQuantizerFromPlaneVectors(refPlaneVectors);
huffman = createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies());
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getFrequencies());
stopwatch.stop();
reportStatusToListeners("Middle plane codebook created in: " + stopwatch.getElapsedTimeString());
writeQuantizerToCompressStream(quantizer, compressStream);
......@@ -208,13 +209,13 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
if (!streamMode && !hasGeneralQuantizer) {
reportStatusToListeners(String.format("Training vector quantizer from plane %d.", planeIndex));
quantizer = trainVectorQuantizerFromPlaneVectors(planeVectors);
huffman = createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies());
huffmanEncoder = createHuffmanEncoder(huffmanSymbols, quantizer.getFrequencies());
writeQuantizerToCompressStream(quantizer, compressStream);
}
final int[] indices = quantizeVectorsImpl(quantizer, planeVectors, options.getWorkerCount());
planeDataSizes[planeCounter++] = writeHuffmanEncodedIndices(compressStream, huffman, indices);
planeDataSizes[planeCounter++] = writeHuffmanEncodedIndices(compressStream, huffmanEncoder, indices);
stopwatch.stop();
if (options.isConsoleApplication()) {
......@@ -283,8 +284,9 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
final long[] voxelLayersSizes = new long[voxelLayerCount];
final VectorQuantizer quantizer = (cachedQuantizer != null) ? cachedQuantizer : loadQuantizerFromCache();
final HuffmanTreeBuilder huffman = (cachedHuffman != null) ? cachedHuffman : createHuffmanCoder(huffmanSymbols,
quantizer.getFrequencies());
final HuffmanEncoder huffmanEncoder = (cachedHuffmanEncoder != null)
? cachedHuffmanEncoder
: createHuffmanEncoder(huffmanSymbols, quantizer.getFrequencies());
if (!streamMode)
writeQuantizerToCompressStream(quantizer, compressStream);
......@@ -309,7 +311,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
final int[] indices = quantizeVectorsImpl(quantizer, voxelData, options.getWorkerCount());
voxelLayersSizes[voxelLayerIndex] = writeHuffmanEncodedIndices(compressStream, huffman, indices);
voxelLayersSizes[voxelLayerIndex] = writeHuffmanEncodedIndices(compressStream, huffmanEncoder, indices);
stopwatch.stop();
if (options.isConsoleApplication()) {
reportStatusToListeners("%d/%d Finished voxel layer %s compression pass in %s",
......
......@@ -5,7 +5,7 @@ import cz.it4i.qcmp.io.InBitStream;
import java.io.IOException;
/**
* Simply wrapper around root huffman node to provide easy decode function.
* Simple wrapper around root huffman node to provide easy decode function.
*/
public class HuffmanDecoder {
private final HuffmanNode root;
......@@ -33,4 +33,8 @@ public class HuffmanDecoder {
}
return currentNode.getSymbol();
}
public HuffmanNode getRoot() {
return root;
}
}
package cz.it4i.qcmp.huffman;
import java.util.HashMap;
/**
* Simple wrapper around root huffman symbol map to provide easy encode function.
*/
public class HuffmanEncoder {
private final HuffmanNode root;
private final HashMap<Integer, boolean[]> symbolCodes;
/**
* Create huffman encoder from symbol map.
*
* @param root Huffman tree root.
* @param symbolCodes Huffman symbol map.
*/
public HuffmanEncoder(final HuffmanNode root, final HashMap<Integer, boolean[]> symbolCodes) {
this.root = root;
this.symbolCodes = symbolCodes;
}
/**
* Get binary code for huffman symbol.
*
* @param symbol Huffman symbol.
* @return Binary code.
*/
public boolean[] getSymbolCode(final int symbol) {
return symbolCodes.get(symbol);
}
public HuffmanNode getRoot() {
return root;
}
}
package cz.it4i.qcmp.huffman;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.PriorityQueue;
public class HuffmanTreeBuilder {
private HuffmanNode root = null;
private HashMap<Integer, boolean[]> symbolCodes;
private HashMap<Integer, Double> symbolProbabilityMap;
private final int[] symbols;
private final long[] symbolFrequencies;
......@@ -76,7 +77,7 @@ public class HuffmanTreeBuilder {
}
private PriorityQueue<HuffmanNode> buildPriorityQueue() {
symbolProbabilityMap = new HashMap<>(symbols.length);
final HashMap<Integer, Double> symbolProbabilityMap = new HashMap<>(symbols.length);
double totalFrequency = 0.0;
for (final long symbolFrequency : symbolFrequencies) {
totalFrequency += symbolFrequency;
......@@ -93,28 +94,18 @@ public class HuffmanTreeBuilder {
return queue;
}
public boolean[] getCode(final int symbol) {
return symbolCodes.get(symbol);
}
public HuffmanNode getRoot() {
return root;
/**
* Create huffman encoder from symbol codes.
*
* @return Huffman encoder.
*/
public HuffmanEncoder createEncoder() {
assert (root != null && symbolCodes != null) : "Huffman tree was not build yet";
return new HuffmanEncoder(root, symbolCodes);
}
public HashMap<Integer, Double> getSymbolProbabilityMap() {
return createSortedHashMap(symbolProbabilityMap);
}
private HashMap<Integer, Double> createSortedHashMap(final HashMap<Integer, Double> map) {
final List<Map.Entry<Integer, Double>> list = new LinkedList<Map.Entry<Integer, Double>>(map.entrySet());
//Custom Comparator
list.sort((t0, t1) -> (-(t0.getValue().compareTo(t1.getValue()))));
//copying the sorted list in HashMap to preserve the iteration order
final HashMap<Integer, Double> sortedHashMap = new LinkedHashMap<Integer, Double>();
for (final Map.Entry<Integer, Double> entry : list) {
sortedHashMap.put(entry.getKey(), entry.getValue());
}
return sortedHashMap;
public HuffmanDecoder createDecoder() {
assert (root != null) : "Huffman tree was not build yet";
return new HuffmanDecoder(root);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment