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

Animation crashes if hidden Labels layer present in viewer #184

Closed
bentaculum opened this issue Nov 2, 2023 · 4 comments · Fixed by #181
Closed

Animation crashes if hidden Labels layer present in viewer #184

bentaculum opened this issue Nov 2, 2023 · 4 comments · Fixed by #181
Labels
bug Something isn't working

Comments

@bentaculum
Copy link

Thanks a lot for developing this nice plugin :)

I just noticed the following: If there is a hidden Labels layer present in the viewer, Animation.animate crashes while trying to update some layer thumbnail. This looks like a bug to me, it didn't occur to me that I'm not allowed to have some hidden layers floating around when creating an animation.

Here is a minimal example. The first animation is written fine, after adding the Labels layer with visible=False I get the error below.

import numpy as np
import napari
from napari_animation import Animation


size = (10, 20, 20, 20)
x = np.random.randint(0, 2, size=size)

viewer = napari.Viewer()
viewer.dims.ndisplay = 3


def animate(v, name):
    v.dims.set_current_step(0, 0)
    a = Animation(v)
    a.capture_keyframe()
    v.dims.set_current_step(0, v.layers[0].data.shape[0] - 1)
    a.capture_keyframe()
    a.animate(f"{name}.mp4", canvas_only=True)

viewer.add_labels(x)
animate(viewer, "only_visible_labels")

viewer.add_labels(x, visible=False)
animate(viewer, "hidden_labels")

Error message:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
File ~/code/track_transformer/scripts/animation_bug.py:25
     22 animate(viewer, "only_visible_labels")
     24 viewer.add_labels(x, visible=False)
---> 25 animate(viewer, "hidden_labels")

File ~/code/track_transformer/scripts/animation_bug.py:19, in animate(v, name)
     17 v.dims.set_current_step(0, v.layers[0].data.shape[0] - 1)
     18 a.capture_keyframe()
---> 19 a.animate(f"{name}.mp4", canvas_only=True)

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari_animation/animation.py:225, in Animation.animate(self, filename, fps, quality, format, canvas_only, scale_factor)
    223 sleep(0.05)
    224 with tqdm(total=n_frames) as pbar:
--> 225     for frame_index, image in enumerate(frame_generator):
    226         if save_as_folder is True:
    227             frame_filename = (
    228                 folder_path / f"{file_path.stem}_{frame_index:06d}.png"
    229             )

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari_animation/frame_sequence.py:152, in FrameSequence.iter_frames(self, viewer, canvas_only, scale_factor)
    150 """Iterate over interpolated viewer states, and yield rendered frames."""
    151 for i, state in enumerate(self):
--> 152     frame = state.render(viewer, canvas_only=canvas_only)
    153     if scale_factor not in (None, 1):
    154         from scipy import ndimage as ndi

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari_animation/viewer_state.py:79, in ViewerState.render(self, viewer, canvas_only)
     61 def render(
     62     self, viewer: napari.viewer.Viewer, canvas_only: bool = True
     63 ) -> np.ndarray:
     64     """Render this ViewerState to an image.
     65 
     66     Parameters
   (...)
     77         An RGBA image of shape (h, w, 4).
     78     """
---> 79     self.apply(viewer)
     80     return viewer.screenshot(canvas_only=canvas_only, flash=False)

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari_animation/viewer_state.py:59, in ViewerState.apply(self, viewer)
     57 # Only set if value differs to avoid expensive redraws
     58 if not np.array_equal(original_value, value):
---> 59     setattr(layer, attribute_name, value)

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari/layers/labels/labels.py:556, in Labels.color(self, color)
    553 else:
    554     color_mode = LabelColorMode.DIRECT
--> 556 self.color_mode = color_mode

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari/layers/labels/labels.py:685, in Labels.color_mode(self, color_mode)
    683 elif color_mode == LabelColorMode.AUTO:
    684     self._label_color_index = {}
--> 685     super()._set_colormap(self._random_colormap)
    687 else:
    688     raise ValueError(trans._("Unsupported Color Mode"))

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari/layers/intensity_mixin.py:71, in IntensityVisualizationMixin._set_colormap(self, colormap)
     69 def _set_colormap(self, colormap):
     70     self._colormap = ensure_colormap(colormap)
---> 71     self._update_thumbnail()
     72     self.events.colormap()

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/napari/layers/image/image.py:953, in _ImageBase._update_thumbnail(self)
    951         colormapped = np.concatenate([downsampled, alpha], axis=2)
    952 else:
