napari / napari-animation

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

Saving animations via command line results in poor quality images #236

Closed cameronhastie closed 1 month ago

cameronhastie commented 1 month ago

Is there an intuitive reason why animation.animate() would yield a different result than using the "save animation" button from the GUI implementation?

For some reason, animation.animate() seems to produce lower-resolution images (compressed) with a lower bitrate than when the same thing is done manually using the "save animation" button in the Napari GUI.

Here is the code I am using to produce to save the animation:

    animation = Animation(viewer)
    time_steps = np.size(viewer_image,2)
    viewer.dims.current_step = (frame_shift, 0, 0)
    viewer.camera.zoom = 10
    animation.capture_keyframe(steps=0)
    viewer.dims.current_step = (frame_shift+time_steps-1, 0, 0)
    viewer.camera.zoom = 10
    animation.capture_keyframe(steps=time_steps-1)
    file_name = 'C:/Users/Cameron/OneDrive - McGill University/Data/2023-2025_TFM_simulations/3_SNR_PePR_Tests/2024-10-21_PePR/SNR=150%_E=1+5_cell.mp4'
    animation.animate(file_name, fps=my_fps, quality=9)
    viewer.close()

Here are some file properties showing that the file created using the command line is lower quality than the file created manually using GUI.

Command line image: image

Manual GUI image: image

alisterburt commented 1 month ago

huh, very strange - the button uses the same API so it must be something else. Are you by any chance on a high DPI screen?

cameronhastie commented 1 month ago

huh, very strange - the button uses the same API so it must be something else. Are you by any chance on a high DPI screen?

My screen is just a Dell S2340L, which I think has a pretty standard DPI. I also just noticed that I am getting the following error:

WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (728, 577) to (736, 592) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to 1 (risking incompatibility).

I tried to fix this using the following forum post suggestions, but my error does not go away. Perhaps the issues are related the the one in the forum post. However, I must admit that I do not understand the error, despite it being written in pretty plain English: https://forum.image.sc/t/issue-saving-animations-in-napari-animation/88868/10

Czaki commented 1 month ago

WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (728, 577) to (736, 592) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to 1 (risking incompatibility).

This message means that the size of the captured canvas is not dividable by 16. And the encoder is compressing in 16x16 chunks of data. So it needs to resize the canvas to be dividable by 16.

Here are some file properties showing that the file created using the command line is lower quality than the file created manually using GUI.

I think that it may be related to viewer window size. I'm not 100% sure, but these from the command line image look close to the default canvas size of napari. So maybe you should call viewer.window.set_geometry(0, 0, 1500, 1000)` at the beginning of the script (you may need to adjust these values to remove the mentioned earlier warning).

cameronhastie commented 1 month ago

viewer.window.set_geometry(0, 0, 1500, 1000)

As far as improving my saved animation quality, this worked! Thanks very much for your help.

However, it still produces an error message (even when I used (0,0,1504,1008), which is divisible by 16) :

| 0/90 [00:00<?, ?it/s]IMAGEIO FFMPEG_WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (1142, 924) to (1152, 928) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to 1 (risking incompatibility).

Is there a way to ensure that the viewer geometry matches the input image for the IMAGEIO FFMPEG_WRITER?

Czaki commented 1 month ago

The problem here is that viewer.window.set_geometry is setting the size of the whole window, not only the portion that is visible in the animation.

Maybe viewer.window.set_geometry(0, 0, 1514, 1012)?

cameronhastie commented 1 month ago

Sorry there was a typo, I used viewer.window.set_geometry(0, 0, 1500, 1000) to produce the above error message.

I got it to work. In the error message, it converts the input image from (1142, 924) to (1152, 928), or (1142, 924) + (10,4). So I just added (10,4) to the viewer's original viewer geometry that produced the error message and re-ran. This did not result in the same error message.

Perhaps there is a way to use error handling to retroactively adjust the viewer geometry using this error message? Not sure, but for me, the error message goes away and I get a nice animation resolution by using:

viewer.window.set_geometry(0, 0, 1510, 1004)

Czaki commented 1 month ago

Perhaps there is a way to use error handling to retroactively adjust the viewer geometry using this error message? Not sure, but for me, the error message goes away and I get a nice animation resolution by using:

Maybe yes. We may just check the canvas size. But it requires some time, or someone knowing codebase.