Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nikon nd2 file causing error #488

Closed
toloudis opened this issue Apr 11, 2023 · 3 comments · Fixed by #498
Closed

Nikon nd2 file causing error #488

toloudis opened this issue Apr 11, 2023 · 3 comments · Fixed by #498
Assignees

Comments

@toloudis
Copy link
Collaborator

This is a fairly recent error experienced by @cfrick13 :

Here is the traceback error I got .
I put the file on isilon for you to check out along with others that read just fine
"\allen\aics\assay-dev\users\Frick\forOthersTemp\forDan_Nikon_Files\H2B 4xx water LWD timelapse 001.nd2"
(other files are in the same folder)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[38], line 2
      1 reader2 = AICSImage(r"C:\Users\chris.frick\Pictures\NikonDemoChris\H2B 4xx water LWD timelapse 001.nd2")
----> 2 out = reader2.get_image_data()
      3 out.shape

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\aics_image.py:711, in AICSImage.get_image_data(self, dimension_order_out, **kwargs)
    709 # If no out orientation, simply return current data as dask array
    710 if dimension_order_out is None:
--> 711     return self.data
    713 # Transform and return
    714 return transforms.reshape_data(
    715     data=self.data,
    716     given_dims=self.dims.order,
    717     return_dims=dimension_order_out,
    718     **kwargs,
    719 )

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\aics_image.py:529, in AICSImage.data(self)
    516 @property
    517 def data(self) -> np.ndarray:
    518     """
    519     Returns
    520     -------
   (...)
    527     Recommended to use `dask_data` for large mosaic images.
    528     """
--> 529     return self.xarray_data.data

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\aics_image.py:469, in AICSImage.xarray_data(self)
    453 """
    454 Returns
    455 -------
   (...)
    462 Recommended to use `xarray_dask_data` for large mosaic images.
    463 """
    464 if self._xarray_data is None:
    465     if (
    466         # Does the user want to get stitched mosaic
    467         self._reconstruct_mosaic
    468         # Does the data have a tile dim
--> 469         and dimensions.DimensionNames.MosaicTile in self.reader.dims.order
    470     ):
    471         try:
    472             self._xarray_data = (
    473                 self._transform_data_array_to_aics_image_standard(
    474                     self.reader.mosaic_xarray_data
    475                 )
    476             )

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\readers\reader.py:532, in Reader.dims(self)
    525 """
    526 Returns
    527 -------
    528 dims: Dimensions
    529     Object with the paired dimension names and their sizes.
    530 """
    531 if self._dims is None:
--> 532     self._dims = Dimensions(dims=self.xarray_dask_data.dims, shape=self.shape)
    534 return self._dims

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\readers\reader.py:359, in Reader.xarray_dask_data(self)
    352 """
    353 Returns
    354 -------
    355 xarray_dask_data: xr.DataArray
    356     The delayed image and metadata as an annotated data array.
    357 """
    358 if self._xarray_dask_data is None:
--> 359     self._xarray_dask_data = self._read_delayed()
    361 return self._xarray_dask_data

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\readers\nd2_reader.py:72, in ND2Reader._read_delayed(self)
     71 def _read_delayed(self) -> xr.DataArray:
---> 72     return self._xarr_reformat(delayed=True)

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\aicsimageio\readers\nd2_reader.py:79, in ND2Reader._xarr_reformat(self, delayed)
     77 def _xarr_reformat(self, delayed: bool) -> xr.DataArray:
     78     with nd2.ND2File(self._path) as rdr:
---> 79         xarr = rdr.to_xarray(
     80             delayed=delayed, squeeze=False, position=self.current_scene_index
     81         )
     82         xarr.attrs[constants.METADATA_UNPROCESSED] = xarr.attrs.pop("metadata")
     83     return xarr.isel({nd2.AXIS.POSITION: 0})

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\nd2\nd2file.py:578, in ND2File.to_xarray(self, delayed, squeeze, position, copy)
    576 data = self.to_dask(copy=copy) if delayed else self.asarray(position)
    577 dims = list(self.sizes)
--> 578 coords = self._expand_coords(squeeze)
    579 if not squeeze:
    580     for missing_dim in set(coords).difference(dims):

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\nd2\nd2file.py:663, in ND2File._expand_coords(self, squeeze)
    654 dx, dy, dz = self.voxel_size()
    656 coords: dict[str, Sized] = {
    657     AXIS.Y: np.arange(self.attributes.heightPx) * dy,
    658     AXIS.X: np.arange(self.attributes.widthPx or 1) * dx,
    659     AXIS.CHANNEL: self._channel_names,
    660     AXIS.POSITION: ["XYPos:0"],  # maybe overwritten below
    661 }
--> 663 for c in self.experiment:
    664     if squeeze and c.count <= 1:
    665         continue

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\functools.py:993, in cached_property.__get__(self, instance, owner)
    991 val = cache.get(self.attrname, _NOT_FOUND)
    992 if val is _NOT_FOUND:
