- base & view model for imread
- transforms bugfix - implement (nan)var and (nan)std - add tests
This commit is contained in:
22
.github/workflows/pytest.yml
vendored
Normal file
22
.github/workflows/pytest.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
name: PyTest
|
||||||
|
|
||||||
|
on: [workflow_call, push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
pip install pytest
|
||||||
|
- name: Test with pytest
|
||||||
|
run: pytest
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
[](https://github.com/wimpomp/ndbioimage/actions/workflows/pytest.yml)
|
||||||
|
|
||||||
# ndbioimage - Work in progress
|
# ndbioimage - Work in progress
|
||||||
|
|
||||||
Exposes (bio) images as a numpy ndarray-like-object, but without loading the whole
|
Exposes (bio) images as a numpy ndarray-like-object, but without loading the whole
|
||||||
@@ -55,9 +57,9 @@ with Imread('image_file.tif', axes='cztxy') as im:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Adding more formats
|
## Adding more formats
|
||||||
Readers for image formats subclass Imread. When an image reader is imported, Imread will
|
Readers for image formats subclass AbstractReader. When an image reader is imported, Imread will
|
||||||
automatically recognize it and use it to open the appropriate file format. Image readers
|
automatically recognize it and use it to open the appropriate file format. Image readers
|
||||||
subclass Imread and are required to implement the following methods:
|
are required to implement the following methods:
|
||||||
|
|
||||||
- staticmethod _can_open(path): return True if path can be opened by this reader
|
- staticmethod _can_open(path): return True if path can be opened by this reader
|
||||||
- property ome: reads metadata from file and adds them to an OME object imported
|
- property ome: reads metadata from file and adds them to an OME object imported
|
||||||
@@ -65,7 +67,7 @@ from the ome-types library
|
|||||||
- \_\_frame__(self, c, z, t): return the frame at channel=c, z-slice=z, time=t from the file
|
- \_\_frame__(self, c, z, t): return the frame at channel=c, z-slice=z, time=t from the file
|
||||||
|
|
||||||
Optional methods:
|
Optional methods:
|
||||||
- open(self): maybe open some file
|
- open(self): maybe open some file handle
|
||||||
- close(self): close any file handles
|
- close(self): close any file handles
|
||||||
|
|
||||||
Optional fields:
|
Optional fields:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ import numpy as np
|
|||||||
from abc import ABC
|
from abc import ABC
|
||||||
from multiprocessing import queues
|
from multiprocessing import queues
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
from .. import Imread, JVM
|
from .. import AbstractReader, JVM
|
||||||
|
|
||||||
|
|
||||||
jars = {'bioformats_package.jar':
|
jars = {'bioformats_package.jar':
|
||||||
@@ -174,7 +174,7 @@ def can_open(path):
|
|||||||
jvm.kill_vm()
|
jvm.kill_vm()
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
""" This class is used as a last resort, when we don't have another way to open the file. We don't like it
|
""" This class is used as a last resort, when we don't have another way to open the file. We don't like it
|
||||||
because it requires the java vm.
|
because it requires the java vm.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ from abc import ABC
|
|||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from .. import Imread
|
from .. import AbstractReader
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
priority = 0
|
priority = 0
|
||||||
do_not_pickle = 'reader', 'filedict'
|
do_not_pickle = 'reader', 'filedict'
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ class Reader(Imread, ABC):
|
|||||||
|
|
||||||
instrument = information.find("Instrument")
|
instrument = information.find("Instrument")
|
||||||
for _ in instrument.find("Microscopes"):
|
for _ in instrument.find("Microscopes"):
|
||||||
ome.instruments.append(model.Instrument())
|
ome.instruments.append(model.Instrument(id='Instrument:0'))
|
||||||
|
|
||||||
for detector in instrument.find("Detectors"):
|
for detector in instrument.find("Detectors"):
|
||||||
try:
|
try:
|
||||||
@@ -414,7 +414,7 @@ class Reader(Imread, ABC):
|
|||||||
return ome
|
return ome
|
||||||
|
|
||||||
def __frame__(self, c=0, z=0, t=0):
|
def __frame__(self, c=0, z=0, t=0):
|
||||||
f = np.zeros(self.file_shape[:2], self.dtype)
|
f = np.zeros(self.base.shape['xy'], self.dtype)
|
||||||
directory_entries = self.filedict[c, z, t]
|
directory_entries = self.filedict[c, z, t]
|
||||||
x_min = min([f.start[f.axes.index('X')] for f in directory_entries])
|
x_min = min([f.start[f.axes.index('X')] for f in directory_entries])
|
||||||
y_min = min([f.start[f.axes.index('Y')] for f in directory_entries])
|
y_min = min([f.start[f.axes.index('Y')] for f in directory_entries])
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ from pathlib import Path
|
|||||||
from struct import unpack
|
from struct import unpack
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from .. import Imread
|
from .. import AbstractReader
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
""" Can read some tif files written with Fiji which are broken because Fiji didn't finish writing. """
|
""" Can read some tif files written with Fiji which are broken because Fiji didn't finish writing. """
|
||||||
priority = 90
|
priority = 90
|
||||||
do_not_pickle = 'reader'
|
do_not_pickle = 'reader'
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import numpy as np
|
|||||||
from ome_types import model
|
from ome_types import model
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from .. import Imread
|
from .. import AbstractReader
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
priority = 20
|
priority = 20
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from ome_types._base_type import quantity_property
|
|||||||
from itertools import product
|
from itertools import product
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from .. import Imread
|
from .. import AbstractReader
|
||||||
|
|
||||||
|
|
||||||
def lazy_property(function, field, *arg_fields):
|
def lazy_property(function, field, *arg_fields):
|
||||||
@@ -36,7 +36,7 @@ class Plane(model.Plane):
|
|||||||
return float((datetime.strptime(info["Time"], "%Y-%m-%d %H:%M:%S %z") - t0).seconds)
|
return float((datetime.strptime(info["Time"], "%Y-%m-%d %H:%M:%S %z") - t0).seconds)
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
priority = 10
|
priority = 10
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ from functools import cached_property
|
|||||||
from ome_types import model
|
from ome_types import model
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from .. import Imread
|
from .. import AbstractReader
|
||||||
|
|
||||||
|
|
||||||
class Reader(Imread, ABC):
|
class Reader(AbstractReader, ABC):
|
||||||
priority = 0
|
priority = 0
|
||||||
do_not_pickle = 'reader'
|
do_not_pickle = 'reader'
|
||||||
|
|
||||||
@@ -67,6 +67,6 @@ class Reader(Imread, ABC):
|
|||||||
|
|
||||||
def __frame__(self, c, z, t):
|
def __frame__(self, c, z, t):
|
||||||
if self.p_ndim == 3:
|
if self.p_ndim == 3:
|
||||||
return np.transpose(self.reader.asarray(z + t * self.file_shape[3]), self.p_transpose)[c]
|
return np.transpose(self.reader.asarray(z + t * self.base.shape['z']), self.p_transpose)[c]
|
||||||
else:
|
else:
|
||||||
return self.reader.asarray(c + z * self.file_shape[2] + t * self.file_shape[2] * self.file_shape[3])
|
return self.reader.asarray(c + z * self.base.shape['c'] + t * self.base.shape['c'] * self.base.shape['z'])
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "ndbioimage"
|
name = "ndbioimage"
|
||||||
version = "2023.7.4"
|
version = "2023.8.0"
|
||||||
description = "Bio image reading, metadata and some affine registration."
|
description = "Bio image reading, metadata and some affine registration."
|
||||||
authors = ["W. Pomp <w.pomp@nki.nl>"]
|
authors = ["W. Pomp <w.pomp@nki.nl>"]
|
||||||
license = "GPLv3"
|
license = "GPLv3"
|
||||||
@@ -22,7 +22,7 @@ pint = "*"
|
|||||||
tqdm = "*"
|
tqdm = "*"
|
||||||
lxml = "*"
|
lxml = "*"
|
||||||
pyyaml = "*"
|
pyyaml = "*"
|
||||||
parfor = "*"
|
parfor = ">=2023.8.2"
|
||||||
JPype1 = "*"
|
JPype1 = "*"
|
||||||
SimpleITK-SimpleElastix = "*"
|
SimpleITK-SimpleElastix = "*"
|
||||||
pytest = { version = "*", optional = true }
|
pytest = { version = "*", optional = true }
|
||||||
|
|||||||
BIN
tests/files/YTL1849A111_2023_05_04__14_46_19_cellnr_1_track.tif
Normal file
BIN
tests/files/YTL1849A111_2023_05_04__14_46_19_cellnr_1_track.tif
Normal file
Binary file not shown.
@@ -1,12 +1,25 @@
|
|||||||
|
import pickle
|
||||||
import pytest
|
import pytest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from multiprocessing import active_children
|
||||||
from ndbioimage import Imread, ReaderNotFoundError
|
from ndbioimage import Imread, ReaderNotFoundError
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("file", (Path(__file__).parent / 'files').iterdir())
|
@pytest.mark.parametrize('file', (Path(__file__).parent / 'files').iterdir())
|
||||||
def test_open(file):
|
def test_open(file):
|
||||||
try:
|
try:
|
||||||
with Imread(file) as im:
|
with Imread(file) as im:
|
||||||
print(im[dict(c=0, z=0, t=0)].mean())
|
mean = im[dict(c=0, z=0, t=0)].mean()
|
||||||
|
b = pickle.dumps(im)
|
||||||
|
jm = pickle.loads(b)
|
||||||
|
assert jm[dict(c=0, z=0, t=0)].mean() == mean
|
||||||
|
v = im.view()
|
||||||
|
assert v[dict(c=0, z=0, t=0)].mean() == mean
|
||||||
|
b = pickle.dumps(v)
|
||||||
|
w = pickle.loads(b)
|
||||||
|
assert w[dict(c=0, z=0, t=0)].mean() == mean
|
||||||
except ReaderNotFoundError:
|
except ReaderNotFoundError:
|
||||||
assert len(Imread.__subclasses__()), "No subclasses for Imread found."
|
assert len(Imread.__subclasses__()), 'No subclasses for Imread found.'
|
||||||
|
|
||||||
|
for child in active_children():
|
||||||
|
child.kill()
|
||||||
|
|||||||
18
tests/test_ufuncs.py
Normal file
18
tests/test_ufuncs.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import pytest
|
||||||
|
import numpy as np
|
||||||
|
from ndbioimage import Imread
|
||||||
|
from itertools import product
|
||||||
|
|
||||||
|
|
||||||
|
r = np.random.randint(0, 255, (64, 64, 2, 3, 4))
|
||||||
|
im = Imread(r)
|
||||||
|
a = np.array(im)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('fun_and_axis', product(
|
||||||
|
(np.sum, np.nansum, np.min, np.nanmin, np.max, np.nanmax, np.argmin, np.argmax,
|
||||||
|
np.mean, np.nanmean, np.var, np.nanvar, np.std, np.nanstd), (None, 0, 1, 2, 3, 4)))
|
||||||
|
def test_ufuncs(fun_and_axis):
|
||||||
|
fun, axis = fun_and_axis
|
||||||
|
assert np.all(np.isclose(fun(im, axis), fun(a, axis))), \
|
||||||
|
f'function {fun.__name__} over axis {axis} does not give the correct result'
|
||||||
Reference in New Issue
Block a user