diff --git a/src/main/java/azgracompress/compression/VQImageCompressor.java b/src/main/java/azgracompress/compression/VQImageCompressor.java index 714857e30d6a5463d6d18ab01a16315becbb29a1..6292c15702129daba0064f278bb67965f285e03e 100644 --- a/src/main/java/azgracompress/compression/VQImageCompressor.java +++ b/src/main/java/azgracompress/compression/VQImageCompressor.java @@ -1,6 +1,7 @@ package azgracompress.compression; import azgracompress.cache.QuantizationCacheManager; +import azgracompress.data.Chunk3D; import azgracompress.fileformat.QuantizationType; import azgracompress.io.InputData; import azgracompress.compression.exception.ImageCompressionException; @@ -236,11 +237,7 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm planeIndex), e); } - System.arraycopy(planeVectors, - 0, - trainData, - (planeCounter * chunkCountPerPlane), - chunkCountPerPlane); + System.arraycopy(planeVectors, 0, trainData, (planeCounter * chunkCountPerPlane), chunkCountPerPlane); ++planeCounter; } } @@ -273,8 +270,19 @@ public class VQImageCompressor extends CompressorDecompressorBase implements IIm reportStatusToListeners("Operation completed."); } + public long[] compressVoxels(DataOutputStream compressStream) throws ImageCompressionException { - + int[][] voxels; + try { + IPlaneLoader loader = PlaneLoaderFactory.getPlaneLoaderForInputFile(options.getInputDataInfo()); + final int[] data = loader.loadAllPlanesU16Data(); + Chunk3D bigVoxel = new Chunk3D(options.getInputDataInfo().getDimensions(), data); + voxels = bigVoxel.divideInto3DVectors(options.getQuantizationVector()); + } catch (Exception e) { + throw new ImageCompressionException("Unable to create data loader or load image data.", e); + } + return null; + } } diff --git a/src/main/java/azgracompress/data/Chunk2D.java b/src/main/java/azgracompress/data/Chunk2D.java index e562d437d6798ce225d61cc651eef3d77e824a2f..51d6a480366baf352d1a10df50cab729d6304ad3 100644 --- a/src/main/java/azgracompress/data/Chunk2D.java +++ b/src/main/java/azgracompress/data/Chunk2D.java @@ -47,8 +47,8 @@ public class Chunk2D { * @return Index inside chunk dimension data array. */ private int index(final int x, final int y, final V2i chunkDims) { - assert (x >= 0 && x < dims.getX()) : "Index X out of bounds."; - assert (y >= 0 && y < dims.getY()) : "Index Y out of bounds."; + assert (x >= 0 && x < chunkDims.getX()) : "Index X out of bounds."; + assert (y >= 0 && y < chunkDims.getY()) : "Index Y out of bounds."; return (y * chunkDims.getX()) + x; } diff --git a/src/main/java/azgracompress/data/Chunk3D.java b/src/main/java/azgracompress/data/Chunk3D.java index a3539dc5c9bea8e46a85929921d789f7b12320a4..7b90b37af8424eb48fb57264b13df3dd61a04dbc 100644 --- a/src/main/java/azgracompress/data/Chunk3D.java +++ b/src/main/java/azgracompress/data/Chunk3D.java @@ -1,7 +1,5 @@ package azgracompress.data; -import java.util.Arrays; - public class Chunk3D { private final int FILL_VALUE = 0; private final int[] data; @@ -40,9 +38,9 @@ public class Chunk3D { * @return Index inside chunk dimension data array. */ private int index(final int x, final int y, final int z, final V3i chunkDims) { - assert (x >= 0 && x < dims.getX()) : "Index X out of bounds."; - assert (y >= 0 && y < dims.getY()) : "Index Y out of bounds."; - assert (z >= 0 && z < dims.getZ()) : "Index Z out of bounds."; + assert (x >= 0 && x < chunkDims.getX()) : "Index X out of bounds."; + assert (y >= 0 && y < chunkDims.getY()) : "Index Y out of bounds."; + assert (z >= 0 && z < chunkDims.getZ()) : "Index Z out of bounds."; // NOTE(Moravec): Description of the following calculation // plane index * plane pixel count @@ -60,7 +58,7 @@ public class Chunk3D { // V // colOffset = x; - return (chunkDims.getZ() * (chunkDims.getX() * chunkDims.getY())) + (y * chunkDims.getX()) + x; + return (z * (chunkDims.getX() * chunkDims.getY())) + (y * chunkDims.getX()) + x; } @@ -163,8 +161,4 @@ public class Chunk3D { public int[] getData() { return data; } - - public void zeroData() { - Arrays.fill(data, 0); - } } diff --git a/src/main/java/azgracompress/io/loader/IPlaneLoader.java b/src/main/java/azgracompress/io/loader/IPlaneLoader.java index b629191af7641a4743ddf35e0ac3d0712d9b52c4..6e3ff5f9b9e8a3373077c188b47ed89a994e970f 100644 --- a/src/main/java/azgracompress/io/loader/IPlaneLoader.java +++ b/src/main/java/azgracompress/io/loader/IPlaneLoader.java @@ -1,6 +1,7 @@ package azgracompress.io.loader; import azgracompress.data.ImageU16; +import azgracompress.data.V3i; import java.io.IOException; @@ -10,4 +11,8 @@ public interface IPlaneLoader { int[] loadPlanesU16Data(int[] planes) throws IOException; int[] loadAllPlanesU16Data() throws IOException; + + default int[][] loadVoxels(final V3i voxelDim) throws IOException { + throw new IOException("NOT IMPLEMENTED"); + } } diff --git a/src/main/java/azgracompress/io/loader/ImageJBufferLoader.java b/src/main/java/azgracompress/io/loader/ImageJBufferLoader.java index 6f6b42a187ea4dda5eca466e7486432010c30724..b9e70a22e393f8c171e6f92398d8ae0e0f04c733 100644 --- a/src/main/java/azgracompress/io/loader/ImageJBufferLoader.java +++ b/src/main/java/azgracompress/io/loader/ImageJBufferLoader.java @@ -1,5 +1,6 @@ package azgracompress.io.loader; +import azgracompress.data.Chunk3D; import azgracompress.data.ImageU16; import azgracompress.data.V3i; import azgracompress.io.BufferInputData; @@ -39,8 +40,8 @@ public class ImageJBufferLoader implements IPlaneLoader { final int planePixelCount = bufferInputData.getDimensions().getX() * bufferInputData.getDimensions().getY(); final short[] srcBuffer = (short[]) bufferInputData.getPixelBuffer(plane); return new ImageU16(bufferInputData.getDimensions().getX(), - bufferInputData.getDimensions().getY(), - copyShortArray(srcBuffer, planePixelCount)); + bufferInputData.getDimensions().getY(), + copyShortArray(srcBuffer, planePixelCount)); } default: throw new IOException("Unable to load unsupported pixel type."); @@ -106,4 +107,133 @@ public class ImageJBufferLoader implements IPlaneLoader { throw new IOException("Unable to load unsupported pixel type."); } } + + @Override + public int[][] loadVoxels(V3i voxelDim) throws IOException { + + switch (bufferInputData.getPixelType()) { + case Gray16: { + final V3i dims = bufferInputData.getDimensions(); + final int voxelSize = (int) voxelDim.multiplyTogether(); + final int voxelCount = Chunk3D.calculateRequiredChunkCount(bufferInputData.getDimensions(), voxelDim); + + int[][] voxels = new int[voxelCount][voxelSize]; + int voxelIndex = 0; + + for (int chunkZOffset = 0; chunkZOffset < dims.getZ(); chunkZOffset += voxelDim.getZ()) { + for (int chunkYOffset = 0; chunkYOffset < dims.getY(); chunkYOffset += voxelDim.getY()) { + for (int chunkXOffset = 0; chunkXOffset < dims.getX(); chunkXOffset += voxelDim.getX()) { + copyDataToVector(voxels[voxelIndex++], voxelDim, chunkXOffset, chunkYOffset, chunkZOffset); + } + } + } + return voxels; + } + default: + throw new IOException("Unable to load unsupported pixel type."); + } + } + + private int readDataAt(final int x, final int y, final int z) { + + final short[] srcBuffer = (short[]) bufferInputData.getPixelBuffer(z); + return srcBuffer[(y * bufferInputData.getDimensions().getX()) + x]; + } + + /** + * Copy this chunk data to chunk vector. + * + * @param vector Chunk vector. + * @param qVectorDims Dimensions of the vector. + * @param chunkXOffset Chunk X offset + * @param chunkYOffset Chunk Y offset. + * @param chunkZOffset Chunk Z offset. + */ + private void copyDataToVector(int[] vector, + final V3i qVectorDims, + final int chunkXOffset, + final int chunkYOffset, + final int chunkZOffset) { + int srcZ; + for (int z = 0; z < qVectorDims.getZ(); z++) { + srcZ = chunkZOffset + z; + fillVoxelFromPlane(vector, qVectorDims, srcZ, z, chunkXOffset, chunkYOffset); + } + } + + private void fillVoxelFromPlane(int[] vector, + final V3i qVectorDims, + final int srcZ, + final int z, + final int chunkXOffset, + final int chunkYOffset) { + + if (srcZ >= bufferInputData.getDimensions().getZ()) + return; + + final short[] srcBuffer = (short[]) bufferInputData.getPixelBuffer(srcZ); + final int width = bufferInputData.getDimensions().getX(); + + int srcX, srcY; + for (int y = 0; y < qVectorDims.getY(); y++) { + srcY = chunkYOffset + y; + for (int x = 0; x < qVectorDims.getX(); x++) { + srcX = chunkXOffset + x; + final int dstIndex = index(x, y, z, qVectorDims); + vector[dstIndex] = isInside(srcX, srcY, srcZ) ? srcBuffer[(srcY * width) + srcX] : 0; + } + } + } + + private boolean isInside(final int x, final int y, final int z) { + return (((x >= 0) && (x < bufferInputData.getDimensions().getX())) && (y >= 0) && + (y < bufferInputData.getDimensions().getY()) && (z >= 0) && + (z < bufferInputData.getDimensions().getZ())); + } + + /** + * Calculate the index inside data array. + * + * @param x Zero based x coordinate. + * @param y Zero based y coordinate. + * @param z Zero based z coordinate. + * @return Index inside data array. + */ + private int index(final int x, final int y, final int z) { + return index(x, y, z, bufferInputData.getDimensions()); + } + + /** + * Calculate the index inside array of dimensions specified by chunkDims. + * + * @param x Zero based x coordinate. + * @param y Zero based y coordinate. + * @param z Zero based z coordinate. + * @param chunkDims Chunk dimensions. + * @return Index inside chunk dimension data array. + */ + private int index(final int x, final int y, final int z, final V3i chunkDims) { + assert (x >= 0 && x < chunkDims.getX()) : "Index X out of bounds."; + assert (y >= 0 && y < chunkDims.getY()) : "Index Y out of bounds."; + assert (z >= 0 && z < chunkDims.getZ()) : "Index Z out of bounds."; + + // NOTE(Moravec): Description of the following calculation + // plane index * plane pixel count + // | | + // V V + // planeOffset = chunkDims.getZ() * (chunkDims.getX() * chunkDims.getY()) + + // row * pixels in row + // | | + // V V + // rowOffset = y * chunkDims.getX(); + + // column + // | + // V + // colOffset = x; + + return (z * (chunkDims.getX() * chunkDims.getY())) + (y * chunkDims.getX()) + x; + } } +