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

Implement in-memory buffer decompression and refactor duplicate code.

This commit generalizes the voxel decompression logic for both stream
and memory decompressor. Both methods use the same method with different
voxel writing function.
parent 9d6fe828
No related branches found
No related tags found
No related merge requests found
......@@ -18,6 +18,13 @@ import java.io.IOException;
@SuppressWarnings("DuplicatedCode")
public class VQImageDecompressor extends CompressorDecompressorBase implements IImageDecompressor {
private interface DecompressVoxelCallback {
void process(final Voxel decompressedVoxel,
final int[][] decompressedVoxelData,
final int planeOffset) throws ImageDecompressionException;
}
public VQImageDecompressor(CompressionOptions options) {
super(options);
}
......@@ -171,6 +178,7 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
}
}
@Override
public void decompressToBuffer(DataInputStream compressedStream,
short[][] buffer,
......@@ -240,16 +248,11 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
}
}
private void decompressVoxelsToBuffer(DataInputStream compressedStream,
short[][] buffer,
QCMPFileHeader header) throws ImageDecompressionException {
throw new ImageDecompressionException("Implement this!!!");
}
private void decompressVoxelsImpl(DataInputStream compressedStream,
QCMPFileHeader header,
DecompressVoxelCallback callback) throws ImageDecompressionException {
private void decompressVoxels(DataInputStream compressedStream,
DataOutputStream decompressStream,
QCMPFileHeader header) throws ImageDecompressionException {
assert (header.getQuantizationType() == QuantizationType.Vector3D);
assert (!header.isCodebookPerPlane()); // SHOULD ALWAYS BE GLOBAL.
......@@ -265,7 +268,6 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
final int voxelLayerCount = VQImageCompressor.calculateVoxelLayerCount(header.getImageSizeZ(), header.getVectorSizeZ());
Stopwatch stopwatch = new Stopwatch();
ImageU16Dataset currentVoxelLayer;
for (int voxelLayerIndex = 0; voxelLayerIndex < voxelLayerCount; voxelLayerIndex++) {
stopwatch.restart();
......@@ -277,31 +279,24 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
final int voxelLayerDataSize = (int) header.getPlaneDataSizes()[voxelLayerIndex];
final int voxelLayerVoxelCount = Voxel.calculateRequiredVoxelCount(currentVoxelLayerDims, voxelDims);
int[][] decompressedVoxels = new int[voxelLayerVoxelCount][vectorSize];
try (InBitStream inBitStream = new InBitStream(compressedStream, header.getBitsPerCodebookIndex(), voxelLayerDataSize)) {
inBitStream.readToBuffer();
inBitStream.setAllowReadFromUnderlyingStream(false);
int[][] decompressedVoxels = new int[voxelLayerVoxelCount][vectorSize];
for (int voxelIndex = 0; voxelIndex < voxelLayerVoxelCount; voxelIndex++) {
final int huffmanSymbol = decodeHuffmanSymbol(huffman, inBitStream);
System.arraycopy(codebook.getVectors()[huffmanSymbol].getVector(),
0, decompressedVoxels[voxelIndex], 0, vectorSize);
System.arraycopy(codebook.getVectors()[huffmanSymbol].getVector(), 0, decompressedVoxels[voxelIndex], 0, vectorSize);
}
final Voxel currentVoxel = new Voxel(currentVoxelLayerDims);
currentVoxelLayer = currentVoxel.reconstructFromVoxelsToDataset(voxelDims, decompressedVoxels);
} catch (Exception e) {
throw new ImageDecompressionException("VQImageDecompressor::decompressVoxels() - Unable to read indices from InBitStream.",
e);
}
for (int layer = 0; layer < currentVoxelLayerDims.getZ(); layer++) {
try {
decompressStream.write(TypeConverter.unsignedShortArrayToByteArray(currentVoxelLayer.getPlaneData(layer), false));
} catch (IOException e) {
throw new ImageDecompressionException("Unable to write to decompress stream.", e);
}
}
final Voxel currentVoxel = new Voxel(currentVoxelLayerDims);
callback.process(currentVoxel, decompressedVoxels, (voxelLayerIndex * voxelLayerDepth));
stopwatch.stop();
if (options.isConsoleApplication()) {
......@@ -315,6 +310,36 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
}
}
private void decompressVoxelsToBuffer(DataInputStream compressedStream,
short[][] buffer,
QCMPFileHeader header) throws ImageDecompressionException {
final V3i voxelDims = new V3i(header.getVectorSizeX(), header.getVectorSizeY(), header.getVectorSizeZ());
decompressVoxelsImpl(compressedStream, header, (decompressedVoxel, decompressedVoxelData, planeOffset) ->
decompressedVoxel.reconstructFromVoxels(voxelDims, decompressedVoxelData, buffer, planeOffset));
}
private void decompressVoxels(DataInputStream compressedStream,
DataOutputStream decompressStream,
QCMPFileHeader header) throws ImageDecompressionException {
final V3i voxelDims = new V3i(header.getVectorSizeX(), header.getVectorSizeY(), header.getVectorSizeZ());
decompressVoxelsImpl(compressedStream, header, (voxel, voxelData, planeOffset) -> {
ImageU16Dataset currentVoxelLayer = voxel.reconstructFromVoxelsToDataset(voxelDims, voxelData);
for (int layer = 0; layer < voxel.getDims().getZ(); layer++) {
try {
decompressStream.write(TypeConverter.unsignedShortArrayToByteArray(currentVoxelLayer.getPlaneData(layer), false));
} catch (IOException e) {
throw new ImageDecompressionException("Unable to write to decompress stream.", e);
}
}
});
}
private int decodeHuffmanSymbol(Huffman huffman, InBitStream inBitStream) throws IOException {
HuffmanNode currentHuffmanNode = huffman.getRoot();
while (!currentHuffmanNode.isLeaf()) {
......
......@@ -63,15 +63,10 @@ public final class Voxel {
return (xChunkCount * yChunkCount * zChunkCount);
}
/**
* Reconstruct an 3D dataset from voxels, which divided the original dataset.
*
* @param voxelDims Voxel dimensions.
* @param voxelData Voxel data.
* @return Dataset reconstructed from the voxel data.
*/
public ImageU16Dataset reconstructFromVoxelsToDataset(final V3i voxelDims, final int[][] voxelData) {
final short[][] reconstructedData = new short[dims.getZ()][dims.toV2i().multiplyTogether()];
public void reconstructFromVoxels(final V3i voxelDims,
final int[][] voxelData,
final short[][] reconstructedData,
final int planeIndexOffset) {
final int xVoxelCount = (int) Math.ceil((double) dims.getX() / (double) voxelDims.getX());
final int yVoxelCount = (int) Math.ceil((double) dims.getY() / (double) voxelDims.getY());
......@@ -110,7 +105,7 @@ public final class Voxel {
final int indexInsidePlane = Block.index(dstX, dstY, planeDimX);
// reconstructedData are 2D data while voxelData are 3D data!
reconstructedData[planeIndex][indexInsidePlane] =
reconstructedData[planeIndexOffset + planeIndex][indexInsidePlane] =
(short) voxelData[(voxelOffset + voxelIndex)][indexInsideVoxel];
}
}
......@@ -120,6 +115,18 @@ public final class Voxel {
voxelOffset += planeVoxelCount;
}
}
/**
* Reconstruct an 3D dataset from voxels, which divided the original dataset.
*
* @param voxelDims Voxel dimensions.
* @param voxelData Voxel data.
* @return Dataset reconstructed from the voxel data.
*/
public ImageU16Dataset reconstructFromVoxelsToDataset(final V3i voxelDims, final int[][] voxelData) {
final short[][] reconstructedData = new short[dims.getZ()][dims.toV2i().multiplyTogether()];
reconstructFromVoxels(voxelDims, voxelData, reconstructedData, 0);
return new ImageU16Dataset(dims.toV2i(), dims.getZ(), reconstructedData);
}
......@@ -174,4 +181,8 @@ public final class Voxel {
}
return reconstructedVoxel;
}
public final V3i getDims() {
return dims;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment