Skip to content

Commit aa6b31e

Browse files
committed
Added Tutorial Hvass-Labs#9
1 parent 21d8b23 commit aa6b31e

File tree

7 files changed

+4513
-0
lines changed

7 files changed

+4513
-0
lines changed

09_Video_Data.ipynb

Lines changed: 2232 additions & 0 deletions
Large diffs are not rendered by default.

cache.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
########################################################################
2+
#
3+
# Cache-wrapper for a function or class.
4+
#
5+
# Save the result of calling a function or creating an object-instance
6+
# to harddisk. This is used to persist the data so it can be reloaded
7+
# very quickly and easily.
8+
#
9+
# Implemented in Python 3.5
10+
#
11+
########################################################################
12+
#
13+
# This file is part of the TensorFlow Tutorials available at:
14+
#
15+
# https://github.com/Hvass-Labs/TensorFlow-Tutorials
16+
#
17+
# Published under the MIT License. See the file LICENSE for details.
18+
#
19+
# Copyright 2016 by Magnus Erik Hvass Pedersen
20+
#
21+
########################################################################
22+
23+
import os
24+
import pickle
25+
import numpy as np
26+
27+
########################################################################
28+
29+
30+
def cache(cache_path, fn, *args, **kwargs):
31+
"""
32+
Cache-wrapper for a function or class. If the cache-file exists
33+
then the data is reloaded and returned, otherwise the function
34+
is called and the result is saved to cache. The fn-argument can
35+
also be a class instead, in which case an object-instance is
36+
created and saved to the cache-file.
37+
38+
:param cache_path:
39+
File-path for the cache-file.
40+
41+
:param fn:
42+
Function or class to be called.
43+
44+
:param args:
45+
Arguments to the function or class-init.
46+
47+
:param kwargs:
48+
Keyword arguments to the function or class-init.
49+
50+
:return:
51+
The result of calling the function or creating the object-instance.
52+
"""
53+
54+
# If the cache-file exists.
55+
if os.path.exists(cache_path):
56+
# Load the cached data from the file.
57+
with open(cache_path, mode='rb') as file:
58+
obj = pickle.load(file)
59+
60+
print("- Data loaded from cache-file: " + cache_path)
61+
else:
62+
# The cache-file does not exist.
63+
64+
# Call the function / class-init with the supplied arguments.
65+
obj = fn(*args, **kwargs)
66+
67+
# Save the data to a cache-file.
68+
with open(cache_path, mode='wb') as file:
69+
pickle.dump(obj, file)
70+
71+
print("- Data saved to cache-file: " + cache_path)
72+
73+
return obj
74+
75+
76+
########################################################################
77+
78+
79+
def convert_numpy2pickle(in_path, out_path):
80+
"""
81+
Convert a numpy-file to pickle-file.
82+
83+
The first version of the cache-function used numpy for saving the data.
84+
Instead of re-calculating all the data, you can just convert the
85+
cache-file using this function.
86+
87+
:param in_path:
88+
Input file in numpy-format written using numpy.save().
89+
90+
:param out_path:
91+
Output file written as a pickle-file.
92+
93+
:return:
94+
Nothing.
95+
"""
96+
97+
# Load the data using numpy.
98+
data = np.load(in_path)
99+
100+
# Save the data using pickle.
101+
with open(out_path, mode='wb') as file:
102+
pickle.dump(data, file)
103+
104+
105+
########################################################################
106+
107+
if __name__ == '__main__':
108+
# This is a short example of using a cache-file.
109+
110+
# This is the function that will only get called if the result
111+
# is not already saved in the cache-file. This would normally
112+
# be a function that takes a long time to compute, or if you
113+
# need persistent data for some other reason.
114+
def expensive_function(a, b):
115+
return a * b
116+
117+
print('Computing expensive_function() ...')
118+
119+
# Either load the result from a cache-file if it already exists,
120+
# otherwise calculate expensive_function(a=123, b=456) and
121+
# save the result to the cache-file for next time.
122+
result = cache(cache_path='cache_expensive_function.pkl',
123+
fn=expensive_function, a=123, b=456)
124+
125+
print('result =', result)
126+
127+
# Newline.
128+
print()
129+
130+
# This is another example which saves an object to a cache-file.
131+
132+
# We want to cache an object-instance of this class.
133+
# The motivation is to do an expensive computation only once,
134+
# or if we need to persist the data for some other reason.
135+
class ExpensiveClass:
136+
def __init__(self, c, d):
137+
self.c = c
138+
self.d = d
139+
self.result = c * d
140+
141+
def print_result(self):
142+
print('c =', self.c)
143+
print('d =', self.d)
144+
print('result = c * d =', self.result)
145+
146+
print('Creating object from ExpensiveClass() ...')
147+
148+
# Either load the object from a cache-file if it already exists,
149+
# otherwise make an object-instance ExpensiveClass(c=123, d=456)
150+
# and save the object to the cache-file for the next time.
151+
obj = cache(cache_path='cache_ExpensiveClass.pkl',
152+
fn=ExpensiveClass, c=123, d=456)
153+
154+
obj.print_result()
155+
156+
########################################################################

