Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: webrtc/modules/video_coding/codecs/test/plot_webrtc_test_logs.py

Issue 2643853002: Add script for plotting statistics from webrtc integration test logs. (Closed)
Patch Set: address comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # 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
11 Usage: $ python plot_webrtc_test_logs.py filename.txt
12 """
13
14 import numpy
15 import sys
16 import re
17
18 import matplotlib.pyplot as plt
19
20 # Log events.
21 EVENT_START = 'RUN ] CodecSettings/PlotVideoProcessorIntegrationTest.'
22 EVENT_END = 'OK ] CodecSettings/PlotVideoProcessorIntegrationTest.'
23
24 # Metrics to plot, tuple: (name to parse in file, label to use when plotting).
25 BITRATE = ('Target Bitrate', 'bitrate (kbps)')
26 WIDTH = ('Width', 'width')
27 HEIGHT = ('Height', 'height')
28 FILENAME = ('Filename', 'clip')
29 CODEC_TYPE = ('Codec type', 'Codec')
30 ENCODER_IMPLEMENTATION_NAME = ('Encoder implementation name', 'enc name')
31 DECODER_IMPLEMENTATION_NAME = ('Decoder implementation name', 'dec name')
32 NUM_FRAMES = ('Total # of frames', 'num frames')
33 CORES = ('#CPU cores used', 'cores')
34 DENOISING = ('Denoising', 'denoising')
35 RESILIENCE = ('Resilience', 'resilience')
36 ERROR_CONCEALMENT = ('Error concealment', 'error concealment')
37 PSNR = ('PSNR avg', 'PSNR (dB)')
38 SSIM = ('SSIM avg', 'SSIM')
39 ENC_BITRATE = ('Encoding bitrate', 'encoded bitrate (kbps)')
40 FRAMERATE = ('Frame rate', 'fps')
41 NUM_DROPPED_FRAMES = ('Number of dropped frames', 'num dropped frames')
42 NUM_FRAMES_TO_TARGET = ('Number of frames to approach target rate',
43 'frames to reach target rate')
44 ENCODE_TIME = ('Encoding time', 'encode time (us)')
45 ENCODE_TIME_AVG = ('Encoding time', 'encode time (us) avg')
46 DECODE_TIME = ('Decoding time', 'decode time (us)')
47 DECODE_TIME_AVG = ('Decoding time', 'decode time (us) avg')
48 FRAME_SIZE = ('Frame sizes', 'frame size (bytes)')
49 FRAME_SIZE_AVG = ('Frame sizes', 'frame size (bytes) avg')
50 AVG_KEY_FRAME_SIZE = ('Average key frame size', 'avg key frame size (bytes)')
51 AVG_NON_KEY_FRAME_SIZE = ('Average non-key frame size',
52 'avg non-key frame size (bytes)')
53
54 # Settings.
55 SETTINGS = [
56 WIDTH,
57 HEIGHT,
58 FILENAME,
59 CODEC_TYPE,
60 NUM_FRAMES,
61 ENCODER_IMPLEMENTATION_NAME,
62 DECODER_IMPLEMENTATION_NAME,
63 ENCODE_TIME,
64 DECODE_TIME,
65 FRAME_SIZE,
66 ]
67
68 # Settings, options for x-axis.
69 X_SETTINGS = [
70 CORES,
71 FRAMERATE,
72 DENOISING,
73 RESILIENCE,
74 ERROR_CONCEALMENT,
75 BITRATE, # TODO(asapersson): Needs to be last.
76 ]
77
78 # Results.
79 RESULTS = [
80 PSNR,
81 SSIM,
82 ENC_BITRATE,
83 NUM_DROPPED_FRAMES,
84 NUM_FRAMES_TO_TARGET,
85 ENCODE_TIME_AVG,
86 DECODE_TIME_AVG,
87 AVG_KEY_FRAME_SIZE,
88 AVG_NON_KEY_FRAME_SIZE,
89 ]
90
91 METRICS_TO_PARSE = SETTINGS + X_SETTINGS + RESULTS
92
93 Y_METRICS = [res[1] for res in RESULTS]
94
95 # Parameters for plotting.
96 FIG_SIZE_SCALE_FACTOR_X = 2
97 FIG_SIZE_SCALE_FACTOR_Y = 2.8
98 GRID_COLOR = [0.45, 0.45, 0.45]
99
100
101 def ParseSetting(filename, setting):
102 """Parses setting from file.
103
104 Args:
105 filename: The name of the file.
106 setting: Name of setting to parse (e.g. width).
107
108 Returns:
109 A list holding parsed settings, e.g. ['width: 128.0', 'width: 160.0'] """
110
111 settings = []
112
113 f = open(filename)
114 while True:
115 line = f.readline()
116 if not line:
117 break
118 if re.search(r'%s' % EVENT_START, line):
119 # Parse event.
120 parsed = {}
121 while True:
122 line = f.readline()
123 if not line:
124 break
125 if re.search(r'%s' % EVENT_END, line):
126 # Add parsed setting to list.
127 if setting in parsed:
128 s = setting + ': ' + str(parsed[setting])
129 if s not in settings:
130 settings.append(s)
131 break
132
133 TryFindMetric(parsed, line, f)
134
135 f.close()
136 return settings
137
138
139 def ParseMetrics(filename, setting1, setting2):
140 """Parses metrics from file.
141
142 Args:
143 filename: The name of the file.
144 setting1: First setting for sorting metrics (e.g. width).
145 setting2: Second setting for sorting metrics (e.g. cores).
146
147 Returns:
148 A dictionary holding parsed metrics.
149
150 For example:
151 metrics[key1][key2][measurement]
152
153 metrics = {
154 "width: 352": {
155 "cores: 1.0": {
156 "encode time (us)": [0.718005, 0.806925, 0.909726, 0.931835, 0.953642],
157 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
158 "bitrate (kbps)": [50, 100, 300, 500, 1000]
159 },
160 "cores: 2.0": {
161 "encode time (us)": [0.718005, 0.806925, 0.909726, 0.931835, 0.953642],
162 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
163 "bitrate (kbps)": [50, 100, 300, 500, 1000]
164 },
165 },
166 "width: 176": {
167 "cores: 1.0": {
168 "encode time (us)": [0.857897, 0.91608, 0.959173, 0.971116, 0.980961],
169 "PSNR (dB)": [30.243646, 33.375592, 37.574387, 39.42184, 41.437897],
170 "bitrate (kbps)": [50, 100, 300, 500, 1000]
171 },
172 }
173 } """
174
175 metrics = {}
176
177 # Parse events.
178 f = open(filename)
179 while True:
180 line = f.readline()
181 if not line:
182 break
183 if re.search(r'%s' % EVENT_START, line):
184 # Parse event.
185 parsed = {}
186 while True:
187 line = f.readline()
188 if not line:
189 break
190 if re.search(r'%s' % EVENT_END, line):
191 # Add parsed values to metrics.
192 key1 = setting1 + ': ' + str(parsed[setting1])
193 key2 = setting2 + ': ' + str(parsed[setting2])
194 if key1 not in metrics:
195 metrics[key1] = {}
196 if key2 not in metrics[key1]:
197 metrics[key1][key2] = {}
198
199 for label in parsed:
200 if label not in metrics[key1][key2]:
201 metrics[key1][key2][label] = []
202 metrics[key1][key2][label].append(parsed[label])
203
204 break
205
206 TryFindMetric(parsed, line, f)
207
208 f.close()
209 return metrics
210
211
212 def TryFindMetric(parsed, line, f):
213 for metric in METRICS_TO_PARSE:
214 name = metric[0]
215 label = metric[1]
216 if re.search(r'%s' % name, line):
217 found, value = GetMetric(name, line)
218 if not found:
219 # TODO(asapersson): Change format.
220 # Try find min, max, average stats.
221 found, minimum = GetMetric("Min", f.readline())
222 if not found:
223 return
224 found, maximum = GetMetric("Max", f.readline())
225 if not found:
226 return
227 found, average = GetMetric("Average", f.readline())
228 if not found:
229 return
230
231 parsed[label + ' min'] = minimum
232 parsed[label + ' max'] = maximum
233 parsed[label + ' avg'] = average
234
235 parsed[label] = value
236 return
237
238
239 def GetMetric(name, string):
240 # Float (e.g. bitrate = 98.8253).
241 pattern = r'%s\s*[:=]\s*([+-]?\d+\.*\d*)' % name
242 m = re.search(r'%s' % pattern, string)
243 if m is not None:
244 return StringToFloat(m.group(1))
245
246 # Alphanumeric characters (e.g. codec type : VP8).
247 pattern = r'%s\s*[:=]\s*(\w+)' % name
248 m = re.search(r'%s' % pattern, string)
249 if m is not None:
250 return True, m.group(1)
251
252 return False, -1
253
254
255 def StringToFloat(value):
256 try:
257 value = float(value)
258 except ValueError:
259 print "Not a float, skipped %s" % value
260 return False, -1
261
262 return True, value
263
264
265 def Plot(y_metric, x_metric, metrics):
266 """Plots y_metric vs x_metric per key in metrics.
267
268 For example:
269 y_metric = 'PSNR (dB)'
270 x_metric = 'bitrate (kbps)'
271 metrics = {
272 "cores: 1.0": {
273 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
274 "bitrate (kbps)": [50, 100, 300, 500, 1000]
275 },
276 "cores: 2.0": {
277 "PSNR (dB)": [25.546029, 29.465518, 34.723535, 36.428493, 38.686551],
278 "bitrate (kbps)": [50, 100, 300, 500, 1000]
279 },
280 }
281 """
282 for key in metrics:
283 data = metrics[key]
284 if y_metric not in data:
285 print "Failed to find metric: %s" % y_metric
286 continue
287
288 y = numpy.array(data[y_metric])
289 x = numpy.array(data[x_metric])
290 if len(y) != len(x):
291 print "Length mismatch for %s, %s" % (y, x)
292 continue
293
294 label = y_metric + ' - ' + str(key)
295
296 plt.plot(x, y, label=label, linewidth=1.5, marker='o', markersize=5,
297 markeredgewidth=0.0)
298
299
300 def PlotFigure(settings, y_metrics, x_metric, metrics, title):
301 """Plots metrics in y_metrics list. One figure is plotted and each entry
302 in the list is plotted in a subplot (and sorted per settings).
303
304 For example:
305 settings = ['width: 128.0', 'width: 160.0']. Sort subplot per setting.
306 y_metrics = ['PSNR (dB)', 'PSNR (dB)']. Metric to plot per subplot.
307 x_metric = 'bitrate (kbps)'
308
309 """
310
311 plt.figure()
312 plt.suptitle(title, fontsize='small', fontweight='bold')
313 rows = len(settings)
314 cols = 1
315 pos = 1
316 while pos <= rows:
317 plt.rc('grid', color=GRID_COLOR)
318 ax = plt.subplot(rows, cols, pos)
319 plt.grid()
320 plt.setp(ax.get_xticklabels(), visible=(pos == rows), fontsize='small')
321 plt.setp(ax.get_yticklabels(), fontsize='small')
322 setting = settings[pos - 1]
323 Plot(y_metrics[pos - 1], x_metric, metrics[setting])
324 plt.title(setting, fontsize='x-small')
325 plt.legend(fontsize='xx-small')
326 pos += 1
327
328 plt.xlabel(x_metric, fontsize='small')
329 plt.subplots_adjust(left=0.04, right=0.98, bottom=0.04, top=0.96, hspace=0.1)
330
331
332 def GetTitle(filename):
333 title = ''
334 codec_types = ParseSetting(filename, CODEC_TYPE[1])
335 for i in range(0, len(codec_types)):
336 title += codec_types[i] + ', '
337
338 framerate = ParseSetting(filename, FRAMERATE[1])
339 for i in range(0, len(framerate)):
340 title += framerate[i].split('.')[0] + ', '
341
342 enc_names = ParseSetting(filename, ENCODER_IMPLEMENTATION_NAME[1])
343 for i in range(0, len(enc_names)):
344 title += enc_names[i] + ', '
345
346 dec_names = ParseSetting(filename, DECODER_IMPLEMENTATION_NAME[1])
347 for i in range(0, len(dec_names)):
348 title += dec_names[i] + ', '
349
350 filenames = ParseSetting(filename, FILENAME[1])
351 title += filenames[0].split('_')[0]
352
353 num_frames = ParseSetting(filename, NUM_FRAMES[1])
354 for i in range(0, len(num_frames)):
355 title += ' (' + num_frames[i].split('.')[0] + ')'
356
357 return title
358
359
360 def ToString(input_list):
361 return ToStringWithoutMetric(input_list, ('', ''))
362
363
364 def ToStringWithoutMetric(input_list, metric):
365 i = 1
366 output_str = ""
367 for m in input_list:
368 if m != metric:
369 output_str = output_str + ("%s. %s\n" % (i, m[1]))
370 i += 1
371 return output_str
372
373
374 def GetIdx(text_list):
375 return int(raw_input(text_list)) - 1
376
377
378 def main():
379 filename = sys.argv[1]
380
381 # Setup.
382 idx_metric = GetIdx("Choose metric:\n0. All\n%s" % ToString(RESULTS))
383 if idx_metric == -1:
384 # Plot all metrics. One subplot for each metric.
385 # Per subplot: metric vs bitrate (per resolution).
386 cores = ParseSetting(filename, CORES[1])
387 setting1 = CORES[1]
388 setting2 = WIDTH[1]
389 sub_keys = [cores[0]] * len(Y_METRICS)
390 y_metrics = Y_METRICS
391 x_metric = BITRATE[1]
392 else:
393 resolutions = ParseSetting(filename, WIDTH[1])
394 idx = GetIdx("Select metric for x-axis:\n%s" % ToString(X_SETTINGS))
395 if X_SETTINGS[idx] == BITRATE:
396 idx = GetIdx("Plot per:\n%s" % ToStringWithoutMetric(X_SETTINGS, BITRATE))
397 idx_setting = METRICS_TO_PARSE.index(X_SETTINGS[idx])
398 # Plot one metric. One subplot for each resolution.
399 # Per subplot: metric vs bitrate (per setting).
400 setting1 = WIDTH[1]
401 setting2 = METRICS_TO_PARSE[idx_setting][1]
402 sub_keys = resolutions
403 y_metrics = [RESULTS[idx_metric][1]] * len(sub_keys)
404 x_metric = BITRATE[1]
405 else:
406 # Plot one metric. One subplot for each resolution.
407 # Per subplot: metric vs setting (per bitrate).
408 setting1 = WIDTH[1]
409 setting2 = BITRATE[1]
410 sub_keys = resolutions
411 y_metrics = [RESULTS[idx_metric][1]] * len(sub_keys)
412 x_metric = X_SETTINGS[idx][1]
413
414 metrics = ParseMetrics(filename, setting1, setting2)
415
416 # Stretch fig size.
417 figsize = plt.rcParams["figure.figsize"]
418 figsize[0] *= FIG_SIZE_SCALE_FACTOR_X
419 figsize[1] *= FIG_SIZE_SCALE_FACTOR_Y
420 plt.rcParams["figure.figsize"] = figsize
421
422 PlotFigure(sub_keys, y_metrics, x_metric, metrics, GetTitle(filename))
423
424 plt.show()
425
426
427 if __name__ == '__main__':
428 main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698