From 048e101e2d64c36b38295df6c84bb2aef96e92b5 Mon Sep 17 00:00:00 2001 From: Ruben Verweij Date: Tue, 14 Mar 2017 16:04:23 +0100 Subject: [PATCH] Cleanup documentation --- docs | 2 +- nd2reader/label_map.py | 80 +++++++++++++-------- nd2reader/raw_metadata.py | 147 +++++++++++++++----------------------- sphinx/index.rst | 4 ++ sphinx/nd2reader.rst | 11 +-- sphinx/tutorial.md | 20 ++++++ 6 files changed, 135 insertions(+), 129 deletions(-) create mode 100644 sphinx/tutorial.md diff --git a/docs b/docs index 248bbb1..ca4aa37 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 248bbb1aa20c11fe1c82c092ce68a3d9526156d2 +Subproject commit ca4aa37a94bb0367e708fb9f6d688e8713c51796 diff --git a/nd2reader/label_map.py b/nd2reader/label_map.py index ebd87b7..dc62b6a 100644 --- a/nd2reader/label_map.py +++ b/nd2reader/label_map.py @@ -7,14 +7,16 @@ class LabelMap(object): """Contains pointers to metadata. This might only be valid for V3 files. """ + def __init__(self, raw_binary_data): self._data = raw_binary_data self._image_data = {} def image_attributes(self): - """ + """Get the location of the image attributes Returns: + int: The location of the image attributes """ return self._get_location(six.b("ImageAttributesLV!")) @@ -32,39 +34,40 @@ class LabelMap(object): @property def image_text_info(self): - """ + """Get the location of the textual image information Returns: + int: The location of the textual image information """ return self._get_location(six.b("ImageTextInfoLV!")) @property def image_metadata(self): - """ + """Get the location of the image metadata Returns: + int: The location of the image metadata """ return self._get_location(six.b("ImageMetadataLV!")) @property 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: + int: The location of the image metadata sequence """ return self._get_location(six.b("ImageMetadataSeqLV|0!")) def get_image_data_location(self, index): - """ - - Args: - index: + """Get the location of the image data Returns: + int: The location of the image data """ if not self._image_data: @@ -77,171 +80,190 @@ class LabelMap(object): @property def image_calibration(self): - """ + """Get the location of the image calibration Returns: + int: The location of the image calibration """ return self._get_location(six.b("ImageCalibrationLV|0!")) @property def image_attributes(self): - """ + """Get the location of the image attributes Returns: + int: The location of the image attributes """ return self._get_location(six.b("ImageAttributesLV!")) @property def x_data(self): - """ + """Get the location of the x data Returns: + int: The location of the x data """ return self._get_location(six.b("CustomData|X!")) @property def y_data(self): - """ + """Get the location of the y data Returns: + int: The location of the y data """ return self._get_location(six.b("CustomData|Y!")) @property def z_data(self): - """ + """Get the location of the z data Returns: + int: The location of the z data """ return self._get_location(six.b("CustomData|Z!")) @property 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: + int: The location of the regions of interest (ROIs) """ return self._get_location(six.b("CustomData|RoiMetadata_v1!")) @property def pfs_status(self): - """ + """Get the location of the perfect focus system (PFS) status Returns: + int: The location of the perfect focus system (PFS) status """ return self._get_location(six.b("CustomData|PFS_STATUS!")) @property def pfs_offset(self): - """ + """Get the location of the perfect focus system (PFS) offset Returns: + int: The location of the perfect focus system (PFS) offset """ return self._get_location(six.b("CustomData|PFS_OFFSET!")) @property def guid(self): - """ + """Get the location of the image guid Returns: + int: The location of the image guid """ return self._get_location(six.b("CustomData|GUIDStore!")) @property def description(self): - """ + """Get the location of the image description Returns: + int: The location of the image description """ return self._get_location(six.b("CustomData|CustomDescriptionV1_0!")) @property def camera_exposure_time(self): - """ + """Get the location of the camera exposure time Returns: + int: The location of the camera exposure time """ return self._get_location(six.b("CustomData|Camera_ExposureTime1!")) @property def camera_temp(self): - """ + """Get the location of the camera temperature Returns: + int: The location of the camera temperature """ return self._get_location(six.b("CustomData|CameraTemp1!")) @property def acquisition_times(self): - """ + """Get the location of the acquisition times, block 1 Returns: + int: The location of the acquisition times, block 1 """ return self._get_location(six.b("CustomData|AcqTimesCache!")) @property def acquisition_times_2(self): - """ + """Get the location of the acquisition times, block 2 Returns: + int: The location of the acquisition times, block 2 """ return self._get_location(six.b("CustomData|AcqTimes2Cache!")) @property def acquisition_frames(self): - """ + """Get the location of the acquisition frames Returns: + int: The location of the acquisition frames """ return self._get_location(six.b("CustomData|AcqFramesCache!")) @property def lut_data(self): - """ + """Get the location of the LUT data Returns: + int: The location of the LUT data """ return self._get_location(six.b("CustomDataVar|LUTDataV1_0!")) @property def grabber_settings(self): - """ + """Get the location of the grabber settings Returns: + int: The location of the grabber settings """ return self._get_location(six.b("CustomDataVar|GrabberCameraSettingsV1_0!")) @property def custom_data(self): - """ + """Get the location of the custom user data Returns: + int: The location of the custom user data """ return self._get_location(six.b("CustomDataVar|CustomDataV2_0!")) @property def app_info(self): - """ + """Get the location of the application info metadata Returns: + int: The location of the application info metadata """ return self._get_location(six.b("CustomDataVar|AppInfo_V1_0!")) diff --git a/nd2reader/raw_metadata.py b/nd2reader/raw_metadata.py index b929afd..aa0496a 100644 --- a/nd2reader/raw_metadata.py +++ b/nd2reader/raw_metadata.py @@ -1,43 +1,12 @@ import re - from nd2reader.common import read_chunk, read_array, read_metadata, parse_date import xmltodict import six 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): - """ - 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): @@ -47,7 +16,7 @@ class RawMetadata(object): @property def __dict__(self): - """Returns the parsed metadata in dictionary form + """Returns the parsed metadata in dictionary form. Returns: dict: the parsed metadata @@ -55,7 +24,7 @@ class RawMetadata(object): return self.get_parsed_metadata() def get_parsed_metadata(self): - """ Returns the parsed metadata in dictionary form + """Returns the parsed metadata in dictionary form. Returns: dict: the parsed metadata @@ -84,9 +53,9 @@ class RawMetadata(object): return self._metadata_parsed 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: list: the color channels """ @@ -108,8 +77,7 @@ class RawMetadata(object): return channels 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 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. @@ -134,8 +102,7 @@ class RawMetadata(object): return self._parse_dimension(r""".*?Z\((\d+)\).*?""") 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 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): """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')] @@ -194,9 +161,10 @@ class RawMetadata(object): This includes the position and size at the given timepoints. Args: - raw_roi_dict: + raw_roi_dict: dictionary of raw roi metadata Returns: + dict: the parsed ROI metadata """ 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. Args: - roi_dict: - animation_dict: + roi_dict: the raw roi dictionary + animation_dict: the raw animation dictionary Returns: + dict: the parsed metadata """ roi_dict["timepoints"].append(animation_dict[six.b('m_dTimeMs')]) @@ -292,13 +261,13 @@ class RawMetadata(object): @staticmethod def _parse_loop_data(loop_data): - """ - Parse the experimental loop data + """Parse the experimental loop data Args: - loop_data: + loop_data: dictionary of experiment loops Returns: + list: list of the parsed loops """ loops = [loop_data] @@ -342,161 +311,156 @@ class RawMetadata(object): return parsed_loops @property - @ignore_missing def image_text_info(self): - """ + """Textual image information Returns: + dict: containing the textual image info """ return read_metadata(read_chunk(self._fh, self._label_map.image_text_info), 1) @property - @ignore_missing def image_metadata_sequence(self): - """ + """Image metadata of the sequence Returns: + dict: containing the metadata """ return read_metadata(read_chunk(self._fh, self._label_map.image_metadata_sequence), 1) @property - @ignore_missing def image_calibration(self): - """ - The amount of pixels per micron. + """The amount of pixels per micron. + Returns: - float: pixels per micron + dict: pixels per micron """ return read_metadata(read_chunk(self._fh, self._label_map.image_calibration), 1) @property - @ignore_missing def image_attributes(self): - """ + """Image attributes Returns: - + dict: containing the image attributes """ return read_metadata(read_chunk(self._fh, self._label_map.image_attributes), 1) @property - @ignore_missing def x_data(self): - """ + """X data Returns: - + dict: x_data """ return read_array(self._fh, 'double', self._label_map.x_data) @property - @ignore_missing def y_data(self): - """ + """Y data Returns: - + dict: y_data """ return read_array(self._fh, 'double', self._label_map.y_data) @property - @ignore_missing def z_data(self): - """ + """Z data Returns: - + dict: z_data """ return read_array(self._fh, 'double', self._label_map.z_data) @property - @ignore_missing 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: dict: ROI metadata dictionary """ return read_metadata(read_chunk(self._fh, self._label_map.roi_metadata), 1) @property - @ignore_missing def pfs_status(self): - """ + """Perfect focus system (PFS) status Returns: + dict: Perfect focus system (PFS) status """ return read_array(self._fh, 'int', self._label_map.pfs_status) @property - @ignore_missing def pfs_offset(self): - """ + """Perfect focus system (PFS) offset Returns: + dict: Perfect focus system (PFS) offset """ return read_array(self._fh, 'int', self._label_map.pfs_offset) @property - @ignore_missing def camera_exposure_time(self): - """ + """Exposure time information Returns: + dict: Camera exposure time """ return read_array(self._fh, 'double', self._label_map.camera_exposure_time) @property - @ignore_missing def lut_data(self): - """ + """LUT information Returns: + dict: LUT information """ return xmltodict.parse(read_chunk(self._fh, self._label_map.lut_data)) @property - @ignore_missing def grabber_settings(self): - """ + """Grabber settings Returns: + dict: Acquisition settings """ return xmltodict.parse(read_chunk(self._fh, self._label_map.grabber_settings)) @property - @ignore_missing def custom_data(self): - """ + """Custom user data Returns: + dict: custom user data """ return xmltodict.parse(read_chunk(self._fh, self._label_map.custom_data)) @property - @ignore_missing def app_info(self): - """ + """NIS elements application info Returns: + dict: (Version) information of the NIS Elements application """ return xmltodict.parse(read_chunk(self._fh, self._label_map.app_info)) @property - @ignore_missing def camera_temp(self): - """ + """Camera temperature + Yields: float: the temperature + """ camera_temp = read_array(self._fh, 'double', self._label_map.camera_temp) if camera_temp: @@ -504,11 +468,12 @@ class RawMetadata(object): yield temp @property - @ignore_missing def acquisition_times(self): - """ + """Acquisition times + Yields: float: the acquisition time + """ acquisition_times = read_array(self._fh, 'double', self._label_map.acquisition_times) if acquisition_times: @@ -516,11 +481,11 @@ class RawMetadata(object): yield acquisition_time @property - @ignore_missing def image_metadata(self): - """ + """Image metadata Returns: + dict: Extra image metadata """ if self._label_map.image_metadata: diff --git a/sphinx/index.rst b/sphinx/index.rst index d647475..cb70957 100644 --- a/sphinx/index.rst +++ b/sphinx/index.rst @@ -6,10 +6,14 @@ ``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 `_ framework, enabling easy access to multidimensional files, lazy slicing, and nice display in IPython. To get started, see the quick start tutorial. + .. toctree:: :maxdepth: 4 :caption: Contents: + nd2reader quick start tutorial nd2reader API reference diff --git a/sphinx/nd2reader.rst b/sphinx/nd2reader.rst index 83db224..d10be6a 100644 --- a/sphinx/nd2reader.rst +++ b/sphinx/nd2reader.rst @@ -1,6 +1,9 @@ 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 ---------- @@ -51,11 +54,3 @@ nd2reader.exceptions module :members: :undoc-members: :show-inheritance: - -Module contents ---------------- - -.. automodule:: nd2reader - :members: - :undoc-members: - :show-inheritance: diff --git a/sphinx/tutorial.md b/sphinx/tutorial.md new file mode 100644 index 0000000..54de0cd --- /dev/null +++ b/sphinx/tutorial.md @@ -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/). \ No newline at end of file