blob: aa26d80e9c4559e3b335cbc4746384c2f32ba212 [file] [log] [blame]
#!/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.......")