convert.py

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
#!/usr/bin/python
2+
3+
########################################################################
4+
#
5+
# Function and script for converting videos to images.
6+
#
7+
# This can be run as a script in a Linux shell by typing:
8+
#
9+
# python convert.py
10+
#
11+
# Or by running:
12+
#
13+
# chmod +x convert.py
14+
# ./convert.py
15+
#
16+
# Requires the program avconv to be installed.
17+
# Tested with avconv v. 9.18-6 on Linux Mint.
18+
#
19+
# Implemented in Python 3.5 (seems to work in Python 2.7 as well)
20+
#
21+
########################################################################
22+
#
23+
# This file is part of the TensorFlow Tutorials available at:
24+
#
25+
# https://github.com/Hvass-Labs/TensorFlow-Tutorials
26+
#
27+
# Published under the MIT License. See the file LICENSE for details.
28+
#
29+
# Copyright 2016 by Magnus Erik Hvass Pedersen
30+
#
31+
########################################################################
32+
33+
import os
34+
import subprocess
35+
import argparse
36+
37+
########################################################################
38+
39+
40+
def video2images(in_dir, out_dir, crop_size, out_size, framerate, video_exts):
41+
"""
42+
Convert videos to images. The videos are located in the directory in_dir
43+
and all its sub-directories which are processed recursively. The directory
44+
structure is replicated to out_dir where the jpeg-images are saved.
45+
46+
:param in_dir:
47+
Input directory for the videos e.g. "/home/magnus/video/"
48+
All sub-directories are processed recursively.
49+
50+
:param out_dir:
51+
Output directory for the images e.g. "/home/magnus/video-images/"
52+
53+
:param crop_size:
54+
Integer. First the videos are cropped to this width and height.
55+
56+
:param out_size:
57+
Integer. After cropping, the videos are resized to this width and height.
58+
59+
:param framerate:
60+
Integer. Number of frames to grab per second.
61+
62+
:param video_exts:
63+
Tuple of strings. Extensions for video-files e.g. ('.mts', '.mp4')
64+
Not case-sensitive.
65+
66+
:return:
67+
Nothing.
68+
"""
69+
70+
# Convert all video extensions to lower-case.
71+
video_exts = tuple(ext.lower() for ext in video_exts)
72+
73+
# Number of videos processed.
74+
video_count = 0
75+
76+
# Process all the sub-dirs recursively.
77+
for current_dir, dir_names, file_names in os.walk(in_dir):
78+
# The current dir relative to the input directory.
79+
relative_path = os.path.relpath(current_dir, in_dir)
80+
81+
# Name of the new directory for the output images.
82+
new_dir = os.path.join(out_dir, relative_path)
83+
84+
# If the output-directory does not exist, then create it.
85+
if not os.path.exists(new_dir):
86+
os.makedirs(new_dir)
87+
88+
# For all the files in the current directory.
89+
for file_name in file_names:
90+
# If the file has a valid video-extension. Compare lower-cases.
91+
if file_name.lower().endswith(video_exts):
92+
# File-path for the input video.
93+
in_file = os.path.join(current_dir, file_name)
94+
95+
# Split the file-path in root and extension.
96+
file_root, file_ext = os.path.splitext(file_name)
97+
98+
# Create the template file-name for the output images.
99+
new_file_name = file_root + "-%4d.jpg"
100+
101+
# Complete file-path for the output images incl. all sub-dirs.
102+
new_file_path = os.path.join(new_dir, new_file_name)
103+
104+
# Clean up the path by removing e.g. "/./"
105+
new_file_path = os.path.normpath(new_file_path)
106+
107+
# Print status.
108+
print("Converting video to images:")
109+
print("- Input video: {0}".format(in_file))
110+
print("- Output images: {0}".format(new_file_path))
111+
112+
# Command to be run in the shell for the video-conversion tool.
113+
cmd = "avconv -i {0} -r {1} -vf crop={2}:{2} -vf scale={3}:{3} -qscale 2 {4}"
114+
115+
# Fill in the arguments for the command-line.
116+
cmd = cmd.format(in_file, framerate, crop_size, out_size, new_file_path)
117+
118+
# Run the command-line in a shell.
119+
subprocess.call(cmd, shell=True)
120+
121+
# Increase the number of videos processed.
122+
video_count += 1
123+
124+
# Print newline.
125+
print()
126+
127+
print("Number of videos converted: {0}".format(video_count))
128+
129+
130+
########################################################################
131+
# This script allows you to run the video-conversion from the command-line.
132+
133+
if __name__ == "__main__":
134+
# Argument description.
135+
desc = "Convert videos to images. " \
136+
"Recursively processes all sub-dirs of INDIR " \
137+
"and replicates the dir-structure to OUTDIR. " \
138+
"The video is first cropped to CROP:CROP pixels, " \
139+
"then resized to SIZE:SIZE pixels and written as a jpeg-file. "
140+
141+
# Create the argument parser.
142+
parser = argparse.ArgumentParser(description=desc)
143+
144+
# Add arguments to the parser.
145+
parser.add_argument("--indir", required=True,
146+
help="input directory where videos are located")
147+
148+
parser.add_argument("--outdir", required=True,
149+
help="output directory where images will be saved")
150+
151+
parser.add_argument("--crop", required=True, type=int,
152+
help="the input videos are first cropped to CROP:CROP pixels")
153+
154+
parser.add_argument("--size", required=True, type=int,
155+
help="the input videos are then resized to SIZE:SIZE pixels")
156+
157+
parser.add_argument("--rate", required=False, type=int, default=5,
158+
help="the number of frames to convert per second")
159+
160+
parser.add_argument("--exts", required=False, nargs="+",
161+
help="list of extensions for video-files e.g. .mts .mp4")
162+
163+
# Parse the command-line arguments.
164+
args = parser.parse_args()
165+
166+
# Get the arguments.
167+
in_dir = args.indir
168+
out_dir = args.outdir
169+
crop_size = args.crop
170+
out_size = args.size
171+
framerate = args.rate
172+
video_exts = args.exts
173+
174+
if video_exts is None:
175+
# Default extensions for video-files.
176+
video_exts = (".MTS", ".mp4")
177+
else:
178+
# A list of strings is provided as a command-line argument, but we
179+
# need a tuple instead of a list, so convert it to a tuple.
180+
video_exts = tuple(video_exts)
181+
182+
# Print the arguments.
183+
print("Convert videos to images.")
184+
print("- Input dir: " + in_dir)
185+
print("- Output dir: " + out_dir)
186+
print("- Crop width and height: {0}".format(crop_size))
187+
print("- Resize width and height: {0}".format(out_size))
188+
print("- Frame-rate: {0}".format(framerate))
189+
print("- Video extensions: {0}".format(video_exts))
190+
print()
191+
192+
# Perform the conversions.
193+
video2images(in_dir=in_dir, out_dir=out_dir,
194+
crop_size=crop_size, out_size=out_size,
195+
framerate=framerate, video_exts=video_exts)
196+
197+
########################################################################

0 commit comments

Comments
 (0)