diff --git a/src/nd2/_parse/_parse.py b/src/nd2/_parse/_parse.py index abe8e7e..913507c 100644 --- a/src/nd2/_parse/_parse.py +++ b/src/nd2/_parse/_parse.py @@ -187,7 +187,7 @@ def _parse_ne_time_loop(item: NETimeLoopPars) -> strct.NETimeLoop: def load_experiment( - level: int, src: RawExperimentDict, dest: list[ExpLoop] | None = None + src: RawExperimentDict, level: int = 0, dest: list[ExpLoop] | None = None ) -> list[ExpLoop]: """Parse the "ImageMetadata[LV]!" section of an nd2 file.""" dest = dest or [] @@ -208,10 +208,24 @@ def load_experiment( if prev.count < loop.count: dest[-1] = loop + # FIXME: + # hack for file in https://github.com/tlambert03/nd2/issues/190 + # there is a better fix, but this is a very rare case + loop_params = src.get("uLoopPars", {}) + if "pSubLoops" in loop_params: + loop_params = cast("NETimeLoopPars", loop_params) + subloops = loop_params["pSubLoops"] + i0 = "i0000000000" + if i0 in subloops: + subnext = subloops[i0]["ppNextLevelEx"] + if i0 in subnext: + experiment = subnext[i0]["SLxExperiment"] + dest.extend(load_experiment(experiment)) + next_level_src = src.get("ppNextLevelEx") if next_level_src: for item in next_level_src.values(): - dest = load_experiment(level + 1, item, dest) + dest = load_experiment(item, level + 1, dest) return dest diff --git a/src/nd2/_sdk_types.py b/src/nd2/_sdk_types.py index 5587984..0ea101b 100644 --- a/src/nd2/_sdk_types.py +++ b/src/nd2/_sdk_types.py @@ -157,6 +157,14 @@ class SpectLoopPars(TypedDict): pPlanes: PicturePlanesDict uiCount: NotRequired[int] + class SLxExperimentDict(TypedDict): + SLxExperiment: RawExperimentDict + + class SubLoopDict(TypedDict): + uiNextLevelCount: int + # this is a dict of keys 'i0000000000', 'i0000000001', etc. + ppNextLevelEx: dict[str, SLxExperimentDict] + class NETimeLoopPars(TypedDict): # keys are '_00' or 'i0000000000' ... pPeriod: dict[str, PeriodDict] @@ -167,6 +175,8 @@ class NETimeLoopPars(TypedDict): uiPeriodCount: int wsCommandAfterPeriod: str wsCommandBeforePeriod: str + # this is a dict of keys 'i0000000000', 'i0000000001', etc. + pSubLoops: NotRequired[dict[str, SubLoopDict]] LoopParsDict: TypeAlias = Union[ TimeLoopPars, XYPosLoopPars, ZStackLoopPars, SpectLoopPars, NETimeLoopPars diff --git a/src/nd2/readers/_modern/modern_reader.py b/src/nd2/readers/_modern/modern_reader.py index cd03035..73d4721 100644 --- a/src/nd2/readers/_modern/modern_reader.py +++ b/src/nd2/readers/_modern/modern_reader.py @@ -219,7 +219,7 @@ def experiment(self) -> list[structures.ExpLoop]: exp = self._decode_chunk(k, strip_prefix=False) exp = exp.get("SLxExperiment", exp) # for v3 only self._raw_experiment = cast("RawExperimentDict", exp) - self._experiment = load_experiment(0, self._raw_experiment) + self._experiment = load_experiment(self._raw_experiment) return self._experiment def loop_indices(self) -> list[dict[str, int]]: