blob: f74fe7e302ab65047297366e63e1d1dbc40082c3 [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
14import helper_functions
15
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000016
17def convert_yuv_to_png_files(yuv_file_name, yuv_frame_width, yuv_frame_height,
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000018 output_directory, ffmpeg_dir=None):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000019 """Converts a YUV video file into PNG frames.
20
21 The function uses ffmpeg to convert the YUV file. The output of ffmpeg is in
22 the form frame_xxxx.png, where xxxx is the frame number, starting from 0001.
23
24 Args:
25 yuv_file_name(string): The name of the YUV file.
26 yuv_frame_width(int): The width of one YUV frame.
27 yuv_frame_height(int): The height of one YUV frame.
28 output_directory(string): The output directory where the PNG frames will be
29 stored.
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000030 ffmpeg_dir(string): The directory containing the ffmpeg executable. If
31 omitted, the PATH will be searched for it.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000032
33 Return:
34 (bool): True if the conversion was OK.
35 """
36 size_string = str(yuv_frame_width) + 'x' + str(yuv_frame_height)
37 output_files_pattern = os.path.join(output_directory, 'frame_%04d.png')
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000038 ffmpeg_executable = 'ffmpeg.exe' if sys.platform == 'win32' else 'ffmpeg'
39 if ffmpeg_dir:
40 ffmpeg_executable = os.path.join(ffmpeg_dir, ffmpeg_executable)
kjellander@webrtc.orgb13dfbf2012-12-18 19:53:00 +000041 command = [ffmpeg_executable, '-s', '%s' % size_string, '-i', '%s'
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000042 % yuv_file_name, '-f', 'image2', '-vcodec', 'png',
43 '%s' % output_files_pattern]
44 try:
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000045 print 'Converting YUV file to PNG images (may take a while)...'
46 print ' '.join(command)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000047 helper_functions.run_shell_command(
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000048 command, fail_msg='Error during YUV to PNG conversion')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000049 except helper_functions.HelperError, err:
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +000050 print >> sys.stderr, 'Error executing command: %s. Error: %s' % (command,
51 err)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000052 return False
53 return True
54
55
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000056def decode_frames(input_directory, zxing_dir=None):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000057 """Decodes the barcodes overlaid in each frame.
58
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000059 The function uses the Zxing command-line tool from the Zxing C++ distribution
60 to decode the barcode in every PNG frame from the input directory. The frames
61 should be named frame_xxxx.png, where xxxx is the frame number. The frame
62 numbers should be consecutive and should start from 0001.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000063 The decoding results in a frame_xxxx.txt file for every successfully decoded
64 barcode. This file contains the decoded barcode as 12-digit string (UPC-A
65 format: 11 digits content + one check digit).
66
67 Args:
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000068 input_directory(string): The input directory from where the PNG frames are
69 read.
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000070 zxing_dir(string): The directory containing the zxing executable. If
71 omitted, the PATH will be searched for it.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000072 Return:
73 (bool): True if the decoding went without errors.
74 """
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000075 zxing_executable = 'zxing.exe' if sys.platform == 'win32' else 'zxing'
76 if zxing_dir:
77 zxing_executable = os.path.join(zxing_dir, zxing_executable)
78 print 'Decoding barcodes from PNG files with %s...' % zxing_executable
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000079 return helper_functions.perform_action_on_all_files(
80 directory=input_directory, file_pattern='frame_',
81 file_extension='png', start_number=1, action=_decode_barcode_in_file,
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000082 command_line_decoder=zxing_executable)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000083
84
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000085def _decode_barcode_in_file(file_name, command_line_decoder):
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000086 """Decodes the barcode in the upper left corner of a PNG file.
87
88 Args:
89 file_name(string): File name of the PNG file.
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000090 command_line_decoder(string): The ZXing command-line decoding tool.
91
92 Return:
93 (bool): True upon success, False otherwise.
94 """
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000095 command = [command_line_decoder, '--try-harder', '--dump-raw', file_name]
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +000096 try:
97 out = helper_functions.run_shell_command(
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +000098 command, fail_msg='Error during decoding of %s' % file_name)
99 print 'Image %s : decoded barcode: %s' % (file_name, out)
100 text_file = open('%s.txt' % file_name[:-4], 'w')
101 text_file.write(out)
102 text_file.close()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000103 except helper_functions.HelperError, err:
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000104 print >> sys.stderr, 'Barcode in %s cannot be decoded.' % file_name
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000105 print >> sys.stderr, err
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000106 return False
107 return True
108
109
110def _generate_stats_file(stats_file_name, input_directory='.'):
111 """Generate statistics file.
112
113 The function generates a statistics file. The contents of the file are in the
114 format <frame_name> <barcode>, where frame name is the name of every frame
115 (effectively the frame number) and barcode is the decoded barcode. The frames
116 and the helper .txt files are removed after they have been used.
117 """
118 file_prefix = os.path.join(input_directory, 'frame_')
119 stats_file = open(stats_file_name, 'w')
120
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000121 print 'Generating stats file: %s' % stats_file_name
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000122 for i in range(1, _count_frames_in(input_directory=input_directory) + 1):
123 frame_number = helper_functions.zero_pad(i)
124 barcode_file_name = file_prefix + frame_number + '.txt'
125 png_frame = file_prefix + frame_number + '.png'
126 entry_frame_number = helper_functions.zero_pad(i-1)
127 entry = 'frame_' + entry_frame_number + ' '
128
129 if os.path.isfile(barcode_file_name):
130 barcode = _read_barcode_from_text_file(barcode_file_name)
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000131 os.remove(barcode_file_name)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000132
133 if _check_barcode(barcode):
134 entry += (helper_functions.zero_pad(int(barcode[0:11])) + '\n')
135 else:
136 entry += 'Barcode error\n' # Barcode is wrongly detected.
137 else: # Barcode file doesn't exist.
138 entry += 'Barcode error\n'
139
140 stats_file.write(entry)
vspasova@webrtc.org1b0a02e2012-08-30 12:56:38 +0000141 os.remove(png_frame)
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000142
143 stats_file.close()
144
145
146def _read_barcode_from_text_file(barcode_file_name):
147 """Reads the decoded barcode for a .txt file.
148
149 Args:
150 barcode_file_name(string): The name of the .txt file.
151 Return:
152 (string): The decoded barcode.
153 """
154 barcode_file = open(barcode_file_name, 'r')
155 barcode = barcode_file.read()
156 barcode_file.close()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000157 return barcode
158
159
160def _check_barcode(barcode):
161 """Check weather the UPC-A barcode was decoded correctly.
162
163 This function calculates the check digit of the provided barcode and compares
164 it to the check digit that was decoded.
165
166 Args:
167 barcode(string): The barcode (12-digit).
168 Return:
169 (bool): True if the barcode was decoded correctly.
170 """
171 if len(barcode) != 12:
172 return False
173
174 r1 = range(0, 11, 2) # Odd digits
175 r2 = range(1, 10, 2) # Even digits except last
176 dsum = 0
177 # Sum all the even digits
178 for i in r1:
179 dsum += int(barcode[i])
180 # Multiply the sum by 3
181 dsum *= 3
182 # Add all the even digits except the check digit (12th digit)
183 for i in r2:
184 dsum += int(barcode[i])
185 # Get the modulo 10
186 dsum = dsum % 10
187 # If not 0 substract from 10
188 if dsum != 0:
189 dsum = 10 - dsum
190 # Compare result and check digit
191 return dsum == int(barcode[11])
192
193
194def _count_frames_in(input_directory = '.'):
195 """Calculates the number of frames in the input directory.
196
197 The function calculates the number of frames in the input directory. The
198 frames should be named frame_xxxx.png, where xxxx is the number of the frame.
199 The numbers should start from 1 and should be consecutive.
200
201 Args:
202 input_directory(string): The input directory.
203 Return:
204 (int): The number of frames.
205 """
206 file_prefix = os.path.join(input_directory, 'frame_')
207 file_exists = True
208 num = 1
209
210 while file_exists:
211 file_name = (file_prefix + helper_functions.zero_pad(num) + '.png')
212 if os.path.isfile(file_name):
213 num += 1
214 else:
215 file_exists = False
216 return num - 1
217
218
219def _parse_args():
220 """Registers the command-line options."""
221 usage = "usage: %prog [options]"
222 parser = optparse.OptionParser(usage=usage)
223
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000224 parser.add_option('--zxing_dir', type='string',
225 help=('The path to the directory where the zxing executable'
226 'is located. If omitted, it will be assumed to be '
227 'present in the PATH.'))
228 parser.add_option('--ffmpeg_dir', type='string', default=None,
229 help=('The path to the directory where the ffmpeg '
230 'executable is located. If omitted, it will be '
231 'assumed to be present in the PATH.'))
232 parser.add_option('--yuv_frame_width', type='int', default=640,
233 help='Width of the YUV file\'s frames. Default: %default')
234 parser.add_option('--yuv_frame_height', type='int', default=480,
235 help='Height of the YUV file\'s frames. Default: %default')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000236 parser.add_option('--yuv_file', type='string', default='output.yuv',
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000237 help='The YUV file to be decoded. Default: %default')
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000238 parser.add_option('--stats_file', type='string', default='stats.txt',
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000239 help='The output stats file. Default: %default')
240 parser.add_option('--png_working_dir', type='string', default='.',
241 help=('The directory for temporary PNG images to be stored '
242 'in when decoding from YUV before they\'re barcode '
243 'decoded. If using Windows and a Cygwin-compiled '
244 'zxing.exe, you should keep the default value to '
245 'avoid problems. Default: %default'))
246 options, _args = parser.parse_args()
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000247 return options
248
249
250def _main():
251 """The main function.
252
253 A simple invocation is:
kjellander@webrtc.orga6ff8452013-05-14 09:43:04 +0000254 ./webrtc/tools/barcode_tools/barcode_decoder.py
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000255 --yuv_file=<path_and_name_of_overlaid_yuv_video>
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000256 --yuv_frame_width=640 --yuv_frame_height=480
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000257 --stats_file=<path_and_name_to_stats_file>
258 """
259 options = _parse_args()
260
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000261 # Convert the overlaid YUV video into a set of PNG frames.
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000262 if not convert_yuv_to_png_files(options.yuv_file, options.yuv_frame_width,
263 options.yuv_frame_height,
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000264 output_directory=options.png_working_dir,
265 ffmpeg_dir=options.ffmpeg_dir):
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000266 print >> sys.stderr, 'An error occurred converting from YUV to PNG frames.'
267 return -1
268
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000269 # Decode the barcodes from the PNG frames.
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000270 if not decode_frames(input_directory=options.png_working_dir,
271 zxing_dir=options.zxing_dir):
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000272 print >> sys.stderr, ('An error occurred decoding barcodes from PNG frames.'
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000273 ' Have you built the zxing C++ executable?')
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000274 return -2
275
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000276 # Generate statistics file.
277 _generate_stats_file(options.stats_file,
kjellander@webrtc.org38ebf982013-03-08 10:58:21 +0000278 input_directory=options.png_working_dir)
279 print 'Completed barcode decoding.'
kjellander@webrtc.orgccb52c22012-10-10 16:11:28 +0000280 return 0
vspasova@webrtc.org400e7da2012-08-15 10:25:12 +0000281
282if __name__ == '__main__':
283 sys.exit(_main())