From 4a227e80484b65c826c1b977443d8f2e8dd026b5 Mon Sep 17 00:00:00 2001 From: Vojtech Moravec <vojtech.moravec.st@vsb.cz> Date: Tue, 22 Sep 2020 12:38:12 +0200 Subject: [PATCH] Implement data wrapping. Implement data wrapping for loadBlock variant with valueAt. We support three different wrapping strategies: LeaveBlank ClampToEdge MirrorerRepeat Those are based on OpenGL texture wrapping. Wrapping is neccessary because otherwise border blocks and voxels and quantized with wrong vectors and BigDataViewer recontruction is horrible. --- .../azgracompress/io/loader/BasicLoader.java | 71 +++++++++++++++++-- .../io/loader/DataWrappingStrategy.java | 19 +++++ .../azgracompress/io/loader/IPlaneLoader.java | 14 ++++ 3 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 src/main/java/azgracompress/io/loader/DataWrappingStrategy.java diff --git a/src/main/java/azgracompress/io/loader/BasicLoader.java b/src/main/java/azgracompress/io/loader/BasicLoader.java index e3a5979..244386b 100644 --- a/src/main/java/azgracompress/io/loader/BasicLoader.java +++ b/src/main/java/azgracompress/io/loader/BasicLoader.java @@ -2,12 +2,15 @@ package azgracompress.io.loader; import azgracompress.data.*; +import javax.print.attribute.standard.RequestingUserName; import java.io.IOException; public abstract class BasicLoader { protected final V3i dims; protected int threadCount = 1; + private DataWrappingStrategy wrappingStrategy = DataWrappingStrategy.MirroredRepeat; + protected BasicLoader(final V3i datasetDims) { this.dims = datasetDims; } @@ -16,6 +19,16 @@ public abstract class BasicLoader { return dims; } + + public DataWrappingStrategy getWrappingStrategy() { + return wrappingStrategy; + } + + public void setWrappingStrategy(DataWrappingStrategy strategy) { + wrappingStrategy = strategy; + } + + /** * Abstract method to load specified plane data. * @@ -123,16 +136,60 @@ public abstract class BasicLoader { return blocks; } + private void loadBlock(final int[] block, final int planeIndex, final int blockXOffset, final int blockYOffset, final V2i blockDim) { int srcX, srcY; for (int y = 0; y < blockDim.getY(); y++) { srcY = blockYOffset + y; - if (srcY >= dims.getY()) - break; + + // Row overflow + if (srcY >= dims.getY()) { + + if (wrappingStrategy == DataWrappingStrategy.LeaveBlank) + break; + + if (wrappingStrategy == DataWrappingStrategy.ClampToEdge) { + final int srcRow = dims.getY() - 1; + final int dstOffset = y * blockDim.getX(); + for (int x = 0; x < blockDim.getX(); x++) { + srcX = (blockXOffset + x); + if (srcX >= dims.getX()) + srcX = dims.getX() - 1; + block[dstOffset + x] = valueAt(planeIndex, Block.index(srcX, srcRow, dims.getX())); + } + continue; + } else if (wrappingStrategy == DataWrappingStrategy.MirroredRepeat) { + final int srcRow = dims.getY() - ((srcY - dims.getY()) + 1); + final int dstOffset = y * blockDim.getX(); + for (int x = 0; x < blockDim.getX(); x++) { + srcX = (blockXOffset + x); + if (srcX >= dims.getX()) + srcX = dims.getX() - 1; + block[dstOffset + x] = valueAt(planeIndex, Block.index(srcX, srcRow, dims.getX())); + } + continue; + } + } + for (int x = 0; x < blockDim.getX(); x++) { srcX = blockXOffset + x; - if (srcX >= dims.getX()) - break; + + // Column overflow. + if (srcX >= dims.getX()) { + + if (wrappingStrategy == DataWrappingStrategy.LeaveBlank) + break; + if (wrappingStrategy == DataWrappingStrategy.ClampToEdge) { + block[Block.index(x, y, blockDim.getX())] = valueAt(planeIndex, Block.index(dims.getX() - 1, srcY, dims.getX())); + continue; + } else if (wrappingStrategy == DataWrappingStrategy.MirroredRepeat) { + + block[Block.index(x, y, blockDim.getX())] = + valueAt(planeIndex, Block.index(dims.getX() - ((srcX - dims.getX()) + 1), srcY, dims.getX())); + continue; + } + + } block[Block.index(x, y, blockDim.getX())] = valueAt(planeIndex, Block.index(srcX, srcY, dims.getX())); } } @@ -142,8 +199,10 @@ public abstract class BasicLoader { int srcX, srcY; for (int y = 0; y < blockDim.getY(); y++) { srcY = blockYOffset + y; - if (srcY >= dims.getY()) - break; + if (srcY >= dims.getY()) { + // handleBlockRowOverflow(block, blockDim, y, srcY); + continue; + } for (int x = 0; x < blockDim.getX(); x++) { srcX = blockXOffset + x; if (srcX >= dims.getX()) diff --git a/src/main/java/azgracompress/io/loader/DataWrappingStrategy.java b/src/main/java/azgracompress/io/loader/DataWrappingStrategy.java new file mode 100644 index 0000000..fc2015e --- /dev/null +++ b/src/main/java/azgracompress/io/loader/DataWrappingStrategy.java @@ -0,0 +1,19 @@ +package azgracompress.io.loader; + +/** + * What to do when blocks or voxels extend outside the source image. These parameters are based on OpenGL texture wrapping options. + */ +public enum DataWrappingStrategy { + /** + * Pixels outside the source range are left blank, value 0. + */ + LeaveBlank, + /** + * The edge value is repeated. + */ + ClampToEdge, + /** + * Pixels will be repeated by mirrored strategy. + */ + MirroredRepeat +} diff --git a/src/main/java/azgracompress/io/loader/IPlaneLoader.java b/src/main/java/azgracompress/io/loader/IPlaneLoader.java index e88a469..5db29c5 100644 --- a/src/main/java/azgracompress/io/loader/IPlaneLoader.java +++ b/src/main/java/azgracompress/io/loader/IPlaneLoader.java @@ -22,6 +22,20 @@ public interface IPlaneLoader { return false; } + /** + * Get data wrapping strategy configured for this loader. + * + * @return Wrapping strategy. + */ + DataWrappingStrategy getWrappingStrategy(); + + /** + * Configure data wrapping strategy for this loader. + * + * @param strategy Wrapping strategy. + */ + void setWrappingStrategy(DataWrappingStrategy strategy); + /** * Get dimensions of the image, for which the loader was created. * -- GitLab