blob: 429866e4fc207bfa3c9738ce1ad77697fa330b96 [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));
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000113 os.remove(file_name)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000114 except helper_functions.HelperError, err:
115 print err
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000116 return False
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000117 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()
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000155 try:
156 os.remove(file_name)
157 except Exception as e:
158 sys.stderr.write('Error in deleting file %s' % file_name)
159 return False
160 return True
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000161
162
163def _overlay_barcode_and_base_frames(barcodes_file, base_file, output_file,
164 barcodes_component_sizes,
165 base_component_sizes):
166 """Overlays the next YUV frame from a file with a barcode.
167
168 Args:
169 barcodes_file(FileObject): The YUV file containing the barcodes (opened).
170 base_file(FileObject): The base YUV file (opened).
171 output_file(FileObject): The output overlaid file (opened).
172 barcodes_component_sizes(list of tuples): The width and height of each Y, U
173 and V plane of the barcodes YUV file.
174 base_component_sizes(list of tuples): The width and height of each Y, U and
175 V plane of the base YUV file.
176 Return:
177 (bool): True if there are more planes (i.e. frames) in the base file, false
178 otherwise.
179 """
180 # We will loop three times - once for the Y, U and V planes
181 for ((barcode_comp_width, barcode_comp_height),
182 (base_comp_width, base_comp_height)) in zip(barcodes_component_sizes,
183 base_component_sizes):
184 for base_row in range(base_comp_height):
185 barcode_plane_traversed = False
186 if (base_row < barcode_comp_height) and not barcode_plane_traversed:
187 barcode_plane = barcodes_file.read(barcode_comp_width)
188 if barcode_plane == "":
189 barcode_plane_traversed = True
190 else:
191 barcode_plane_traversed = True
192 base_plane = base_file.read(base_comp_width)
193
194 if base_plane == "":
195 return False
196
197 if not barcode_plane_traversed:
198 # Substitute part of the base component with the top component
199 output_file.write(barcode_plane)
200 base_plane = base_plane[barcode_comp_width:]
201 output_file.write(base_plane)
202 return True
203
204
205def overlay_yuv_files(barcode_width, barcode_height, base_width, base_height,
206 barcodes_file_name, base_file_name, output_file_name):
207 """Overlays two YUV files starting from the upper left corner of both.
208
209 Args:
210 barcode_width(int): The width of the barcode (to be overlaid).
211 barcode_height(int): The height of the barcode (to be overlaid).
212 base_width(int): The width of a frame of the base file.
213 base_height(int): The height of a frame of the base file.
214 barcodes_file_name(string): The name of the YUV file containing the YUV
215 barcodes.
216 base_file_name(string): The name of the base YUV file.
217 output_file_name(string): The name of the output file where the overlaid
218 video will be written.
219 """
220 # Component sizes = [Y_sizes, U_sizes, V_sizes]
221 barcodes_component_sizes = [(barcode_width, barcode_height),
222 (barcode_width/2, barcode_height/2),
223 (barcode_width/2, barcode_height/2)]
224 base_component_sizes = [(base_width, base_height),
225 (base_width/2, base_height/2),
226 (base_width/2, base_height/2)]
227
228 barcodes_file = open(barcodes_file_name, 'rb')
229 base_file = open(base_file_name, 'rb')
230 output_file = open(output_file_name, 'wb')
231
232 data_left = True
233 while data_left:
234 data_left = _overlay_barcode_and_base_frames(barcodes_file, base_file,
235 output_file,
236 barcodes_component_sizes,
237 base_component_sizes)
238
239 barcodes_file.close()
240 base_file.close()
241 output_file.close()
242
243
244def calculate_frames_number_from_yuv(yuv_width, yuv_height, file_name):
245 """Calculates the number of frames of a YUV video.
246
247 Args:
248 yuv_width(int): Width of a frame of the yuv file.
249 yuv_height(int): Height of a frame of the YUV file.
250 file_name(string): The name of the YUV file.
251 Return:
252 (int): The number of frames in the YUV file.
253 """
254 file_size = os.path.getsize(file_name)
255
256 y_plane_size = yuv_width * yuv_height
257 u_plane_size = (yuv_width/2) * (yuv_height/2) # Equals to V plane size too
258 frame_size = y_plane_size + (2 * u_plane_size)
259 return int(file_size/frame_size) # Should be int anyway
260
261
262def _parse_args():
263 """Registers the command-line options."""
264 usage = "usage: %prog [options]"
265 parser = optparse.OptionParser(usage=usage)
266
267 parser.add_option('--barcode_width', type='int',
268 default=_DEFAULT_BARCODE_WIDTH,
269 help=('Width of the barcodes to be overlaid on top of the'
270 ' base file. Default: %default'))
271 parser.add_option('--barcode_height', type='int', default=32,
272 help=('Height of the barcodes to be overlaid on top of the'
273 ' base file. Default: %default'))
274 parser.add_option('--base_frame_width', type='int', default=352,
275 help=('Width of the base YUV file\'s frames. '
276 'Default: %default'))
277 parser.add_option('--base_frame_height', type='int', default=288,
278 help=('Height of the top YUV file\'s frames. '
279 'Default: %default'))
280 parser.add_option('--barcodes_yuv', type='string',
281 default=_DEFAULT_BARCODES_FILE,
282 help=('The YUV file with the barcodes in YUV. '
283 'Default: %default'))
284 parser.add_option('--base_yuv', type='string', default='base.yuv',
285 help=('The base YUV file to be overlaid. '
286 'Default: %default'))
287 parser.add_option('--output_yuv', type='string', default='output.yuv',
288 help=('The output YUV file containing the base overlaid'
289 ' with the barcodes. Default: %default'))
290 parser.add_option('--png_barcodes_output_dir', type='string', default='.',
291 help=('Output directory where the PNG barcodes will be '
292 'generated. Default: %default'))
293 parser.add_option('--png_barcodes_input_dir', type='string', default='.',
294 help=('Input directory from where the PNG barcodes will be '
295 'read. Default: %default'))
296 parser.add_option('--yuv_barcodes_output_dir', type='string', default='.',
297 help=('Output directory where the YUV barcodes will be '
298 'generated. Default: %default'))
299 parser.add_option('--yuv_frames_input_dir', type='string', default='.',
300 help=('Input directory from where the YUV will be '
301 'read before combination. Default: %default'))
302 parser.add_option('--zxing_dir', type='string', default='zxing',
303 help=('Path to the Zxing barcodes library. '
304 'Default: %default'))
305 options = parser.parse_args()[0]
306 return options
307
308
309def _main():
310 """The main function.
311
312 A simple invocation will be:
313 ./tools/barcode_tools/barcode_encoder.py --barcode_height=32
314 --base_frame_width=352 --base_frame_height=288
315 --base_yuv=<path_and_name_of_base_file>
316 --output_yuv=<path and name_of_output_file>
317 """
318 options = _parse_args()
319 # The barcodes with will be different than the base frame width only if
320 # explicitly specified at the command line.
321 if options.barcode_width == _DEFAULT_BARCODE_WIDTH:
322 options.barcode_width = options.base_frame_width
323 # If the user provides a value for the barcodes YUV video file, we will keep
324 # it. Otherwise we create a temp file which is removed after it has been used.
325 keep_barcodes_yuv_file = False
326 if options.barcodes_yuv != _DEFAULT_BARCODES_FILE:
327 keep_barcodes_yuv_file = True
328
329 # Calculate the number of barcodes - it is equal to the number of frames in
330 # the base file.
331 number_of_barcodes = calculate_frames_number_from_yuv(
332 options.base_frame_width, options.base_frame_height, options.base_yuv)
333
334 script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
vspasova@webrtc.org28655422012-08-15 14:35:40 +0000335 zxing_dir = os.path.join(script_dir, 'third_party', 'zxing')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000336 # Generate barcodes - will generate them in PNG.
337 generate_upca_barcodes(number_of_barcodes, options.barcode_width,
338 options.barcode_height,
339 output_directory=options.png_barcodes_output_dir,
340 path_to_zxing=zxing_dir)
341 # Convert the PNG barcodes to to YUV format.
342 convert_png_to_yuv_barcodes(options.png_barcodes_input_dir,
343 options.yuv_barcodes_output_dir)
344 # Combine the YUV barcodes into one YUV file.
345 combine_yuv_frames_into_one_file(options.barcodes_yuv,
346 input_directory=options.yuv_frames_input_dir)
347 # Overlay the barcodes over the base file.
348 overlay_yuv_files(options.barcode_width, options.barcode_height,
349 options.base_frame_width, options.base_frame_height,
350 options.barcodes_yuv, options.base_yuv, options.output_yuv)
351
352 if not keep_barcodes_yuv_file:
353 # Remove the temporary barcodes YUV file
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000354 os.remove(options.barcodes_yuv)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000355
356
357if __name__ == '__main__':
358 sys.exit(_main())