 1b0a02e12e
			
		
	
	1b0a02e12e
	
	
	
		
			
			Review URL: https://webrtc-codereview.appspot.com/761006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2688 4adac7df-926f-26a2-2b94-8c16560cd09d
		
			
				
	
	
		
			358 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python
 | |
| # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 | |
| #
 | |
| # Use of this source code is governed by a BSD-style license
 | |
| # that can be found in the LICENSE file in the root of the source
 | |
| # tree. An additional intellectual property rights grant can be found
 | |
| # in the file PATENTS.  All contributing project authors may
 | |
| # be found in the AUTHORS file in the root of the source tree.
 | |
| 
 | |
| import optparse
 | |
| import os
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| import helper_functions
 | |
| 
 | |
| _DEFAULT_BARCODE_WIDTH = 352
 | |
| _DEFAULT_BARCODES_FILE = 'barcodes.yuv'
 | |
| 
 | |
| 
 | |
| def generate_upca_barcodes(number_of_barcodes, barcode_width, barcode_height,
 | |
|                            output_directory='.',
 | |
|                            path_to_zxing='zxing-read-only'):
 | |
|   """Generates UPC-A barcodes.
 | |
| 
 | |
|   This function generates a number_of_barcodes UPC-A barcodes. The function
 | |
|   calls an example Java encoder from the Zxing library. The barcodes are
 | |
|   generated as PNG images. The width of the barcodes shouldn't be less than 102
 | |
|   pixels because otherwise Zxing can't properly generate the barcodes.
 | |
| 
 | |
|   Args:
 | |
|     number_of_barcodes(int): The number of barcodes to generate.
 | |
|     barcode_width(int): Width of barcode in pixels.
 | |
|     barcode_height(int): Height of barcode in pixels.
 | |
|     output_directory(string): Output directory where to store generated
 | |
|       barcodes.
 | |
|     path_to_zxing(string): The path to Zxing.
 | |
| 
 | |
|   Return:
 | |
|     (bool): True if the conversion is successful.
 | |
|   """
 | |
|   base_file_name = os.path.join(output_directory, "barcode_")
 | |
|   jars = helper_functions.form_jars_string(path_to_zxing)
 | |
|   command_line_encoder ='com.google.zxing.client.j2se.CommandLineEncoder'
 | |
|   barcode_width = str(barcode_width)
 | |
|   barcode_height = str(barcode_height)
 | |
| 
 | |
|   errors = False
 | |
|   for i in range(number_of_barcodes):
 | |
|     suffix = helper_functions.zero_pad(i)
 | |
|     # Barcodes starting from 0
 | |
|     content = helper_functions.zero_pad(i, 11)
 | |
|     output_file_name = base_file_name + suffix + ".png"
 | |
| 
 | |
|     command = ["java", "-cp", jars, command_line_encoder,
 | |
|                "--barcode_format=UPC_A", "--height=%s" % barcode_height,
 | |
|                "--width=%s" % barcode_width,
 | |
|                "--output=%s" % (output_file_name), "%s" % (content)]
 | |
|     try:
 | |
|       helper_functions.run_shell_command(
 | |
|           command, msg=('Error during barcode %s generation' % content))
 | |
|     except helper_functions.HelperError, err:
 | |
|       print err
 | |
|       errors = True
 | |
|   return not errors
 | |
| 
 | |
| 
 | |
| def convert_png_to_yuv_barcodes(input_directory='.', output_directory='.'):
 | |
|   """Converts PNG barcodes to YUV barcode images.
 | |
| 
 | |
|   This function reads all the PNG files from the input directory which are in
 | |
|   the format frame_xxxx.png, where xxxx is the number of the frame, starting
 | |
|   from 0000. The frames should be consecutive numbers. The output YUV file is
 | |
|   named frame_xxxx.yuv. The function uses ffmpeg to do the conversion.
 | |
| 
 | |
|   Args:
 | |
|     input_directory(string): The input direcotry to read the PNG barcodes from.
 | |
|     output_directory(string): The putput directory to write the YUV files to.
 | |
|   Return:
 | |
|     (bool): True if the conversion was without errors.
 | |
|   """
 | |
|   return helper_functions.perform_action_on_all_files(
 | |
|       input_directory, 'barcode_', 'png', 0, _convert_to_yuv_and_delete,
 | |
|       output_directory=output_directory, pattern='barcode_')
 | |
| 
 | |
| 
 | |
| def _convert_to_yuv_and_delete(output_directory, file_name, pattern):
 | |
