diff --git a/src/main/java/com/glencoesoftware/bioformats2raw/BioTekReader.java b/src/main/java/com/glencoesoftware/bioformats2raw/BioTekReader.java index b201a6d..3ee3d77 100644 --- a/src/main/java/com/glencoesoftware/bioformats2raw/BioTekReader.java +++ b/src/main/java/com/glencoesoftware/bioformats2raw/BioTekReader.java @@ -212,17 +212,53 @@ protected void initFile(String id) throws FormatException, IOException { String[] files = parent.list(true); Arrays.sort(files); + + // is there only one well in the directory? + // compare the well identifiers (relative file name up to first _) + boolean sameWell = true; + int endIndex = files[0].indexOf("_"); + if (endIndex > 0) { + String wellCheck = files[0].substring(0, endIndex); + LOGGER.debug("well check string = {}", wellCheck); + for (int i=0; i allFiles = new ArrayList(); + for (String well : wellDirs) { + Location wellDir = new Location(plateDir, well).getAbsoluteFile(); + LOGGER.debug("looking in well directory = {}", wellDir); + String[] f = wellDir.list(true); + for (String file : f) { + LOGGER.debug(" adding well file {}", file); + allFiles.add(new Location(wellDir, file).getAbsolutePath()); + } + } + LOGGER.debug("found files = {}", allFiles); + files = allFiles.toArray(new String[allFiles.size()]); + Arrays.sort(files); + } + Pattern regexA = Pattern.compile(TIFF_REGEX_A); Pattern regexB = Pattern.compile(TIFF_REGEX_B); Pattern regexZ = Pattern.compile(TIFF_REGEX_Z); + ArrayList validWellRowCol = new ArrayList(); int maxRow = 0; - int minRow = Integer.MAX_VALUE; int maxCol = 0; - int minCol = Integer.MAX_VALUE; int maxPlateAcq = 0; Map maxField = new HashMap(); - for (String f : files) { + for (String absolutePath : files) { + String f = new Location(absolutePath).getName(); Matcher m = regexA.matcher(f); int rowIndex = -1; int colIndex = -1; @@ -269,20 +305,17 @@ protected void initFile(String id) throws FormatException, IOException { well.setFieldCount(fieldIndex + 1); } int c = well.addChannelName(fieldIndex, channelName); - well.addFile(new PlaneIndex(fieldIndex, z, c, t), - new Location(parent, f).getAbsolutePath()); + well.addFile(new PlaneIndex(fieldIndex, z, c, t), absolutePath); if (rowIndex > maxRow) { maxRow = rowIndex; } - if (rowIndex < minRow) { - minRow = rowIndex; - } if (colIndex > maxCol) { maxCol = colIndex; } - if (colIndex < minCol) { - minCol = colIndex; + WellIndex rowColPair = new WellIndex(rowIndex, colIndex); + if (!validWellRowCol.contains(rowColPair)) { + validWellRowCol.add(rowColPair); } Integer maxFieldIndex = maxField.get(0); if (maxFieldIndex == null) { @@ -292,6 +325,7 @@ protected void initFile(String id) throws FormatException, IOException { } } wells.sort(null); + validWellRowCol.sort(null); // split brightfield channels into a separate plate acquisition maxField.put(1, -1); @@ -443,12 +477,14 @@ else if (pa == 1) { int nextImage = 0; int[] nextWellSample = new int[(maxRow + 1) * (maxCol + 1)]; - int totalColumns = (maxCol - minCol) + 1; for (int w=0; w { + public int row; + public int col; + + public WellIndex(int r, int c) { + this.row = r; + this.col = c; + } + + @Override + public int compareTo(WellIndex w) { + if (this.row != w.row) { + return this.row - w.row; + } + return this.col - w.col; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof WellIndex)) { + return false; + } + return compareTo((WellIndex) o) == 0; + } + + @Override + public int hashCode() { + // this would need fixing if we had more than 65535 rows or columns + return (row & 0xffff) << 16 | (col & 0xffff); + } + } + } diff --git a/src/test/java/com/glencoesoftware/bioformats2raw/test/BioTekTest.java b/src/test/java/com/glencoesoftware/bioformats2raw/test/BioTekTest.java index cae226e..290c935 100644 --- a/src/test/java/com/glencoesoftware/bioformats2raw/test/BioTekTest.java +++ b/src/test/java/com/glencoesoftware/bioformats2raw/test/BioTekTest.java @@ -71,7 +71,12 @@ void createPlate(String[] filenames) throws IOException { Path testTiff = getTestFile("test.tiff"); for (String f : filenames) { - Files.copy(testTiff, input.resolve(f)); + Path copyLocation = input.resolve(f); + if (!input.equals(copyLocation.getParent())) { + // supplied file path includes a subdirectory + copyLocation.getParent().toFile().mkdirs(); + } + Files.copy(testTiff, copyLocation); } // reset the input path to the first file in the list @@ -80,16 +85,15 @@ void createPlate(String[] filenames) throws IOException { } /** - * Create an artificial single well BioTek plate with the given - * list of file names. + * Create an artificial BioTek plate with the given list of file names. * Checks that the correct well row/column and ZCT sizes are detected. * * This test will be run once for each Arguments object * returned by getTestCases below. * * @param paths path to each file in the plate - * @param wellRow well row index (from 0) - * @param wellColumn well column index (from 0) + * @param wellRow row index for each well (from 0) + * @param wellColumn column index for each well (from 0) * @param fields number of fields in the well * @param sizeZ number of Z sections * @param sizeC number of channels @@ -97,7 +101,7 @@ void createPlate(String[] filenames) throws IOException { */ @ParameterizedTest @MethodSource("getTestCases") - public void testBioTek(String[] paths, int wellRow, int wellColumn, + public void testBioTek(String[] paths, int[] wellRow, int[] wellColumn, int fields, int sizeZ, int sizeC, int sizeT) throws Exception { // set up the artificial plate @@ -112,29 +116,33 @@ public void testBioTek(String[] paths, int wellRow, int wellColumn, // an exception which would fail the test reader.setId(input.toString()); - // the number of OME Images should match the expected field count + // the number of OME Images should match the expected well * field count // this should be the same as the series count - assertEquals(metadata.getImageCount(), fields); + assertEquals(metadata.getImageCount(), fields * wellRow.length); assertEquals(metadata.getImageCount(), reader.getSeriesCount()); - // there should be exactly one plate, with exactly one well + // there should be exactly one plate assertEquals(metadata.getPlateCount(), 1); - assertEquals(metadata.getWellCount(0), 1); + assertEquals(metadata.getWellCount(0), wellRow.length); // the well's row and column indexes should match expectations // this is especially important for "sparse" plates where the first // row and/or column in the plate are missing - assertEquals(metadata.getWellRow(0, 0).getValue(), wellRow); - assertEquals(metadata.getWellColumn(0, 0).getValue(), wellColumn); - // all of the Images should be linked to the well - assertEquals(metadata.getWellSampleCount(0, 0), fields); - for (int f=0; f getTestCases() { return Stream.of( Arguments.of(new String[] { "A1_-1_1_1_Tsf[Phase Contrast]_001.tif" - }, 0, 0, 1, 1, 1, 1), + }, new int[] {0}, new int[] {0}, 1, 1, 1, 1), Arguments.of(new String[] { "A1_01_1_1_Phase Contrast_001.tif", "A1_01_1_2_Phase Contrast_001.tif", @@ -186,33 +194,37 @@ static Stream getTestCases() { "A1_01_1_7_Phase Contrast_001.tif", "A1_01_1_8_Phase Contrast_001.tif", "A1_01_1_9_Phase Contrast_001.tif", - }, 0, 0, 9, 1, 1, 1), + }, new int[] {0}, new int[] {0}, 9, 1, 1, 1), Arguments.of(new String[] { "P24_1_Bright Field_1_001_02.tif" - }, 15, 23, 1, 1, 1, 1), + }, new int[] {15}, new int[] {23}, 1, 1, 1, 1), Arguments.of(new String[] { "B2_1_Bright Field_1_001_02.tif" - }, 1, 1, 1, 1, 1, 1), + }, new int[] {1}, new int[] {1}, 1, 1, 1, 1), Arguments.of(new String[] { "A1_1_Stitched[AandB_Phase Contrast]_1_001_-1.tif" - }, 0, 0, 1, 1, 1, 1), + }, new int[] {0}, new int[] {0}, 1, 1, 1, 1), Arguments.of(new String[] { "A1_1Z0_DAPI_1_001_.tif", "A1_1Z1_DAPI_1_001_.tif", "A1_1Z2_DAPI_1_001_.tif", "A1_1Z3_DAPI_1_001_.tif", "A1_1Z4_DAPI_1_001_.tif" - }, 0, 0, 1, 5, 1, 1), + }, new int[] {0}, new int[] {0}, 1, 5, 1, 1), Arguments.of(new String[] { "A1_-1_1_1_Stitched[Channel1 300,400]_001.tif", "A1_-1_2_1_Stitched[Channel2 500,600]_001.tif", "A1_-1_3_1_Stitched[Channel 3 600,650]_001.tif" - }, 0, 0, 1, 1, 3, 1), + }, new int[] {0}, new int[] {0}, 1, 1, 3, 1), Arguments.of(new String[] { "A1_-2_1_1_Tsf[Stitched[Channel1 300,400]]_001.tif", "A1_-2_2_1_Tsf[Stitched[Channel2 500,600]]_001.tif", "A1_-2_3_1_Tsf[Stitched[Channel 3 600,650]]_001.tif" - }, 0, 0, 1, 1, 3, 1) + }, new int[] {0}, new int[] {0}, 1, 1, 3, 1), + Arguments.of(new String[] { + "B2/B2_1_Bright Field_1_001_02.tif", + "D3/D3_1_Bright Field_1_001_02.tif", + }, new int[] {1, 3}, new int[] {1, 2}, 1, 1, 1, 1) ); }