-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_imshow.py
More file actions
112 lines (91 loc) · 3.84 KB
/
_imshow.py
File metadata and controls
112 lines (91 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import base64
import io
import tempfile
from typing import (
Any,
Iterable,
)
import numpngw
import numpy as np
import skvideo.io
try:
from IPython.core.display import HTML as IPythonHTML
except ImportError:
IPythonHTML = None
def _render_html_apng(array: np.ndarray | Iterable[np.ndarray], fps: float = 25) -> str:
# The image is a single frame, create an animation with a single frame
if isinstance(array, np.ndarray) and array.ndim == 3:
return _render_html_apng([array], fps=fps)
# Assume that the image is a list of frames, create a temporal stack
elif not isinstance(array, np.ndarray):
return _render_html_apng(np.array(list(array)), fps=fps)
# The image is a temporal stack, create an animated PNG
elif isinstance(array, np.ndarray) and array.ndim == 4:
buf = io.BytesIO()
numpngw.write_apng(buf, array, delay=1000 / fps, use_palette=False)
buf.seek(0)
buf_base64_str = base64.b64encode(buf.read()).decode('ascii')
return f'<img src="data:image/apng;base64, {buf_base64_str}"/>'
# The image is not a valid type
else:
raise ValueError('Array must be 3D or 4D data.')
def _render_html_h264(array: np.ndarray | Iterable[np.ndarray], fps: float = 25) -> str:
# The image is a single frame, create an animation with a single frame
if isinstance(array, np.ndarray) and array.ndim == 3:
return _render_html_h264([array], fps=fps)
# Encode video
with tempfile.NamedTemporaryFile(suffix='.mp4') as mp4_file:
with skvideo.io.FFmpegWriter(
mp4_file.name,
outputdict={
'-vcodec': 'h264',
'-pix_fmt': 'yuv420p',
'-r': str(fps),
},
) as writer:
for frame in array:
writer.writeFrame(frame)
buf = mp4_file.read()
# Produce HTML
buf_base64_str = base64.b64encode(buf).decode('ascii')
return(
'<video autoplay loop muted style="max-width: 100%;">'
f'<source type="video/mp4" src="data:video/mp4;base64, {buf_base64_str}"/>'
'</video>'
)
html_plugins = dict(
apng=_render_html_apng,
h264=_render_html_h264,
)
def imshow(array: np.ndarray | Iterable[np.ndarray], *colorbars, fps: float = 25, format: str = 'auto') -> Any:
"""
Display an image or a sequence of images in a Jupyter notebook.
Arguments:
array: The image or sequence of images to display. Can be a 3D NumPy array (RGB image) or 4D NumPy array
(stack of RGB images), or an iterable of 3D arrays (sequence of RGB images).
colorbars: Optional colorbars to display alongside the image.
fps: The frames per second for the animation. Default is 25.
format: The format to use for displaying the image. Can be `'apng'` or `'h264'`. Default is `'auto'`, which
will use `'apng'` for single images, and `'h264'` for image stacks or sequences.
"""
assert IPythonHTML is not None, 'Please install IPython to use this function.'
if format == 'auto':
if isinstance(array, np.ndarray) and (array.ndim == 3 or (array.ndim == 4 and array.shape[0] == 1)):
format = 'apng'
else:
format = 'h264'
# Resolve the format to the proper plugin
_render_html = html_plugins.get(format, None)
if _render_html is None:
raise ValueError(f'Format "{format}" not supported.')
# Delegate to the selected plugin
nl = '\n' # Python <3.12 does not allow backslashes in f-strings expressions
return IPythonHTML(
f"""
<div style="display: inline-flex; padding: 0.5em;">
<div style="display: inline-block; font-size: 0;">
{_render_html(array, fps=fps)}
</div>
{nl.join(cb.tohtml() for cb in colorbars)}
</div>
""")