|   """Converts a PNG file to a YUV file and deletes the PNG file.
 | |
| 
 | |
|   Args:
 | |
|     output_directory(string): The output directory for the YUV file.
 | |
|     file_name(string): The PNG file name.
 | |
|     pattern(string): The file pattern of the PNG/YUV file. The PNG/YUV files are
 | |
|       named patternxx..x.png/yuv, where xx..x are digits starting from 00..0.
 | |
|   Return:
 | |
|     (bool): True upon successful conversion, false otherwise.
 | |
|   """
 | |
|   # Pattern should be in file name
 | |
|   if not pattern in file_name:
 | |
|     return False
 | |
|   pattern_position = file_name.rfind(pattern)
 | |
| 
 | |
|   # Strip the path to the PNG file and replace the png extension with yuv
 | |
|   yuv_file_name = file_name[pattern_position:-3] + 'yuv'
 | |
|   yuv_file_name = os.path.join(output_directory, yuv_file_name)
 | |
| 
 | |
|   command = ['ffmpeg', '-i', '%s' % (file_name), '-pix_fmt', 'yuv420p',
 | |
|              '%s' % (yuv_file_name)]
 | |
|   try:
 | |
|     helper_functions.run_shell_command(
 | |
|         command, msg=('Error during PNG to YUV conversion of %s' %
 | |
|                        file_name));
 | |
|     os.remove(file_name)
 | |
|   except helper_functions.HelperError, err:
 | |
|     print err
 | |
|     return False
 | |
|   return True
 | |
| 
 | |
| 
 | |
| def combine_yuv_frames_into_one_file(output_file_name, input_directory='.'):
 | |
|   """Combines several YUV frames into one YUV video file.
 | |
| 
 | |
|   The function combines the YUV frames from input_directory into one YUV video
 | |
|   file. The frames should be named in the format frame_xxxx.yuv where xxxx
 | |
|   stands for the frame number. The numbers have to be consecutive and start from
 | |
|   0000. The YUV frames are removed after they have been added to the video.
 | |
| 
 | |
|   Args:
 | |
|     output_file_name(string): The name of the file to produce.
 | |
|     input_directory(string): The directory from which the YUV frames are read.
 | |
|   Return:
 | |
|     (bool): True if the frame stitching went OK.
 | |
|   """
 | |
|   output_file = open(output_file_name, "wb")
 | |
|   success = helper_functions.perform_action_on_all_files(
 | |
|       input_directory, 'barcode_', 'yuv', 0, _add_to_file_and_delete,
 | |
|       output_file=output_file)
 | |
|   output_file.close()
 | |
|   return success
 | |
| 
 | |
| def _add_to_file_and_delete(output_file, file_name):
 | |
|   """Adds the contents of a file to a previously opened file.
 | |
| 
 | |
|   Args:
 | |
|     output_file(file): The ouput file, previously opened.
 | |
|     file_name(string): The file name of the file to add to the output file.
 | |
| 
 | |
|   Return:
 | |
|     (bool): True if successful, False otherwise.
 | |
|   """
 | |
|   input_file = open(file_name, "rb")
 | |
|   input_file_contents = input_file.read()
 | |
|   output_file.write(input_file_contents)
 | |
|   input_file.close()
 | |
|   try:
 | |
|     os.remove(file_name)
 | |
|   except Exception as e:
 | |
|     sys.stderr.write('Error in deleting file %s' % file_name)
 | |
|     return False
 | |
|   return True
 | |
| 
 | |
| 
 | |
| def _overlay_barcode_and_base_frames(barcodes_file, base_file, output_file,
 | |
|                                      barcodes_component_sizes,
 | |
|                                      base_component_sizes):
 | |
|   """Overlays the next YUV frame from a file with a barcode.
 | |
| 
 | |
|   Args:
 | |
|     barcodes_file(FileObject): The YUV file containing the barcodes (opened).
 | |
|     base_file(FileObject): The base YUV file (opened).
 | |
|     output_file(FileObject): The output overlaid file (opened).
 | |
|     barcodes_component_sizes(list of tuples): The width and height of each Y, U
 | |
|       and V plane of the barcodes YUV file.
 | |
|     base_component_sizes(list of tuples): The width and height of each Y, U and
 | |
|       V plane of the base YUV file.
 | |
|   Return:
 | |
|     (bool): True if there are more planes (i.e. frames) in the base file, false
 | |
|       otherwise.
 | |
|   """
 | |
|   # We will loop three times - once for the Y, U and V planes
 | |
|   for ((barcode_comp_width, barcode_comp_height),
 | |
|       (base_comp_width, base_comp_height)) in zip(barcodes_component_sizes,
 | |
|                                                   base_component_sizes):
 | |
|     for base_row in range(base_comp_height):
 | |
|       barcode_plane_traversed = False
 | |
