Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

admin/prepare-bioformats_reader-to-work-with-new-bioformats_jar-based-on-scyjava #402

Merged
merged 13 commits into from May 26, 2022
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ optionally installed using `[...]` syntax.
- For multiple additional supported formats: `pip install aicsimageio[base-imageio,nd2]`
- For all additional supported (and openly licensed) formats: `pip install aicsimageio[all]`
- Due to the GPL license, LIF support is not included with the `[all]` extra, and must be installed manually with `pip install aicsimageio readlif>=0.6.4`
- Due to the GPL license, Bio-Formats support is not included with the `[all]` extra, and must be installed manually with `pip install aicsimageio bioformats_jar`
- Due to the GPL license, CZI support is not included with the `[all]` extra, and must be installed manually with `pip install aicsimageio aicspylibczi>=3.0.5`
- Due to the GPL license, Bio-Formats support is not included with the `[all]` extra, and must be installed manually with `pip install aicsimageio bioformats_jar`.

**Important!!** Bio-Formats support also requires a `java` executable in the environment. The simplest method is to install `bioformats_jar` from conda: `conda install -c conda-forge bioformats_jar` (which will additionally bring `openjdk`).
tlambert03 marked this conversation as resolved.
Show resolved Hide resolved

## Documentation

Expand Down
86 changes: 54 additions & 32 deletions aicsimageio/readers/bioformats_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .reader import Reader

if TYPE_CHECKING:
from bioformats_jar import _loci
from fsspec.spec import AbstractFileSystem

from .. import types
Expand All @@ -32,21 +33,27 @@
try:
import jpype
from bioformats_jar import get_loci
except ImportError:
except ImportError as e:
raise ImportError(
"bioformats_jar is required for this reader. "
"Install with `pip install bioformats_jar`"
)
"Install with `pip install bioformats_jar` or `conda install bioformats_jar`"
) from e
Copy link
Collaborator

Choose a reason for hiding this comment

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

💯 love this!

try:
from jgo.jgo import ExecutableNotFound
except ImportError:

class ExecutableNotFound(Exception): # type: ignore
...


class BioformatsReader(Reader):
"""Read files using bioformats.

This reader requires `bioformats_jar` to be installed in the environment, and
requires the java executable to be available on the path, or via the JAVA_HOME
environment variable.
requires the java executable to be available on the path (or via the JAVA_HOME
environment variable), along with the `mvn` executable.

To install java with conda, run `conda install -c conda-forge openjdk`.
To install java and maven with conda, run `conda install -c conda-forge scyjava`.
You may need to deactivate/reactivate your environment after installing. If you
are *still* getting a `JVMNotFoundException`, try setting JAVA_HOME as follows:

Expand Down Expand Up @@ -84,6 +91,7 @@ class BioformatsReader(Reader):
tile_size: Optional[Tuple[int, int]]
Tuple that sets the tile size of y and x axis, respectively
By default, it will use optimal values computed by bioformats itself

Raises
------
exceptions.UnsupportedFileFormatError
Expand Down Expand Up @@ -133,12 +141,12 @@ def __init__(
self._scenes: Tuple[str, ...] = tuple(
str(md.getImageName(i)) for i in range(md.getImageCount())
)
except jpype.JVMNotFoundException:
except RuntimeError:
raise
except Exception:
except Exception as e:
raise exceptions.UnsupportedFileFormatError(
self.__class__.__name__, self._path
)
) from e

@property
def scenes(self) -> Tuple[str, ...]:
Expand Down Expand Up @@ -198,9 +206,7 @@ def _to_xarray(self, delayed: bool = True) -> xr.DataArray:
@staticmethod
def bioformats_version() -> str:
"""The version of the bioformats_package.jar being used."""
from bioformats_jar import get_loci

return get_loci().__version__
return _try_get_loci().__version__


class CoreMeta(NamedTuple):
Expand Down Expand Up @@ -280,20 +286,7 @@ def __init__(
dask_tiles: bool = False,
tile_size: Optional[Tuple[int, int]] = None,
):
try:
loci = get_loci()
except jpype.JVMNotFoundException as e:
raise type(e)(
str(e) + "\n\nBioformatsReader requires a java executable to be "
"available in your environment. If you are using conda, you can "
"install with `conda install -c conda-forge openjdk`.\n\n"
"Note: you may need to reactivate your conda environment after "
"installing opendjk. If you still have this error, try:\n\n"
"# mac and linux:\n"
"export JAVA_HOME=$CONDA_PREFIX\n\n"
"# windows:\n"
"set JAVA_HOME=%CONDA_PREFIX%\\Library"
)
loci = _try_get_loci() # may raise RuntimeError

self._path = str(path)
self._r = loci.formats.ImageReader()
Expand Down Expand Up @@ -533,9 +526,7 @@ def _dask_chunk(self, block_id: Tuple[int, ...]) -> np.ndarray:
@classmethod
def _create_ome_meta(cls) -> Any:
"""create an OMEXMLMetadata object to populate"""
from bioformats_jar import get_loci

loci = get_loci()
loci = _try_get_loci()
if not cls._service:
factory = loci.common.services.ServiceFactory()
cls._service = factory.getInstance(loci.formats.services.OMEXMLService)
Expand All @@ -544,9 +535,7 @@ def _create_ome_meta(cls) -> Any:

def _pixtype2dtype(pixeltype: int, little_endian: bool) -> np.dtype:
"""Convert a loci.formats PixelType integer into a numpy dtype."""
from bioformats_jar import get_loci

FT = get_loci().formats.FormatTools
FT = _try_get_loci().formats.FormatTools
fmt2type: Dict[int, str] = {
FT.INT8: "i1",
FT.UINT8: "u1",
Expand Down Expand Up @@ -617,3 +606,36 @@ def _hide_memoization_warning() -> None:

System = jpype.JPackage("java").lang.System
System.err.close()


MAVEN_ERROR_MSG = """
BioformatsReader requires the maven ('mvn') executable to be
available in your environment. If you are using conda, you can
install with `conda install -c conda-forge scyjava`.

