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