Skip to content

Commit

Permalink
Merge pull request #91 from melissalinkert/well-indexing
Browse files Browse the repository at this point in the history
Add "row_index" and "column_index" for each well
  • Loading branch information
chris-allan authored Mar 24, 2021
2 parents 0b2ff91 + 35813db commit c75b088
Show file tree
Hide file tree
Showing 5 changed files with 397 additions and 53 deletions.
27 changes: 16 additions & 11 deletions src/main/java/com/glencoesoftware/bioformats2raw/Converter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,6 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {

Map<String, Object> well = new HashMap<String, Object>();
well.put("path", wellPath);
wells.add(well);

List<Map<String, Object>> imageList =
new ArrayList<Map<String, Object>>();
Expand Down Expand Up @@ -1228,31 +1227,37 @@ private void saveHCSMetadata(IMetadata meta) throws IOException {
int column = index.getWellColumnIndex();
int row = index.getWellRowIndex();

boolean foundColumn = false;
for (Map<String, Object> colMap : columns) {
if (colMap.get("name").equals(String.valueOf(column))) {
foundColumn = true;
int columnIndex = -1;
for (int c=0; c<columns.size(); c++) {
if (columns.get(c).get("name").equals(String.valueOf(column))) {
columnIndex = c;
break;
}
}
if (!foundColumn) {
if (columnIndex < 0) {
Map<String, Object> colMap = new HashMap<String, Object>();
colMap.put("name", String.valueOf(column));
columnIndex = columns.size();
columns.add(colMap);
}

boolean foundRow = false;
for (Map<String, Object> rowMap : rows) {
if (rowMap.get("name").equals(String.valueOf(row))) {
foundRow = true;
int rowIndex = -1;
for (int r=0; r<rows.size(); r++) {
if (rows.get(r).get("name").equals(String.valueOf(row))) {
rowIndex = r;
break;
}
}
if (!foundRow) {
if (rowIndex < 0) {
Map<String, Object> rowMap = new HashMap<String, Object>();
rowMap.put("name", String.valueOf(row));
rowIndex = rows.size();
rows.add(rowMap);
}

well.put("row_index", rowIndex);
well.put("column_index", columnIndex);
wells.add(well);
}

maxField = (int) Math.max(maxField, index.getFieldIndex());
Expand Down
255 changes: 213 additions & 42 deletions src/test/java/com/glencoesoftware/bioformats2raw/test/ZarrTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -793,31 +793,15 @@ public void testHCSMetadata() throws IOException {

ZarrGroup z = ZarrGroup.open(output);

// check valid group layout
// METADATA.ome.xml, .zattrs (Plate), .zgroup (Plate) and 2 rows
assertEquals(5, Files.list(output).toArray().length);
for (int row=0; row<2; row++) {
Path rowPath = output.resolve(Integer.toString(row));
// .zgroup (Row) and 3 columns
assertEquals(4, Files.list(rowPath).toArray().length);
for (int col=0; col<3; col++) {
Path colPath = rowPath.resolve(Integer.toString(col));
ZarrGroup colGroup = ZarrGroup.open(colPath);
// .zattrs (Column/Image), .zgroup (Column/Image) and 2 fields
assertEquals(4, Files.list(colPath).toArray().length);
for (int field=0; field<2; field++) {
// append resolution index
ZarrArray series0 = colGroup.openArray(field + "/0");
assertArrayEquals(new int[] {1, 1, 1, 512, 512}, series0.getShape());
assertArrayEquals(new int[] {1, 1, 1, 512, 512}, series0.getChunks());
}
}
}
int rowCount = 2;
int colCount = 3;
int fieldCount = 2;
checkPlateGroupLayout(output, rowCount, colCount, fieldCount, 512, 512);

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

List<Map<String, Object>> acquisitions =
(List<Map<String, Object>>) plate.get("acquisitions");
Expand All @@ -831,15 +815,8 @@ public void testHCSMetadata() throws IOException {
assertEquals(1, acquisitions.size());
assertEquals("0", acquisitions.get(0).get("id"));

assertEquals(2, rows.size());
for (int row=0; row<rows.size(); row++) {
assertEquals(String.valueOf(row), rows.get(row).get("name"));
}

assertEquals(3, columns.size());
for (int col=0; col<columns.size(); col++) {
assertEquals(String.valueOf(col), columns.get(col).get("name"));
}
checkDimension(rows, rowCount);
checkDimension(columns, colCount);

assertEquals(rows.size() * columns.size(), wells.size());
for (int row=0; row<rows.size(); row++) {
Expand All @@ -856,21 +833,155 @@ public void testHCSMetadata() throws IOException {
String columnName = (String) column.get("name");
ZarrGroup wellGroup = ZarrGroup.open(
output.resolve(rowName).resolve(columnName));
Map<String, Object> well =
(Map<String, Object>) wellGroup.getAttributes().get("well");
List<Map<String, Object>> images =
(List<Map<String, Object>>) well.get("images");
assertEquals(2, images.size());
Map<String, Object> field1 = images.get(0); // Field 1
assertEquals(field1.get("path"), "0");
assertEquals(0, field1.get("acquisition"));
Map<String, Object> field2 = images.get(1); // Field 2
assertEquals(field2.get("path"), "1");
assertEquals(0, field2.get("acquisition"));
checkWell(wellGroup, fieldCount);
}
}
}

/**
* 96 well plate with only well E6.
*/
@Test
public void testSingleWell() throws IOException {
input = getTestFile("E6-only.ome.xml");
assertTool();

ZarrGroup z = ZarrGroup.open(output);

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

// 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>> acquisitions =
(List<Map<String, Object>>) plate.get("acquisitions");
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");

assertEquals(1, acquisitions.size());
assertEquals("0", acquisitions.get(0).get("id"));

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

assertEquals(1, wells.size());
Map<String, Object> well = wells.get(0);
String wellPath = (String) well.get("path");
assertEquals("4/5", wellPath);
assertEquals(4, ((Number) well.get("row_index")).intValue());
assertEquals(5, ((Number) well.get("column_index")).intValue());

// check well metadata
ZarrGroup wellGroup = ZarrGroup.open(output.resolve(wellPath));
checkWell(wellGroup, fieldCount);
}

/**
* 96 well plate with only wells C4 and H2.
*/
@Test
public void testTwoWells() throws IOException {
input = getTestFile("C4-H2-only.ome.xml");
assertTool();

ZarrGroup z = ZarrGroup.open(output);

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

// 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>> acquisitions =
(List<Map<String, Object>>) plate.get("acquisitions");
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");

assertEquals(1, acquisitions.size());
assertEquals("0", acquisitions.get(0).get("id"));

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

assertEquals(2, wells.size());
Map<String, Object> well = wells.get(0);
String wellPath = (String) well.get("path");
assertEquals("2/3", wellPath);
assertEquals(2, ((Number) well.get("row_index")).intValue());
assertEquals(3, ((Number) well.get("column_index")).intValue());
ZarrGroup wellGroup = ZarrGroup.open(output.resolve(wellPath));
checkWell(wellGroup, fieldCount);

well = wells.get(1);
wellPath = (String) well.get("path");
assertEquals("7/1", wellPath);
assertEquals(7, ((Number) well.get("row_index")).intValue());
assertEquals(1, ((Number) well.get("column_index")).intValue());
wellGroup = ZarrGroup.open(output.resolve(wellPath));
checkWell(wellGroup, fieldCount);
}

/**
* 96 well plate with all wells in row F.
*/
@Test
public void testOnePlateRow() throws IOException {
input = getTestFile("row-F-only.ome.xml");
assertTool();

ZarrGroup z = ZarrGroup.open(output);

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

// 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>> acquisitions =
(List<Map<String, Object>>) plate.get("acquisitions");
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");

assertEquals(1, acquisitions.size());
assertEquals("0", acquisitions.get(0).get("id"));

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

assertEquals(colCount, wells.size());
for (int col=0; col<wells.size(); col++) {
Map<String, Object> well = wells.get(col);
String wellPath = (String) well.get("path");
assertEquals("5/" + col, wellPath);
assertEquals(5, ((Number) well.get("row_index")).intValue());
assertEquals(col, ((Number) well.get("column_index")).intValue());
ZarrGroup wellGroup = ZarrGroup.open(output.resolve(wellPath));
checkWell(wellGroup, fieldCount);
}
}

/**
* Convert an RGB image. Ensure that the Channels are correctly split.
*/
Expand All @@ -889,7 +1000,6 @@ public void testRGBChannelSeparator() throws Exception {
assertEquals(3, pixels.sizeOfChannelList());
}


/**
* Check that a root group and attributes are created and populated.
*/
Expand All @@ -914,4 +1024,65 @@ public void testNoRootGroupOption() throws Exception {
assertTrue(!Files.exists(output.resolve(".zattrs")));
assertTrue(!Files.exists(output.resolve(".zgroup")));
}

private void checkPlateGroupLayout(Path root, int rowCount, int colCount,
int fieldCount, int x, int y)
throws IOException
{
// check valid group layout
// METADATA.ome.xml, .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));
// .zgroup (Row) and columns
assertEquals(colCount + 1, Files.list(rowPath).toArray().length);
for (int col=0; col<colCount; col++) {
Path colPath = rowPath.resolve(Integer.toString(col));
ZarrGroup colGroup = ZarrGroup.open(colPath);
// .zattrs (Column/Image), .zgroup (Column/Image) and fields
assertEquals(fieldCount + 2, Files.list(colPath).toArray().length);
for (int field=0; field<fieldCount; field++) {
// append resolution index
ZarrArray series0 = colGroup.openArray(field + "/0");
assertArrayEquals(new int[] {1, 1, 1, y, x}, series0.getShape());
assertArrayEquals(new int[] {1, 1, 1, y, x}, series0.getChunks());
}
}
}
}

private void checkDimension(List<Map<String, Object>> dims, int dimCount)
throws IOException
{
assertEquals(dimCount, dims.size());
for (int dim=0; dim<dims.size(); dim++) {
assertEquals(String.valueOf(dim), dims.get(dim).get("name"));
}
}

private void checkWell(ZarrGroup wellGroup, int fieldCount)
throws IOException
{
Map<String, Object> well =
(Map<String, Object>) wellGroup.getAttributes().get("well");
List<Map<String, Object>> images =
(List<Map<String, Object>>) well.get("images");
assertEquals(fieldCount, images.size());

for (int i=0; i<fieldCount; i++) {
Map<String, Object> field = images.get(i);
assertEquals(field.get("path"), String.valueOf(i));
assertEquals(0, field.get("acquisition"));
}
}

private Path getTestFile(String resourceName) throws IOException {
try {
return Paths.get(this.getClass().getResource(resourceName).toURI());
}
catch (Exception e) {
throw new IOException(e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?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="3" ExternalDescription="External Description" ExternalIdentifier="External Identifier" ID="Well:0_0_0_0" Row="2" 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="1" ExternalDescription="External Description" ExternalIdentifier="External Identifier" ID="Well:0_0_1_0" Row="7" 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>
<PlateAcquisition EndTime="2006-05-04T18:13:51" ID="PlateAcquisition:0" Name="PlateAcquisition Name 0" StartTime="2006-05-04T18:13:51">
<Description>PlateAcquisition 0 of 1</Description>
<WellSampleRef ID="WellSample:0_0_0_0_0_0"></WellSampleRef>
<WellSampleRef ID="WellSample:0_0_1_0_0_0"></WellSampleRef></PlateAcquisition></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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?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="5" ExternalDescription="External Description" ExternalIdentifier="External Identifier" ID="Well:0_0_0_0" Row="4" 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>
<PlateAcquisition EndTime="2006-05-04T18:13:51" ID="PlateAcquisition:0" Name="PlateAcquisition Name 0" StartTime="2006-05-04T18:13:51">
<Description>PlateAcquisition 0 of 1</Description>
<WellSampleRef ID="WellSample:0_0_0_0_0_0"></WellSampleRef></PlateAcquisition></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></OME>
Loading

0 comments on commit c75b088

Please sign in to comment.