--> 953     downsampled = ndi.zoom(
    954         image, zoom_factor, prefilter=False, order=0
    955     )
    956     low, high = self.contrast_limits
    957     downsampled = np.clip(downsampled, low, high)

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/scipy/ndimage/_interpolation.py:770, in zoom(input, zoom, output, order, mode, cval, prefilter, grid_mode)
    768 if input.ndim < 1:
    769     raise RuntimeError('input and output rank must be > 0')
--> 770 zoom = _ni_support._normalize_sequence(zoom, input.ndim)
    771 output_shape = tuple(
    772         [int(round(ii * jj)) for ii, jj in zip(input.shape, zoom)])
    773 complex_output = numpy.iscomplexobj(input)

File ~/micromamba/envs/trackastra/lib/python3.10/site-packages/scipy/ndimage/_ni_support.py:68, in _normalize_sequence(input, rank)
     66     if len(normalized) != rank:
     67         err = "sequence argument must have length equal to input rank"
---> 68         raise RuntimeError(err)
     69 else:
     70     normalized = [input] * rank

RuntimeError: sequence argument must have length equal to input rank

python: 3.10.12
napari: 0.4.18
napari-animation: 0.0.7

@bentaculum bentaculum changed the title Animation crashes with if hidden Labels layer present in viewer Animation crashes if hidden Labels layer present in viewer Nov 2, 2023
@alisterburt
Copy link
Collaborator

Hey @bentaculum

Thanks for the nice bug report! I haven't had a chance to take a look yet, I want to help but to set expectations I just moved to a new continent and started a new job so am pretty low bandwidth right now. If you're capable of finding the bug/submitting a pull request I'm very happy to get a fix merged but not sure when I can take a look myself right now.

Many thanks!

Alister

@psobolewskiPhD
Copy link
Member

Could you post full output of napari --info?
I have a different issue with Labels animations, see:
#180
so it's interesting that you say the visible Labels works for you.

Anyhow I have a fix PR (#181) for labels (and other layer types) and with that branch I can replicate the issue.
I'll try and take a look at the root cause of this issue too, because it involves the same lines of code (the storing of the viewer state)

@psobolewskiPhD
Copy link
Member

There was an issue with #181 that was spotted by a user on image.sc (https://forum.image.sc/t/issue-saving-animations-in-napari-animation/88868/8?u=psobolewskiphd). After fixing, I can no longer reproduce this issue, so I think #181 solves this! 🎉

@bentaculum
Copy link
Author

Hi @psobolewskiPhD, thanks for looking into this. Here's my napari --info in case still needed:

napari --info
napari: 0.4.18
Platform: macOS-12.7-arm64-arm-64bit
System: MacOS 12.7
Python: 3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:41:52) [Clang 15.0.7 ]
Qt: 5.15.8
PyQt5: 5.15.9
NumPy: 1.23.5
SciPy: 1.11.2
Dask: 2023.9.2
VisPy: 0.12.2
magicgui: 0.7.3
superqt: 0.5.4
in-n-out: 0.1.8
app-model: 0.2.1
npe2: 0.7.2

OpenGL:
  - GL version:  2.1 Metal - 76.3
  - MAX_TEXTURE_SIZE: 16384

Screens:
  - screen 1: resolution 1728x1117, scale 2.0

Settings path:
  - /Users/gallusse/Library/Application Support/napari/trackastra_0d3c3869e2a7fdacd6ade9a31c9f813beb43b53b/settings.yaml
Plugins:
  - napari: 0.4.18 (77 contributions)
  - napari-animation: 0.0.7 (2 contributions)
  - napari-console: 0.0.8 (0 contributions)
  - napari-manual-transforms: 0.0.3 (2 contributions)
  - napari-open-ctc: 0.1.dev2+geee37b4.d20231030 (2 contributions)
  - napari-svg: 0.1.10 (2 contributions)
  - trackastra: 0.1.0 (2 contributions)

brisvag added a commit that referenced this issue Dec 14, 2023
…alue comparisons (#181)

Closes: #180
Closes: #184

This adds a new function to utils.py for carrying out the comparison of
layer_attribute. In particular if an attribute is a dict then the
function recurses using key/value pairs.
This is needed for example for Labels layer `color` which is a dict of
np.array. But other layers also have nested dicts (e.g. Surfaces)

Additionally, I implement two tests that use all napari layers. 
The first compares attributes between viewer_state and the actual layer
and the second checks whether the animation has frames.
These tests fail in main but pass with the fix mentioned above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
3 participants