Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,24 @@ default List<String> getAxisNames() throws ZarrException {
default List<String> getLabels() throws IOException, ZarrException {
StoreHandle labelsHandle = getStoreHandle().resolve("labels");

// Try v0.5: labels/zarr.json with {"attributes": {"labels": [...]}}
// Try v0.5+: labels/zarr.json with {"attributes": {"ome": {"labels": [...]}}}.
// Falls back to the non-conformant legacy layout {"attributes": {"labels": [...]}}.
StoreHandle zarrJson = labelsHandle.resolve(Node.ZARR_JSON);
if (zarrJson.exists()) {
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v3.Node.makeObjectMapper();
byte[] bytes = Utils.toArray(zarrJson.readNonNull());
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
com.fasterxml.jackson.databind.JsonNode attrs = root.get("attributes");
if (attrs != null && attrs.has("labels")) {
com.fasterxml.jackson.databind.JsonNode labelsNode = attrs.get("labels");
com.fasterxml.jackson.databind.JsonNode labelsNode = null;
if (attrs != null) {
com.fasterxml.jackson.databind.JsonNode ome = attrs.get("ome");
if (ome != null && ome.has("labels")) {
labelsNode = ome.get("labels");
} else if (attrs.has("labels")) {
labelsNode = attrs.get("labels");
}
}
if (labelsNode != null) {
List<String> result = new ArrayList<>();
for (com.fasterxml.jackson.databind.JsonNode item : labelsNode) {
result.add(item.asText());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.zarr.zarrjava.experimental.ome.metadata;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand All @@ -16,8 +17,9 @@ public final class Axis {
public final String unit;
@Nullable
public final Boolean discrete;
/** Human-readable axis name, serialized as {@code longName} ({@code long_name} is accepted on read). */
@Nullable
@JsonProperty("long_name")
@JsonProperty("longName")
public final String longName;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
Expand All @@ -26,7 +28,7 @@ public Axis(
@Nullable @JsonProperty("type") String type,
@Nullable @JsonProperty("unit") String unit,
@Nullable @JsonProperty("discrete") Boolean discrete,
@Nullable @JsonProperty("long_name") String longName
@Nullable @JsonProperty("longName") @JsonAlias("long_name") String longName
) {
this.name = name;
this.type = type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package dev.zarr.zarrjava.experimental.ome.metadata;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import javax.annotation.Nullable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
* OME-Zarr {@code image-label} metadata of a label image (display colors, per-label properties
* and the source image).
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public final class ImageLabel {

@Nullable
public final List<Color> colors;
@Nullable
public final List<Property> properties;
@Nullable
public final Source source;
/** Older OME-Zarr versions carried a {@code version} inside {@code image-label}; kept for round-tripping. */
@Nullable
public final String version;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public ImageLabel(
@Nullable @JsonProperty("colors") List<Color> colors,
@Nullable @JsonProperty("properties") List<Property> properties,
@Nullable @JsonProperty("source") Source source,
@Nullable @JsonProperty("version") String version
) {
this.colors = colors;
this.properties = properties;
this.source = source;
this.version = version;
}

public ImageLabel(
@Nullable List<Color> colors,
@Nullable List<Property> properties,
@Nullable Source source
) {
this(colors, properties, source, null);
}

/** Display color of a single label value. Additional keys are preserved. */
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Color {
@JsonProperty("label-value")
public final int labelValue;
@Nullable
public final List<Integer> rgba;
private final Map<String, Object> additionalProperties = new LinkedHashMap<>();

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Color(
@JsonProperty(value = "label-value", required = true) int labelValue,
@Nullable @JsonProperty("rgba") List<Integer> rgba
) {
this.labelValue = labelValue;
this.rgba = rgba;
}

/** Keys other than {@code label-value} and {@code rgba}. */
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String key, Object value) {
additionalProperties.put(key, value);
}
}

/** Arbitrary properties associated with a single label value. */
public static final class Property {
@JsonProperty("label-value")
public final int labelValue;
private final Map<String, Object> additionalProperties = new LinkedHashMap<>();

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Property(@JsonProperty(value = "label-value", required = true) int labelValue) {
this.labelValue = labelValue;
}

/** Keys other than {@code label-value}. */
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String key, Object value) {
additionalProperties.put(key, value);
}
}

/** Reference to the image the label image was derived from. */
@JsonInclude(JsonInclude.Include.NON_NULL)
public static final class Source {
/** Spec default for {@link #image} when absent. */
public static final String DEFAULT_IMAGE = "../../";

/** Relative path to the source image group, or null if absent (spec default {@code ../../}). */
@Nullable
public final String image;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public Source(@Nullable @JsonProperty("image") String image) {
this.image = image;
}

/** Returns {@link #image}, falling back to the spec default {@code ../../}. */
public String resolveImage() {
return image != null ? image : DEFAULT_IMAGE;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ public final class OmeMetadata {
public final PlateMetadata plate;
@Nullable
public final WellMetadata well;
/** Paths of the label images, present in the metadata of a {@code labels} group. */
@Nullable
public final List<String> labels;
/** Display/source information of a label image. */
@Nullable
@JsonProperty("image-label")
public final ImageLabel imageLabel;
/** Image paths of a bioformats2raw collection, present in the metadata of the {@code OME} group. */
@Nullable
public final List<String> series;

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public OmeMetadata(
Expand All @@ -31,18 +41,41 @@ public OmeMetadata(
@Nullable @JsonProperty("omero") OmeroMetadata omero,
@Nullable @JsonProperty("bioformats2raw.layout") Integer bioformats2rawLayout,
@Nullable @JsonProperty("plate") PlateMetadata plate,
@Nullable @JsonProperty("well") WellMetadata well
@Nullable @JsonProperty("well") WellMetadata well,
@Nullable @JsonProperty("labels") List<String> labels,
@Nullable @JsonProperty("image-label") ImageLabel imageLabel,
@Nullable @JsonProperty("series") List<String> series
) {
this.version = version;
this.multiscales = multiscales;
this.omero = omero;
this.bioformats2rawLayout = bioformats2rawLayout;
this.plate = plate;
this.well = well;
this.labels = labels;
this.imageLabel = imageLabel;
this.series = series;
}

public OmeMetadata(
String version,
@Nullable List<MultiscalesEntry> multiscales,
@Nullable OmeroMetadata omero,
@Nullable Integer bioformats2rawLayout,
@Nullable PlateMetadata plate,
@Nullable WellMetadata well
) {
this(version, multiscales, omero, bioformats2rawLayout, plate, well, null, null, null);
}

/** Convenience constructor for multiscale images (omero/layout/plate/well all null). */
public OmeMetadata(String version, List<MultiscalesEntry> multiscales) {
this(version, multiscales, null, null, null, null);
}

/** Returns a copy with {@code multiscales} replaced and all other fields preserved. */
public OmeMetadata withMultiscales(@Nullable List<MultiscalesEntry> multiscales) {
return new OmeMetadata(version, multiscales, omero, bioformats2rawLayout, plate, well,
labels, imageLabel, series);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ public Integer getBioformats2rawLayout() {
return omeMetadata.bioformats2rawLayout;
}

/**
* Returns the {@code image-label} metadata if this multiscale image is a label image, or null if not.
*/
@Nullable
public dev.zarr.zarrjava.experimental.ome.metadata.ImageLabel getImageLabel() {
return omeMetadata.imageLabel;
}

@Override
public dev.zarr.zarrjava.core.Array openScaleLevel(int i) throws IOException, ZarrException {
String path = getMultiscalesEntry(0).datasets.get(i).path;
Expand All @@ -107,7 +115,7 @@ public void createScaleLevel(
MultiscalesEntry updated = current.withDataset(new Dataset(path, coordinateTransformations));
List<MultiscalesEntry> updatedList = new java.util.ArrayList<>(omeMetadata.multiscales);
updatedList.set(0, updated);
omeMetadata = new OmeMetadata(omeMetadata.version, updatedList);
omeMetadata = omeMetadata.withMultiscales(updatedList);
setAttributes(omeAttributes(omeMetadata));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ public Integer getBioformats2rawLayout() {
return omeMetadata.bioformats2rawLayout;
}

/**
* Returns the {@code image-label} metadata if this multiscale image is a label image, or null if not.
*/
@Nullable
public dev.zarr.zarrjava.experimental.ome.metadata.ImageLabel getImageLabel() {
return omeMetadata.imageLabel;
}

OmeMetadata getRawOmeMetadata() {
return omeMetadata;
}
Expand Down Expand Up @@ -164,6 +172,11 @@ public void createScaleLevel(
null, null, null, castIntList(raw.get("mapAxis")), castV06Transform(raw.get("transformation"))));
continue;
}
if ("projectAxis".equals(type)) {
v06Transforms.add(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ProjectAxisCoordinateTransformation(
null, null, null, castIntList(raw.get("droppedInputs")), castIntList(raw.get("createdOutputs"))));
continue;
}
if ("affine".equals(type)) {
v06Transforms.add(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.AffineCoordinateTransformation(
null, null, null, castMatrix(raw.get("affine")), rawPath));
Expand All @@ -176,12 +189,12 @@ public void createScaleLevel(
}
if ("displacements".equals(type)) {
v06Transforms.add(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.DisplacementsCoordinateTransformation(
null, null, null, rawPath));
null, null, null, rawPath, castString(raw.get("interpolation"))));
continue;
}
if ("coordinates".equals(type)) {
v06Transforms.add(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.CoordinatesCoordinateTransformation(
null, null, null, rawPath));
null, null, null, rawPath, castString(raw.get("interpolation"))));
continue;
}
if ("bijection".equals(type)) {
Expand Down Expand Up @@ -218,14 +231,7 @@ public void createScaleLevel(
MultiscalesEntry updated = current.withDataset(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.Dataset(path, v06Transforms));
List<MultiscalesEntry> updatedList = new ArrayList<>(omeMetadata.multiscales);
updatedList.set(0, updated);
omeMetadata = new OmeMetadata(
omeMetadata.version,
updatedList,
omeMetadata.omero,
omeMetadata.bioformats2rawLayout,
omeMetadata.scene,
omeMetadata.plate,
omeMetadata.well);
omeMetadata = omeMetadata.withMultiscales(updatedList);
setAttributes(omeAttributes(omeMetadata));
}

Expand Down Expand Up @@ -294,6 +300,19 @@ private static CoordinateTransformation mapTransform(
}
return generic;
}
if (ct instanceof dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ProjectAxisCoordinateTransformation) {
dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ProjectAxisCoordinateTransformation t =
(dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ProjectAxisCoordinateTransformation) ct;
dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation generic =
new dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation("projectAxis");
if (t.droppedInputs != null) {
generic.raw.put("droppedInputs", t.droppedInputs);
}
if (t.createdOutputs != null) {
generic.raw.put("createdOutputs", t.createdOutputs);
}
return generic;
}
if (ct instanceof dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.AffineCoordinateTransformation) {
dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.AffineCoordinateTransformation t =
(dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.AffineCoordinateTransformation) ct;
Expand Down Expand Up @@ -322,6 +341,9 @@ private static CoordinateTransformation mapTransform(
dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation generic =
new dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation("displacements");
generic.raw.put("path", t.path);
if (t.interpolation != null) {
generic.raw.put("interpolation", t.interpolation);
}
return generic;
}
if (ct instanceof dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.CoordinatesCoordinateTransformation) {
Expand All @@ -330,6 +352,9 @@ private static CoordinateTransformation mapTransform(
dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation generic =
new dev.zarr.zarrjava.experimental.ome.metadata.transform.GenericCoordinateTransformation("coordinates");
generic.raw.put("path", t.path);
if (t.interpolation != null) {
generic.raw.put("interpolation", t.interpolation);
}
return generic;
}
if (ct instanceof dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.BijectionCoordinateTransformation) {
Expand All @@ -354,8 +379,8 @@ private static CoordinateTransformation mapTransform(
List<Map<String, Object>> transformed = new ArrayList<>();
for (dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ByDimensionCoordinateTransformation.ByDimensionTransformation item : t.transformations) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("input_axes", item.inputAxes);
map.put("output_axes", item.outputAxes);
map.put("inputAxes", item.inputAxes);
map.put("outputAxes", item.outputAxes);
map.put("transformation", item.transformation != null ? mapTransform(item.transformation) : null);
transformed.add(map);
}
Expand Down Expand Up @@ -411,6 +436,10 @@ private static List<Double> castDoubleList(List<?> values) {
return out;
}

private static String castString(Object raw) {
return raw instanceof String ? (String) raw : null;
}

private static List<Integer> castIntList(Object raw) {
if (!(raw instanceof List)) {
return null;
Expand Down Expand Up @@ -489,8 +518,8 @@ private static List<dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.B
}
Map<?, ?> map = (Map<?, ?>) item;
out.add(new dev.zarr.zarrjava.experimental.ome.v0_6.metadata.transform.ByDimensionCoordinateTransformation.ByDimensionTransformation(
castIntList(map.get("input_axes")),
castIntList(map.get("output_axes")),
castIntList(map.containsKey("inputAxes") ? map.get("inputAxes") : map.get("input_axes")),
castIntList(map.containsKey("outputAxes") ? map.get("outputAxes") : map.get("output_axes")),
castV06Transform(map.get("transformation"))));
}
return out;
Expand Down
Loading
Loading