napari / napari-animation

A napari plugin for making animations
https://napari.github.io/napari-animation/
Other
74 stars 27 forks source link

Fail to create animation in headless mode #187

Open melissawm opened 7 months ago

melissawm commented 7 months ago

Hi, I'm trying to create an animation from napari in headless mode.

I am using a jupyter notebook. The images are correctly displayed in normal Qt mode, and the animation gets generated correctly, but not in headless mode. After setting

%set_env QT_QPA_PLATFORM=offscreen

# here I create my images, a (1279, 1760, 1760) array. 

from napari_animation import Animation
animation = Animation(viewer)
viewer.update_console({'animation': animation})

animation.capture_keyframe()
for i in range(0,21):
    viewer.dims.current_step = (60*i, 879, 879)
    animation.capture_keyframe()
viewer.reset_view()
animation.capture_keyframe()

animation.animate('demo2D.mov', canvas_only=True, fps=60)

when I try to create an animation from my script I get

WARNING: QOpenGLWidget: Failed to create context
WARNING:vispy:QOpenGLWidget: Failed to create context

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[54], line 5
      2 animation = Animation(viewer)
      3 viewer.update_console({'animation': animation})
----> 5 animation.capture_keyframe()
      6 for i in range(0,21):
      7     viewer.dims.current_step = (60*i, 879, 879)

File /usr/local/lib/python3.9/site-packages/napari_animation/animation.py:82, in Animation.capture_keyframe(self, steps, ease, insert, position)
     79         else:
     80             raise ValueError("No selected keyframe to replace !")
---> 82 new_frame = KeyFrame.from_viewer(self.viewer, steps=steps, ease=ease)
     83 new_frame.name = f"Key Frame {next(self._keyframe_counter)}"
     85 if insert:

File /usr/local/lib/python3.9/site-packages/napari_animation/key_frame.py:55, in KeyFrame.from_viewer(cls, viewer, steps, ease)
     47 @classmethod
     48 def from_viewer(
     49     cls, viewer: napari.viewer.Viewer, steps=15, ease=Easing.LINEAR
     50 ):
     51     """Create a KeyFrame from a viewer instance."""
     52     return cls(
     53         viewer_state=ViewerState.from_viewer(viewer),
     54         thumbnail=make_thumbnail(
---> 55             viewer.screenshot(canvas_only=True, flash=False)
     56         ),
     57         steps=steps,
     58         ease=ease,
     59     )

File /usr/local/lib/python3.9/site-packages/napari/viewer.py:129, in Viewer.screenshot(self, path, size, scale, canvas_only, flash)
     93 def screenshot(
     94     self,
     95     path=None,
   (...)
    100     flash: bool = True,
    101 ):
    102     """Take currently displayed screen and convert to an image array.
    103 
    104     Parameters
   (...)
    127         upper-left corner of the rendered region.
    128     """
--> 129     return self.window.screenshot(
    130         path=path,
    131         size=size,
    132         scale=scale,
    133         flash=flash,
    134         canvas_only=canvas_only,
    135     )

File /usr/local/lib/python3.9/site-packages/napari/_qt/qt_main_window.py:1402, in Window.screenshot(self, path, size, scale, flash, canvas_only)
   1373 def screenshot(
   1374     self, path=None, size=None, scale=None, flash=True, canvas_only=False
   1375 ):
   1376     """Take currently displayed viewer and convert to an image array.
   1377 
   1378     Parameters
   (...)
   1400         upper-left corner of the rendered region.
   1401     """
-> 1402     img = QImg2array(self._screenshot(size, scale, flash, canvas_only))
   1403     if path is not None:
   1404         imsave(path, img)  # scikit-image imsave method

File /usr/local/lib/python3.9/site-packages/napari/_qt/utils.py:113, in QImg2array(img)
    111     arr = np.array(b).reshape(h, w, c)
    112 else:
--> 113     b.setsize(h * w * c)
    114     arr = np.frombuffer(b, np.uint8).reshape(h, w, c)
    116 # Format of QImage is ARGB32_Premultiplied, but color channels are
    117 # reversed.

AttributeError: 'NoneType' object has no attribute 'setsize'
psobolewskiPhD commented 6 months ago

I don't think this is a Napari-animation issue per se. If you instead of all the animation stuff use:

screenshot = viewer.screenshot()

I suspect you will get the same errors?