vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 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 |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 12 | import sys |
| 13 | |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 14 | if __name__ == '__main__': |
| 15 | # Make sure we always can import helper_functions. |
| 16 | sys.path.append(os.path.dirname(__file__)) |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 17 | |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 18 | import helper_functions |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 19 | |
phoglund@webrtc.org | 8400246 | 2013-07-29 11:01:03 +0000 | [diff] [blame] | 20 | # Chrome browsertests will throw away stderr; avoid that output gets lost. |
| 21 | sys.stderr = sys.stdout |
| 22 | |
| 23 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 24 | def ConvertYuvToPngFiles(yuv_file_name, yuv_frame_width, yuv_frame_height, |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 25 | output_directory, ffmpeg_path): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 26 | """Converts a YUV video file into PNG frames. |
| 27 | |
| 28 | The function uses ffmpeg to convert the YUV file. The output of ffmpeg is in |
| 29 | the form frame_xxxx.png, where xxxx is the frame number, starting from 0001. |
| 30 | |
| 31 | Args: |
| 32 | yuv_file_name(string): The name of the YUV file. |
| 33 | yuv_frame_width(int): The width of one YUV frame. |
| 34 | yuv_frame_height(int): The height of one YUV frame. |
| 35 | output_directory(string): The output directory where the PNG frames will be |
| 36 | stored. |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 37 | ffmpeg_path(string): The path to the ffmpeg executable. If None, the PATH |
| 38 | will be searched for it. |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 39 | |
| 40 | Return: |
| 41 | (bool): True if the conversion was OK. |
| 42 | """ |
| 43 | size_string = str(yuv_frame_width) + 'x' + str(yuv_frame_height) |
| 44 | output_files_pattern = os.path.join(output_directory, 'frame_%04d.png') |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 45 | if not ffmpeg_path: |
| 46 | ffmpeg_path = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg' |
| 47 | command = [ffmpeg_path, '-s', '%s' % size_string, '-i', '%s' |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 48 | % yuv_file_name, '-f', 'image2', '-vcodec', 'png', |
| 49 | '%s' % output_files_pattern] |
| 50 | try: |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 51 | print 'Converting YUV file to PNG images (may take a while)...' |
| 52 | print ' '.join(command) |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 53 | helper_functions.run_shell_command( |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 54 | command, fail_msg='Error during YUV to PNG conversion') |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 55 | except helper_functions.HelperError, err: |
phoglund@webrtc.org | 8400246 | 2013-07-29 11:01:03 +0000 | [diff] [blame] | 56 | print 'Error executing command: %s. Error: %s' % (command, err) |
| 57 | return False |
| 58 | except OSError: |
Henrik Kjellander | 57e5fd2 | 2015-05-25 12:55:39 +0200 | [diff] [blame] | 59 | print 'Did not find %s. Have you installed it?' % ffmpeg_path |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 60 | return False |
| 61 | return True |
| 62 | |
| 63 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 64 | def DecodeFrames(input_directory, zxing_path): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 65 | """Decodes the barcodes overlaid in each frame. |
| 66 | |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 67 | The function uses the Zxing command-line tool from the Zxing C++ distribution |
| 68 | to decode the barcode in every PNG frame from the input directory. The frames |
| 69 | should be named frame_xxxx.png, where xxxx is the frame number. The frame |
| 70 | numbers should be consecutive and should start from 0001. |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 71 | The decoding results in a frame_xxxx.txt file for every successfully decoded |
| 72 | barcode. This file contains the decoded barcode as 12-digit string (UPC-A |
| 73 | format: 11 digits content + one check digit). |
| 74 | |
| 75 | Args: |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 76 | input_directory(string): The input directory from where the PNG frames are |
| 77 | read. |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 78 | zxing_path(string): The path to the zxing binary. If specified as None, |
| 79 | the PATH will be searched for it. |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 80 | Return: |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 81 | (bool): True if the decoding succeeded. |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 82 | """ |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 83 | if not zxing_path: |
| 84 | zxing_path = 'zxing.exe' if sys.platform == 'win32' else 'zxing' |
| 85 | print 'Decoding barcodes from PNG files with %s...' % zxing_path |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 86 | return helper_functions.perform_action_on_all_files( |
| 87 | directory=input_directory, file_pattern='frame_', |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 88 | file_extension='png', start_number=1, action=_DecodeBarcodeInFile, |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 89 | command_line_decoder=zxing_path) |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 90 | |
| 91 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 92 | def _DecodeBarcodeInFile(file_name, command_line_decoder): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 93 | """Decodes the barcode in the upper left corner of a PNG file. |
| 94 | |
| 95 | Args: |
| 96 | file_name(string): File name of the PNG file. |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 97 | command_line_decoder(string): The ZXing command-line decoding tool. |
| 98 | |
| 99 | Return: |
| 100 | (bool): True upon success, False otherwise. |
| 101 | """ |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 102 | command = [command_line_decoder, '--try-harder', '--dump-raw', file_name] |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 103 | try: |
| 104 | out = helper_functions.run_shell_command( |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 105 | command, fail_msg='Error during decoding of %s' % file_name) |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 106 | text_file = open('%s.txt' % file_name[:-4], 'w') |
| 107 | text_file.write(out) |
| 108 | text_file.close() |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 109 | except helper_functions.HelperError, err: |
phoglund@webrtc.org | 8400246 | 2013-07-29 11:01:03 +0000 | [diff] [blame] | 110 | print 'Barcode in %s cannot be decoded.' % file_name |
| 111 | print err |
| 112 | return False |
| 113 | except OSError: |
Henrik Kjellander | 57e5fd2 | 2015-05-25 12:55:39 +0200 | [diff] [blame] | 114 | print 'Did not find %s. Have you installed it?' % command_line_decoder |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 115 | return False |
| 116 | return True |
| 117 | |
| 118 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 119 | def _GenerateStatsFile(stats_file_name, input_directory='.'): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 120 | """Generate statistics file. |
| 121 | |
| 122 | The function generates a statistics file. The contents of the file are in the |
| 123 | format <frame_name> <barcode>, where frame name is the name of every frame |
| 124 | (effectively the frame number) and barcode is the decoded barcode. The frames |
| 125 | and the helper .txt files are removed after they have been used. |
| 126 | """ |
| 127 | file_prefix = os.path.join(input_directory, 'frame_') |
| 128 | stats_file = open(stats_file_name, 'w') |
| 129 | |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 130 | print 'Generating stats file: %s' % stats_file_name |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 131 | for i in range(1, _CountFramesIn(input_directory=input_directory) + 1): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 132 | frame_number = helper_functions.zero_pad(i) |
| 133 | barcode_file_name = file_prefix + frame_number + '.txt' |
| 134 | png_frame = file_prefix + frame_number + '.png' |
| 135 | entry_frame_number = helper_functions.zero_pad(i-1) |
| 136 | entry = 'frame_' + entry_frame_number + ' ' |
| 137 | |
| 138 | if os.path.isfile(barcode_file_name): |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 139 | barcode = _ReadBarcodeFromTextFile(barcode_file_name) |
vspasova@webrtc.org | 1b0a02e | 2012-08-30 12:56:38 +0000 | [diff] [blame] | 140 | os.remove(barcode_file_name) |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 141 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 142 | if _CheckBarcode(barcode): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 143 | entry += (helper_functions.zero_pad(int(barcode[0:11])) + '\n') |
| 144 | else: |
| 145 | entry += 'Barcode error\n' # Barcode is wrongly detected. |
| 146 | else: # Barcode file doesn't exist. |
| 147 | entry += 'Barcode error\n' |
| 148 | |
| 149 | stats_file.write(entry) |
vspasova@webrtc.org | 1b0a02e | 2012-08-30 12:56:38 +0000 | [diff] [blame] | 150 | os.remove(png_frame) |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 151 | |
| 152 | stats_file.close() |
| 153 | |
| 154 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 155 | def _ReadBarcodeFromTextFile(barcode_file_name): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 156 | """Reads the decoded barcode for a .txt file. |
| 157 | |
| 158 | Args: |
| 159 | barcode_file_name(string): The name of the .txt file. |
| 160 | Return: |
| 161 | (string): The decoded barcode. |
| 162 | """ |
| 163 | barcode_file = open(barcode_file_name, 'r') |
| 164 | barcode = barcode_file.read() |
| 165 | barcode_file.close() |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 166 | return barcode |
| 167 | |
| 168 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 169 | def _CheckBarcode(barcode): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 170 | """Check weather the UPC-A barcode was decoded correctly. |
| 171 | |
| 172 | This function calculates the check digit of the provided barcode and compares |
| 173 | it to the check digit that was decoded. |
| 174 | |
| 175 | Args: |
| 176 | barcode(string): The barcode (12-digit). |
| 177 | Return: |
| 178 | (bool): True if the barcode was decoded correctly. |
| 179 | """ |
| 180 | if len(barcode) != 12: |
| 181 | return False |
| 182 | |
| 183 | r1 = range(0, 11, 2) # Odd digits |
| 184 | r2 = range(1, 10, 2) # Even digits except last |
| 185 | dsum = 0 |
| 186 | # Sum all the even digits |
| 187 | for i in r1: |
| 188 | dsum += int(barcode[i]) |
| 189 | # Multiply the sum by 3 |
| 190 | dsum *= 3 |
| 191 | # Add all the even digits except the check digit (12th digit) |
| 192 | for i in r2: |
| 193 | dsum += int(barcode[i]) |
| 194 | # Get the modulo 10 |
| 195 | dsum = dsum % 10 |
| 196 | # If not 0 substract from 10 |
| 197 | if dsum != 0: |
| 198 | dsum = 10 - dsum |
| 199 | # Compare result and check digit |
| 200 | return dsum == int(barcode[11]) |
| 201 | |
| 202 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 203 | def _CountFramesIn(input_directory='.'): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 204 | """Calculates the number of frames in the input directory. |
| 205 | |
| 206 | The function calculates the number of frames in the input directory. The |
| 207 | frames should be named frame_xxxx.png, where xxxx is the number of the frame. |
| 208 | The numbers should start from 1 and should be consecutive. |
| 209 | |
| 210 | Args: |
| 211 | input_directory(string): The input directory. |
| 212 | Return: |
| 213 | (int): The number of frames. |
| 214 | """ |
| 215 | file_prefix = os.path.join(input_directory, 'frame_') |
| 216 | file_exists = True |
| 217 | num = 1 |
| 218 | |
| 219 | while file_exists: |
| 220 | file_name = (file_prefix + helper_functions.zero_pad(num) + '.png') |
| 221 | if os.path.isfile(file_name): |
| 222 | num += 1 |
| 223 | else: |
| 224 | file_exists = False |
| 225 | return num - 1 |
| 226 | |
| 227 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 228 | def _ParseArgs(): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 229 | """Registers the command-line options.""" |
| 230 | usage = "usage: %prog [options]" |
| 231 | parser = optparse.OptionParser(usage=usage) |
| 232 | |
phoglund@webrtc.org | 3260f10 | 2013-11-25 14:10:20 +0000 | [diff] [blame] | 233 | parser.add_option('--zxing_path', type='string', |
| 234 | help=('The path to where the zxing executable is located. ' |
| 235 | 'If omitted, it will be assumed to be present in the ' |
| 236 | 'PATH with the name zxing[.exe].')) |
| 237 | parser.add_option('--ffmpeg_path', type='string', |
| 238 | help=('The path to where the ffmpeg executable is located. ' |
| 239 | 'If omitted, it will be assumed to be present in the ' |
| 240 | 'PATH with the name ffmpeg[.exe].')) |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 241 | parser.add_option('--yuv_frame_width', type='int', default=640, |
| 242 | help='Width of the YUV file\'s frames. Default: %default') |
| 243 | parser.add_option('--yuv_frame_height', type='int', default=480, |
| 244 | help='Height of the YUV file\'s frames. Default: %default') |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 245 | parser.add_option('--yuv_file', type='string', default='output.yuv', |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 246 | help='The YUV file to be decoded. Default: %default') |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 247 | parser.add_option('--stats_file', type='string', default='stats.txt', |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 248 | help='The output stats file. Default: %default') |
| 249 | parser.add_option('--png_working_dir', type='string', default='.', |
| 250 | help=('The directory for temporary PNG images to be stored ' |
| 251 | 'in when decoding from YUV before they\'re barcode ' |
| 252 | 'decoded. If using Windows and a Cygwin-compiled ' |
| 253 | 'zxing.exe, you should keep the default value to ' |
| 254 | 'avoid problems. Default: %default')) |
Henrik Kjellander | 57e5fd2 | 2015-05-25 12:55:39 +0200 | [diff] [blame] | 255 | options, _ = parser.parse_args() |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 256 | return options |
| 257 | |
| 258 | |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 259 | def main(): |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 260 | """The main function. |
| 261 | |
| 262 | A simple invocation is: |
kjellander@webrtc.org | a6ff845 | 2013-05-14 09:43:04 +0000 | [diff] [blame] | 263 | ./webrtc/tools/barcode_tools/barcode_decoder.py |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 264 | --yuv_file=<path_and_name_of_overlaid_yuv_video> |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 265 | --yuv_frame_width=640 --yuv_frame_height=480 |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 266 | --stats_file=<path_and_name_to_stats_file> |
| 267 | """ |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 268 | options = _ParseArgs() |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 269 | |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 270 | # Convert the overlaid YUV video into a set of PNG frames. |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 271 | if not ConvertYuvToPngFiles(options.yuv_file, options.yuv_frame_width, |
| 272 | options.yuv_frame_height, |
| 273 | output_directory=options.png_working_dir, |
| 274 | ffmpeg_path=options.ffmpeg_path): |
phoglund@webrtc.org | 8400246 | 2013-07-29 11:01:03 +0000 | [diff] [blame] | 275 | print 'An error occurred converting from YUV to PNG frames.' |
kjellander@webrtc.org | ccb52c2 | 2012-10-10 16:11:28 +0000 | [diff] [blame] | 276 | return -1 |
| 277 | |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 278 | # Decode the barcodes from the PNG frames. |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 279 | if not DecodeFrames(input_directory=options.png_working_dir, |
| 280 | zxing_path=options.zxing_path): |
phoglund@webrtc.org | 8400246 | 2013-07-29 11:01:03 +0000 | [diff] [blame] | 281 | print 'An error occurred decoding barcodes from PNG frames.' |
kjellander@webrtc.org | ccb52c2 | 2012-10-10 16:11:28 +0000 | [diff] [blame] | 282 | return -2 |
| 283 | |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 284 | # Generate statistics file. |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 285 | _GenerateStatsFile(options.stats_file, |
| 286 | input_directory=options.png_working_dir) |
kjellander@webrtc.org | 38ebf98 | 2013-03-08 10:58:21 +0000 | [diff] [blame] | 287 | print 'Completed barcode decoding.' |
kjellander@webrtc.org | ccb52c2 | 2012-10-10 16:11:28 +0000 | [diff] [blame] | 288 | return 0 |
vspasova@webrtc.org | 400e7da | 2012-08-15 10:25:12 +0000 | [diff] [blame] | 289 | |
| 290 | if __name__ == '__main__': |
kjellander | f5318e1 | 2017-03-09 02:26:46 -0800 | [diff] [blame^] | 291 | sys.exit(main()) |