Skip to content

Commit 6f31a28

Browse files
committed
add omero, bioformats2raw, labels, hcs
1 parent 33f57b7 commit 6f31a28

17 files changed

Lines changed: 797 additions & 5 deletions

src/main/java/dev/zarr/zarrjava/ome/MultiscaleImage.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@
77

88
import java.io.IOException;
99
import java.nio.ByteBuffer;
10+
import java.util.ArrayList;
11+
import java.util.Collections;
1012
import java.util.List;
1113

1214
/**
1315
* Unified interface for reading OME-Zarr multiscale images across Zarr format versions.
1416
*/
1517
public interface MultiscaleImage {
1618

19+
/**
20+
* Returns the store handle for this multiscale image node.
21+
*/
22+
StoreHandle getStoreHandle();
23+
1724
/**
1825
* Returns the multiscale node descriptor at index {@code i}.
1926
*/
@@ -34,13 +41,62 @@ public interface MultiscaleImage {
3441
*/
3542
default List<String> getAxisNames() throws ZarrException {
3643
UnifiedMultiscaleNode node = getMultiscaleNode(0);
37-
List<String> names = new java.util.ArrayList<>();
44+
List<String> names = new ArrayList<>();
3845
for (dev.zarr.zarrjava.ome.metadata.Axis axis : node.axes) {
3946
names.add(axis.name);
4047
}
4148
return names;
4249
}
4350

51+
/**
52+
* Returns all label names from the {@code labels/} sub-group, or an empty list if none exist.
53+
*/
54+
default List<String> getLabels() throws IOException, ZarrException {
55+
StoreHandle labelsHandle = getStoreHandle().resolve("labels");
56+
57+
// Try v0.5: labels/zarr.json with {"attributes": {"labels": [...]}}
58+
StoreHandle zarrJson = labelsHandle.resolve(Node.ZARR_JSON);
59+
if (zarrJson.exists()) {
60+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v3.Node.makeObjectMapper();
61+
byte[] bytes = Utils.toArray(zarrJson.readNonNull());
62+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
63+
com.fasterxml.jackson.databind.JsonNode attrs = root.get("attributes");
64+
if (attrs != null && attrs.has("labels")) {
65+
com.fasterxml.jackson.databind.JsonNode labelsNode = attrs.get("labels");
66+
List<String> result = new ArrayList<>();
67+
for (com.fasterxml.jackson.databind.JsonNode item : labelsNode) {
68+
result.add(item.asText());
69+
}
70+
return result;
71+
}
72+
}
73+
74+
// Try v0.4: labels/.zattrs with {"labels": [...]}
75+
StoreHandle zattrs = labelsHandle.resolve(Node.ZATTRS);
76+
if (zattrs.exists()) {
77+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v2.Node.makeObjectMapper();
78+
byte[] bytes = Utils.toArray(zattrs.readNonNull());
79+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
80+
if (root.has("labels")) {
81+
com.fasterxml.jackson.databind.JsonNode labelsNode = root.get("labels");
82+
List<String> result = new ArrayList<>();
83+
for (com.fasterxml.jackson.databind.JsonNode item : labelsNode) {
84+
result.add(item.asText());
85+
}
86+
return result;
87+
}
88+
}
89+
90+
return Collections.emptyList();
91+
}
92+
93+
/**
94+
* Opens the named label image from the {@code labels/} sub-group.
95+
*/
96+
default MultiscaleImage openLabel(String name) throws IOException, ZarrException {
97+
return MultiscaleImage.open(getStoreHandle().resolve("labels").resolve(name));
98+
}
99+
44100
/**
45101
* Opens an OME-Zarr multiscale image at the given store handle, auto-detecting the Zarr version.
46102
*
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dev.zarr.zarrjava.ome;
2+
3+
import dev.zarr.zarrjava.ZarrException;
4+
import dev.zarr.zarrjava.core.Node;
5+
import dev.zarr.zarrjava.ome.metadata.PlateMetadata;
6+
import dev.zarr.zarrjava.store.StoreHandle;
7+
import dev.zarr.zarrjava.utils.Utils;
8+
9+
import java.io.IOException;
10+
11+
/**
12+
* Unified interface for reading OME-Zarr HCS plates across Zarr format versions.
13+
*/
14+
public interface Plate {
15+
16+
/**
17+
* Returns the plate metadata.
18+
*/
19+
PlateMetadata getPlateMetadata() throws ZarrException;
20+
21+
/**
22+
* Opens the well at the given row/column path (e.g. {@code "A/1"}).
23+
*/
24+
Well openWell(String rowColPath) throws IOException, ZarrException;
25+
26+
/**
27+
* Returns the store handle for this plate node.
28+
*/
29+
StoreHandle getStoreHandle();
30+
31+
/**
32+
* Opens an OME-Zarr plate at the given store handle, auto-detecting the Zarr version.
33+
*/
34+
static Plate open(StoreHandle storeHandle) throws IOException, ZarrException {
35+
// Try v0.5: zarr.json with "ome" -> "plate"
36+
StoreHandle zarrJson = storeHandle.resolve(Node.ZARR_JSON);
37+
if (zarrJson.exists()) {
38+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v3.Node.makeObjectMapper();
39+
byte[] bytes = Utils.toArray(zarrJson.readNonNull());
40+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
41+
com.fasterxml.jackson.databind.JsonNode attrs = root.get("attributes");
42+
if (attrs != null && attrs.has("ome") && attrs.get("ome").has("plate")) {
43+
return dev.zarr.zarrjava.ome.v0_5.Plate.openPlate(storeHandle);
44+
}
45+
}
46+
47+
// Try v0.4: .zattrs with "plate"
48+
StoreHandle zattrs = storeHandle.resolve(Node.ZATTRS);
49+
if (zattrs.exists()) {
50+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v2.Node.makeObjectMapper();
51+
byte[] bytes = Utils.toArray(zattrs.readNonNull());
52+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
53+
if (root.has("plate")) {
54+
return dev.zarr.zarrjava.ome.v0_4.Plate.openPlate(storeHandle);
55+
}
56+
}
57+
58+
throw new ZarrException("No OME-Zarr plate metadata found at " + storeHandle);
59+
}
60+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dev.zarr.zarrjava.ome;
2+
3+
import dev.zarr.zarrjava.ZarrException;
4+
import dev.zarr.zarrjava.core.Node;
5+
import dev.zarr.zarrjava.ome.metadata.WellMetadata;
6+
import dev.zarr.zarrjava.store.StoreHandle;
7+
import dev.zarr.zarrjava.utils.Utils;
8+
9+
import java.io.IOException;
10+
11+
/**
12+
* Unified interface for reading OME-Zarr HCS wells across Zarr format versions.
13+
*/
14+
public interface Well {
15+
16+
/**
17+
* Returns the well metadata.
18+
*/
19+
WellMetadata getWellMetadata() throws ZarrException;
20+
21+
/**
22+
* Opens the image at the given path within this well (e.g. {@code "0"}).
23+
*/
24+
MultiscaleImage openImage(String path) throws IOException, ZarrException;
25+
26+
/**
27+
* Returns the store handle for this well node.
28+
*/
29+
StoreHandle getStoreHandle();
30+
31+
/**
32+
* Opens an OME-Zarr well at the given store handle, auto-detecting the Zarr version.
33+
*/
34+
static Well open(StoreHandle storeHandle) throws IOException, ZarrException {
35+
// Try v0.5: zarr.json with "ome" -> "well"
36+
StoreHandle zarrJson = storeHandle.resolve(Node.ZARR_JSON);
37+
if (zarrJson.exists()) {
38+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v3.Node.makeObjectMapper();
39+
byte[] bytes = Utils.toArray(zarrJson.readNonNull());
40+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
41+
com.fasterxml.jackson.databind.JsonNode attrs = root.get("attributes");
42+
if (attrs != null && attrs.has("ome") && attrs.get("ome").has("well")) {
43+
return dev.zarr.zarrjava.ome.v0_5.Well.openWell(storeHandle);
44+
}
45+
}
46+
47+
// Try v0.4: .zattrs with "well"
48+
StoreHandle zattrs = storeHandle.resolve(Node.ZATTRS);
49+
if (zattrs.exists()) {
50+
com.fasterxml.jackson.databind.ObjectMapper mapper = dev.zarr.zarrjava.v2.Node.makeObjectMapper();
51+
byte[] bytes = Utils.toArray(zattrs.readNonNull());
52+
com.fasterxml.jackson.databind.JsonNode root = mapper.readTree(bytes);
53+
if (root.has("well")) {
54+
return dev.zarr.zarrjava.ome.v0_4.Well.openWell(storeHandle);
55+
}
56+
}
57+
58+
throw new ZarrException("No OME-Zarr well metadata found at " + storeHandle);
59+
}
60+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package dev.zarr.zarrjava.ome.metadata;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
import javax.annotation.Nullable;
8+
9+
/** An HCS acquisition entry within a plate. */
10+
@JsonInclude(JsonInclude.Include.NON_NULL)
11+
public final class Acquisition {
12+
13+
public final int id;
14+
@Nullable
15+
public final String name;
16+
@Nullable
17+
public final Integer maximumfieldcount;
18+
@Nullable
19+
public final String description;
20+
@Nullable
21+
public final Long starttime;
22+
@Nullable
23+
public final Long endtime;
24+
25+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
26+
public Acquisition(
27+
@JsonProperty(value = "id", required = true) int id,
28+
@Nullable @JsonProperty("name") String name,
29+
@Nullable @JsonProperty("maximumfieldcount") Integer maximumfieldcount,
30+
@Nullable @JsonProperty("description") String description,
31+
@Nullable @JsonProperty("starttime") Long starttime,
32+
@Nullable @JsonProperty("endtime") Long endtime
33+
) {
34+
this.id = id;
35+
this.name = name;
36+
this.maximumfieldcount = maximumfieldcount;
37+
this.description = description;
38+
this.starttime = starttime;
39+
this.endtime = endtime;
40+
}
41+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.zarr.zarrjava.ome.metadata;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
/** A named entry used for plate rows/columns. */
8+
@JsonInclude(JsonInclude.Include.NON_NULL)
9+
public final class NamedEntry {
10+
11+
public final String name;
12+
13+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
14+
public NamedEntry(
15+
@JsonProperty(value = "name", required = true) String name
16+
) {
17+
this.name = name;
18+
}
19+
}
Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
11
package dev.zarr.zarrjava.ome.metadata;
22

33
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
45
import com.fasterxml.jackson.annotation.JsonProperty;
56

7+
import javax.annotation.Nullable;
68
import java.util.List;
79

810
/** OME-Zarr metadata stored under {@code attributes["ome"]} (v0.5). */
11+
@JsonInclude(JsonInclude.Include.NON_NULL)
912
public final class OmeMetadata {
1013

1114
public final String version;
15+
@Nullable
1216
public final List<MultiscalesEntry> multiscales;
17+
@Nullable
18+
public final OmeroMetadata omero;
19+
@Nullable
20+
@JsonProperty("bioformats2raw.layout")
21+
public final Integer bioformats2rawLayout;
22+
@Nullable
23+
public final PlateMetadata plate;
24+
@Nullable
25+
public final WellMetadata well;
1326

1427
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
1528
public OmeMetadata(
1629
@JsonProperty(value = "version", required = true) String version,
17-
@JsonProperty(value = "multiscales", required = true) List<MultiscalesEntry> multiscales
30+
@Nullable @JsonProperty("multiscales") List<MultiscalesEntry> multiscales,
31+
@Nullable @JsonProperty("omero") OmeroMetadata omero,
32+
@Nullable @JsonProperty("bioformats2raw.layout") Integer bioformats2rawLayout,
33+
@Nullable @JsonProperty("plate") PlateMetadata plate,
34+
@Nullable @JsonProperty("well") WellMetadata well
1835
) {
1936
this.version = version;
2037
this.multiscales = multiscales;
38+
this.omero = omero;
39+
this.bioformats2rawLayout = bioformats2rawLayout;
40+
this.plate = plate;
41+
this.well = well;
42+
}
43+
44+
/** Convenience constructor for multiscale images (omero/layout/plate/well all null). */
45+
public OmeMetadata(String version, List<MultiscalesEntry> multiscales) {
46+
this(version, multiscales, null, null, null, null);
2147
}
2248
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.zarr.zarrjava.ome.metadata;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
import javax.annotation.Nullable;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/** Omero display metadata stored in OME-Zarr attributes. */
12+
@JsonInclude(JsonInclude.Include.NON_NULL)
13+
public final class OmeroMetadata {
14+
15+
@Nullable
16+
public final List<Map<String, Object>> channels;
17+
@Nullable
18+
public final Map<String, Object> rdefs;
19+
20+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
21+
public OmeroMetadata(
22+
@Nullable @JsonProperty("channels") List<Map<String, Object>> channels,
23+
@Nullable @JsonProperty("rdefs") Map<String, Object> rdefs
24+
) {
25+
this.channels = channels;
26+
this.rdefs = rdefs;
27+
}
28+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package dev.zarr.zarrjava.ome.metadata;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
import javax.annotation.Nullable;
8+
import java.util.List;
9+
10+
/** OME-Zarr HCS plate metadata. */
11+
@JsonInclude(JsonInclude.Include.NON_NULL)
12+
public final class PlateMetadata {
13+
14+
public final List<NamedEntry> columns;
15+
public final List<NamedEntry> rows;
16+
public final List<WellRef> wells;
17+
@Nullable
18+
public final List<Acquisition> acquisitions;
19+
@Nullable
20+
public final Integer field_count;
21+
@Nullable
22+
public final String name;
23+
@Nullable
24+
public final String version;
25+
26+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
27+
public PlateMetadata(
28+
@JsonProperty(value = "columns", required = true) List<NamedEntry> columns,
29+
@JsonProperty(value = "rows", required = true) List<NamedEntry> rows,
30+
@JsonProperty(value = "wells", required = true) List<WellRef> wells,
31+
@Nullable @JsonProperty("acquisitions") List<Acquisition> acquisitions,
32+
@Nullable @JsonProperty("field_count") Integer field_count,
33+
@Nullable @JsonProperty("name") String name,
34+
@Nullable @JsonProperty("version") String version
35+
) {
36+
this.columns = columns;
37+
this.rows = rows;
38+
this.wells = wells;
39+
this.acquisitions = acquisitions;
40+
this.field_count = field_count;
41+
this.name = name;
42+
this.version = version;
43+
}
44+
}

0 commit comments

Comments
 (0)