Browse Source

Cleanup documentation

feature/load_slices
Ruben Verweij 8 years ago
parent
commit
048e101e2d
6 changed files with 135 additions and 129 deletions
  1. +1
    -1
      docs
  2. +51
    -29
      nd2reader/label_map.py
  3. +56
    -91
      nd2reader/raw_metadata.py
  4. +4
    -0
      sphinx/index.rst
  5. +3
    -8
      sphinx/nd2reader.rst
  6. +20
    -0
      sphinx/tutorial.md

+ 1
- 1
docs

@ -1 +1 @@
Subproject commit 248bbb1aa20c11fe1c82c092ce68a3d9526156d2
Subproject commit ca4aa37a94bb0367e708fb9f6d688e8713c51796

+ 51
- 29
nd2reader/label_map.py View File

@ -7,14 +7,16 @@ class LabelMap(object):
"""Contains pointers to metadata. This might only be valid for V3 files. """Contains pointers to metadata. This might only be valid for V3 files.
""" """
def __init__(self, raw_binary_data): def __init__(self, raw_binary_data):
self._data = raw_binary_data self._data = raw_binary_data
self._image_data = {} self._image_data = {}
def image_attributes(self): def image_attributes(self):
"""
"""Get the location of the image attributes
Returns: Returns:
int: The location of the image attributes
""" """
return self._get_location(six.b("ImageAttributesLV!")) return self._get_location(six.b("ImageAttributesLV!"))
@ -32,39 +34,40 @@ class LabelMap(object):
@property @property
def image_text_info(self): def image_text_info(self):
"""
"""Get the location of the textual image information
Returns: Returns:
int: The location of the textual image information
""" """
return self._get_location(six.b("ImageTextInfoLV!")) return self._get_location(six.b("ImageTextInfoLV!"))
@property @property
def image_metadata(self): def image_metadata(self):
"""
"""Get the location of the image metadata
Returns: Returns:
int: The location of the image metadata
""" """
return self._get_location(six.b("ImageMetadataLV!")) return self._get_location(six.b("ImageMetadataLV!"))
@property @property
def image_metadata_sequence(self): def image_metadata_sequence(self):
# there is always only one of these, even though it has a pipe followed by a zero, which is how they do indexes
"""
"""Get the location of the image metadata sequence. There is always only one of these, even though it has a pipe
followed by a zero, which is how they do indexes.
Returns: Returns:
int: The location of the image metadata sequence
""" """
return self._get_location(six.b("ImageMetadataSeqLV|0!")) return self._get_location(six.b("ImageMetadataSeqLV|0!"))
def get_image_data_location(self, index): def get_image_data_location(self, index):
"""
Args:
index:
"""Get the location of the image data
Returns: Returns:
int: The location of the image data
""" """
if not self._image_data: if not self._image_data:
@ -77,171 +80,190 @@ class LabelMap(object):
@property @property
def image_calibration(self): def image_calibration(self):
"""
"""Get the location of the image calibration
Returns: Returns:
int: The location of the image calibration
""" """
return self._get_location(six.b("ImageCalibrationLV|0!")) return self._get_location(six.b("ImageCalibrationLV|0!"))
@property @property
def image_attributes(self): def image_attributes(self):
"""
"""Get the location of the image attributes
Returns: Returns:
int: The location of the image attributes
""" """
return self._get_location(six.b("ImageAttributesLV!")) return self._get_location(six.b("ImageAttributesLV!"))
@property @property
def x_data(self): def x_data(self):
"""
"""Get the location of the x data
Returns: Returns:
int: The location of the x data
""" """
return self._get_location(six.b("CustomData|X!")) return self._get_location(six.b("CustomData|X!"))
@property @property
def y_data(self): def y_data(self):
"""
"""Get the location of the y data
Returns: Returns:
int: The location of the y data
""" """
return self._get_location(six.b("CustomData|Y!")) return self._get_location(six.b("CustomData|Y!"))
@property @property
def z_data(self): def z_data(self):
"""
"""Get the location of the z data
Returns: Returns:
int: The location of the z data
""" """
return self._get_location(six.b("CustomData|Z!")) return self._get_location(six.b("CustomData|Z!"))
@property @property
def roi_metadata(self): def roi_metadata(self):
"""
Information about any regions of interest (ROIs) defined in the nd2 file
"""Information about any regions of interest (ROIs) defined in the nd2 file
Returns: Returns:
int: The location of the regions of interest (ROIs)
""" """
return self._get_location(six.b("CustomData|RoiMetadata_v1!")) return self._get_location(six.b("CustomData|RoiMetadata_v1!"))
@property @property
def pfs_status(self): def pfs_status(self):
"""
"""Get the location of the perfect focus system (PFS) status
Returns: Returns:
int: The location of the perfect focus system (PFS) status
""" """
return self._get_location(six.b("CustomData|PFS_STATUS!")) return self._get_location(six.b("CustomData|PFS_STATUS!"))
@property @property
def pfs_offset(self): def pfs_offset(self):
"""
"""Get the location of the perfect focus system (PFS) offset
Returns: Returns:
int: The location of the perfect focus system (PFS) offset
""" """
return self._get_location(six.b("CustomData|PFS_OFFSET!")) return self._get_location(six.b("CustomData|PFS_OFFSET!"))
@property @property
def guid(self): def guid(self):
"""
"""Get the location of the image guid
Returns: Returns:
int: The location of the image guid
""" """
return self._get_location(six.b("CustomData|GUIDStore!")) return self._get_location(six.b("CustomData|GUIDStore!"))
@property @property
def description(self): def description(self):
"""
"""Get the location of the image description
Returns: Returns:
int: The location of the image description
""" """
return self._get_location(six.b("CustomData|CustomDescriptionV1_0!")) return self._get_location(six.b("CustomData|CustomDescriptionV1_0!"))
@property @property
def camera_exposure_time(self): def camera_exposure_time(self):
"""
"""Get the location of the camera exposure time
Returns: Returns:
int: The location of the camera exposure time
""" """
return self._get_location(six.b("CustomData|Camera_ExposureTime1!")) return self._get_location(six.b("CustomData|Camera_ExposureTime1!"))
@property @property
def camera_temp(self): def camera_temp(self):
"""
"""Get the location of the camera temperature
Returns: Returns:
int: The location of the camera temperature
""" """
return self._get_location(six.b("CustomData|CameraTemp1!")) return self._get_location(six.b("CustomData|CameraTemp1!"))
@property @property
def acquisition_times(self): def acquisition_times(self):
"""
"""Get the location of the acquisition times, block 1
Returns: Returns:
int: The location of the acquisition times, block 1
""" """
return self._get_location(six.b("CustomData|AcqTimesCache!")) return self._get_location(six.b("CustomData|AcqTimesCache!"))
@property @property
def acquisition_times_2(self): def acquisition_times_2(self):
"""
"""Get the location of the acquisition times, block 2
Returns: Returns:
int: The location of the acquisition times, block 2
""" """
return self._get_location(six.b("CustomData|AcqTimes2Cache!")) return self._get_location(six.b("CustomData|AcqTimes2Cache!"))
@property @property
def acquisition_frames(self): def acquisition_frames(self):
"""
"""Get the location of the acquisition frames
Returns: Returns:
int: The location of the acquisition frames
""" """
return self._get_location(six.b("CustomData|AcqFramesCache!")) return self._get_location(six.b("CustomData|AcqFramesCache!"))
@property @property
def lut_data(self): def lut_data(self):
"""
"""Get the location of the LUT data
Returns: Returns:
int: The location of the LUT data
""" """
return self._get_location(six.b("CustomDataVar|LUTDataV1_0!")) return self._get_location(six.b("CustomDataVar|LUTDataV1_0!"))
@property @property
def grabber_settings(self): def grabber_settings(self):
"""
"""Get the location of the grabber settings
Returns: Returns:
int: The location of the grabber settings
""" """
return self._get_location(six.b("CustomDataVar|GrabberCameraSettingsV1_0!")) return self._get_location(six.b("CustomDataVar|GrabberCameraSettingsV1_0!"))
@property @property
def custom_data(self): def custom_data(self):
"""
"""Get the location of the custom user data
Returns: Returns:
int: The location of the custom user data
""" """
return self._get_location(six.b("CustomDataVar|CustomDataV2_0!")) return self._get_location(six.b("CustomDataVar|CustomDataV2_0!"))
@property @property
def app_info(self): def app_info(self):
"""
"""Get the location of the application info metadata
Returns: Returns:
int: The location of the application info metadata
""" """
return self._get_location(six.b("CustomDataVar|AppInfo_V1_0!")) return self._get_location(six.b("CustomDataVar|AppInfo_V1_0!"))

