blob: 2abd677b4b2cb41426ace9982ed973589443eefc [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
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000012import sys
13
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000014if __name__ == '__main__':
15 # Make sure we always can import helper_functions.
16 sys.path.append(os.path.dirname(__file__))
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000017
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000018import helper_functions
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000019
phoglund@webrtc.org84002462013-07-29 11:01:03 +000020# Chrome browsertests will throw away stderr; avoid that output gets lost.
21sys.stderr = sys.stdout
22
23
kjellanderc88b5d52017-04-05 06:42:43 -070024def ConvertYuvToPngFiles(yuv_file_name, yuv_frame_width, yuv_frame_height,
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000025 output_directory, ffmpeg_path):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000026 """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.org3260f102013-11-25 14:10:20 +000037 ffmpeg_path(string): The path to the ffmpeg executable. If None, the PATH
38 will be searched for it.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000039
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.org3260f102013-11-25 14:10:20 +000045 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.org400e7da2012-08-15 10:25:12 +000048 % yuv_file_name, '-f', 'image2', '-vcodec', 'png',
49 '%s' % output_files_pattern]
50 try:
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000051 print 'Converting YUV file to PNG images (may take a while)...'
52 print ' '.join(command)
kjellanderc88b5d52017-04-05 06:42:43 -070053 helper_functions.RunShellCommand(
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000054 command, fail_msg='Error during YUV to PNG conversion')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000055 except helper_functions.HelperError, err:
phoglund@webrtc.org84002462013-07-29 11:01:03 +000056 print 'Error executing command: %s. Error: %s' % (command, err)
57 return False
58 except OSError:
Henrik Kjellander57e5fd22015-05-25 12:55:39 +020059 print 'Did not find %s. Have you installed it?' % ffmpeg_path
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000060 return False
61 return True
62
63
kjellanderc88b5d52017-04-05 06:42:43 -070064def DecodeFrames(input_directory, zxing_path):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000065 """Decodes the barcodes overlaid in each frame.
66
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000067 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.org400e7da2012-08-15 10:25:12 +000071 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.org400e7da2012-08-15 10:25:12 +000076 input_directory(string): The input directory from where the PNG frames are
77 read.
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000078 zxing_path(string): The path to the zxing binary. If specified as None,
79 the PATH will be searched for it.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000080 Return:
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000081 (bool): True if the decoding succeeded.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000082 """
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000083 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
kjellanderc88b5d52017-04-05 06:42:43 -070086 return helper_functions.PerformActionOnAllFiles(
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000087 directory=input_directory, file_pattern='frame_',
kjellanderc88b5d52017-04-05 06:42:43 -070088 file_extension='png', start_number=1, action=_DecodeBarcodeInFile,
phoglund@webrtc.org3260f102013-11-25 14:10:20 +000089 command_line_decoder=zxing_path)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000090
91
kjellanderc88b5d52017-04-05 06:42:43 -070092def _DecodeBarcodeInFile(file_name, command_line_decoder):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000093 """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.org400e7da2012-08-15 10:25:12 +000097 command_line_decoder(string): The ZXing command-line decoding tool.
98
99 Return:
100 (bool): True upon success, False otherwise.
101 """
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000102 command = [command_line_decoder, '--try-harder', '--dump-raw', file_name]
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000103 try:
kjellanderc88b5d52017-04-05 06:42:43 -0700104 out = helper_functions.RunShellCommand(
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000105 command, fail_msg='Error during decoding of %s' % file_name)
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000106 text_file = open('%s.txt' % file_name[:-4], 'w')
107 text_file.write(out)
108 text_file.close()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000109 except helper_functions.HelperError, err:
phoglund@webrtc.org84002462013-07-29 11:01:03 +0000110 print 'Barcode in %s cannot be decoded.' % file_name
111 print err
112 return False
113 except OSError:
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200114 print 'Did not find %s. Have you installed it?' % command_line_decoder
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000115 return False
116 return True
117
118
kjellanderc88b5d52017-04-05 06:42:43 -0700119def _GenerateStatsFile(stats_file_name, input_directory='.'):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000120 """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.org38ebf982013-03-08 10:58:21 +0000130 print 'Generating stats file: %s' % stats_file_name
kjellanderc88b5d52017-04-05 06:42:43 -0700131 for i in range(1, _CountFramesIn(input_directory=input_directory) + 1):
132 frame_number = helper_functions.ZeroPad(i)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000133 barcode_file_name = file_prefix + frame_number + '.txt'
134 png_frame = file_prefix + frame_number + '.png'
kjellanderc88b5d52017-04-05 06:42:43 -0700135 entry_frame_number = helper_functions.ZeroPad(i-1)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000136 entry = 'frame_' + entry_frame_number + ' '
137
138 if os.path.isfile(barcode_file_name):
kjellanderc88b5d52017-04-05 06:42:43 -0700139 barcode = _ReadBarcodeFromTextFile(barcode_file_name)
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000140 os.remove(barcode_file_name)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000141
kjellanderc88b5d52017-04-05 06:42:43 -0700142 if _CheckBarcode(barcode):
143 entry += (helper_functions.ZeroPad(int(barcode[0:11])) + '\n')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000144 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.org1b0a02e2012-08-30 12:56:38 +0000150 os.remove(png_frame)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000151
152 stats_file.close()
153
154
kjellanderc88b5d52017-04-05 06:42:43 -0700155def _ReadBarcodeFromTextFile(barcode_file_name):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000156 """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.org400e7da2012-08-15 10:25:12 +0000166 return barcode
167
168
kjellanderc88b5d52017-04-05 06:42:43 -0700169def _CheckBarcode(barcode):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000170 """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
kjellanderc88b5d52017-04-05 06:42:43 -0700203def _CountFramesIn(input_directory='.'):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000204 """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:
kjellanderc88b5d52017-04-05 06:42:43 -0700220 file_name = (file_prefix + helper_functions.ZeroPad(num) + '.png')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000221 if os.path.isfile(file_name):
222 num += 1
223 else:
224 file_exists = False
225 return num - 1
226
227
kjellanderc88b5d52017-04-05 06:42:43 -0700228def _ParseArgs():
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000229 """Registers the command-line options."""
230 usage = "usage: %prog [options]"
231 parser = optparse.OptionParser(usage=usage)
232
phoglund@webrtc.org3260f102013-11-25 14:10:20 +0000233 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.org38ebf982013-03-08 10:58:21 +0000241 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.org400e7da2012-08-15 10:25:12 +0000245 parser.add_option('--yuv_file', type='string', default='output.yuv',
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000246 help='The YUV file to be decoded. Default: %default')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000247 parser.add_option('--stats_file', type='string', default='stats.txt',
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000248 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 Kjellander57e5fd22015-05-25 12:55:39 +0200255 options, _ = parser.parse_args()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000256 return options
257
258
kjellanderc88b5d52017-04-05 06:42:43 -0700259def main():
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000260 """The main function.
261
262 A simple invocation is:
kjellanderd2b63cf2017-06-30 03:04:59 -0700263 ./webrtc/rtc_tools/barcode_tools/barcode_decoder.py
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000264 --yuv_file=<path_and_name_of_overlaid_yuv_video>
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000265 --yuv_frame_width=640 --yuv_frame_height=480
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000266 --stats_file=<path_and_name_to_stats_file>
267 """
kjellanderc88b5d52017-04-05 06:42:43 -0700268 options = _ParseArgs()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000269
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000270 # Convert the overlaid YUV video into a set of PNG frames.
kjellanderc88b5d52017-04-05 06:42:43 -0700271 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.org84002462013-07-29 11:01:03 +0000275 print 'An error occurred converting from YUV to PNG frames.'
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000276 return -1
277
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000278 # Decode the barcodes from the PNG frames.
kjellanderc88b5d52017-04-05 06:42:43 -0700279 if not DecodeFrames(input_directory=options.png_working_dir,
280 zxing_path=options.zxing_path):
phoglund@webrtc.org84002462013-07-29 11:01:03 +0000281 print 'An error occurred decoding barcodes from PNG frames.'
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000282 return -2
283
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000284 # Generate statistics file.
kjellanderc88b5d52017-04-05 06:42:43 -0700285 _GenerateStatsFile(options.stats_file,
286 input_directory=options.png_working_dir)
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000287 print 'Completed barcode decoding.'
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000288 return 0
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000289
290if __name__ == '__main__':
kjellanderc88b5d52017-04-05 06:42:43 -0700291 sys.exit(main())