diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff70b16..631948b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - java: [8, 11, 17, 21] + java: [17, 21, 25] os: [ubuntu-latest] runs-on: ${{ matrix.os }} env: @@ -38,7 +38,7 @@ jobs: path: build/distributions/*.zip retention-days: 30 - name: Publish artifacts - if: github.event_name != 'pull_request' && matrix.java == 11 && github.repository_owner == 'glencoesoftware' + if: github.event_name != 'pull_request' && matrix.java == 17 && github.repository_owner == 'glencoesoftware' run: | ./gradlew -PArtifactoryUserName=${ArtifactoryUserName} -PArtifactoryPassword=${ArtifactoryPassword} publish release: @@ -50,7 +50,7 @@ jobs: - name: Download artifacts from build uses: actions/download-artifact@v4 with: - name: raw2ometiff 8 + name: raw2ometiff 17 - name: List artifacts run: ls -R - name: Create a GitHub release diff --git a/README.md b/README.md index 14f5e69..fbe2577 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ This is the second half of iSyntax/.mrxs => OME-TIFF conversion. Requirements ============ -Java 8 or later is required. +As of 0.10.0, Java 11 or later is required. libblosc (https://github.com/Blosc/c-blosc) version 1.9.0 or later must be installed separately. The native libraries are not packaged with any relevant jars. See also note in jzarr readme (https://github.com/bcdev/jzarr/blob/master/README.md) @@ -27,6 +27,8 @@ Installation Development Installation ======================== +As of 0.10.0, Java 17 is required to build raw2ometiff. + 1. Clone the repository: git clone git@github.com:glencoesoftware/raw2ometiff.git diff --git a/build.gradle b/build.gradle index bf288b3..003c8a7 100644 --- a/build.gradle +++ b/build.gradle @@ -8,9 +8,14 @@ plugins { group = 'com.glencoesoftware' version = '0.10.0-SNAPSHOT' -mainClassName = 'com.glencoesoftware.pyramid.PyramidFromDirectoryWriter' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +application { + mainClass = 'com.glencoesoftware.pyramid.PyramidFromDirectoryWriter' +} +java { + toolchain { + languageVersion = JavaLanguageVersion.of(11) + } +} repositories { mavenCentral() @@ -30,12 +35,11 @@ repositories { dependencies { implementation 'net.java.dev.jna:jna:5.10.0' - implementation 'dev.zarr:jzarr:0.4.2' - implementation 'dev.zarr:zarr-java:0.0.10' + implementation 'dev.zarr:zarr-java:0.1.0' implementation 'info.picocli:picocli:4.7.5' implementation 'me.tongfei:progressbar:0.9.0' implementation 'ome:formats-bsd:8.5.0' - implementation 'com.glencoesoftware:bioformats2raw:0.12.0-rc1' + implementation 'com.glencoesoftware:bioformats2raw:0.12.0-rc2' implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.3.16' implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.3.16' @@ -62,7 +66,7 @@ jar { "Implementation-Title": "raw2ometiff converter", "Implementation-Version": archiveVersion, "Implementation-Vendor": "Glencoe Software Inc.", - "Main-Class": mainClassName + "Main-Class": application.mainClass ) } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 943f0cb..e644113 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 20db9ad..37f78a6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 6689b85..7101f8e 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/src/main/java/com/glencoesoftware/pyramid/PyramidFromDirectoryWriter.java b/src/main/java/com/glencoesoftware/pyramid/PyramidFromDirectoryWriter.java index 3786117..3f0c8f4 100755 --- a/src/main/java/com/glencoesoftware/pyramid/PyramidFromDirectoryWriter.java +++ b/src/main/java/com/glencoesoftware/pyramid/PyramidFromDirectoryWriter.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; @@ -74,26 +73,19 @@ import com.glencoesoftware.bioformats2raw.IProgressListener; import com.glencoesoftware.bioformats2raw.NoOpProgressListener; import com.glencoesoftware.bioformats2raw.ProgressBarListener; -import ucar.ma2.InvalidRangeException; import picocli.CommandLine; import picocli.CommandLine.Option; import picocli.CommandLine.Parameters; -// Zarr v2 - -import com.bc.zarr.DataType; -import com.bc.zarr.ZarrArray; -import com.bc.zarr.ZarrGroup; - -// Zarr v3 - import dev.zarr.zarrjava.ZarrException; +import dev.zarr.zarrjava.core.Array; +import dev.zarr.zarrjava.core.Attributes; +import dev.zarr.zarrjava.core.Group; +import dev.zarr.zarrjava.core.Node; import dev.zarr.zarrjava.store.FilesystemStore; import dev.zarr.zarrjava.utils.Utils; -import dev.zarr.zarrjava.v3.Array; -import dev.zarr.zarrjava.v3.Group; -import dev.zarr.zarrjava.v3.Node; +import dev.zarr.zarrjava.v2.Endianness; /** * Writes a pyramid OME-TIFF file or Bio-Formats 5.9.x "Faas" TIFF file. @@ -126,6 +118,8 @@ public class PyramidFromDirectoryWriter implements Callable { private static final String OMEXML_FILE = "METADATA.ome.xml"; private static final String V3_GROUP_FILE = "zarr.json"; + private static final String V2_GROUP_FILE = ".zgroup"; + private static final String V2_ATTRS_FILE = ".zattrs"; private static final Logger LOG = LoggerFactory.getLogger(PyramidFromDirectoryWriter.class); @@ -159,12 +153,8 @@ public class PyramidFromDirectoryWriter implements Callable { String imageFile = null; - // used for reading v2 data - private ZarrGroup reader = null; - - // used for reading v3 data - private Group v3Reader = null; - private FilesystemStore v3Store = null; + private Group reader = null; + private FilesystemStore store = null; /** Writer metadata. */ OMEPyramidStore metadata; @@ -715,23 +705,22 @@ private byte[] getInputTileBytes(PyramidSeries s, int resolution, y * descriptor.tileSizeY, x * descriptor.tileSizeX); int[] shape = s.getArray(1, 1, 1, realHeight, realWidth); - if (isV3()) { - return readV3Tile(s, descriptor, pos, shape, gridPosition); - } - return readV2Tile(s, descriptor, pos, shape, gridPosition); + return readTile(s, descriptor, pos, shape, gridPosition); } - private byte[] readV3Tile(PyramidSeries s, ResolutionDescriptor descriptor, + private byte[] readTile(PyramidSeries s, ResolutionDescriptor descriptor, int[] pos, int[] shape, int[] gridPosition) throws FormatException, IOException { - Array block = getZarrV3Array(descriptor.path); + Array block = getZarrArray(descriptor.path); if (block == null) { throw new FormatException("Could not find block = " + descriptor.path + ", position = [" + pos[0] + ", " + pos[1] + ", " + pos[2] + "]"); } try { - ucar.ma2.Array tile = block.read(Utils.toLongArray(gridPosition), shape); + ucar.ma2.Array tile = block.read( + Utils.toLongArray(gridPosition), + Utils.toLongArray(shape)); ByteBuffer buf = tile.getDataAsByteBuffer( s.littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); byte[] bytes = new byte[buf.remaining()]; @@ -743,50 +732,10 @@ private byte[] readV3Tile(PyramidSeries s, ResolutionDescriptor descriptor, } } - private byte[] readV2Tile(PyramidSeries s, ResolutionDescriptor descriptor, - int[] pos, int[] shape, int[] gridPosition) - throws FormatException, IOException - { - ZarrArray block = reader.openArray(descriptor.path); - - if (block == null) { - throw new FormatException("Could not find block = " + descriptor.path + - ", position = [" + pos[0] + ", " + pos[1] + ", " + pos[2] + "]"); - } - - byte[] tile = null; - try { - Object bytes = block.read(shape, gridPosition); - if (bytes instanceof byte[]) { - tile = (byte[]) bytes; - } - else if (bytes instanceof short[]) { - tile = DataTools.shortsToBytes((short[]) bytes, s.littleEndian); - } - else if (bytes instanceof int[]) { - tile = DataTools.intsToBytes((int[]) bytes, s.littleEndian); - } - else if (bytes instanceof long[]) { - tile = DataTools.longsToBytes((long[]) bytes, s.littleEndian); - } - else if (bytes instanceof float[]) { - tile = DataTools.floatsToBytes((float[]) bytes, s.littleEndian); - } - else if (bytes instanceof double[]) { - tile = DataTools.doublesToBytes((double[]) bytes, s.littleEndian); - } - } - catch (InvalidRangeException e) { - throw new IOException("Could not read from " + descriptor.path, e); - } - - return tile; - } - /** * Translate Zarr attributes to the current metadata store. */ - private void populateMetadata() throws IOException { + private void populateMetadata() throws FormatException, IOException { if (plateData != null) { List> acquisitions = (List>) plateData.get("acquisitions"); @@ -844,18 +793,10 @@ private void populateMetadata() throws IOException { metadata.setWellColumn(new NonNegativeInteger(colIndex), 0, i); metadata.setWellRow(new NonNegativeInteger(rowIndex), 0, i); - Map wellGroupAttrs = null; - if (isV3()) { - Group v3WellGroup = getZarrGroupV3(well); - wellGroupAttrs = v3WellGroup.metadata.attributes; - } - else { - ZarrGroup wellGroup = getZarrGroup(well); - wellGroupAttrs = wellGroup.getAttributes(); - } + Group wellGroup = getZarrGroup(well); + Attributes wellGroupAttrs = getGroupAttributes(wellGroup); - Map wellAttr = - (Map) wellGroupAttrs.get("well"); + Attributes wellAttr = wellGroupAttrs.getAttributes("well"); List> images = (List>) wellAttr.get("images"); for (int img=0; img !key.equals(V3_GROUP_FILE)) + .filter(key -> !key.equals(V2_GROUP_FILE)) + .filter(key -> !key.equals(V2_ATTRS_FILE)) + .count(); } - private int getSubgroupCount(String path) throws IOException { - if (isV3()) { - return (int) v3Store.resolve(path).listChildren() - .filter(key -> !key.equals(V3_GROUP_FILE)) - .count(); + private Group getZarrGroup(String... path) + throws FormatException, IOException + { + try { + return Group.open(store.resolve(path)); + } + catch (ZarrException e) { + throw new FormatException(e); } - return getZarrGroup(path).getGroupKeys().size(); } - private Group getZarrGroupV3(String... path) throws IOException { - return Group.open(v3Store.resolve(path)); + private Attributes getGroupAttributes(Group g) + throws FormatException, IOException + { + try { + return g.metadata().attributes(); + } + catch (ZarrException e) { + throw new FormatException(e); + } } - private Array getZarrV3Array(String... path) + private Array getZarrArray(String... path) throws FormatException, IOException { try { - return Array.open(v3Store.resolve(path)); + return Array.open(store.resolve(path)); } catch (ZarrException e) { throw new FormatException(e); @@ -1018,22 +985,17 @@ private int getSeriesCount() throws IOException { LOG.debug(" returning plate-based series count = {}", count); return count; } - if (isV3()) { - return (int) v3Reader.storeHandle.listChildren() - .filter(key -> !key.equals("OME") && !key.equals(V3_GROUP_FILE)) - .filter(key -> isV3Group(key)) - .count(); - } - Set groupKeys = reader.getGroupKeys(); - groupKeys.remove("OME"); - int groupKeyCount = groupKeys.size(); - LOG.debug(" group key count = {}", groupKeyCount); - return groupKeyCount; + return (int) reader.storeHandle.listChildren() + .filter(key -> !key.equals("OME") && !key.equals(V3_GROUP_FILE)) + .filter(key -> !key.equals(V2_GROUP_FILE)) + .filter(key -> !key.equals(V2_ATTRS_FILE)) + .filter(key -> isGroup(key)) + .count(); } - private boolean isV3Group(String... key) { + private boolean isGroup(String... key) { try { - return Node.open(v3Reader.storeHandle.resolve(key)) instanceof Group; + return Node.open(reader.storeHandle.resolve(key)) instanceof Group; } catch (ZarrException|IOException e) { LOG.debug("Could not open " + key, e); @@ -1047,30 +1009,26 @@ private boolean isV3Group(String... key) { * * @param s current series */ - private void findNumberOfResolutions(PyramidSeries s) throws IOException { + private void findNumberOfResolutions(PyramidSeries s) + throws FormatException, IOException + { int arrayKeys = 0; List> multiscales = null; - if (isV3()) { - Group v3Series = getZarrGroupV3(s.path); - if (v3Series == null) { - throw new IOException("Expected series " + s.index + " not found"); - } + Group seriesGroup = getZarrGroup(s.path); + if (seriesGroup == null) { + throw new IOException("Expected series " + s.index + " not found"); + } - Map ome = - (Map) v3Series.metadata.attributes.get("ome"); + if (isV3()) { + Attributes ome = getGroupAttributes(seriesGroup).getAttributes("ome"); multiscales = (List>) ome.get("multiscales"); } else { - ZarrGroup seriesGroup = getZarrGroup(s.path); - if (seriesGroup == null) { - throw new IOException("Expected series " + s.index + " not found"); - } - arrayKeys = seriesGroup.getArrayKeys().size(); + arrayKeys = getSubgroupCount(s.path); - Map seriesAttributes = seriesGroup.getAttributes(); - multiscales = - (List>) seriesAttributes.get("multiscales"); + multiscales = (List>) + getGroupAttributes(seriesGroup).get("multiscales"); } // use multiscales metadata if it exists, to distinguish between @@ -1084,6 +1042,9 @@ private void findNumberOfResolutions(PyramidSeries s) throws IOException { s.numberOfResolutions = datasets.size(); } } + else { + LOG.warn("Multiscales metadata not found"); + } if (s.numberOfResolutions == 0) { s.numberOfResolutions = arrayKeys; @@ -1099,17 +1060,13 @@ public void initialize() { createReader(); - if (reader == null && v3Store == null) { + if (reader == null && store == null) { throw new FormatException("Could not create a reader"); } - Map attributes = null; + Attributes attributes = getGroupAttributes(reader); if (isV3()) { - attributes = v3Reader.metadata.attributes; - attributes = (Map) attributes.get("ome"); - } - else { - attributes = reader.getAttributes(); + attributes = attributes.getAttributes("ome"); } Integer layoutVersion = (Integer) attributes.get("bioformats2raw.layout"); if (layoutVersion == null) { @@ -1167,6 +1124,7 @@ else if (imageFile != null && !imageFile.isEmpty()) { } int seriesCount = getSeriesCount(); + LOG.debug("seriesCount = {}", seriesCount); boolean flatHierarchy = false; if (seriesCount < 1) { @@ -1209,12 +1167,7 @@ else if (imageFile != null && !imageFile.isEmpty()) { try { // if we're skipping the series in the hierarchy, // check that there is actually an array at the resolution level - if (isV3()) { - getZarrV3Array("0"); - } - else { - ZarrArray.open(inputDirectory.resolve("0")); - } + getZarrArray("0"); s.path = ""; } catch (IOException e) { @@ -1269,26 +1222,24 @@ else if (imageFile != null && !imageFile.isEmpty()) { s.dimensionLengths[s.dimensionOrder.indexOf("C") - 2] = s.c; // make sure that OME-XML and first resolution array have same dimensions - Map firstResAttrs = null; int[] dims = null; boolean bigEndian = false; PixelType type = null; + Group imgGroup = getZarrGroup(s.path); + Array imgArray = getZarrArray(s.path, "0"); + dims = Utils.toIntArray(imgArray.metadata().shape); + Attributes firstResAttrs = getGroupAttributes(imgGroup); if (isV3()) { - Group v3ImgGroup = getZarrGroupV3(s.path); - firstResAttrs = - (Map) v3ImgGroup.metadata().attributes.get("ome"); - Array v3Array = getZarrV3Array(s.path, "0"); - dims = Utils.toIntArray(v3Array.metadata().shape); - type = getV3PixelType(v3Array.metadata().dataType); + firstResAttrs = firstResAttrs.getAttributes("ome"); + type = getV3PixelType( + ((dev.zarr.zarrjava.v3.Array) imgArray).metadata().dataType()); } else { - ZarrGroup imgGroup = getZarrGroup(s.path); - ZarrArray imgArray = imgGroup.openArray("0"); - dims = imgArray.getShape(); - firstResAttrs = imgGroup.getAttributes(); - bigEndian = imgArray.getByteOrder() == ByteOrder.BIG_ENDIAN; - type = getPixelType(imgArray.getDataType()); + dev.zarr.zarrjava.v2.DataType v2Type = + ((dev.zarr.zarrjava.v2.Array) imgArray).metadata().dataType(); + bigEndian = v2Type.getEndianness() == Endianness.BIG; + type = getPixelType(v2Type); } List> imgMultiscales = @@ -1398,12 +1349,7 @@ else if (rgb) { } s.planeCount *= effectiveChannels; - if (isV3()) { - s.describePyramidV3(v3Store, metadata); - } - else { - s.describePyramid(reader, metadata); - } + s.describePyramid(store, metadata); metadata.setTiffDataIFD(new NonNegativeInteger(totalPlanes), s.index, 0); @@ -2117,24 +2063,19 @@ private void createReader() throws IOException { LOG.debug("attempting to open {}", zarr); if (Files.exists(zarr)) { LOG.debug(" zarr directory exists"); + try { - reader = ZarrGroup.open(zarr.toString()); + store = new FilesystemStore(zarr); + reader = Group.open(store.resolve()); } - catch (IOException e) { - LOG.debug("Could not open as v2", e); - } - - // couldn't open or no attributes implies we should try - // reading as v3 instead of v2 - if (reader == null || reader.getAttributes().size() == 0) { - v3Store = new FilesystemStore(zarr); - v3Reader = Group.open(v3Store.resolve()); + catch (ZarrException e) { + throw new IOException("Could not read " + zarr, e); } } } private boolean isV3() { - return v3Store != null; + return reader instanceof dev.zarr.zarrjava.v3.Group; } /** diff --git a/src/main/java/com/glencoesoftware/pyramid/PyramidSeries.java b/src/main/java/com/glencoesoftware/pyramid/PyramidSeries.java index d11cc1e..5c8b8fc 100644 --- a/src/main/java/com/glencoesoftware/pyramid/PyramidSeries.java +++ b/src/main/java/com/glencoesoftware/pyramid/PyramidSeries.java @@ -18,18 +18,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// Zarr v2 - -import com.bc.zarr.ZarrArray; -import com.bc.zarr.ZarrGroup; - -// Zarr v3 - import dev.zarr.zarrjava.ZarrException; +import dev.zarr.zarrjava.core.Array; +import dev.zarr.zarrjava.core.Attributes; +import dev.zarr.zarrjava.core.Group; import dev.zarr.zarrjava.store.FilesystemStore; import dev.zarr.zarrjava.utils.Utils; -import dev.zarr.zarrjava.v3.Array; -import dev.zarr.zarrjava.v3.Group; public class PyramidSeries { @@ -131,22 +125,33 @@ public int[] getArray(int ti, int ci, int zi, int yi, int xi) { } /** - * Calculate image width and height for each resolution, for v3 input. + * Calculate image width and height for each resolution. * Uses the first tile in the resolution to find the tile size. * - * @param v3 store used to get dataset attributes + * @param store store used to get dataset attributes * @param metadata additional OME-XML metadata */ - public void describePyramidV3(FilesystemStore v3, OMEPyramidStore metadata) + public void describePyramid(FilesystemStore store, OMEPyramidStore metadata) throws FormatException, IOException { LOG.info("Number of resolution levels: {}", numberOfResolutions); - Group subgroup = Group.open(v3.resolve(path)); - Map ome = - (Map) subgroup.metadata.attributes.get("ome"); - List> multiscales = - (List>) ome.get("multiscales"); + List> multiscales = null; + + try { + Group subgroup = Group.open(store.resolve(path)); + Attributes attrs = subgroup.metadata().attributes(); + if (subgroup instanceof dev.zarr.zarrjava.v3.Group) { + Attributes ome = attrs.getAttributes("ome"); + multiscales = (List>) ome.get("multiscales"); + } + else { + multiscales = (List>) attrs.get("multiscales"); + } + } + catch (ZarrException e) { + throw new FormatException(e); + } parseMultiscales(multiscales); @@ -158,7 +163,7 @@ public void describePyramidV3(FilesystemStore v3, OMEPyramidStore metadata) descriptor.path = path + "/" + descriptor.path; } try { - Array array = Array.open(v3.resolve(descriptor.path)); + Array array = Array.open(store.resolve(descriptor.path)); int[] shape = Utils.toIntArray(array.metadata().shape); int[] chunk = array.metadata().chunkShape(); setupResolution(descriptor, resolution, shape, chunk, metadata); @@ -170,35 +175,6 @@ public void describePyramidV3(FilesystemStore v3, OMEPyramidStore metadata) } } - /** - * Calculate image width and height for each resolution, for v2 input. - * Uses the first tile in the resolution to find the tile size. - * - * @param reader reader used to get dataset attributes - * @param metadata additional OME-XML metadata - */ - public void describePyramid(ZarrGroup reader, OMEPyramidStore metadata) - throws FormatException, IOException - { - LOG.info("Number of resolution levels: {}", numberOfResolutions); - - List> multiscales = - (List>) reader.openSubGroup(path).getAttributes().get( - "multiscales"); - parseMultiscales(multiscales); - - resolutions = new ArrayList(); - for (int resolution = 0; resolution < numberOfResolutions; resolution++) { - ResolutionDescriptor descriptor = new ResolutionDescriptor(); - descriptor.path = path + "/" + resolution; - ZarrArray array = reader.openArray(descriptor.path); - int[] dimensions = array.getShape(); - int[] blockSizes = array.getChunks(); - setupResolution(descriptor, resolution, dimensions, blockSizes, metadata); - resolutions.add(descriptor); - } - } - /** * Convenience method that delegates to FormatTools to calculate * the Z, C, and T index for a given plane index. diff --git a/src/test/java/com/glencoesoftware/raw2ometiff/test/ConversionTest.java b/src/test/java/com/glencoesoftware/raw2ometiff/test/ConversionTest.java index 6f3c55a..ee1102a 100644 --- a/src/test/java/com/glencoesoftware/raw2ometiff/test/ConversionTest.java +++ b/src/test/java/com/glencoesoftware/raw2ometiff/test/ConversionTest.java @@ -22,8 +22,6 @@ import java.util.Map; import java.util.stream.Stream; -import com.bc.zarr.ZarrArray; -import com.bc.zarr.ZarrGroup; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.glencoesoftware.bioformats2raw.Converter; @@ -49,9 +47,6 @@ import org.apache.commons.lang3.SystemUtils; -import org.junit.Assert; -import org.junit.Assume; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.Test; @@ -59,6 +54,14 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +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.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + public class ConversionTest { private static final String V2_ARGUMENT = "0.4"; @@ -109,12 +112,12 @@ void assertBioFormats2Raw(String...additionalArgs) throws IOException { converter = new Converter(); CommandLine.call(converter, args.toArray(new String[]{})); if (args.contains(V3_ARGUMENT)) { - Assert.assertTrue(Files.exists(output.resolve("zarr.json"))); + assertTrue(Files.exists(output.resolve("zarr.json"))); } else { - Assert.assertTrue(Files.exists(output.resolve(".zattrs"))); + assertTrue(Files.exists(output.resolve(".zattrs"))); } - Assert.assertTrue(Files.exists( + assertTrue(Files.exists( output.resolve("OME").resolve("METADATA.ome.xml"))); } catch (RuntimeException rt) { @@ -157,25 +160,25 @@ void assertTool(int seriesCount, int fileCount, String...additionalArgs) writer = new PyramidFromDirectoryWriter(); CommandLine.call(writer, args.toArray(new String[]{})); if (fileCount == 0) { - Assert.assertTrue(Files.exists(outputOmeTiff)); + assertTrue(Files.exists(outputOmeTiff)); } else if (seriesCount == fileCount) { String prefix = output.resolve("output").toString(); for (int i=0; i options = new HashMap(); for (int i = 0; i < args.length; i += 2) { options.put(args[i], args[i+1]); @@ -291,18 +294,18 @@ void iteratePixels(ImageReader outputReader) throws Exception { inputReader.setSeries(series); outputReader.setSeries(series); - Assert.assertEquals( + assertEquals( inputReader.getImageCount(), outputReader.getImageCount()); - Assert.assertEquals(inputReader.getSizeC(), outputReader.getSizeC()); + assertEquals(inputReader.getSizeC(), outputReader.getSizeC()); for (int plane=0; plane