+ 56
- 91
nd2reader/raw_metadata.py View File

@ -1,43 +1,12 @@
import re import re
from nd2reader.common import read_chunk, read_array, read_metadata, parse_date from nd2reader.common import read_chunk, read_array, read_metadata, parse_date
import xmltodict import xmltodict
import six import six
import numpy as np import numpy as np
def ignore_missing(func):
"""
Ignore missing properties
Args:
func: function to decorate
Returns:
function: a wrapper function
"""
def wrapper(*args, **kwargs):
"""
Wrapper function to ignore missing class properties
Args:
*args:
**kwargs:
Returns:
"""
try:
return func(*args, **kwargs)
except:
return None
return wrapper
class RawMetadata(object): class RawMetadata(object):
"""
RawMetadata class parses and stores the raw metadata that is read from the binary file in dict format
"""RawMetadata class parses and stores the raw metadata that is read from the binary file in dict format.
""" """
def __init__(self, fh, label_map): def __init__(self, fh, label_map):
@ -47,7 +16,7 @@ class RawMetadata(object):
@property @property
def __dict__(self): def __dict__(self):
"""Returns the parsed metadata in dictionary form
"""Returns the parsed metadata in dictionary form.
Returns: Returns:
dict: the parsed metadata dict: the parsed metadata
@ -55,7 +24,7 @@ class RawMetadata(object):
return self.get_parsed_metadata() return self.get_parsed_metadata()
def get_parsed_metadata(self): def get_parsed_metadata(self):
""" Returns the parsed metadata in dictionary form
"""Returns the parsed metadata in dictionary form.
Returns: Returns:
dict: the parsed metadata dict: the parsed metadata
@ -84,9 +53,9 @@ class RawMetadata(object):
return self._metadata_parsed return self._metadata_parsed
def _parse_channels(self): def _parse_channels(self):
"""
These are labels created by the NIS Elements user. Typically they may a short description of the filter cube
used (e.g. "bright field", "GFP", etc.)
"""These are labels created by the NIS Elements user. Typically they may a short description of the filter cube
used (e.g. 'bright field', 'GFP', etc.)
Returns: Returns:
list: the color channels list: the color channels
""" """
@ -108,8 +77,7 @@ class RawMetadata(object):
return channels return channels
def _parse_fields_of_view(self): def _parse_fields_of_view(self):
"""
The metadata contains information about fields of view, but it contains it even if some fields
"""The metadata contains information about fields of view, but it contains it even if some fields
of view were cropped. We can't find anything that states which fields of view are actually of view were cropped. We can't find anything that states which fields of view are actually
in the image data, so we have to calculate it. There probably is something somewhere, since in the image data, so we have to calculate it. There probably is something somewhere, since
NIS Elements can figure it out, but we haven't found it yet. NIS Elements can figure it out, but we haven't found it yet.
@ -134,8 +102,7 @@ class RawMetadata(object):
return self._parse_dimension(r""".*?Z\((\d+)\).*?""") return self._parse_dimension(r""".*?Z\((\d+)\).*?""")
def _parse_dimension_text(self): def _parse_dimension_text(self):
"""
While there are metadata values that represent a lot of what we want to capture, they seem to be unreliable.
"""While there are metadata values that represent a lot of what we want to capture, they seem to be unreliable.
Sometimes certain elements don't exist, or change their data type randomly. However, the human-readable text Sometimes certain elements don't exist, or change their data type randomly. However, the human-readable text
is always there and in the same exact format, so we just parse that instead. is always there and in the same exact format, so we just parse that instead.
@ -165,7 +132,7 @@ class RawMetadata(object):
def _parse_total_images_per_channel(self): def _parse_total_images_per_channel(self):
"""The total number of images per channel. """The total number of images per channel.
Warning: this may be inaccurate as it includes "gap" images.
Warning: this may be inaccurate as it includes 'gap' images.
""" """
return self.image_attributes[six.b('SLxImageAttributes')][six.b('uiSequenceCount')] return self.image_attributes[six.b('SLxImageAttributes')][six.b('uiSequenceCount')]
@ -194,9 +161,10 @@ class RawMetadata(object):
This includes the position and size at the given timepoints. This includes the position and size at the given timepoints.
Args: Args:
raw_roi_dict:
raw_roi_dict: dictionary of raw roi metadata
Returns: Returns:
dict: the parsed ROI metadata
""" """
number_of_timepoints = raw_roi_dict[six.b('m_vectAnimParams_Size')] number_of_timepoints = raw_roi_dict[six.b('m_vectAnimParams_Size')]
@ -243,10 +211,11 @@ class RawMetadata(object):
Parses a ROI vector animation object and adds it to the global list of timepoints and positions. Parses a ROI vector animation object and adds it to the global list of timepoints and positions.
Args: Args:
roi_dict:
animation_dict:
roi_dict: the raw roi dictionary
animation_dict: the raw animation dictionary
Returns: Returns:
dict: the parsed metadata
""" """
roi_dict["timepoints"].append(animation_dict[six.b('m_dTimeMs')]) roi_dict["timepoints"].append(animation_dict[six.b('m_dTimeMs')])
@ -292,13 +261,13 @@ class RawMetadata(object):
@staticmethod @staticmethod
def _parse_loop_data(loop_data): def _parse_loop_data(loop_data):
"""
Parse the experimental loop data
"""Parse the experimental loop data
Args: Args:
loop_data:
loop_data: dictionary of experiment loops
Returns: Returns:
list: list of the parsed loops
""" """
loops = [loop_data] loops = [loop_data]
@ -342,161 +311,156 @@ class RawMetadata(object):
return parsed_loops return parsed_loops
@property @property
@ignore_missing
def image_text_info(self): def image_text_info(self):
"""
"""Textual image information
Returns: Returns:
dict: containing the textual image info
""" """
return read_metadata(read_chunk(self._fh, self._label_map.image_text_info), 1) return read_metadata(read_chunk(self._fh, self._label_map.image_text_info), 1)
@property @property
@ignore_missing
def image_metadata_sequence(self): def image_metadata_sequence(self):
"""
"""Image metadata of the sequence
Returns: Returns:
dict: containing the metadata
""" """
return read_metadata(read_chunk(self._fh, self._label_map.image_metadata_sequence), 1) return read_metadata(read_chunk(self._fh, self._label_map.image_metadata_sequence), 1)
@property @property
@ignore_missing
def image_calibration(self): def image_calibration(self):
"""
The amount of pixels per micron.
"""The amount of pixels per micron.
Returns: Returns:
float: pixels per micron
dict: pixels per micron
""" """
return read_metadata(read_chunk(self._fh, self._label_map.image_calibration), 1) return read_metadata(read_chunk(self._fh, self._label_map.image_calibration), 1)
@property @property
@ignore_missing
def image_attributes(self): def image_attributes(self):
"""
"""Image attributes
Returns: Returns:
dict: containing the image attributes
""" """
return read_metadata(read_chunk(self._fh, self._label_map.image_attributes), 1) return read_metadata(read_chunk(self._fh, self._label_map.image_attributes), 1)
@property @property
@ignore_missing
def x_data(self): def x_data(self):
"""
"""X data
Returns: Returns:
dict: x_data
""" """
return read_array(self._fh, 'double', self._label_map.x_data) return read_array(self._fh, 'double', self._label_map.x_data)
@property @property
@ignore_missing
def y_data(self): def y_data(self):
"""
"""Y data
Returns: Returns:
dict: y_data
""" """
return read_array(self._fh, 'double', self._label_map.y_data) return read_array(self._fh, 'double', self._label_map.y_data)
@property @property
@ignore_missing
def z_data(self): def z_data(self):
"""
"""Z data
Returns: Returns:
dict: z_data
""" """
return read_array(self._fh, 'double', self._label_map.z_data) return read_array(self._fh, 'double', self._label_map.z_data)
@property @property
@ignore_missing
def roi_metadata(self): def roi_metadata(self):
"""
Contains information about the defined ROIs: shape, position and type (reference/background/stimulation).
"""Contains information about the defined ROIs: shape, position and type (reference/background/stimulation).
Returns: Returns:
dict: ROI metadata dictionary dict: ROI metadata dictionary
""" """
return read_metadata(read_chunk(self._fh, self._label_map.roi_metadata), 1) return read_metadata(read_chunk(self._fh, self._label_map.roi_metadata), 1)
@property @property
@ignore_missing
def pfs_status(self): def pfs_status(self):
"""
"""Perfect focus system (PFS) status
Returns: Returns:
dict: Perfect focus system (PFS) status
""" """
return read_array(self._fh, 'int', self._label_map.pfs_status) return read_array(self._fh, 'int', self._label_map.pfs_status)
@property @property
@ignore_missing
def pfs_offset(self): def pfs_offset(self):
"""
"""Perfect focus system (PFS) offset
Returns: Returns:
dict: Perfect focus system (PFS) offset
""" """
return read_array(self._fh, 'int', self._label_map.pfs_offset) return read_array(self._fh, 'int', self._label_map.pfs_offset)
@property @property
@ignore_missing
def camera_exposure_time(self): def camera_exposure_time(self):
"""
"""Exposure time information
Returns: Returns:
dict: Camera exposure time
""" """
return read_array(self._fh, 'double', self._label_map.camera_exposure_time) return read_array(self._fh, 'double', self._label_map.camera_exposure_time)
@property @property
@ignore_missing
def lut_data(self): def lut_data(self):
"""
"""LUT information
Returns: Returns:
dict: LUT information
""" """
return xmltodict.parse(read_chunk(self._fh, self._label_map.lut_data)) return xmltodict.parse(read_chunk(self._fh, self._label_map.lut_data))
@property @property
@ignore_missing
def grabber_settings(self): def grabber_settings(self):
"""
"""Grabber settings
Returns: Returns:
dict: Acquisition settings
""" """
return xmltodict.parse(read_chunk(self._fh, self._label_map.grabber_settings)) return xmltodict.parse(read_chunk(self._fh, self._label_map.grabber_settings))
@property @property
@ignore_missing
def custom_data(self): def custom_data(self):
"""
"""Custom user data
Returns: Returns:
dict: custom user data
""" """
return xmltodict.parse(read_chunk(self._fh, self._label_map.custom_data)) return xmltodict.parse(read_chunk(self._fh, self._label_map.custom_data))
@property @property
@ignore_missing
def app_info(self): def app_info(self):
"""
"""NIS elements application info
Returns: Returns:
dict: (Version) information of the NIS Elements application
""" """
return xmltodict.parse(read_chunk(self._fh, self._label_map.app_info)) return xmltodict.parse(read_chunk(self._fh, self._label_map.app_info))
@property @property
@ignore_missing
def camera_temp(self): def camera_temp(self):
"""
"""Camera temperature
Yields: Yields:
float: the temperature float: the temperature
""" """
camera_temp = read_array(self._fh, 'double', self._label_map.camera_temp) camera_temp = read_array(self._fh, 'double', self._label_map.camera_temp)
if camera_temp: if camera_temp:
@ -504,11 +468,12 @@ class RawMetadata(object):
yield temp yield temp
@property @property
@ignore_missing
def acquisition_times(self): def acquisition_times(self):
"""
"""Acquisition times
Yields: Yields:
float: the acquisition time float: the acquisition time
""" """
acquisition_times = read_array(self._fh, 'double', self._label_map.acquisition_times) acquisition_times = read_array(self._fh, 'double', self._label_map.acquisition_times)
if acquisition_times: if acquisition_times:
@ -516,11 +481,11 @@ class RawMetadata(object):
yield acquisition_time yield acquisition_time
@property @property
@ignore_missing
def image_metadata(self): def image_metadata(self):
"""
"""Image metadata
Returns: Returns:
dict: Extra image metadata
""" """
if self._label_map.image_metadata: if self._label_map.image_metadata:


