|  | #!/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 os | 
|  | import xlsxwriter | 
|  | import xlrd | 
|  | import logging | 
|  | from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics | 
|  | from VideoScaler import GetDownScaledOutFile, DownScaling, UpScaling, GetUpScaledOutFile | 
|  | from Config import DnScaleRatio, FrameNum, Clips, QualityList, LoggerName, Path_ScalingResults, ScalQty_WtCols, \ | 
|  | ScalQty_startRow, LineColors, ScalSumQty_WtCols, ContentPath | 
|  | from Utils import GetVideoInfo, Cleanfolder, GetShortContentName, CreateChart_Scatter, AddSeriesToChart_Scatter, UpdateChart, \ | 
|  | InsertChartsToSheet, CalcRowsClassAndContentDict, GetContents, CreateChart_Line, AddSeriesToChart_Line | 
|  |  | 
|  | subloggername = "ScalingTest" | 
|  | loggername = LoggerName + '.' + '%s' % subloggername | 
|  | logger = logging.getLogger(loggername) | 
|  |  | 
|  | StatsMetrics = ['Max', 'Std+', 'Average', 'Std-', 'Min'] | 
|  |  | 
|  | def GetScalingResultExcelFile(rationum, scalAlgoNum): | 
|  | filename = "ScalingResults_RatioNum_%d_AlgoNum_%d.xlsx"\ | 
|  | % (rationum, scalAlgoNum) | 
|  | file = os.path.join(Path_ScalingResults, filename) | 
|  | return file | 
|  |  | 
|  | def GetScalingResultExcelFile_PerContent(content): | 
|  | filename = GetShortContentName(content) | 
|  | filename = "ScalingResults_%s.xlsx" % filename | 
|  | file = os.path.join(Path_ScalingResults, filename) | 
|  | return file | 
|  |  | 
|  | def Run_Scaling_Test(content, dnScalAlgo, upScalAlgo, path_dnscl, path_upscl, | 
|  | path_log, path_cfg, savememory, keepupscaledyuv): | 
|  | logger.info("start running scaling tests with content %s" | 
|  | % os.path.basename(content)) | 
|  | cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips) | 
|  | if totalnum < FrameNum: | 
|  | logger.error("content %s includes total %d frames < specified % frames!" | 
|  | % (content, totalnum, FrameNum)) | 
|  | return | 
|  |  | 
|  | DnScaledRes = [(int(width / ratio), int(height / ratio)) | 
|  | for ratio in DnScaleRatio] | 
|  | for i in range(len(DnScaledRes)): | 
|  | if savememory: | 
|  | Cleanfolder(path_cfg) | 
|  | if not keepupscaledyuv: | 
|  | Cleanfolder(path_upscl) | 
|  |  | 
|  | DnScaledW = DnScaledRes[i][0] | 
|  | DnScaledH = DnScaledRes[i][1] | 
|  | logger.info("start downscaling content to %dx%d" | 
|  | % (DnScaledW, DnScaledH)) | 
|  | # downscaling | 
|  | dnscalyuv = GetDownScaledOutFile(content, width, height, DnScaledW, | 
|  | DnScaledH, path_dnscl, dnScalAlgo) | 
|  | if not os.path.isfile(dnscalyuv): | 
|  | dnscalyuv = DownScaling(content, FrameNum, width, height, DnScaledW, | 
|  | DnScaledH, path_dnscl, dnScalAlgo) | 
|  |  | 
|  | upscaleyuv = UpScaling(dnscalyuv, FrameNum, DnScaledW, DnScaledH, width, | 
|  | height, path_upscl, upScalAlgo) | 
|  |  | 
|  | CalculateQualityMetric(content, FrameNum, upscaleyuv, width, height, | 
|  | path_log, path_cfg) | 
|  |  | 
|  | if savememory: | 
|  | Cleanfolder(path_dnscl) | 
|  |  | 
|  | logger.info("finish running scaling test.") | 
|  |  | 
|  | def GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content, scaleRatios, | 
|  | path_log): | 
|  | contshortname = GetShortContentName(content) | 
|  | logger.info("start generate excel file for content :%s" % contshortname) | 
|  | excFile = GetScalingResultExcelFile_PerContent(content) | 
|  | wb = xlsxwriter.Workbook(excFile) | 
|  | shtname = contshortname | 
|  | sht = wb.add_worksheet(shtname) | 
|  |  | 
|  | sht.write(1, 0, 'Content Name') | 
|  | sht.write(2, 0, contshortname) | 
|  | sht.write(1, 1, 'Scaling Ratio') | 
|  | sht.write_column(2, 1, scaleRatios) | 
|  | pre_title = ['Width', 'Height', 'DnScaledWidth', 'DnScaledHeight'] | 
|  | sht.write_row(1, 2, pre_title) | 
|  | for dn_algo, up_algo, col in zip(dnScalAlgos, upScalAlgos, ScalQty_WtCols): | 
|  | algos = dn_algo + '--' + up_algo | 
|  | sht.write(0, col, algos) | 
|  | sht.write_row(1, col, QualityList) | 
|  |  | 
|  | cls, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips) | 
|  | rows = [ScalQty_startRow + i for i in range(len(scaleRatios))] | 
|  | continfos = [] | 
|  | for ratio, row in zip(scaleRatios, rows): | 
|  | dw = int(w / ratio) | 
|  | dh = int(h / ratio) | 
|  | info = [w, h, dw, dh] | 
|  | sht.write_row(row, 2, info) | 
|  | continfos.append(info) | 
|  |  | 
|  | charts = []; y_mins = {}; y_maxs = {} | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | chart_title = 'Scaling Quality - %s' % qty | 
|  | xaxis_name = 'scaling ratio' | 
|  | chart = CreateChart_Scatter(wb, chart_title, xaxis_name, qty) | 
|  | charts.append(chart) | 
|  | y_mins[x] = []; y_maxs[x] = [] | 
|  |  | 
|  | for dn_algo, up_algo, col, i in zip(dnScalAlgos, upScalAlgos, ScalQty_WtCols, | 
|  | range(len(dnScalAlgos))): | 
|  | qualities = [] | 
|  | seriname = dn_algo + '--' + up_algo | 
|  | for ratio, row, idx in zip(scaleRatios, rows, range(len(scaleRatios))): | 
|  | w = continfos[idx][0] | 
|  | h = continfos[idx][1] | 
|  | dw = continfos[idx][2] | 
|  | dh = continfos[idx][3] | 
|  | dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh, | 
|  | path_log, dn_algo) | 
|  | upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h, | 
|  | path_log, up_algo) | 
|  | qtys = GatherQualityMetrics(upScalOut, path_log) | 
|  | sht.write_row(row, col, qtys) | 
|  | qualities.append(qtys) | 
|  | for x in range(len(QualityList)): | 
|  | AddSeriesToChart_Scatter(shtname, rows, col + x, 1, charts[x], | 
|  | seriname, LineColors[i]) | 
|  |  | 
|  | # get min and max of y-axis for a certain dn up scaling algo | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | qs = [row[x] for row in qualities] | 
|  | y_mins[x].append(min(qs)) | 
|  | y_maxs[x].append(max(qs)) | 
|  |  | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | ymin = min(y_mins[x]) | 
|  | ymax = max(y_maxs[x]) | 
|  | margin = 0.1  # add 10% on min and max value for y_axis range | 
|  | num_precsn = 5 if 'MS-SSIM' in qty else 3 | 
|  | UpdateChart(charts[x], ymin, ymax, margin, qty, num_precsn) | 
|  |  | 
|  | startrow = rows[-1] + 2; startcol = 1 | 
|  | InsertChartsToSheet(sht, startrow, startcol, charts) | 
|  |  | 
|  | wb.close() | 
|  | logger.info("finish export scaling quality results to excel file.") | 
|  |  | 
|  | def GenerateSummarySheet(wb, dnScalAlgos, upScalAlgos, ratio, path_log): | 
|  | logger.info("start generate summary sheet for ratio %2.2f" % ratio) | 
|  |  | 
|  | shts = [] | 
|  | shtname = "Ratio=%1.2f" % ratio | 
|  | sht = wb.add_worksheet(shtname) | 
|  | shts.append(sht) | 
|  | sht.write(1, 0, 'Content Class') | 
|  | sht.write(1, 1, 'Content NO.') | 
|  | sht.write(1, 2, 'Content Name') | 
|  | pre_title = ['Width', 'Height', 'DnScaledWidth', 'DnScaledHeight'] | 
|  | sht.write_row(1, 3, pre_title) | 
|  |  | 
|  | for dn_algo, up_algo, col in zip(dnScalAlgos, upScalAlgos, ScalSumQty_WtCols): | 
|  | algos = dn_algo + '--' + up_algo | 
|  | sht.write(0, col, algos) | 
|  | sht.write_row(1, col, QualityList) | 
|  |  | 
|  | content_infos = {}; totalnum_contents = 0 | 
|  | for (clss, contents), row_clss in zip(ContentsDict.items(), Rows_Class): | 
|  | sht.write(row_clss, 0, clss) | 
|  | totalnum_contents = totalnum_contents + len(contents) | 
|  | for content, row_cont in zip(contents, range(len(contents))): | 
|  | cl, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips) | 
|  | dw = int(w / ratio) | 
|  | dh = int(h / ratio) | 
|  | shortcntname = GetShortContentName(content) | 
|  | sht.write(row_clss + row_cont, 2, shortcntname) | 
|  | infos = [w, h, dw, dh] | 
|  | sht.write_row(row_clss + row_cont, 3, infos) | 
|  | content_infos[shortcntname] = infos | 
|  |  | 
|  | charts = []; y_mins = {}; y_maxs = {} | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | chart_title = '%s of %s' % (qty, shtname) | 
|  | chart = CreateChart_Scatter(wb, chart_title, 'Contents', qty) | 
|  | charts.append(chart) | 
|  | y_mins[x] = []; y_maxs[x] = [] | 
|  |  | 
|  | rows_all = [ScalQty_startRow + i for i in range(totalnum_contents)] | 
|  | sht.write_column(ScalQty_startRow, 1, range(totalnum_contents)) | 
|  | for dn_algo, up_algo, col, i in zip(dnScalAlgos, upScalAlgos, | 
|  | ScalSumQty_WtCols, | 
|  | range(len(dnScalAlgos))): | 
|  | qualities = [] | 
|  | seriname = dn_algo + '--' + up_algo | 
|  | for (clss, contents), row_clss in zip(ContentsDict.items(), Rows_Class): | 
|  | for content, row_cont in zip(contents, range(len(contents))): | 
|  | key = GetShortContentName(content) | 
|  | w = content_infos[key][0] | 
|  | h = content_infos[key][1] | 
|  | dw = content_infos[key][2] | 
|  | dh = content_infos[key][3] | 
|  | dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh, | 
|  | path_log, dn_algo) | 
|  | upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h, | 
|  | path_log, up_algo) | 
|  | qtys = GatherQualityMetrics(upScalOut, path_log) | 
|  | sht.write_row(row_clss + row_cont, col, qtys) | 
|  | qualities.append(qtys) | 
|  |  | 
|  | for x in range(len(QualityList)): | 
|  | AddSeriesToChart_Scatter(shtname, rows_all, col + x, 1, charts[x], | 
|  | seriname, LineColors[i]) | 
|  |  | 
|  | # get min and max of y-axis for a certain dn up scaling algo | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | qs = [row[x] for row in qualities] | 
|  | y_mins[x].append(min(qs)) | 
|  | y_maxs[x].append(max(qs)) | 
|  |  | 
|  | for qty, x in zip(QualityList, range(len(QualityList))): | 
|  | ymin = min(y_mins[x]) | 
|  | ymax = max(y_maxs[x]) | 
|  | margin = 0.1  # add 10% on min and max value for y_axis range | 
|  | num_precsn = 5 if 'MS-SSIM' in qty else 3 | 
|  | UpdateChart(charts[x], ymin, ymax, margin, qty, num_precsn) | 
|  |  | 
|  | startrow = rows_all[-1] + 2; startcol = 1 | 
|  | InsertChartsToSheet(sht, startrow, startcol, charts) | 
|  | logger.info("finish average sheet for ratio:%2.2f." % ratio) | 
|  |  | 
|  | return sht | 
|  |  | 
|  | def GenerateAverageSheet(wb, sumsht, dnScalAlgos, upScalAlgos, ratio): | 
|  | logger.info("start generate average sheet for ratio %2.2f" % ratio) | 
|  |  | 
|  | rdsht = sumsht | 
|  | rdshtname = rdsht.get_name() | 
|  | shtname = "AverageForRatio=%1.2f" % ratio | 
|  | sht = wb.add_worksheet(shtname) | 
|  | sht.write(1, 0, 'QualityMetrics') | 
|  | sht.write(1, 1, 'Content Class') | 
|  | sht.write(1, 2, 'Content Number') | 
|  |  | 
|  | interval = 1 | 
|  | step = len(StatsMetrics) + interval | 
|  | startcol = 3 | 
|  | cols_avg = [startcol + step * i for i in range(len(dnScalAlgos))] | 
|  | for col, dn_algo, up_algo in zip(cols_avg, dnScalAlgos, upScalAlgos): | 
|  | algos = dn_algo + '--' + up_algo | 
|  | sht.write(0, col, algos) | 
|  | sht.write_row(1, col, StatsMetrics) | 
|  |  | 
|  | step = len(ContentsDict) + 1  # 1 extra row for total of each class | 
|  | startrow = 2 | 
|  | rows_qtymtr = [startrow + step * i for i in range(len(QualityList))] | 
|  | for qty, row_qm, y in zip(QualityList, rows_qtymtr, range(len(QualityList))): | 
|  | sht.write(row_qm, 0, qty) | 
|  |  | 
|  | #charts = [] | 
|  | #titlename = 'Quality Statistics %s' % qty | 
|  | #chart = CreateChart_Line(wb, titlename, qty) | 
|  | #charts.append(chart) | 
|  |  | 
|  | totalnum_contents = 0 | 
|  | for (cls, contents), idx, rdrow_cls in zip(ContentsDict.items(), | 
|  | range(len(ContentsDict)), | 
|  | Rows_Class): | 
|  | sht.write(row_qm + idx, 1, cls) | 
|  | num_content = len(contents) | 
|  | totalnum_contents = totalnum_contents + num_content | 
|  | sht.write(row_qm + idx, 2, num_content) | 
|  | for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg): | 
|  | startcell = xlrd.cellname(rdrow_cls, rdcol + y) | 
|  | endcell = xlrd.cellname(rdrow_cls + num_content - 1, rdcol + y) | 
|  | formula = '=MAX(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell) | 
|  | sht.write(row_qm + idx, wtcol, formula) | 
|  |  | 
|  | formula = '=SUM(\'%s\'!%s:%s)/%d' % (rdshtname, startcell, | 
|  | endcell, num_content) | 
|  | sht.write(row_qm + idx, wtcol + 2, formula) | 
|  |  | 
|  | formula = '=MIN(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell) | 
|  | sht.write(row_qm + idx, wtcol + 4, formula) | 
|  |  | 
|  | avgcell = xlrd.cellname(row_qm + idx, wtcol + 2) | 
|  | formula = '= %s + _xlfn.STDEV.P(\'%s\'!%s:%s)'\ | 
|  | % (avgcell, rdshtname, startcell, endcell) | 
|  | sht.write(row_qm + idx, wtcol + 1, formula) | 
|  |  | 
|  | formula = '= %s - _xlfn.STDEV.P(\'%s\'!%s:%s)'\ | 
|  | % (avgcell, rdshtname, startcell, endcell) | 
|  | sht.write(row_qm + idx, wtcol + 3, formula) | 
|  |  | 
|  | #write total contents statistics | 
|  | wtrow = row_qm + len(ContentsDict) | 
|  | sht.write(wtrow, 1, 'Total') | 
|  | sht.write(wtrow, 2, totalnum_contents) | 
|  | for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg): | 
|  | startcell = xlrd.cellname(ScalQty_startRow, rdcol + y) | 
|  | endcell = xlrd.cellname(ScalQty_startRow + totalnum_contents - 1, | 
|  | rdcol + y) | 
|  | formula = '=MAX(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell) | 
|  | sht.write(wtrow, wtcol, formula) | 
|  |  | 
|  | formula = '=SUM(\'%s\'!%s:%s)/%d'\ | 
|  | % (rdshtname, startcell, endcell, totalnum_contents) | 
|  | sht.write(wtrow, wtcol + 2, formula) | 
|  |  | 
|  | formula = '=MIN(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell) | 
|  | sht.write(wtrow, wtcol + 4, formula) | 
|  |  | 
|  | avgcell = xlrd.cellname(wtrow, wtcol + 2) | 
|  | formula = '= %s + _xlfn.STDEV.P(\'%s\'!%s:%s)'\ | 
|  | % (avgcell, rdshtname, startcell, endcell) | 
|  | sht.write(wtrow, wtcol + 1, formula) | 
|  |  | 
|  | formula = '= %s - _xlfn.STDEV.P(\'%s\'!%s:%s)'\ | 
|  | % (avgcell, rdshtname, startcell, endcell) | 
|  | sht.write(wtrow, wtcol + 3, formula) | 
|  |  | 
|  | logger.info("finish average sheet for ratio:%2.2f." % ratio) | 
|  |  | 
|  |  | 
|  | def SaveScalingResultsToExcel(dnScalAlgos, upScalAlgos, path_log): | 
|  | logger.info("start saving scaling quality results to excel files.......") | 
|  | if not os.path.exists(Path_ScalingResults): | 
|  | os.makedirs(Path_ScalingResults) | 
|  |  | 
|  | logger.info("start generating per content scaling quality excel file.......") | 
|  | contents = GetContents(ContentPath, Clips) | 
|  | for content in contents: | 
|  | GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content, | 
|  | DnScaleRatio, path_log) | 
|  |  | 
|  | scaleRatios = DnScaleRatio | 
|  | scaleRatios.remove(1.0) | 
|  | logger.info("start generating scaling quality summary excel file.......") | 
|  | sumexcFile = GetScalingResultExcelFile(len(scaleRatios), len(DnScaleRatio)) | 
|  | wb = xlsxwriter.Workbook(sumexcFile) | 
|  | # to generate rows number of starting of each class: Rows_Class | 
|  | global ContentsDict, Rows_Class | 
|  | ContentsDict, Rows_Class = CalcRowsClassAndContentDict(ScalQty_startRow, | 
|  | ContentPath, Clips) | 
|  |  | 
|  | sumShts = [] | 
|  | for ratio in scaleRatios: | 
|  | sht = GenerateSummarySheet(wb, dnScalAlgos, upScalAlgos, ratio, path_log) | 
|  | sumShts.append(sht) | 
|  | for ratio, sumsht in zip(scaleRatios, sumShts): | 
|  | GenerateAverageSheet(wb, sumsht, dnScalAlgos, upScalAlgos, ratio) | 
|  |  | 
|  | wb.close() | 
|  | logger.info("finish saving scaling quality results to excel files.......") |