|       if (base_row < barcode_comp_height) and not barcode_plane_traversed:
 | |
|         barcode_plane = barcodes_file.read(barcode_comp_width)
 | |
|         if barcode_plane == "":
 | |
|           barcode_plane_traversed = True
 | |
|       else:
 | |
|         barcode_plane_traversed = True
 | |
|       base_plane = base_file.read(base_comp_width)
 | |
| 
 | |
|       if base_plane == "":
 | |
|         return False
 | |
| 
 | |
|       if not barcode_plane_traversed:
 | |
|         # Substitute part of the base component with the top component
 | |
|         output_file.write(barcode_plane)
 | |
|         base_plane = base_plane[barcode_comp_width:]
 | |
|       output_file.write(base_plane)
 | |
|   return True
 | |
| 
 | |
| 
 | |
| def overlay_yuv_files(barcode_width, barcode_height, base_width, base_height,
 | |
|                       barcodes_file_name, base_file_name, output_file_name):
 | |
|   """Overlays two YUV files starting from the upper left corner of both.
 | |
| 
 | |
|   Args:
 | |
|     barcode_width(int): The width of the barcode (to be overlaid).
 | |
|     barcode_height(int): The height of the barcode (to be overlaid).
 | |
|     base_width(int): The width of a frame of the base file.
 | |
|     base_height(int): The height of a frame of the base file.
 | |
|     barcodes_file_name(string): The name of the YUV file containing the YUV
 | |
|       barcodes.
 | |
|     base_file_name(string): The name of the base YUV file.
 | |
|     output_file_name(string): The name of the output file where the overlaid
 | |
|       video will be written.
 | |
|   """
 | |
|   # Component sizes = [Y_sizes, U_sizes, V_sizes]
 | |
|   barcodes_component_sizes = [(barcode_width, barcode_height),
 | |
|                               (barcode_width/2, barcode_height/2),
 | |
|                               (barcode_width/2, barcode_height/2)]
 | |
|   base_component_sizes = [(base_width, base_height),
 | |
|                           (base_width/2, base_height/2),
 | |
|                           (base_width/2, base_height/2)]
 | |
| 
 | |
|   barcodes_file = open(barcodes_file_name, 'rb')
 | |
|   base_file = open(base_file_name, 'rb')
 | |
|   output_file = open(output_file_name, 'wb')
 | |
| 
 | |
|   data_left = True
 | |
|   while data_left:
 | |
|     data_left = _overlay_barcode_and_base_frames(barcodes_file, base_file,
 | |
|                                                  output_file,
 | |
|                                                  barcodes_component_sizes,
 | |
|                                                  base_component_sizes)
 | |
| 
 | |
|   barcodes_file.close()
 | |
|   base_file.close()
 | |
|   output_file.close()
 | |
| 
 | |
| 
 | |
| def calculate_frames_number_from_yuv(yuv_width, yuv_height, file_name):
 | |
|   """Calculates the number of frames of a YUV video.
 | |
| 
 | |
|   Args:
 | |
|     yuv_width(int): Width of a frame of the yuv file.
 | |
|     yuv_height(int): Height of a frame of the YUV file.
 | |
|     file_name(string): The name of the YUV file.
 | |
|   Return:
 | |
|     (int): The number of frames in the YUV file.
 | |
|   """
 | |
|   file_size = os.path.getsize(file_name)
 | |
| 
 | |
|   y_plane_size = yuv_width * yuv_height
 | |
|   u_plane_size = (yuv_width/2) * (yuv_height/2)  # Equals to V plane size too
 | |
|   frame_size = y_plane_size + (2 * u_plane_size)
 | |
|   return int(file_size/frame_size)  # Should be int anyway
 | |
| 
 | |
| 
 | |
| def _parse_args():
 | |
|   """Registers the command-line options."""
 | |
|   usage = "usage: %prog [options]"
 | |
|   parser = optparse.OptionParser(usage=usage)
 | |
| 
 | |
|   parser.add_option('--barcode_width', type='int',
 | |
|                     default=_DEFAULT_BARCODE_WIDTH,
 | |
|                     help=('Width of the barcodes to be overlaid on top of the'
 | |
|                           ' base file. Default: %default'))
 | |
|   parser.add_option('--barcode_height', type='int', default=32,
 | |
|                     help=('Height of the barcodes to be overlaid on top of the'
 | |
|                           ' base file. Default: %default'))
 | |
|   parser.add_option('--base_frame_width', type='int', default=352,
 | |
|                     help=('Width of the base YUV file\'s frames. '
 | |
|                           'Default: %default'))
 | |
