diff --git a/src/main/java/azgracompress/compression/ImageCompressor.java b/src/main/java/azgracompress/compression/ImageCompressor.java index 9f0ed92889886b34ca612f49e23fd08a3be5ba59..9d4b817291d0e44f8af6d52953a9a53eb5185356 100644 --- a/src/main/java/azgracompress/compression/ImageCompressor.java +++ b/src/main/java/azgracompress/compression/ImageCompressor.java @@ -1,21 +1,11 @@ package azgracompress.compression; import azgracompress.cli.ParsedCliOptions; -import azgracompress.data.Chunk2D; -import azgracompress.data.ImageU16; -import azgracompress.data.V2i; import azgracompress.fileformat.QCMPFileHeader; -import azgracompress.io.OutBitStream; -import azgracompress.io.RawDataIO; -import azgracompress.quantization.vector.CodebookEntry; -import azgracompress.quantization.vector.LBGResult; -import azgracompress.quantization.vector.LBGVectorQuantizer; -import azgracompress.quantization.vector.VectorQuantizer; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileOutputStream; -import java.io.IOException; public class ImageCompressor extends CompressorDecompressorBase { @@ -32,96 +22,37 @@ public class ImageCompressor extends CompressorDecompressorBase { Log(String.format("Compression with BPP = %d", options.getBitsPerPixel())); FileOutputStream fos = new FileOutputStream(options.getOutputFile(), false); - DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(fos, 8192)); + DataOutputStream compressStream = new DataOutputStream(new BufferedOutputStream(fos, 8192)); // Create and write header to output stream. final QCMPFileHeader header = createHeader(); - header.writeHeader(dataOutputStream); + header.writeHeader(compressStream); boolean compressionResult = true; switch (options.getQuantizationType()) { case Scalar: { SQImageCompressor compressor = new SQImageCompressor(options); - compressor.compress(dataOutputStream); + compressor.compress(compressStream); } break; case Vector1D: - case Vector2D: - compressUsingVectorQuantization(dataOutputStream); - break; + case Vector2D: { + VQImageCompressor compressor = new VQImageCompressor(options); + compressor.compress(compressStream); + } + break; case Vector3D: case Invalid: throw new Exception("Not supported quantization type"); } - dataOutputStream.flush(); + compressStream.flush(); fos.flush(); - dataOutputStream.close(); + compressStream.close(); fos.close(); } - private int[][] getPlaneVectors(final ImageU16 plane) { - final V2i qVector = options.getVectorDimension(); - - if (qVector.getY() > 1) { - // 2D Quantization. - return Chunk2D.chunksAsImageVectors(plane.as2dChunk().divideIntoChunks(qVector)); - } else { - // 1D Quantization. - return plane.as2dChunk().divideInto1DVectors(qVector.getX()); - } - } - - private VectorQuantizer trainVectorQuantizerFromPlaneVectors(final int[][] planeVectors) { - LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeVectors, codebookSize); - LBGResult vqResult = vqInitializer.findOptimalCodebook(false); - // TODO(Moravec): If verbose ask initializer for result. - return new VectorQuantizer(vqResult.getCodebook()); - } - - private void compressUsingVectorQuantization(DataOutputStream compressStream) throws Exception { - VectorQuantizer quantizer = null; - if (options.hasReferencePlaneIndex()) { - final ImageU16 referencePlane = RawDataIO.loadImageU16(options.getInputFile(), - options.getImageDimension(), - options.getReferencePlaneIndex()); - - Log("Creating codebook from reference plane..."); - final int[][] refPlaneVectors = getPlaneVectors(referencePlane); - quantizer = trainVectorQuantizerFromPlaneVectors(refPlaneVectors); - writeCodebookToOutputStream(quantizer, compressStream); - Log("Wrote reference codebook."); - } - - final int[] planeIndices = getPlaneIndicesForCompression(); - - for (final int planeIndex : planeIndices) { - Log(String.format("Loading plane %d...", planeIndex)); - final ImageU16 plane = RawDataIO.loadImageU16(options.getInputFile(), - options.getImageDimension(), - planeIndex); - - final int[][] planeVectors = getPlaneVectors(plane); - - if (!options.hasReferencePlaneIndex()) { - Log("Creating plane codebook..."); - quantizer = trainVectorQuantizerFromPlaneVectors(planeVectors); - writeCodebookToOutputStream(quantizer, compressStream); - Log("Wrote plane codebook."); - } - - assert (quantizer != null); - - Log("Writing quantization indices..."); - final int[] indices = quantizer.quantizeIntoIndices(planeVectors); - - OutBitStream outBitStream = new OutBitStream(compressStream, options.getBitsPerPixel(), 2048); - outBitStream.write(indices); - outBitStream.flush(); - Log(String.format("Finished processing of plane %d", planeIndex)); - } - } private QCMPFileHeader createHeader() { QCMPFileHeader header = new QCMPFileHeader(); @@ -144,14 +75,5 @@ public class ImageCompressor extends CompressorDecompressorBase { return header; } - private void writeCodebookToOutputStream(final VectorQuantizer quantizer, - DataOutputStream compressStream) throws IOException { - final CodebookEntry[] codebook = quantizer.getCodebook(); - for (final CodebookEntry entry : codebook) { - final int[] entryVector = entry.getVector(); - for (final int vecVal : entryVector) { - compressStream.writeShort(vecVal); - } - } - } + } diff --git a/src/main/java/azgracompress/compression/SQImageCompressor.java b/src/main/java/azgracompress/compression/SQImageCompressor.java index fd5a59e61f7b8def46d732f32ff438929c09011d..8d0881e3d11f3462ed8d34ed8cc7283c3ebe5564 100644 --- a/src/main/java/azgracompress/compression/SQImageCompressor.java +++ b/src/main/java/azgracompress/compression/SQImageCompressor.java @@ -46,7 +46,7 @@ public class SQImageCompressor extends CompressorDecompressorBase { } /** - * Compress the image file specified by parsed CLI options using scalar quantization.. + * Compress the image file specified by parsed CLI options using scalar quantization. * * @param compressStream Stream to which compressed data will be written. * @throws Exception When compress process fails. diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java index cfb69b8ea0072a5f59d5b66288a9a91dda5713c8..86f4a0b3b018a14b5832ded77fffc0bbf88a69b3 100644 --- a/src/main/java/azgracompress/compression/VQImageCompressor.java +++ b/src/main/java/azgracompress/compression/VQImageCompressor.java @@ -1,4 +1,121 @@ package azgracompress.compression; -public class VQImageCompressor { +import azgracompress.cli.ParsedCliOptions; +import azgracompress.data.Chunk2D; +import azgracompress.data.ImageU16; +import azgracompress.data.V2i; +import azgracompress.io.OutBitStream; +import azgracompress.io.RawDataIO; +import azgracompress.quantization.vector.CodebookEntry; +import azgracompress.quantization.vector.LBGResult; +import azgracompress.quantization.vector.LBGVectorQuantizer; +import azgracompress.quantization.vector.VectorQuantizer; + +import java.io.DataOutputStream; +import java.io.IOException; + +public class VQImageCompressor extends CompressorDecompressorBase { + + public VQImageCompressor(ParsedCliOptions options) { + super(options); + } + + /** + * Get image vectors from the plane. Vector dimensions are specified by parsed CLI options. + * + * @param plane Image plane. + * @return Image vectors. + */ + private int[][] getPlaneVectors(final ImageU16 plane) { + final V2i qVector = options.getVectorDimension(); + + if (qVector.getY() > 1) { + // 2D Quantization, return `matrices`. + return Chunk2D.chunksAsImageVectors(plane.as2dChunk().divideIntoChunks(qVector)); + } else { + // 1D Quantization, return row vectors. + return plane.as2dChunk().divideInto1DVectors(qVector.getX()); + } + } + + /** + * Train vector quantizer from plane vectors. + * + * @param planeVectors Image vectors. + * @return Trained vector quantizer with codebook of set size. + */ + private VectorQuantizer trainVectorQuantizerFromPlaneVectors(final int[][] planeVectors) { + LBGVectorQuantizer vqInitializer = new LBGVectorQuantizer(planeVectors, codebookSize); + LBGResult vqResult = vqInitializer.findOptimalCodebook(false); + return new VectorQuantizer(vqResult.getCodebook()); + } + + /** + * Write the vector codebook to the compress stream. + * + * @param quantizer Quantizer with the codebook. + * @param compressStream Stream with compressed data. + * @throws IOException When unable to write quantizer. + */ + private void writeQuantizerToCompressStream(final VectorQuantizer quantizer, + DataOutputStream compressStream) throws IOException { + final CodebookEntry[] codebook = quantizer.getCodebook(); + for (final CodebookEntry entry : codebook) { + final int[] entryVector = entry.getVector(); + for (final int vecVal : entryVector) { + compressStream.writeShort(vecVal); + } + } + } + + /** + * Compress the image file specified by parsed CLI options using vector quantization. + * + * @param compressStream Stream to which compressed data will be written. + * @throws Exception When compress process fails. + */ + public void compress(DataOutputStream compressStream) throws Exception { + VectorQuantizer quantizer = null; + if (options.hasReferencePlaneIndex()) { + final ImageU16 referencePlane = RawDataIO.loadImageU16(options.getInputFile(), + options.getImageDimension(), + options.getReferencePlaneIndex()); + + Log("Creating codebook from reference plane..."); + final int[][] refPlaneVectors = getPlaneVectors(referencePlane); + quantizer = trainVectorQuantizerFromPlaneVectors(refPlaneVectors); + writeQuantizerToCompressStream(quantizer, compressStream); + Log("Wrote reference codebook."); + } + + final int[] planeIndices = getPlaneIndicesForCompression(); + + for (final int planeIndex : planeIndices) { + Log(String.format("Loading plane %d...", planeIndex)); + final ImageU16 plane = RawDataIO.loadImageU16(options.getInputFile(), + options.getImageDimension(), + planeIndex); + + final int[][] planeVectors = getPlaneVectors(plane); + + if (!options.hasReferencePlaneIndex()) { + Log("Creating plane codebook..."); + quantizer = trainVectorQuantizerFromPlaneVectors(planeVectors); + writeQuantizerToCompressStream(quantizer, compressStream); + Log("Wrote plane codebook."); + } + + assert (quantizer != null); + + Log("Writing quantization indices..."); + final int[] indices = quantizer.quantizeIntoIndices(planeVectors); + + OutBitStream outBitStream = new OutBitStream(compressStream, options.getBitsPerPixel(), 2048); + outBitStream.write(indices); + outBitStream.flush(); + Log(String.format("Finished processing of plane %d", planeIndex)); + } + } + + }