blob: 94721d8fbfd56f68f38e8cba07af45c384b7f523 [file] [log] [blame]
#!/usr/bin/env python3
# SPDX-License-Identifier: Apache-2.0
# -----------------------------------------------------------------------------
# Copyright 2020-2021 Arm Limited
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy
# of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# -----------------------------------------------------------------------------
"""
The ``astc_test_result_report.py`` script consolidates all current sets of
reference results into a single report giving PSNR diffs (absolute) and
performance diffs (relative speedup, 1 = no change).
"""
import re
import os
import sys
import testlib.resultset as trs
from collections import defaultdict as ddict
CONFIG_FILTER = [
re.compile(r"^.*1\.7.*$"),
re.compile(r"^.*sse.*$")
]
TESTSET_FILTER = [
re.compile(r"^Small$"),
re.compile(r"^Frymire$"),
]
QUALITY_FILTER = [
]
BLOCKSIZE_FILTER = [
re.compile(r"^12x12$")
]
def find_reference_results():
"""
Scrape the Test/Images directory for result CSV files and return an
mapping of the result sets.
Returns:
Returns a three deep tree of dictionaries, with the final dict
pointing at a `ResultSet` object. The hierarchy is:
imageSet => quality => encoder => result
"""
scriptDir = os.path.dirname(__file__)
imageDir = os.path.join(scriptDir, "Images")
# Pattern for extracting useful data from the CSV file name
filePat = re.compile(r"astc_reference-(.+)_(.+)_results\.csv")
# Build a three level dictionary we can write into
results = ddict(lambda: ddict(lambda: ddict()))
# Final all CSVs, load them and store them in the dict tree
for root, dirs, files in os.walk(imageDir):
for name in files:
match = filePat.match(name)
if match:
# Skip results set in the filter
skip = [1 for filt in CONFIG_FILTER if filt.match(name)]
if skip:
continue
fullPath = os.path.join(root, name)
encoder = match.group(1)
quality = match.group(2)
imageSet = os.path.basename(root)
# Skip results set in the filter
skip = [1 for filt in TESTSET_FILTER if filt.match(imageSet)]
if skip:
continue
# Skip results set in the filter
skip = [1 for filt in QUALITY_FILTER if filt.match(quality)]
if skip:
continue
testRef = trs.ResultSet(imageSet)
testRef.load_from_file(fullPath)
patchedRef = trs.ResultSet(imageSet)
for result in testRef.records:
skip = [1 for filt in BLOCKSIZE_FILTER if filt.match(result.blkSz)]
if not skip:
patchedRef.add_record(result)
results[imageSet][quality]["ref-%s" % encoder] = patchedRef
return results
class DeltaRecord():
"""
Record a single image result from N different encoders.
Attributes:
imageSet: The image set this cme from.
quality: The compressor quality used.
encoders: The names of the encoders used. The first encoder in this
list will be used as the reference result.
records: The raw records for the encoders. The order of records in this
list matches the order of the `encoders` list.
"""
def __init__(self, imageSet, quality, encoders, records):
self.imageSet = imageSet
self.quality = quality
self.encoders = list(encoders)
self.records = list(records)
assert(len(self.encoders) == len(self.records))
def get_delta_header(self, tag):
"""
Get the delta encoding header.
Args:
tag: The field name to include in the tag.
Return:
The array of strings, providing the header names.
"""
result = []
for encoder in self.encoders[1:]:
result.append("%s %s" % (tag, encoder))
return result
def get_abs_delta(self, field):
"""
Get an absolute delta result.
Args:
field: The Record attribute name to diff.
Return:
The array of float delta values.
"""
result = []
root = self.records[0]
for record in self.records[1:]:
result.append(getattr(record, field) - getattr(root, field))
return result
def get_rel_delta(self, field):
"""
Get an relative delta result (score / ref).
Args:
field: The Record attribute name to diff.
Return:
The array of float delta values.
"""
result = []
root = self.records[0]
for record in self.records[1:]:
result.append(getattr(record, field) / getattr(root, field))
return result
def get_irel_delta(self, field):
"""
Get an inverse relative delta result (ref / score).
Args:
field: The Record attribute name to diff.
Return:
The array of float delta values.
"""
return [1.0 / x for x in self.get_rel_delta(field)]
def get_full_row_header_csv(self):
"""
Get a CSV encoded delta record header.
Return:
The string for the row.
"""
rows = [
"Image Set",
"Quality",
"Size",
"Name"
]
rows.append("")
rows.extend(self.get_delta_header("PSNR"))
rows.append("")
rows.extend(self.get_delta_header("Speed"))
return ",".join(rows)
def get_full_row_csv(self):
"""
Get a CSV encoded delta record.
Return:
The string for the row.
"""
rows = [
self.imageSet,
self.quality,
self.records[0].name,
self.records[0].blkSz
]
rows.append("")
data = ["%0.3f" % x for x in self.get_abs_delta("psnr")]
rows.extend(data)
rows.append("")
data = ["%0.3f" % x for x in self.get_irel_delta("cTime")]
rows.extend(data)
return ",".join(rows)
def print_result_set(imageSet, quality, encoders, results, printHeader):
"""
Attributes:
imageSet: The image set name.
quality: The compressor quality used.
encoders: The names of the encoders used. The first encoder in this
list will be used as the reference result.
results: The dict of results, indexed by encoder.
printHeader: True if the table header should be printed, else False.
"""
results = [results[x] for x in encoders]
recordSizes = [len(x.records) for x in results]
# Skip result sets that are not the same size
# TODO: We can take the set intersection here to report what we can
if min(recordSizes) != max(recordSizes):
return
# Interleave all result records
recordSets = zip(*[x.records for x in results])
# Iterate each image
for recordSet in recordSets:
base = recordSet[0]
# Sanity check consistency
for record in recordSet[1:]:
assert(record.blkSz == base.blkSz)
assert(record.name == base.name)
dr = DeltaRecord(imageSet, quality, encoders, recordSet)
if printHeader:
print(dr.get_full_row_header_csv())
printHeader = False
print(dr.get_full_row_csv())
def main():
"""
The main function.
Returns:
int: The process return code.
"""
results = find_reference_results()
imageSet = sorted(results.keys())
first = True
for image in imageSet:
qualityTree = results[image]
qualitySet = sorted(qualityTree.keys())
for qual in qualitySet:
encoderTree = qualityTree[qual]
encoderSet = sorted(encoderTree.keys())
if len(encoderSet) > 1:
print_result_set(image, qual, encoderSet, encoderTree, first)
first = False
return 0
if __name__ == "__main__":
sys.exit(main())