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

Basic preparation for second version of QCMP file header.

parent 7a06c09a
Branches
No related tags found
No related merge requests found
......@@ -44,7 +44,7 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
*/
@Override
public boolean validateHeader() {
if (!magicValue.equals(MAGIC_VALUE))
if (!magicValue.equals(getMagicValue()))
return false;
if (bitsPerCodebookIndex == 0)
......@@ -139,26 +139,10 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
builder.append("Header is:\t\t invalid\n");
return;
}
builder.append("HeaderVersion\t\t: ").append(VERSION).append('\n');
builder.append("Magic value\t\t: ").append(magicValue).append('\n');
builder.append("Quantization type\t: ");
switch (quantizationType) {
case Scalar:
builder.append("Scalar\n");
break;
case Vector1D:
builder.append("Vector1D\n");
break;
case Vector2D:
builder.append("Vector2D\n");
break;
case Vector3D:
builder.append("Vector3D\n");
break;
case Invalid:
builder.append("INVALID\n");
break;
}
builder.append("HeaderVersion\t\t: ").append(getHeaderVersion()).append('\n');
builder.append("Magic value\t\t: ").append(getMagicValue()).append('\n');
builder.append("Quantization type\t: ").append(quantizationType).append('\n');
builder.append("Bits per pixel\t\t: ").append(bitsPerCodebookIndex).append('\n');
builder.append("Codebook\t\t: ").append(codebookPerPlane ? "one per plane\n" : "one for all\n");
......@@ -176,6 +160,10 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
.append(vectorSizeY).append('x')
.append(vectorSizeZ).append('\n');
printFileSizeInfo(builder, inputFile);
}
protected void printFileSizeInfo(final StringBuilder builder, final String inputFile) {
final long headerSize = getHeaderSize();
final long fileSize = new File(inputFile).length();
final long dataSize = fileSize - headerSize;
......@@ -190,7 +178,7 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
builder.append("Data size\t\t: ");
Utils.prettyPrintFileSize(builder, dataSize).append(correctFileSize ? "(correct)\n" : "(INVALID)\n");
final long pixelCount = imageSizeX * imageSizeY * imageSizeZ;
final long pixelCount = (long) imageSizeX * imageSizeY * imageSizeZ;
final long uncompressedSize = 2 * pixelCount; // We assert 16 bit (2 byte) pixel.
final double compressionRatio = (double) fileSize / (double) uncompressedSize;
builder.append(String.format("Compression ratio\t: %.4f\n", compressionRatio));
......@@ -201,7 +189,7 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
builder.append("\n=== Input file is ").append(correctFileSize ? "VALID" : "INVALID").append(" ===\n");
}
private long calculateDataSizeForSq() {
protected long calculateDataSizeForSq() {
final int LONG_BYTES = 8;
// Quantization value count.
final int codebookSize = (int) Math.pow(2, bitsPerCodebookIndex);
......@@ -218,7 +206,7 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
return (codebookDataSize + totalPlaneDataSize);
}
private long calculateDataSizeForVq() {
protected long calculateDataSizeForVq() {
final int LONG_BYTES = 8;
// Vector count in codebook
final int codebookSize = (int) Math.pow(2, bitsPerCodebookIndex);
......@@ -252,7 +240,7 @@ public class QCMPFileHeaderV1 implements IFileHeader, Cloneable {
@Override
public String getMagicValue() {
return magicValue;
return MAGIC_VALUE;
}
//endregion
......
package cz.it4i.qcmp.fileformat;
import cz.it4i.qcmp.U16;
import cz.it4i.qcmp.compression.VQImageCompressor;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class QCMPFileHeaderV2 extends QCMPFileHeaderV1 {
//region Constants
private static final int VERSION = 2;
private static final int BASE_QCMP_HEADER_SIZE = 50;
public static final String MAGIC_VALUE = "QCMPFLV2";
//endregion
//region Header fields
private int channelCount;
private int timepointCount;
private int metadataSize;
private byte[] metadata;
//endregion
//region IFileHeader implementation
/**
* Validate that all header values are in their valid range.
*
* @return True if this is valid QCMPFILE header.
*/
@Override
public boolean validateHeader() {
if (!super.validateHeader())
return false;
if (!U16.isInRange(channelCount))
return false;
if (!U16.isInRange(timepointCount))
return false;
return metadataSize >= 0;
}
@Override
public void writeToStream(final DataOutputStream outputStream) throws IOException {
// TODO
}
@Override
public void readFromStream(final DataInputStream inputStream) throws IOException {
// TODO
}
@Override
public int getHeaderVersion() {
return VERSION;
}
@Override
public void report(final StringBuilder builder, final String inputFile) {
if (!validateHeader()) {
builder.append("Header is:\t\t invalid\n");
return;
}
builder.append("HeaderVersion\t\t: ").append(getHeaderVersion()).append('\n');
builder.append("Magic value\t\t: ").append(getMagicValue()).append('\n');
builder.append("Quantization type\t: ").append(quantizationType).append('\n');
builder.append("Bits per pixel\t\t: ").append(bitsPerCodebookIndex).append('\n');
builder.append("Codebook\t\t: ").append(codebookPerPlane ? "one per plane\n" : "one for all\n");
final int codebookSize = (int) Math.pow(2, bitsPerCodebookIndex);
builder.append("Codebook size\t\t: ").append(codebookSize).append('\n');
builder.append("Image stack size\t: ")
.append(imageSizeX).append('x')
.append(imageSizeY).append('x')
.append(imageSizeZ).append('x')
.append(channelCount).append('x')
.append(timepointCount).append(" [XxYxZxCxT]").append('\n');
builder.append("Quantization vector\t: ")
.append(vectorSizeX).append('x')
.append(vectorSizeY).append('x')
.append(vectorSizeZ).append('\n');
builder.append("MetadataSize\t: ").append(metadataSize).append('\n');
printFileSizeInfo(builder, inputFile);
}
@Override
protected long calculateDataSizeForSq() {
// TODO(Moravec): Fix this calculation. Size of the huffman tree will be added to chunk size.
final int LONG_BYTES = 8;
// Quantization value count.
final int codebookSize = (int) Math.pow(2, bitsPerCodebookIndex);
// Total codebook size in bytes. Also symbol frequencies for Huffman.
final long codebookDataSize = ((2 * codebookSize) + (LONG_BYTES * codebookSize)) * (codebookPerPlane ? imageSizeZ : 1);
// Indices are encoded using huffman. Plane data size is written in the header.
long totalPlaneDataSize = 0;
for (final long planeDataSize : planeDataSizes) {
totalPlaneDataSize += planeDataSize;
}
return (codebookDataSize + totalPlaneDataSize);
}
@Override
protected long calculateDataSizeForVq() {
// TODO(Moravec): Fix this calculation. Size of the huffman tree will be added to chunk size.
final int LONG_BYTES = 8;
// Vector count in codebook
final int codebookSize = (int) Math.pow(2, bitsPerCodebookIndex);
// Single vector size in bytes.
final int vectorDataSize = 2 * vectorSizeX * vectorSizeY * vectorSizeZ;
// Total codebook size in bytes.
final long codebookDataSize = ((codebookSize * vectorDataSize) + (codebookSize * LONG_BYTES)) * (codebookPerPlane ? imageSizeZ : 1);
// Indices are encoded using huffman. Plane data size is written in the header.
long totalPlaneDataSize = 0;
for (final long planeDataSize : planeDataSizes) {
totalPlaneDataSize += planeDataSize;
}
return (codebookDataSize + totalPlaneDataSize);
}
@Override
public String getMagicValue() {
return MAGIC_VALUE;
}
//endregion
//region Cloneable implementation
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public QCMPFileHeaderV2 copyOf() {
try {
return (QCMPFileHeaderV2) this.clone();
} catch (final CloneNotSupportedException e) {
return null;
}
}
//endregion
//region Getters and Setters
public int getChannelCount() {
return channelCount;
}
public void setChannelCount(final int channelCount) {
this.channelCount = channelCount;
}
public int getTimepointCount() {
return timepointCount;
}
public void setTimepointCount(final int timepointCount) {
this.timepointCount = timepointCount;
}
public int getMetadataSize() {
return metadataSize;
}
public void setMetadataSize(final int metadataSize) {
this.metadataSize = metadataSize;
}
public byte[] getMetadata() {
return metadata;
}
public void setMetadata(final byte[] metadata) {
this.metadata = metadata;
}
public long getHeaderSize() {
final int chunkCount = (quantizationType != QuantizationType.Vector3D)
? imageSizeZ
: VQImageCompressor.calculateVoxelLayerCount(imageSizeZ, vectorSizeZ);
return BASE_QCMP_HEADER_SIZE + metadataSize + (chunkCount * 4L);
}
//endregion
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment