Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDT: add support for zipped data #6

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 51 additions & 16 deletions src/main/java/io/scif/lifesci/SDTFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@
import io.scif.util.FormatTools;

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipInputStream;

import net.imagej.axis.Axes;
import net.imagej.axis.CalibratedAxis;
import net.imglib2.Interval;

import org.scijava.io.handle.DataHandle;
import org.scijava.io.handle.DataHandleInputStream;
import org.scijava.io.location.Location;
import org.scijava.plugin.Plugin;
import org.scijava.util.Bytes;
Expand Down Expand Up @@ -224,7 +227,7 @@ protected void typedParse(final DataHandle<Location> stream,
{
stream.setLittleEndian(true);

log().info("Reading SDT header");
log().debug("Reading SDT header");

// read file header information
final SDTInfo info = new SDTInfo(stream, meta.getTable());
Expand Down Expand Up @@ -271,10 +274,12 @@ public ByteArrayPlane openPlane(final int imageIndex, final long planeIndex,
final int sizeY = (int) m.get(imageIndex).getAxisLength(Axes.Y);
final int bpp = FormatTools.getBytesPerPixel(m.get(imageIndex)
.getPixelType());
// bytes per transient
final int bpt = bpp * m.getTimeBins();
final boolean little = m.get(imageIndex).isLittleEndian();

final int paddedWidth = sizeX + ((4 - (sizeX % 4)) % 4);
final int planeSize = paddedWidth * sizeY * m.getTimeBins() * bpp;
final int planeSize = paddedWidth * sizeY * bpt;

final int x = (int) bounds.min(m.get(imageIndex).getAxisIndex(Axes.X)), //
y = (int) bounds.min(m.get(imageIndex).getAxisIndex(Axes.Y)), //
Expand All @@ -284,7 +289,7 @@ public ByteArrayPlane openPlane(final int imageIndex, final long planeIndex,
final boolean merge = m.mergeIntensity();
// Csarseven data has to read the entire image, so we may need to crop out
// the undesired region
byte[] b = !merge ? buf : new byte[sizeY * sizeX * m.getTimeBins() * bpp];
byte[] b = !merge ? buf : new byte[sizeY * sizeX * bpt];

final SDTInfo info = m.getSDTInfo();

Expand Down Expand Up @@ -314,7 +319,7 @@ else if (info.noOfDataBlocks > 1) {
int tmpOff = info.dataBlockOffs;
final boolean crop = x == 0 && y == 0 && w == sizeX && y == sizeY;
if (crop && !merge) {
b = new byte[sizeY * sizeX * m.getTimeBins() * bpp];
b = new byte[sizeY * sizeX * bpt];
}
// Data is stored by row, bottom row first
for (int row = h - 1; row >= 0; row--) {
Expand All @@ -324,11 +329,10 @@ else if (info.noOfDataBlocks > 1) {
info.readBlockHeader(getHandle());
// Skip to the desired plane (channel)
// Assuming channels are interleaved
getHandle().skip(planeIndex * m.getTimeBins() * bpp);
getHandle().skip(planeIndex * bpt);
// Reads the current data block. Each data block contains all
// the time bins for a single pixel position.
getHandle().read(b, ((row * w) + col) * m.getTimeBins() * bpp, m
.getTimeBins() * bpp);
getHandle().read(b, ((row * w) + col) * bpt, bpt);
// Update offset to point to the next data block (pixel)
tmpOff = info.nextBlockOffs;
}
Expand All @@ -337,8 +341,8 @@ else if (info.noOfDataBlocks > 1) {
// We always have to read the entire plane since we go from pixel to
// pixel, so now we can crop out what we didn't need.
for (int row = 0; row < h; row++) {
System.arraycopy(b, ((row * sizeX) + x) * bpp * m.getTimeBins(),
buf, row * w * bpp * m.getTimeBins(), w * m.getTimeBins() * bpp);
System.arraycopy(b, ((row * sizeX) + x) * bpt,
buf, row * w * bpt, w * bpt);
}
}
}
Expand All @@ -347,18 +351,28 @@ else if (info.noOfDataBlocks > 1) {
// binOffset points to the start of the pixels, then we skip the
// required number of planes and rows.
getHandle().seek(m.getBinOffset() + planeIndex * planeSize + y *
paddedWidth * bpp * m.getTimeBins());
paddedWidth * bpt);
}

// For the SDT subtypes with complete planes per data block, we can read
// the requested plane data now.
if (info.measMode == 13 || info.noOfDataBlocks == 1) {
final InputStream is;
// compression detection
// see https://www.lfd.uci.edu/~gohlke/code/sdtfile.py.html
if ((info.blockType & 0x1000) != 0) {
is = new ZipInputStream(new DataHandleInputStream<>(getHandle()));
((ZipInputStream) is).getNextEntry();
}
else
is = new DataHandleInputStream<>(getHandle());

for (int row = 0; row < h; row++) {
getHandle().skipBytes(x * bpp * m.getTimeBins());
getHandle().read(b, row * bpp * m.getTimeBins() * w, w * m
.getTimeBins() * bpp);
getHandle().skipBytes(bpp * m.getTimeBins() * (paddedWidth - x - w));
is.skip(bpt * x);
readBytes(b, is, row * bpt * w, bpt * w);
is.skip(bpt * (paddedWidth - x - w));
}
is.close();
}

// no pixel merging required
Expand All @@ -367,10 +381,10 @@ else if (info.noOfDataBlocks > 1) {
}

for (int row = 0; row < h; row++) {
final int yi = (y + row) * sizeX * m.getTimeBins() * bpp;
final int yi = (y + row) * sizeX * bpt;
final int ri = row * w * bpp;
for (int col = 0; col < w; col++) {
final int xi = yi + (x + col) * m.getTimeBins() * bpp;
final int xi = yi + (x + col) * bpt;
final int ci = ri + col * bpp;
// combine all lifetime bins into single intensity value
short sum = 0;
Expand All @@ -382,5 +396,26 @@ else if (info.noOfDataBlocks > 1) {
}
return plane;
}

/**
* Reads {@code nBytes} bytes from input stream {@code is} to {@code buf}
* starting at {@code offset}.
*
* @param buf the destination
* @param is the source
* @param offset the offset in {@code buf} to start writing
* @param nBytes the number of bytes to read
* @throws IOException see {@link InputStream#read(byte[], int, int)}
*/
private static void readBytes(final byte[] buf, final InputStream is,
final int offset, final int nBytes) throws IOException {
int nRead = 0;
while (nRead < nBytes) {
int n = is.read(buf, offset + nRead, nBytes - nRead);
if (n <= 0)
break;
nRead += n;
}
}
}
}