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

Implement Vector3D global codebook compression.

parent c4a34234
No related branches found
No related tags found
No related merge requests found
package azgracompress.compression; package azgracompress.compression;
import azgracompress.compression.exception.ImageDecompressionException; import azgracompress.compression.exception.ImageDecompressionException;
import azgracompress.compression.listeners.IProgressListener;
import azgracompress.compression.listeners.IStatusListener;
import azgracompress.data.ImageU16Dataset; import azgracompress.data.ImageU16Dataset;
import azgracompress.fileformat.QCMPFileHeader; import azgracompress.fileformat.QCMPFileHeader;
import azgracompress.utilities.Stopwatch; import azgracompress.utilities.Stopwatch;
...@@ -47,9 +45,9 @@ public class ImageDecompressor extends CompressorDecompressorBase { ...@@ -47,9 +45,9 @@ public class ImageDecompressor extends CompressorDecompressorBase {
break; break;
case Vector1D: case Vector1D:
case Vector2D: case Vector2D:
case Vector3D:
decompressor = new VQImageDecompressor(options); decompressor = new VQImageDecompressor(options);
break; break;
case Vector3D:
case Invalid: case Invalid:
default: default:
return null; return null;
...@@ -70,7 +68,7 @@ public class ImageDecompressor extends CompressorDecompressorBase { ...@@ -70,7 +68,7 @@ public class ImageDecompressor extends CompressorDecompressorBase {
StringBuilder logBuilder = new StringBuilder(); StringBuilder logBuilder = new StringBuilder();
boolean validFile = true; boolean validFile = true;
QCMPFileHeader header = null; QCMPFileHeader header;
try (FileInputStream fileInputStream = new FileInputStream(options.getInputDataInfo().getFilePath()); try (FileInputStream fileInputStream = new FileInputStream(options.getInputDataInfo().getFilePath());
DataInputStream dataInputStream = new DataInputStream(fileInputStream)) { DataInputStream dataInputStream = new DataInputStream(fileInputStream)) {
header = readQCMPFileHeader(dataInputStream); header = readQCMPFileHeader(dataInputStream);
......
package azgracompress.compression; package azgracompress.compression;
import azgracompress.cache.QuantizationCacheManager; import azgracompress.cache.QuantizationCacheManager;
import azgracompress.fileformat.QuantizationType;
import azgracompress.io.InputData;
import azgracompress.compression.exception.ImageCompressionException; import azgracompress.compression.exception.ImageCompressionException;
import azgracompress.data.Chunk2D; import azgracompress.data.Chunk2D;
import azgracompress.data.ImageU16; import azgracompress.data.ImageU16;
import azgracompress.data.Range;
import azgracompress.fileformat.QuantizationType;
import azgracompress.huffman.Huffman; import azgracompress.huffman.Huffman;
import azgracompress.io.InputData;
import azgracompress.io.loader.IPlaneLoader; import azgracompress.io.loader.IPlaneLoader;
import azgracompress.io.loader.PlaneLoaderFactory; import azgracompress.io.loader.PlaneLoaderFactory;
import azgracompress.quantization.vector.*; import azgracompress.quantization.vector.*;
import azgracompress.utilities.Stopwatch; import azgracompress.utilities.Stopwatch;
import org.jetbrains.annotations.NotNull;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
...@@ -104,7 +106,11 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm ...@@ -104,7 +106,11 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
return compressVoxels(compressStream); return compressVoxels(compressStream);
} }
assert (options.getQuantizationVector().getZ() == 1); assert (options.getQuantizationVector().getZ() == 1);
return compress1D2DVectors(compressStream);
}
@NotNull
private long[] compress1D2DVectors(DataOutputStream compressStream) throws ImageCompressionException {
final InputData inputDataInfo = options.getInputDataInfo(); final InputData inputDataInfo = options.getInputDataInfo();
Stopwatch stopwatch = new Stopwatch(); Stopwatch stopwatch = new Stopwatch();
final boolean hasGeneralQuantizer = options.getCodebookType() != CompressionOptions.CodebookType.Individual; final boolean hasGeneralQuantizer = options.getCodebookType() != CompressionOptions.CodebookType.Individual;
...@@ -113,7 +119,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm ...@@ -113,7 +119,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
try { try {
planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputDataInfo); planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(inputDataInfo);
} catch (Exception e) { } catch (Exception e) {
throw new ImageCompressionException("Unable to create SCIFIO reader. " + e.getMessage()); throw new ImageCompressionException("Unable to create plane reader. " + e.getMessage());
} }
VectorQuantizer quantizer = null; VectorQuantizer quantizer = null;
Huffman huffman = null; Huffman huffman = null;
...@@ -285,19 +291,67 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm ...@@ -285,19 +291,67 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
reportStatusToListeners("Operation completed."); reportStatusToListeners("Operation completed.");
} }
/**
* Calculate the number of voxel layers needed for dataset of plane count.
*
* @param datasetPlaneCount Dataset plane count
* @param voxelDepth Z dimension of voxel.
* @return Number of voxel layers.
*/
public static int calculateVoxelLayerCount(final int datasetPlaneCount, final int voxelDepth) {
return (datasetPlaneCount / voxelDepth);
}
public long[] compressVoxels(DataOutputStream compressStream) throws ImageCompressionException { public long[] compressVoxels(DataOutputStream compressStream) throws ImageCompressionException {
// int[][] voxels; assert (options.getCodebookType() == CompressionOptions.CodebookType.Global);
// try { final IPlaneLoader planeLoader;
// IPlaneLoader loader = PlaneLoaderFactory.getPlaneLoaderForInputFile(options.getInputDataInfo()); final int[] huffmanSymbols = createHuffmanSymbols(getCodebookSize());
// final int[] data = loader.loadAllPlanesU16Data(); try {
// Chunk3D bigVoxel = new Chunk3D(options.getInputDataInfo().getDimensions(), data); planeLoader = PlaneLoaderFactory.getPlaneLoaderForInputFile(options.getInputDataInfo());
// voxels = bigVoxel.divideInto3DVectors(options.getQuantizationVector()); } catch (Exception e) {
// } catch (Exception e) { throw new ImageCompressionException("Unable to create plane reader. " + e.getMessage());
// throw new ImageCompressionException("Unable to create data loader or load image data.", e); }
// }
return null; final int voxelLayerDepth = options.getQuantizationVector().getZ();
final int voxelLayerCount = calculateVoxelLayerCount(options.getInputDataInfo().getDimensions().getZ(), voxelLayerDepth);
long[] voxelLayersSizes = new long[voxelLayerCount];
final VectorQuantizer quantizer = loadQuantizerFromCache();
final Huffman huffman = createHuffmanCoder(huffmanSymbols, quantizer.getFrequencies());
writeQuantizerToCompressStream(quantizer, compressStream);
int[][] voxelData;
Stopwatch stopwatch = new Stopwatch();
for (int voxelLayerIndex = 0; voxelLayerIndex < voxelLayerCount; voxelLayerIndex++) {
stopwatch.restart();
final int fromZ = (voxelLayerIndex * voxelLayerDepth);
// TODO(Moravec): There is a problem!
// If dataset.Z is not divisible by voxel.Z we end up creating a lot stupid voxels.
// Those stupid voxels have only one or two layers of actual data and the rest are zeros.
// This ends up increasing the file size because they have quite long Huffman codes.
final int toZ = (voxelLayerIndex == voxelLayerCount - 1)
? options.getInputDataInfo().getDimensions().getZ()
: (voxelLayerDepth + (voxelLayerIndex * voxelLayerDepth));
final Range<Integer> voxelLayerRange = new Range<>(fromZ, toZ);
try {
voxelData = planeLoader.loadVoxels(options.getQuantizationVector(), voxelLayerRange);
System.out.println("voxelData.length=" + voxelData.length);
} catch (IOException e) {
throw new ImageCompressionException("Unable to load voxels from voxel layer " + voxelLayerRange, e);
}
final int[] indices = quantizer.quantizeIntoIndices(voxelData, options.getWorkerCount());
voxelLayersSizes[voxelLayerIndex] = writeHuffmanEncodedIndices(compressStream, huffman, indices);
stopwatch.stop();
reportProgressToListeners(voxelLayerIndex, voxelLayerCount,
"%d/%d Finished voxel layer %s compression pass in %s",
voxelLayerIndex, voxelLayerCount, voxelLayerRange.toString(), stopwatch.getElapsedTimeString());
}
return voxelLayersSizes;
} }
} }
...@@ -76,14 +76,16 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I ...@@ -76,14 +76,16 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
public long getExpectedDataSize(QCMPFileHeader header) { public long getExpectedDataSize(QCMPFileHeader header) {
// Vector count in codebook // Vector count in codebook
final int codebookSize = (int) Math.pow(2, header.getBitsPerCodebookIndex()); final int codebookSize = (int) Math.pow(2, header.getBitsPerCodebookIndex());
System.out.println("codebookSize=" + codebookSize);
// Single vector size in bytes. // Single vector size in bytes.
assert (header.getVectorSizeZ() == 1);
final int vectorDataSize = 2 * header.getVectorSizeX() * header.getVectorSizeY() * header.getVectorSizeZ(); final int vectorDataSize = 2 * header.getVectorSizeX() * header.getVectorSizeY() * header.getVectorSizeZ();
System.out.println("vectorDataSize=" + vectorDataSize);
// Total codebook size in bytes. // Total codebook size in bytes.
final long codebookDataSize = ((codebookSize * vectorDataSize) + (codebookSize * LONG_BYTES)) * final long codebookDataSize = ((codebookSize * vectorDataSize) + (codebookSize * LONG_BYTES)) *
(header.isCodebookPerPlane() ? header.getImageSizeZ() : 1); (header.isCodebookPerPlane() ? header.getImageSizeZ() : 1);
System.out.println("codebookDataSize=" + codebookDataSize);
// Indices are encoded using huffman. Plane data size is written in the header. // Indices are encoded using huffman. Plane data size is written in the header.
long[] planeDataSizes = header.getPlaneDataSizes(); long[] planeDataSizes = header.getPlaneDataSizes();
...@@ -91,6 +93,8 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I ...@@ -91,6 +93,8 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
for (final long planeDataSize : planeDataSizes) { for (final long planeDataSize : planeDataSizes) {
totalPlaneDataSize += planeDataSize; totalPlaneDataSize += planeDataSize;
} }
System.out.println("totalPlaneDataSize=" + totalPlaneDataSize);
System.out.println("TOTAL=" + (codebookDataSize + totalPlaneDataSize));
return (codebookDataSize + totalPlaneDataSize); return (codebookDataSize + totalPlaneDataSize);
} }
...@@ -176,7 +180,9 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I ...@@ -176,7 +180,9 @@ public class VQImageDecompressor extends CompressorDecompressorBase implements I
} }
@Override @Override
public void decompressToBuffer(DataInputStream compressedStream, short[][] buffer, QCMPFileHeader header) throws ImageDecompressionException { public void decompressToBuffer(DataInputStream compressedStream,
short[][] buffer,
QCMPFileHeader header) throws ImageDecompressionException {
// TODO: Think how to remove the duplicate code. // TODO: Think how to remove the duplicate code.
final int codebookSize = (int) Math.pow(2, header.getBitsPerCodebookIndex()); final int codebookSize = (int) Math.pow(2, header.getBitsPerCodebookIndex());
assert (header.getVectorSizeZ() == 1); assert (header.getVectorSizeZ() == 1);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment