Skip to content

Commit

Permalink
Merge pull request #60 from mtbc/serve-masks
Browse files Browse the repository at this point in the history
initial provision of masks by microservice
  • Loading branch information
joshmoore authored Aug 18, 2020
2 parents 1ca87a0 + 994b59c commit bf2bf5d
Show file tree
Hide file tree
Showing 15 changed files with 2,674 additions and 266 deletions.
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,19 @@ In addition to your usual OMERO.server configuration, the microservice's
: zlib compression level for chunks, default 6

`omero.ms.zarr.folder.layout`
: for directory listings, default `nested` chunks, can be `flattened`; `none` disables directory listings
: for directory listings, default `flattened` chunks, can be `nested`; `none` disables directory listings

`omero.ms.zarr.mask.split.enable`
: if masks split by ROI should be offered; default is `false`, can be set to `true`

`omero.ms.zarr.mask.overlap.color`
: color to use for overlapping region in labeled masks, as a signed integer as in the OME Model; not set by default

`omero.ms.zarr.mask.overlap.value`
: value to set for overlapping region in labeled masks, as a signed integer or "LOWEST" or "HIGHEST" (the default); setting to null disables overlap support

`omero.ms.zarr.mask-cache.size`
: mask cache size in megabytes, default 250

`omero.ms.zarr.net.path.image`
: URI template path for getting image data, default `/image/{image}.zarr/` where `{image}` signifies the image ID and is mandatory
Expand Down
97 changes: 90 additions & 7 deletions src/main/java/org/openmicroscopy/ms/zarr/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,25 @@ public class Configuration {
public final static String CONF_CHUNK_SIZE_MIN = "chunk.size.min";
public final static String CONF_COMPRESS_ZLIB_LEVEL = "compress.zlib.level";
public final static String CONF_FOLDER_LAYOUT = "folder.layout";
public final static String CONF_MASK_CACHE_SIZE = "mask-cache.size";
public final static String CONF_MASK_SPLIT_ENABLE = "mask.split.enable";
public final static String CONF_MASK_OVERLAP_COLOR = "mask.overlap.color";
public final static String CONF_MASK_OVERLAP_VALUE = "mask.overlap.value";
public final static String CONF_NET_PATH_IMAGE = "net.path.image";
public final static String CONF_NET_PORT = "net.port";

/* Configuration initialized to default values. */
private int cacheSize = 16;
private int bufferCacheSize = 16;
private long maskCacheSize = 250 * 1000000L;
private List<Character> chunkSizeAdjust = ImmutableList.of('X', 'Y', 'Z');
private int chunkSizeMin = 0x100000;
private int zlibLevel = 6;
private Boolean foldersNested = true;
private Boolean foldersNested = false;
private String netPath = getRegexForNetPath("/image/" + PLACEHOLDER_IMAGE_ID + ".zarr/");
private int netPort = 8080;
private boolean maskSplitEnable = false;
private Integer maskOverlapColor = null;
private Long maskOverlapValue = Long.MAX_VALUE;

/**
* Convert the given URI path to a regular expression in which {@link #PLACEHOLDER_IMAGE_ID} matches the image ID.
Expand Down Expand Up @@ -115,19 +123,23 @@ public Configuration(Map<String, String> configuration) {
* @param configuration the configuration keys and values to apply over the current state.
*/
private void setConfiguration(Map<String, String> configuration) {
final String cacheSize = configuration.get(CONF_BUFFER_CACHE_SIZE);
final String bufferCacheSize = configuration.get(CONF_BUFFER_CACHE_SIZE);
final String chunkSizeAdjust = configuration.get(CONF_CHUNK_SIZE_ADJUST);
final String chunkSizeMin = configuration.get(CONF_CHUNK_SIZE_MIN);
final String zlibLevel = configuration.get(CONF_COMPRESS_ZLIB_LEVEL);
final String folderLayout = configuration.get(CONF_FOLDER_LAYOUT);
final String maskCacheSize = configuration.get(CONF_MASK_CACHE_SIZE);
final String maskSplitEnable = configuration.get(CONF_MASK_SPLIT_ENABLE);
final String maskOverlapColor = configuration.get(CONF_MASK_OVERLAP_COLOR);
final String maskOverlapValue = configuration.get(CONF_MASK_OVERLAP_VALUE);
final String netPath = configuration.get(CONF_NET_PATH_IMAGE);
final String netPort = configuration.get(CONF_NET_PORT);

if (cacheSize != null) {
if (bufferCacheSize != null) {
try {
this.cacheSize = Integer.parseInt(cacheSize);
this.bufferCacheSize = Integer.parseInt(bufferCacheSize);
} catch (NumberFormatException nfe) {
final String message = "buffer cache size must be an integer, not " + cacheSize;
final String message = "buffer cache size must be an integer, not " + bufferCacheSize;
LOGGER.error(message);
throw new IllegalArgumentException(message);
}
Expand Down Expand Up @@ -195,6 +207,49 @@ private void setConfiguration(Map<String, String> configuration) {
}
}

if (maskCacheSize != null) {
try {
this.maskCacheSize = Long.parseLong(maskCacheSize) * 1000000L;
} catch (NumberFormatException nfe) {
final String message = "mask cache size must be an integer, not " + maskCacheSize;
LOGGER.error(message);
throw new IllegalArgumentException(message);
}
}

if (maskSplitEnable != null) {
this.maskSplitEnable = Boolean.parseBoolean(maskSplitEnable);
}

if (maskOverlapColor != null) {
try {
this.maskOverlapColor = Integer.parseInt(maskOverlapColor);
} catch (NumberFormatException nfe) {
final String message = "mask overlap color must be an integer, not " + maskOverlapColor;
LOGGER.error(message);
throw new IllegalArgumentException(message);
}
}

if (maskOverlapValue != null) {
try {
this.maskOverlapValue = Long.parseLong(maskOverlapValue);
} catch (NumberFormatException nfe) {
switch (maskOverlapValue.toLowerCase()) {
case "highest":
this.maskOverlapValue = Long.MAX_VALUE;
break;
case "lowest":
this.maskOverlapValue = Long.MIN_VALUE;
break;
default:
final String message = "mask overlap value must be an integer or HIGHEST or LOWEST, not " + maskOverlapValue;
LOGGER.error(message);
throw new IllegalArgumentException(message);
}
}
}

if (netPath != null) {
if (netPath.indexOf('\\') == -1) {
if (netPath.contains(PLACEHOLDER_IMAGE_ID)) {
Expand Down Expand Up @@ -232,7 +287,7 @@ private void setConfiguration(Map<String, String> configuration) {
* @return the configured pixel buffer cache size
*/
public int getBufferCacheSize() {
return cacheSize;
return bufferCacheSize;
}

/**
Expand Down Expand Up @@ -263,6 +318,34 @@ public Boolean getFoldersNested() {
return foldersNested;
}

/**
* @return the configured mask cache size
*/
public long getMaskCacheSize() {
return maskCacheSize;
}

/**
* @return if split masks are enabled
*/
public boolean isSplitMasksEnabled() {
return maskSplitEnable;
}

/**
* @return the configured mask overlap color ({@code null} for no color)
*/
public Integer getMaskOverlapColor() {
return maskOverlapColor;
}

/**
* @return the configured mask overlap value ({@code null} for no overlaps)
*/
public Long getMaskOverlapValue() {
return maskOverlapValue;
}

/**
* @return the configured URI path as a regular expression
*/
Expand Down
91 changes: 91 additions & 0 deletions src/main/java/org/openmicroscopy/ms/zarr/OmeroDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@

import ome.io.nio.PixelsService;
import ome.model.core.Pixels;
import ome.model.roi.Mask;
import ome.model.roi.Roi;

import java.util.Iterator;
import java.util.SortedSet;
import java.util.function.Function;

import com.google.common.collect.ImmutableSortedSet;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
Expand Down Expand Up @@ -89,4 +95,89 @@ Pixels getPixels(long imageId) {
return withSession(session ->
(Pixels) session.createQuery(hql).setParameter(0, imageId).uniqueResult());
}

/**
* @param imageId the ID of an image
* @return how many Rois the image has
*/
long getRoiCountOfImage(long imageId) {
LOGGER.debug("fetch Roi count for Image:{}", imageId);
final String hql =
"SELECT COUNT(id) FROM Roi " +
"WHERE image.id = ?";
return withSession(session ->
(Long) session.createQuery(hql).setParameter(0, imageId).uniqueResult());
}

/**
* @param imageId the ID of an image
* @return the IDs of the image's Rois
*/
SortedSet<Long> getRoiIdsOfImage(long imageId) {
LOGGER.debug("fetch Roi IDs for Image:{}", imageId);
final String hql =
"SELECT id FROM Roi " +
"WHERE image.id = ?";
return withSession(session ->
ImmutableSortedSet.copyOf((Iterator<Long>) session.createQuery(hql).setParameter(0, imageId).iterate()));
}

/**
* @param imageId the ID of an image
* @return the IDs of the image's Rois, limited to those that have a Mask
*/
SortedSet<Long> getRoiIdsWithMaskOfImage(long imageId) {
LOGGER.debug("fetch Roi IDs for Image:{} where each Roi has a Mask", imageId);
final String hql =
"SELECT DISTINCT roi.id FROM Mask " +
"WHERE roi.image.id = ?";
return withSession(session ->
ImmutableSortedSet.copyOf((Iterator<Long>) session.createQuery(hql).setParameter(0, imageId).iterate()));
}

/**
* @param roiId the ID of a Roi
* @return how many masks the Roi has
*/
long getMaskCountOfRoi(long roiId) {
LOGGER.debug("fetch Mask count for Roi:{}", roiId);
final String hql =
"SELECT COUNT(id) FROM Mask " +
"WHERE roi.id = ?";
return withSession(session ->
(Long) session.createQuery(hql).setParameter(0, roiId).uniqueResult());
}

/**
* @param roiId the ID of a Roi
* @return the IDs of the Roi's Masks
*/
SortedSet<Long> getMaskIdsOfRoi(long roiId) {
LOGGER.debug("fetch Mask IDs for Roi:{}", roiId);
final String hql =
"SELECT id FROM Mask " +
"WHERE roi.id = ?";
return withSession(session ->
ImmutableSortedSet.copyOf((Iterator<Long>) session.createQuery(hql).setParameter(0, roiId).iterate()));
}

/**
* @param roiId the ID of a Roi
* @return the Roi
*/
Roi getRoi(long roiId) {
LOGGER.debug("fetch Roi:{}", roiId);
return withSession(session ->
(Roi) session.get(Roi.class, roiId));
}

/**
* @param maskId the ID of a Mask
* @return the Mask
*/
Mask getMask(long maskId) {
LOGGER.debug("fetch Mask:{}", maskId);
return withSession(session ->
(Mask) session.get(Mask.class, maskId));
}
}
Loading

0 comments on commit bf2bf5d

Please sign in to comment.