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

[WIP] rework artists.stitch_to_animation #1183

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
- new artist helper function: `norm_from_channel`
- new artist helper function: `ticks_from_norm`
- new artist iterator `ChopHandler`
- `artists.stitch_to_animation`: new kwargs for more gif customization

### Fixed
- fixed Quick2D/Quick1D issues where collapsing unused dims did not work
- wt5 explore : fixed bug where data will not load interactively if directory is not cwd
- constants in chopped data will inherit the units of the original data

## Changed
- use `pillow` directly and remove `ImageIO` dependency
- refactor of artists.quick1D and artists.quick2D
- quick2D and quick1D will not force `autosave=True` if the number of figures is large. Instead, interactive plotting will be truncated if the number of figures is large.
- artists now gets turbo colormap straight from matplotlib
Expand Down
73 changes: 54 additions & 19 deletions WrightTools/artists/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# --- import --------------------------------------------------------------------------------------


import os
import pathlib

import numpy as np

Expand All @@ -15,6 +15,7 @@

from matplotlib.colors import Normalize, CenteredNorm, TwoSlopeNorm
from mpl_toolkits.axes_grid1 import make_axes_locatable
from typing import List

import imageio.v3 as iio
import warnings
Expand Down Expand Up @@ -877,7 +878,7 @@ def savefig(path, fig=None, close=True, **kwargs):
if fig is None:
fig = plt.gcf()

path = os.path.abspath(path)
path = pathlib.Path(path).resolve()

kwargs["dpi"] = kwargs.get("dpi", 300)
kwargs["transparent"] = kwargs.get("transparent", False)
Expand Down Expand Up @@ -1085,37 +1086,71 @@ def subplots_adjust(fig=None, inches=1):
fig.subplots_adjust(top=top, right=right, bottom=bottom, left=left)


def stitch_to_animation(paths, outpath=None, *, duration=0.5, palettesize=256, verbose=True):
"""Stitch a series of images into an animation.

Currently supports animated gifs, other formats coming as needed.
def stitch_to_animation(
paths,
outpath=None,
duration=0.5,
ignore_alpha: bool = True,
reduce: int = None,
verbose=True,
**kwargs,
):
"""Stitch a series of images into a gif.

Parameters
----------
paths : list of strings
Filepaths to the images to stitch together, in order of apperence.
Filepaths to the images to stitch together, in order of appearance.
outpath : string (optional)
Path of output, including extension. If None, bases output path on path
of first path in `images`. Default is None.
Path of output, including extension. If None, bases output path on `paths[0]`. Default is None.
duration : number or list of numbers (optional)
Duration of (each) frame in seconds. Default is 0.5.
palettesize : int (optional)
The number of colors in the resulting animation. Input is rounded to
the nearest power of 2. Default is 256.
ignore_alpha : bool (optional)
When True, transparency is excluded from the gif and color palette may be higher res.
reduce : int (optional)
Reduces the resolution along both image dimensions by a factor of `reduce`.
verbose : bool (optional)
Toggle talkback. Default is True.

Returns:
--------
outpath : path-like
path to generated gif
"""
import contextlib
from PIL import Image

# parse filename
if outpath is None:
outpath = os.path.splitext(paths[0])[0] + ".gif"
outpath = pathlib.Path(paths[0]).with_suffix(".gif")
# write
t = wt_kit.Timer(verbose=False)
with t, iio.imopen(outpath, "w") as gif:
for p in paths:
frame = iio.imread(p)
gif.write(
frame, plugin="pillow", duration=duration * 1e3, loop=0, palettesize=palettesize
)

def process_imgs(imgs: List[Image.Image]):
count = 0
for img in imgs:
if verbose:
print(f"processing {count+1} / {len(paths)}...")
if ignore_alpha:
img = img.convert("RGB")
if reduce is not None:
img = img.reduce(reduce)
count += 1
yield img

with t, contextlib.ExitStack() as stack:
imgs = process_imgs(stack.enter_context(Image.open(p)) for p in paths)
img = next(imgs)
img.save(
fp=outpath,
format="GIF",
append_images=imgs,
save_all=True,
duration=duration * 1e3,
loop=0,
**kwargs,
)

if verbose:
interval = np.round(t.interval, 2)
print("gif generated in {0} seconds - saved at {1}".format(interval, outpath))
Expand Down
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#! /usr/bin/env python3

import os
import pathlib
from setuptools import setup, find_packages


here = os.path.abspath(os.path.dirname(__file__))
here = pathlib.Path(__file__).resolve().parent


def read(fname):
with open(os.path.join(here, fname)) as f:
with open(here / fname) as f:
return f.read()


Expand All @@ -25,7 +25,7 @@ def read(fname):
]
}

with open(os.path.join(here, "WrightTools", "VERSION")) as version_file:
with open(here / "WrightTools" / "VERSION") as version_file:
version = version_file.read().strip()

docs_require = ["sphinx", "sphinx-gallery==0.8.2", "sphinx-rtd-theme"]
Expand All @@ -37,7 +37,7 @@ def read(fname):
python_requires=">=3.7",
install_requires=[
"h5py",
"imageio>=2.28.0",
"pillow",
"matplotlib>=3.4.0",
"numexpr",
"numpy>=1.15.0,<2.0",
Expand Down
Loading