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

Write compressed plane data sizes to the header.

parent c1d45d0d
No related branches found
No related tags found
No related merge requests found
No preview for this file type
......@@ -12,7 +12,7 @@ public interface IImageCompressor {
* @param compressStream Compressed data stream.
* @throws ImageCompressionException when compression fails.
*/
void compress(DataOutputStream compressStream) throws ImageCompressionException;
long[] compress(DataOutputStream compressStream) throws ImageCompressionException;
/**
* Train codebook from selected frames and save the learned codebook to cache file.
......
......@@ -4,12 +4,11 @@ import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.exception.ImageCompressionException;
import azgracompress.fileformat.QCMPFileHeader;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.*;
public class ImageCompressor extends CompressorDecompressorBase {
final int PLANE_DATA_SIZES_OFFSET = 23;
private final int codebookSize;
public ImageCompressor(ParsedCliOptions options) {
......@@ -67,18 +66,19 @@ public class ImageCompressor extends CompressorDecompressorBase {
return false;
}
long[] planeDataSizes = null;
try (FileOutputStream fos = new FileOutputStream(options.getOutputFile(), false);
DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(fos, 8192))) {
final QCMPFileHeader header = createHeader();
header.writeHeader(compressStream);
imageCompressor.compress(compressStream);
planeDataSizes = imageCompressor.compress(compressStream);
if (options.isVerbose()) {
reportCompressionRatio(header, compressStream.size());
}
} catch (ImageCompressionException ex) {
System.err.println(ex.getMessage());
return false;
......@@ -86,9 +86,35 @@ public class ImageCompressor extends CompressorDecompressorBase {
e.printStackTrace();
return false;
}
if (planeDataSizes == null) {
System.err.println("Plane data sizes are unknown!");
return false;
}
try (RandomAccessFile raf = new RandomAccessFile(options.getOutputFile(), "rw")) {
raf.seek(PLANE_DATA_SIZES_OFFSET);
writePlaneDataSizes(raf, planeDataSizes);
} catch (IOException ex) {
ex.printStackTrace();
return false;
}
return true;
}
/**
* Write plane data size to compressed file.
*
* @param outStream Compressed file stream.
* @param planeDataSizes Written compressed plane sizes.
* @throws IOException when fails to write plane data size.
*/
private void writePlaneDataSizes(RandomAccessFile outStream, final long[] planeDataSizes) throws IOException {
for (final long planeDataSize : planeDataSizes) {
outStream.writeInt((int) planeDataSize);
}
}
/**
* Create QCMPFile header for compressed file.
......
......@@ -88,8 +88,9 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
* @param compressStream Stream to which compressed data will be written.
* @throws ImageCompressionException When compress process fails.
*/
public void compress(DataOutputStream compressStream) throws ImageCompressionException {
public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
Stopwatch stopwatch = new Stopwatch();
long[] planeDataSizes = new long[options.getImageDimension().getZ()];
final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.hasReferencePlaneIndex();
ScalarQuantizer quantizer = null;
......@@ -161,10 +162,12 @@ public class SQImageCompressor extends CompressorDecompressorBase implements IIm
} catch (Exception ex) {
throw new ImageCompressionException("Unable to write indices to OutBitStream.", ex);
}
// TODO: Fill plane data size
stopwatch.stop();
Log("Plane time: " + stopwatch.getElapsedTimeString());
Log(String.format("Finished processing of plane %d", planeIndex));
}
return planeDataSizes;
}
private int[] loadConfiguredPlanesData() throws ImageCompressionException {
......
......@@ -3,7 +3,9 @@ package azgracompress.compression;
import azgracompress.cli.ParsedCliOptions;
import azgracompress.compression.exception.ImageDecompressionException;
import azgracompress.fileformat.QCMPFileHeader;
import azgracompress.huffman.Huffman;
import azgracompress.io.InBitStream;
import azgracompress.quantization.scalar.ScalarQuantizationCodebook;
import azgracompress.utilities.Stopwatch;
import azgracompress.utilities.TypeConverter;
......@@ -16,17 +18,20 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
super(options);
}
private int[] readScalarQuantizationValues(DataInputStream compressedStream,
final int n) throws ImageDecompressionException {
int[] quantizationValues = new int[n];
private ScalarQuantizationCodebook readScalarQuantizationValues(DataInputStream compressedStream) throws ImageDecompressionException {
int[] quantizationValues = new int[codebookSize];
long[] symbolFrequencies = new long[codebookSize];
try {
for (int i = 0; i < n; i++) {
for (int i = 0; i < codebookSize; i++) {
quantizationValues[i] = compressedStream.readUnsignedShort();
}
for (int i = 0; i < codebookSize; i++) {
symbolFrequencies[i] = compressedStream.readLong();
}
} catch (IOException ioEx) {
throw new ImageDecompressionException("Unable to read quantization values from compressed stream.", ioEx);
}
return quantizationValues;
return new ScalarQuantizationCodebook(quantizationValues, symbolFrequencies);
}
@Override
......@@ -51,6 +56,8 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
public void decompress(DataInputStream compressedStream,
DataOutputStream decompressStream,
QCMPFileHeader header) throws ImageDecompressionException {
final int[] huffmanSymbols = createHuffmanSymbols();
final int codebookSize = (int) Math.pow(2, header.getBitsPerPixel());
final int planeCountForDecompression = header.getImageSizeZ();
......@@ -58,10 +65,13 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
final int planeIndicesDataSize = (int) Math.ceil((planePixelCount * header.getBitsPerPixel()) / 8.0);
int[] quantizationValues = null;
Huffman huffman = null;
if (!header.isCodebookPerPlane()) {
// There is only one codebook.
Log("Loading reference codebook...");
quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
huffman = null;
// TODO(Moravec): Handle loading of Huffman.
//quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
}
Stopwatch stopwatch = new Stopwatch();
......@@ -69,9 +79,13 @@ public class SQImageDecompressor extends CompressorDecompressorBase implements I
stopwatch.restart();
if (header.isCodebookPerPlane()) {
Log("Loading plane codebook...");
quantizationValues = readScalarQuantizationValues(compressedStream, codebookSize);
ScalarQuantizationCodebook codebook = readScalarQuantizationValues(compressedStream);
quantizationValues = codebook.getCentroids();
huffman = new Huffman(huffmanSymbols, codebook.getSymbolFrequencies());
huffman.buildHuffmanTree();
}
assert (quantizationValues != null);
assert (huffman != null);
Log(String.format("Decompressing plane %d...", planeIndex));
byte[] decompressedPlaneData = null;
......
......@@ -85,7 +85,8 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
* @param compressStream Stream to which compressed data will be written.
* @throws ImageCompressionException When compress process fails.
*/
public void compress(DataOutputStream compressStream) throws ImageCompressionException {
public long[] compress(DataOutputStream compressStream) throws ImageCompressionException {
long[] planeDataSizes = new long[options.getImageDimension().getZ()];
Stopwatch stopwatch = new Stopwatch();
final boolean hasGeneralQuantizer = options.hasCodebookCacheFolder() || options.hasReferencePlaneIndex();
VectorQuantizer quantizer = null;
......@@ -149,10 +150,12 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm
} catch (Exception ex) {
throw new ImageCompressionException("Unable to write indices to OutBitStream.", ex);
}
// TODO: Fill plane data size
stopwatch.stop();
Log("Plane time: " + stopwatch.getElapsedTimeString());
Log(String.format("Finished processing of plane %d.", planeIndex));
}
return planeDataSizes;
}
......
......@@ -69,6 +69,12 @@ public class QCMPFileHeader {
outputStream.writeShort(vectorSizeX);
outputStream.writeShort(vectorSizeY);
outputStream.writeShort(vectorSizeZ);
// NOTE(Moravec): Allocate space for plane data sizes. Offset: 23.
for (int i = 0; i < imageSizeZ; i++) {
outputStream.writeInt(0x0);
}
}
public boolean readHeader(DataInputStream inputStream) throws IOException {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment