blob: 244a8ccaac9af4ff5f7e7d36cf67c32cf228700b [file] [log] [blame]
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +00001#!/usr/bin/env python
2# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3#
4# Use of this source code is governed by a BSD-style license
5# that can be found in the LICENSE file in the root of the source
6# tree. An additional intellectual property rights grant can be found
7# in the file PATENTS. All contributing project authors may
8# be found in the AUTHORS file in the root of the source tree.
9
10import optparse
11import os
12import subprocess
13import sys
14
15import helper_functions
16
17_DEFAULT_BARCODE_WIDTH = 352
18_DEFAULT_BARCODES_FILE = 'barcodes.yuv'
19
20
21def generate_upca_barcodes(number_of_barcodes, barcode_width, barcode_height,
22 output_directory='.',
23 path_to_zxing='zxing-read-only'):
24 """Generates UPC-A barcodes.
25
26 This function generates a number_of_barcodes UPC-A barcodes. The function
27 calls an example Java encoder from the Zxing library. The barcodes are
28 generated as PNG images. The width of the barcodes shouldn't be less than 102
29 pixels because otherwise Zxing can't properly generate the barcodes.
30
31 Args:
32 number_of_barcodes(int): The number of barcodes to generate.
33 barcode_width(int): Width of barcode in pixels.
34 barcode_height(int): Height of barcode in pixels.
35 output_directory(string): Output directory where to store generated
36 barcodes.
37 path_to_zxing(string): The path to Zxing.
38
39 Return:
40 (bool): True if the conversion is successful.
41 """
42 base_file_name = os.path.join(output_directory, "barcode_")
43 jars = helper_functions.form_jars_string(path_to_zxing)
44 command_line_encoder ='com.google.zxing.client.j2se.CommandLineEncoder'
45 barcode_width = str(barcode_width)
46 barcode_height = str(barcode_height)
47
48 errors = False
49 for i in range(number_of_barcodes):
50 suffix = helper_functions.zero_pad(i)
51 # Barcodes starting from 0
52 content = helper_functions.zero_pad(i, 11)
53 output_file_name = base_file_name + suffix + ".png"
54
55 command = ["java", "-cp", jars, command_line_encoder,
56 "--barcode_format=UPC_A", "--height=%s" % barcode_height,
57 "--width=%s" % barcode_width,
58 "--output=%s" % (output_file_name), "%s" % (content)]
59 try:
60 helper_functions.run_shell_command(
61 command, msg=('Error during barcode %s generation' % content))
62 except helper_functions.HelperError, err:
63 print err
64 errors = True
65 return not errors
66
67
68def convert_png_to_yuv_barcodes(input_directory='.', output_directory='.'):
69 """Converts PNG barcodes to YUV barcode images.
70
71 This function reads all the PNG files from the input directory which are in
72 the format frame_xxxx.png, where xxxx is the number of the frame, starting
73 from 0000. The frames should be consecutive numbers. The output YUV file is
74 named frame_xxxx.yuv. The function uses ffmpeg to do the conversion.
75
76 Args:
77 input_directory(string): The input direcotry to read the PNG barcodes from.
78 output_directory(string): The putput directory to write the YUV files to.
79 Return:
80 (bool): True if the conversion was without errors.
81 """
82 return helper_functions.perform_action_on_all_files(
83 input_directory, 'barcode_', 'png', 0, _convert_to_yuv_and_delete,
84 output_directory=output_directory, pattern='barcode_')
85
86
87def _convert_to_yuv_and_delete(output_directory, file_name, pattern):
88 """Converts a PNG file to a YUV file and deletes the PNG file.
89
90 Args:
91 output_directory(string): The output directory for the YUV file.
92 file_name(string): The PNG file name.
93 pattern(string): The file pattern of the PNG/YUV file. The PNG/YUV files are
94 named patternxx..x.png/yuv, where xx..x are digits starting from 00..0.
95 Return:
96 (bool): True upon successful conversion, false otherwise.
97 """
98 # Pattern should be in file name
99 if not pattern in file_name:
100 return False
101 pattern_position = file_name.rfind(pattern)
102
103 # Strip the path to the PNG file and replace the png extension with yuv
104 yuv_file_name = file_name[pattern_position:-3] + 'yuv'
105 yuv_file_name = os.path.join(output_directory, yuv_file_name)
106
107 command = ['ffmpeg', '-i', '%s' % (file_name), '-pix_fmt', 'yuv420p',
108 '%s' % (yuv_file_name)]
109 try:
110 helper_functions.run_shell_command(
111 command, msg=('Error during PNG to YUV conversion of %s' %
112 file_name));
113 helper_functions.delete_file(file_name)
114 except helper_functions.HelperError, err:
115 print err
116 return Flase
117 return True
118
119
120def combine_yuv_frames_into_one_file(output_file_name, input_directory='.'):
121 """Combines several YUV frames into one YUV video file.
122
123 The function combines the YUV frames from input_directory into one YUV video
124 file. The frames should be named in the format frame_xxxx.yuv where xxxx
125 stands for the frame number. The numbers have to be consecutive and start from
126 0000. The YUV frames are removed after they have been added to the video.
127
128 Args:
129 output_file_name(string): The name of the file to produce.
130 input_directory(string): The directory from which the YUV frames are read.
131 Return:
132 (bool): True if the frame stitching went OK.
133 """
134 output_file = open(output_file_name, "wb")
135 success = helper_functions.perform_action_on_all_files(
136 input_directory, 'barcode_', 'yuv', 0, _add_to_file_and_delete,
137 output_file=output_file)
138 output_file.close()
139 return success
140
141def _add_to_file_and_delete(output_file, file_name):
142 """Adds the contents of a file to a previously opened file.
143
144 Args:
145 output_file(file): The ouput file, previously opened.
146 file_name(string): The file name of the file to add to the output file.
147
148 Return:
149 (bool): True if successful, False otherwise.
150 """
151 input_file = open(file_name, "rb")
152 input_file_contents = input_file.read()
153 output_file.write(input_file_contents)
154 input_file.close()
155 return helper_functions.delete_file(file_name)
156
157
158def _overlay_barcode_and_base_frames(barcodes_file, base_file, output_file,
159 barcodes_component_sizes,
160 base_component_sizes):
161 """Overlays the next YUV frame from a file with a barcode.
162
163 Args:
164 barcodes_file(FileObject): The YUV file containing the barcodes (opened).
165 base_file(FileObject): The base YUV file (opened).
166 output_file(FileObject): The output overlaid file (opened).
167 barcodes_component_sizes(list of tuples): The width and height of each Y, U
168 and V plane of the barcodes YUV file.
169 base_component_sizes(list of tuples): The width and height of each Y, U and
170 V plane of the base YUV file.
171 Return:
172 (bool): True if there are more planes (i.e. frames) in the base file, false
173 otherwise.
174 """
175 # We will loop three times - once for the Y, U and V planes
176 for ((barcode_comp_width, barcode_comp_height),
177 (base_comp_width, base_comp_height)) in zip(barcodes_component_sizes,
178 base_component_sizes):
179 for base_row in range(base_comp_height):
180 barcode_plane_traversed = False
181 if (base_row < barcode_comp_height) and not barcode_plane_traversed:
182 barcode_plane = barcodes_file.read(barcode_comp_width)
183 if barcode_plane == "":
184 barcode_plane_traversed = True
185 else:
186 barcode_plane_traversed = True
187 base_plane = base_file.read(base_comp_width)
188
189 if base_plane == "":
190 return False
191
192 if not barcode_plane_traversed:
193 # Substitute part of the base component with the top component
194 output_file.write(barcode_plane)
195 base_plane = base_plane[barcode_comp_width:]
196 output_file.write(base_plane)
197 return True
198
199
200def overlay_yuv_files(barcode_width, barcode_height, base_width, base_height,
201 barcodes_file_name, base_file_name, output_file_name):
202 """Overlays two YUV files starting from the upper left corner of both.
203
204 Args:
205 barcode_width(int): The width of the barcode (to be overlaid).
206 barcode_height(int): The height of the barcode (to be overlaid).
207 base_width(int): The width of a frame of the base file.
208 base_height(int): The height of a frame of the base file.
209 barcodes_file_name(string): The name of the YUV file containing the YUV
210 barcodes.
211 base_file_name(string): The name of the base YUV file.
212 output_file_name(string): The name of the output file where the overlaid
213 video will be written.
214 """
215 # Component sizes = [Y_sizes, U_sizes, V_sizes]
216 barcodes_component_sizes = [(barcode_width, barcode_height),
217 (barcode_width/2, barcode_height/2),
218 (barcode_width/2, barcode_height/2)]
219 base_component_sizes = [(base_width, base_height),
220 (base_width/2, base_height/2),
221 (base_width/2, base_height/2)]
222
223 barcodes_file = open(barcodes_file_name, 'rb')
224 base_file = open(base_file_name, 'rb')
225 output_file = open(output_file_name, 'wb')
226
227 data_left = True
228 while data_left:
229 data_left = _overlay_barcode_and_base_frames(barcodes_file, base_file,
230 output_file,
231 barcodes_component_sizes,
232 base_component_sizes)
233
234 barcodes_file.close()
235 base_file.close()
236 output_file.close()
237
238
239def calculate_frames_number_from_yuv(yuv_width, yuv_height, file_name):
240 """Calculates the number of frames of a YUV video.
241
242 Args:
243 yuv_width(int): Width of a frame of the yuv file.
244 yuv_height(int): Height of a frame of the YUV file.
245 file_name(string): The name of the YUV file.
246 Return:
247 (int): The number of frames in the YUV file.
248 """
249 file_size = os.path.getsize(file_name)
250
251 y_plane_size = yuv_width * yuv_height
252 u_plane_size = (yuv_width/2) * (yuv_height/2) # Equals to V plane size too
253 frame_size = y_plane_size + (2 * u_plane_size)
254 return int(file_size/frame_size) # Should be int anyway
255
256
257def _parse_args():
258 """Registers the command-line options."""
259 usage = "usage: %prog [options]"
260 parser = optparse.OptionParser(usage=usage)
261
262 parser.add_option('--barcode_width', type='int',
263 default=_DEFAULT_BARCODE_WIDTH,
264 help=('Width of the barcodes to be overlaid on top of the'
265 ' base file. Default: %default'))
266 parser.add_option('--barcode_height', type='int', default=32,
267 help=('Height of the barcodes to be overlaid on top of the'
268 ' base file. Default: %default'))
269 parser.add_option('--base_frame_width', type='int', default=352,
270 help=('Width of the base YUV file\'s frames. '
271 'Default: %default'))
272 parser.add_option('--base_frame_height', type='int', default=288,
273 help=('Height of the top YUV file\'s frames. '
274 'Default: %default'))
275 parser.add_option('--barcodes_yuv', type='string',
276 default=_DEFAULT_BARCODES_FILE,
277 help=('The YUV file with the barcodes in YUV. '
278 'Default: %default'))
279 parser.add_option('--base_yuv', type='string', default='base.yuv',
280 help=('The base YUV file to be overlaid. '
281 'Default: %default'))
282 parser.add_option('--output_yuv', type='string', default='output.yuv',
283 help=('The output YUV file containing the base overlaid'
284 ' with the barcodes. Default: %default'))
285 parser.add_option('--png_barcodes_output_dir', type='string', default='.',
286 help=('Output directory where the PNG barcodes will be '
287 'generated. Default: %default'))
288 parser.add_option('--png_barcodes_input_dir', type='string', default='.',
289 help=('Input directory from where the PNG barcodes will be '
290 'read. Default: %default'))
291 parser.add_option('--yuv_barcodes_output_dir', type='string', default='.',
292 help=('Output directory where the YUV barcodes will be '
293 'generated. Default: %default'))
294 parser.add_option('--yuv_frames_input_dir', type='string', default='.',
295 help=('Input directory from where the YUV will be '
296 'read before combination. Default: %default'))
297 parser.add_option('--zxing_dir', type='string', default='zxing',
298 help=('Path to the Zxing barcodes library. '
299 'Default: %default'))
300 options = parser.parse_args()[0]
301 return options
302
303
304def _main():
305 """The main function.
306
307 A simple invocation will be:
308 ./tools/barcode_tools/barcode_encoder.py --barcode_height=32
309 --base_frame_width=352 --base_frame_height=288
310 --base_yuv=<path_and_name_of_base_file>
311 --output_yuv=<path and name_of_output_file>
312 """
313 options = _parse_args()
314 # The barcodes with will be different than the base frame width only if
315 # explicitly specified at the command line.
316 if options.barcode_width == _DEFAULT_BARCODE_WIDTH:
317 options.barcode_width = options.base_frame_width
318 # If the user provides a value for the barcodes YUV video file, we will keep
319 # it. Otherwise we create a temp file which is removed after it has been used.
320 keep_barcodes_yuv_file = False
321 if options.barcodes_yuv != _DEFAULT_BARCODES_FILE:
322 keep_barcodes_yuv_file = True
323
324 # Calculate the number of barcodes - it is equal to the number of frames in
325 # the base file.
326 number_of_barcodes = calculate_frames_number_from_yuv(
327 options.base_frame_width, options.base_frame_height, options.base_yuv)
328
329 script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
330 zxing_dir = os.path.join(script_dir, '..', 'third_party', 'zxing')
331 # Generate barcodes - will generate them in PNG.
332 generate_upca_barcodes(number_of_barcodes, options.barcode_width,
333 options.barcode_height,
334 output_directory=options.png_barcodes_output_dir,
335 path_to_zxing=zxing_dir)
336 # Convert the PNG barcodes to to YUV format.
337 convert_png_to_yuv_barcodes(options.png_barcodes_input_dir,
338 options.yuv_barcodes_output_dir)
339 # Combine the YUV barcodes into one YUV file.
340 combine_yuv_frames_into_one_file(options.barcodes_yuv,
341 input_directory=options.yuv_frames_input_dir)
342 # Overlay the barcodes over the base file.
343 overlay_yuv_files(options.barcode_width, options.barcode_height,
344 options.base_frame_width, options.base_frame_height,
345 options.barcodes_yuv, options.base_yuv, options.output_yuv)
346
347 if not keep_barcodes_yuv_file:
348 # Remove the temporary barcodes YUV file
349 helper_functions.delete_file(options.barcodes_yuv)
350
351
352if __name__ == '__main__':
353 sys.exit(_main())