17 Commits

Author SHA1 Message Date
  William Niu 078bba08a2 Update stitched.py 2 years ago
  Ruben Verweij 80ae99091b Update README.md 3 years ago
  Ruben Verweij 7f89b4a7cf Delete .codeclimate.yml 3 years ago
  Ruben Verweij b06b6292a2 Delete code-climate.yml 3 years ago
  Ruben Verweij c0cfed1c01 Rename build script & try to add code climate coverage 3 years ago
  Ruben Verweij e05e3f959e Remove travis, migrate to github actions 3 years ago
  Ruben Verweij e6cc0ada0b Update README.md 3 years ago
  Ruben Verweij 517ee41e8e Update python-package.yml 3 years ago
  Ruben Verweij d99cfb382d Create python-package.yml 3 years ago
  Ruben Verweij 794e795c42 Version 3.3.0 3 years ago
  Ruben Verweij ac6c4a39e4 Install package on travis to correctly get version number 3 years ago
  Ruben Verweij 270e42e5bb Test importlib import on travis 3 years ago
  Ruben Verweij ee87f14bcc Test importlib import on travis 3 years ago
  Ruben Verweij 38a89daa34 Update release.txt 3 years ago
  Ruben Verweij 406e69ee8c Update dependencies 3 years ago
  Ruben Verweij a58d8d75de Fix #42: read version number from setup.py 3 years ago
  Ruben Verweij 19d296598a Fixes issue #50 3 years ago
14 changed files with 217 additions and 204 deletions
Split View
  1. +0
    -15
      .codeclimate.yml
  2. +40
    -0
      .github/workflows/python-package.yml
  3. +1
    -1
      .gitmodules
  4. +0
    -33
      .travis.yml
  5. +1
    -1
      MANIFEST.in
  6. +1
    -3
      README.md
  7. +1
    -1
      docs
  8. +9
    -1
      nd2reader/__init__.py
  9. +9
    -25
      nd2reader/stitched.py
  10. +1
    -1
      release.txt
  11. +2
    -2
      requirements.txt
  12. +4
    -2
      setup.py
  13. +0
    -119
      sphinx/tutorial.md
  14. +148
    -0
      sphinx/tutorial.rst

+ 0
- 15
.codeclimate.yml View File

@ -1,15 +0,0 @@
engines:
duplication:
enabled: true
config:
languages:
- python
fixme:
enabled: true
radon:
enabled: true
ratings:
paths:
- "**.py"
exclude_paths:
- tests/

+ 40
- 0
.github/workflows/python-package.yml View File

@ -0,0 +1,40 @@
name: Build
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Install as package
run: |
python setup.py develop
- name: Test with pytest
run: |
pytest

+ 1
- 1
.gitmodules View File

@ -1,4 +1,4 @@
[submodule "docs"]
path = docs
url = https://github.com/rbnvrw/nd2reader.git
url = git@github.com:rbnvrw/nd2reader.git
branch = gh-pages

+ 0
- 33
.travis.yml View File

@ -1,33 +0,0 @@
env:
global:
- CC_TEST_REPORTER_ID=8582900c285e4da0f253555b1bac1ba501bd6ff07850b0f227166b3cdac59ecc
language: python
git:
depth: 3
notifications:
email: false
python:
- 3.5
- 3.6
- 3.7
- 3.8
install:
- pip install --upgrade pip setuptools wheel
- pip install --only-binary=numpy numpy
- pip install -r requirements.txt
- pip install 'coverage>=4.0,<4.4' --force-reinstall # Upstream bug: https://github.com/nedbat/coveragepy/issues/578
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script: python ./test.py
after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT

+ 1
- 1
MANIFEST.in View File

@ -1,6 +1,6 @@
include MANIFEST.in
include VERSION
include setup.py
include setup.cfg
include README.md
include LICENSE
include COPYING

+ 1
- 3
README.md View File

