From 7f1caa6dff604873f520a0fda564f055f1baae4e Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:23:30 +0200 Subject: [PATCH 01/10] remove U. Dundee from footer (#23) --- ngff_spec/pre_build.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ngff_spec/pre_build.py b/ngff_spec/pre_build.py index e26064bf..133e0672 100644 --- a/ngff_spec/pre_build.py +++ b/ngff_spec/pre_build.py @@ -148,8 +148,7 @@ def build_footer(): footer_content = f"""
""" From cb4c128b3945b3c5cba58ff4b395bc7d0e8e8c2f Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:47:17 +0100 Subject: [PATCH 02/10] Add CFF citation support and contribution instructions (#25) * Add CFF citation support and contribution instructions * Delete footer.md * remove author information from supplementary pages I.e., examples, schemas, etc. --- ngff_spec/CITATION.cff | 88 ++++++++++++++++++++++++++++++++++++ ngff_spec/citing.md | 6 ++- ngff_spec/contribute.md | 36 +++++++++++++++ ngff_spec/examples.md | 1 + ngff_spec/myst.yml | 84 ++++++++++++++++++++++++++++++++++ ngff_spec/pre_build.py | 18 ++++++-- ngff_spec/schemas.md | 8 +++- ngff_spec/version_history.md | 4 +- 8 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 ngff_spec/CITATION.cff diff --git a/ngff_spec/CITATION.cff b/ngff_spec/CITATION.cff new file mode 100644 index 00000000..8d8affd6 --- /dev/null +++ b/ngff_spec/CITATION.cff @@ -0,0 +1,88 @@ +cff-version: 1.2.0 +message: Please cite the following works when using this project. +abstract: >- + A growing community is constructing a next-generation file format (NGFF) for + bioimaging to overcome problems of scalability and heterogeneity. Organized by + the Open Microscopy Environment (OME), individuals and institutes across + diverse modalities facing these problems have designed a format specification + process (OME-NGFF) to address these needs. Those community members have built + the format itself – OME-Zarr – along with tools and data resources available + today to increase FAIR access and remove barriers in the scientific process. + The current momentum offers an opportunity to unify a key component of the + bioimaging domain — the file format that underlies so many personal, + institutional, and global data management and analysis tasks. +title: Next-generation file format specification +authors: + - family-names: NGFF-community + given-names: '' +identifiers: + - type: doi + value: 10.1038/s41592-021-01326-w + description: Original Nature Methods article (2021) + - type: doi + value: 10.1101/2023.02.17.528834v2 + description: OME-Zarr preprint (2023) +keywords: + - FAIR + - community + - bioimaging + - data + - cloud + - format +license: CC-BY-4.0 +url: https://ngff.openmicroscopy.org +repository: https://github.com/ome/ngff-spec +preferred-citation: + type: article + doi: 10.1038/s41592-021-01326-w + journal: Nature Methods + month: 11 + start: 1496 + end: 1498 + title: >- + OME-NGFF: a next-generation file format for expanding bioimaging data-access + strategies + issue: 12 + volume: 18 + year: 2021 + authors: + - given-names: Josh + family-names: Moore + orcid: https://orcid.org/0000-0003-4028-811X + - given-names: Chris + family-names: Allan + - given-names: Sébastien + family-names: Besson + orcid: https://orcid.org/0000-0001-8783-1429 + - given-names: Jean-Marie + family-names: Burel + orcid: https://orcid.org/0000-0002-1789-1861 + - given-names: Erin + family-names: Diel + orcid: https://orcid.org/0000-0003-2526-3512 + - given-names: David + family-names: Gault + - given-names: Kevin + family-names: Kozlowski + - given-names: Dominik + family-names: Lindner + orcid: https://orcid.org/0000-0001-8038-1250 + - given-names: Melissa + family-names: Linkert + - given-names: Trevor + family-names: Manz + orcid: https://orcid.org/0000-0001-7694-5164 + - given-names: Will + family-names: Moore + orcid: https://orcid.org/0000-0002-7264-8338 + - given-names: Constantin + family-names: Pape + orcid: https://orcid.org/0000-0001-6562-7187 + - given-names: Christian + family-names: Tischer + orcid: https://orcid.org/0000-0003-4105-1990 + - given-names: Jason R. + family-names: Swedlow + orcid: https://orcid.org/0000-0002-2198-1958 +type: software +repository-code: https://github.com/ome/ngff diff --git a/ngff_spec/citing.md b/ngff_spec/citing.md index 6f801dfa..978817fb 100644 --- a/ngff_spec/citing.md +++ b/ngff_spec/citing.md @@ -1,5 +1,7 @@ -Citing -====== +--- +title: Citing +author: "" +--- (citing)= [Next-generation file format (NGFF) specifications for storing bioimaging data in the cloud.](https://ngff.openmicroscopy.org/0.4) diff --git a/ngff_spec/contribute.md b/ngff_spec/contribute.md index 2b802510..ac9b9042 100644 --- a/ngff_spec/contribute.md +++ b/ngff_spec/contribute.md @@ -1,3 +1,6 @@ +--- +author: "" +--- # Contribution guide Contributions to the spec text, examples and schemas are highly welcome @@ -28,6 +31,39 @@ jupyter book build ngff_spec You'll find the built webpages under `_build/html`. +## First contribution + +If you haven't contributed to the spec before, +please add yourself as an author in the `ngff_spec/myst.yml` metadata file. +This should look like this, for example: + +```yaml + - name: John A. Doe + id: jdoe + orcid: xxxx-xxxx-xxxx-xxxx + github: jdoe + affiliations: + - id: key + institution: ICSLDJ University + city: Doeburg + ror: https://ror.org/.... + - name: Jane Doe + affiliations: key +``` + +For more information see [myst documentation on author formatting](https://mystmd.org/guide/frontmatter#frontmatter-authors). + +When you submit your first PR, +make sure to rebuild the `CITATION.cff` file in the root of this repository. +To do so, run the following command: + +```bash +cd ngff_spec/ && jupyter book build --cff +``` + +Copy the generated `CITATION.cff` file from `ngff_spec/CITATION.cff` to the root of this repository +and submit it along with your PR. + ### Text format Contributions should conform to [Semantic Line Breaks (SemBr)](https://sembr.org/), diff --git a/ngff_spec/examples.md b/ngff_spec/examples.md index 4f25ecbd..94df347f 100644 --- a/ngff_spec/examples.md +++ b/ngff_spec/examples.md @@ -1,6 +1,7 @@ --- title: NGFF metadata JSON Examples short_title: JSON Examples +author: "" --- This section contains JSON examples for various metadata layouts. diff --git a/ngff_spec/myst.yml b/ngff_spec/myst.yml index 9f5d92be..223bc014 100644 --- a/ngff_spec/myst.yml +++ b/ngff_spec/myst.yml @@ -10,6 +10,90 @@ project: - format: pdf template: plain_latex_book output: exports/book.pdf + - format: cff + type: software + identifiers: + - type: doi + value: 10.1038/s41592-021-01326-w + description: Original Nature Methods article (2021) + - type: doi + value: 10.1101/2023.02.17.528834v2 + description: OME-Zarr preprint (2023) + repository-code: 'https://github.com/ome/ngff' + url: 'https://ngff.openmicroscopy.org' + abstract: >- + A growing community is constructing a next-generation file + format (NGFF) for bioimaging to overcome problems of + scalability and heterogeneity. Organized by the Open + Microscopy Environment (OME), individuals and institutes + across diverse modalities facing these problems have + designed a format specification process (OME-NGFF) to + address these needs. Those community members have built + the format itself – OME-Zarr – along with tools and data + resources available today to increase FAIR access and + remove barriers in the scientific process. The current + momentum offers an opportunity to unify a key component of + the bioimaging domain — the file format that underlies so + many personal, institutional, and global data management + and analysis tasks. + keywords: + - FAIR + - community + - bioimaging + - data + - cloud + - format + license: CC-BY-4.0 + preferred-citation: + type: article + doi: "10.1038/s41592-021-01326-w" + journal: "Nature Methods" + month: 11 + start: 1496 + end: 1498 + title: "OME-NGFF: a next-generation file format for expanding bioimaging data-access strategies" + issue: 12 + volume: 18 + year: 2021 + authors: + - given-names: "Josh" + family-names: "Moore" + orcid: "https://orcid.org/0000-0003-4028-811X" + - given-names: "Chris" + family-names: "Allan" + - given-names: "Sébastien" + family-names: "Besson" + orcid: "https://orcid.org/0000-0001-8783-1429" + - given-names: "Jean-Marie" + family-names: "Burel" + orcid: "https://orcid.org/0000-0002-1789-1861" + - given-names: "Erin" + family-names: "Diel" + orcid: "https://orcid.org/0000-0003-2526-3512" + - given-names: "David" + family-names: "Gault" + - given-names: "Kevin" + family-names: "Kozlowski" + - given-names: "Dominik" + family-names: "Lindner" + orcid: "https://orcid.org/0000-0001-8038-1250" + - given-names: "Melissa" + family-names: "Linkert" + - given-names: "Trevor" + family-names: "Manz" + orcid: "https://orcid.org/0000-0001-7694-5164" + - given-names: "Will" + family-names: "Moore" + orcid: "https://orcid.org/0000-0002-7264-8338" + - given-names: "Constantin" + family-names: "Pape" + orcid: "https://orcid.org/0000-0001-6562-7187" + - given-names: "Christian" + family-names: "Tischer" + orcid: "https://orcid.org/0000-0003-4105-1990" + - given-names: "Jason R." + family-names: "Swedlow" + orcid: "https://orcid.org/0000-0002-2198-1958" toc: - file: specification.md - file: examples.md diff --git a/ngff_spec/pre_build.py b/ngff_spec/pre_build.py index 133e0672..f007a641 100644 --- a/ngff_spec/pre_build.py +++ b/ngff_spec/pre_build.py @@ -19,6 +19,7 @@ def build_json_examples(): index_md = """--- title: NGFF metadata JSON Examples short_title: JSON Examples +author: "" --- This section contains JSON examples for various metadata layouts. @@ -31,7 +32,10 @@ def build_json_examples(): index_md += f"\n## {example}\n" # add header - markdown_content = f"""# {example}\n\n + markdown_content = f"""--- +title: {example} Examples +author: "" +--- This document contains JSON examples for {example} metadata layouts. @@ -75,7 +79,11 @@ def build_json_schemas(): os.makedirs(output_directory, exist_ok=True) schema_files = glob.glob(os.path.join(schema_source_dir, '*.schema'), recursive=True) - index_markdown = """# JSON Schemas + index_markdown = """--- +title: NGFF metadata JSON Schemas +short_title: JSON Schemas +author: "" +--- This section contains JSON schemas for various metadata layouts. Find below links to auto-generated markdown pages or interactive HTML pages for each schema. @@ -111,7 +119,11 @@ def build_json_schemas(): with open(output_path_md, 'r') as md_file: md_content = md_file.read() crossref = f"schemas:{Path(schema_file).stem}" - md_content = f"({crossref})=\n\n{md_content}" + md_content = f"""--- +author: "" +--- +({crossref})=\n\n{md_content} +""" with open(output_path_md, 'w') as md_file: md_file.write(md_content) diff --git a/ngff_spec/schemas.md b/ngff_spec/schemas.md index da86063d..1ea2fa16 100644 --- a/ngff_spec/schemas.md +++ b/ngff_spec/schemas.md @@ -1,7 +1,11 @@ -# JSON Schemas +--- +title: NGFF metadata JSON Schemas +short_title: JSON Schemas +author: "" +--- This section contains JSON schemas for various metadata layouts. -Find below links to auto_generated markdown pages or interactive HTML pages for each schema. +Find below links to auto-generated markdown pages or interactive HTML pages for each schema. | Schema | Markdown | HTML | |--------|----------|------| diff --git a/ngff_spec/version_history.md b/ngff_spec/version_history.md index 62de9369..473327a7 100644 --- a/ngff_spec/version_history.md +++ b/ngff_spec/version_history.md @@ -1,5 +1,7 @@ +--- +author: "" +--- # Version History - (history)= All notable changes to this project will be documented in this file. From b55a32e302678d45656c932e0ab30bd54f6a282a Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:05:43 +0100 Subject: [PATCH 03/10] Add RFC5 to version history (#28) --- ngff_spec/version_history.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ngff_spec/version_history.md b/ngff_spec/version_history.md index 473327a7..f96eeed7 100644 --- a/ngff_spec/version_history.md +++ b/ngff_spec/version_history.md @@ -8,6 +8,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.6dev1] - 2025-11-18 + +### Added + +Initial proposal for RFC-5: Transforms. [See proposal text](https://ngff.openmicroscopy.org/rfc/5/versions/1/index.html). + ## [0.5.2] - 2025-01-10 ### Changed From 26b8dc7d7aa759b5763977a23a1265e302e8f9da Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:03:26 +0100 Subject: [PATCH 04/10] Set BSD3 license (#27) --- LICENSE | 27 +++++++++++++++++++++++++++ ngff_spec/myst.yml | 1 + 2 files changed, 28 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..0958bf36 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (C) 2020 Open Microscopy Environment: + - Glencoe Software, Inc. + - University of Dundee + - German BioImaging e.V. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/ngff_spec/myst.yml b/ngff_spec/myst.yml index 223bc014..0430c11a 100644 --- a/ngff_spec/myst.yml +++ b/ngff_spec/myst.yml @@ -1,6 +1,7 @@ version: 1 project: title: Next-generation file format specification + license: BSD-3-Clause authors: - name: NGFF-community github: ome/ngff-spec From e3d2f8ffbbcfb0e0906e901ec572f5c49b36328d Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:04:29 +0100 Subject: [PATCH 05/10] Revert "Set BSD3 license (#27)" (#30) This reverts commit 26b8dc7d7aa759b5763977a23a1265e302e8f9da. --- LICENSE | 27 --------------------------- ngff_spec/myst.yml | 1 - 2 files changed, 28 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 0958bf36..00000000 --- a/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (C) 2020 Open Microscopy Environment: - - Glencoe Software, Inc. - - University of Dundee - - German BioImaging e.V. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/ngff_spec/myst.yml b/ngff_spec/myst.yml index 0430c11a..223bc014 100644 --- a/ngff_spec/myst.yml +++ b/ngff_spec/myst.yml @@ -1,7 +1,6 @@ version: 1 project: title: Next-generation file format specification - license: BSD-3-Clause authors: - name: NGFF-community github: ome/ngff-spec From f7ed63b72c733f8d94a610976096e63ae3b9c2a6 Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:01:28 +0100 Subject: [PATCH 06/10] Set local_scheme to no-local-version in setuptools_scm (#32) --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index f9e7d114..9f9e03b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ testing = [ [tool.setuptools_scm] write_to = "ngff_spec/_version.py" fallback_version = "0.0.1+nogit" +local_scheme = "no-local-version" [build-system] requires = ["setuptools >= 77.0.3", "setuptools-scm"] From 58873cc71af9cf752a7a0d0b7e2da996908eef23 Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:32:58 +0100 Subject: [PATCH 07/10] Hardcode version (#33) * Set local_scheme to no-local-version in setuptools_scm * hardcode version and use hatch for build * track `_version.py` --- .gitignore | 4 ---- ngff_spec/_version.py | 1 + pyproject.toml | 14 +++++++------- 3 files changed, 8 insertions(+), 11 deletions(-) create mode 100644 ngff_spec/_version.py diff --git a/.gitignore b/.gitignore index f97a7680..4c0f08bd 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,6 @@ var/ *.egg-info/ .installed.cfg *.egg -*_version.py # PyInstaller # Usually these files are written by a python script from a template @@ -88,9 +87,6 @@ target/ # OS .DS_Store -# written by setuptools_scm -*/_version.py - # PyBuilder .idea venv/ diff --git a/ngff_spec/_version.py b/ngff_spec/_version.py new file mode 100644 index 00000000..658ac144 --- /dev/null +++ b/ngff_spec/_version.py @@ -0,0 +1 @@ +__version__ = '0.6.dev1' diff --git a/pyproject.toml b/pyproject.toml index 9f9e03b1..b0003918 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,6 @@ name = "ngff-spec" dynamic = ["version"] description = "Next-generation file format specification" -license = {file = "LICENSE"} requires-python = ">=3.10" dependencies = [ @@ -19,11 +18,12 @@ testing = [ "pytest", # https://docs.pytest.org/en/latest/contents.html ] -[tool.setuptools_scm] -write_to = "ngff_spec/_version.py" -fallback_version = "0.0.1+nogit" -local_scheme = "no-local-version" +[tool.hatch.version] +path = "ngff_spec/_version.py" + +[tool.hatch.build.targets.wheel] +packages = ["ngff_spec"] [build-system] -requires = ["setuptools >= 77.0.3", "setuptools-scm"] -build-backend = "setuptools.build_meta" \ No newline at end of file +requires = ["hatchling>=1.8.0,<1.21.1"] +build-backend = "hatchling.build" \ No newline at end of file From ed1c56cf50bea837df75695ecf6a7b62639ad3c1 Mon Sep 17 00:00:00 2001 From: Johannes Soltwedel <38459088+jo-mueller@users.noreply.github.com> Date: Tue, 2 Dec 2025 12:18:23 +0100 Subject: [PATCH 08/10] RFC5: coordinate systems and transformations (#17) * upgrade to Jupyter book 2 * correctly mark examples * bump mystmd dependency * pin mystmd * hashpin template * remove legacy dependencies * Create README.md for OME-NGFF specification * remove legacy files * use auto-built footer for copyright * Update `coordinateSystems` metadata * Updated `axes` metadata and moved under `coordinateSystems` header level * Update array coordinate systems metadata and merge existing examples * Update coordinate convention metadata * Updated coordinate transformations metadata * updated matrix transformations * update transformation types * update transformation types metadata * harmonize link syntax * removed deprecated statement about `byDimension` transform * harmonized indendations and json style * fixed `byDimension` metadata * make examples collapsible * renamed example folder * add orcid logo to editor info * fix reference * change precedence See discussion [here](https://github.com/bogovicj/ngff-rfc5-coordinate-transformation-examples/issues/11#issuecomment-3431688131) * added examples for transformations with discrete axes * fix link to example * untrack autogenerated files * update affine examples and fix variable ordering * Fixed scale examples * removed example from additional details * specify affines/rotations as 2D matrices in parameter table * renamed coordinate transformations schema * Refactor rotation property to use `mtxFlatOrNested` * Delete schemas.md * remove `mtxFloatOrNested` affine/rotation matrices should always be 2D * "zarr array" instead of "binary data" * WIP: Update rfc5 schemas (#1) I'm merging this so that all necessary changes regarding the addition of rfc5-stuff to the main branch of the ngff-spec repo can be in one place. This branch was supposed to be a break-out to keep the commit-history of https://github.com/ome/ngff-spec/pull/17 clean. * Update input/output to input_axes/output_axes in schema * Add description and required field to byDimension * Update mapAxis schema to use integer array * Add path and interpolation to displacements schema The displacements object now includes a required 'path' property for specifying the zarr array location and an 'interpolation' property with supported methods. This enhances the schema's ability to describe displacement fields and their application. * Add path and interpolation to coordinates schema Introduces 'path' and 'interpolation' properties to the 'coordinates' object in the schema, specifying the location of the coordinate field and the interpolation method to use. The 'path' property is now required. * only allow paths, no URLs * move required fields to correct places * name musnt't be empty * added axis types to schema * Added descriptions to schemas * updated versions to 0.6dev2 * fix paths and versions * pull schema from correct location * Added action to run the tests * update config reference * update versions in test suite * Revert "update config reference" This reverts commit ff3fedd4143d888ad5f107969042851ea86dc271. * update all version references to "0.6dev2" * update version reference * at least two spatial axes * allow any axis type * update version reference * Add maxItems constraint to axes schema Set a maximum of 5 items for the axes array in the schema to enforce limits on the number of axes allowed. * Refactor image schema for coordinate transformations Refactors the definition of coordinateTransformations and coordinateSystems to use inline array schemas with stricter constraints. Adds a new multiscale_coordinateTransformations definition to support scale and translate transformations for multiscale datasets. * Update to NGFF 0.6dev2 and refactor coordinate systems Updated example and test JSON files to use the NGFF 0.6dev2 specification. Replaced 'axes' with 'coordinateSystems', added explicit 'input' and 'output' fields to coordinateTransformations, and restructured transformation chains for clarity and compliance with the new spec. * fix schema resolution * sync PR with latest RFC5 proposal (#2) Co-authored-by: Will Moore <900055+will-moore@users.noreply.github.com> Co-authored-by: David Stansby| `identity` - | - | The identity transformation is the default transformation and is typically not explicitly defined. - | |||
|---|---|---|---|---|---|
| `mapAxis` - | `"mapAxis":Dict[String:String]` - | A `maxAxis` transformation specifies an axis permutation as a map between axis names. - | |||
| `translation` - | one of: `"translation":List[number]`, `"path":str` - | translation vector, stored either as a list of numbers (`"translation"`) or as binary data at a location - in this container (`path`). - | |||
| `scale` - | one of: `"scale":List[number]`, `"path":str` - | scale vector, stored either as a list of numbers (`scale`) or as binary data at a location in this - container (`path`). - | |||
| `affine` - | one of: `"affine":List[List[number]]`, `"path":str` - | affine transformation matrix stored as a flat array stored either with json uing the affine field - or as binary data at a location in this container (path). If both are present, the binary values at path should be used. - | |||
| `rotation` - | one of: `"rotation":List[number]`, `"path":str` - | rotation transformation matrix stored as an array stored either - with json or as binary data at a location in this container (path). - If both are present, the binary parameters at path are used. - | |||
| `sequence` - | `"transformations":List[Transformation]` - | A sequence of transformations, Applying the sequence applies the composition of all transforms in the list, in order. - | |||
| `displacements` - | `"path":str` `"interpolation":str` - | Displacement field transformation located at (path). - | |||
| `coordinates` - | `"path":str` `"interpolation":str` - | Coordinate field transformation located at (path). - | |||
| `inverseOf` - | `"transform":Transform` - | The inverse of a transformation. Useful if a transform is not closed-form invertible. See Forward and inverse for details and examples. - | |||
| `bijection` - | `"forward":Transform` `"inverse":Transform` - | Explicitly define an invertible transformation by providing a forward transformation and its inverse. - | |||
| `byDimension` - | `"transformations":List[Transformation]` - | Define a high dimensional transformation using lower dimensional transformations on subsets of
- dimensions.
-
- | type | fields | description
- | |
store.zarr # Root folder of the zarr store │ ├── zarr.json # coordinate transformations describing the relationship between two image coordinate systems │ # are stored in the attributes of their parent group. -│ # transformations between 'volume' and 'crop' coordinate systems are stored here. +│ # transformations between coordinate systems in the 'volume' and 'crop' multiscale images are stored here. │ -├── coordinateTransformations # transformations that use array storage go in a "coordinateTransformations" zarr group. +├── coordinateTransformations # transformations that use array storage for their parameters should go in a zarr group named "coordinateTransformations". │ └── displacements # for example, a zarr array containing a displacement field │ └── zarr.json │ ├── volume -│ ├── zarr.json # group level attributes (multiscales) -│ └── 0 # a group containing the 0th scale -│ └── image # a zarr array -│ └── zarr.json # physical coordinate system and transformations here -│ # the array attributes +│ ├── zarr.json # group level attributes (multiscales) +│ └── 0 # a group containing the 0th scale +│ └── image # a zarr array +│ └── zarr.json # physical coordinate system and transformations here └── crop - ├── .zattrs # group level attributes (multiscales) - └── 0 # a group containing the 0th scale - └── image # a zarr array - └── zarr.json # physical coordinate system and transformations here - # the array attributes -``` - -### Additional details - -Most coordinate transformations MUST specify their input and output coordinate systems -using `input` and `output` with a string value corresponding to the name of a coordinate system. -The coordinate system's name may be the path to an array, and therefore may not appear in the list of coordinate systems. + ├── zarr.json # group level attributes (multiscales) + └── 0 # a group containing the 0th scale + └── image # a zarr array + └── zarr.json # physical coordinate system and transformations here ++ +::::{admonition} Example +:class: dropdown +(spec:example:coordinate_transformation)= +Two instruments simultaneously image the same sample from two different angles, +and the 3D data from both instruments are calibrated to "micrometer" units. +An analysis of sample A requires measurements from images taken from both instruments at certain points in space. +Suppose a region of interest (ROI) is determined from the image obtained from instrument 2, +but quantification from that region is needed for instrument 1. +Since measurements were collected at different angles, +a measurement by instrument 1 at the point with image array coordinates (x,y,z) +may not correspond to the measurement at the same array coordinates in instrument 2 +(i.e., it may not be the same physical location in the sample). +To analyze both images together, they must be transformed to a common coordinate system. -Exceptions are if the the coordinate transformation appears in the `transformations` list of a `sequence` or is the `transformation` of an `inverseOf` transformation. -In these two cases input and output SHOULD be omitted -(see below for details). +The set of coordinate transformations encodes relationships between coordinate systems, +specifically, how to convert points from one coordinate system to another. +Implementations can apply the coordinate transform to images or points +in coordinate system "sampleA_instrument2" to bring them into the "sampleA_instrument1" coordinate system. +In this case, image data within the ROI defined in image2 should be transformed to the "sampleA_image1" coordinate system, +then used for quantification with the instrument 1 image. -Transformations in the `transformations` list of a `byDimensions` transformation MUST provide `input` and `output` -as arrays of strings corresponding to axis names of the parent transformation's input and output coordinate systems -(see below for details). +The `coordinateTransformations` in the parent-level metadata would contain the following data. +The transformation parameters are stored in a separate zarr-group +under `coordinateTransformations/sampleA_instrument2-to-instrument1` as shown above. -````{admonition} Example +```json +"coordinateTransformations": [ + { + "type": "affine", + "path": "coordinateTransformations/sampleA_instrument2-to-instrument1", + "input": "sampleA_instrument2", + "output": "sampleA_instrument1" + } +] +``` -The sequence transformation's input corresponds to an array coordinate system at path "my/array". +And the image at the path `sampleA_instrument1` would have the following as the first coordinate system: ```json -"coordinateSystems" : [ - { "name" : "in", "axes" : [{"name" : "j"}, {"name":"i"}] }, - { "name" : "outScale", "axes" : [{"name" : "y"}, {"name":"x"}] }, - { "name" : "outSeq", "axes" : [{"name" : "y"}, {"name":"x"}] }, - { "name" : "outInv", "axes" : [{"name" : "y"}, {"name":"x"}] }, - { "name" : "outByDim", "axes" : [{"name" : "y"}, {"name":"x"}] } -], -"coordinateTransformations" : [ - { - "type": "scale", - "input" : "in", - "output" : "outScale", - "scale" : [ 0.5, 1.2 ] - }, +"coordinateSystems": [ { - "type" : "sequence", - "input" : "my/array", - "output" : "outSeq", - "transformations" : [ - { "type": "scale", "scale" : [ 0.5, 0.6 ] }, - { "type": "translation", "translation" : [ 2, 5 ] } + "name": "sampleA-instrument1", + "axes": [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} ] }, +] +``` + +The image at path `sampleA_instrument2` would have this as the first listed coordinate system: + +```json +[ { - "type": "inverseOf", - "input" : "in", - "output" : "outInv", - "transformation" : { - "type": "displacements", - "path": "path/to/displacements" - } - }, - { - "type": "byDimension", - "input" : "in", - "output" : "outDim", - "transformations" : [ - { "type" : "translation", "translation" : [1], "input" : ["i"], "output" : ["x"]}, - { "type" : "scale", "scale" : [2.0], "input" : ["j"], "output" : ["y"]} + "name": "sampleA-instrument2", + "axes": [ + {"name": "z", "type": "space", "unit": "micrometer"}, + {"name": "y", "type": "space", "unit": "micrometer"}, + {"name": "x", "type": "space", "unit": "micrometer"} ] } -] +], ``` +:::: -```` +#### Additional details + +Most coordinate transformations MUST specify their input and output coordinate systems +using `input` and `output` with a string value +that MUST correspond to the name of a coordinate system or the path to a multiscales group. +Exceptions are if the coordinate transformation is wrapped in another transformation, +e.g. as part of a `transformations` list of a `sequence` or +as `transformation` of an `inverseOf` transformation. +In these two cases input and output could, in some cases, be omitted (see below for details). +If unused, the `input` and `output` fields MAY be null. + +If used in a parent-level zarr-group, the `input` and `output` fields +can be the name of a `coordinateSystem` in the same parent-level group or the path to a multiscale image group. +If either `input` or `output` is a path to a multiscale image group, +the authoritative coordinate system for the respective image is the first `coordinateSystem` defined therein. +If the names of `input` or `output` correspond to both an existing path to a multiscale image group +and the name of a `coordinateSystem` defined in the same metadata document, +the `coordinateSystem` MUST take precedent. + +For usage in multiscales, see [the multiscales section](#multiscales-md) for details. Coordinate transformations are functions of *points* in the input space to *points* in the output space. We call this the "forward" direction. -Points are ordered lists of coordinates, where a coordinate is the location/value of that point along its corresponding axis. +Points are ordered lists of coordinates, +where a coordinate is the location/value of that point along its corresponding axis. The indexes of axis dimensions correspond to indexes into transformation parameter arrays. -For example, the scale transformation above defines the function: - -``` -x = 0.5 * i -y = 1.2 * j -``` - -i.e., the mapping from the first input axis to the first output axis is determined by the first scale parameter. When rendering transformed images and interpolating, -implementations may need the "inverse" transformation - from the output to the input coordinate system. -Inverse transformations will not be explicitly specified when they can be computed in closed form from the forward transformation. -Inverse transformations used for image rendering may be specified using the `inverseOf` transformation type, for example: +implementations may need the "inverse" transformation - +from the output to the input coordinate system. +Inverse transformations will not be explicitly specified +when they can be computed in closed form from the forward transformation. +Inverse transformations used for image rendering may be specified using +the `inverseOf` transformation type, for example: ```json { "type": "inverseOf", "transformation" : { "type": "displacements", - "path": "path/to/displacements", + "path": "/path/to/displacements", }, "input": "input_image", - "output": "output_image" + "output": "output_image", } ``` -Implementations SHOULD be able to compute and apply the inverse of some coordinate transformations when they are computable in closed-form -(as the [Transformation types](#trafo-types-md) section below indicates). -If an operation is requested that requires the inverse of a transformation that can not be inverted in closed-form, -implementations MAY estimate an inverse, or MAY output a warning that the requested operation is unsupported. +Implementations SHOULD be able to compute and apply +the inverse of some coordinate transformations when they are computable +in closed-form (as the [Transformation types](#trafo-types-md) section below indicates). +If an operation is requested that requires +the inverse of a transformation that can not be inverted in closed-form, +implementations MAY estimate an inverse, +or MAY output a warning that the requested operation is unsupported. #### Matrix transformations (matrix-trafo-md)= Two transformation types ([affine](#affine-md) and [rotation](#rotation-md)) are parametrized by matrices. Matrices are applied to column vectors that represent points in the input coordinate system. -The first (last) axis in a coordinate system is the top (bottom) entry in the column vector. +The first and last axes in a coordinate system correspond to the top and bottom entries in the column vector, respectively. Matrices are stored as two-dimensional arrays, either as json or in a zarr array. When stored as a 2D zarr array, the first dimension indexes rows and the second dimension indexes columns (e.g., an array of `"shape":[3,4]` has 3 rows and 4 columns). -When stored as a 2D json array, the inner array contains rows -(e.g. `[[1,2,3], [4,5,6]]` has 2 rows and 3 columns). +When stored as a 2D json array, the inner array contains rows (e.g. `[[1,2,3], [4,5,6]]` has 2 rows and 3 columns). -````{admonition} Example +::::{admonition} Example +:class: dropdown For matrix transformations, points in the coordinate system: @@ -733,25 +673,30 @@ because it is computed with the matrix-vector multiplication: [ 0 0 -1] [3] [-3] ``` -```` +:::: ### Transformation types (trafo-types-md)= -Input and output dimensionality may be determined by the value of the "input" and "output" fields, respectively. -If the value of "input" is an array, its shape gives the input dimension, -otherwise it is given by the length of "axes" for the coordinate system with the name of the "input". -If the value of "output" is an array, its shape gives the output dimension, -otherwise it is given by the length of "axes" for the coordinate system with the name of the "output". +Input and output dimensionality may be determined by the coordinate system referred to by the `input` and `output` fields, respectively. +If the value of `input` is a path to an array, its shape gives the input dimension, +otherwise it is given by the length of `axes` for the coordinate system with the name of the `input`. +If the value of `output` is an array, its shape gives the output dimension, +otherwise it is given by the length of `axes` for the coordinate system with the name of the `output`. #### identity (identity-md)= `identity` transformations map input coordinates to output coordinates without modification. -The position of the ith axis of the output coordinate system is set to the position of the ith axis of the input coordinate system. +The position of the i-th axis of the output coordinate system +is set to the position of the ith axis of the input coordinate system. `identity` transformations are invertible. -````{admonition} Example +The `input` and `output` fields MAY be omitted if wrapped in another transformation that provides `input`/`output` +(e.g., [`sequence`](#sequence-md), [`inverseOf`](#inverseof-md), ['byDimension](#bydimension-md) or [`bijection`](#bijection-md)). + +::::{admonition} Example +:class: dropdown ```{literalinclude} examples/transformations/identity.json :language: json @@ -764,19 +709,27 @@ x = i y = j ``` -```` +:::: #### mapAxis (mapAxis-md)= -`mapAxis` transformations describe axis permutations as a mapping of axis names. -Transformations MUST include a `mapAxis` field whose value is an object, all of whose values are strings. -If the object contains `"x":"i"`, then the transform sets the value of the output coordinate for axis "x" to the value of the coordinate of input axis "i" (think `x = i`). -For every axis in its output coordinate system, the `mapAxis` MUST have a corresponding field. -For every value of the object there MUST be an axis of the input coordinate system with that name. -Note that the order of the keys could be reversed. +`mapAxis` transformations describe axis permutations as a transpose vector of integers. +Transformations MUST include a `mapAxis` field +whose value is an array of integers that specifies the new ordering in terms of indices of the old order. +The length of the array MUST equal the number of dimensions in both the input and output coordinate systems. +Each integer in the array MUST be a valid zero-based index into the input coordinate system's axes +(i.e., between 0 and N-1 for an N-dimensional input). +Each index MUST appear exactly once in the array. +The value at position `i` in the array indicates which input axis becomes the `i`-th output axis. +`mapAxis` transforms are invertible. -````{admonition} Example +The `input` and `output` fields MAY be omitted if wrapped in another transformation that provides `input`/`output` +(e.g., [`sequence`](#sequence-md), [`inverseOf`](#inverseof-md), ['byDimension](#bydimension-md) or [`bijection`](#bijection-md)). + + +::::{admonition} Example 1 +:class: dropdown ```{literalinclude} examples/transformations/mapAxis1.json :language: json @@ -796,9 +749,10 @@ x = j y = i ``` -```` +:::: -````{admonition} Example +::::{admonition} Example 2 +:class: dropdown ```{literalinclude} examples/transformations/mapAxis2.json :language: json @@ -817,25 +771,30 @@ x = a y = b z = b ``` -```` +:::: #### translation (translation-md)= `translation` transformations are special cases of affine transformations. When possible, a translation transformation should be preferred to its equivalent affine. -Input and output dimensionality MUST be identical and MUST equal the the length of the "translation" array (N). +Input and output dimensionality MUST be identical +and MUST equal the the length of the "translation" array (N). `translation` transformations are invertible. -
interpolation attributes MAY be provided.
+ Its value indicates the interpolation to use
+ if transforming points not on the array's discrete grid.
Values could be:
linear (default)input_axes and output_axes fields
+ whose values are arrays of strings.
+ Every axis name in a child transformation's input_axes
+ MUST correspond to a name of some axis in this parent object's input coordinate system.
+ Every axis name in the parent byDimension's output coordinate system
+ MUST appear in exactly one child transformation's output_axes array.
+ Each child transformation's input_axes and output_axes arrays
+ MUST have the same length as that transformation's parameter arrays.