| #!/usr/bin/env python |
| ## Copyright (c) 2021, Alliance for Open Media. All rights reserved |
| ## |
| ## This source code is subject to the terms of the BSD 3-Clause Clear License and the |
| ## Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear License was |
| ## not distributed with this source code in the LICENSE file, you can obtain it |
| ## at aomedia.org/license/software-license/bsd-3-c-c/. 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 aomedia.org/license/patent-license/. |
| ## |
| __author__ = "maggie.sun@intel.com, ryanlei@fb.com" |
| |
| import sys |
| import xlsxwriter |
| import xlrd |
| import argparse |
| from Config import VbaBinFile, QualityList, CalcBDRateInExcel, \ |
| EnablePreInterpolation |
| from CalcBDRate import BD_RATE |
| |
| xlrd.xlsx.ensure_elementtree_imported(False, None) |
| xlrd.xlsx.Element_has_iter = True |
| |
| class ConvexHullData: |
| ContentName = "" |
| ContentClass = "" |
| NumRDPoints = 0 |
| RDPoints = {} |
| |
| def __init__(self, Name="", Class="", num=0): |
| self.ContentName = Name |
| self.ContentClass = Class |
| self.NumRDPoints = num |
| self.RDPoints = {} |
| |
| |
| def ParseArguments(raw_args): |
| parser = argparse.ArgumentParser(prog='ConvexHullBDRate.py', |
| usage='%(prog)s [options]', description='') |
| parser.add_argument('-i1', '--input1', dest='Input1', type=str, |
| required=True, metavar='', |
| help="convex hull summary excel file for base mode") |
| parser.add_argument('-i2', '--input2', dest='Input2', type=str, |
| required=True, metavar='', |
| help="convex hull summary excel file for target mode") |
| parser.add_argument('-o', '--output', dest='Output', type=str, |
| required=True, metavar='', |
| help="output excel file with BDRATE for base and target" |
| " modes") |
| if len(raw_args) == 1: |
| parser.print_help() |
| sys.exit(1) |
| args = parser.parse_args(raw_args[1:]) |
| |
| global InputBase, InputTarget, Output |
| InputBase = args.Input1 |
| InputTarget = args.Input2 |
| Output = args.Output |
| |
| |
| def read_cell_as_str(sht, row, col): |
| cell_val = sht.cell(row, col).value |
| if cell_val == '': |
| return '' |
| else: |
| return str(cell_val) |
| |
| |
| def read_cell_as_float(sht, row, col): |
| cell_val = sht.cell(row, col).value |
| if cell_val == '': |
| return '' |
| else: |
| return float(cell_val) |
| |
| |
| def read_cell_as_int(sht, row, col): |
| cell_val = sht.cell(row, col).value |
| if cell_val == '': |
| return '' |
| else: |
| return int(cell_val) |
| |
| |
| def ParseConvexHullRD(xls, EnablePreInterpolation = False): |
| wb = xlrd.open_workbook(xls) |
| shts = wb.sheet_names() #list of sheet names |
| data = {} #dict of data, key is the sheet name |
| |
| cvx_cols = 4 |
| if EnablePreInterpolation: |
| cvx_cols = 6 |
| |
| cols = [3 + i * cvx_cols for i in range(len(QualityList))] |
| for sht_name in shts: |
| sht = wb.sheet_by_name(sht_name) |
| #skip the title row |
| rows = sht.nrows |
| start_row = 1 |
| while start_row < rows: |
| row = start_row |
| cls = read_cell_as_str(sht, row, 0) |
| name = read_cell_as_str(sht, row, 1) |
| num = read_cell_as_int(sht, row, 2) |
| if cls == '' or name == '' or num == '': |
| print("Error: read empty cells") |
| exit() |
| |
| point = ConvexHullData(name, cls, num) |
| rd_data = {} |
| for row in range(num): |
| for qty, col in zip(QualityList, cols): |
| res = read_cell_as_str(sht, start_row+row, col) #Resolution |
| qp = read_cell_as_int(sht, start_row+row, col + 1) #QP |
| br = read_cell_as_float(sht, start_row+row, col + 2) #Bitrate |
| q = read_cell_as_float(sht, start_row+row, col + 3) #Quality |
| if EnablePreInterpolation: |
| int_br = read_cell_as_float(sht, start_row+row, col + 4) #Int_Bitrate |
| int_q = read_cell_as_float(sht, start_row+row, col + 5) # Int_Quality |
| if int_br != '' and int_q != '': |
| if qty in rd_data: |
| rd_data[qty].append((br, q, int_br, int_q)) |
| else: |
| rd_data.update({qty: [(br, q, int_br, int_q)]}) |
| else: |
| if br != '' and q != '': |
| if qty in rd_data: |
| rd_data[qty].append((br, q)) |
| else: |
| rd_data.update({qty: [(br, q)]}) |
| |
| start_row += num |
| point.RDPoints = rd_data |
| if sht_name in data: |
| data[sht_name].append(point) |
| else: |
| data.update({sht_name: [point]}) |
| |
| return shts, data |
| |
| |
| def WriteOutputHeaderRow(sht, EnablePreInterpolation = False): |
| sht.write(0, 0, 'Content Class') |
| sht.write(0, 1, 'Content Name') |
| sht.write(0, 2, 'Num RD Points') |
| col = 3 |
| for qty in QualityList: |
| sht.write(0, col, 'Bitrate(kbps)') |
| sht.write(0, col + 1, qty) |
| if EnablePreInterpolation: |
| sht.write(0, col + 2, 'Int_Bitrate(kbps)') |
| sht.write(0, col + 3, 'Int_' + qty) |
| col += 4 |
| else: |
| col += 2 |
| |
| col += 1 |
| for qty in QualityList: |
| sht.write(0, col, 'Bitrate(kbps)') |
| sht.write(0, col + 1, qty) |
| if EnablePreInterpolation: |
| sht.write(0, col + 2, 'Int_Bitrate(kbps)') |
| sht.write(0, col + 3, 'Int_' + qty) |
| col += 4 |
| else: |
| col += 2 |
| col += 1 |
| for (idx, qty) in zip(range(len(QualityList)), QualityList): |
| sht.write(0, col + idx, "BDRATE-%s" % qty) |
| |
| |
| def WriteRDData(sht, rd_data, start_row, start_col, format, |
| EnablePreInterpolation = False): |
| col = start_col |
| max_rows = 0 |
| for qty in QualityList: |
| row = start_row |
| for (line, point) in zip(range(len(rd_data.RDPoints[qty])), |
| rd_data.RDPoints[qty]): |
| if point[0] != '': |
| sht.write_number(row + line, col, point[0], format) #Bitrate |
| if point[1] != '': |
| sht.write_number(row + line, col + 1, point[1], format) #Quality |
| if EnablePreInterpolation: |
| if point[2] != '': |
| sht.write_number(row + line, col + 2, point[2], format) # Int_Bitrate |
| if point[3] != '': |
| sht.write_number(row + line, col + 3, point[3], format) # Int_Quality |
| if EnablePreInterpolation: |
| col += 4 |
| else: |
| col += 2 |
| max_rows = max(max_rows, len(rd_data.RDPoints[qty])) |
| return max_rows |
| |
| |
| def WriteRDRecord(sht, base_data, target_data, start_row, bdrate_fmt, float_fmt, |
| EnablePreInterpolation = False): |
| sht.write(start_row, 0, base_data.ContentClass) |
| sht.write(start_row, 1, base_data.ContentName) |
| |
| #write base data |
| base_start_col = 3 |
| cvx_cols = 2 |
| if EnablePreInterpolation: |
| cvx_cols = 4 |
| |
| base_max_rows = WriteRDData(sht, base_data, start_row, base_start_col, |
| float_fmt, EnablePreInterpolation) |
| |
| #write target data |
| target_start_col = base_start_col + cvx_cols * len(QualityList) + 1 |
| target_max_rows = WriteRDData(sht, target_data, start_row, target_start_col, |
| float_fmt, EnablePreInterpolation) |
| |
| #write bdrate formula |
| bdrate_start_col = target_start_col + cvx_cols * len(QualityList) + 1 |
| total_rows = max(base_max_rows, target_max_rows) |
| sht.write(start_row, 2, total_rows) |
| for (qty, col) in zip(QualityList, range(len(QualityList))): |
| if CalcBDRateInExcel: |
| refbr_b = xlrd.cellnameabs(start_row, |
| base_start_col + col * cvx_cols) |
| refbr_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| base_start_col + col * cvx_cols) |
| refq_b = xlrd.cellnameabs(start_row, |
| base_start_col + col * cvx_cols + 1) |
| refq_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| base_start_col + col * cvx_cols + 1) |
| |
| testbr_b = xlrd.cellnameabs(start_row, |
| target_start_col + col * cvx_cols) |
| testbr_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| target_start_col + col * cvx_cols) |
| testq_b = xlrd.cellnameabs(start_row, |
| target_start_col + col * cvx_cols + 1) |
| testq_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| target_start_col + col * cvx_cols + 1) |
| |
| # 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)'\ |
| % (refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e, testq_b, testq_e) |
| sht.write_formula(start_row, bdrate_start_col + col, formula, bdrate_fmt) |
| |
| if EnablePreInterpolation: |
| refbr_b = xlrd.cellnameabs(start_row, |
| base_start_col + col * cvx_cols + 2) |
| refbr_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| base_start_col + col * cvx_cols + 2) |
| refq_b = xlrd.cellnameabs(start_row, |
| base_start_col + col * cvx_cols + 3) |
| refq_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| base_start_col + col * cvx_cols + 3) |
| |
| testbr_b = xlrd.cellnameabs(start_row, |
| target_start_col + col * cvx_cols + 2) |
| testbr_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| target_start_col + col * cvx_cols + 2) |
| testq_b = xlrd.cellnameabs(start_row, |
| target_start_col + col * cvx_cols + 3) |
| testq_e = xlrd.cellnameabs(start_row + total_rows - 1, |
| target_start_col + col * cvx_cols + 3) |
| formula = '=bdRateExtend(%s:%s,%s:%s,%s:%s,%s:%s)' \ |
| % (refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e, testq_b, |
| testq_e) |
| |
| sht.write_formula(start_row + 1, bdrate_start_col + col, formula, bdrate_fmt) |
| else: |
| refbrs = [base_data.RDPoints[qty][i][0] for i in range(len(base_data.RDPoints[qty]))] |
| refqtys = [base_data.RDPoints[qty][i][1] for i in range(len(base_data.RDPoints[qty]))] |
| testbrs = [target_data.RDPoints[qty][i][0] for i in range(len(target_data.RDPoints[qty]))] |
| testqtys = [target_data.RDPoints[qty][i][1] for i in range(len(target_data.RDPoints[qty]))] |
| (err, bdrate) = BD_RATE(qty, refbrs, refqtys, testbrs, testqtys) |
| if (err != -1): |
| bdrate /= 100.0 |
| sht.write_number(start_row, bdrate_start_col + col, bdrate, bdrate_fmt) |
| else: |
| sht.write(start_row, bdrate_start_col + col, bdrate) |
| if EnablePreInterpolation: |
| refbrs = [base_data.RDPoints[qty][i][2] for i in range(len(base_data.RDPoints[qty]))] |
| refqtys = [base_data.RDPoints[qty][i][3] for i in range(len(base_data.RDPoints[qty]))] |
| testbrs = [target_data.RDPoints[qty][i][2] for i in range(len(target_data.RDPoints[qty]))] |
| testqtys = [target_data.RDPoints[qty][i][3] for i in range(len(target_data.RDPoints[qty]))] |
| (err, bdrate) = BD_RATE(qty, refbrs, refqtys, testbrs, testqtys) |
| if (err != -1): |
| bdrate /= 100.0 |
| sht.write_number(start_row + 1, bdrate_start_col + col, bdrate, bdrate_fmt) |
| else: |
| sht.write(start_row + 1, bdrate_start_col + col, bdrate) |
| return total_rows |
| |
| |
| def FindContent(name, rd_data): |
| for data in rd_data: |
| if name == data.ContentName: |
| return data |
| return '' |
| |
| ###################################### |
| # main |
| ###################################### |
| if __name__ == "__main__": |
| #sys.argv = ["", |
| # "-i1", |
| # "D:\\AOM\\AOM-Research-Latest\\aom\\tools\\convexhull_framework\\analysis-v1.0.0\\summary\\ConvexHullData_ScaleAlgosNum_6_aom_av2_0.xlsx", |
| # "-i2", |
| # "D:\\AOM\\AOM-Research-Latest\\aom\\tools\\convexhull_framework\\analysis-ext-quant\\summary\\ConvexHullData_ScaleAlgosNum_6_aom_av2_0.xlsx", |
| # "-o", |
| # "ConvexHullBDRate.xlsm"] |
| ParseArguments(sys.argv) |
| |
| base_shts, base_rd_data = ParseConvexHullRD(InputBase, EnablePreInterpolation) |
| target_shts, target_rd_data = ParseConvexHullRD(InputTarget, EnablePreInterpolation) |
| |
| output_wb = xlsxwriter.Workbook(Output) |
| # vba file needed when to calculate bdrate |
| output_wb.add_vba_project(VbaBinFile) |
| bdrate_fmt = output_wb.add_format() |
| bdrate_fmt.set_num_format('0.00%') |
| float_fmt = output_wb.add_format() |
| float_fmt.set_num_format('0.00') |
| |
| for sht_name in base_shts: |
| if sht_name in target_shts: |
| sht = output_wb.add_worksheet(sht_name) |
| WriteOutputHeaderRow(sht, EnablePreInterpolation) |
| start_row = 1 |
| for base_data in base_rd_data[sht_name]: |
| ContentName = base_data.ContentName |
| target_data = FindContent(ContentName, target_rd_data[sht_name]) |
| if target_data != '': |
| total_rows = WriteRDRecord(sht, base_data, target_data, |
| start_row, bdrate_fmt, float_fmt, |
| EnablePreInterpolation) |
| start_row += total_rows |
| |
| output_wb.close() |