@ -2,9 +2,7 @@
[![Anaconda-Server Badge](https://anaconda.org/conda-forge/nd2reader/badges/version.svg)](https://anaconda.org/conda-forge/nd2reader)
[![Anaconda-Server Badge](https://anaconda.org/conda-forge/nd2reader/badges/downloads.svg)](https://anaconda.org/conda-forge/nd2reader)
[![Build Status](https://travis-ci.org/rbnvrw/nd2reader.svg?branch=master)](https://travis-ci.org/rbnvrw/nd2reader)
[![Test Coverage](https://codeclimate.com/github/rbnvrw/nd2reader/badges/coverage.svg)](https://codeclimate.com/github/rbnvrw/nd2reader/coverage)
[![Code Climate](https://codeclimate.com/github/rbnvrw/nd2reader/badges/gpa.svg)](https://codeclimate.com/github/rbnvrw/nd2reader)
[![Build status](https://github.com/rbnvrw/nd2reader/actions/workflows/python-package.yml/badge.svg)](https://github.com/rbnvrw/nd2reader/actions/workflows/python-package.yml)
### About


+ 1
- 1
docs

@ -1 +1 @@
Subproject commit f700c239f8f9d7d1f99a3c10d9f67e2b3b8ef307
Subproject commit 42dbfdf18e0ed7656ef73ac66e889c3138ef756f

+ 9
- 1
nd2reader/__init__.py View File

@ -2,4 +2,12 @@ from os import path
from nd2reader.reader import ND2Reader
from nd2reader.legacy import Nd2
__version__ = '3.2.3'
try:
import importlib.metadata as importlib_metadata
except:
import importlib_metadata
try:
__version__ = importlib_metadata.version(__name__)
except:
print('Unable to read version number')

+ 9
- 25
nd2reader/stitched.py View File

@ -1,40 +1,29 @@
# -*- coding: utf-8 -*-
import numpy as np # type: ignore
import warnings
def get_unwanted_bytes_ids(image_group_data, image_data_start, height, width):
# Check if the byte array size conforms to the image axes size. If not, check
# that the number of unexpected (unwanted) bytes is a multiple of the number of
# rows (height), as the same unmber of unwanted bytes is expected to be
# rows (height), as the same number of unwanted bytes is expected to be
# appended at the end of each row. Then, returns the indexes of the unwanted
# bytes.
# Skip the first 4 elements that correspond to the time stamp
number_of_true_channels = int(len(image_group_data[4:]) / (height * width))
n_unwanted_bytes = (len(image_group_data[image_data_start:])) % (height * width)
n_unwanted_bytes = (len(image_group_data[4:])) % (height * width)
if not n_unwanted_bytes:
return np.arange(0)
assert 0 == n_unwanted_bytes % height, (
"An unexpected number of extra bytes was encountered based on the expected"
+ " frame size, therefore the file could not be parsed."
"An unexpected number of extra bytes was encountered based on the expected"
+ " frame size, therefore the file could not be parsed."
)
return np.arange(
image_data_start + height * number_of_true_channels,
image_data_start + width * number_of_true_channels,
len(image_group_data) - n_unwanted_bytes + 1,
height * number_of_true_channels,
width * number_of_true_channels,
)
def remove_bytes_by_id(byte_ids, image_group_data, height):
# Remove bytes by ID.
bytes_per_row = len(byte_ids) // height
warnings.warn(
f"{len(byte_ids)} ({bytes_per_row}*{height}) unexpected zero "
+ "bytes were found in the ND2 file and removed to allow further parsing."
)
for i in range(len(byte_ids)):
del image_group_data[byte_ids[i] : (byte_ids[i] + bytes_per_row)]
def remove_parsed_unwanted_bytes(image_group_data, image_data_start, height, width):
# Stitched ND2 files have been reported to contain unexpected (according to
# image shape) zero bytes at the end of each image data row. This hinders
@ -44,11 +33,6 @@ def remove_parsed_unwanted_bytes(image_group_data, image_data_start, height, wid
image_group_data, image_data_start, height, width
)
if 0 != len(unwanted_byte_ids):
assert np.all(
image_group_data[unwanted_byte_ids + np.arange(len(unwanted_byte_ids))] == 0
), (
f"{len(unwanted_byte_ids)} unexpected non-zero bytes were found"
+ " in the ND2 file, the file could not be parsed."
)
remove_bytes_by_id(unwanted_byte_ids, image_group_data, height)
image_group_data = np.delete(np.array(image_group_data), unwanted_byte_ids)
return image_group_data

+ 1
- 1
release.txt View File

@ -1,4 +1,4 @@
Update version in 'nd2reader/__init__.py' file
Update version in 'setup.py' file
Rebuild sphinx documentation


+ 2
- 2
requirements.txt View File

@ -1,4 +1,4 @@
numpy>=1.9.2
numpy>=1.14
six>=1.4
xmltodict>=0.9.2
pims>=0.3.0
pims>=0.3.0

+ 4
- 2
setup.py View File

@ -1,16 +1,18 @@
from setuptools import setup
from nd2reader import __version__ as VERSION
VERSION = '3.3.0'
if __name__ == '__main__':
setup(
name='nd2reader',
packages=['nd2reader'],
install_requires=[
'numpy>=1.6.2',
'numpy>=1.14',
'six>=1.4',
'xmltodict>=0.9.2',
'pims>=0.3.0'
],
python_requires=">=3.6",
version=VERSION,
description='A tool for reading ND2 files produced by NIS Elements',
author='Ruben Verweij',


+ 0
- 119
sphinx/tutorial.md View File

@ -1,119 +0,0 @@
# Tutorial
### Installation
The package is available on PyPi. Install it using:
```
pip install nd2reader
```
If you don't already have the packages `numpy`, `pims`, `six` and `xmltodict`, they will be installed automatically if you use the `setup.py` script.
`nd2reader` is an order of magnitude faster in Python 3. I recommend using it unless you have no other choice. Python 2.7 and Python >= 3.4 are supported.
#### Installation via Conda Forge
Installing `nd2reader` from the `conda-forge` channel can be achieved by adding `conda-forge` to your channels with:
```
conda config --add channels conda-forge
```
Once the `conda-forge` channel has been enabled, `nd2reader` can be installed with:
```
conda install nd2reader
```
It is possible to list all of the versions of `nd2reader` available on your platform with:
```
conda search nd2reader --channel conda-forge
```
### Opening ND2s
`nd2reader` follows the [pims](https://github.com/soft-matter/pims) framework. To open a file and show the first frame:
```python
from nd2reader import ND2Reader
import matplotlib.pyplot as plt
with ND2Reader('my_directory/example.nd2') as images:
plt.imshow(images[0])
```
After opening the file, all `pims` features are supported. Please refer to the [pims documentation](http://soft-matter.github.io/pims/).
### ND2 metadata
The ND2 file contains various metadata, such as acquisition information,
regions of interest and custom user comments. Most of this metadata is parsed
and available in dictionary form. For example:
```python
from nd2reader import ND2Reader
with ND2Reader('my_directory/example.nd2') as images:
# width and height of the image
print('%d x %d px' % (images.metadata['width'], images.metadata['height']))
```
All metadata properties are:
* `width`: the width of the image in pixels
* `height`: the height of the image in pixels
* `date`: the date the image was taken
* `fields_of_view`: the fields of view in the image
* `frames`: a list of all frame numbers
* `z_levels`: the z levels in the image
* `total_images_per_channel`: the number of images per color channel
* `channels`: the color channels
* `pixel_microns`: the amount of microns per pixel
* `rois`: the regions of interest (ROIs) defined by the user
* `experiment`: information about the nature and timings of the ND experiment
### Iterating over fields of view
Using `NDExperiments` in the Nikon software, it is possible to acquire images on different `(x, y)` positions.
This is referred to as different fields of view. Using this reader, the fields of view are on the `v` axis.
For example:
```python
from nd2reader import ND2Reader
with ND2Reader('my_directory/example.nd2') as images:
# width and height of the image
print(images.metadata)
```
will output
```python
{'channels': ['BF100xoil-1x-R', 'BF+RITC'],
'date': datetime.datetime(2017, 10, 30, 14, 35, 18),
'experiment': {'description': 'ND Acquisition',
'loops': [{'duration': 0,
'sampling_interval': 0.0,
'start': 0,
'stimulation': False}]},
'fields_of_view': [0, 1],
'frames': [0],
'height': 1895,
'num_frames': 1,
'pixel_microns': 0.09214285714285715,
'total_images_per_channel': 6,
'width': 2368,
'z_levels': [0, 1, 2]}
```
for our example file. As you can see from the metadata, it has two fields of view. We can also look at the sizes of the axes:
```python
print(images.sizes)
```
```python
{'c': 2, 't': 1, 'v': 2, 'x': 2368, 'y': 1895, 'z': 3}
```
As you can see, the fields of view are listed on the `v` axis. It is therefore possible to loop over them like this:
```python
images.iter_axes = 'v'
for fov in images:
print(fov) # Frame containing one field of view
```
For more information on axis bundling and iteration, refer to the [pims documentation](http://soft-matter.github.io/pims/v0.4/multidimensional.html#axes-bundling).

+ 148
- 0
sphinx/tutorial.rst View File

@ -0,0 +1,148 @@
Tutorial
========
Installation
~~~~~~~~~~~~
The package is available on PyPi. Install it using:
::
pip install nd2reader
If you don't already have the packages ``numpy``, ``pims``, ``six`` and
``xmltodict``, they will be installed automatically if you use the
``setup.py`` script. ``nd2reader`` is an order of magnitude faster in
Python 3. I recommend using it unless you have no other choice. Python
2.7 and Python >= 3.4 are supported.
Installation via Conda Forge
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Installing ``nd2reader`` from the ``conda-forge`` channel can be
achieved by adding ``conda-forge`` to your channels with:
::
conda config --add channels conda-forge
Once the ``conda-forge`` channel has been enabled, ``nd2reader`` can be
installed with:
::
conda install nd2reader
It is possible to list all of the versions of ``nd2reader`` available on
your platform with:
::
conda search nd2reader --channel conda-forge
Opening ND2s
~~~~~~~~~~~~
``nd2reader`` follows the `pims <https://github.com/soft-matter/pims>`__
framework. To open a file and show the first frame:
.. code:: python
from nd2reader import ND2Reader
import matplotlib.pyplot as plt
with ND2Reader('my_directory/example.nd2') as images:
plt.imshow(images[0])
After opening the file, all ``pims`` features are supported. Please
refer to the `pims
documentation <http://soft-matter.github.io/pims/>`__.
ND2 metadata
~~~~~~~~~~~~
The ND2 file contains various metadata, such as acquisition information,
regions of interest and custom user comments. Most of this metadata is
parsed and available in dictionary form. For example:
.. code:: python
from nd2reader import ND2Reader
with ND2Reader('my_directory/example.nd2') as images:
# width and height of the image
print('%d x %d px' % (images.metadata['width'], images.metadata['height']))
All metadata properties are:
- ``width``: the width of the image in pixels
- ``height``: the height of the image in pixels
- ``date``: the date the image was taken
- ``fields_of_view``: the fields of view in the image
- ``frames``: a list of all frame numbers
- ``z_levels``: the z levels in the image
- ``total_images_per_channel``: the number of images per color channel
- ``channels``: the color channels
- ``pixel_microns``: the amount of microns per pixel
- ``rois``: the regions of interest (ROIs) defined by the user
- ``experiment``: information about the nature and timings of the ND
experiment
Iterating over fields of view
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using ``NDExperiments`` in the Nikon software, it is possible to acquire
images on different ``(x, y)`` positions. This is referred to as
different fields of view. Using this reader, the fields of view are on
the ``v`` axis. For example:
.. code:: python
from nd2reader import ND2Reader
with ND2Reader('my_directory/example.nd2') as images:
# width and height of the image
print(images.metadata)
will output
.. code:: python
{'channels': ['BF100xoil-1x-R', 'BF+RITC'],
'date': datetime.datetime(2017, 10, 30, 14, 35, 18),
'experiment': {'description': 'ND Acquisition',
'loops': [{'duration': 0,
'sampling_interval': 0.0,
'start': 0,
'stimulation': False}]},
'fields_of_view': [0, 1],
'frames': [0],
'height': 1895,
'num_frames': 1,
'pixel_microns': 0.09214285714285715,
'total_images_per_channel': 6,
'width': 2368,
'z_levels': [0, 1, 2]}
for our example file. As you can see from the metadata, it has two
fields of view. We can also look at the sizes of the axes:
.. code:: python
print(images.sizes)
.. code:: python
{'c': 2, 't': 1, 'v': 2, 'x': 2368, 'y': 1895, 'z': 3}
As you can see, the fields of view are listed on the ``v`` axis. It is
therefore possible to loop over them like this:
.. code:: python
images.iter_axes = 'v'
for fov in images:
print(fov) # Frame containing one field of view
For more information on axis bundling and iteration, refer to the `pims
documentation <http://soft-matter.github.io/pims/v0.4/multidimensional.html#axes-bundling>`__.

Loading…
Cancel
Save