@ -1,11 +0,0 @@ | |||
.gitignore | |||
.git | |||
*.md | |||
*.txt | |||
Dockerfile | |||
Makefile | |||
setup.cfg | |||
env | |||
*.egg-info | |||
build | |||
dist |
@ -1,79 +0,0 @@ | |||
## [2.1.1] - 2016-02-15 | |||
### FIXED | |||
- `Image` objects behave properly when passed to numpy functions. Things like `np.mean` will now return scalar values as expected, instead of `Image` objects | |||
## [2.1.0] - 2016-01-16 | |||
### ADDED | |||
- `select` now supports `start` and `stop` keyword arguments to put bounds on images | |||
## [2.0.2] - 2016-01-06 | |||
### ADDED | |||
- `Nd2.pixel_microns` gives the width of a pixel | |||
## [2.0.1] - 2016-01-06 | |||
### FIXED | |||
- Channel name parsing issue | |||
- `select` method works for files with a single frame | |||
## [2.0.0] - 2015-12-20 | |||
### ADDED | |||
- `select()` method to rapidly iterate over a subset of images matching certain criteria | |||
- We parse metadata relating to the physical camera used to produce the images | |||
- Raw metadata can be accessed conveniently, to allow contributors to find more interesting things to add | |||
- An XML parsing library was added since the raw metadata contains some XML blocks | |||
- The version number is now available in the nd2reader module | |||
- Created a DOI to allow citation of the code | |||
### FIXED | |||
- Channel names were not always being parsed properly | |||
### REMOVED | |||
- The `ImageGroup` container object | |||
- The `data` attribute on Images. Images now inherit from ndarray, making this redundant | |||
- The `image_sets` iterator | |||
## [1.1.4] - 2015-10-27 | |||
### FIXED | |||
- Implemented missing get_image_by_attributes method | |||
## [1.1.3] - 2015-10-16 | |||
### FIXED | |||
- ND2s with a single image can now be parsed | |||
## [1.1.2] - 2015-10-09 | |||
### ADDED | |||
- `Image` objects now have a `frame_number` attribute. | |||
- `Nd2` can be used as a context manager | |||
- More unit tests and functional tests | |||
### CHANGED | |||
- `Image` objects now directly subclass Numpy arrays | |||
- Refactored code to permit parsing of different versions of ND2s, which will allow us to add support for NIS Elements 3.x. | |||
### DEPRECATED | |||
- The `data` attribute is no longer needed since `Image` is now a Numpy array | |||
- The `image_sets` iterator will be removed in the near future. You should implement this yourself | |||
## [1.1.1] - 2015-09-02 | |||
### FIXED | |||
- Images returned by indexing would sometimes be skipped when the file contained multiple channels | |||
### CHANGED | |||
- Dockerfile now installs scikit-image to make visual testing possible | |||
## [1.1.0] - 2015-06-03 | |||
### ADDED | |||
- Indexing and slicing of images | |||
- Python 3 support | |||
- Dockerfile support for Python 3.4 | |||
- Makefile commands for convenient testing in Docker | |||
- Unit tests | |||
### CHANGED | |||
- Switched to setuptools to automatically install missing dependencies | |||
- Made the interface for most metadata public | |||
- Refactored some poorly-named things | |||
## [1.0.0] - 2015-05-23 | |||
### ADDED | |||
- First stable release! |
@ -1,44 +0,0 @@ | |||
# This is just for functional testing. We install scikit-image just as a convenient way to view images. Many other | |||
# packages could easily accomplish this. | |||
FROM debian:latest | |||
MAINTAINER Jim Rybarski <jim@rybarski.com> | |||
RUN mkdir -p /var/nds2 | |||
RUN apt-get update && apt-get install -y --no-install-recommends \ | |||
build-essential \ | |||
liblapack-dev \ | |||
libblas-dev \ | |||
libatlas3-base \ | |||
python \ | |||
python3 \ | |||
python-dev \ | |||
python3-dev \ | |||
python-pip \ | |||
python3-pip \ | |||
python-numpy \ | |||
python3-numpy \ | |||
libfreetype6-dev \ | |||
python-matplotlib \ | |||
python3-matplotlib \ | |||
libfreetype6-dev \ | |||
libpng-dev \ | |||
libjpeg-dev \ | |||
pkg-config \ | |||
python-skimage \ | |||
python3-skimage \ | |||
tk \ | |||
tk-dev \ | |||
python-tk \ | |||
python3-tk \ | |||
&& pip install -U \ | |||
cython \ | |||
scikit-image \ | |||
xmltodict \ | |||
&& pip3 install -U \ | |||
cython \ | |||
scikit-image \ | |||
xmltodict \ | |||
&& rm -rf /var/lib/apt/lists/* | |||
WORKDIR /opt/nd2reader |
@ -1,37 +0,0 @@ | |||
.PHONY: info build shell py2 py3 test ftest publish | |||
info: | |||
@echo "" | |||
@echo "Available Make Commands" | |||
@echo "" | |||
@echo "build: builds the image" | |||
@echo "shell: starts a bash shell in the container | |||
@echo "py2: maps ~/Documents/nd2s to /var/nd2s and runs a Python 2.7 interpreter" | |||
@echo "py3: maps ~/Documents/nd2s to /var/nd2s and runs a Python 3.4 interpreter" | |||
@echo "test: runs all unit tests (in Python 3.4)" | |||
@echo "ftest: runs all functional tests (requires specific ND2 files that are not publicly available" | |||
@echo "publish: publishes the code base to PyPI (maintainers only)" | |||
@echo "" | |||
build: | |||
docker build -t jimrybarski/nd2reader . | |||
shell: | |||
xhost local:root; docker run --rm -v $(CURDIR):/opt/nd2reader -v ~/nd2s:/var/nd2s -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$(DISPLAY) -it jimrybarski/nd2reader bash | |||
py2: | |||
xhost local:root; docker run --rm -v $(CURDIR):/opt/nd2reader -v ~/nd2s:/var/nd2s -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$(DISPLAY) -it jimrybarski/nd2reader python2.7 | |||
py3: | |||
xhost local:root; docker run --rm -v $(CURDIR):/opt/nd2reader -v ~/nd2s:/var/nd2s -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$(DISPLAY) -it jimrybarski/nd2reader python3.4 | |||
test: build | |||
docker run --rm -v $(CURDIR):/opt/nd2reader -it jimrybarski/nd2reader python3.4 test.py | |||
docker run --rm -v $(CURDIR):/opt/nd2reader -it jimrybarski/nd2reader python2.7 test.py | |||
ftest: build | |||
xhost local:root; docker run --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$(DISPLAY) -v $(CURDIR):/opt/nd2reader -v ~/nd2s:/var/nd2s -it jimrybarski/nd2reader python3.4 /opt/nd2reader/ftest.py | |||
xhost local:root; docker run --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$(DISPLAY) -v $(CURDIR):/opt/nd2reader -v ~/nd2s:/var/nd2s -it jimrybarski/nd2reader python2.7 /opt/nd2reader/ftest.py | |||
publish: | |||
python setup.py sdist upload -r pypi |
@ -1,7 +0,0 @@ | |||
import unittest | |||
from functional_tests.FYLM141111001 import FYLM141111Tests | |||
from functional_tests.single import SingleTests | |||
from functional_tests.monocycle import Monocycle1Tests, Monocycle2Tests, OneTests | |||
if __name__ == '__main__': | |||
unittest.main() |
@ -1,240 +0,0 @@ | |||
""" | |||
These tests require that you have a specific ND2 file created by the developer of nd2reader. You will never need to | |||
run them unless you're Jim Rybarski. | |||
""" | |||
from nd2reader import Nd2 | |||
from skimage import io | |||
import numpy as np | |||
from datetime import datetime | |||
import unittest | |||
import time | |||
class FYLM141111Tests(unittest.TestCase): | |||
def setUp(self): | |||
self.nd2 = Nd2("/var/nd2s/FYLM-141111-001.nd2") | |||
def tearDown(self): | |||
self.nd2.close() | |||
def test_shape(self): | |||
self.assertEqual(self.nd2.height, 1280) | |||
self.assertEqual(self.nd2.width, 800) | |||
def test_date(self): | |||
self.assertEqual(self.nd2.date, datetime(2014, 11, 11, 15, 59, 19)) | |||
@unittest.skip("This will fail until we address issue #59") | |||
def test_length(self): | |||
self.assertEqual(len(self.nd2), 17808) | |||
def test_frames(self): | |||
self.assertEqual(len(self.nd2.frames), 636) | |||
def test_fovs(self): | |||
self.assertEqual(len(self.nd2.fields_of_view), 8) | |||
def test_channels(self): | |||
self.assertTupleEqual(tuple(sorted(self.nd2.channels)), ('', 'GFP')) | |||
def test_z_levels(self): | |||
self.assertTupleEqual(tuple(self.nd2.z_levels), (0, 1, 2)) | |||
def test_pixel_size(self): | |||
self.assertGreater(self.nd2.pixel_microns, 0.0) | |||
def test_image(self): | |||
image = self.nd2[14] | |||
self.assertEqual(image.field_of_view, 2) | |||
self.assertEqual(image.frame_number, 0) | |||
self.assertAlmostEqual(image.timestamp, 19.0340758) | |||
self.assertEqual(image.channel, '') | |||
self.assertEqual(image.z_level, 1) | |||
self.assertEqual(image.height, self.nd2.height) | |||
self.assertEqual(image.width, self.nd2.width) | |||
def test_last_image(self): | |||
image = self.nd2[30526] | |||
self.assertEqual(image.frame_number, 635) | |||
def test_bad_image(self): | |||
image = self.nd2[13] | |||
self.assertIsNone(image) | |||
def test_iteration(self): | |||
images = [image for image in self.nd2[:10]] | |||
self.assertEqual(len(images), 10) | |||
def test_iteration_step(self): | |||
images = [image for image in self.nd2[:10:2]] | |||
self.assertEqual(len(images), 5) | |||
def test_iteration_backwards(self): | |||
images = [image for image in self.nd2[:10:-1]] | |||
self.assertEqual(len(images), 10) | |||
def test_get_image_by_attribute_ok(self): | |||
image = self.nd2.get_image(4, 0, 'GFP', 1) | |||
self.assertIsNotNone(image) | |||
image = self.nd2.get_image(4, 0, '', 0) | |||
self.assertIsNotNone(image) | |||
image = self.nd2.get_image(4, 0, '', 1) | |||
self.assertIsNotNone(image) | |||
def test_images(self): | |||
self.assertTupleEqual((self.nd2[0].z_level, self.nd2[0].channel), (0, '')) | |||
self.assertIsNone(self.nd2[1]) | |||
self.assertTupleEqual((self.nd2[2].z_level, self.nd2[2].channel), (1, '')) | |||
self.assertTupleEqual((self.nd2[3].z_level, self.nd2[3].channel), (1, 'GFP')) | |||
self.assertTupleEqual((self.nd2[4].z_level, self.nd2[4].channel), (2, '')) | |||
self.assertIsNone(self.nd2[5]) | |||
self.assertTupleEqual((self.nd2[6].z_level, self.nd2[6].channel), (0, '')) | |||
self.assertIsNone(self.nd2[7]) | |||
self.assertTupleEqual((self.nd2[8].z_level, self.nd2[8].channel), (1, '')) | |||
self.assertTupleEqual((self.nd2[9].z_level, self.nd2[9].channel), (1, 'GFP')) | |||
self.assertTupleEqual((self.nd2[10].z_level, self.nd2[10].channel), (2, '')) | |||
self.assertIsNone(self.nd2[11]) | |||
self.assertTupleEqual((self.nd2[12].z_level, self.nd2[12].channel), (0, '')) | |||
self.assertIsNone(self.nd2[13]) | |||
self.assertTupleEqual((self.nd2[14].z_level, self.nd2[14].channel), (1, '')) | |||
self.assertTupleEqual((self.nd2[15].z_level, self.nd2[15].channel), (1, 'GFP')) | |||
self.assertTupleEqual((self.nd2[16].z_level, self.nd2[16].channel), (2, '')) | |||
self.assertIsNone(self.nd2[17]) | |||
self.assertTupleEqual((self.nd2[18].z_level, self.nd2[18].channel), (0, '')) | |||
self.assertIsNone(self.nd2[19]) | |||
self.assertIsNone(self.nd2[47]) | |||
self.assertTupleEqual((self.nd2[48].z_level, self.nd2[48].channel), (0, '')) | |||
self.assertIsNone(self.nd2[49]) | |||
self.assertTupleEqual((self.nd2[50].z_level, self.nd2[50].channel), (1, '')) | |||
self.assertIsNone(self.nd2[51]) | |||
self.assertTupleEqual((self.nd2[52].z_level, self.nd2[52].channel), (2, '')) | |||
self.assertIsNone(self.nd2[53]) | |||
self.assertTupleEqual((self.nd2[54].z_level, self.nd2[54].channel), (0, '')) | |||
def test_get_image_by_attribute_none(self): | |||
# Should handle missing images without an exception | |||
image = self.nd2.get_image(4, 0, 'GFP', 0) | |||
self.assertIsNone(image) | |||
def test_index(self): | |||
# Do indexes get added to images properly? | |||
for n, image in enumerate(self.nd2): | |||
if image is not None: | |||
self.assertEqual(n, image.index) | |||
if n > 50: | |||
break | |||
def test_select(self): | |||
# If we take the first 20 GFP images, they should be identical to the first 20 items iterated from select() | |||
# if we set our criteria to just 'GFP' | |||
manual_images = [] | |||
for _, image in zip(range(20), self.nd2): | |||
if image is not None and image.channel == 'GFP': | |||
manual_images.append(image) | |||
filter_images = [] | |||
for image in self.nd2.select(channels='GFP'): | |||
filter_images.append(image) | |||
if len(filter_images) == len(manual_images): | |||
break | |||
self.assertEqual(len(manual_images), len(filter_images)) | |||
self.assertGreater(len(manual_images), 0) | |||
for a, b in zip(manual_images, filter_images): | |||
self.assertTrue(np.array_equal(a, b)) | |||
self.assertEqual(a.index, b.index) | |||
self.assertEqual(a.field_of_view, b.field_of_view) | |||
self.assertEqual(a.channel, b.channel) | |||
def test_select_order_all(self): | |||
# If we select every possible image using select(), we should just get every image in order | |||
n = 0 | |||
for image in self.nd2.select(channels=['', 'GFP'], z_levels=[0, 1, 2], fields_of_view=list(range(8))): | |||
while True: | |||
indexed_image = self.nd2[n] | |||
if indexed_image is not None: | |||
break | |||
n += 1 | |||
self.assertTrue(np.array_equal(image, indexed_image)) | |||
n += 1 | |||
if n > 100: | |||
break | |||
def test_select_order_subset(self): | |||
# Test that images are always yielded in increasing order. This guarantees that no matter what subset of images | |||
# we're filtering, we still get them in the chronological order they were acquired | |||
n = -1 | |||
for image in self.nd2.select(channels='', z_levels=[0, 1], fields_of_view=[1, 2, 4]): | |||
self.assertGreater(image.index, n) | |||
self.assertEqual(image.channel, '') | |||
self.assertIn(image.field_of_view, (1, 2, 4)) | |||
self.assertIn(image.z_level, (0, 1)) | |||
n = image.index | |||
if n > 100: | |||
break | |||
def test_select_start(self): | |||
count = 0 | |||
for _ in self.nd2.select(channels='GFP', start=29000): | |||
count += 1 | |||
self.assertEqual(127, count) | |||
def test_select_stop(self): | |||
count = 0 | |||
for _ in self.nd2.select(channels='GFP', stop=20): | |||
count += 1 | |||
self.assertEqual(count, 3) | |||
def test_select_start_stop(self): | |||
count = 0 | |||
for _ in self.nd2.select(channels='GFP', start=10, stop=20): | |||
count += 1 | |||
self.assertEqual(count, 1) | |||
def test_select_start_stop_brightfield(self): | |||
count = 0 | |||
for _ in self.nd2.select(channels='', start=10, stop=20): | |||
count += 1 | |||
self.assertEqual(count, 5) | |||
def test_select_faster(self): | |||
select_count = 0 | |||
select_start = time.time() | |||
for i in self.nd2.select(channels='GFP', start=10, stop=50): | |||
if i is not None and i.channel == 'GFP': | |||
select_count += 1 | |||
select_duration = time.time() - select_start | |||
direct_count = 0 | |||
direct_start = time.time() | |||
for i in self.nd2[10:50]: | |||
if i is not None and i.channel == 'GFP': | |||
direct_count += 1 | |||
direct_duration = time.time() - direct_start | |||
self.assertEqual(select_count, direct_count) | |||
self.assertGreater(direct_duration, select_duration) | |||
def test_pixel_microns(self): | |||
self.assertEqual(round(self.nd2.pixel_microns, 2), 0.22) | |||
def test_numpy_operations(self): | |||
# just to make sure we can do this kind of thing and get scalars | |||
self.assertTrue(0 < np.mean(self.nd2[0]) < np.sum(self.nd2[0])) | |||
def test_numpy_mean(self): | |||
# make sure we get the right value and type | |||
expected_mean = 17513.053581054686 | |||
mean = np.mean(self.nd2[0]) | |||
self.assertEqual(type(mean), np.float64) | |||
self.assertAlmostEqual(expected_mean, mean) | |||
def test_subtract_images(self): | |||
# just to prove we can really treat Image like an array | |||
diff = self.nd2[0] - self.nd2[2] | |||
self.assertTrue(np.any(diff)) | |||
def test_show(self): | |||
io.imshow(self.nd2[0]) | |||
io.show() | |||
self.assertTrue(True) |
@ -1,146 +0,0 @@ | |||
""" | |||
Tests on ND2s that have 1 or 2 cycles only. This is unlike the ND2s I work with typically, which are all done over very long periods of time. | |||
""" | |||
from nd2reader import Nd2 | |||
import numpy as np | |||
import unittest | |||
class Monocycle1Tests(unittest.TestCase): | |||
def setUp(self): | |||
self.nd2 = Nd2("/var/nd2s/simone1.nd2") | |||
def tearDown(self): | |||
self.nd2.close() | |||
def test_channels(self): | |||
self.assertListEqual(self.nd2.channels, ['Cy3Narrow', 'TxRed-modified', 'FITC', 'DAPI']) | |||
def test_pixel_size(self): | |||
self.assertGreater(self.nd2.pixel_microns, 0.0) | |||
def test_select(self): | |||
manual_images = [] | |||
for _, image in zip(range(20), self.nd2): | |||
if image is not None and image.channel == 'FITC': | |||
manual_images.append(image) | |||
filter_images = [] | |||
for image in self.nd2.select(channels='FITC'): | |||
filter_images.append(image) | |||
if len(filter_images) == len(manual_images): | |||
break | |||
self.assertEqual(len(manual_images), len(filter_images)) | |||
self.assertGreater(len(manual_images), 0) | |||
for a, b in zip(manual_images, filter_images): | |||
self.assertTrue(np.array_equal(a, b)) | |||
self.assertEqual(a.index, b.index) | |||
self.assertEqual(a.field_of_view, b.field_of_view) | |||
self.assertEqual(a.channel, b.channel) | |||
def test_select_order_all(self): | |||
# If we select every possible image using select(), we should just get every image in order | |||
n = 0 | |||
for image in self.nd2.select(channels=['Cy3Narrow', 'DAPI', 'FITC', 'TxRed-modified'], | |||
z_levels=list(range(35)), | |||
fields_of_view=list(range(5))): | |||
while True: | |||
indexed_image = self.nd2[n] | |||
if indexed_image is not None: | |||
break | |||
n += 1 | |||
self.assertTrue(np.array_equal(image, indexed_image)) | |||
n += 1 | |||
if n > 100: | |||
# Quit after the first hundred images just to save time. | |||
# If there's a problem, we'll have seen it by now. | |||
break | |||
def test_select_order_subset(self): | |||
# Test that images are always yielded in increasing order. This guarantees that no matter what subset of images | |||
# we're filtering, we still get them in the chronological order they were acquired | |||
n = -1 | |||
for image in self.nd2.select(channels='FITC', | |||
z_levels=[0, 1], | |||
fields_of_view=[1, 2, 4]): | |||
self.assertGreater(image.index, n) | |||
self.assertEqual(image.channel, 'FITC') | |||
self.assertIn(image.field_of_view, (1, 2, 4)) | |||
self.assertIn(image.z_level, (0, 1)) | |||
n = image.index | |||
if n > 100: | |||
break | |||
class Monocycle2Tests(unittest.TestCase): | |||
def setUp(self): | |||
self.nd2 = Nd2("/var/nd2s/hawkjo.nd2") | |||
def tearDown(self): | |||
self.nd2.close() | |||
def test_pixel_size(self): | |||
self.assertGreater(round(self.nd2.pixel_microns, 2), 0.26) | |||
def test_select(self): | |||
manual_images = [] | |||
for _, image in zip(range(20), self.nd2): | |||
if image is not None and image.channel == 'HHQ 500 LP 1': | |||
manual_images.append(image) | |||
filter_images = [] | |||
for image in self.nd2.select(channels='HHQ 500 LP 1'): | |||
filter_images.append(image) | |||
if len(filter_images) == len(manual_images): | |||
break | |||
self.assertEqual(len(manual_images), len(filter_images)) | |||
self.assertGreater(len(manual_images), 0) | |||
for a, b in zip(manual_images, filter_images): | |||
self.assertTrue(np.array_equal(a, b)) | |||
self.assertEqual(a.index, b.index) | |||
self.assertEqual(a.field_of_view, b.field_of_view) | |||
self.assertEqual(a.channel, b.channel) | |||
def test_select_order_all(self): | |||
# If we select every possible image using select(), we should just get every image in order | |||
n = 0 | |||
for image in self.nd2.select(channels=['HHQ 500 LP 1', 'HHQ 500 LP 2'], | |||
z_levels=[0], | |||
fields_of_view=list(range(100))): | |||
while True: | |||
indexed_image = self.nd2[n] | |||
if indexed_image is not None: | |||
break | |||
n += 1 | |||
self.assertTrue(np.array_equal(image, indexed_image)) | |||
n += 1 | |||
if n > 100: | |||
# Quit after the first hundred images just to save time. | |||
# If there's a problem, we'll have seen it by now. | |||
break | |||
def test_select_order_subset(self): | |||
# Test that images are always yielded in increasing order. This guarantees that no matter what subset of images | |||
# we're filtering, we still get them in the chronological order they were acquired | |||
n = -1 | |||
for image in self.nd2.select(channels='HHQ 500 LP 2', | |||
z_levels=[0], | |||
fields_of_view=[1, 2, 4]): | |||
self.assertGreater(image.index, n) | |||
self.assertEqual(image.channel, 'HHQ 500 LP 2') | |||
self.assertIn(image.field_of_view, (1, 2, 4)) | |||
self.assertEqual(image.z_level, 0) | |||
n = image.index | |||
if n > 100: | |||
break | |||
class OneTests(unittest.TestCase): | |||
def test_opens(self): | |||
# just testing that this doesn't throw an exception | |||
nd2 = Nd2("/var/nd2s/001.nd2") | |||
self.assertIsNotNone(nd2) | |||
nd2.close() |
@ -1,68 +0,0 @@ | |||
""" | |||
These tests require that you have a specific ND2 file created by the developer of nd2reader. You will never need to | |||
run them unless you're Jim Rybarski. | |||
""" | |||
from nd2reader import Nd2 | |||
from datetime import datetime | |||
import unittest | |||
class SingleTests(unittest.TestCase): | |||
def setUp(self): | |||
self.nd2 = Nd2("/var/nd2s/single.nd2") | |||
def tearDown(self): | |||
self.nd2.close() | |||
def test_shape(self): | |||
self.assertEqual(self.nd2.height, 512) | |||
self.assertEqual(self.nd2.width, 512) | |||
def test_date(self): | |||
self.assertEqual(self.nd2.date, datetime(2015, 10, 15, 9, 33, 5)) | |||
def test_length(self): | |||
self.assertEqual(len(self.nd2), 1) | |||
def test_channels(self): | |||
self.assertEqual(self.nd2.channels, ['Quad Band 2']) | |||
def test_pixel_size(self): | |||
self.assertGreater(self.nd2.pixel_microns, 0.0) | |||
def test_actual_length(self): | |||
count = 0 | |||
for image in self.nd2: | |||
if image is not None: | |||
count += 1 | |||
self.assertEqual(len(self.nd2), count) | |||
def test_frames(self): | |||
self.assertEqual(len(self.nd2.frames), 1) | |||
def test_fovs(self): | |||
self.assertEqual(len(self.nd2.fields_of_view), 1) | |||
def test_z_levels(self): | |||
self.assertTupleEqual(tuple(self.nd2.z_levels), (0,)) | |||
def test_image(self): | |||
image = self.nd2[0] | |||
self.assertIsNotNone(image) | |||
def test_iteration(self): | |||
images = [image for image in self.nd2] | |||
self.assertEqual(len(images), 1) | |||
def test_iteration_step(self): | |||
images = [image for image in self.nd2[::2]] | |||
self.assertEqual(len(images), 1) | |||
def test_iteration_backwards(self): | |||
images = [image for image in self.nd2[::-1]] | |||
self.assertEqual(len(images), 1) | |||
def test_select_bounds_wrong(self): | |||
images = [i for i in self.nd2.select(start=0, stop=12481247)] | |||
self.assertEqual(len(images), 1) |
@ -1,3 +1,3 @@ | |||
from nd2reader.main import Nd2 | |||
from nd2reader.nd2reader import ND2Reader | |||
__version__ = '2.1.3' |
@ -0,0 +1,64 @@ | |||
from pims import FramesSequenceND, Frame | |||
import numpy as np | |||
from nd2reader.exc import NoImageError | |||
from nd2reader.parser import get_parser | |||
from nd2reader.version import get_version | |||
import six | |||
class ND2Reader(FramesSequenceND): | |||
""" | |||
PIMS wrapper for the ND2 reader | |||
""" | |||
def __init__(self, filename): | |||
self.filename = filename | |||
# first use the parser to parse the file | |||
self._fh = open(filename, "rb") | |||
major_version, minor_version = get_version(self._fh) | |||
self._parser = get_parser(self._fh, major_version, minor_version) | |||
self._metadata = self._parser.metadata | |||
self._roi_metadata = self._parser.roi_metadata | |||
# Set data type | |||
bit_depth = self._parser.raw_metadata.image_attributes[six.b('SLxImageAttributes')][six.b('uiBpcInMemory')] | |||
if bit_depth <= 16: | |||
self._dtype = np.float16 | |||
elif bit_depth <= 32: | |||
self._dtype = np.float32 | |||
else: | |||
self._dtype = np.float64 | |||
# Setup the axes | |||
self._init_axis('x', self._metadata.width) | |||
self._init_axis('y', self._metadata.height) | |||
self._init_axis('c', len(self._metadata.channels)) | |||
self._init_axis('t', len(self._metadata.frames)) | |||
self._init_axis('z', len(self._metadata.z_levels)) | |||
def close(self): | |||
if self._fh is not None: | |||
self._fh.close() | |||
def get_frame_2D(self, c, t, z): | |||
""" | |||
Gets a given frame using the parser | |||
:param c: | |||
:param t: | |||
:param z: | |||
:return: | |||
""" | |||
c_name = self._metadata.channels[c] | |||
try: | |||
image = self._parser.driver.get_image_by_attributes(t, 0, c_name, z, self._metadata.width, | |||
self._metadata.height) | |||
except (TypeError, NoImageError): | |||
return Frame([]) | |||
else: | |||
return Frame(image, frame_no=image.frame_number) | |||
@property | |||
def pixel_type(self): | |||
return self._dtype |
@ -1,3 +1,4 @@ | |||
numpy>=1.9.2 | |||
six>=1.4 | |||
xmltodict>=0.9.2 | |||
xmltodict>=0.9.2 | |||
pims>=0.3.0 |