Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix problems with missing groups/metadata and sparse plates #119

Merged
merged 14 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 9 additions & 5 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,6 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {
String rowName = String.valueOf(r);
row.put("name", rowName);
rows.add(row);
root.createSubGroup(rowName);
}
}
catch (NullPointerException e) {
Expand All @@ -1363,7 +1362,9 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {
acquisition.put("id", String.valueOf(pa));
acquisitions.add(acquisition);
}
plateMap.put("acquisitions", acquisitions);
if (acquisitions.size() > 0) {
plateMap.put("acquisitions", acquisitions);
}

List<Map<String, Object>> wells = new ArrayList<Map<String, Object>>();
int maxField = Integer.MIN_VALUE;
Expand All @@ -1377,7 +1378,10 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {

List<Map<String, Object>> imageList =
new ArrayList<Map<String, Object>>();
ZarrGroup wellGroup = root.createSubGroup(wellPath);
String rowPath = index.getRowPath();
ZarrGroup rowGroup = root.createSubGroup(rowPath);
String columnPath = index.getColumnPath();
ZarrGroup columnGroup = rowGroup.createSubGroup(columnPath);
for (HCSIndex field : hcsIndexes) {
if (field.getPlateIndex() == index.getPlateIndex() &&
field.getWellRowIndex() == index.getWellRowIndex() &&
Expand All @@ -1393,9 +1397,9 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {

Map<String, Object> wellMap = new HashMap<String, Object>();
wellMap.put("images", imageList);
Map<String, Object> attributes = wellGroup.getAttributes();
Map<String, Object> attributes = columnGroup.getAttributes();
attributes.put("well", wellMap);
wellGroup.writeAttributes(attributes);
columnGroup.writeAttributes(attributes);

// make sure the row/column indexes are added to the plate attributes
// this is necessary when Plate.Rows or Plate.Columns is not set
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/com/glencoesoftware/bioformats2raw/HCSIndex.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,25 @@ public void setFieldIndex(int fieldIndex) {
this.field = fieldIndex;
}

/**
* @return row path relative to the plate group
*/
public String getRowPath() {
return String.format("%d", getWellRowIndex());
}

/**
* @return column path relative to the row group
*/
public String getColumnPath() {
return String.format("%d", getWellColumnIndex());
}

/**
* @return well path relative to the plate group
*/
public String getWellPath() {
return String.format("%d/%d", getWellRowIndex(), getWellColumnIndex());
return String.format("%s/%s", getRowPath(), getColumnPath());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -868,6 +869,65 @@ public void testHCSMetadata() throws Exception {
assertEquals(1, ome.sizeOfPlateList());
}

/**
* Convert a plate with default options.
* The output should be compliant with OME Zarr HCS.
*/
@Test
public void testHCSMetadataNoAcquisitions() throws Exception {
input = getTestFile("C4-H2-only-no-acqs.xml");
assertTool();

ZarrGroup z = ZarrGroup.open(output);

int rowCount = 8;
int colCount = 12;
int fieldCount = 1;

// Only two rows are filled out with one column each (two Wells total)
checkPlateGroupLayout(output, 2, 1, fieldCount, 2, 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check won't be consistent with lines 907-924. The idea is to now have basically everything except arrays written for any wells that don't have data, right? Or have I misunderstood the problem?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed earlier today, and I did indeed misunderstand. This PR is meant to follow the example in https://github.com/ome/ngff/blob/0c286908a4fbaceb2c622b21e5a52cc041eea7c0/latest/index.bs#L468, with the added constraint that wells without pixel data cannot have a .zgroup.


// check plate/well level metadata
Map<String, Object> plate =
(Map<String, Object>) z.getAttributes().get("plate");
assertEquals(fieldCount, ((Number) plate.get("field_count")).intValue());

List<Map<String, Object>> rows =
(List<Map<String, Object>>) plate.get("rows");
List<Map<String, Object>> columns =
(List<Map<String, Object>>) plate.get("columns");
List<Map<String, Object>> wells =
(List<Map<String, Object>>) plate.get("wells");

assertFalse(plate.containsKey("acquisitions"));

checkDimension(rows, rowCount);
checkDimension(columns, colCount);

assertEquals(rows.size() * columns.size(), wells.size());
for (int row=0; row<rows.size(); row++) {
for (int col=0; col<columns.size(); col++) {
int well = row * columns.size() + col;
assertEquals(row + "/" + col, wells.get(well).get("path"));
}
}

// check well metadata
for (Map<String, Object> row : rows) {
String rowName = (String) row.get("name");
for (Map<String, Object> column : columns) {
String columnName = (String) column.get("name");
ZarrGroup wellGroup = ZarrGroup.open(
output.resolve(rowName).resolve(columnName));
checkWell(wellGroup, fieldCount);
}
}

// check OME metadata
OME ome = getOMEMetadata();
assertEquals(1, ome.sizeOfPlateList());
}

/**
* 96 well plate with only well E6.
*/
Expand Down Expand Up @@ -1211,7 +1271,7 @@ private void checkPlateGroupLayout(Path root, int rowCount, int colCount,
throws IOException
{
// check valid group layout
// OME (OME-XML metadata), .zattrs (Plate), .zgroup (Plate) and rows
// OME (OME-XML metadata) folder, .zattrs (Plate), .zgroup (Plate) and rows
assertEquals(rowCount + 3, Files.list(root).toArray().length);
for (int row=0; row<rowCount; row++) {
Path rowPath = root.resolve(Integer.toString(row));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Creator="OME Bio-Formats 6.6.1-SNAPSHOT" xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd">
<Plate ColumnNamingConvention="number" Columns="12" ExternalIdentifier="External Identifier" ID="Plate:0" Name="Plate Name 0" RowNamingConvention="letter" Rows="8" Status="Plate status" WellOriginX="0.0" WellOriginXUnit="µm" WellOriginY="1.0" WellOriginYUnit="µm">
<Description>Plate 0 of 1</Description>
<Well Color="255" Column="0" ExternalDescription="External Description" ExternalIdentifier="External Identifier" ID="Well:0_0_0_0" Row="0" Type="Transfection: done">
<WellSample ID="WellSample:0_0_0_0_0_0" Index="0" PositionX="0.0" PositionXUnit="reference frame" PositionY="1.0" PositionYUnit="reference frame" Timepoint="2006-05-04T18:13:51">
<ImageRef ID="Image:0"></ImageRef></WellSample></Well>
<Well Color="255" Column="0" ExternalDescription="External Description" ExternalIdentifier="External Identifier" ID="Well:0_0_1_0" Row="1" Type="Transfection: done">
<WellSample ID="WellSample:0_0_1_0_0_0" Index="1" PositionX="0.0" PositionXUnit="reference frame" PositionY="1.0" PositionYUnit="reference frame" Timepoint="2006-05-04T18:13:51">
<ImageRef ID="Image:1"></ImageRef></WellSample></Well>
</Plate>
<Image ID="Image:0" Name="test">
<Description>Image Description 0</Description>
<Pixels BigEndian="false" DimensionOrder="XYZCT" ID="Pixels:0" Interleaved="false" PhysicalSizeX="1.0" PhysicalSizeXUnit="µm" PhysicalSizeY="1.0" PhysicalSizeYUnit="µm" PhysicalSizeZ="1.0" PhysicalSizeZUnit="µm" SignificantBits="8" SizeC="1" SizeT="1" SizeX="2" SizeY="2" SizeZ="1" Type="uint8">
<Channel AcquisitionMode="FluorescenceLifetime" Color="1687603455" ContrastMethod="Brightfield" EmissionWavelength="300.3" EmissionWavelengthUnit="nm" ExcitationWavelength="400.3" ExcitationWavelengthUnit="nm" Fluor="Fluor" ID="Channel:0:0" IlluminationType="Oblique" NDFilter="1.0" Name="Name" PinholeSize="0.5" PinholeSizeUnit="µm" PockelCellSetting="0" SamplesPerPixel="1"/>
<Plane DeltaT="0.1" DeltaTUnit="s" ExposureTime="10.0" ExposureTimeUnit="s" PositionX="1.0" PositionXUnit="reference frame" PositionY="1.0" PositionYUnit="reference frame" PositionZ="1.0" PositionZUnit="reference frame" TheC="0" TheT="0" TheZ="0"/>
<BinData xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06" Length="4" BigEndian="false">AAAAAA==</BinData></Pixels></Image>
<Image ID="Image:1" Name="test 2">
<Description>Image Description 1</Description>
<Pixels BigEndian="false" DimensionOrder="XYZCT" ID="Pixels:1" Interleaved="false" PhysicalSizeX="1.0" PhysicalSizeXUnit="µm" PhysicalSizeY="1.0" PhysicalSizeYUnit="µm" PhysicalSizeZ="1.0" PhysicalSizeZUnit="µm" SignificantBits="8" SizeC="1" SizeT="1" SizeX="2" SizeY="2" SizeZ="1" Type="uint8">
<Channel AcquisitionMode="FluorescenceLifetime" Color="1687603455" ContrastMethod="Brightfield" EmissionWavelength="300.3" EmissionWavelengthUnit="nm" ExcitationWavelength="400.3" ExcitationWavelengthUnit="nm" Fluor="Fluor" ID="Channel:1:0" IlluminationType="Oblique" NDFilter="1.0" Name="Name" PinholeSize="0.5" PinholeSizeUnit="µm" PockelCellSetting="0" SamplesPerPixel="1"/>
<Plane DeltaT="0.1" DeltaTUnit="s" ExposureTime="10.0" ExposureTimeUnit="s" PositionX="1.0" PositionXUnit="reference frame" PositionY="1.0" PositionYUnit="reference frame" PositionZ="1.0" PositionZUnit="reference frame" TheC="0" TheT="0" TheZ="0"/>
<BinData xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06" Length="4" BigEndian="false">AQEBAQ==</BinData></Pixels></Image></OME>