1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
#!/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())
|