Skip to content

Commit 9b9da05

Browse files
Update to handle input with less than 5 dimensions
1 parent d2cea15 commit 9b9da05

3 files changed

Lines changed: 166 additions & 59 deletions

File tree

src/main/java/com/glencoesoftware/pyramid/PyramidFromDirectoryWriter.java

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,9 @@ private byte[] getInputTileBytes(PyramidSeries s, int resolution,
668668
realHeight = region.height;
669669
}
670670

671-
int[] gridPosition = new int[] {pos[2], pos[1], pos[0],
672-
y * descriptor.tileSizeY, x * descriptor.tileSizeX};
673-
int[] shape = new int[] {1, 1, 1, realHeight, realWidth};
671+
int[] gridPosition = descriptor.getArray(pos[2], pos[1], pos[0],
672+
y * descriptor.tileSizeY, x * descriptor.tileSizeX);
673+
int[] shape = descriptor.getArray(1, 1, 1, realHeight, realWidth);
674674

675675
ZarrArray block = reader.openArray(descriptor.path);
676676

@@ -1098,53 +1098,48 @@ else if (imageFile != null && !imageFile.isEmpty()) {
10981098
ZarrGroup imgGroup = getZarrGroup(s.path);
10991099
ZarrArray imgArray = imgGroup.openArray("0");
11001100
int[] dims = imgArray.getShape();
1101-
// allow mismatch in channel count...
1102-
for (int d=0; d<dims.length; d++) {
1103-
char dimension = s.dimensionOrder.charAt(dims.length - d - 1);
1104-
int check = 0;
1105-
switch (dimension) {
1106-
case 'X':
1107-
check = x;
1108-
break;
1109-
case 'Y':
1110-
check = y;
1111-
break;
1112-
case 'Z':
1113-
check = s.z;
1114-
break;
1115-
case 'C':
1116-
check = dims[1];
1117-
break;
1118-
case 'T':
1119-
check = s.t;
1120-
break;
1121-
default:
1122-
throw new FormatException("Unrecognized dimension: " + dimension);
1123-
}
1124-
if (dims[d] != check) {
1125-
throw new FormatException("Dimension mismatch: " +
1126-
dimension + ": Zarr (" + dims[d] + "), OME-XML: (" +
1127-
check + ")");
1101+
1102+
List<Map<String, Object>> imgMultiscales =
1103+
(List<Map<String, Object>>) imgGroup.getAttributes().get("multiscales");
1104+
List<Map<String, Object>> imgAxes = null;
1105+
int channelIndex = -1;
1106+
if (imgMultiscales != null) {
1107+
Map<String, Object> multiscale = imgMultiscales.get(0);
1108+
imgAxes = (List<Map<String, Object>>) multiscale.get("axes");
1109+
if (imgAxes != null) {
1110+
for (int a=0; a<imgAxes.size(); a++) {
1111+
if (imgAxes.get(a).get("name").toString().equalsIgnoreCase("c")) {
1112+
channelIndex = a;
1113+
break;
1114+
}
1115+
}
11281116
}
11291117
}
1118+
else {
1119+
channelIndex = 1;
1120+
}
1121+
11301122
// ...but if the channel count mismatches, metadata needs to be corrected
1131-
if (s.c > dims[1]) {
1132-
LOG.debug("OME-XML has {} channels; using {} Zarr channels instead",
1133-
s.c, dims[1]);
1134-
mergeChannels(seriesIndex, s.c, false);
1135-
s.c = dims[1];
1136-
}
1137-
else if (s.c < dims[1]) {
1138-
LOG.debug(
1139-
"OME-XML has {} channels; adding {} to match {} Zarr channels",
1140-
s.c, dims[1] - s.c + 1, dims[1]);
1141-
for (int channel=s.c; channel<dims[1]; channel++) {
1142-
metadata.setChannelID(
1143-
MetadataTools.createLSID("Channel", seriesIndex, channel),
1144-
seriesIndex, channel);
1123+
if (channelIndex >= 0) {
1124+
if (s.c > dims[channelIndex]) {
1125+
LOG.debug("OME-XML has {} channels; using {} Zarr channels instead",
1126+
s.c, dims[channelIndex]);
1127+
mergeChannels(seriesIndex, s.c, false);
1128+
s.c = dims[channelIndex];
1129+
}
1130+
else if (s.c < dims[channelIndex]) {
1131+
LOG.debug(
1132+
"OME-XML has {} channels; adding {} to match {} Zarr channels",
1133+
s.c, dims[channelIndex] - s.c + 1, dims[channelIndex]);
1134+
for (int channel=s.c; channel<dims[channelIndex]; channel++) {
1135+
metadata.setChannelID(
1136+
MetadataTools.createLSID("Channel", seriesIndex, channel),
1137+
seriesIndex, channel);
1138+
}
1139+
metadata.setPixelsSizeC(
1140+
new PositiveInteger(dims[channelIndex]), seriesIndex);
1141+
s.c = dims[channelIndex];
11451142
}
1146-
metadata.setPixelsSizeC(new PositiveInteger(dims[1]), seriesIndex);
1147-
s.c = dims[1];
11481143
}
11491144

11501145
s.dimensionLengths[s.dimensionOrder.indexOf("C") - 2] = s.c;

src/main/java/com/glencoesoftware/pyramid/PyramidSeries.java

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.io.IOException;
1111
import java.util.ArrayList;
1212
import java.util.List;
13+
import java.util.Map;
1314
import loci.formats.FormatException;
1415
import loci.formats.FormatTools;
1516
import loci.formats.ome.OMEPyramidStore;
@@ -65,20 +66,46 @@ public void describePyramid(ZarrGroup reader, OMEPyramidStore metadata)
6566
throws FormatException, IOException
6667
{
6768
LOG.info("Number of resolution levels: {}", numberOfResolutions);
69+
70+
List<Map<String, Object>> multiscales =
71+
(List<Map<String, Object>>) reader.openSubGroup(path).getAttributes().get(
72+
"multiscales");
73+
Map<String, Object> multiscale = multiscales.get(0);
74+
List<Map<String, Object>> axes = null;
75+
if (multiscales != null) {
76+
axes = (List<Map<String, Object>>) multiscale.get("axes");
77+
}
78+
6879
resolutions = new ArrayList<ResolutionDescriptor>();
6980
for (int resolution = 0; resolution < numberOfResolutions; resolution++) {
7081
ResolutionDescriptor descriptor = new ResolutionDescriptor();
7182
descriptor.resolutionNumber = resolution;
7283
descriptor.path = path + "/" + resolution;
7384

85+
if (axes != null) {
86+
for (Map<String, Object> axis : axes) {
87+
descriptor.addAxis(axis.get("name").toString());
88+
}
89+
}
90+
else {
91+
descriptor.addAxis("T");
92+
descriptor.addAxis("C");
93+
descriptor.addAxis("Z");
94+
descriptor.addAxis("Y");
95+
descriptor.addAxis("X");
96+
}
97+
7498
ZarrArray array = reader.openArray(descriptor.path);
7599
int[] dimensions = array.getShape();
76100
int[] blockSizes = array.getChunks();
77101

78-
descriptor.sizeX = dimensions[dimensions.length - 1];
79-
descriptor.sizeY = dimensions[dimensions.length - 2];
80-
descriptor.tileSizeX = blockSizes[blockSizes.length - 1];
81-
descriptor.tileSizeY = blockSizes[blockSizes.length - 2];
102+
int xIndex = descriptor.getIndex("X");
103+
int yIndex = descriptor.getIndex("Y");
104+
105+
descriptor.sizeX = dimensions[xIndex];
106+
descriptor.sizeY = dimensions[yIndex];
107+
descriptor.tileSizeX = blockSizes[xIndex];
108+
descriptor.tileSizeY = blockSizes[yIndex];
82109

83110
if (descriptor.tileSizeX % 16 != 0) {
84111
LOG.debug("Tile width ({}) not a multiple of 16; correcting",
@@ -116,15 +143,32 @@ public void describePyramid(ZarrGroup reader, OMEPyramidStore metadata)
116143
}
117144
}
118145

119-
if (dimensions.length != 5) {
120-
throw new FormatException(String.format(
121-
"Expected 5 dimensions in series %d, found %d",
122-
index, dimensions.length));
123-
}
124-
for (int i=0; i<dimensions.length-2; i++) {
125-
if (dimensions[i] != dimensionLengths[2 - i]) {
126-
throw new FormatException(
127-
"Dimension order mismatch in series " + index);
146+
for (int i=0; i<dimensionLengths.length; i++) {
147+
// dimensionLengths is in ZCT order, independent of dimensionOrder
148+
// the two orders may be different if the --rgb flag was used
149+
String axis = "ZCT".substring(i, i + 1);
150+
int axisIndex = descriptor.getIndex(axis);
151+
LOG.debug("Checking axis {} with index {}, position {}",
152+
axis, axisIndex, i);
153+
154+
if (axisIndex < 0 && dimensionLengths[i] > 1) {
155+
throw new FormatException(axis + " axis expected but not defined");
156+
}
157+
else if (axisIndex >= 0 &&
158+
dimensions[axisIndex] != dimensionLengths[i])
159+
{
160+
// a mismatch on C is usually OK (due to --rgb flag),
161+
// but log it anyway
162+
// a mismatch anywhere else is a problem
163+
if (axis.equalsIgnoreCase("c")) {
164+
LOG.debug("Mismatch on dimension {}; expected {} got {}",
165+
axis, dimensions[axisIndex], dimensionLengths[i]);
166+
}
167+
else {
168+
throw new FormatException(
169+
"Mismatch on dimension " + axis + "; expected " +
170+
dimensions[axisIndex] + ", got " + dimensionLengths[i]);
171+
}
128172
}
129173
}
130174
}

src/main/java/com/glencoesoftware/pyramid/ResolutionDescriptor.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
*/
88
package com.glencoesoftware.pyramid;
99

10+
import java.util.ArrayList;
11+
1012
public class ResolutionDescriptor {
1113
/** Path to resolution. */
1214
String path;
@@ -31,4 +33,70 @@ public class ResolutionDescriptor {
3133

3234
/** Number of tiles along Y axis. */
3335
Integer numberOfTilesY;
36+
37+
/** Axes in the underlying array, in order. */
38+
ArrayList<String> axes = new ArrayList<String>();
39+
40+
/**
41+
* Add named axis to ordered list of axes in this resolution.
42+
* Names are stored as upper-case only.
43+
*
44+
* @param axis name e.g. "x"
45+
*/
46+
public void addAxis(String axis) {
47+
axes.add(axis.toUpperCase());
48+
}
49+
50+
/**
51+
* Find the index in the ordered list of the named axis.
52+
*
53+
* @param axis name e.g. "x"
54+
* @return index into list of axes
55+
*/
56+
public int getIndex(String axis) {
57+
return axes.indexOf(axis.toUpperCase());
58+
}
59+
60+
/**
61+
* Create an indexing array (e.g. shape or offset) for this resolution,
62+
* which represents the given 5D values.
63+
* Since the resolution's underlying array may have less than 5 dimensions,
64+
* this is mapping from the 5D space of the OME data model to the
65+
* ND space of this resolution's array.
66+
*
67+
* @param t T index
68+
* @param c C index
69+
* @param z Z index
70+
* @param y Y index
71+
* @param x X index
72+
* @return array representing the given indexes, in this resolution's
73+
* dimensional space
74+
*/
75+
public int[] getArray(int t, int c, int z, int y, int x) {
76+
int[] returnArray = new int[axes.size()];
77+
for (int i=0; i<axes.size(); i++) {
78+
char axis = axes.get(i).charAt(0);
79+
switch (axis) {
80+
case 'X':
81+
returnArray[i] = x;
82+
break;
83+
case 'Y':
84+
returnArray[i] = y;
85+
break;
86+
case 'Z':
87+
returnArray[i] = z;
88+
break;
89+
case 'C':
90+
returnArray[i] = c;
91+
break;
92+
case 'T':
93+
returnArray[i] = t;
94+
break;
95+
default:
96+
throw new IllegalArgumentException("Unexpected axis: " + axis);
97+
}
98+
}
99+
return returnArray;
100+
}
101+
34102
}

0 commit comments

Comments
 (0)