nd2


Namend2 JSON
Version 0.10.0 PyPI version JSON
download
home_page
SummaryYet another nd2 (Nikon NIS Elements) file reader
upload_time2024-03-17 19:42:49
maintainer
docs_urlNone
author
requires_python>=3.8
licenseBSD 3-Clause License
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # nd2

[![License](https://img.shields.io/pypi/l/nd2.svg?color=green)](https://github.com/tlambert03/nd2/raw/main/LICENSE)
[![PyPI](https://img.shields.io/pypi/v/nd2.svg?color=green)](https://pypi.org/project/nd2)
[![Python Version](https://img.shields.io/pypi/pyversions/nd2.svg?color=green)](https://python.org)
[![Tests](https://github.com/tlambert03/nd2/actions/workflows/ci.yml/badge.svg)](https://github.com/tlambert03/nd2/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/tlambert03/nd2/branch/main/graph/badge.svg)](https://codecov.io/gh/tlambert03/nd2)
[![Benchmarks](https://img.shields.io/badge/⏱-codspeed-%23FF7B53)](https://codspeed.io/tlambert03/nd2)

`.nd2` (Nikon NIS Elements) file reader.

This reader provides a pure python implementation of the Nikon ND2 SDK.

> It _used_ to wrap the official SDK with Cython, but has since been completely
> rewritten to be pure python (for performance, ease of distribution, and
> maintenance) while retaining complete API parity with the official SDK.
>
> **Note:** This library is not affiliated with Nikon in any way, but we are
> grateful for assistance from the SDK developers at Laboratory Imaging.

Features good metadata retrieval, direct `to_dask` and `to_xarray` options
for lazy and/or annotated arrays, and output to OME-TIFF.

This library is tested against many nd2 files with the goal of maximizing
compatibility and data extraction. (If you find an nd2 file that fails in some
way, please [open an issue](https://github.com/tlambert03/nd2/issues/new) with
the file!)

### :book: [Documentation](https://tlambert03.github.io/nd2)

## install

```sh
pip install nd2
```

or from conda:

```sh
conda install -c conda-forge nd2
```

### Legacy nd2 file support

Legacy nd2 (JPEG2000) files are also supported, but require `imagecodecs`. To
install with support for these files use the `legacy` extra:

```sh
pip install nd2[legacy]
```

### Faster XML parsing

Much of the metadata in the file stored as XML. If found in the environment,
`nd2` will use [`lxml`](https://pypi.org/project/lxml/) which is much faster
than the built-in `xml` module. To install with support for `lxml` use:

```sh
pip install nd2 lxml
```

## Usage and API

Full API documentation is available at
[https://tlambert03.github.io/nd2](https://tlambert03.github.io/nd2)

Quick summary below:

```python
import nd2
import numpy as np

my_array = nd2.imread('some_file.nd2')                          # read to numpy array
my_array = nd2.imread('some_file.nd2', dask=True)               # read to dask array
my_array = nd2.imread('some_file.nd2', xarray=True)             # read to xarray
my_array = nd2.imread('some_file.nd2', xarray=True, dask=True)  # read to dask-xarray

# or open a file with nd2.ND2File
f = nd2.ND2File('some_file.nd2')

# (you can also use nd2.ND2File() as a context manager)
with nd2.ND2File('some_file.nd2') as ndfile:
    print(ndfile.metadata)
    ...


# ATTRIBUTES:   # example output
f.path          # 'some_file.nd2'
f.shape         # (10, 2, 256, 256)
f.ndim          # 4
f.dtype         # np.dtype('uint16')
f.size          # 1310720  (total voxel elements)
f.sizes         # {'T': 10, 'C': 2, 'Y': 256, 'X': 256}
f.is_rgb        # False (whether the file is rgb)
                # if the file is RGB, `f.sizes` will have
                # an additional {'S': 3} component

# ARRAY OUTPUTS
f.asarray()         # in-memory np.ndarray - or use np.asarray(f)
f.to_dask()         # delayed dask.array.Array
f.to_xarray()       # in-memory xarray.DataArray, with labeled axes/coords
f.to_xarray(delayed=True)   # delayed xarray.DataArray

# OME-TIFF OUTPUT (new in v0.10.0)
f.write_tiff('output.ome.tif')  # write to ome-tiff file

                    # see below for examples of these structures
# METADATA          # returns instance of ...
f.attributes        # nd2.structures.Attributes
f.metadata          # nd2.structures.Metadata
f.frame_metadata(0) # nd2.structures.FrameMetadata (frame-specific meta)
f.experiment        # List[nd2.structures.ExpLoop]
f.text_info         # dict of misc info
f.voxel_size()      # VoxelSize(x=0.65, y=0.65, z=1.0)

f.rois              # Dict[int, nd2.structures.ROI]
f.binary_data       # any binary masks stored in the file.  See below.
f.events()          # returns tabular "Recorded Data" view from in NIS Elements/Viewer
                    # with info for each frame in the experiment.
                    # output is passabled to pandas.DataFrame

f.ome_metadata()    # returns metadata as an ome_types.OME object
                    # (requires ome-types package)

# allll the metadata we can find...
# no attempt made to standardize or parse it
# look in here if you're searching for metadata that isn't exposed in the above
# but try not to rely on it, as it's not guaranteed to be stable
f.unstructured_metadata()

f.close()           # don't forget to close when not using a context manager!
f.closed            # boolean, whether the file is closed
```

## Metadata structures

These follow the structure of the nikon SDK outputs (where relevant).
Here are some example outputs

<details>

<summary><code>attributes</code></summary>

```python
Attributes(
    bitsPerComponentInMemory=16,
    bitsPerComponentSignificant=16,
    componentCount=2,
    heightPx=32,
    pixelDataType='unsigned',
    sequenceCount=60,
    widthBytes=128,
    widthPx=32,
    compressionLevel=None,
    compressionType=None,
    tileHeightPx=None,
    tileWidthPx=None,
    channelCount=2
)
```

</details>

<details>

<summary><code>metadata</code></summary>

_Note: the `metadata` for legacy (JPEG2000) files will be a plain unstructured dict._

```python
Metadata(
    contents=Contents(channelCount=2, frameCount=60),
    channels=[
        Channel(
            channel=ChannelMeta(
                name='Widefield Green',
                index=0,
                color=Color(r=91, g=255, b=0, a=1.0),
                emissionLambdaNm=535.0,
                excitationLambdaNm=None
            ),
            loops=LoopIndices(NETimeLoop=None, TimeLoop=0, XYPosLoop=1, ZStackLoop=2),
            microscope=Microscope(
                objectiveMagnification=10.0,
                objectiveName='Plan Fluor 10x Ph1 DLL',
                objectiveNumericalAperture=0.3,
                zoomMagnification=1.0,
                immersionRefractiveIndex=1.0,
                projectiveMagnification=None,
                pinholeDiameterUm=None,
                modalityFlags=['fluorescence']
            ),
            volume=Volume(
                axesCalibrated=[True, True, True],
                axesCalibration=[0.652452890023035, 0.652452890023035, 1.0],
                axesInterpretation=(
                    <AxisInterpretation.distance: 'distance'>,
                    <AxisInterpretation.distance: 'distance'>,
                    <AxisInterpretation.distance: 'distance'>
                ),
                bitsPerComponentInMemory=16,
                bitsPerComponentSignificant=16,
                cameraTransformationMatrix=[-0.9998932296054086, -0.014612644841559427, 0.014612644841559427, -0.9998932296054086],
                componentCount=1,
                componentDataType='unsigned',
                voxelCount=[32, 32, 5],
                componentMaxima=[0.0],
                componentMinima=[0.0],
                pixelToStageTransformationMatrix=None
            )
        ),
        Channel(
            channel=ChannelMeta(
                name='Widefield Red',
                index=1,
                color=Color(r=255, g=85, b=0, a=1.0),
                emissionLambdaNm=620.0,
                excitationLambdaNm=None
            ),
            loops=LoopIndices(NETimeLoop=None, TimeLoop=0, XYPosLoop=1, ZStackLoop=2),
            microscope=Microscope(
                objectiveMagnification=10.0,
                objectiveName='Plan Fluor 10x Ph1 DLL',
                objectiveNumericalAperture=0.3,
                zoomMagnification=1.0,
                immersionRefractiveIndex=1.0,
                projectiveMagnification=None,
                pinholeDiameterUm=None,
                modalityFlags=['fluorescence']
            ),
            volume=Volume(
                axesCalibrated=[True, True, True],
                axesCalibration=[0.652452890023035, 0.652452890023035, 1.0],
                axesInterpretation=(
                    <AxisInterpretation.distance: 'distance'>,
                    <AxisInterpretation.distance: 'distance'>,
                    <AxisInterpretation.distance: 'distance'>
                ),
                bitsPerComponentInMemory=16,
                bitsPerComponentSignificant=16,
                cameraTransformationMatrix=[-0.9998932296054086, -0.014612644841559427, 0.014612644841559427, -0.9998932296054086],
                componentCount=1,
                componentDataType='unsigned',
                voxelCount=[32, 32, 5],
                componentMaxima=[0.0],
                componentMinima=[0.0],
                pixelToStageTransformationMatrix=None
            )
        )
    ]
)
```

</details>

<details>

<summary><code>experiment</code></summary>

```python
[
    TimeLoop(
        count=3,
        nestingLevel=0,
        parameters=TimeLoopParams(
            startMs=0.0,
            periodMs=1.0,
            durationMs=0.0,
            periodDiff=PeriodDiff(avg=16278.339965820312, max=16411.849853515625, min=16144.830078125)
        ),
        type='TimeLoop'
    ),
    XYPosLoop(
        count=4,
        nestingLevel=1,
        parameters=XYPosLoopParams(
            isSettingZ=True,
            points=[
                Position(stagePositionUm=[26950.2, -1801.6000000000001, 498.46000000000004], pfsOffset=None, name=None),
                Position(stagePositionUm=[31452.2, -1801.6000000000001, 670.7], pfsOffset=None, name=None),
                Position(stagePositionUm=[35234.3, 2116.4, 664.08], pfsOffset=None, name=None),
                Position(stagePositionUm=[40642.9, -3585.1000000000004, 555.12], pfsOffset=None, name=None)
            ]
        ),
        type='XYPosLoop'
    ),
    ZStackLoop(count=5, nestingLevel=2, parameters=ZStackLoopParams(homeIndex=2, stepUm=1.0, bottomToTop=True, deviceName='Ti2 ZDrive'), type='ZStackLoop')
]
```

</details>

<details>

<summary><code>rois</code></summary>

ROIs found in the metadata are available at `ND2File.rois`, which is a
`dict` of `nd2.structures.ROI` objects, keyed by the ROI ID:

```python
{
    1: ROI(
        id=1,
        info=RoiInfo(
            shapeType=<RoiShapeType.Rectangle: 3>,
            interpType=<InterpType.StimulationROI: 4>,
            cookie=1,
            color=255,
            label='',
            stimulationGroup=0,
            scope=1,
            appData=0,
            multiFrame=False,
            locked=False,
            compCount=2,
            bpc=16,
            autodetected=False,
            gradientStimulation=False,
            gradientStimulationBitDepth=0,
            gradientStimulationLo=0.0,
            gradientStimulationHi=0.0
        ),
        guid='{87190352-9B32-46E4-8297-C46621C1E1EF}',
        animParams=[
            AnimParam(
                timeMs=0.0,
                enabled=1,
                centerX=-0.4228425369685782,
                centerY=-0.5194951478743071,
                centerZ=0.0,
                rotationZ=0.0,
                boxShape=BoxShape(
                    sizeX=0.21256931608133062,
                    sizeY=0.21441774491682075,
                    sizeZ=0.0
                ),
                extrudedShape=ExtrudedShape(sizeZ=0, basePoints=[])
            )
        ]
    ),
    ...
}
```

</details>

<details>

<summary><code>text_info</code></summary>

```python
{
    'capturing': 'Flash4.0, SN:101412\r\nSample 1:\r\n  Exposure: 100 ms\r\n  Binning: 1x1\r\n  Scan Mode: Fast\r\nSample 2:\r\n  Exposure: 100 ms\r\n  Binning: 1x1\r\n  Scan Mode: Fast',
    'date': '9/28/2021  9:41:27 AM',
    'description': 'Metadata:\r\nDimensions: T(3) x XY(4) x λ(2) x Z(5)\r\nCamera Name: Flash4.0, SN:101412\r\nNumerical Aperture: 0.3\r\nRefractive Index: 1\r\nNumber of Picture Planes: 2\r\nPlane #1:\r\n Name: Widefield Green\r\n Component Count: 1\r\n Modality: Widefield Fluorescence\r\n Camera Settings:   Exposure: 100 ms\r\n  Binning: 1x1\r\n  Scan Mode: Fast\r\n Microscope Settings:   Nikon Ti2, FilterChanger(Turret-Lo): 3 (FITC)\r\n  Nikon Ti2, Shutter(FL-Lo): Open\r\n  Nikon Ti2, Shutter(DIA LED): Closed\r\n  Nikon Ti2, Illuminator(DIA): Off\r\n  Nikon Ti2, Illuminator(DIA) Iris intensity: 3.0\r\n  Analyzer Slider: Extracted\r\n  Analyzer Cube: Extracted\r\n  Condenser: 1 (Shutter)\r\n  PFS, state: On\r\n  PFS, offset: 7959\r\n  PFS, mirror: Inserted\r\n  PFS, Dish Type: Glass\r\n  Zoom: 1.00x\r\n  Sola, Shutter(Sola): Active\r\n  Sola, Illuminator(Sola) Voltage: 100.0\r\nPlane #2:\r\n Name: Widefield Red\r\n Component Count: 1\r\n Modality: Widefield Fluorescence\r\n Camera Settings:   Exposure: 100 ms\r\n  Binning: 1x1\r\n  Scan Mode: Fast\r\n Microscope Settings:   Nikon Ti2, FilterChanger(Turret-Lo): 4 (TRITC)\r\n  Nikon Ti2, Shutter(FL-Lo): Open\r\n  Nikon Ti2, Shutter(DIA LED): Closed\r\n  Nikon Ti2, Illuminator(DIA): Off\r\n  Nikon Ti2, Illuminator(DIA) Iris intensity: 1.5\r\n  Analyzer Slider: Extracted\r\n  Analyzer Cube: Extracted\r\n  Condenser: 1 (Shutter)\r\n  PFS, state: On\r\n  PFS, offset: 7959\r\n  PFS, mirror: Inserted\r\n  PFS, Dish Type: Glass\r\n  Zoom: 1.00x\r\n  Sola, Shutter(Sola): Active\r\n  Sola, Illuminator(Sola) Voltage: 100.0\r\nTime Loop: 3\r\n- Equidistant (Period 1 ms)\r\nZ Stack Loop: 5\r\n- Step: 1 µm\r\n- Device: Ti2 ZDrive',
    'optics': 'Plan Fluor 10x Ph1 DLL'
}
```

</details>

<details>

<summary><code>binary_data</code></summary>

This property returns an `nd2.BinaryLayers` object representing all of the
binary masks in the nd2 file.

A `nd2.BinaryLayers` object is a sequence of individual `nd2.BinaryLayer`
objects (one for each binary layer found in the file). Each `BinaryLayer` in
the sequence is a named tuple that has, among other things, a `name` attribute,
and a `data` attribute that is list of numpy arrays (one for each frame in the
experiment) or `None` if the binary layer had no data in that frame.

The most common use case will be to cast either the entire `BinaryLayers` object
or an individual `BinaryLayer` to a `numpy.ndarray`:

```python
>>> import nd2
>>> nd2file = nd2.ND2File('path/to/file.nd2')
>>> binary_layers = nd2file.binary_data

# The output array will have shape
# (n_binary_layers, *coord_shape, *frame_shape).
>>> np.asarray(binary_layers)
```

For example, if the data in the nd2 file has shape `(nT, nZ, nC, nY, nX)`, and
there are 4 binary layers, then the output of `np.asarray(nd2file.binary_data)` will
have shape `(4, nT, nZ, nY, nX)`. (Note that the `nC` dimension is not present
in the output array, and the binary layers are always in the first axis).

You can also cast an individual `BinaryLayer` to a numpy array:

```python
>>> binary_layer = binary_layers[0]
>>> np.asarray(binary_layer)
```

</details>

<details>

<summary><code>events()</code></summary>

This property returns the tabular data reported in the `Image Properties >
Recorded Data` tab of the NIS Viewer.

(There will be a column for each tag in the `CustomDataV2_0` section of
`custom_data` above, as well as any additional events found in the metadata)

The format of the return type data is controlled by the `orient` argument:

- `'records'` : list of dicts - `[{column -> value}, ...]` (default)
- `'dict'` : dict of dicts - `{column -> {index -> value}, ...}`
- `'list'` : dict of lists - `{column -> [value, ...]}`

Not every column header appears in every event, so when `orient` is either
`'dict'` or `'list'`, `float('nan')` will be inserted to maintain a consistent
length for each column.

```python

# with `orient='records'` (DEFAULT)
[
    {
        'Time [s]': 1.32686654,
        'Z-Series': -2.0,
        'Exposure Time [ms]': 100.0,
        'PFS Offset': 0,
        'PFS Status': 0,
        'X Coord [µm]': 31452.2,
        'Y Coord [µm]': -1801.6,
        'Z Coord [µm]': 552.74,
        'Ti2 ZDrive [µm]': 552.74
    },
    {
        'Time [s]': 1.69089657,
        'Z-Series': -1.0,
        'Exposure Time [ms]': 100.0,
        'PFS Offset': 0,
        'PFS Status': 0,
        'X Coord [µm]': 31452.2,
        'Y Coord [µm]': -1801.6,
        'Z Coord [µm]': 553.74,
        'Ti2 ZDrive [µm]': 553.74
    },
    {
        'Time [s]': 2.04194662,
        'Z-Series': 0.0,
        'Exposure Time [ms]': 100.0,
        'PFS Offset': 0,
        'PFS Status': 0,
        'X Coord [µm]': 31452.2,
        'Y Coord [µm]': -1801.6,
        'Z Coord [µm]': 554.74,
        'Ti2 ZDrive [µm]': 554.74
    },
    {
        'Time [s]': 2.38194662,
        'Z-Series': 1.0,
        'Exposure Time [ms]': 100.0,
        'PFS Offset': 0,
        'PFS Status': 0,
        'X Coord [µm]': 31452.2,
        'Y Coord [µm]': -1801.6,
        'Z Coord [µm]': 555.74,
        'Ti2 ZDrive [µm]': 555.74
    },
    {
        'Time [s]': 2.63795663,
        'Z-Series': 2.0,
        'Exposure Time [ms]': 100.0,
        'PFS Offset': 0,
        'PFS Status': 0,
        'X Coord [µm]': 31452.2,
        'Y Coord [µm]': -1801.6,
        'Z Coord [µm]': 556.74,
        'Ti2 ZDrive [µm]': 556.74
    }
]

# with `orient='list'`
{
    'Time [s]': array([1.32686654, 1.69089657, 2.04194662, 2.38194662, 2.63795663]),
    'Z-Series': array([-2., -1.,  0.,  1.,  2.]),
    'Exposure Time [ms]': array([100., 100., 100., 100., 100.]),
    'PFS Offset': array([0, 0, 0, 0, 0], dtype=int32),
    'PFS Status': array([0, 0, 0, 0, 0], dtype=int32),
    'X Coord [µm]': array([31452.2, 31452.2, 31452.2, 31452.2, 31452.2]),
    'Y Coord [µm]': array([-1801.6, -1801.6, -1801.6, -1801.6, -1801.6]),
    'Z Coord [µm]': array([552.74, 553.74, 554.74, 555.74, 556.74]),
    'Ti2 ZDrive [µm]': array([552.74, 553.74, 554.74, 555.74, 556.74])
}

# with `orient='dict'`
{
    'Time [s]': {0: 1.32686654, 1: 1.69089657, 2: 2.04194662, 3: 2.38194662, 4: 2.63795663},
    'Z-Series': {0: -2.0, 1: -1.0, 2: 0.0, 3: 1.0, 4: 2.0},
    'Exposure Time [ms]': {0: 100.0, 1: 100.0, 2: 100.0, 3: 100.0, 4: 100.0},
    'PFS Offset []': {0: 0, 1: 0, 2: 0, 3: 0, 4: 0},
    'PFS Status []': {0: 0, 1: 0, 2: 0, 3: 0, 4: 0},
    'X Coord [µm]': {0: 31452.2, 1: 31452.2, 2: 31452.2, 3: 31452.2, 4: 31452.2},
    'Y Coord [µm]': {0: -1801.6, 1: -1801.6, 2: -1801.6, 3: -1801.6, 4: -1801.6},
    'Z Coord [µm]': {0: 552.74, 1: 553.74, 2: 554.74, 3: 555.74, 4: 556.74},
    'Ti2 ZDrive [µm]': {0: 552.74, 1: 553.74, 2: 554.74, 3: 555.74, 4: 556.74}
}


```

You can pass the output of `events()` to `pandas.DataFrame`:

```python
In [1]: pd.DataFrame(nd2file.events())
Out[1]:
     Time [s]  Z-Series  Exposure Time [ms]  PFS Offset  PFS Status []  X Coord [µm]  Y Coord [µm]  Z Coord [µm]  Ti2 ZDrive [µm]
0    1.326867      -2.0               100.0              0              0       31452.2       -1801.6        552.74           552.74
1    1.690897      -1.0               100.0              0              0       31452.2       -1801.6        553.74           553.74
2    2.041947       0.0               100.0              0              0       31452.2       -1801.6        554.74           554.74
3    2.381947       1.0               100.0              0              0       31452.2       -1801.6        555.74           555.74
4    2.637957       2.0               100.0              0              0       31452.2       -1801.6        556.74           556.74
5    8.702229      -2.0               100.0              0              0       31452.2       -1801.6        552.70           552.70
6    9.036269      -1.0               100.0              0              0       31452.2       -1801.6        553.70           553.70
7    9.330319       0.0               100.0              0              0       31452.2       -1801.6        554.68           554.68
8    9.639349       1.0               100.0              0              0       31452.2       -1801.6        555.70           555.70
9    9.906369       2.0               100.0              0              0       31452.2       -1801.6        556.64           556.64
10  11.481439      -2.0               100.0              0              0       31452.2       -1801.6        552.68           552.68
11  11.796479      -1.0               100.0              0              0       31452.2       -1801.6        553.68           553.68
12  12.089479       0.0               100.0              0              0       31452.2       -1801.6        554.68           554.68
13  12.371539       1.0               100.0              0              0       31452.2       -1801.6        555.68           555.68
14  12.665469       2.0               100.0              0              0       31452.2       -1801.6        556.68           556.68

```

</details>

<details>

<summary><code>ome_metadata()</code></summary>

See the [ome-types documentation](https://ome-types.readthedocs.io/) for details on
the `OME` type returned by this method.

```python
In [1]: ome = nd2file.ome_metadata()

In [2]: print(ome)
OME(
    instruments=[<1 Instrument>],
    images=[<1 Image>],
    creator='nd2 v0.7.1'
)

In [3]: print(ome.to_xml())
<OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd"
     Creator="nd2 v0.7.1.dev2+g4ea166e.d20230709">
  <Instrument ID="Instrument:0">
    <Detector Model="Hamamatsu Dual C14440-20UP" SerialNumber="Hamamatsu Dual C14440-20UP" ID="Detector:0"/>
  </Instrument>
  <Image ID="Image:0" Name="test39">
    <AcquisitionDate>2023-07-08T09:30:55</AcquisitionDate>
    ...
```

</details>

## Contributing / Development

To test locally and contribute. Clone this repo, then:

```
pip install -e .[dev]
```

To download sample data:

```
pip install requests
python scripts/download_samples.py
```

then run tests:

```
pytest
```

(and feel free to open an issue if that doesn't work!)

## alternatives

Here are some other nd2 readers that I know of, though many
of them are unmaintained:

- [pims_nd2](https://github.com/soft-matter/pims_nd2) - _pims-based reader.
  ctypes wrapper around the v9.00 (2015) SDK_
- [nd2reader](https://github.com/rbnvrw/nd2reader) - _pims-based reader, using
  reverse-engineered file headers. mostly tested on files from NIS Elements
  4.30.02_
- [nd2file](https://github.com/csachs/nd2file) - _another pure-python, chunk map
  reader, unmaintained?_
- [pyND2SDK](https://github.com/aarpon/pyND2SDK) - _windows-only cython wrapper
  around the v9.00 (2015) SDK. not on PyPI_

The motivating factors for this library were:

- support for as many nd2 files as possible, with a large test suite
  an and emphasis on correctness
- pims-independent delayed reader based on dask
- axis-associated metadata via xarray

            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "nd2",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "",
    "author": "",
    "author_email": "Talley Lambert <talley.lambert@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/92/f4/8b3dd637f641a6f2a89b51249fd5f9e35394e27c4781758f4d0c37dbbaed/nd2-0.10.0.tar.gz",
    "platform": null,
    "description": "# nd2\n\n[![License](https://img.shields.io/pypi/l/nd2.svg?color=green)](https://github.com/tlambert03/nd2/raw/main/LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/nd2.svg?color=green)](https://pypi.org/project/nd2)\n[![Python Version](https://img.shields.io/pypi/pyversions/nd2.svg?color=green)](https://python.org)\n[![Tests](https://github.com/tlambert03/nd2/actions/workflows/ci.yml/badge.svg)](https://github.com/tlambert03/nd2/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/tlambert03/nd2/branch/main/graph/badge.svg)](https://codecov.io/gh/tlambert03/nd2)\n[![Benchmarks](https://img.shields.io/badge/\u23f1-codspeed-%23FF7B53)](https://codspeed.io/tlambert03/nd2)\n\n`.nd2` (Nikon NIS Elements) file reader.\n\nThis reader provides a pure python implementation of the Nikon ND2 SDK.\n\n> It _used_ to wrap the official SDK with Cython, but has since been completely\n> rewritten to be pure python (for performance, ease of distribution, and\n> maintenance) while retaining complete API parity with the official SDK.\n>\n> **Note:** This library is not affiliated with Nikon in any way, but we are\n> grateful for assistance from the SDK developers at Laboratory Imaging.\n\nFeatures good metadata retrieval, direct `to_dask` and `to_xarray` options\nfor lazy and/or annotated arrays, and output to OME-TIFF.\n\nThis library is tested against many nd2 files with the goal of maximizing\ncompatibility and data extraction. (If you find an nd2 file that fails in some\nway, please [open an issue](https://github.com/tlambert03/nd2/issues/new) with\nthe file!)\n\n### :book: [Documentation](https://tlambert03.github.io/nd2)\n\n## install\n\n```sh\npip install nd2\n```\n\nor from conda:\n\n```sh\nconda install -c conda-forge nd2\n```\n\n### Legacy nd2 file support\n\nLegacy nd2 (JPEG2000) files are also supported, but require `imagecodecs`. To\ninstall with support for these files use the `legacy` extra:\n\n```sh\npip install nd2[legacy]\n```\n\n### Faster XML parsing\n\nMuch of the metadata in the file stored as XML. If found in the environment,\n`nd2` will use [`lxml`](https://pypi.org/project/lxml/) which is much faster\nthan the built-in `xml` module. To install with support for `lxml` use:\n\n```sh\npip install nd2 lxml\n```\n\n## Usage and API\n\nFull API documentation is available at\n[https://tlambert03.github.io/nd2](https://tlambert03.github.io/nd2)\n\nQuick summary below:\n\n```python\nimport nd2\nimport numpy as np\n\nmy_array = nd2.imread('some_file.nd2')                          # read to numpy array\nmy_array = nd2.imread('some_file.nd2', dask=True)               # read to dask array\nmy_array = nd2.imread('some_file.nd2', xarray=True)             # read to xarray\nmy_array = nd2.imread('some_file.nd2', xarray=True, dask=True)  # read to dask-xarray\n\n# or open a file with nd2.ND2File\nf = nd2.ND2File('some_file.nd2')\n\n# (you can also use nd2.ND2File() as a context manager)\nwith nd2.ND2File('some_file.nd2') as ndfile:\n    print(ndfile.metadata)\n    ...\n\n\n# ATTRIBUTES:   # example output\nf.path          # 'some_file.nd2'\nf.shape         # (10, 2, 256, 256)\nf.ndim          # 4\nf.dtype         # np.dtype('uint16')\nf.size          # 1310720  (total voxel elements)\nf.sizes         # {'T': 10, 'C': 2, 'Y': 256, 'X': 256}\nf.is_rgb        # False (whether the file is rgb)\n                # if the file is RGB, `f.sizes` will have\n                # an additional {'S': 3} component\n\n# ARRAY OUTPUTS\nf.asarray()         # in-memory np.ndarray - or use np.asarray(f)\nf.to_dask()         # delayed dask.array.Array\nf.to_xarray()       # in-memory xarray.DataArray, with labeled axes/coords\nf.to_xarray(delayed=True)   # delayed xarray.DataArray\n\n# OME-TIFF OUTPUT (new in v0.10.0)\nf.write_tiff('output.ome.tif')  # write to ome-tiff file\n\n                    # see below for examples of these structures\n# METADATA          # returns instance of ...\nf.attributes        # nd2.structures.Attributes\nf.metadata          # nd2.structures.Metadata\nf.frame_metadata(0) # nd2.structures.FrameMetadata (frame-specific meta)\nf.experiment        # List[nd2.structures.ExpLoop]\nf.text_info         # dict of misc info\nf.voxel_size()      # VoxelSize(x=0.65, y=0.65, z=1.0)\n\nf.rois              # Dict[int, nd2.structures.ROI]\nf.binary_data       # any binary masks stored in the file.  See below.\nf.events()          # returns tabular \"Recorded Data\" view from in NIS Elements/Viewer\n                    # with info for each frame in the experiment.\n                    # output is passabled to pandas.DataFrame\n\nf.ome_metadata()    # returns metadata as an ome_types.OME object\n                    # (requires ome-types package)\n\n# allll the metadata we can find...\n# no attempt made to standardize or parse it\n# look in here if you're searching for metadata that isn't exposed in the above\n# but try not to rely on it, as it's not guaranteed to be stable\nf.unstructured_metadata()\n\nf.close()           # don't forget to close when not using a context manager!\nf.closed            # boolean, whether the file is closed\n```\n\n## Metadata structures\n\nThese follow the structure of the nikon SDK outputs (where relevant).\nHere are some example outputs\n\n<details>\n\n<summary><code>attributes</code></summary>\n\n```python\nAttributes(\n    bitsPerComponentInMemory=16,\n    bitsPerComponentSignificant=16,\n    componentCount=2,\n    heightPx=32,\n    pixelDataType='unsigned',\n    sequenceCount=60,\n    widthBytes=128,\n    widthPx=32,\n    compressionLevel=None,\n    compressionType=None,\n    tileHeightPx=None,\n    tileWidthPx=None,\n    channelCount=2\n)\n```\n\n</details>\n\n<details>\n\n<summary><code>metadata</code></summary>\n\n_Note: the `metadata` for legacy (JPEG2000) files will be a plain unstructured dict._\n\n```python\nMetadata(\n    contents=Contents(channelCount=2, frameCount=60),\n    channels=[\n        Channel(\n            channel=ChannelMeta(\n                name='Widefield Green',\n                index=0,\n                color=Color(r=91, g=255, b=0, a=1.0),\n                emissionLambdaNm=535.0,\n                excitationLambdaNm=None\n            ),\n            loops=LoopIndices(NETimeLoop=None, TimeLoop=0, XYPosLoop=1, ZStackLoop=2),\n            microscope=Microscope(\n                objectiveMagnification=10.0,\n                objectiveName='Plan Fluor 10x Ph1 DLL',\n                objectiveNumericalAperture=0.3,\n                zoomMagnification=1.0,\n                immersionRefractiveIndex=1.0,\n                projectiveMagnification=None,\n                pinholeDiameterUm=None,\n                modalityFlags=['fluorescence']\n            ),\n            volume=Volume(\n                axesCalibrated=[True, True, True],\n                axesCalibration=[0.652452890023035, 0.652452890023035, 1.0],\n                axesInterpretation=(\n                    <AxisInterpretation.distance: 'distance'>,\n                    <AxisInterpretation.distance: 'distance'>,\n                    <AxisInterpretation.distance: 'distance'>\n                ),\n                bitsPerComponentInMemory=16,\n                bitsPerComponentSignificant=16,\n                cameraTransformationMatrix=[-0.9998932296054086, -0.014612644841559427, 0.014612644841559427, -0.9998932296054086],\n                componentCount=1,\n                componentDataType='unsigned',\n                voxelCount=[32, 32, 5],\n                componentMaxima=[0.0],\n                componentMinima=[0.0],\n                pixelToStageTransformationMatrix=None\n            )\n        ),\n        Channel(\n            channel=ChannelMeta(\n                name='Widefield Red',\n                index=1,\n                color=Color(r=255, g=85, b=0, a=1.0),\n                emissionLambdaNm=620.0,\n                excitationLambdaNm=None\n            ),\n            loops=LoopIndices(NETimeLoop=None, TimeLoop=0, XYPosLoop=1, ZStackLoop=2),\n            microscope=Microscope(\n                objectiveMagnification=10.0,\n                objectiveName='Plan Fluor 10x Ph1 DLL',\n                objectiveNumericalAperture=0.3,\n                zoomMagnification=1.0,\n                immersionRefractiveIndex=1.0,\n                projectiveMagnification=None,\n                pinholeDiameterUm=None,\n                modalityFlags=['fluorescence']\n            ),\n            volume=Volume(\n                axesCalibrated=[True, True, True],\n                axesCalibration=[0.652452890023035, 0.652452890023035, 1.0],\n                axesInterpretation=(\n                    <AxisInterpretation.distance: 'distance'>,\n                    <AxisInterpretation.distance: 'distance'>,\n                    <AxisInterpretation.distance: 'distance'>\n                ),\n                bitsPerComponentInMemory=16,\n                bitsPerComponentSignificant=16,\n                cameraTransformationMatrix=[-0.9998932296054086, -0.014612644841559427, 0.014612644841559427, -0.9998932296054086],\n                componentCount=1,\n                componentDataType='unsigned',\n                voxelCount=[32, 32, 5],\n                componentMaxima=[0.0],\n                componentMinima=[0.0],\n                pixelToStageTransformationMatrix=None\n            )\n        )\n    ]\n)\n```\n\n</details>\n\n<details>\n\n<summary><code>experiment</code></summary>\n\n```python\n[\n    TimeLoop(\n        count=3,\n        nestingLevel=0,\n        parameters=TimeLoopParams(\n            startMs=0.0,\n            periodMs=1.0,\n            durationMs=0.0,\n            periodDiff=PeriodDiff(avg=16278.339965820312, max=16411.849853515625, min=16144.830078125)\n        ),\n        type='TimeLoop'\n    ),\n    XYPosLoop(\n        count=4,\n        nestingLevel=1,\n        parameters=XYPosLoopParams(\n            isSettingZ=True,\n            points=[\n                Position(stagePositionUm=[26950.2, -1801.6000000000001, 498.46000000000004], pfsOffset=None, name=None),\n                Position(stagePositionUm=[31452.2, -1801.6000000000001, 670.7], pfsOffset=None, name=None),\n                Position(stagePositionUm=[35234.3, 2116.4, 664.08], pfsOffset=None, name=None),\n                Position(stagePositionUm=[40642.9, -3585.1000000000004, 555.12], pfsOffset=None, name=None)\n            ]\n        ),\n        type='XYPosLoop'\n    ),\n    ZStackLoop(count=5, nestingLevel=2, parameters=ZStackLoopParams(homeIndex=2, stepUm=1.0, bottomToTop=True, deviceName='Ti2 ZDrive'), type='ZStackLoop')\n]\n```\n\n</details>\n\n<details>\n\n<summary><code>rois</code></summary>\n\nROIs found in the metadata are available at `ND2File.rois`, which is a\n`dict` of `nd2.structures.ROI` objects, keyed by the ROI ID:\n\n```python\n{\n    1: ROI(\n        id=1,\n        info=RoiInfo(\n            shapeType=<RoiShapeType.Rectangle: 3>,\n            interpType=<InterpType.StimulationROI: 4>,\n            cookie=1,\n            color=255,\n            label='',\n            stimulationGroup=0,\n            scope=1,\n            appData=0,\n            multiFrame=False,\n            locked=False,\n            compCount=2,\n            bpc=16,\n            autodetected=False,\n            gradientStimulation=False,\n            gradientStimulationBitDepth=0,\n            gradientStimulationLo=0.0,\n            gradientStimulationHi=0.0\n        ),\n        guid='{87190352-9B32-46E4-8297-C46621C1E1EF}',\n        animParams=[\n            AnimParam(\n                timeMs=0.0,\n                enabled=1,\n                centerX=-0.4228425369685782,\n                centerY=-0.5194951478743071,\n                centerZ=0.0,\n                rotationZ=0.0,\n                boxShape=BoxShape(\n                    sizeX=0.21256931608133062,\n                    sizeY=0.21441774491682075,\n                    sizeZ=0.0\n                ),\n                extrudedShape=ExtrudedShape(sizeZ=0, basePoints=[])\n            )\n        ]\n    ),\n    ...\n}\n```\n\n</details>\n\n<details>\n\n<summary><code>text_info</code></summary>\n\n```python\n{\n    'capturing': 'Flash4.0, SN:101412\\r\\nSample 1:\\r\\n  Exposure: 100 ms\\r\\n  Binning: 1x1\\r\\n  Scan Mode: Fast\\r\\nSample 2:\\r\\n  Exposure: 100 ms\\r\\n  Binning: 1x1\\r\\n  Scan Mode: Fast',\n    'date': '9/28/2021  9:41:27 AM',\n    'description': 'Metadata:\\r\\nDimensions: T(3) x XY(4) x \u03bb(2) x Z(5)\\r\\nCamera Name: Flash4.0, SN:101412\\r\\nNumerical Aperture: 0.3\\r\\nRefractive Index: 1\\r\\nNumber of Picture Planes: 2\\r\\nPlane #1:\\r\\n Name: Widefield Green\\r\\n Component Count: 1\\r\\n Modality: Widefield Fluorescence\\r\\n Camera Settings:   Exposure: 100 ms\\r\\n  Binning: 1x1\\r\\n  Scan Mode: Fast\\r\\n Microscope Settings:   Nikon Ti2, FilterChanger(Turret-Lo): 3 (FITC)\\r\\n  Nikon Ti2, Shutter(FL-Lo): Open\\r\\n  Nikon Ti2, Shutter(DIA LED): Closed\\r\\n  Nikon Ti2, Illuminator(DIA): Off\\r\\n  Nikon Ti2, Illuminator(DIA) Iris intensity: 3.0\\r\\n  Analyzer Slider: Extracted\\r\\n  Analyzer Cube: Extracted\\r\\n  Condenser: 1 (Shutter)\\r\\n  PFS, state: On\\r\\n  PFS, offset: 7959\\r\\n  PFS, mirror: Inserted\\r\\n  PFS, Dish Type: Glass\\r\\n  Zoom: 1.00x\\r\\n  Sola, Shutter(Sola): Active\\r\\n  Sola, Illuminator(Sola) Voltage: 100.0\\r\\nPlane #2:\\r\\n Name: Widefield Red\\r\\n Component Count: 1\\r\\n Modality: Widefield Fluorescence\\r\\n Camera Settings:   Exposure: 100 ms\\r\\n  Binning: 1x1\\r\\n  Scan Mode: Fast\\r\\n Microscope Settings:   Nikon Ti2, FilterChanger(Turret-Lo): 4 (TRITC)\\r\\n  Nikon Ti2, Shutter(FL-Lo): Open\\r\\n  Nikon Ti2, Shutter(DIA LED): Closed\\r\\n  Nikon Ti2, Illuminator(DIA): Off\\r\\n  Nikon Ti2, Illuminator(DIA) Iris intensity: 1.5\\r\\n  Analyzer Slider: Extracted\\r\\n  Analyzer Cube: Extracted\\r\\n  Condenser: 1 (Shutter)\\r\\n  PFS, state: On\\r\\n  PFS, offset: 7959\\r\\n  PFS, mirror: Inserted\\r\\n  PFS, Dish Type: Glass\\r\\n  Zoom: 1.00x\\r\\n  Sola, Shutter(Sola): Active\\r\\n  Sola, Illuminator(Sola) Voltage: 100.0\\r\\nTime Loop: 3\\r\\n- Equidistant (Period 1 ms)\\r\\nZ Stack Loop: 5\\r\\n- Step: 1 \u00b5m\\r\\n- Device: Ti2 ZDrive',\n    'optics': 'Plan Fluor 10x Ph1 DLL'\n}\n```\n\n</details>\n\n<details>\n\n<summary><code>binary_data</code></summary>\n\nThis property returns an `nd2.BinaryLayers` object representing all of the\nbinary masks in the nd2 file.\n\nA `nd2.BinaryLayers` object is a sequence of individual `nd2.BinaryLayer`\nobjects (one for each binary layer found in the file). Each `BinaryLayer` in\nthe sequence is a named tuple that has, among other things, a `name` attribute,\nand a `data` attribute that is list of numpy arrays (one for each frame in the\nexperiment) or `None` if the binary layer had no data in that frame.\n\nThe most common use case will be to cast either the entire `BinaryLayers` object\nor an individual `BinaryLayer` to a `numpy.ndarray`:\n\n```python\n>>> import nd2\n>>> nd2file = nd2.ND2File('path/to/file.nd2')\n>>> binary_layers = nd2file.binary_data\n\n# The output array will have shape\n# (n_binary_layers, *coord_shape, *frame_shape).\n>>> np.asarray(binary_layers)\n```\n\nFor example, if the data in the nd2 file has shape `(nT, nZ, nC, nY, nX)`, and\nthere are 4 binary layers, then the output of `np.asarray(nd2file.binary_data)` will\nhave shape `(4, nT, nZ, nY, nX)`. (Note that the `nC` dimension is not present\nin the output array, and the binary layers are always in the first axis).\n\nYou can also cast an individual `BinaryLayer` to a numpy array:\n\n```python\n>>> binary_layer = binary_layers[0]\n>>> np.asarray(binary_layer)\n```\n\n</details>\n\n<details>\n\n<summary><code>events()</code></summary>\n\nThis property returns the tabular data reported in the `Image Properties >\nRecorded Data` tab of the NIS Viewer.\n\n(There will be a column for each tag in the `CustomDataV2_0` section of\n`custom_data` above, as well as any additional events found in the metadata)\n\nThe format of the return type data is controlled by the `orient` argument:\n\n- `'records'` : list of dicts - `[{column -> value}, ...]` (default)\n- `'dict'` : dict of dicts - `{column -> {index -> value}, ...}`\n- `'list'` : dict of lists - `{column -> [value, ...]}`\n\nNot every column header appears in every event, so when `orient` is either\n`'dict'` or `'list'`, `float('nan')` will be inserted to maintain a consistent\nlength for each column.\n\n```python\n\n# with `orient='records'` (DEFAULT)\n[\n    {\n        'Time [s]': 1.32686654,\n        'Z-Series': -2.0,\n        'Exposure Time [ms]': 100.0,\n        'PFS Offset': 0,\n        'PFS Status': 0,\n        'X Coord [\u00b5m]': 31452.2,\n        'Y Coord [\u00b5m]': -1801.6,\n        'Z Coord [\u00b5m]': 552.74,\n        'Ti2 ZDrive [\u00b5m]': 552.74\n    },\n    {\n        'Time [s]': 1.69089657,\n        'Z-Series': -1.0,\n        'Exposure Time [ms]': 100.0,\n        'PFS Offset': 0,\n        'PFS Status': 0,\n        'X Coord [\u00b5m]': 31452.2,\n        'Y Coord [\u00b5m]': -1801.6,\n        'Z Coord [\u00b5m]': 553.74,\n        'Ti2 ZDrive [\u00b5m]': 553.74\n    },\n    {\n        'Time [s]': 2.04194662,\n        'Z-Series': 0.0,\n        'Exposure Time [ms]': 100.0,\n        'PFS Offset': 0,\n        'PFS Status': 0,\n        'X Coord [\u00b5m]': 31452.2,\n        'Y Coord [\u00b5m]': -1801.6,\n        'Z Coord [\u00b5m]': 554.74,\n        'Ti2 ZDrive [\u00b5m]': 554.74\n    },\n    {\n        'Time [s]': 2.38194662,\n        'Z-Series': 1.0,\n        'Exposure Time [ms]': 100.0,\n        'PFS Offset': 0,\n        'PFS Status': 0,\n        'X Coord [\u00b5m]': 31452.2,\n        'Y Coord [\u00b5m]': -1801.6,\n        'Z Coord [\u00b5m]': 555.74,\n        'Ti2 ZDrive [\u00b5m]': 555.74\n    },\n    {\n        'Time [s]': 2.63795663,\n        'Z-Series': 2.0,\n        'Exposure Time [ms]': 100.0,\n        'PFS Offset': 0,\n        'PFS Status': 0,\n        'X Coord [\u00b5m]': 31452.2,\n        'Y Coord [\u00b5m]': -1801.6,\n        'Z Coord [\u00b5m]': 556.74,\n        'Ti2 ZDrive [\u00b5m]': 556.74\n    }\n]\n\n# with `orient='list'`\n{\n    'Time [s]': array([1.32686654, 1.69089657, 2.04194662, 2.38194662, 2.63795663]),\n    'Z-Series': array([-2., -1.,  0.,  1.,  2.]),\n    'Exposure Time [ms]': array([100., 100., 100., 100., 100.]),\n    'PFS Offset': array([0, 0, 0, 0, 0], dtype=int32),\n    'PFS Status': array([0, 0, 0, 0, 0], dtype=int32),\n    'X Coord [\u00b5m]': array([31452.2, 31452.2, 31452.2, 31452.2, 31452.2]),\n    'Y Coord [\u00b5m]': array([-1801.6, -1801.6, -1801.6, -1801.6, -1801.6]),\n    'Z Coord [\u00b5m]': array([552.74, 553.74, 554.74, 555.74, 556.74]),\n    'Ti2 ZDrive [\u00b5m]': array([552.74, 553.74, 554.74, 555.74, 556.74])\n}\n\n# with `orient='dict'`\n{\n    'Time [s]': {0: 1.32686654, 1: 1.69089657, 2: 2.04194662, 3: 2.38194662, 4: 2.63795663},\n    'Z-Series': {0: -2.0, 1: -1.0, 2: 0.0, 3: 1.0, 4: 2.0},\n    'Exposure Time [ms]': {0: 100.0, 1: 100.0, 2: 100.0, 3: 100.0, 4: 100.0},\n    'PFS Offset []': {0: 0, 1: 0, 2: 0, 3: 0, 4: 0},\n    'PFS Status []': {0: 0, 1: 0, 2: 0, 3: 0, 4: 0},\n    'X Coord [\u00b5m]': {0: 31452.2, 1: 31452.2, 2: 31452.2, 3: 31452.2, 4: 31452.2},\n    'Y Coord [\u00b5m]': {0: -1801.6, 1: -1801.6, 2: -1801.6, 3: -1801.6, 4: -1801.6},\n    'Z Coord [\u00b5m]': {0: 552.74, 1: 553.74, 2: 554.74, 3: 555.74, 4: 556.74},\n    'Ti2 ZDrive [\u00b5m]': {0: 552.74, 1: 553.74, 2: 554.74, 3: 555.74, 4: 556.74}\n}\n\n\n```\n\nYou can pass the output of `events()` to `pandas.DataFrame`:\n\n```python\nIn [1]: pd.DataFrame(nd2file.events())\nOut[1]:\n     Time [s]  Z-Series  Exposure Time [ms]  PFS Offset  PFS Status []  X Coord [\u00b5m]  Y Coord [\u00b5m]  Z Coord [\u00b5m]  Ti2 ZDrive [\u00b5m]\n0    1.326867      -2.0               100.0              0              0       31452.2       -1801.6        552.74           552.74\n1    1.690897      -1.0               100.0              0              0       31452.2       -1801.6        553.74           553.74\n2    2.041947       0.0               100.0              0              0       31452.2       -1801.6        554.74           554.74\n3    2.381947       1.0               100.0              0              0       31452.2       -1801.6        555.74           555.74\n4    2.637957       2.0               100.0              0              0       31452.2       -1801.6        556.74           556.74\n5    8.702229      -2.0               100.0              0              0       31452.2       -1801.6        552.70           552.70\n6    9.036269      -1.0               100.0              0              0       31452.2       -1801.6        553.70           553.70\n7    9.330319       0.0               100.0              0              0       31452.2       -1801.6        554.68           554.68\n8    9.639349       1.0               100.0              0              0       31452.2       -1801.6        555.70           555.70\n9    9.906369       2.0               100.0              0              0       31452.2       -1801.6        556.64           556.64\n10  11.481439      -2.0               100.0              0              0       31452.2       -1801.6        552.68           552.68\n11  11.796479      -1.0               100.0              0              0       31452.2       -1801.6        553.68           553.68\n12  12.089479       0.0               100.0              0              0       31452.2       -1801.6        554.68           554.68\n13  12.371539       1.0               100.0              0              0       31452.2       -1801.6        555.68           555.68\n14  12.665469       2.0               100.0              0              0       31452.2       -1801.6        556.68           556.68\n\n```\n\n</details>\n\n<details>\n\n<summary><code>ome_metadata()</code></summary>\n\nSee the [ome-types documentation](https://ome-types.readthedocs.io/) for details on\nthe `OME` type returned by this method.\n\n```python\nIn [1]: ome = nd2file.ome_metadata()\n\nIn [2]: print(ome)\nOME(\n    instruments=[<1 Instrument>],\n    images=[<1 Image>],\n    creator='nd2 v0.7.1'\n)\n\nIn [3]: print(ome.to_xml())\n<OME xmlns=\"http://www.openmicroscopy.org/Schemas/OME/2016-06\"\n     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n     xsi:schemaLocation=\"http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd\"\n     Creator=\"nd2 v0.7.1.dev2+g4ea166e.d20230709\">\n  <Instrument ID=\"Instrument:0\">\n    <Detector Model=\"Hamamatsu Dual C14440-20UP\" SerialNumber=\"Hamamatsu Dual C14440-20UP\" ID=\"Detector:0\"/>\n  </Instrument>\n  <Image ID=\"Image:0\" Name=\"test39\">\n    <AcquisitionDate>2023-07-08T09:30:55</AcquisitionDate>\n    ...\n```\n\n</details>\n\n## Contributing / Development\n\nTo test locally and contribute. Clone this repo, then:\n\n```\npip install -e .[dev]\n```\n\nTo download sample data:\n\n```\npip install requests\npython scripts/download_samples.py\n```\n\nthen run tests:\n\n```\npytest\n```\n\n(and feel free to open an issue if that doesn't work!)\n\n## alternatives\n\nHere are some other nd2 readers that I know of, though many\nof them are unmaintained:\n\n- [pims_nd2](https://github.com/soft-matter/pims_nd2) - _pims-based reader.\n  ctypes wrapper around the v9.00 (2015) SDK_\n- [nd2reader](https://github.com/rbnvrw/nd2reader) - _pims-based reader, using\n  reverse-engineered file headers. mostly tested on files from NIS Elements\n  4.30.02_\n- [nd2file](https://github.com/csachs/nd2file) - _another pure-python, chunk map\n  reader, unmaintained?_\n- [pyND2SDK](https://github.com/aarpon/pyND2SDK) - _windows-only cython wrapper\n  around the v9.00 (2015) SDK. not on PyPI_\n\nThe motivating factors for this library were:\n\n- support for as many nd2 files as possible, with a large test suite\n  an and emphasis on correctness\n- pims-independent delayed reader based on dask\n- axis-associated metadata via xarray\n",
    "bugtrack_url": null,
    "license": "BSD 3-Clause License",
    "summary": "Yet another nd2 (Nikon NIS Elements) file reader",
    "version": "0.10.0",
    "project_urls": {
        "changelog": "https://github.com/tlambert03/nd2/blob/main/CHANGELOG.md",
        "documentation": "https://tlambert03.github.io/nd2/",
        "homepage": "https://github.com/tlambert03/nd2",
        "repository": "https://github.com/tlambert03/nd2"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a8c2ee754be8c3df717490fbe042a16a41a0795851714cb45717336924f499d8",
                "md5": "ca34adcc3e1c1a6317a16821ad1be177",
                "sha256": "ca495e4eb6f34ca0a7f47b75918c2bfd333ea6af5c0f059fb67d077f409cccb3"
            },
            "downloads": -1,
            "filename": "nd2-0.10.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ca34adcc3e1c1a6317a16821ad1be177",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 81046,
            "upload_time": "2024-03-17T19:42:47",
            "upload_time_iso_8601": "2024-03-17T19:42:47.266744Z",
            "url": "https://files.pythonhosted.org/packages/a8/c2/ee754be8c3df717490fbe042a16a41a0795851714cb45717336924f499d8/nd2-0.10.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "92f48b3dd637f641a6f2a89b51249fd5f9e35394e27c4781758f4d0c37dbbaed",
                "md5": "95672ef842371da11448454bc81d27cb",
                "sha256": "ba7bb780c8e33308f795a9a3f7c8b6b927c64b9d0742536ceedd3e8ba046ebb3"
            },
            "downloads": -1,
            "filename": "nd2-0.10.0.tar.gz",
            "has_sig": false,
            "md5_digest": "95672ef842371da11448454bc81d27cb",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 645332,
            "upload_time": "2024-03-17T19:42:49",
            "upload_time_iso_8601": "2024-03-17T19:42:49.545476Z",
            "url": "https://files.pythonhosted.org/packages/92/f4/8b3dd637f641a6f2a89b51249fd5f9e35394e27c4781758f4d0c37dbbaed/nd2-0.10.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-17 19:42:49",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "tlambert03",
    "github_project": "nd2",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "nd2"
}
        
Elapsed time: 0.22846s