| #!/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() |