1*77c1e3ccSAndroid Build Coastguard Worker#!/usr/bin/python 2*77c1e3ccSAndroid Build Coastguard Worker# 3*77c1e3ccSAndroid Build Coastguard Worker# Copyright (c) 2016, Alliance for Open Media. All rights reserved. 4*77c1e3ccSAndroid Build Coastguard Worker# 5*77c1e3ccSAndroid Build Coastguard Worker# This source code is subject to the terms of the BSD 2 Clause License and 6*77c1e3ccSAndroid Build Coastguard Worker# the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 7*77c1e3ccSAndroid Build Coastguard Worker# was not distributed with this source code in the LICENSE file, you can 8*77c1e3ccSAndroid Build Coastguard Worker# obtain it at www.aomedia.org/license/software. If the Alliance for Open 9*77c1e3ccSAndroid Build Coastguard Worker# Media Patent License 1.0 was not distributed with this source code in the 10*77c1e3ccSAndroid Build Coastguard Worker# PATENTS file, you can obtain it at www.aomedia.org/license/patent. 11*77c1e3ccSAndroid Build Coastguard Worker# 12*77c1e3ccSAndroid Build Coastguard Worker 13*77c1e3ccSAndroid Build Coastguard Worker"""Converts video encoding result data from text files to visualization 14*77c1e3ccSAndroid Build Coastguard Workerdata source.""" 15*77c1e3ccSAndroid Build Coastguard Worker 16*77c1e3ccSAndroid Build Coastguard Worker__author__ = "[email protected] (James Zern)," 17*77c1e3ccSAndroid Build Coastguard Worker__author__ += "[email protected] (Jim Bankoski)" 18*77c1e3ccSAndroid Build Coastguard Worker 19*77c1e3ccSAndroid Build Coastguard Workerimport fnmatch 20*77c1e3ccSAndroid Build Coastguard Workerimport numpy as np 21*77c1e3ccSAndroid Build Coastguard Workerimport scipy as sp 22*77c1e3ccSAndroid Build Coastguard Workerimport scipy.interpolate 23*77c1e3ccSAndroid Build Coastguard Workerimport os 24*77c1e3ccSAndroid Build Coastguard Workerimport re 25*77c1e3ccSAndroid Build Coastguard Workerimport string 26*77c1e3ccSAndroid Build Coastguard Workerimport sys 27*77c1e3ccSAndroid Build Coastguard Workerimport math 28*77c1e3ccSAndroid Build Coastguard Workerimport warnings 29*77c1e3ccSAndroid Build Coastguard Worker 30*77c1e3ccSAndroid Build Coastguard Workerimport gviz_api 31*77c1e3ccSAndroid Build Coastguard Worker 32*77c1e3ccSAndroid Build Coastguard Workerfrom os.path import basename 33*77c1e3ccSAndroid Build Coastguard Workerfrom os.path import splitext 34*77c1e3ccSAndroid Build Coastguard Worker 35*77c1e3ccSAndroid Build Coastguard Workerwarnings.simplefilter('ignore', np.RankWarning) 36*77c1e3ccSAndroid Build Coastguard Workerwarnings.simplefilter('ignore', RuntimeWarning) 37*77c1e3ccSAndroid Build Coastguard Worker 38*77c1e3ccSAndroid Build Coastguard Workerdef bdsnr2(metric_set1, metric_set2): 39*77c1e3ccSAndroid Build Coastguard Worker """ 40*77c1e3ccSAndroid Build Coastguard Worker BJONTEGAARD Bjontegaard metric calculation adapted 41*77c1e3ccSAndroid Build Coastguard Worker Bjontegaard's snr metric allows to compute the average % saving in decibels 42*77c1e3ccSAndroid Build Coastguard Worker between two rate-distortion curves [1]. This is an adaptation of that 43*77c1e3ccSAndroid Build Coastguard Worker method that fixes inconsistencies when the curve fit operation goes awry 44*77c1e3ccSAndroid Build Coastguard Worker by replacing the curve fit function with a Piecewise Cubic Hermite 45*77c1e3ccSAndroid Build Coastguard Worker Interpolating Polynomial and then integrating that by evaluating that 46*77c1e3ccSAndroid Build Coastguard Worker function at small intervals using the trapezoid method to calculate 47*77c1e3ccSAndroid Build Coastguard Worker the integral. 48*77c1e3ccSAndroid Build Coastguard Worker 49*77c1e3ccSAndroid Build Coastguard Worker metric_set1 - list of tuples ( bitrate, metric ) for first graph 50*77c1e3ccSAndroid Build Coastguard Worker metric_set2 - list of tuples ( bitrate, metric ) for second graph 51*77c1e3ccSAndroid Build Coastguard Worker """ 52*77c1e3ccSAndroid Build Coastguard Worker 53*77c1e3ccSAndroid Build Coastguard Worker if not metric_set1 or not metric_set2: 54*77c1e3ccSAndroid Build Coastguard Worker return 0.0 55*77c1e3ccSAndroid Build Coastguard Worker 56*77c1e3ccSAndroid Build Coastguard Worker try: 57*77c1e3ccSAndroid Build Coastguard Worker 58*77c1e3ccSAndroid Build Coastguard Worker # pchip_interlopate requires keys sorted by x axis. x-axis will 59*77c1e3ccSAndroid Build Coastguard Worker # be our metric not the bitrate so sort by metric. 60*77c1e3ccSAndroid Build Coastguard Worker metric_set1.sort() 61*77c1e3ccSAndroid Build Coastguard Worker metric_set2.sort() 62*77c1e3ccSAndroid Build Coastguard Worker 63*77c1e3ccSAndroid Build Coastguard Worker # Pull the log of the rate and clamped psnr from metric_sets. 64*77c1e3ccSAndroid Build Coastguard Worker log_rate1 = [math.log(x[0]) for x in metric_set1] 65*77c1e3ccSAndroid Build Coastguard Worker metric1 = [100.0 if x[1] == float('inf') else x[1] for x in metric_set1] 66*77c1e3ccSAndroid Build Coastguard Worker log_rate2 = [math.log(x[0]) for x in metric_set2] 67*77c1e3ccSAndroid Build Coastguard Worker metric2 = [100.0 if x[1] == float('inf') else x[1] for x in metric_set2] 68*77c1e3ccSAndroid Build Coastguard Worker 69*77c1e3ccSAndroid Build Coastguard Worker # Integration interval. This metric only works on the area that's 70*77c1e3ccSAndroid Build Coastguard Worker # overlapping. Extrapolation of these things is sketchy so we avoid. 71*77c1e3ccSAndroid Build Coastguard Worker min_int = max([min(log_rate1), min(log_rate2)]) 72*77c1e3ccSAndroid Build Coastguard Worker max_int = min([max(log_rate1), max(log_rate2)]) 73*77c1e3ccSAndroid Build Coastguard Worker 74*77c1e3ccSAndroid Build Coastguard Worker # No overlap means no sensible metric possible. 75*77c1e3ccSAndroid Build Coastguard Worker if max_int <= min_int: 76*77c1e3ccSAndroid Build Coastguard Worker return 0.0 77*77c1e3ccSAndroid Build Coastguard Worker 78*77c1e3ccSAndroid Build Coastguard Worker # Use Piecewise Cubic Hermite Interpolating Polynomial interpolation to 79*77c1e3ccSAndroid Build Coastguard Worker # create 100 new samples points separated by interval. 80*77c1e3ccSAndroid Build Coastguard Worker lin = np.linspace(min_int, max_int, num=100, retstep=True) 81*77c1e3ccSAndroid Build Coastguard Worker interval = lin[1] 82*77c1e3ccSAndroid Build Coastguard Worker samples = lin[0] 83*77c1e3ccSAndroid Build Coastguard Worker v1 = scipy.interpolate.pchip_interpolate(log_rate1, metric1, samples) 84*77c1e3ccSAndroid Build Coastguard Worker v2 = scipy.interpolate.pchip_interpolate(log_rate2, metric2, samples) 85*77c1e3ccSAndroid Build Coastguard Worker 86*77c1e3ccSAndroid Build Coastguard Worker # Calculate the integral using the trapezoid method on the samples. 87*77c1e3ccSAndroid Build Coastguard Worker int_v1 = np.trapz(v1, dx=interval) 88*77c1e3ccSAndroid Build Coastguard Worker int_v2 = np.trapz(v2, dx=interval) 89*77c1e3ccSAndroid Build Coastguard Worker 90*77c1e3ccSAndroid Build Coastguard Worker # Calculate the average improvement. 91*77c1e3ccSAndroid Build Coastguard Worker avg_exp_diff = (int_v2 - int_v1) / (max_int - min_int) 92*77c1e3ccSAndroid Build Coastguard Worker 93*77c1e3ccSAndroid Build Coastguard Worker except (TypeError, ZeroDivisionError, ValueError, np.RankWarning) as e: 94*77c1e3ccSAndroid Build Coastguard Worker return 0 95*77c1e3ccSAndroid Build Coastguard Worker 96*77c1e3ccSAndroid Build Coastguard Worker return avg_exp_diff 97*77c1e3ccSAndroid Build Coastguard Worker 98*77c1e3ccSAndroid Build Coastguard Workerdef bdrate2(metric_set1, metric_set2): 99*77c1e3ccSAndroid Build Coastguard Worker """ 100*77c1e3ccSAndroid Build Coastguard Worker BJONTEGAARD Bjontegaard metric calculation adapted 101*77c1e3ccSAndroid Build Coastguard Worker Bjontegaard's metric allows to compute the average % saving in bitrate 102*77c1e3ccSAndroid Build Coastguard Worker between two rate-distortion curves [1]. This is an adaptation of that 103*77c1e3ccSAndroid Build Coastguard Worker method that fixes inconsistencies when the curve fit operation goes awry 104*77c1e3ccSAndroid Build Coastguard Worker by replacing the curve fit function with a Piecewise Cubic Hermite 105*77c1e3ccSAndroid Build Coastguard Worker Interpolating Polynomial and then integrating that by evaluating that 106*77c1e3ccSAndroid Build Coastguard Worker function at small intervals using the trapezoid method to calculate 107*77c1e3ccSAndroid Build Coastguard Worker the integral. 108*77c1e3ccSAndroid Build Coastguard Worker 109*77c1e3ccSAndroid Build Coastguard Worker metric_set1 - list of tuples ( bitrate, metric ) for first graph 110*77c1e3ccSAndroid Build Coastguard Worker metric_set2 - list of tuples ( bitrate, metric ) for second graph 111*77c1e3ccSAndroid Build Coastguard Worker """ 112*77c1e3ccSAndroid Build Coastguard Worker 113*77c1e3ccSAndroid Build Coastguard Worker if not metric_set1 or not metric_set2: 114*77c1e3ccSAndroid Build Coastguard Worker return 0.0 115*77c1e3ccSAndroid Build Coastguard Worker 116*77c1e3ccSAndroid Build Coastguard Worker try: 117*77c1e3ccSAndroid Build Coastguard Worker 118*77c1e3ccSAndroid Build Coastguard Worker # pchip_interlopate requires keys sorted by x axis. x-axis will 119*77c1e3ccSAndroid Build Coastguard Worker # be our metric not the bitrate so sort by metric. 120*77c1e3ccSAndroid Build Coastguard Worker metric_set1.sort(key=lambda tup: tup[1]) 121*77c1e3ccSAndroid Build Coastguard Worker metric_set2.sort(key=lambda tup: tup[1]) 122*77c1e3ccSAndroid Build Coastguard Worker 123*77c1e3ccSAndroid Build Coastguard Worker # Pull the log of the rate and clamped psnr from metric_sets. 124*77c1e3ccSAndroid Build Coastguard Worker log_rate1 = [math.log(x[0]) for x in metric_set1] 125*77c1e3ccSAndroid Build Coastguard Worker metric1 = [100.0 if x[1] == float('inf') else x[1] for x in metric_set1] 126*77c1e3ccSAndroid Build Coastguard Worker log_rate2 = [math.log(x[0]) for x in metric_set2] 127*77c1e3ccSAndroid Build Coastguard Worker metric2 = [100.0 if x[1] == float('inf') else x[1] for x in metric_set2] 128*77c1e3ccSAndroid Build Coastguard Worker 129*77c1e3ccSAndroid Build Coastguard Worker # Integration interval. This metric only works on the area that's 130*77c1e3ccSAndroid Build Coastguard Worker # overlapping. Extrapolation of these things is sketchy so we avoid. 131*77c1e3ccSAndroid Build Coastguard Worker min_int = max([min(metric1), min(metric2)]) 132*77c1e3ccSAndroid Build Coastguard Worker max_int = min([max(metric1), max(metric2)]) 133*77c1e3ccSAndroid Build Coastguard Worker 134*77c1e3ccSAndroid Build Coastguard Worker # No overlap means no sensible metric possible. 135*77c1e3ccSAndroid Build Coastguard Worker if max_int <= min_int: 136*77c1e3ccSAndroid Build Coastguard Worker return 0.0 137*77c1e3ccSAndroid Build Coastguard Worker 138*77c1e3ccSAndroid Build Coastguard Worker # Use Piecewise Cubic Hermite Interpolating Polynomial interpolation to 139*77c1e3ccSAndroid Build Coastguard Worker # create 100 new samples points separated by interval. 140*77c1e3ccSAndroid Build Coastguard Worker lin = np.linspace(min_int, max_int, num=100, retstep=True) 141*77c1e3ccSAndroid Build Coastguard Worker interval = lin[1] 142*77c1e3ccSAndroid Build Coastguard Worker samples = lin[0] 143*77c1e3ccSAndroid Build Coastguard Worker v1 = scipy.interpolate.pchip_interpolate(metric1, log_rate1, samples) 144*77c1e3ccSAndroid Build Coastguard Worker v2 = scipy.interpolate.pchip_interpolate(metric2, log_rate2, samples) 145*77c1e3ccSAndroid Build Coastguard Worker 146*77c1e3ccSAndroid Build Coastguard Worker # Calculate the integral using the trapezoid method on the samples. 147*77c1e3ccSAndroid Build Coastguard Worker int_v1 = np.trapz(v1, dx=interval) 148*77c1e3ccSAndroid Build Coastguard Worker int_v2 = np.trapz(v2, dx=interval) 149*77c1e3ccSAndroid Build Coastguard Worker 150*77c1e3ccSAndroid Build Coastguard Worker # Calculate the average improvement. 151*77c1e3ccSAndroid Build Coastguard Worker avg_exp_diff = (int_v2 - int_v1) / (max_int - min_int) 152*77c1e3ccSAndroid Build Coastguard Worker 153*77c1e3ccSAndroid Build Coastguard Worker except (TypeError, ZeroDivisionError, ValueError, np.RankWarning) as e: 154*77c1e3ccSAndroid Build Coastguard Worker return 0 155*77c1e3ccSAndroid Build Coastguard Worker 156*77c1e3ccSAndroid Build Coastguard Worker # Convert to a percentage. 157*77c1e3ccSAndroid Build Coastguard Worker avg_diff = (math.exp(avg_exp_diff) - 1) * 100 158*77c1e3ccSAndroid Build Coastguard Worker 159*77c1e3ccSAndroid Build Coastguard Worker return avg_diff 160*77c1e3ccSAndroid Build Coastguard Worker 161*77c1e3ccSAndroid Build Coastguard Worker 162*77c1e3ccSAndroid Build Coastguard Worker 163*77c1e3ccSAndroid Build Coastguard Workerdef FillForm(string_for_substitution, dictionary_of_vars): 164*77c1e3ccSAndroid Build Coastguard Worker """ 165*77c1e3ccSAndroid Build Coastguard Worker This function substitutes all matches of the command string //%% ... %%// 166*77c1e3ccSAndroid Build Coastguard Worker with the variable represented by ... . 167*77c1e3ccSAndroid Build Coastguard Worker """ 168*77c1e3ccSAndroid Build Coastguard Worker return_string = string_for_substitution 169*77c1e3ccSAndroid Build Coastguard Worker for i in re.findall("//%%(.*)%%//", string_for_substitution): 170*77c1e3ccSAndroid Build Coastguard Worker return_string = re.sub("//%%" + i + "%%//", dictionary_of_vars[i], 171*77c1e3ccSAndroid Build Coastguard Worker return_string) 172*77c1e3ccSAndroid Build Coastguard Worker return return_string 173*77c1e3ccSAndroid Build Coastguard Worker 174*77c1e3ccSAndroid Build Coastguard Worker 175*77c1e3ccSAndroid Build Coastguard Workerdef HasMetrics(line): 176*77c1e3ccSAndroid Build Coastguard Worker """ 177*77c1e3ccSAndroid Build Coastguard Worker The metrics files produced by aomenc are started with a B for headers. 178*77c1e3ccSAndroid Build Coastguard Worker """ 179*77c1e3ccSAndroid Build Coastguard Worker # If the first char of the first word on the line is a digit 180*77c1e3ccSAndroid Build Coastguard Worker if len(line) == 0: 181*77c1e3ccSAndroid Build Coastguard Worker return False 182*77c1e3ccSAndroid Build Coastguard Worker if len(line.split()) == 0: 183*77c1e3ccSAndroid Build Coastguard Worker return False 184*77c1e3ccSAndroid Build Coastguard Worker if line.split()[0][0:1].isdigit(): 185*77c1e3ccSAndroid Build Coastguard Worker return True 186*77c1e3ccSAndroid Build Coastguard Worker return False 187*77c1e3ccSAndroid Build Coastguard Worker 188*77c1e3ccSAndroid Build Coastguard Workerdef GetMetrics(file_name): 189*77c1e3ccSAndroid Build Coastguard Worker metric_file = open(file_name, "r") 190*77c1e3ccSAndroid Build Coastguard Worker return metric_file.readline().split(); 191*77c1e3ccSAndroid Build Coastguard Worker 192*77c1e3ccSAndroid Build Coastguard Workerdef ParseMetricFile(file_name, metric_column): 193*77c1e3ccSAndroid Build Coastguard Worker metric_set1 = set([]) 194*77c1e3ccSAndroid Build Coastguard Worker metric_file = open(file_name, "r") 195*77c1e3ccSAndroid Build Coastguard Worker for line in metric_file: 196*77c1e3ccSAndroid Build Coastguard Worker metrics = string.split(line) 197*77c1e3ccSAndroid Build Coastguard Worker if HasMetrics(line): 198*77c1e3ccSAndroid Build Coastguard Worker if metric_column < len(metrics): 199*77c1e3ccSAndroid Build Coastguard Worker try: 200*77c1e3ccSAndroid Build Coastguard Worker tuple = float(metrics[0]), float(metrics[metric_column]) 201*77c1e3ccSAndroid Build Coastguard Worker except: 202*77c1e3ccSAndroid Build Coastguard Worker tuple = float(metrics[0]), 0 203*77c1e3ccSAndroid Build Coastguard Worker else: 204*77c1e3ccSAndroid Build Coastguard Worker tuple = float(metrics[0]), 0 205*77c1e3ccSAndroid Build Coastguard Worker metric_set1.add(tuple) 206*77c1e3ccSAndroid Build Coastguard Worker metric_set1_sorted = sorted(metric_set1) 207*77c1e3ccSAndroid Build Coastguard Worker return metric_set1_sorted 208*77c1e3ccSAndroid Build Coastguard Worker 209*77c1e3ccSAndroid Build Coastguard Worker 210*77c1e3ccSAndroid Build Coastguard Workerdef FileBetter(file_name_1, file_name_2, metric_column, method): 211*77c1e3ccSAndroid Build Coastguard Worker """ 212*77c1e3ccSAndroid Build Coastguard Worker Compares two data files and determines which is better and by how 213*77c1e3ccSAndroid Build Coastguard Worker much. Also produces a histogram of how much better, by PSNR. 214*77c1e3ccSAndroid Build Coastguard Worker metric_column is the metric. 215*77c1e3ccSAndroid Build Coastguard Worker """ 216*77c1e3ccSAndroid Build Coastguard Worker # Store and parse our two files into lists of unique tuples. 217*77c1e3ccSAndroid Build Coastguard Worker 218*77c1e3ccSAndroid Build Coastguard Worker # Read the two files, parsing out lines starting with bitrate. 219*77c1e3ccSAndroid Build Coastguard Worker metric_set1_sorted = ParseMetricFile(file_name_1, metric_column) 220*77c1e3ccSAndroid Build Coastguard Worker metric_set2_sorted = ParseMetricFile(file_name_2, metric_column) 221*77c1e3ccSAndroid Build Coastguard Worker 222*77c1e3ccSAndroid Build Coastguard Worker 223*77c1e3ccSAndroid Build Coastguard Worker def GraphBetter(metric_set1_sorted, metric_set2_sorted, base_is_set_2): 224*77c1e3ccSAndroid Build Coastguard Worker """ 225*77c1e3ccSAndroid Build Coastguard Worker Search through the sorted metric file for metrics on either side of 226*77c1e3ccSAndroid Build Coastguard Worker the metric from file 1. Since both lists are sorted we really 227*77c1e3ccSAndroid Build Coastguard Worker should not have to search through the entire range, but these 228*77c1e3ccSAndroid Build Coastguard Worker are small files.""" 229*77c1e3ccSAndroid Build Coastguard Worker total_bitrate_difference_ratio = 0.0 230*77c1e3ccSAndroid Build Coastguard Worker count = 0 231*77c1e3ccSAndroid Build Coastguard Worker for bitrate, metric in metric_set1_sorted: 232*77c1e3ccSAndroid Build Coastguard Worker if bitrate == 0: 233*77c1e3ccSAndroid Build Coastguard Worker continue 234*77c1e3ccSAndroid Build Coastguard Worker for i in range(len(metric_set2_sorted) - 1): 235*77c1e3ccSAndroid Build Coastguard Worker s2_bitrate_0, s2_metric_0 = metric_set2_sorted[i] 236*77c1e3ccSAndroid Build Coastguard Worker s2_bitrate_1, s2_metric_1 = metric_set2_sorted[i + 1] 237*77c1e3ccSAndroid Build Coastguard Worker # We have a point on either side of our metric range. 238*77c1e3ccSAndroid Build Coastguard Worker if metric > s2_metric_0 and metric <= s2_metric_1: 239*77c1e3ccSAndroid Build Coastguard Worker 240*77c1e3ccSAndroid Build Coastguard Worker # Calculate a slope. 241*77c1e3ccSAndroid Build Coastguard Worker if s2_metric_1 - s2_metric_0 != 0: 242*77c1e3ccSAndroid Build Coastguard Worker metric_slope = ((s2_bitrate_1 - s2_bitrate_0) / 243*77c1e3ccSAndroid Build Coastguard Worker (s2_metric_1 - s2_metric_0)) 244*77c1e3ccSAndroid Build Coastguard Worker else: 245*77c1e3ccSAndroid Build Coastguard Worker metric_slope = 0 246*77c1e3ccSAndroid Build Coastguard Worker 247*77c1e3ccSAndroid Build Coastguard Worker estimated_s2_bitrate = (s2_bitrate_0 + (metric - s2_metric_0) * 248*77c1e3ccSAndroid Build Coastguard Worker metric_slope) 249*77c1e3ccSAndroid Build Coastguard Worker 250*77c1e3ccSAndroid Build Coastguard Worker if estimated_s2_bitrate == 0: 251*77c1e3ccSAndroid Build Coastguard Worker continue 252*77c1e3ccSAndroid Build Coastguard Worker # Calculate percentage difference as given by base. 253*77c1e3ccSAndroid Build Coastguard Worker if base_is_set_2 == 0: 254*77c1e3ccSAndroid Build Coastguard Worker bitrate_difference_ratio = ((bitrate - estimated_s2_bitrate) / 255*77c1e3ccSAndroid Build Coastguard Worker bitrate) 256*77c1e3ccSAndroid Build Coastguard Worker else: 257*77c1e3ccSAndroid Build Coastguard Worker bitrate_difference_ratio = ((bitrate - estimated_s2_bitrate) / 258*77c1e3ccSAndroid Build Coastguard Worker estimated_s2_bitrate) 259*77c1e3ccSAndroid Build Coastguard Worker 260*77c1e3ccSAndroid Build Coastguard Worker total_bitrate_difference_ratio += bitrate_difference_ratio 261*77c1e3ccSAndroid Build Coastguard Worker count += 1 262*77c1e3ccSAndroid Build Coastguard Worker break 263*77c1e3ccSAndroid Build Coastguard Worker 264*77c1e3ccSAndroid Build Coastguard Worker # Calculate the average improvement between graphs. 265*77c1e3ccSAndroid Build Coastguard Worker if count != 0: 266*77c1e3ccSAndroid Build Coastguard Worker avg = total_bitrate_difference_ratio / count 267*77c1e3ccSAndroid Build Coastguard Worker 268*77c1e3ccSAndroid Build Coastguard Worker else: 269*77c1e3ccSAndroid Build Coastguard Worker avg = 0.0 270*77c1e3ccSAndroid Build Coastguard Worker 271*77c1e3ccSAndroid Build Coastguard Worker return avg 272*77c1e3ccSAndroid Build Coastguard Worker 273*77c1e3ccSAndroid Build Coastguard Worker # Be fair to both graphs by testing all the points in each. 274*77c1e3ccSAndroid Build Coastguard Worker if method == 'avg': 275*77c1e3ccSAndroid Build Coastguard Worker avg_improvement = 50 * ( 276*77c1e3ccSAndroid Build Coastguard Worker GraphBetter(metric_set1_sorted, metric_set2_sorted, 1) - 277*77c1e3ccSAndroid Build Coastguard Worker GraphBetter(metric_set2_sorted, metric_set1_sorted, 0)) 278*77c1e3ccSAndroid Build Coastguard Worker elif method == 'dsnr': 279*77c1e3ccSAndroid Build Coastguard Worker avg_improvement = bdsnr2(metric_set1_sorted, metric_set2_sorted) 280*77c1e3ccSAndroid Build Coastguard Worker else: 281*77c1e3ccSAndroid Build Coastguard Worker avg_improvement = bdrate2(metric_set2_sorted, metric_set1_sorted) 282*77c1e3ccSAndroid Build Coastguard Worker 283*77c1e3ccSAndroid Build Coastguard Worker return avg_improvement 284*77c1e3ccSAndroid Build Coastguard Worker 285*77c1e3ccSAndroid Build Coastguard Worker 286*77c1e3ccSAndroid Build Coastguard Workerdef HandleFiles(variables): 287*77c1e3ccSAndroid Build Coastguard Worker """ 288*77c1e3ccSAndroid Build Coastguard Worker This script creates html for displaying metric data produced from data 289*77c1e3ccSAndroid Build Coastguard Worker in a video stats file, as created by the AOM project when enable_psnr 290*77c1e3ccSAndroid Build Coastguard Worker is turned on: 291*77c1e3ccSAndroid Build Coastguard Worker 292*77c1e3ccSAndroid Build Coastguard Worker Usage: visual_metrics.py template.html pattern base_dir sub_dir [ sub_dir2 ..] 293*77c1e3ccSAndroid Build Coastguard Worker 294*77c1e3ccSAndroid Build Coastguard Worker The script parses each metrics file [see below] that matches the 295*77c1e3ccSAndroid Build Coastguard Worker statfile_pattern in the baseline directory and looks for the file that 296*77c1e3ccSAndroid Build Coastguard Worker matches that same file in each of the sub_dirs, and compares the resultant 297*77c1e3ccSAndroid Build Coastguard Worker metrics bitrate, avg psnr, glb psnr, and ssim. " 298*77c1e3ccSAndroid Build Coastguard Worker 299*77c1e3ccSAndroid Build Coastguard Worker It provides a table in which each row is a file in the line directory, 300*77c1e3ccSAndroid Build Coastguard Worker and a column for each subdir, with the cells representing how that clip 301*77c1e3ccSAndroid Build Coastguard Worker compares to baseline for that subdir. A graph is given for each which 302*77c1e3ccSAndroid Build Coastguard Worker compares filesize to that metric. If you click on a point in the graph it 303*77c1e3ccSAndroid Build Coastguard Worker zooms in on that point. 304*77c1e3ccSAndroid Build Coastguard Worker 305*77c1e3ccSAndroid Build Coastguard Worker a SAMPLE metrics file: 306*77c1e3ccSAndroid Build Coastguard Worker 307*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 308*77c1e3ccSAndroid Build Coastguard Worker 25.911 38.242 38.104 38.258 38.121 75.790 14103 309*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 310*77c1e3ccSAndroid Build Coastguard Worker 49.982 41.264 41.129 41.255 41.122 83.993 19817 311*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 312*77c1e3ccSAndroid Build Coastguard Worker 74.967 42.911 42.767 42.899 42.756 87.928 17332 313*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 314*77c1e3ccSAndroid Build Coastguard Worker 100.012 43.983 43.838 43.881 43.738 89.695 25389 315*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 316*77c1e3ccSAndroid Build Coastguard Worker 149.980 45.338 45.203 45.184 45.043 91.591 25438 317*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 318*77c1e3ccSAndroid Build Coastguard Worker 199.852 46.225 46.123 46.113 45.999 92.679 28302 319*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 320*77c1e3ccSAndroid Build Coastguard Worker 249.922 46.864 46.773 46.777 46.673 93.334 27244 321*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 322*77c1e3ccSAndroid Build Coastguard Worker 299.998 47.366 47.281 47.317 47.220 93.844 27137 323*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 324*77c1e3ccSAndroid Build Coastguard Worker 349.769 47.746 47.677 47.722 47.648 94.178 32226 325*77c1e3ccSAndroid Build Coastguard Worker Bitrate AVGPsnr GLBPsnr AVPsnrP GLPsnrP VPXSSIM Time(us) 326*77c1e3ccSAndroid Build Coastguard Worker 399.773 48.032 47.971 48.013 47.946 94.362 36203 327*77c1e3ccSAndroid Build Coastguard Worker 328*77c1e3ccSAndroid Build Coastguard Worker sample use: 329*77c1e3ccSAndroid Build Coastguard Worker visual_metrics.py template.html "*stt" aom aom_b aom_c > metrics.html 330*77c1e3ccSAndroid Build Coastguard Worker """ 331*77c1e3ccSAndroid Build Coastguard Worker 332*77c1e3ccSAndroid Build Coastguard Worker # The template file is the html file into which we will write the 333*77c1e3ccSAndroid Build Coastguard Worker # data from the stats file, formatted correctly for the gviz_api. 334*77c1e3ccSAndroid Build Coastguard Worker template_file = open(variables[1], "r") 335*77c1e3ccSAndroid Build Coastguard Worker page_template = template_file.read() 336*77c1e3ccSAndroid Build Coastguard Worker template_file.close() 337*77c1e3ccSAndroid Build Coastguard Worker 338*77c1e3ccSAndroid Build Coastguard Worker # This is the path match pattern for finding stats files amongst 339*77c1e3ccSAndroid Build Coastguard Worker # all the other files it could be. eg: *.stt 340*77c1e3ccSAndroid Build Coastguard Worker file_pattern = variables[2] 341*77c1e3ccSAndroid Build Coastguard Worker 342*77c1e3ccSAndroid Build Coastguard Worker # This is the directory with files that we will use to do the comparison 343*77c1e3ccSAndroid Build Coastguard Worker # against. 344*77c1e3ccSAndroid Build Coastguard Worker baseline_dir = variables[3] 345*77c1e3ccSAndroid Build Coastguard Worker snrs = '' 346*77c1e3ccSAndroid Build Coastguard Worker filestable = {} 347*77c1e3ccSAndroid Build Coastguard Worker 348*77c1e3ccSAndroid Build Coastguard Worker filestable['dsnr'] = '' 349*77c1e3ccSAndroid Build Coastguard Worker filestable['drate'] = '' 350*77c1e3ccSAndroid Build Coastguard Worker filestable['avg'] = '' 351*77c1e3ccSAndroid Build Coastguard Worker 352*77c1e3ccSAndroid Build Coastguard Worker # Dirs is directories after the baseline to compare to the base. 353*77c1e3ccSAndroid Build Coastguard Worker dirs = variables[4:len(variables)] 354*77c1e3ccSAndroid Build Coastguard Worker 355*77c1e3ccSAndroid Build Coastguard Worker # Find the metric files in the baseline directory. 356*77c1e3ccSAndroid Build Coastguard Worker dir_list = sorted(fnmatch.filter(os.listdir(baseline_dir), file_pattern)) 357*77c1e3ccSAndroid Build Coastguard Worker 358*77c1e3ccSAndroid Build Coastguard Worker metrics = GetMetrics(baseline_dir + "/" + dir_list[0]) 359*77c1e3ccSAndroid Build Coastguard Worker 360*77c1e3ccSAndroid Build Coastguard Worker metrics_js = 'metrics = ["' + '", "'.join(metrics) + '"];' 361*77c1e3ccSAndroid Build Coastguard Worker 362*77c1e3ccSAndroid Build Coastguard Worker for column in range(1, len(metrics)): 363*77c1e3ccSAndroid Build Coastguard Worker 364*77c1e3ccSAndroid Build Coastguard Worker for metric in ['avg','dsnr','drate']: 365*77c1e3ccSAndroid Build Coastguard Worker description = {"file": ("string", "File")} 366*77c1e3ccSAndroid Build Coastguard Worker 367*77c1e3ccSAndroid Build Coastguard Worker # Go through each directory and add a column header to our description. 368*77c1e3ccSAndroid Build Coastguard Worker countoverall = {} 369*77c1e3ccSAndroid Build Coastguard Worker sumoverall = {} 370*77c1e3ccSAndroid Build Coastguard Worker 371*77c1e3ccSAndroid Build Coastguard Worker for directory in dirs: 372*77c1e3ccSAndroid Build Coastguard Worker description[directory] = ("number", directory) 373*77c1e3ccSAndroid Build Coastguard Worker countoverall[directory] = 0 374*77c1e3ccSAndroid Build Coastguard Worker sumoverall[directory] = 0 375*77c1e3ccSAndroid Build Coastguard Worker 376*77c1e3ccSAndroid Build Coastguard Worker # Data holds the data for the visualization, name given comes from 377*77c1e3ccSAndroid Build Coastguard Worker # gviz_api sample code. 378*77c1e3ccSAndroid Build Coastguard Worker data = [] 379*77c1e3ccSAndroid Build Coastguard Worker for filename in dir_list: 380*77c1e3ccSAndroid Build Coastguard Worker row = {'file': splitext(basename(filename))[0] } 381*77c1e3ccSAndroid Build Coastguard Worker baseline_file_name = baseline_dir + "/" + filename 382*77c1e3ccSAndroid Build Coastguard Worker 383*77c1e3ccSAndroid Build Coastguard Worker # Read the metric file from each of the directories in our list. 384*77c1e3ccSAndroid Build Coastguard Worker for directory in dirs: 385*77c1e3ccSAndroid Build Coastguard Worker metric_file_name = directory + "/" + filename 386*77c1e3ccSAndroid Build Coastguard Worker 387*77c1e3ccSAndroid Build Coastguard Worker # If there is a metric file in the current directory, open it 388*77c1e3ccSAndroid Build Coastguard Worker # and calculate its overall difference between it and the baseline 389*77c1e3ccSAndroid Build Coastguard Worker # directory's metric file. 390*77c1e3ccSAndroid Build Coastguard Worker if os.path.isfile(metric_file_name): 391*77c1e3ccSAndroid Build Coastguard Worker overall = FileBetter(baseline_file_name, metric_file_name, 392*77c1e3ccSAndroid Build Coastguard Worker column, metric) 393*77c1e3ccSAndroid Build Coastguard Worker row[directory] = overall 394*77c1e3ccSAndroid Build Coastguard Worker 395*77c1e3ccSAndroid Build Coastguard Worker sumoverall[directory] += overall 396*77c1e3ccSAndroid Build Coastguard Worker countoverall[directory] += 1 397*77c1e3ccSAndroid Build Coastguard Worker 398*77c1e3ccSAndroid Build Coastguard Worker data.append(row) 399*77c1e3ccSAndroid Build Coastguard Worker 400*77c1e3ccSAndroid Build Coastguard Worker # Add the overall numbers. 401*77c1e3ccSAndroid Build Coastguard Worker row = {"file": "OVERALL" } 402*77c1e3ccSAndroid Build Coastguard Worker for directory in dirs: 403*77c1e3ccSAndroid Build Coastguard Worker row[directory] = sumoverall[directory] / countoverall[directory] 404*77c1e3ccSAndroid Build Coastguard Worker data.append(row) 405*77c1e3ccSAndroid Build Coastguard Worker 406*77c1e3ccSAndroid Build Coastguard Worker # write the tables out 407*77c1e3ccSAndroid Build Coastguard Worker data_table = gviz_api.DataTable(description) 408*77c1e3ccSAndroid Build Coastguard Worker data_table.LoadData(data) 409*77c1e3ccSAndroid Build Coastguard Worker 410*77c1e3ccSAndroid Build Coastguard Worker filestable[metric] = ( filestable[metric] + "filestable_" + metric + 411*77c1e3ccSAndroid Build Coastguard Worker "[" + str(column) + "]=" + 412*77c1e3ccSAndroid Build Coastguard Worker data_table.ToJSon(columns_order=["file"]+dirs) + "\n" ) 413*77c1e3ccSAndroid Build Coastguard Worker 414*77c1e3ccSAndroid Build Coastguard Worker filestable_avg = filestable['avg'] 415*77c1e3ccSAndroid Build Coastguard Worker filestable_dpsnr = filestable['dsnr'] 416*77c1e3ccSAndroid Build Coastguard Worker filestable_drate = filestable['drate'] 417*77c1e3ccSAndroid Build Coastguard Worker 418*77c1e3ccSAndroid Build Coastguard Worker # Now we collect all the data for all the graphs. First the column 419*77c1e3ccSAndroid Build Coastguard Worker # headers which will be Datarate and then each directory. 420*77c1e3ccSAndroid Build Coastguard Worker columns = ("datarate",baseline_dir) 421*77c1e3ccSAndroid Build Coastguard Worker description = {"datarate":("number", "Datarate")} 422*77c1e3ccSAndroid Build Coastguard Worker for directory in dirs: 423*77c1e3ccSAndroid Build Coastguard Worker description[directory] = ("number", directory) 424*77c1e3ccSAndroid Build Coastguard Worker 425*77c1e3ccSAndroid Build Coastguard Worker description[baseline_dir] = ("number", baseline_dir) 426*77c1e3ccSAndroid Build Coastguard Worker 427*77c1e3ccSAndroid Build Coastguard Worker snrs = snrs + "snrs[" + str(column) + "] = [" 428*77c1e3ccSAndroid Build Coastguard Worker 429*77c1e3ccSAndroid Build Coastguard Worker # Now collect the data for the graphs, file by file. 430*77c1e3ccSAndroid Build Coastguard Worker for filename in dir_list: 431*77c1e3ccSAndroid Build Coastguard Worker 432*77c1e3ccSAndroid Build Coastguard Worker data = [] 433*77c1e3ccSAndroid Build Coastguard Worker 434*77c1e3ccSAndroid Build Coastguard Worker # Collect the file in each directory and store all of its metrics 435*77c1e3ccSAndroid Build Coastguard Worker # in the associated gviz metrics table. 436*77c1e3ccSAndroid Build Coastguard Worker all_dirs = dirs + [baseline_dir] 437*77c1e3ccSAndroid Build Coastguard Worker for directory in all_dirs: 438*77c1e3ccSAndroid Build Coastguard Worker 439*77c1e3ccSAndroid Build Coastguard Worker metric_file_name = directory + "/" + filename 440*77c1e3ccSAndroid Build Coastguard Worker if not os.path.isfile(metric_file_name): 441*77c1e3ccSAndroid Build Coastguard Worker continue 442*77c1e3ccSAndroid Build Coastguard Worker 443*77c1e3ccSAndroid Build Coastguard Worker # Read and parse the metrics file storing it to the data we'll 444*77c1e3ccSAndroid Build Coastguard Worker # use for the gviz_api.Datatable. 445*77c1e3ccSAndroid Build Coastguard Worker metrics = ParseMetricFile(metric_file_name, column) 446*77c1e3ccSAndroid Build Coastguard Worker for bitrate, metric in metrics: 447*77c1e3ccSAndroid Build Coastguard Worker data.append({"datarate": bitrate, directory: metric}) 448*77c1e3ccSAndroid Build Coastguard Worker 449*77c1e3ccSAndroid Build Coastguard Worker data_table = gviz_api.DataTable(description) 450*77c1e3ccSAndroid Build Coastguard Worker data_table.LoadData(data) 451*77c1e3ccSAndroid Build Coastguard Worker snrs = snrs + "'" + data_table.ToJSon( 452*77c1e3ccSAndroid Build Coastguard Worker columns_order=tuple(["datarate",baseline_dir]+dirs)) + "'," 453*77c1e3ccSAndroid Build Coastguard Worker 454*77c1e3ccSAndroid Build Coastguard Worker snrs = snrs + "]\n" 455*77c1e3ccSAndroid Build Coastguard Worker 456*77c1e3ccSAndroid Build Coastguard Worker formatters = "" 457*77c1e3ccSAndroid Build Coastguard Worker for i in range(len(dirs)): 458*77c1e3ccSAndroid Build Coastguard Worker formatters = "%s formatter.format(better, %d);" % (formatters, i+1) 459*77c1e3ccSAndroid Build Coastguard Worker 460*77c1e3ccSAndroid Build Coastguard Worker print FillForm(page_template, vars()) 461*77c1e3ccSAndroid Build Coastguard Worker return 462*77c1e3ccSAndroid Build Coastguard Worker 463*77c1e3ccSAndroid Build Coastguard Workerif len(sys.argv) < 3: 464*77c1e3ccSAndroid Build Coastguard Worker print HandleFiles.__doc__ 465*77c1e3ccSAndroid Build Coastguard Workerelse: 466*77c1e3ccSAndroid Build Coastguard Worker HandleFiles(sys.argv) 467