Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
import base64
|
4 |
+
import tempfile
|
5 |
+
from PIL import Image
|
6 |
+
import numpy as np
|
7 |
+
from moviepy.editor import VideoFileClip
|
8 |
+
import moviepy.video.fx.all as vfx
|
9 |
+
|
10 |
+
## Session state ##
|
11 |
+
if 'clip_width' not in st.session_state:
|
12 |
+
st.session_state.clip_width = 0
|
13 |
+
if 'clip_height' not in st.session_state:
|
14 |
+
st.session_state.clip_height = 0
|
15 |
+
if 'clip_duration' not in st.session_state:
|
16 |
+
st.session_state.clip_duration = 0
|
17 |
+
if 'clip_fps' not in st.session_state:
|
18 |
+
st.session_state.clip_fps = 0
|
19 |
+
if 'clip_total_frames' not in st.session_state:
|
20 |
+
st.session_state.clip_total_frames = 0
|
21 |
+
|
22 |
+
st.title('🎈 Animated GIF Maker')
|
23 |
+
|
24 |
+
## Upload file ##
|
25 |
+
st.sidebar.header('Upload file')
|
26 |
+
uploaded_file = st.sidebar.file_uploader("Choose a file", type=['mov', 'mp4'])
|
27 |
+
st.sidebar.markdown('''
|
28 |
+
[Download example file](https://github.com/dataprofessor/animated-gif/raw/master/example/streamlit-app-starter-kit-screencast.mov)
|
29 |
+
|
30 |
+
---
|
31 |
+
Made with ❤️ by Chanin Nantasenamat ([Data Professor](https://youtube.com/dataprofessor))
|
32 |
+
''')
|
33 |
+
|
34 |
+
## Display gif generation parameters once file has been uploaded ##
|
35 |
+
if uploaded_file is not None:
|
36 |
+
## Save to temp file ##
|
37 |
+
tfile = tempfile.NamedTemporaryFile(delete=False)
|
38 |
+
tfile.write(uploaded_file.read())
|
39 |
+
|
40 |
+
## Open file ##
|
41 |
+
clip = VideoFileClip(tfile.name)
|
42 |
+
|
43 |
+
st.session_state.clip_duration = clip.duration
|
44 |
+
|
45 |
+
## Input widgets ##
|
46 |
+
st.sidebar.header('Input parameters')
|
47 |
+
selected_resolution_scaling = st.sidebar.slider('Scaling of video resolution', 0.0, 1.0, 0.5 )
|
48 |
+
selected_speedx = st.sidebar.slider('Playback speed', 0.1, 10.0, 5.0)
|
49 |
+
selected_export_range = st.sidebar.slider('Duration range to export', 0, int(st.session_state.clip_duration), (0, int(st.session_state.clip_duration) ))
|
50 |
+
|
51 |
+
## Resizing of video ##
|
52 |
+
clip = clip.resize(selected_resolution_scaling)
|
53 |
+
|
54 |
+
st.session_state.clip_width = clip.w
|
55 |
+
st.session_state.clip_height = clip.h
|
56 |
+
st.session_state.clip_duration = clip.duration
|
57 |
+
st.session_state.clip_total_frames = clip.duration * clip.fps
|
58 |
+
st.session_state.clip_fps = st.sidebar.slider('FPS', 10, 60, 20)
|
59 |
+
|
60 |
+
## Display output ##
|
61 |
+
st.subheader('Metrics')
|
62 |
+
col1, col2, col3, col4, col5 = st.columns(5)
|
63 |
+
col1.metric('Width', st.session_state.clip_width, 'pixels')
|
64 |
+
col2.metric('Height', st.session_state.clip_height, 'pixels')
|
65 |
+
col3.metric('Duration', st.session_state.clip_duration, 'seconds')
|
66 |
+
col4.metric('FPS', st.session_state.clip_fps, '')
|
67 |
+
col5.metric('Total Frames', st.session_state.clip_total_frames, 'frames')
|
68 |
+
|
69 |
+
# Extract video frame as a display image
|
70 |
+
st.subheader('Preview')
|
71 |
+
|
72 |
+
with st.expander('Show image'):
|
73 |
+
selected_frame = st.slider('Preview a time frame (s)', 0, int(st.session_state.clip_duration), int(np.median(st.session_state.clip_duration)) )
|
74 |
+
clip.save_frame('frame.gif', t=selected_frame)
|
75 |
+
frame_image = Image.open('frame.gif')
|
76 |
+
st.image(frame_image)
|
77 |
+
|
78 |
+
## Print image parameters ##
|
79 |
+
st.subheader('Image parameters')
|
80 |
+
with st.expander('Show image parameters'):
|
81 |
+
st.write(f'File name: `{uploaded_file.name}`')
|
82 |
+
st.write('Image size:', frame_image.size)
|
83 |
+
st.write('Video resolution scaling', selected_resolution_scaling)
|
84 |
+
st.write('Speed playback:', selected_speedx)
|
85 |
+
st.write('Export duration:', selected_export_range)
|
86 |
+
st.write('Frames per second (FPS):', st.session_state.clip_fps)
|
87 |
+
|
88 |
+
## Export animated GIF ##
|
89 |
+
st.subheader('Generate GIF')
|
90 |
+
generate_gif = st.button('Generate Animated GIF')
|
91 |
+
|
92 |
+
if generate_gif:
|
93 |
+
clip = clip.subclip(selected_export_range[0], selected_export_range[1]).speedx(selected_speedx)
|
94 |
+
|
95 |
+
frames = []
|
96 |
+
for frame in clip.iter_frames():
|
97 |
+
frames.append(np.array(frame))
|
98 |
+
|
99 |
+
image_list = []
|
100 |
+
|
101 |
+
for frame in frames:
|
102 |
+
im = Image.fromarray(frame)
|
103 |
+
image_list.append(im)
|
104 |
+
|
105 |
+
image_list[0].save('export.gif', format = 'GIF', save_all = True, loop = 0, append_images = image_list)
|
106 |
+
|
107 |
+
#clip.write_gif('export.gif', fps=st.session_state.clip_fps)
|
108 |
+
|
109 |
+
## Download ##
|
110 |
+
st.subheader('Download')
|
111 |
+
|
112 |
+
#video_file = open('export.gif', 'rb')
|
113 |
+
#video_bytes = video_file.read()
|
114 |
+
#st.video(video_bytes)
|
115 |
+
|
116 |
+
file_ = open('export.gif', 'rb')
|
117 |
+
contents = file_.read()
|
118 |
+
data_url = base64.b64encode(contents).decode("utf-8")
|
119 |
+
file_.close()
|
120 |
+
st.markdown(
|
121 |
+
f'<img src="data:image/gif;base64,{data_url}" alt="cat gif">',
|
122 |
+
unsafe_allow_html=True,
|
123 |
+
)
|
124 |
+
|
125 |
+
fsize = round(os.path.getsize('export.gif')/(1024*1024), 1)
|
126 |
+
st.info(f'File size of generated GIF: {fsize} MB', icon='💾')
|
127 |
+
|
128 |
+
fname = uploaded_file.name.split('.')[0]
|
129 |
+
with open('export.gif', 'rb') as file:
|
130 |
+
btn = st.download_button(
|
131 |
+
label='Download image',
|
132 |
+
data=file,
|
133 |
+
file_name=f'{fname}_scaling-{selected_resolution_scaling}_fps-{st.session_state.clip_fps}_speed-{selected_speedx}_duration-{selected_export_range[0]}-{selected_export_range[1]}.gif',
|
134 |
+
mime='image/gif'
|
135 |
+
)
|
136 |
+
|
137 |
+
## Default page ##
|
138 |
+
else:
|
139 |
+
st.warning('👈 Upload a video file')
|