|   parser.add_option('--base_frame_height', type='int', default=288,
 | |
|                     help=('Height of the top YUV file\'s frames. '
 | |
|                           'Default: %default'))
 | |
|   parser.add_option('--barcodes_yuv', type='string',
 | |
|                     default=_DEFAULT_BARCODES_FILE,
 | |
|                     help=('The YUV file with the barcodes in YUV. '
 | |
|                           'Default: %default'))
 | |
|   parser.add_option('--base_yuv', type='string', default='base.yuv',
 | |
|                     help=('The base YUV file to be overlaid. '
 | |
|                           'Default: %default'))
 | |
|   parser.add_option('--output_yuv', type='string', default='output.yuv',
 | |
|                     help=('The output YUV file containing the base overlaid'
 | |
|                           ' with the barcodes. Default: %default'))
 | |
|   parser.add_option('--png_barcodes_output_dir', type='string', default='.',
 | |
|                     help=('Output directory where the PNG barcodes will be '
 | |
|                           'generated. Default: %default'))
 | |
|   parser.add_option('--png_barcodes_input_dir', type='string', default='.',
 | |
|                     help=('Input directory from where the PNG barcodes will be '
 | |
|                           'read. Default: %default'))
 | |
|   parser.add_option('--yuv_barcodes_output_dir', type='string', default='.',
 | |
|                     help=('Output directory where the YUV barcodes will be '
 | |
|                           'generated. Default: %default'))
 | |
|   parser.add_option('--yuv_frames_input_dir', type='string', default='.',
 | |
|                     help=('Input directory from where the YUV will be '
 | |
|                           'read before combination. Default: %default'))
 | |
|   parser.add_option('--zxing_dir', type='string', default='zxing',
 | |
|                     help=('Path to the Zxing barcodes library. '
 | |
|                           'Default: %default'))
 | |
|   options = parser.parse_args()[0]
 | |
|   return options
 | |
| 
 | |
| 
 | |
| def _main():
 | |
|   """The main function.
 | |
| 
 | |
|   A simple invocation will be:
 | |
|   ./tools/barcode_tools/barcode_encoder.py --barcode_height=32
 | |
|   --base_frame_width=352 --base_frame_height=288
 | |
|   --base_yuv=<path_and_name_of_base_file>
 | |
|   --output_yuv=<path and name_of_output_file>
 | |
|   """
 | |
|   options = _parse_args()
 | |
|   # The barcodes with will be different than the base frame width only if
 | |
|   # explicitly specified at the command line.
 | |
|   if options.barcode_width == _DEFAULT_BARCODE_WIDTH:
 | |
|     options.barcode_width = options.base_frame_width
 | |
|   # If the user provides a value for the barcodes YUV video file, we will keep
 | |
|   # it. Otherwise we create a temp file which is removed after it has been used.
 | |
|   keep_barcodes_yuv_file = False
 | |
|   if options.barcodes_yuv != _DEFAULT_BARCODES_FILE:
 | |
|     keep_barcodes_yuv_file = True
 | |
| 
 | |
|   # Calculate the number of barcodes - it is equal to the number of frames in
 | |
|   # the base file.
 | |
|   number_of_barcodes = calculate_frames_number_from_yuv(
 | |
|       options.base_frame_width, options.base_frame_height, options.base_yuv)
 | |
| 
 | |
|   script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
 | |
|   zxing_dir = os.path.join(script_dir, 'third_party', 'zxing')
 | |
|   # Generate barcodes - will generate them in PNG.
 | |
|   generate_upca_barcodes(number_of_barcodes, options.barcode_width,
 | |
|                          options.barcode_height,
 | |
|                          output_directory=options.png_barcodes_output_dir,
 | |
|                          path_to_zxing=zxing_dir)
 | |
|   # Convert the PNG barcodes to to YUV format.
 | |
|   convert_png_to_yuv_barcodes(options.png_barcodes_input_dir,
 | |
|                               options.yuv_barcodes_output_dir)
 | |
|   # Combine the YUV barcodes into one YUV file.
 | |
|   combine_yuv_frames_into_one_file(options.barcodes_yuv,
 | |
|                                    input_directory=options.yuv_frames_input_dir)
 | |
|   # Overlay the barcodes over the base file.
 | |
|   overlay_yuv_files(options.barcode_width, options.barcode_height,
 | |
|                     options.base_frame_width, options.base_frame_height,
 | |
|                     options.barcodes_yuv, options.base_yuv, options.output_yuv)
 | |
| 
 | |
|   if not keep_barcodes_yuv_file:
 | |
|     # Remove the temporary barcodes YUV file
 | |
|     os.remove(options.barcodes_yuv)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|   sys.exit(_main()) |