1cc1dc7a3Sopenharmony_ci#!/usr/bin/env python3 2cc1dc7a3Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 3cc1dc7a3Sopenharmony_ci# ----------------------------------------------------------------------------- 4cc1dc7a3Sopenharmony_ci# Copyright 2020-2021 Arm Limited 5cc1dc7a3Sopenharmony_ci# 6cc1dc7a3Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may not 7cc1dc7a3Sopenharmony_ci# use this file except in compliance with the License. You may obtain a copy 8cc1dc7a3Sopenharmony_ci# of the License at: 9cc1dc7a3Sopenharmony_ci# 10cc1dc7a3Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 11cc1dc7a3Sopenharmony_ci# 12cc1dc7a3Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 13cc1dc7a3Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14cc1dc7a3Sopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15cc1dc7a3Sopenharmony_ci# License for the specific language governing permissions and limitations 16cc1dc7a3Sopenharmony_ci# under the License. 17cc1dc7a3Sopenharmony_ci# ----------------------------------------------------------------------------- 18cc1dc7a3Sopenharmony_ci""" 19cc1dc7a3Sopenharmony_ciThe ``astc_test_result_report.py`` script consolidates all current sets of 20cc1dc7a3Sopenharmony_cireference results into a single report giving PSNR diffs (absolute) and 21cc1dc7a3Sopenharmony_ciperformance diffs (relative speedup, 1 = no change). 22cc1dc7a3Sopenharmony_ci""" 23cc1dc7a3Sopenharmony_ci 24cc1dc7a3Sopenharmony_ciimport re 25cc1dc7a3Sopenharmony_ciimport os 26cc1dc7a3Sopenharmony_ciimport sys 27cc1dc7a3Sopenharmony_ci 28cc1dc7a3Sopenharmony_ci 29cc1dc7a3Sopenharmony_ciimport testlib.resultset as trs 30cc1dc7a3Sopenharmony_cifrom collections import defaultdict as ddict 31cc1dc7a3Sopenharmony_ci 32cc1dc7a3Sopenharmony_ci 33cc1dc7a3Sopenharmony_ciCONFIG_FILTER = [ 34cc1dc7a3Sopenharmony_ci re.compile(r"^.*1\.7.*$"), 35cc1dc7a3Sopenharmony_ci re.compile(r"^.*sse.*$") 36cc1dc7a3Sopenharmony_ci] 37cc1dc7a3Sopenharmony_ci 38cc1dc7a3Sopenharmony_ciTESTSET_FILTER = [ 39cc1dc7a3Sopenharmony_ci re.compile(r"^Small$"), 40cc1dc7a3Sopenharmony_ci re.compile(r"^Frymire$"), 41cc1dc7a3Sopenharmony_ci] 42cc1dc7a3Sopenharmony_ci 43cc1dc7a3Sopenharmony_ciQUALITY_FILTER = [ 44cc1dc7a3Sopenharmony_ci] 45cc1dc7a3Sopenharmony_ci 46cc1dc7a3Sopenharmony_ciBLOCKSIZE_FILTER = [ 47cc1dc7a3Sopenharmony_ci re.compile(r"^12x12$") 48cc1dc7a3Sopenharmony_ci] 49cc1dc7a3Sopenharmony_ci 50cc1dc7a3Sopenharmony_ci 51cc1dc7a3Sopenharmony_cidef find_reference_results(): 52cc1dc7a3Sopenharmony_ci """ 53cc1dc7a3Sopenharmony_ci Scrape the Test/Images directory for result CSV files and return an 54cc1dc7a3Sopenharmony_ci mapping of the result sets. 55cc1dc7a3Sopenharmony_ci 56cc1dc7a3Sopenharmony_ci Returns: 57cc1dc7a3Sopenharmony_ci Returns a three deep tree of dictionaries, with the final dict 58cc1dc7a3Sopenharmony_ci pointing at a `ResultSet` object. The hierarchy is: 59cc1dc7a3Sopenharmony_ci 60cc1dc7a3Sopenharmony_ci imageSet => quality => encoder => result 61cc1dc7a3Sopenharmony_ci """ 62cc1dc7a3Sopenharmony_ci scriptDir = os.path.dirname(__file__) 63cc1dc7a3Sopenharmony_ci imageDir = os.path.join(scriptDir, "Images") 64cc1dc7a3Sopenharmony_ci 65cc1dc7a3Sopenharmony_ci # Pattern for extracting useful data from the CSV file name 66cc1dc7a3Sopenharmony_ci filePat = re.compile(r"astc_reference-(.+)_(.+)_results\.csv") 67cc1dc7a3Sopenharmony_ci 68cc1dc7a3Sopenharmony_ci # Build a three level dictionary we can write into 69cc1dc7a3Sopenharmony_ci results = ddict(lambda: ddict(lambda: ddict())) 70cc1dc7a3Sopenharmony_ci 71cc1dc7a3Sopenharmony_ci # Final all CSVs, load them and store them in the dict tree 72cc1dc7a3Sopenharmony_ci for root, dirs, files in os.walk(imageDir): 73cc1dc7a3Sopenharmony_ci for name in files: 74cc1dc7a3Sopenharmony_ci match = filePat.match(name) 75cc1dc7a3Sopenharmony_ci if match: 76cc1dc7a3Sopenharmony_ci 77cc1dc7a3Sopenharmony_ci # Skip results set in the filter 78cc1dc7a3Sopenharmony_ci skip = [1 for filt in CONFIG_FILTER if filt.match(name)] 79cc1dc7a3Sopenharmony_ci if skip: 80cc1dc7a3Sopenharmony_ci continue 81cc1dc7a3Sopenharmony_ci 82cc1dc7a3Sopenharmony_ci fullPath = os.path.join(root, name) 83cc1dc7a3Sopenharmony_ci 84cc1dc7a3Sopenharmony_ci encoder = match.group(1) 85cc1dc7a3Sopenharmony_ci quality = match.group(2) 86cc1dc7a3Sopenharmony_ci imageSet = os.path.basename(root) 87cc1dc7a3Sopenharmony_ci 88cc1dc7a3Sopenharmony_ci # Skip results set in the filter 89cc1dc7a3Sopenharmony_ci skip = [1 for filt in TESTSET_FILTER if filt.match(imageSet)] 90cc1dc7a3Sopenharmony_ci if skip: 91cc1dc7a3Sopenharmony_ci continue 92cc1dc7a3Sopenharmony_ci 93cc1dc7a3Sopenharmony_ci # Skip results set in the filter 94cc1dc7a3Sopenharmony_ci skip = [1 for filt in QUALITY_FILTER if filt.match(quality)] 95cc1dc7a3Sopenharmony_ci if skip: 96cc1dc7a3Sopenharmony_ci continue 97cc1dc7a3Sopenharmony_ci 98cc1dc7a3Sopenharmony_ci testRef = trs.ResultSet(imageSet) 99cc1dc7a3Sopenharmony_ci testRef.load_from_file(fullPath) 100cc1dc7a3Sopenharmony_ci 101cc1dc7a3Sopenharmony_ci patchedRef = trs.ResultSet(imageSet) 102cc1dc7a3Sopenharmony_ci 103cc1dc7a3Sopenharmony_ci for result in testRef.records: 104cc1dc7a3Sopenharmony_ci skip = [1 for filt in BLOCKSIZE_FILTER if filt.match(result.blkSz)] 105cc1dc7a3Sopenharmony_ci if not skip: 106cc1dc7a3Sopenharmony_ci patchedRef.add_record(result) 107cc1dc7a3Sopenharmony_ci 108cc1dc7a3Sopenharmony_ci results[imageSet][quality]["ref-%s" % encoder] = patchedRef 109cc1dc7a3Sopenharmony_ci 110cc1dc7a3Sopenharmony_ci return results 111cc1dc7a3Sopenharmony_ci 112cc1dc7a3Sopenharmony_ci 113cc1dc7a3Sopenharmony_ciclass DeltaRecord(): 114cc1dc7a3Sopenharmony_ci """ 115cc1dc7a3Sopenharmony_ci Record a single image result from N different encoders. 116cc1dc7a3Sopenharmony_ci 117cc1dc7a3Sopenharmony_ci Attributes: 118cc1dc7a3Sopenharmony_ci imageSet: The image set this cme from. 119cc1dc7a3Sopenharmony_ci quality: The compressor quality used. 120cc1dc7a3Sopenharmony_ci encoders: The names of the encoders used. The first encoder in this 121cc1dc7a3Sopenharmony_ci list will be used as the reference result. 122cc1dc7a3Sopenharmony_ci records: The raw records for the encoders. The order of records in this 123cc1dc7a3Sopenharmony_ci list matches the order of the `encoders` list. 124cc1dc7a3Sopenharmony_ci """ 125cc1dc7a3Sopenharmony_ci 126cc1dc7a3Sopenharmony_ci def __init__(self, imageSet, quality, encoders, records): 127cc1dc7a3Sopenharmony_ci self.imageSet = imageSet 128cc1dc7a3Sopenharmony_ci self.quality = quality 129cc1dc7a3Sopenharmony_ci 130cc1dc7a3Sopenharmony_ci self.encoders = list(encoders) 131cc1dc7a3Sopenharmony_ci self.records = list(records) 132cc1dc7a3Sopenharmony_ci 133cc1dc7a3Sopenharmony_ci assert(len(self.encoders) == len(self.records)) 134cc1dc7a3Sopenharmony_ci 135cc1dc7a3Sopenharmony_ci def get_delta_header(self, tag): 136cc1dc7a3Sopenharmony_ci """ 137cc1dc7a3Sopenharmony_ci Get the delta encoding header. 138cc1dc7a3Sopenharmony_ci 139cc1dc7a3Sopenharmony_ci Args: 140cc1dc7a3Sopenharmony_ci tag: The field name to include in the tag. 141cc1dc7a3Sopenharmony_ci 142cc1dc7a3Sopenharmony_ci Return: 143cc1dc7a3Sopenharmony_ci The array of strings, providing the header names. 144cc1dc7a3Sopenharmony_ci """ 145cc1dc7a3Sopenharmony_ci result = [] 146cc1dc7a3Sopenharmony_ci 147cc1dc7a3Sopenharmony_ci for encoder in self.encoders[1:]: 148cc1dc7a3Sopenharmony_ci result.append("%s %s" % (tag, encoder)) 149cc1dc7a3Sopenharmony_ci 150cc1dc7a3Sopenharmony_ci return result 151cc1dc7a3Sopenharmony_ci 152cc1dc7a3Sopenharmony_ci def get_abs_delta(self, field): 153cc1dc7a3Sopenharmony_ci """ 154cc1dc7a3Sopenharmony_ci Get an absolute delta result. 155cc1dc7a3Sopenharmony_ci 156cc1dc7a3Sopenharmony_ci Args: 157cc1dc7a3Sopenharmony_ci field: The Record attribute name to diff. 158cc1dc7a3Sopenharmony_ci 159cc1dc7a3Sopenharmony_ci Return: 160cc1dc7a3Sopenharmony_ci The array of float delta values. 161cc1dc7a3Sopenharmony_ci """ 162cc1dc7a3Sopenharmony_ci result = [] 163cc1dc7a3Sopenharmony_ci 164cc1dc7a3Sopenharmony_ci root = self.records[0] 165cc1dc7a3Sopenharmony_ci for record in self.records[1:]: 166cc1dc7a3Sopenharmony_ci result.append(getattr(record, field) - getattr(root, field)) 167cc1dc7a3Sopenharmony_ci 168cc1dc7a3Sopenharmony_ci return result 169cc1dc7a3Sopenharmony_ci 170cc1dc7a3Sopenharmony_ci def get_rel_delta(self, field): 171cc1dc7a3Sopenharmony_ci """ 172cc1dc7a3Sopenharmony_ci Get an relative delta result (score / ref). 173cc1dc7a3Sopenharmony_ci 174cc1dc7a3Sopenharmony_ci Args: 175cc1dc7a3Sopenharmony_ci field: The Record attribute name to diff. 176cc1dc7a3Sopenharmony_ci 177cc1dc7a3Sopenharmony_ci Return: 178cc1dc7a3Sopenharmony_ci The array of float delta values. 179cc1dc7a3Sopenharmony_ci """ 180cc1dc7a3Sopenharmony_ci result = [] 181cc1dc7a3Sopenharmony_ci 182cc1dc7a3Sopenharmony_ci root = self.records[0] 183cc1dc7a3Sopenharmony_ci for record in self.records[1:]: 184cc1dc7a3Sopenharmony_ci result.append(getattr(record, field) / getattr(root, field)) 185cc1dc7a3Sopenharmony_ci 186cc1dc7a3Sopenharmony_ci return result 187cc1dc7a3Sopenharmony_ci 188cc1dc7a3Sopenharmony_ci def get_irel_delta(self, field): 189cc1dc7a3Sopenharmony_ci """ 190cc1dc7a3Sopenharmony_ci Get an inverse relative delta result (ref / score). 191cc1dc7a3Sopenharmony_ci 192cc1dc7a3Sopenharmony_ci Args: 193cc1dc7a3Sopenharmony_ci field: The Record attribute name to diff. 194cc1dc7a3Sopenharmony_ci 195cc1dc7a3Sopenharmony_ci Return: 196cc1dc7a3Sopenharmony_ci The array of float delta values. 197cc1dc7a3Sopenharmony_ci """ 198cc1dc7a3Sopenharmony_ci return [1.0 / x for x in self.get_rel_delta(field)] 199cc1dc7a3Sopenharmony_ci 200cc1dc7a3Sopenharmony_ci def get_full_row_header_csv(self): 201cc1dc7a3Sopenharmony_ci """ 202cc1dc7a3Sopenharmony_ci Get a CSV encoded delta record header. 203cc1dc7a3Sopenharmony_ci 204cc1dc7a3Sopenharmony_ci Return: 205cc1dc7a3Sopenharmony_ci The string for the row. 206cc1dc7a3Sopenharmony_ci """ 207cc1dc7a3Sopenharmony_ci rows = [ 208cc1dc7a3Sopenharmony_ci "Image Set", 209cc1dc7a3Sopenharmony_ci "Quality", 210cc1dc7a3Sopenharmony_ci "Size", 211cc1dc7a3Sopenharmony_ci "Name" 212cc1dc7a3Sopenharmony_ci ] 213cc1dc7a3Sopenharmony_ci 214cc1dc7a3Sopenharmony_ci rows.append("") 215cc1dc7a3Sopenharmony_ci rows.extend(self.get_delta_header("PSNR")) 216cc1dc7a3Sopenharmony_ci 217cc1dc7a3Sopenharmony_ci rows.append("") 218cc1dc7a3Sopenharmony_ci rows.extend(self.get_delta_header("Speed")) 219cc1dc7a3Sopenharmony_ci 220cc1dc7a3Sopenharmony_ci return ",".join(rows) 221cc1dc7a3Sopenharmony_ci 222cc1dc7a3Sopenharmony_ci def get_full_row_csv(self): 223cc1dc7a3Sopenharmony_ci """ 224cc1dc7a3Sopenharmony_ci Get a CSV encoded delta record. 225cc1dc7a3Sopenharmony_ci 226cc1dc7a3Sopenharmony_ci Return: 227cc1dc7a3Sopenharmony_ci The string for the row. 228cc1dc7a3Sopenharmony_ci """ 229cc1dc7a3Sopenharmony_ci rows = [ 230cc1dc7a3Sopenharmony_ci self.imageSet, 231cc1dc7a3Sopenharmony_ci self.quality, 232cc1dc7a3Sopenharmony_ci self.records[0].name, 233cc1dc7a3Sopenharmony_ci self.records[0].blkSz 234cc1dc7a3Sopenharmony_ci ] 235cc1dc7a3Sopenharmony_ci 236cc1dc7a3Sopenharmony_ci rows.append("") 237cc1dc7a3Sopenharmony_ci data = ["%0.3f" % x for x in self.get_abs_delta("psnr")] 238cc1dc7a3Sopenharmony_ci rows.extend(data) 239cc1dc7a3Sopenharmony_ci 240cc1dc7a3Sopenharmony_ci rows.append("") 241cc1dc7a3Sopenharmony_ci data = ["%0.3f" % x for x in self.get_irel_delta("cTime")] 242cc1dc7a3Sopenharmony_ci rows.extend(data) 243cc1dc7a3Sopenharmony_ci 244cc1dc7a3Sopenharmony_ci return ",".join(rows) 245cc1dc7a3Sopenharmony_ci 246cc1dc7a3Sopenharmony_ci 247cc1dc7a3Sopenharmony_cidef print_result_set(imageSet, quality, encoders, results, printHeader): 248cc1dc7a3Sopenharmony_ci """ 249cc1dc7a3Sopenharmony_ci Attributes: 250cc1dc7a3Sopenharmony_ci imageSet: The image set name. 251cc1dc7a3Sopenharmony_ci quality: The compressor quality used. 252cc1dc7a3Sopenharmony_ci encoders: The names of the encoders used. The first encoder in this 253cc1dc7a3Sopenharmony_ci list will be used as the reference result. 254cc1dc7a3Sopenharmony_ci results: The dict of results, indexed by encoder. 255cc1dc7a3Sopenharmony_ci printHeader: True if the table header should be printed, else False. 256cc1dc7a3Sopenharmony_ci """ 257cc1dc7a3Sopenharmony_ci results = [results[x] for x in encoders] 258cc1dc7a3Sopenharmony_ci recordSizes = [len(x.records) for x in results] 259cc1dc7a3Sopenharmony_ci 260cc1dc7a3Sopenharmony_ci # Skip result sets that are not the same size 261cc1dc7a3Sopenharmony_ci # TODO: We can take the set intersection here to report what we can 262cc1dc7a3Sopenharmony_ci if min(recordSizes) != max(recordSizes): 263cc1dc7a3Sopenharmony_ci return 264cc1dc7a3Sopenharmony_ci 265cc1dc7a3Sopenharmony_ci # Interleave all result records 266cc1dc7a3Sopenharmony_ci recordSets = zip(*[x.records for x in results]) 267cc1dc7a3Sopenharmony_ci 268cc1dc7a3Sopenharmony_ci # Iterate each image 269cc1dc7a3Sopenharmony_ci for recordSet in recordSets: 270cc1dc7a3Sopenharmony_ci base = recordSet[0] 271cc1dc7a3Sopenharmony_ci 272cc1dc7a3Sopenharmony_ci # Sanity check consistency 273cc1dc7a3Sopenharmony_ci for record in recordSet[1:]: 274cc1dc7a3Sopenharmony_ci assert(record.blkSz == base.blkSz) 275cc1dc7a3Sopenharmony_ci assert(record.name == base.name) 276cc1dc7a3Sopenharmony_ci 277cc1dc7a3Sopenharmony_ci dr = DeltaRecord(imageSet, quality, encoders, recordSet) 278cc1dc7a3Sopenharmony_ci 279cc1dc7a3Sopenharmony_ci if printHeader: 280cc1dc7a3Sopenharmony_ci print(dr.get_full_row_header_csv()) 281cc1dc7a3Sopenharmony_ci printHeader = False 282cc1dc7a3Sopenharmony_ci 283cc1dc7a3Sopenharmony_ci print(dr.get_full_row_csv()) 284cc1dc7a3Sopenharmony_ci 285cc1dc7a3Sopenharmony_ci 286cc1dc7a3Sopenharmony_cidef main(): 287cc1dc7a3Sopenharmony_ci """ 288cc1dc7a3Sopenharmony_ci The main function. 289cc1dc7a3Sopenharmony_ci 290cc1dc7a3Sopenharmony_ci Returns: 291cc1dc7a3Sopenharmony_ci int: The process return code. 292cc1dc7a3Sopenharmony_ci """ 293cc1dc7a3Sopenharmony_ci 294cc1dc7a3Sopenharmony_ci results = find_reference_results() 295cc1dc7a3Sopenharmony_ci 296cc1dc7a3Sopenharmony_ci imageSet = sorted(results.keys()) 297cc1dc7a3Sopenharmony_ci 298cc1dc7a3Sopenharmony_ci first = True 299cc1dc7a3Sopenharmony_ci for image in imageSet: 300cc1dc7a3Sopenharmony_ci qualityTree = results[image] 301cc1dc7a3Sopenharmony_ci qualitySet = sorted(qualityTree.keys()) 302cc1dc7a3Sopenharmony_ci 303cc1dc7a3Sopenharmony_ci for qual in qualitySet: 304cc1dc7a3Sopenharmony_ci encoderTree = qualityTree[qual] 305cc1dc7a3Sopenharmony_ci encoderSet = sorted(encoderTree.keys()) 306cc1dc7a3Sopenharmony_ci 307cc1dc7a3Sopenharmony_ci if len(encoderSet) > 1: 308cc1dc7a3Sopenharmony_ci print_result_set(image, qual, encoderSet, encoderTree, first) 309cc1dc7a3Sopenharmony_ci first = False 310cc1dc7a3Sopenharmony_ci 311cc1dc7a3Sopenharmony_ci return 0 312cc1dc7a3Sopenharmony_ci 313cc1dc7a3Sopenharmony_ci 314cc1dc7a3Sopenharmony_ciif __name__ == "__main__": 315cc1dc7a3Sopenharmony_ci sys.exit(main()) 316