blob: d598ffb66d84469b449f8f5f120e8d669141f608 [file] [log] [blame]
asapersson38e93242017-02-10 09:37:171# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
2#
3# Use of this source code is governed by a BSD-style license
4# that can be found in the LICENSE file in the root of the source
5# tree. An additional intellectual property rights grant can be found
6# in the file PATENTS. All contributing project authors may
7# be found in the AUTHORS file in the root of the source tree.
8
9"""Plots statistics from WebRTC integration test logs.
10
11Usage: $ python plot_webrtc_test_logs.py filename.txt
12"""
13
14import numpy
15import sys
16import re
17
18import matplotlib.pyplot as plt
19
20# Log events.
21EVENT_START = 'RUN ] CodecSettings/PlotVideoProcessorIntegrationTest.'
22EVENT_END = 'OK ] CodecSettings/PlotVideoProcessorIntegrationTest.'
23
24# Metrics to plot, tuple: (name to parse in file, label to use when plotting).
brandtrbea36fd2017-08-07 10:36:5425BITRATE = ('Target bitrate', 'target bitrate (kbps)')
asapersson38e93242017-02-10 09:37:1726WIDTH = ('Width', 'width')
27HEIGHT = ('Height', 'height')
28FILENAME = ('Filename', 'clip')
29CODEC_TYPE = ('Codec type', 'Codec')
30ENCODER_IMPLEMENTATION_NAME = ('Encoder implementation name', 'enc name')
31DECODER_IMPLEMENTATION_NAME = ('Decoder implementation name', 'dec name')
asapersson30df64f2017-05-19 11:07:3832CODEC_IMPLEMENTATION_NAME = ('Codec implementation name', 'codec name')
brandtrbea36fd2017-08-07 10:36:5433CORES = ('# CPU cores used', 'CPU cores used')
asapersson38e93242017-02-10 09:37:1734DENOISING = ('Denoising', 'denoising')
35RESILIENCE = ('Resilience', 'resilience')
36ERROR_CONCEALMENT = ('Error concealment', 'error concealment')
asaperssonabc00802017-02-23 09:33:0437QP = ('Average QP', 'avg QP')
asapersson38e93242017-02-10 09:37:1738PSNR = ('PSNR avg', 'PSNR (dB)')
39SSIM = ('SSIM avg', 'SSIM')
brandtrbea36fd2017-08-07 10:36:5440ENC_BITRATE = ('Encoded bitrate', 'encoded bitrate (kbps)')
asapersson38e93242017-02-10 09:37:1741FRAMERATE = ('Frame rate', 'fps')
brandtrbea36fd2017-08-07 10:36:5442NUM_FRAMES = ('# processed frames', 'num frames')
43NUM_DROPPED_FRAMES = ('# dropped frames', 'num dropped frames')
44NUM_FRAMES_TO_TARGET = ('# frames to convergence',
asapersson38e93242017-02-10 09:37:1745 'frames to reach target rate')
46ENCODE_TIME = ('Encoding time', 'encode time (us)')
47ENCODE_TIME_AVG = ('Encoding time', 'encode time (us) avg')
48DECODE_TIME = ('Decoding time', 'decode time (us)')
49DECODE_TIME_AVG = ('Decoding time', 'decode time (us) avg')
50FRAME_SIZE = ('Frame sizes', 'frame size (bytes)')
51FRAME_SIZE_AVG = ('Frame sizes', 'frame size (bytes) avg')
52AVG_KEY_FRAME_SIZE = ('Average key frame size', 'avg key frame size (bytes)')
53AVG_NON_KEY_FRAME_SIZE = ('Average non-key frame size',
54 'avg non-key frame size (bytes)')
55
56# Settings.
57SETTINGS = [
58 WIDTH,
59 HEIGHT,
60 FILENAME,
asapersson38e93242017-02-10 09:37:1761 NUM_FRAMES,
asapersson38e93242017-02-10 09:37:1762 ENCODE_TIME,
63 DECODE_TIME,
64 FRAME_SIZE,
65]
66
67# Settings, options for x-axis.
68X_SETTINGS = [
69 CORES,
70 FRAMERATE,
71 DENOISING,
72 RESILIENCE,
73 ERROR_CONCEALMENT,
74 BITRATE, # TODO(asapersson): Needs to be last.
75]
76
asapersson7041eed2017-02-13 09:37:5777# Settings, options for subplots.
78SUBPLOT_SETTINGS = [
79 CODEC_TYPE,
80 ENCODER_IMPLEMENTATION_NAME,
81 DECODER_IMPLEMENTATION_NAME,
asapersson30df64f2017-05-19 11:07:3882 CODEC_IMPLEMENTATION_NAME,
asapersson7041eed2017-02-13 09:37:5783] + X_SETTINGS
84
asapersson38e93242017-02-10 09:37:1785# Results.
86RESULTS = [
87 PSNR,
88 SSIM,
89 ENC_BITRATE,
90 NUM_DROPPED_FRAMES,
91 NUM_FRAMES_TO_TARGET,
92 ENCODE_TIME_AVG,
93 DECODE_TIME_AVG,
asaperssonabc00802017-02-23 09:33:0494 QP,
asapersson38e93242017-02-10 09:37:1795 AVG_KEY_FRAME_SIZE,
96 AVG_NON_KEY_FRAME_SIZE,
97]
98
asapersson7041eed2017-02-13 09:37:5799METRICS_TO_PARSE = SETTINGS + SUBPLOT_SETTINGS + RESULTS
asapersson38e93242017-02-10 09:37:17100
101Y_METRICS = [res[1] for res in RESULTS]
102
103# Parameters for plotting.
asapersson30df64f2017-05-19 11:07:38104FIG_SIZE_SCALE_FACTOR_X = 1.6
105FIG_SIZE_SCALE_FACTOR_Y = 1.8
asapersson38e93242017-02-10 09:37:17106GRID_COLOR = [0.45, 0.45, 0.45]
107
108
109def ParseSetting(filename, setting):
110 """Parses setting from file.
111
112 Args:
113 filename: The name of the file.
114 setting: Name of setting to parse (e.g. width).
115
116 Returns:
117 A list holding parsed settings, e.g. ['width: 128.0', 'width: 160.0'] """
118
119 settings = []
120
kjellanderdd460e22017-04-12 19:06:13121 settings_file = open(filename)
asapersson38e93242017-02-10 09:37:17122 while True:
kjellanderdd460e22017-04-12 19:06:13123 line = settings_file.readline()
asapersson38e93242017-02-10 09:37:17124 if not line:
125 break
126 if re.search(r'%s' % EVENT_START, line):
127 # Parse event.
128 parsed = {}
129 while True:
kjellanderdd460e22017-04-12 19:06:13130 line = settings_file.readline()
asapersson38e93242017-02-10 09:37:17131 if not line:
132 break
133 if re.search(r'%s' % EVENT_END, line):
134 # Add parsed setting to list.
135 if setting in parsed:
136 s = setting + ': ' + str(parsed[setting])
137 if s not in settings:
138 settings.append(s)
139 break
140
kjellanderdd460e22017-04-12 19:06:13141 TryFindMetric(parsed, line, settings_file)
asapersson38e93242017-02-10 09:37:17142
kjellanderdd460e22017-04-12 19:06:13143 settings_file.close()
asapersson38e93242017-02-10 09:37:17144 return settings
145
146
147def ParseMetrics(filename, setting1, setting2):
148 """Parses metrics from file.
149
150 Args:
151 filename: The name of the file.
152 setting1: First setting for sorting metrics (e.g. width).
asapersson996103a2017-02-21 16:30:04153 setting2: Second setting for sorting metrics (e.g. CPU cores used).
asapersson38e93242017-02-10 09:37:17154
155 Returns:
156 A dictionary holding parsed metrics.
157
158 For example:
159 metrics[key1][key2][measurement]
160
161 metrics = {
162 "width: 352": {
asapersson996103a2017-02-21 16:30:04163 "CPU cores used: 1.0": {
asapersson38e93242017-02-10 09:37:17164 "encode time (us)": [0.718005, 0.806925, 0.909726, 0.931835, 0.953642],
165 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
166 "bitrate (kbps)": [50, 100, 300, 500, 1000]
167 },
asapersson996103a2017-02-21 16:30:04168 "CPU cores used: 2.0": {
asapersson38e93242017-02-10 09:37:17169 "encode time (us)": [0.718005, 0.806925, 0.909726, 0.931835, 0.953642],
170 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
171 "bitrate (kbps)": [50, 100, 300, 500, 1000]
172 },
173 },
174 "width: 176": {
asapersson996103a2017-02-21 16:30:04175 "CPU cores used: 1.0": {
asapersson38e93242017-02-10 09:37:17176 "encode time (us)": [0.857897, 0.91608, 0.959173, 0.971116, 0.980961],
177 "PSNR (dB)": [30.243646, 33.375592, 37.574387, 39.42184, 41.437897],
178 "bitrate (kbps)": [50, 100, 300, 500, 1000]
179 },
180 }
181 } """
182
183 metrics = {}
184
185 # Parse events.
kjellanderdd460e22017-04-12 19:06:13186 settings_file = open(filename)
asapersson38e93242017-02-10 09:37:17187 while True:
kjellanderdd460e22017-04-12 19:06:13188 line = settings_file.readline()
asapersson38e93242017-02-10 09:37:17189 if not line:
190 break
191 if re.search(r'%s' % EVENT_START, line):
192 # Parse event.
193 parsed = {}
194 while True:
kjellanderdd460e22017-04-12 19:06:13195 line = settings_file.readline()
asapersson38e93242017-02-10 09:37:17196 if not line:
197 break
198 if re.search(r'%s' % EVENT_END, line):
199 # Add parsed values to metrics.
200 key1 = setting1 + ': ' + str(parsed[setting1])
201 key2 = setting2 + ': ' + str(parsed[setting2])
202 if key1 not in metrics:
203 metrics[key1] = {}
204 if key2 not in metrics[key1]:
205 metrics[key1][key2] = {}
206
207 for label in parsed:
208 if label not in metrics[key1][key2]:
209 metrics[key1][key2][label] = []
210 metrics[key1][key2][label].append(parsed[label])
211
212 break
213
kjellanderdd460e22017-04-12 19:06:13214 TryFindMetric(parsed, line, settings_file)
asapersson38e93242017-02-10 09:37:17215
kjellanderdd460e22017-04-12 19:06:13216 settings_file.close()
asapersson38e93242017-02-10 09:37:17217 return metrics
218
219
kjellanderdd460e22017-04-12 19:06:13220def TryFindMetric(parsed, line, settings_file):
asapersson38e93242017-02-10 09:37:17221 for metric in METRICS_TO_PARSE:
222 name = metric[0]
223 label = metric[1]
224 if re.search(r'%s' % name, line):
225 found, value = GetMetric(name, line)
226 if not found:
227 # TODO(asapersson): Change format.
228 # Try find min, max, average stats.
kjellanderdd460e22017-04-12 19:06:13229 found, minimum = GetMetric("Min", settings_file.readline())
asapersson38e93242017-02-10 09:37:17230 if not found:
231 return
kjellanderdd460e22017-04-12 19:06:13232 found, maximum = GetMetric("Max", settings_file.readline())
asapersson38e93242017-02-10 09:37:17233 if not found:
234 return
kjellanderdd460e22017-04-12 19:06:13235 found, average = GetMetric("Average", settings_file.readline())
asapersson38e93242017-02-10 09:37:17236 if not found:
237 return
238
239 parsed[label + ' min'] = minimum
240 parsed[label + ' max'] = maximum
241 parsed[label + ' avg'] = average
242
243 parsed[label] = value
244 return
245
246
247def GetMetric(name, string):
248 # Float (e.g. bitrate = 98.8253).
249 pattern = r'%s\s*[:=]\s*([+-]?\d+\.*\d*)' % name
250 m = re.search(r'%s' % pattern, string)
251 if m is not None:
252 return StringToFloat(m.group(1))
253
254 # Alphanumeric characters (e.g. codec type : VP8).
255 pattern = r'%s\s*[:=]\s*(\w+)' % name
256 m = re.search(r'%s' % pattern, string)
257 if m is not None:
258 return True, m.group(1)
259
260 return False, -1
261
262
263def StringToFloat(value):
264 try:
265 value = float(value)
266 except ValueError:
267 print "Not a float, skipped %s" % value
268 return False, -1
269
270 return True, value
271
272
273def Plot(y_metric, x_metric, metrics):
274 """Plots y_metric vs x_metric per key in metrics.
275
276 For example:
277 y_metric = 'PSNR (dB)'
278 x_metric = 'bitrate (kbps)'
279 metrics = {
asapersson996103a2017-02-21 16:30:04280 "CPU cores used: 1.0": {
asapersson38e93242017-02-10 09:37:17281 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
282 "bitrate (kbps)": [50, 100, 300, 500, 1000]
283 },
asapersson996103a2017-02-21 16:30:04284 "CPU cores used: 2.0": {
asapersson38e93242017-02-10 09:37:17285 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
286 "bitrate (kbps)": [50, 100, 300, 500, 1000]
287 },
288 }
289 """
asapersson30df64f2017-05-19 11:07:38290 for key in sorted(metrics):
asapersson38e93242017-02-10 09:37:17291 data = metrics[key]
292 if y_metric not in data:
293 print "Failed to find metric: %s" % y_metric
294 continue
295
296 y = numpy.array(data[y_metric])
297 x = numpy.array(data[x_metric])
298 if len(y) != len(x):
299 print "Length mismatch for %s, %s" % (y, x)
300 continue
301
302 label = y_metric + ' - ' + str(key)
303
304 plt.plot(x, y, label=label, linewidth=1.5, marker='o', markersize=5,
305 markeredgewidth=0.0)
306
307
308def PlotFigure(settings, y_metrics, x_metric, metrics, title):
309 """Plots metrics in y_metrics list. One figure is plotted and each entry
310 in the list is plotted in a subplot (and sorted per settings).
311
312 For example:
313 settings = ['width: 128.0', 'width: 160.0']. Sort subplot per setting.
314 y_metrics = ['PSNR (dB)', 'PSNR (dB)']. Metric to plot per subplot.
315 x_metric = 'bitrate (kbps)'
316
317 """
318
319 plt.figure()
asapersson30df64f2017-05-19 11:07:38320 plt.suptitle(title, fontsize='large', fontweight='bold')
321 settings.sort()
asapersson38e93242017-02-10 09:37:17322 rows = len(settings)
323 cols = 1
324 pos = 1
325 while pos <= rows:
326 plt.rc('grid', color=GRID_COLOR)
327 ax = plt.subplot(rows, cols, pos)
328 plt.grid()
asapersson30df64f2017-05-19 11:07:38329 plt.setp(ax.get_xticklabels(), visible=(pos == rows), fontsize='large')
330 plt.setp(ax.get_yticklabels(), fontsize='large')
asapersson38e93242017-02-10 09:37:17331 setting = settings[pos - 1]
332 Plot(y_metrics[pos - 1], x_metric, metrics[setting])
asapersson30df64f2017-05-19 11:07:38333 if setting.startswith(WIDTH[1]):
334 plt.title(setting, fontsize='medium')
335 plt.legend(fontsize='large', loc='best')
asapersson38e93242017-02-10 09:37:17336 pos += 1
337
asapersson30df64f2017-05-19 11:07:38338 plt.xlabel(x_metric, fontsize='large')
339 plt.subplots_adjust(left=0.06, right=0.98, bottom=0.05, top=0.94, hspace=0.08)
asapersson38e93242017-02-10 09:37:17340
341
asapersson30df64f2017-05-19 11:07:38342def GetTitle(filename, setting):
asapersson38e93242017-02-10 09:37:17343 title = ''
asapersson30df64f2017-05-19 11:07:38344 if setting != CODEC_IMPLEMENTATION_NAME[1] and setting != CODEC_TYPE[1]:
345 codec_types = ParseSetting(filename, CODEC_TYPE[1])
346 for i in range(0, len(codec_types)):
347 title += codec_types[i] + ', '
asapersson38e93242017-02-10 09:37:17348
asapersson30df64f2017-05-19 11:07:38349 if setting != CORES[1]:
350 cores = ParseSetting(filename, CORES[1])
351 for i in range(0, len(cores)):
352 title += cores[i].split('.')[0] + ', '
asapersson996103a2017-02-21 16:30:04353
asapersson30df64f2017-05-19 11:07:38354 if setting != FRAMERATE[1]:
355 framerate = ParseSetting(filename, FRAMERATE[1])
356 for i in range(0, len(framerate)):
357 title += framerate[i].split('.')[0] + ', '
asapersson38e93242017-02-10 09:37:17358
asapersson30df64f2017-05-19 11:07:38359 if (setting != CODEC_IMPLEMENTATION_NAME[1] and
360 setting != ENCODER_IMPLEMENTATION_NAME[1]):
361 enc_names = ParseSetting(filename, ENCODER_IMPLEMENTATION_NAME[1])
362 for i in range(0, len(enc_names)):
363 title += enc_names[i] + ', '
asapersson38e93242017-02-10 09:37:17364
asapersson30df64f2017-05-19 11:07:38365 if (setting != CODEC_IMPLEMENTATION_NAME[1] and
366 setting != DECODER_IMPLEMENTATION_NAME[1]):
367 dec_names = ParseSetting(filename, DECODER_IMPLEMENTATION_NAME[1])
368 for i in range(0, len(dec_names)):
369 title += dec_names[i] + ', '
asapersson38e93242017-02-10 09:37:17370
371 filenames = ParseSetting(filename, FILENAME[1])
372 title += filenames[0].split('_')[0]
373
374 num_frames = ParseSetting(filename, NUM_FRAMES[1])
375 for i in range(0, len(num_frames)):
376 title += ' (' + num_frames[i].split('.')[0] + ')'
377
378 return title
379
380
381def ToString(input_list):
382 return ToStringWithoutMetric(input_list, ('', ''))
383
384
385def ToStringWithoutMetric(input_list, metric):
386 i = 1
387 output_str = ""
388 for m in input_list:
389 if m != metric:
390 output_str = output_str + ("%s. %s\n" % (i, m[1]))
391 i += 1
392 return output_str
393
394
395def GetIdx(text_list):
396 return int(raw_input(text_list)) - 1
397
398
399def main():
400 filename = sys.argv[1]
401
402 # Setup.
403 idx_metric = GetIdx("Choose metric:\n0. All\n%s" % ToString(RESULTS))
404 if idx_metric == -1:
405 # Plot all metrics. One subplot for each metric.
406 # Per subplot: metric vs bitrate (per resolution).
407 cores = ParseSetting(filename, CORES[1])
408 setting1 = CORES[1]
409 setting2 = WIDTH[1]
410 sub_keys = [cores[0]] * len(Y_METRICS)
411 y_metrics = Y_METRICS
412 x_metric = BITRATE[1]
413 else:
414 resolutions = ParseSetting(filename, WIDTH[1])
415 idx = GetIdx("Select metric for x-axis:\n%s" % ToString(X_SETTINGS))
416 if X_SETTINGS[idx] == BITRATE:
asapersson7041eed2017-02-13 09:37:57417 idx = GetIdx("Plot per:\n%s" % ToStringWithoutMetric(SUBPLOT_SETTINGS,
418 BITRATE))
419 idx_setting = METRICS_TO_PARSE.index(SUBPLOT_SETTINGS[idx])
asapersson38e93242017-02-10 09:37:17420 # Plot one metric. One subplot for each resolution.
421 # Per subplot: metric vs bitrate (per setting).
422 setting1 = WIDTH[1]
423 setting2 = METRICS_TO_PARSE[idx_setting][1]
424 sub_keys = resolutions
425 y_metrics = [RESULTS[idx_metric][1]] * len(sub_keys)
426 x_metric = BITRATE[1]
427 else:
428 # Plot one metric. One subplot for each resolution.
429 # Per subplot: metric vs setting (per bitrate).
430 setting1 = WIDTH[1]
431 setting2 = BITRATE[1]
432 sub_keys = resolutions
433 y_metrics = [RESULTS[idx_metric][1]] * len(sub_keys)
434 x_metric = X_SETTINGS[idx][1]
435
436 metrics = ParseMetrics(filename, setting1, setting2)
437
438 # Stretch fig size.
439 figsize = plt.rcParams["figure.figsize"]
440 figsize[0] *= FIG_SIZE_SCALE_FACTOR_X
441 figsize[1] *= FIG_SIZE_SCALE_FACTOR_Y
442 plt.rcParams["figure.figsize"] = figsize
443
asapersson30df64f2017-05-19 11:07:38444 PlotFigure(sub_keys, y_metrics, x_metric, metrics,
445 GetTitle(filename, setting2))
asapersson38e93242017-02-10 09:37:17446
447 plt.show()
448
449
450if __name__ == '__main__':
451 main()