Add BDRate calculation with Python implementation. A new flag in config file controls which implementation want to be used: python or macro in excel file. Change-Id: Iea1114003f0c876cce275d49dd0d1d22871b1a54
diff --git a/tools/convexhull_framework/src/CalcBDRate.py b/tools/convexhull_framework/src/CalcBDRate.py new file mode 100644 index 0000000..a7557cb --- /dev/null +++ b/tools/convexhull_framework/src/CalcBDRate.py
@@ -0,0 +1,63 @@ +#!/usr/bin/env python +## Copyright (c) 2019, Alliance for Open Media. All rights reserved +## +## This source code is subject to the terms of the BSD 2 Clause License and +## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License +## was not distributed with this source code in the LICENSE file, you can +## obtain it at www.aomedia.org/license/software. If the Alliance for Open +## Media Patent License 1.0 was not distributed with this source code in the +## PATENTS file, you can obtain it at www.aomedia.org/license/patent. +## +__author__ = "maggie.sun@intel.com, ryan.lei@intel.com" + +import numpy +import math +import scipy.interpolate +import logging +from Config import LoggerName + +subloggername = "CalcBDRate" +loggername = LoggerName + '.' + '%s' % subloggername +logger = logging.getLogger(loggername) + +# BJONTEGAARD Bjontegaard metric +# Calculation is adapted from Google implementation +# PCHIP method - Piecewise Cubic Hermite Interpolating Polynomial interpolation +def BD_RATE(br1, qtyMtrc1, br2, qtyMtrc2): + brqtypairs1 = [(br1[i], qtyMtrc1[i]) for i in range(min(len(qtyMtrc1), len(br1)))] + brqtypairs2 = [(br2[i], qtyMtrc2[i]) for i in range(min(len(qtyMtrc2), len(br2)))] + + if not brqtypairs1 or not brqtypairs2: + logger.info("one of input lists is empty!") + return 0.0 + + brqtypairs1.sort(key=lambda tup: tup[1]) + brqtypairs2.sort(key=lambda tup: tup[1]) + + logbr1 = [math.log(x[0]) for x in brqtypairs1] + qmetrics1 = [100.0 if x[1] == float('inf') else x[1] for x in brqtypairs1] + logbr2 = [math.log(x[0]) for x in brqtypairs2] + qmetrics2 = [100.0 if x[1] == float('inf') else x[1] for x in brqtypairs2] + + min_int = max(min(qmetrics1), min(qmetrics2)) + max_int = min(max(qmetrics1), max(qmetrics2)) + if min_int >= max_int: + logger.info("no overlap from input 2 lists of quality metrics!") + return 0.0 + + lin = numpy.linspace(min_int, max_int, num=100, retstep=True) + interval = lin[1] + samples = lin[0] + + v1 = scipy.interpolate.pchip_interpolate(qmetrics1, logbr1, samples) + v2 = scipy.interpolate.pchip_interpolate(qmetrics2, logbr2, samples) + + # Calculate the integral using the trapezoid method on the samples. + int1 = numpy.trapz(v1, dx=interval) + int2 = numpy.trapz(v2, dx=interval) + + # find avg diff + avg_exp_diff = (int2 - int1) / (max_int - min_int) + avg_diff = (math.exp(avg_exp_diff) - 1) * 100 + + return avg_diff
diff --git a/tools/convexhull_framework/src/Config.py b/tools/convexhull_framework/src/Config.py index 5a6ebb0..027e596 100644 --- a/tools/convexhull_framework/src/Config.py +++ b/tools/convexhull_framework/src/Config.py
@@ -115,6 +115,7 @@ 'HDRTools'] VMAF = os.path.join(BinPath, 'vmafossexec.exe') HDRTool = os.path.join(BinPath, 'HDRMetrics.exe') +CalcBDRateInExcel = False ######################## config for exporting data to excel ################# #https://xlsxwriter.readthedocs.io/working_with_colors.html#colors
diff --git a/tools/convexhull_framework/src/ConvexHullTest.py b/tools/convexhull_framework/src/ConvexHullTest.py index 327249b..27f65bc 100644 --- a/tools/convexhull_framework/src/ConvexHullTest.py +++ b/tools/convexhull_framework/src/ConvexHullTest.py
@@ -356,7 +356,9 @@ # main ###################################### if __name__ == "__main__": + #sys.argv = ["", "-f", "encode", "-c", "hevc", "-m", "ffmpeg", "-p", "medium"] #sys.argv = ["","-f","convexhull","-c","hevc","-m","ffmpeg","-p","medium"] + #sys.argv = ["", "-f", "summary", "-c", "hevc", "-m", "ffmpeg", "-p", "medium"] ParseArguments(sys.argv) # preparation for executing functions
diff --git a/tools/convexhull_framework/src/PostAnalysis_Summary.py b/tools/convexhull_framework/src/PostAnalysis_Summary.py index 1d711e9..5478a16 100644 --- a/tools/convexhull_framework/src/PostAnalysis_Summary.py +++ b/tools/convexhull_framework/src/PostAnalysis_Summary.py
@@ -14,9 +14,10 @@ import xlsxwriter import xlrd from Config import QPs, DnScaleRatio, QualityList, VbaBinFile, CvxH_WtRows,\ - CvxH_WtLastCol, LoggerName + CvxH_WtLastCol, LoggerName, CalcBDRateInExcel, CvxH_WtCols from Utils import GetShortContentName, CalcRowsClassAndContentDict,\ SweepScalingAlgosInOneResultFile +from CalcBDRate import BD_RATE import logging subloggername = "PostAnalysisSummary" @@ -30,8 +31,9 @@ ################################################################################ ### Helper Functions ########################################################### def GetSummaryFileName(encMethod, codecName, preset, path): - name = 'ConvexHullSummary_ScaleAlgosNum_%d_%s_%s_%s.xlsm'\ - % (len(DnScaleRatio), encMethod, codecName, preset) + filetype = 'xlsm' if CalcBDRateInExcel else 'xlsx' + name = 'ConvexHullSummary_ScaleAlgosNum_%d_%s_%s_%s.%s'\ + % (len(DnScaleRatio), encMethod, codecName, preset, filetype) return os.path.join(path, name) def GetConvexHullRDFileName(encMethod, codecName, preset, path): @@ -77,7 +79,7 @@ logger.warning("not find convex hull result file for content:%s" % content) -def CalBDRate_OneSheet(sht, cols, contentsdict, rows_class, cols_bdmtrs, cellformat): +def CalBDRateWithExcel_OneSheet(sht, cols, contentsdict, rows_class, cols_bdmtrs, cellformat): row_refst = 0 bdstep = 3 for cols_bd, residx in zip(cols_bdmtrs, range(1, len(DnScaleRatio))): @@ -97,16 +99,16 @@ refq_e = xlrd.cellnameabs(row_class + row_cont + row_refst + bdstep, cols[0] + 1 + y) - testbr_b = xlrd.cellnameabs(row_class + row_cont, + testbr_b = xlrd.cellnameabs(row_class + row_cont + row_refst, cols[residx]) - testbr_e = xlrd.cellnameabs(row_class + row_cont + bdstep, - cols[residx]) - testq_b = xlrd.cellnameabs(row_class + row_cont, + testbr_e = xlrd.cellnameabs(row_class + row_cont + row_refst + + bdstep, cols[residx]) + testq_b = xlrd.cellnameabs(row_class + row_cont + row_refst, cols[residx] + 1 + y) - testq_e = xlrd.cellnameabs(row_class + row_cont + bdstep, - cols[residx] + 1 + y) + testq_e = xlrd.cellnameabs(row_class + row_cont + row_refst + + bdstep, cols[residx] + 1 + y) - #formula = '=-bdrate(%s:%s,%s:%s,%s:%s,%s:%s)' % ( + #formula = '=bdrate(%s:%s,%s:%s,%s:%s,%s:%s)' % ( #refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e, # testq_b, testq_e) formula = '=bdRateExtend(%s:%s,%s:%s,%s:%s,%s:%s)'\ @@ -115,6 +117,36 @@ sht.write_formula(row_class + row_cont, cols_bd + y, formula, cellformat) + +def CalBDRateWithPython_OneSheet(sht, contentsdict, rows_class, cols_bdmtrs, infile_path, cellformat): + row_refst = 0 + bdstep = 3 + assert row_refst + bdstep < len(CvxH_WtRows) + resultfiles = os.listdir(infile_path) + shtname = sht.get_name() + rdrows = CvxH_WtRows + rdcols = CvxH_WtCols + for cols_bd, residx in zip(cols_bdmtrs, range(1, len(DnScaleRatio))): + sht.write(0, cols_bd, 'BD-Rate %.2f vs. %.2f' % (DnScaleRatio[residx], + DnScaleRatio[0])) + sht.write_row(1, cols_bd, QualityList) + for (cls, contents), row_class in zip(contentsdict.items(), rows_class): + rows_content = [i * len(QPs) for i in range(len(contents))] + for row_cont, content in zip(rows_content, contents): + key = GetShortContentName(content) + for resfile in resultfiles: + if key in resfile: + rdwb = xlrd.open_workbook(os.path.join(infile_path, resfile)) + rdsht = rdwb.sheet_by_name(shtname) + break + for y in range(len(QualityList)): + refbrs = rdsht.col_values(rdcols[0], rdrows[row_refst], rdrows[row_refst + bdstep] + 1) + refqtys = rdsht.col_values(rdcols[0] + 1 + y, rdrows[row_refst], rdrows[row_refst + bdstep] + 1) + testbrs = rdsht.col_values(rdcols[residx], rdrows[row_refst], rdrows[row_refst + bdstep] + 1) + testqtys = rdsht.col_values(rdcols[residx] + 1 + y, rdrows[row_refst], rdrows[row_refst + bdstep] + 1) + bdrate = BD_RATE(refbrs, refqtys, testbrs, testqtys) / 100.0 + sht.write(row_class + row_cont, cols_bd + y, bdrate, cellformat) + def GenerateFormula_SumRows(shtname, rows, col): cells = '' for row in rows: @@ -334,8 +366,8 @@ contentsdict, rows_class = CalcRowsClassAndContentDict(rowstart, content_path, clips, len(QPs)) - - wb.add_vba_project(VbaBinFile) + if CalcBDRateInExcel: + wb.add_vba_project(VbaBinFile) cellformat = wb.add_format() cellformat.set_num_format('0.00%') #cols_bdmtrs is the column number to write the bdrate data @@ -348,8 +380,12 @@ CopyResultDataToSummaryFile_Onesheet(sht, sum_wtcols, contentsdict, rows_class, infile_path) # calculate bd rate in each scaling sheet - CalBDRate_OneSheet(sht, sum_wtcols, contentsdict, rows_class, - cols_bdmtrs, cellformat) + if CalcBDRateInExcel: + CalBDRateWithExcel_OneSheet(sht, sum_wtcols, contentsdict, rows_class, + cols_bdmtrs, cellformat) + else: + CalBDRateWithPython_OneSheet(sht, contentsdict, rows_class, cols_bdmtrs, + infile_path, cellformat) # calculate average bitrate and quality metrics for each category and # write to "average" sheet