napari / napari-animation

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

Fading out image not working #152

Closed kolibril13 closed 9 months ago

kolibril13 commented 1 year ago

In the below example,

img.opacity = 0
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

should fade out the cell image, but just disappears suddenly. Afterwards it should fade in again, and works perfectly for the fade in.

img.opacity =1
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

Any idea what is causing this fadeout bug? Thanks for reading this issue! :)

from napari_animation import Animation
from napari_animation.easing import Easing

import napari
from skimage import data

viewer = napari.view_image(data.cells3d(), channel_axis=1, ndisplay=3)

img = viewer.layers[1]
img.opacity =1
viewer.camera.angles = (0, 0, 90)

animation = Animation(viewer)
viewer.camera.zoom = 1
animation.capture_keyframe(steps=30)

viewer.camera.zoom = 2
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

viewer.camera.angles = (20, -25, 140)
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

img.opacity = 0 # <- why is this not fading out, but disappearing suddenly?
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

img.opacity =1
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

viewer.camera.zoom = 1
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

viewer.camera.angles = (0, 0, 90)
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

animation.animate("hello.mp4", canvas_only=True, fps=60)

https://user-images.githubusercontent.com/44469195/222421231-0ed90022-3c2d-4c0c-859c-5d52a72ddbf3.mp4

alisterburt commented 1 year ago

hey! interpolation is type based in napari-animation, I assume you are setting integers and napari is not coercing them to floats, could you check if this is indeed the root cause? Would ideally be fixed upstream in napari

kolibril13 commented 1 year ago

Good idea! I just tried

viewer.camera.angles = (20, -25, 140)
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

img.opacity = 0.0
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

img.opacity = 1.0
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

but that did not solve the problem. In the first example, both values were ints. And there the fadeout did not work, but the fade in works. So ints are maybe not the problem.

Just tested again a new script, here I only set the opacity to 0.5, but it completely removes it. Is there maybe something happening internally that sets the opacity to 0 ? If yes, then this might be the same bug as https://github.com/napari/napari-animation/issues/149

from napari_animation import Animation
from napari_animation.easing import Easing

img = viewer.layers[1]
img.opacity =1
viewer.camera.angles = (0, 0, 90)

animation = Animation(viewer)

img.opacity = 1
animation.capture_keyframe(steps=60)

animation.capture_keyframe(steps=120)

img.opacity = 0.5 # <- here, the opacity is set to zero I guess
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

img.opacity = 1.0
animation.capture_keyframe(steps=60, ease=Easing.QUADRATIC)

https://user-images.githubusercontent.com/44469195/222435842-d2ec5923-051d-4ba7-bc90-3562034da60a.mp4

alisterburt commented 1 year ago

Thanks for spelunking - I'll have to dig properly to figure this out but the reproducible example is a super useful starting point

psobolewskiPhD commented 9 months ago

I can reproduce this too. It does seem related to #149 Again the viewer_state of key_frames is correct, so I assume it's an interpolation issue with decreasing values.

psobolewskiPhD commented 9 months ago

Ok, here the snippets are the problem actaully. @alisterburt was on the right track with typing issue. So the snippet has img.opacity=1 This makes starting opacity an int:

In [2]: viewer.layers[-1]._get_state()
Out[2]: 
{'name': 'Image [1]',
 'metadata': {},
 'scale': [1.0, 1.0, 1.0],
 'translate': [0.0, 0.0, 0.0],
 'rotate': [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
 'shear': [0.0, 0.0, 0.0],
 'affine': array([[1., 0., 0., 0.],
        [0., 1., 0., 0.],
        [0., 0., 1., 0.],
        [0., 0., 0., 1.]]),
 'opacity': 1,

This is then stored in the viewer_state and when interpolation is performed, the wrong interpolation is chosen here because the initial value (a) is an int: https://github.com/napari/napari-animation/blob/969fefb5f442d7f6e1ba9ca7ae287a67f55c5c6d/napari_animation/interpolation/base_interpolation.py#L70-L88

So you need to set opacity as a float currently, but this should probably be coerced on the napari side?