--> 993     val = self.func(instance)
    994     try:
    995         cache[self.attrname] = val

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\nd2\nd2file.py:198, in ND2File.experiment(self)
    195 @cached_property
    196 def experiment(self) -> list[ExpLoop]:
    197     """Loop information for each nd axis."""
--> 198     exp = self._rdr.experiment()
    200     # https://github.com/tlambert03/nd2/issues/78
    201     # the SDK doesn't always do a good job of pulling position names from metadata
    202     # here, we try to extract it manually.  Might be error prone, so currently
    203     # we just ignore errors.
    204     if not self.is_legacy and IMG_METADATA in self._rdr._meta_map:  # type: ignore

File src\nd2\_sdk\latest.pyx:170, in nd2._sdk.latest.ND2Reader.experiment()

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\nd2\structures.py:75, in _Loop.create(cls, obj)
     73 @classmethod
     74 def create(cls, obj: dict) -> ExpLoop:
---> 75     return globals()[obj["type"]](**obj)

File <string>:7, in __init__(self, count, nestingLevel, parameters, type)

File ~\AppData\Local\Continuum\anaconda3\envs\nikon\lib\site-packages\nd2\structures.py:88, in TimeLoop.__post_init__(self)
     86 def __post_init__(self):
     87     if isinstance(self.parameters, dict):
---> 88         self.parameters = TimeLoopParams(**self.parameters)

TypeError: __init__() missing 1 required positional argument: 'periodDiff'
@SeanLeRoy SeanLeRoy self-assigned this Apr 17, 2023
@SeanLeRoy
Copy link
Collaborator

SeanLeRoy commented May 17, 2023

Hi @tlambert03 I am hoping you might have some insight on this issue. It seems that periodDiff is not available in the place the nd2 package is looking for it (as you can likely see in the stacktrace above). I am not incredibly familiar with Nikon images, would making periodDiff optional (like in this fork/branch I created of nd2) be a bad move? Doing so seems to let the image be read as normal as far as I can tell, but I may be missing something.

@tlambert03
Copy link
Collaborator

tlambert03 commented May 17, 2023

hey @SeanLeRoy, sorry about that. Would actually be curious to see the file if you can share (haven't yet actually come across an nd2 file that didn't have that and would be curious to see more). In any case, yes a fix would be most welcome, but I think I would do it a bit lower down in the file, setting it to optional on the dataclass like this:

@dataclass
class TimeLoopParams:
    startMs: float
    periodMs: float
    durationMs: float
    periodDiff: PeriodDiff | None = None 

... and then see if all the tests pass (including the typing tests, that might show whether we've made assumptions elsewhere that it will be there)

edit: or possible make it a default_factory

@SeanLeRoy
Copy link
Collaborator

@tlambert03 I tried that originally, but because Period (here) inherits the fields from TimeLoopParams it gives the below error since the count field (which is required) would be following a default field periodDiff (both in the case where a default is directly applied like in your snippet and in the field(default_factory=PeriodDiff) case unfortunately. I'll run the tests and see what happens, let me know if there is anything else you'd like me to try.

You can check out the file here

Traceback (most recent call last):
  File "/Users/seanm/code/aicsimageio/488-nikon-nd2-error.py", line 1, in <module>
    from aicsimageio import AICSImage
  File "/Users/seanm/code/aicsimageio/aicsimageio/__init__.py", line 6, in <module>
    from .aics_image import AICSImage  # noqa: F401
  File "/Users/seanm/code/aicsimageio/aicsimageio/aics_image.py", line 36, in <module>
    class AICSImage(ImageContainer):
  File "/Users/seanm/code/aicsimageio/aicsimageio/aics_image.py", line 139, in AICSImage
    ReaderClass = _load_reader(reader_path)
  File "/Users/seanm/code/aicsimageio/aicsimageio/aics_image.py", line 32, in _load_reader
    loaded_mod = importlib.import_module(module_path)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/Users/seanm/code/aicsimageio/aicsimageio/readers/nd2_reader.py", line 17, in <module>
    import nd2
  File "/Users/seanm/code/aicsimageio/venv/lib/python3.10/site-packages/nd2/__init__.py", line 21, in <module>
    from . import structures
  File "/Users/seanm/code/aicsimageio/venv/lib/python3.10/site-packages/nd2/structures.py", line 137, in <module>
    class Period(TimeLoopParams):
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1185, in dataclass
    return wrap(cls)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1176, in wrap
    return _process_class(cls, init, repr, eq, order, unsafe_hash,
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 1025, in _process_class
    _init_fn(all_init_fields,
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/dataclasses.py", line 546, in _init_fn
    raise TypeError(f'non-default argument {f.name!r} '
TypeError: non-default argument 'count' follows default argument

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants