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