+ 4
- 0
sphinx/index.rst View File

@ -6,10 +6,14 @@
``nd2reader``: a pure-Python package for reading Nikon .nd2 files ``nd2reader``: a pure-Python package for reading Nikon .nd2 files
================================================================= =================================================================
`nd2reader` is a pure-Python package that reads images produced by NIS Elements 4.0+. It has only been definitively tested on NIS Elements 4.30.02 Build 1053. Support for older versions is being actively worked on.
The reader is written in the `pims <https://github.com/soft-matter/pims>`_ framework, enabling easy access to multidimensional files, lazy slicing, and nice display in IPython. To get started, see the quick start tutorial.
.. toctree:: .. toctree::
:maxdepth: 4 :maxdepth: 4
:caption: Contents: :caption: Contents:
nd2reader quick start tutorial <tutorial>
nd2reader API reference <nd2reader> nd2reader API reference <nd2reader>


+ 3
- 8
sphinx/nd2reader.rst View File

@ -1,6 +1,9 @@
nd2reader package nd2reader package
================= =================
In general, you should only have to use the ``nd2reader.reader`` module. The rest of the submodules are for internal
use only.
Submodules Submodules
---------- ----------
@ -51,11 +54,3 @@ nd2reader.exceptions module
:members: :members:
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
Module contents
---------------
.. automodule:: nd2reader
:members:
:undoc-members:
:show-inheritance:

+ 20
- 0
sphinx/tutorial.md View File

@ -0,0 +1,20 @@
# Tutorial
### Installation
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.
### 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/).

Loading…
Cancel
Save