Alternatively, install from https://maven.apache.org/download.cgi
"""

JAVA_ERROR_MSG = """
BioformatsReader requires a java executable to be available
in your environment. If you are using conda, you can install
with `conda install -c conda-forge scyjava`.

Note: you may need to reactivate your conda environment after
installing opendjk. If you still have this error, try:

# mac and linux:
export JAVA_HOME=$CONDA_PREFIX

# windows:
set JAVA_HOME=%CONDA_PREFIX%\\Library
"""


def _try_get_loci() -> _loci.__module_protocol__:
try:
return get_loci()
except ExecutableNotFound as e:
raise RuntimeError(MAVEN_ERROR_MSG) from e
except jpype.JVMNotFoundException as e:
raise RuntimeError(JAVA_ERROR_MSG) from e
Comment on lines +654 to +656
Copy link
Collaborator

Choose a reason for hiding this comment

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

These verbose error messages are fantastic!

13 changes: 11 additions & 2 deletions aicsimageio/tests/readers/extra_readers/test_bioformats_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@

from ...conftest import LOCAL, get_resource_full_path, host

try:
bfj = pytest.importorskip("bioformats_jar")
bf_version = tuple(int(x) for x in str(bfj.__version__).split(".")[:2])
except Exception:
bf_version = (6, 7)

# bioformats changed their DICOM scene labeling scheme at some point
SERIES_0 = "PRIMARY" if bf_version > (6, 7) else "Series 0"


@host
@pytest.mark.parametrize(
Expand Down Expand Up @@ -209,8 +218,8 @@
),
(
"DICOM_samples_MR-MONO2-8-16x-heart.dcm",
"Series 0",
("Series 0",),
SERIES_0,
(SERIES_0,),
(1, 1, 16, 256, 256),
np.uint8,
dimensions.DEFAULT_DIMENSION_ORDER,
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ xfail_strict = true
filterwarnings =
ignore::UserWarning
ignore::FutureWarning
ignore:distutils Version classes are deprecated:
Copy link
Collaborator

Choose a reason for hiding this comment

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

What code is bringing about this warning?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

both xarray and setuptools:

================================================================= warnings summary =================================================================
../../../../miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pycompat.py:22
aicsimageio/tests/readers/extra_readers/test_bioformats_reader.py::test_bioformats_reader[DICOM_samples_MR-MONO2-8-16x-heart.dcm-PRIMARY-expected_scenes16-expected_shape16-uint8-TCZYX-expected_channel_names16-expected_physical_pixel_sizes16-LOCAL]
aicsimageio/tests/readers/extra_readers/test_bioformats_reader.py::test_bioformats_reader[DICOM_samples_MR-MONO2-8-16x-heart.dcm-PRIMARY-expected_scenes16-expected_shape16-uint8-TCZYX-expected_channel_names16-expected_physical_pixel_sizes16-LOCAL]
aicsimageio/tests/readers/extra_readers/test_bioformats_reader.py::test_bioformats_reader[DICOM_samples_MR-MONO2-8-16x-heart.dcm-PRIMARY-expected_scenes16-expected_shape16-uint8-TCZYX-expected_channel_names16-expected_physical_pixel_sizes16-LOCAL]
  /Users/talley/miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pycompat.py:22: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    duck_array_version = LooseVersion(duck_array_module.__version__)

../../../../miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pycompat.py:37
../../../../miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pycompat.py:37
  /Users/talley/miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pycompat.py:37: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    duck_array_version = LooseVersion("0.0.0")

../../../../miniconda3/envs/aics/lib/python3.9/site-packages/setuptools/_distutils/version.py:351
../../../../miniconda3/envs/aics/lib/python3.9/site-packages/setuptools/_distutils/version.py:351
../../../../miniconda3/envs/aics/lib/python3.9/site-packages/setuptools/_distutils/version.py:351
../../../../miniconda3/envs/aics/lib/python3.9/site-packages/setuptools/_distutils/version.py:351
  /Users/talley/miniconda3/envs/aics/lib/python3.9/site-packages/setuptools/_distutils/version.py:351: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    other = LooseVersion(other)

../../../../miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/npcompat.py:82
  /Users/talley/miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/npcompat.py:82: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(np.__version__) >= "1.20.0":

../../../../miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pdcompat.py:45
  /Users/talley/miniconda3/envs/aics/lib/python3.9/site-packages/xarray/core/pdcompat.py:45: DeprecationWarning: distutils Version classes are deprecated. Use packaging.version instead.
    if LooseVersion(pd.__version__) < "0.25.0":

-- Docs: https://docs.pytest.org/en/stable/warnings.html

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh gotcha. I've been getting a lot of those LooseVersion warnings on other projects. Good to know I can borrow this ignoration 😁

addopts = -p no:faulthandler

[flake8]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def run(self):
"wheel>=0.34.2",
# reader deps
*all_formats,
"bioformats_jar", # to test bioformats
"bioformats_jar @ git+ssh://[email protected]/tlambert03/bioformats_jar.git#egg=bioformats_jar", # to test bioformats
"bfio>=2.3.0",
"readlif>=0.6.4", # to test lif
"aicspylibczi>=3.0.5", # to test czi
Expand Down