diff --git a/src/main/java/azgracompress/compression/VQImageDecompressor.java b/src/main/java/azgracompress/compression/VQImageDecompressor.java index ec0d3795680948aed3e74a519ddafe143e6f4fe6..1c152848fb5dbdcbe8b7a16aad85ac9195a94dba 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 5078efc5915eba4d2d042228c5b669f6894ad8e8..7a5944505738c86ee697b34457ab620f059bcc77 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; + } }