From 94c922c95e135d5516ee7e1e215c565d69ea4ba7 Mon Sep 17 00:00:00 2001 From: Vojtech Moravec <vojtech.moravec.st@vsb.cz> Date: Tue, 18 Aug 2020 11:49:09 +0200 Subject: [PATCH] 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. --- .../compression/VQImageDecompressor.java | 67 +++++++++++++------ src/main/java/azgracompress/data/Voxel.java | 31 ++++++--- 2 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/main/java/azgracompress/compression/VQImageDecompressor.java b/src/main/java/azgracompress/compression/VQImageDecompressor.java index ec0d379..1c15284 100644 --- a/src/main/java/azgracompress/compression/VQImageDecompressor.java +++ b/src/main/java/azgracompress/compression/VQImageDecompressor.java @@ -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()) { diff --git a/src/main/java/azgracompress/data/Voxel.java b/src/main/java/azgracompress/data/Voxel.java index 5078efc..7a59445 100644 --- a/src/main/java/azgracompress/data/Voxel.java +++ b/src/main/java/azgracompress/data/Voxel.java @@ -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; + } } -- GitLab