diff --git a/tools/barcode_tools/README b/tools/barcode_tools/README deleted file mode 100644 index 880cc7d9cf..0000000000 --- a/tools/barcode_tools/README +++ /dev/null @@ -1,17 +0,0 @@ -This file explains how to set up the Zebra Crossing (Zxing) library in order to -use it in the barcode encoder and decoder tools. Zxing could be found at: -https://code.google.com/p/zxing/ - -After checkout, the relevant files from Zxing should be in third_party/zxing, -relative to this README. - -In order to run barcode_encoder.py and barcode_decoder.py we need to have build -two jar files: zxing/core/core.jar and zxing/javase/javase.jar. In order to -build these we have to have Ant already installed. Building is as simple as -running the build_zxing.py script: -./build_zxing.py, -which should automatically call ant with the respective build files from the -Zxing checkout. - -For more information on how to run barcode_encoder.py and barcode_decoder.py -check the documentation in the main functions inside these tools. diff --git a/tools/barcode_tools/DEPS b/webrtc/tools/barcode_tools/DEPS similarity index 100% rename from tools/barcode_tools/DEPS rename to webrtc/tools/barcode_tools/DEPS diff --git a/webrtc/tools/barcode_tools/README b/webrtc/tools/barcode_tools/README new file mode 100644 index 0000000000..00c5136c88 --- /dev/null +++ b/webrtc/tools/barcode_tools/README @@ -0,0 +1,34 @@ +This file explains how to get the dependencies needed for the barcode tools. + +barcode_encoder.py +================== +This script depends on: +* Zxing (Java version) +* Ant (must be installed manually) +* Java + +To automatically download Zxing for the encoder script, checkout this directory +as a separate gclient solution, like this: +gclient config http://webrtc.googlecode.com/svn/trunk/webrtc/tools/barcode_tools +gclient sync +Then the Zxing Java source code will be put in third_party/zxing. + +In order to run barcode_encoder.py you then need to build: +* zxing/core/core.jar +* zxing/javase/javase.jar +These are compiled using Ant by running build_zxing.py: +python build_zxing.py + +For more info about Zxing, see https://code.google.com/p/zxing/ + + +barcode_decoder.py +================== +This script depends on: +* Zxing (C++ version). You need to checkout from Subversion and build the libs + and zxing SCons targets. SVN URL: http://zxing.googlecode.com/svn/trunk/cpp +* FFMPEG fmpeg 0.11.1 + +These dependencies must be precompiled separately before running the script. +Make sure to add FFMPEG to the PATH environment variable and provide the path +to the zxing executable using the mandatory command line flag to the script. diff --git a/tools/barcode_tools/barcode_decoder.py b/webrtc/tools/barcode_tools/barcode_decoder.py similarity index 60% rename from tools/barcode_tools/barcode_decoder.py rename to webrtc/tools/barcode_tools/barcode_decoder.py index 4fddfba1e5..40f7c1c1dc 100755 --- a/tools/barcode_tools/barcode_decoder.py +++ b/webrtc/tools/barcode_tools/barcode_decoder.py @@ -13,11 +13,9 @@ import sys import helper_functions -_DEFAULT_BARCODE_WIDTH = 352 - def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height, - output_directory = '.'): + output_directory, ffmpeg_dir=None): """Converts a YUV video file into PNG frames. The function uses ffmpeg to convert the YUV file. The output of ffmpeg is in @@ -29,25 +27,25 @@ def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height, yuv_frame_height(int): The height of one YUV frame. output_directory(string): The output directory where the PNG frames will be stored. + ffmpeg_dir(string): The directory containing the ffmpeg executable. If + omitted, the PATH will be searched for it. Return: (bool): True if the conversion was OK. """ size_string = str(yuv_frame_width) + 'x' + str(yuv_frame_height) output_files_pattern = os.path.join(output_directory, 'frame_%04d.png') - ffmpeg_executable = 'ffmpeg' - if sys.platform == 'win32': - if os.getenv('FFMPEG_HOME'): - ffmpeg_executable = os.path.join(os.getenv('FFMPEG_HOME'), 'bin', - 'ffmpeg.exe') - else: - ffmpeg_executable = 'ffmpeg.exe' + ffmpeg_executable = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg' + if ffmpeg_dir: + ffmpeg_executable = os.path.join(ffmpeg_dir, ffmpeg_executable) command = [ffmpeg_executable, '-s', '%s' % size_string, '-i', '%s' % yuv_file_name, '-f', 'image2', '-vcodec', 'png', '%s' % output_files_pattern] try: + print 'Converting YUV file to PNG images (may take a while)...' + print ' '.join(command) helper_functions.run_shell_command( - command, msg='Error during YUV to PNG conversion') + command, fail_msg='Error during YUV to PNG conversion') except helper_functions.HelperError, err: print >> sys.stderr, 'Error executing command: %s. Error: %s' % (command, err) @@ -55,69 +53,55 @@ def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height, return True -def decode_frames(barcode_width, barcode_height, input_directory='.', - path_to_zxing='zxing-read-only'): +def decode_frames(input_directory, zxing_dir=None): """Decodes the barcodes overlaid in each frame. - The function uses the example Java command-line tool from the Zxing - distribution to decode the barcode in every PNG frame from the input - directory. The frames should be named frame_xxxx.png, where xxxx is the frame - number. The frame numbers should be consecutive and should start from 0001. + The function uses the Zxing command-line tool from the Zxing C++ distribution + to decode the barcode in every PNG frame from the input directory. The frames + should be named frame_xxxx.png, where xxxx is the frame number. The frame + numbers should be consecutive and should start from 0001. The decoding results in a frame_xxxx.txt file for every successfully decoded barcode. This file contains the decoded barcode as 12-digit string (UPC-A format: 11 digits content + one check digit). Args: - barcode_width(int): Width of the barcode. - barcode_height(int): Height of the barcode. input_directory(string): The input directory from where the PNG frames are read. - path_to_zxing(string): The path to Zxing. + zxing_dir(string): The directory containing the zxing executable. If + omitted, the PATH will be searched for it. Return: (bool): True if the decoding went without errors. """ - jars = helper_functions.form_jars_string(path_to_zxing) - command_line_decoder = 'com.google.zxing.client.j2se.CommandLineRunner' + zxing_executable = 'zxing.exe' if sys.platform == 'win32' else 'zxing' + if zxing_dir: + zxing_executable = os.path.join(zxing_dir, zxing_executable) + print 'Decoding barcodes from PNG files with %s...' % zxing_executable return helper_functions.perform_action_on_all_files( directory=input_directory, file_pattern='frame_', file_extension='png', start_number=1, action=_decode_barcode_in_file, - barcode_width=barcode_width, barcode_height=barcode_height, jars=jars, - command_line_decoder=command_line_decoder) + command_line_decoder=zxing_executable) -def _decode_barcode_in_file(file_name, barcode_width, barcode_height, jars, - command_line_decoder): +def _decode_barcode_in_file(file_name, command_line_decoder): """Decodes the barcode in the upper left corner of a PNG file. Args: file_name(string): File name of the PNG file. - barcode_width(int): Width of the barcode (in pixels). - barcode_height(int): Height of the barcode (in pixels) - jars(string): The Zxing core and javase string. command_line_decoder(string): The ZXing command-line decoding tool. Return: (bool): True upon success, False otherwise. """ - java_executable = 'java' - if sys.platform == 'win32': - if os.getenv('JAVA_HOME'): - java_executable = os.path.join(os.getenv('JAVA_HOME'), 'bin', - 'java.exe') - else: - java_executable = 'java.exe' - command = [java_executable, '-Djava.awt.headless=true', '-cp', '%s' % jars, - '%s' % command_line_decoder, '--products_only', - '--dump_results', '--brief', '--crop=%d,%d,%d,%d' % - (0, 0, barcode_width, barcode_height), - '%s' % file_name] + command = [command_line_decoder, '--try-harder', '--dump-raw', file_name] try: out = helper_functions.run_shell_command( - command, msg='Error during decoding of %s' % file_name) - if not 'Success' in out: - print >> sys.stderr, 'Barcode in %s cannot be decoded\n' % file_name - return False + command, fail_msg='Error during decoding of %s' % file_name) + print 'Image %s : decoded barcode: %s' % (file_name, out) + text_file = open('%s.txt' % file_name[:-4], 'w') + text_file.write(out) + text_file.close() except helper_functions.HelperError, err: + print >> sys.stderr, 'Barcode in %s cannot be decoded.' % file_name print >> sys.stderr, err return False return True @@ -134,6 +118,7 @@ def _generate_stats_file(stats_file_name, input_directory='.'): file_prefix = os.path.join(input_directory, 'frame_') stats_file = open(stats_file_name, 'w') + print 'Generating stats file: %s' % stats_file_name for i in range(1, _count_frames_in(input_directory=input_directory) + 1): frame_number = helper_functions.zero_pad(i) barcode_file_name = file_prefix + frame_number + '.txt' @@ -236,30 +221,29 @@ def _parse_args(): usage = "usage: %prog [options]" parser = optparse.OptionParser(usage=usage) - parser.add_option('--yuv_frame_width', type='int', default=352, - help=('Width of the YUV file\'s frames. ' - 'Default: %default')) - parser.add_option('--yuv_frame_height', type='int', default=288, - help=('Height of the YUV file\'s frames. ' - 'Default: %default')) - parser.add_option('--barcode_width', type='int', - default=_DEFAULT_BARCODE_WIDTH, - help=('Width of the barcodes. Default: %default')) - parser.add_option('--barcode_height', type='int', default=32, - help=('Height of the barcodes. Default: %default')) + parser.add_option('--zxing_dir', type='string', + help=('The path to the directory where the zxing executable' + 'is located. If omitted, it will be assumed to be ' + 'present in the PATH.')) + parser.add_option('--ffmpeg_dir', type='string', default=None, + help=('The path to the directory where the ffmpeg ' + 'executable is located. If omitted, it will be ' + 'assumed to be present in the PATH.')) + parser.add_option('--yuv_frame_width', type='int', default=640, + help='Width of the YUV file\'s frames. Default: %default') + parser.add_option('--yuv_frame_height', type='int', default=480, + help='Height of the YUV file\'s frames. Default: %default') parser.add_option('--yuv_file', type='string', default='output.yuv', - help=('The YUV file to be decoded. Default: %default')) + help='The YUV file to be decoded. Default: %default') parser.add_option('--stats_file', type='string', default='stats.txt', - help=('The output stats file. Default: %default')) - parser.add_option('--png_output_dir', type='string', default='.', - help=('The output directory for the generated PNG files. ' - 'Default: %default')) - parser.add_option('--png_input_dir', type='string', default='.', - help=('The input directory for the generated PNG files. ' - 'Default: %default')) - parser.add_option('--path_to_zxing', type='string', default='zxing', - help=('The path to Zxing. Default: %default')) - options = parser.parse_args()[0] + help='The output stats file. Default: %default') + parser.add_option('--png_working_dir', type='string', default='.', + help=('The directory for temporary PNG images to be stored ' + 'in when decoding from YUV before they\'re barcode ' + 'decoded. If using Windows and a Cygwin-compiled ' + 'zxing.exe, you should keep the default value to ' + 'avoid problems. Default: %default')) + options, _args = parser.parse_args() return options @@ -269,41 +253,30 @@ def _main(): A simple invocation is: ./tools/barcode_tolls/barcode_decoder.py --yuv_file= - --yuv_frame_width=352 --yuv_frame_height=288 --barcode_height=32 + --yuv_frame_width=640 --yuv_frame_height=480 --stats_file= - - NOTE: On Windows, if you don't have ffmpeg and Java in your PATH, you can - set the JAVA_HOME and FFMPEG_HOME environment variables to help the script - find the executables to use. """ 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.yuv_frame_width - - script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - zxing_dir = os.path.join(script_dir, 'third_party', 'zxing') - # Convert the overlaid YUV video into a set of PNG frames. if not convert_yuv_to_png_files(options.yuv_file, options.yuv_frame_width, options.yuv_frame_height, - output_directory=options.png_output_dir): + output_directory=options.png_working_dir, + ffmpeg_dir=options.ffmpeg_dir): print >> sys.stderr, 'An error occurred converting from YUV to PNG frames.' return -1 # Decode the barcodes from the PNG frames. - if not decode_frames(options.barcode_width, options.barcode_height, - input_directory=options.png_input_dir, - path_to_zxing=zxing_dir): + if not decode_frames(input_directory=options.png_working_dir, + zxing_dir=options.zxing_dir): print >> sys.stderr, ('An error occurred decoding barcodes from PNG frames.' - 'Have you built the zxing library JAR files?') + ' Have you built the zxing C++ executable?') return -2 # Generate statistics file. _generate_stats_file(options.stats_file, - input_directory=options.png_input_dir) + input_directory=options.png_working_dir) + print 'Completed barcode decoding.' return 0 if __name__ == '__main__': diff --git a/tools/barcode_tools/barcode_encoder.py b/webrtc/tools/barcode_tools/barcode_encoder.py similarity index 94% rename from tools/barcode_tools/barcode_encoder.py rename to webrtc/tools/barcode_tools/barcode_encoder.py index 0e1cd3badf..8c3a5bf7b7 100755 --- a/tools/barcode_tools/barcode_encoder.py +++ b/webrtc/tools/barcode_tools/barcode_encoder.py @@ -39,7 +39,7 @@ def generate_upca_barcodes(number_of_barcodes, barcode_width, barcode_height, (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) + jars = _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) @@ -57,8 +57,8 @@ def generate_upca_barcodes(number_of_barcodes, barcode_width, barcode_height, "--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: + command, fail_msg=('Error during barcode %s generation' % content)) + except helper_functions.HelperError as err: print >> sys.stderr, err errors = True return not errors @@ -107,10 +107,10 @@ def _convert_to_yuv_and_delete(output_directory, file_name, pattern): '%s' % (yuv_file_name)] try: helper_functions.run_shell_command( - command, msg=('Error during PNG to YUV conversion of %s' % - file_name)) + command, fail_msg=('Error during PNG to YUV conversion of %s' % + file_name)) os.remove(file_name) - except helper_functions.HelperError, err: + except helper_functions.HelperError as err: print >> sys.stderr, err return False return True @@ -153,8 +153,8 @@ def _add_to_file_and_delete(output_file, file_name): input_file.close() try: os.remove(file_name) - except Exception: - sys.stderr.write('Error in deleting file %s' % file_name) + except OSError as e: + print >> sys.stderr, 'Error deleting file %s.\nError: %s' % (file_name, e) return False return True @@ -258,6 +258,21 @@ def calculate_frames_number_from_yuv(yuv_width, yuv_height, file_name): return int(file_size/frame_size) # Should be int anyway +def _form_jars_string(path_to_zxing): + """Forms the the Zxing core and javase jars argument. + + Args: + path_to_zxing(string): The path to the Zxing checkout folder. + Return: + (string): The newly formed jars argument. + """ + javase_jar = os.path.join(path_to_zxing, "javase", "javase.jar") + core_jar = os.path.join(path_to_zxing, "core", "core.jar") + delimiter = ':' + if os.name != 'posix': + delimiter = ';' + return javase_jar + delimiter + core_jar + def _parse_args(): """Registers the command-line options.""" usage = "usage: %prog [options]" diff --git a/tools/barcode_tools/build_zxing.py b/webrtc/tools/barcode_tools/build_zxing.py similarity index 87% rename from tools/barcode_tools/build_zxing.py rename to webrtc/tools/barcode_tools/build_zxing.py index 71b5e603af..466bd50fd9 100755 --- a/tools/barcode_tools/build_zxing.py +++ b/webrtc/tools/barcode_tools/build_zxing.py @@ -27,8 +27,9 @@ def run_ant_build_command(path_to_ant_build_file): if process.returncode != 0: print >> sys.stderr, 'Failed to execute: %s' % ' '.join(cmd) return process.returncode - except Exception: - print >> sys.stderr, 'Failed to execute: %s' % ' '.join(cmd) + except subprocess.CalledProcessError as e: + print >> sys.stderr, 'Failed to execute: %s.\nCause: %s' % (' '.join(cmd), + e) return -1 def _main(): diff --git a/tools/barcode_tools/helper_functions.py b/webrtc/tools/barcode_tools/helper_functions.py similarity index 77% rename from tools/barcode_tools/helper_functions.py rename to webrtc/tools/barcode_tools/helper_functions.py index 0342497018..bb0b167334 100644 --- a/tools/barcode_tools/helper_functions.py +++ b/webrtc/tools/barcode_tools/helper_functions.py @@ -34,12 +34,12 @@ def zero_pad(number, padding=_DEFAULT_PADDING): return str(number).zfill(padding) -def run_shell_command(command, msg=None): +def run_shell_command(cmd_list, fail_msg=None): """Executes a command. Args: - command(list): Command list to execute. - msg(string): Message describing the error in case the command fails. + cmd_list(list): Command list to execute. + fail_msg(string): Message describing the error in case the command fails. Return: (string): The standard output from running the command. @@ -47,36 +47,18 @@ def run_shell_command(command, msg=None): Raise: HelperError: If command fails. """ - cmd_list = [str(x) for x in command] - cmd = ' '.join(cmd_list) - process = subprocess.Popen(cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, error = process.communicate() if process.returncode != 0: - if msg: - print >> sys.stderr, msg + if fail_msg: + print >> sys.stderr, fail_msg raise HelperError('Failed to run %s: command returned %d and printed ' - '%s and %s' % (cmd, process.returncode, output, error)) + '%s and %s' % (' '.join(cmd_list), process.returncode, + output, error)) return output.strip() -def form_jars_string(path_to_zxing): - """Forms the the Zxing core and javase jars argument. - - Args: - path_to_zxing(string): The path to the Zxing checkout folder. - Return: - (string): The newly formed jars argument. - """ - javase_jar = os.path.join(path_to_zxing, "javase", "javase.jar") - core_jar = os.path.join(path_to_zxing, "core", "core.jar") - delimiter = ':' - if os.name != 'posix': - delimiter = ';' - return javase_jar + delimiter + core_jar - - def perform_action_on_all_files(directory, file_pattern, file_extension, start_number, action, **kwargs): """Function that performs a given action on all files matching a pattern. @@ -112,5 +94,3 @@ def perform_action_on_all_files(directory, file_pattern, file_extension, else: file_exists = False return not errors - - diff --git a/tools/barcode_tools/yuv_cropper.py b/webrtc/tools/barcode_tools/yuv_cropper.py similarity index 100% rename from tools/barcode_tools/yuv_cropper.py rename to webrtc/tools/barcode_tools/yuv_cropper.py