Skip to content

Commit

Permalink
Merge pull request #83 from chris-allan/ngff-optimization-001
Browse files Browse the repository at this point in the history
First set of NGFF optimizations
  • Loading branch information
chris-allan authored Jun 10, 2021
2 parents 690da03 + e8dd5de commit 344a4dd
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 188 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,10 @@ public byte[] renderImageRegion(omero.client client) {
Map<Long, Pixels> imagePixels = retrievePixDescription(
iQuery, Arrays.asList(imageRegionCtx.imageId));
Pixels pixels = imagePixels.get(imageRegionCtx.imageId);
RenderingDef renderingDef =
getRenderingDef(client, imageRegionCtx.imageId);
if (pixels != null) {
return getRegion(client, pixels);
return getRegion(client, pixels, renderingDef);
}
log.debug("Cannot find Image:{}", imageRegionCtx.imageId);
} catch (Exception e) {
Expand All @@ -338,13 +340,14 @@ public byte[] renderImageRegion(omero.client client) {
* defined by <code>imageRegionCtx.format</code>.
* @param client OMERO client to use for querying.
* @param pixels pixels metadata
* @param renderingDef rendering settings to use
* @return Image region as a byte array.
* @throws QuantizationException
*/
private byte[] getRegion(omero.client client, Pixels pixels)
throws IllegalArgumentException, ServerError, IOException,
QuantizationException {
RenderingDef renderingDef = getRenderingDef(client, pixels.getId());
private byte[] getRegion(
omero.client client, Pixels pixels, RenderingDef renderingDef)
throws IllegalArgumentException, ServerError, IOException,
QuantizationException {
return compress(getBufferedImage(render(client, pixels, renderingDef)));
}

Expand Down Expand Up @@ -404,6 +407,26 @@ protected byte[] compress(BufferedImage image) throws IOException {
return toReturn;
}

/**
* Returns a pixel buffer for a given set of pixels.
* @param pixels pixels metadata
* @return See above.
* @see PixelsService#getPixelBuffer(Pixels)
*/
private PixelBuffer getPixelBuffer(Pixels pixels) {
Tracer tracer = Tracing.currentTracer();
ScopedSpan span = tracer.startScopedSpan("get_pixel_buffer");
try {
span.tag("omero.pixels_id", Long.toString(pixels.getId()));
return pixelsService.getPixelBuffer(pixels, false);
} catch (Exception e) {
span.error(e);
throw e;
} finally {
span.finish();
}
}

/**
* Prepares an in memory pixel buffer of the desired project pixels based
* on input.
Expand All @@ -419,7 +442,7 @@ private PixelBuffer prepareProjectedPixelBuffer(
int projectedSizeC = 0;
ChannelBinding[] channelBindings =
renderer.getChannelBindings();
PixelBuffer pixelBuffer = pixelsService.getPixelBuffer(pixels, false);
PixelBuffer pixelBuffer = getPixelBuffer(pixels);
int start = Optional
.ofNullable(imageRegionCtx.projectionStart)
.orElse(0);
Expand All @@ -428,7 +451,7 @@ private PixelBuffer prepareProjectedPixelBuffer(
.orElse(pixels.getSizeZ() - 1);
Tracer tracer = Tracing.currentTracer();
ScopedSpan span = tracer.startScopedSpan(
"prepare_porjected_pixel_buffer");
"prepare_projected_pixel_buffer");
try {
for (int i = 0; i < channelBindings.length; i++) {
if (!channelBindings[i].getActive()) {
Expand Down Expand Up @@ -494,8 +517,7 @@ protected Array render(
ScopedSpan span = tracer.startScopedSpan("render_as_packed_int");
span.tag("omero.pixels_id", pixels.getId().toString());
Renderer renderer = null;
try (PixelBuffer pixelBuffer =
pixelsService.getPixelBuffer(pixels, false)) {
try (PixelBuffer pixelBuffer = getPixelBuffer(pixels)) {
renderer = new Renderer(
quantumFactory, renderingModels, pixels, renderingDef,
pixelBuffer, lutProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ public ZarrPixelBuffer getLabelImagePixelBuffer(Pixels pixels, String uuid)
throw new IllegalArgumentException("NGFF dir not configured");
}
Path root = ngffDir.resolve(getLabelImageSubPath(pixels, uuid));
return new ZarrPixelBuffer(root, maxTileLength);
return new ZarrPixelBuffer(pixels, root, maxTileLength);
}

/**
Expand All @@ -214,7 +214,8 @@ public PixelBuffer getPixelBuffer(Pixels pixels, boolean write) {
if (ngffDir != null) {
try {
Path root = ngffDir.resolve(getImageSubPath(pixels));
PixelBuffer v = new ZarrPixelBuffer(root, maxTileLength);
PixelBuffer v =
new ZarrPixelBuffer(pixels, root, maxTileLength);
log.info("Using NGFF Pixel Buffer");
return v;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@

import org.slf4j.LoggerFactory;

import brave.ScopedSpan;
import brave.Tracer;
import brave.Tracing;
import io.vertx.core.MultiMap;
import ome.io.nio.PixelBuffer;
import ome.model.enums.Family;
Expand Down Expand Up @@ -79,8 +82,8 @@ public class ThumbnailCtx extends ImageRegionCtx {

/**
* Apply the first resolution level larger than the thumbnail
* @param resolutionDescriptions
* @return
* @param renderer fully initialized renderer
* @param pixelBuffer pixel buffer providing data for the image
*/
@Override
public void setResolutionLevel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ public ThumbnailsRequestHandler(
*/
public Map<Long, byte[]> renderThumbnails(omero.client client) {
Map<Long, byte[]> thumbnails = new HashMap<Long, byte[]>();
ScopedSpan span =
Tracing.currentTracer().startScopedSpan("render_thumbnails");
try {
span.tag("omero.image_ids", thumbnailCtx.imageIds.toString());
ServiceFactoryPrx sf = client.getSession();
IQueryPrx iQuery = sf.getQueryService();
long userId = sf.getAdminService().getEventContext().userId;
Expand Down Expand Up @@ -123,7 +126,10 @@ public Map<Long, byte[]> renderThumbnails(omero.client client) {
thumbnails.put(imageId, thumbnail);
}
} catch (Exception e) {
span.error(e);
log.error("Exception while rendering thumbnails", e);
} finally {
span.finish();
}
return thumbnails;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import loci.formats.FormatTools;
import ome.io.nio.DimensionsOutOfBoundsException;
import ome.io.nio.PixelBuffer;
import ome.model.core.Pixels;
import ome.util.PixelData;
import ucar.ma2.InvalidRangeException;

Expand All @@ -49,6 +50,9 @@ public class ZarrPixelBuffer implements PixelBuffer {
private static final org.slf4j.Logger log =
LoggerFactory.getLogger(PixelBuffer.class);

/** Reference to the pixels. */
private final Pixels pixels;

/** Root of the OME-NGFF multiscale we are operating on */
private final Path root;

Expand Down Expand Up @@ -77,8 +81,9 @@ public class ZarrPixelBuffer implements PixelBuffer {
* read operations
* @throws IOException
*/
public ZarrPixelBuffer(Path root, Integer maxTileLength)
public ZarrPixelBuffer(Pixels pixels, Path root, Integer maxTileLength)
throws IOException {
this.pixels = pixels;
this.root = root;
rootGroup = ZarrGroup.open(this.root);
rootGroupAttributes = rootGroup.getAttributes();
Expand Down Expand Up @@ -156,9 +161,7 @@ private int getBytesPerPixel() {
* @param offset The offset of the region
* @return byte array of data from the ZarrArray
*/
private byte[] getBytes(int[] shape, int[] offset) {
ScopedSpan span = Tracing.currentTracer()
.startScopedSpan("get_bytes_zarr");
private ByteBuffer getBytes(int[] shape, int[] offset) {
if (shape[4] > maxTileLength) {
throw new IllegalArgumentException(String.format(
"sizeX %d > maxTileLength %d", shape[4], maxTileLength));
Expand All @@ -167,7 +170,12 @@ private byte[] getBytes(int[] shape, int[] offset) {
throw new IllegalArgumentException(String.format(
"sizeY %d > maxTileLength %d", shape[3], maxTileLength));
}
ScopedSpan span = Tracing.currentTracer()
.startScopedSpan("get_bytes");
try {
span.tag("omero.zarr.shape", Arrays.toString(shape));
span.tag("omero.zarr.offset", Arrays.toString(offset));
span.tag("omero.zarr.array", array.toString());
int length = IntStream.of(shape).reduce(1, Math::multiplyExact);
int bytesPerPixel = getBytesPerPixel();
ByteBuffer asByteBuffer = ByteBuffer.allocate(
Expand All @@ -176,38 +184,38 @@ private byte[] getBytes(int[] shape, int[] offset) {
switch (dataType) {
case u1:
case i1:
return (byte[]) array.read(shape, offset);
return ByteBuffer.wrap((byte[]) array.read(shape, offset));
case u2:
case i2:
{
short[] data = (short[]) array.read(shape, offset);
asByteBuffer.asShortBuffer().put(data);
return asByteBuffer.array();
return asByteBuffer;
}
case u4:
case i4:
{
int[] data = (int[]) array.read(shape, offset);
asByteBuffer.asIntBuffer().put(data);
return asByteBuffer.array();
return asByteBuffer;
}
case i8:
{
long[] data = (long[]) array.read(shape, offset);
asByteBuffer.asLongBuffer().put(data);
return asByteBuffer.array();
return asByteBuffer;
}
case f4:
{
float[] data = (float[]) array.read(shape, offset);
asByteBuffer.asFloatBuffer().put(data);
return asByteBuffer.array();
return asByteBuffer;
}
case f8:
{
double[] data = (double[]) array.read(shape, offset);
asByteBuffer.asDoubleBuffer().put(data);
return asByteBuffer.array();
return asByteBuffer;
}
default:
log.error("Unsupported data type" + dataType);
Expand All @@ -221,24 +229,6 @@ private byte[] getBytes(int[] shape, int[] offset) {
}
}

/**
* Retrieves the array shapes of all subresolutions of this multiscale
* buffer.
* @return See above.
* @throws IOException
*/
public int[][] getShapes() throws IOException {
List<Map<String, String>> datasets = getDatasets();
List<int[]> shapes = new ArrayList<int[]>();
for (Map<String, String> dataset : datasets) {
ZarrArray resolutionArray =
ZarrArray.open(root.resolve(dataset.get("path")));
int[] shape = resolutionArray.getShape();
shapes.add(0, shape);
}
return shapes.toArray(new int[shapes.size()][]);
}

/**
* Retrieves the array chunk sizes of all subresolutions of this multiscale
* buffer.
Expand Down Expand Up @@ -424,10 +414,9 @@ public PixelData getTile(Integer z, Integer c, Integer t, Integer x, Integer y,
try {
checkBounds(x + w, y + h, z, c, t);
int[] shape = new int[] { 1, 1, 1, h, w };
int[] offsets = new int[] { t, c, z, y, x };
byte[] asArray = getBytes(shape, offsets);
int[] offset = new int[] { t, c, z, y, x };
PixelData d = new PixelData(
getPixelsType(), ByteBuffer.wrap(asArray));
getPixelsType(), getBytes(shape, offset));
d.setOrder(ByteOrder.BIG_ENDIAN);
return d;
} catch (DimensionsOutOfBoundsException e) {
Expand Down Expand Up @@ -699,11 +688,16 @@ public Dimension getTileSize() {
@Override
public List<List<Integer>> getResolutionDescriptions() {
try {
int[][] shapes = getShapes();
int resolutionLevels = getResolutionLevels();
List<List<Integer>> resolutionDescriptions =
new ArrayList<List<Integer>>();
for (int[] shape : shapes) {
resolutionDescriptions.add(Arrays.asList(shape[4], shape[3]));
int sizeX = pixels.getSizeX();
int sizeY = pixels.getSizeY();
for (int i = 0; i < resolutionLevels; i++) {
double scale = Math.pow(2, i);
resolutionDescriptions.add(
0, Arrays.asList(
(int) (sizeX / scale), (int) (sizeY / scale)));
}
return resolutionDescriptions;
} catch (Exception e) {
Expand Down
Loading

0 comments on commit 344a4dd

Please sign in to comment.