1cc1dc7a3Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 2cc1dc7a3Sopenharmony_ci# ----------------------------------------------------------------------------- 3cc1dc7a3Sopenharmony_ci# Copyright 2020-2022 Arm Limited 4cc1dc7a3Sopenharmony_ci# 5cc1dc7a3Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); you may not 6cc1dc7a3Sopenharmony_ci# use this file except in compliance with the License. You may obtain a copy 7cc1dc7a3Sopenharmony_ci# of the License at: 8cc1dc7a3Sopenharmony_ci# 9cc1dc7a3Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 10cc1dc7a3Sopenharmony_ci# 11cc1dc7a3Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 12cc1dc7a3Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13cc1dc7a3Sopenharmony_ci# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14cc1dc7a3Sopenharmony_ci# License for the specific language governing permissions and limitations 15cc1dc7a3Sopenharmony_ci# under the License. 16cc1dc7a3Sopenharmony_ci# ----------------------------------------------------------------------------- 17cc1dc7a3Sopenharmony_ci""" 18cc1dc7a3Sopenharmony_ciA ResultSet stores a set of results about the performance of a TestSet. Each 19cc1dc7a3Sopenharmony_ciset keeps result Records for each image and block size tested, that store the 20cc1dc7a3Sopenharmony_ciPSNR and coding time. 21cc1dc7a3Sopenharmony_ci 22cc1dc7a3Sopenharmony_ciResultSets are often backed by a CSV file on disk, and a ResultSet can be 23cc1dc7a3Sopenharmony_cicompared against a set of reference results created by an earlier test run. 24cc1dc7a3Sopenharmony_ci""" 25cc1dc7a3Sopenharmony_ci 26cc1dc7a3Sopenharmony_ci 27cc1dc7a3Sopenharmony_ciimport csv 28cc1dc7a3Sopenharmony_ciimport enum 29cc1dc7a3Sopenharmony_ciimport numpy as np 30cc1dc7a3Sopenharmony_ciimport os 31cc1dc7a3Sopenharmony_ci 32cc1dc7a3Sopenharmony_ci 33cc1dc7a3Sopenharmony_ci@enum.unique 34cc1dc7a3Sopenharmony_ciclass Result(enum.IntEnum): 35cc1dc7a3Sopenharmony_ci """ 36cc1dc7a3Sopenharmony_ci An enumeration of test result status values. 37cc1dc7a3Sopenharmony_ci 38cc1dc7a3Sopenharmony_ci Attributes: 39cc1dc7a3Sopenharmony_ci NOTRUN: The test has not been run. 40cc1dc7a3Sopenharmony_ci PASS: The test passed. 41cc1dc7a3Sopenharmony_ci WARN: The test image quality was below the pass threshold but above 42cc1dc7a3Sopenharmony_ci the fail threshold. 43cc1dc7a3Sopenharmony_ci FAIL: The test image quality was below the fail threshold. 44cc1dc7a3Sopenharmony_ci """ 45cc1dc7a3Sopenharmony_ci NOTRUN = 0 46cc1dc7a3Sopenharmony_ci PASS = 1 47cc1dc7a3Sopenharmony_ci WARN = 2 48cc1dc7a3Sopenharmony_ci FAIL = 3 49cc1dc7a3Sopenharmony_ci 50cc1dc7a3Sopenharmony_ci 51cc1dc7a3Sopenharmony_ciclass ResultSummary(): 52cc1dc7a3Sopenharmony_ci """ 53cc1dc7a3Sopenharmony_ci An result summary data container, storing number of results of each type. 54cc1dc7a3Sopenharmony_ci 55cc1dc7a3Sopenharmony_ci Attributes: 56cc1dc7a3Sopenharmony_ci notruns: The number of tests that did not run. 57cc1dc7a3Sopenharmony_ci passes: The number of tests that passed. 58cc1dc7a3Sopenharmony_ci warnings: The number of tests that produced a warning. 59cc1dc7a3Sopenharmony_ci fails: The number of tests that failed. 60cc1dc7a3Sopenharmony_ci tTimes: Total time speedup vs reference (<1 is slower, >1 is faster). 61cc1dc7a3Sopenharmony_ci cTimes: Coding time speedup vs reference (<1 is slower, >1 is faster). 62cc1dc7a3Sopenharmony_ci psnrs: Coding time quality vs reference (<0 is worse, >0 is better). 63cc1dc7a3Sopenharmony_ci """ 64cc1dc7a3Sopenharmony_ci 65cc1dc7a3Sopenharmony_ci def __init__(self): 66cc1dc7a3Sopenharmony_ci """ 67cc1dc7a3Sopenharmony_ci Create a new result summary. 68cc1dc7a3Sopenharmony_ci """ 69cc1dc7a3Sopenharmony_ci # Pass fail metrics 70cc1dc7a3Sopenharmony_ci self.notruns = 0 71cc1dc7a3Sopenharmony_ci self.passes = 0 72cc1dc7a3Sopenharmony_ci self.warnings = 0 73cc1dc7a3Sopenharmony_ci self.fails = 0 74cc1dc7a3Sopenharmony_ci 75cc1dc7a3Sopenharmony_ci # Relative results 76cc1dc7a3Sopenharmony_ci self.tTimesRel = [] 77cc1dc7a3Sopenharmony_ci self.cTimesRel = [] 78cc1dc7a3Sopenharmony_ci self.psnrRel = [] 79cc1dc7a3Sopenharmony_ci 80cc1dc7a3Sopenharmony_ci # Absolute results 81cc1dc7a3Sopenharmony_ci self.cTime = [] 82cc1dc7a3Sopenharmony_ci self.psnr = [] 83cc1dc7a3Sopenharmony_ci 84cc1dc7a3Sopenharmony_ci def add_record(self, record): 85cc1dc7a3Sopenharmony_ci """ 86cc1dc7a3Sopenharmony_ci Add a record to this summary. 87cc1dc7a3Sopenharmony_ci 88cc1dc7a3Sopenharmony_ci Args: 89cc1dc7a3Sopenharmony_ci record (Record): The Record to add. 90cc1dc7a3Sopenharmony_ci """ 91cc1dc7a3Sopenharmony_ci if record.status == Result.PASS: 92cc1dc7a3Sopenharmony_ci self.passes += 1 93cc1dc7a3Sopenharmony_ci elif record.status == Result.WARN: 94cc1dc7a3Sopenharmony_ci self.warnings += 1 95cc1dc7a3Sopenharmony_ci elif record.status == Result.FAIL: 96cc1dc7a3Sopenharmony_ci self.fails += 1 97cc1dc7a3Sopenharmony_ci else: 98cc1dc7a3Sopenharmony_ci self.notruns += 1 99cc1dc7a3Sopenharmony_ci 100cc1dc7a3Sopenharmony_ci if record.tTimeRel is not None: 101cc1dc7a3Sopenharmony_ci self.tTimesRel.append(record.tTimeRel) 102cc1dc7a3Sopenharmony_ci self.cTimesRel.append(record.cTimeRel) 103cc1dc7a3Sopenharmony_ci self.psnrRel.append(record.psnrRel) 104cc1dc7a3Sopenharmony_ci 105cc1dc7a3Sopenharmony_ci self.cTime.append(record.cTime) 106cc1dc7a3Sopenharmony_ci self.psnr.append(record.psnr) 107cc1dc7a3Sopenharmony_ci 108cc1dc7a3Sopenharmony_ci def get_worst_result(self): 109cc1dc7a3Sopenharmony_ci """ 110cc1dc7a3Sopenharmony_ci Get the worst result in this set. 111cc1dc7a3Sopenharmony_ci 112cc1dc7a3Sopenharmony_ci Returns: 113cc1dc7a3Sopenharmony_ci Result: The worst test result. 114cc1dc7a3Sopenharmony_ci """ 115cc1dc7a3Sopenharmony_ci if self.fails: 116cc1dc7a3Sopenharmony_ci return Result.FAIL 117cc1dc7a3Sopenharmony_ci 118cc1dc7a3Sopenharmony_ci if self.warnings: 119cc1dc7a3Sopenharmony_ci return Result.WARN 120cc1dc7a3Sopenharmony_ci 121cc1dc7a3Sopenharmony_ci if self.passes: 122cc1dc7a3Sopenharmony_ci return Result.PASS 123cc1dc7a3Sopenharmony_ci 124cc1dc7a3Sopenharmony_ci return Result.NOTRUN 125cc1dc7a3Sopenharmony_ci 126cc1dc7a3Sopenharmony_ci def __str__(self): 127cc1dc7a3Sopenharmony_ci # Overall pass/fail results 128cc1dc7a3Sopenharmony_ci overall = self.get_worst_result().name 129cc1dc7a3Sopenharmony_ci dat = (overall, self.passes, self.warnings, self.fails) 130cc1dc7a3Sopenharmony_ci result = ["\nSet Status: %s (Pass: %u | Warn: %u | Fail: %u)" % dat] 131cc1dc7a3Sopenharmony_ci 132cc1dc7a3Sopenharmony_ci if (self.tTimesRel): 133cc1dc7a3Sopenharmony_ci # Performance summaries 134cc1dc7a3Sopenharmony_ci dat = (np.mean(self.tTimesRel), np.std(self.tTimesRel)) 135cc1dc7a3Sopenharmony_ci result.append("\nTotal speed: Mean: %+0.3f x Std: %0.2f x" % dat) 136cc1dc7a3Sopenharmony_ci 137cc1dc7a3Sopenharmony_ci dat = (np.mean(self.cTimesRel), np.std(self.cTimesRel)) 138cc1dc7a3Sopenharmony_ci result.append("Coding speed: Mean: %+0.3f x Std: %0.2f x" % dat) 139cc1dc7a3Sopenharmony_ci 140cc1dc7a3Sopenharmony_ci dat = (np.mean(self.psnrRel), np.std(self.psnrRel)) 141cc1dc7a3Sopenharmony_ci result.append("Quality diff: Mean: %+0.3f dB Std: %0.2f dB" % dat) 142cc1dc7a3Sopenharmony_ci 143cc1dc7a3Sopenharmony_ci dat = (np.mean(self.cTime), np.std(self.cTime)) 144cc1dc7a3Sopenharmony_ci result.append("Coding time: Mean: %+0.3f s Std: %0.2f s" % dat) 145cc1dc7a3Sopenharmony_ci 146cc1dc7a3Sopenharmony_ci dat = (np.mean(self.psnr), np.std(self.psnr)) 147cc1dc7a3Sopenharmony_ci result.append("Quality: Mean: %+0.3f dB Std: %0.2f dB" % dat) 148cc1dc7a3Sopenharmony_ci 149cc1dc7a3Sopenharmony_ci return "\n".join(result) 150cc1dc7a3Sopenharmony_ci 151cc1dc7a3Sopenharmony_ci 152cc1dc7a3Sopenharmony_ciclass Record(): 153cc1dc7a3Sopenharmony_ci """ 154cc1dc7a3Sopenharmony_ci A single result record, sotring results for a singel image and block size. 155cc1dc7a3Sopenharmony_ci 156cc1dc7a3Sopenharmony_ci Attributes: 157cc1dc7a3Sopenharmony_ci blkSz: The block size. 158cc1dc7a3Sopenharmony_ci name: The test image name. 159cc1dc7a3Sopenharmony_ci psnr: The image quality (PSNR dB) 160cc1dc7a3Sopenharmony_ci tTime: The total compression time. 161cc1dc7a3Sopenharmony_ci cTime: The coding compression time. 162cc1dc7a3Sopenharmony_ci cRate: The coding compression rate. 163cc1dc7a3Sopenharmony_ci status: The test Result. 164cc1dc7a3Sopenharmony_ci """ 165cc1dc7a3Sopenharmony_ci 166cc1dc7a3Sopenharmony_ci def __init__(self, blkSz, name, psnr, tTime, cTime, cRate): 167cc1dc7a3Sopenharmony_ci """ 168cc1dc7a3Sopenharmony_ci Create a result record, initially in the NOTRUN status. 169cc1dc7a3Sopenharmony_ci 170cc1dc7a3Sopenharmony_ci Args: 171cc1dc7a3Sopenharmony_ci blkSz (str): The block size. 172cc1dc7a3Sopenharmony_ci name (str): The test image name. 173cc1dc7a3Sopenharmony_ci psnr (float): The image quality PSNR, in dB. 174cc1dc7a3Sopenharmony_ci tTime (float): The total compression time, in seconds. 175cc1dc7a3Sopenharmony_ci cTime (float): The coding compression time, in seconds. 176cc1dc7a3Sopenharmony_ci cRate (float): The coding compression rate, in MPix/s. 177cc1dc7a3Sopenharmony_ci tTimeRel (float): The relative total time speedup vs reference. 178cc1dc7a3Sopenharmony_ci cTimeRel (float): The relative coding time speedup vs reference. 179cc1dc7a3Sopenharmony_ci cRateRel (float): The relative rate speedup vs reference. 180cc1dc7a3Sopenharmony_ci psnrRel (float): The relative PSNR dB vs reference. 181cc1dc7a3Sopenharmony_ci """ 182cc1dc7a3Sopenharmony_ci self.blkSz = blkSz 183cc1dc7a3Sopenharmony_ci self.name = name 184cc1dc7a3Sopenharmony_ci self.psnr = psnr 185cc1dc7a3Sopenharmony_ci self.tTime = tTime 186cc1dc7a3Sopenharmony_ci self.cTime = cTime 187cc1dc7a3Sopenharmony_ci self.cRate = cRate 188cc1dc7a3Sopenharmony_ci self.status = Result.NOTRUN 189cc1dc7a3Sopenharmony_ci 190cc1dc7a3Sopenharmony_ci self.tTimeRel = None 191cc1dc7a3Sopenharmony_ci self.cTimeRel = None 192cc1dc7a3Sopenharmony_ci self.cRateRel = None 193cc1dc7a3Sopenharmony_ci self.psnrRel = None 194cc1dc7a3Sopenharmony_ci 195cc1dc7a3Sopenharmony_ci def set_status(self, result): 196cc1dc7a3Sopenharmony_ci """ 197cc1dc7a3Sopenharmony_ci Set the result status. 198cc1dc7a3Sopenharmony_ci 199cc1dc7a3Sopenharmony_ci Args: 200cc1dc7a3Sopenharmony_ci result (Result): The test result. 201cc1dc7a3Sopenharmony_ci """ 202cc1dc7a3Sopenharmony_ci self.status = result 203cc1dc7a3Sopenharmony_ci 204cc1dc7a3Sopenharmony_ci def __str__(self): 205cc1dc7a3Sopenharmony_ci return "'%s' / '%s'" % (self.blkSz, self.name) 206cc1dc7a3Sopenharmony_ci 207cc1dc7a3Sopenharmony_ci 208cc1dc7a3Sopenharmony_ciclass ResultSet(): 209cc1dc7a3Sopenharmony_ci """ 210cc1dc7a3Sopenharmony_ci A set of results for a TestSet, across one or more block sizes. 211cc1dc7a3Sopenharmony_ci 212cc1dc7a3Sopenharmony_ci Attributes: 213cc1dc7a3Sopenharmony_ci testSet: The test set these results are linked to. 214cc1dc7a3Sopenharmony_ci records: The list of test results. 215cc1dc7a3Sopenharmony_ci """ 216cc1dc7a3Sopenharmony_ci 217cc1dc7a3Sopenharmony_ci def __init__(self, testSet): 218cc1dc7a3Sopenharmony_ci """ 219cc1dc7a3Sopenharmony_ci Create a new empty ResultSet. 220cc1dc7a3Sopenharmony_ci 221cc1dc7a3Sopenharmony_ci Args: 222cc1dc7a3Sopenharmony_ci testSet (TestSet): The test set these results are linked to. 223cc1dc7a3Sopenharmony_ci """ 224cc1dc7a3Sopenharmony_ci self.testSet = testSet 225cc1dc7a3Sopenharmony_ci self.records = [] 226cc1dc7a3Sopenharmony_ci 227cc1dc7a3Sopenharmony_ci def add_record(self, record): 228cc1dc7a3Sopenharmony_ci """ 229cc1dc7a3Sopenharmony_ci Add a new test record to this result set. 230cc1dc7a3Sopenharmony_ci 231cc1dc7a3Sopenharmony_ci Args: 232cc1dc7a3Sopenharmony_ci record (Record): The test record to add. 233cc1dc7a3Sopenharmony_ci """ 234cc1dc7a3Sopenharmony_ci self.records.append(record) 235cc1dc7a3Sopenharmony_ci 236cc1dc7a3Sopenharmony_ci def get_record(self, testSet, blkSz, name): 237cc1dc7a3Sopenharmony_ci """ 238cc1dc7a3Sopenharmony_ci Get a record matching the arguments. 239cc1dc7a3Sopenharmony_ci 240cc1dc7a3Sopenharmony_ci Args: 241cc1dc7a3Sopenharmony_ci testSet (TestSet): The test set to get results from. 242cc1dc7a3Sopenharmony_ci blkSz (str): The block size. 243cc1dc7a3Sopenharmony_ci name (str): The test name. 244cc1dc7a3Sopenharmony_ci 245cc1dc7a3Sopenharmony_ci Returns: 246cc1dc7a3Sopenharmony_ci Record: The test result, if present. 247cc1dc7a3Sopenharmony_ci 248cc1dc7a3Sopenharmony_ci Raises: 249cc1dc7a3Sopenharmony_ci KeyError: No match could be found. 250cc1dc7a3Sopenharmony_ci """ 251cc1dc7a3Sopenharmony_ci if testSet != self.testSet: 252cc1dc7a3Sopenharmony_ci raise KeyError() 253cc1dc7a3Sopenharmony_ci 254cc1dc7a3Sopenharmony_ci for record in self.records: 255cc1dc7a3Sopenharmony_ci if record.blkSz == blkSz and record.name == name: 256cc1dc7a3Sopenharmony_ci return record 257cc1dc7a3Sopenharmony_ci 258cc1dc7a3Sopenharmony_ci raise KeyError() 259cc1dc7a3Sopenharmony_ci 260cc1dc7a3Sopenharmony_ci def get_matching_record(self, other): 261cc1dc7a3Sopenharmony_ci """ 262cc1dc7a3Sopenharmony_ci Get a record matching the config of another record. 263cc1dc7a3Sopenharmony_ci 264cc1dc7a3Sopenharmony_ci Args: 265cc1dc7a3Sopenharmony_ci other (Record): The pattern result record to match. 266cc1dc7a3Sopenharmony_ci 267cc1dc7a3Sopenharmony_ci Returns: 268cc1dc7a3Sopenharmony_ci Record: The result, if present. 269cc1dc7a3Sopenharmony_ci 270cc1dc7a3Sopenharmony_ci Raises: 271cc1dc7a3Sopenharmony_ci KeyError: No match could be found. 272cc1dc7a3Sopenharmony_ci """ 273cc1dc7a3Sopenharmony_ci for record in self.records: 274cc1dc7a3Sopenharmony_ci if record.blkSz == other.blkSz and record.name == other.name: 275cc1dc7a3Sopenharmony_ci return record 276cc1dc7a3Sopenharmony_ci 277cc1dc7a3Sopenharmony_ci raise KeyError() 278cc1dc7a3Sopenharmony_ci 279cc1dc7a3Sopenharmony_ci def get_results_summary(self): 280cc1dc7a3Sopenharmony_ci """ 281cc1dc7a3Sopenharmony_ci Get a results summary of all the records in this result set. 282cc1dc7a3Sopenharmony_ci 283cc1dc7a3Sopenharmony_ci Returns: 284cc1dc7a3Sopenharmony_ci ResultSummary: The result summary. 285cc1dc7a3Sopenharmony_ci """ 286cc1dc7a3Sopenharmony_ci summary = ResultSummary() 287cc1dc7a3Sopenharmony_ci for record in self.records: 288cc1dc7a3Sopenharmony_ci summary.add_record(record) 289cc1dc7a3Sopenharmony_ci 290cc1dc7a3Sopenharmony_ci return summary 291cc1dc7a3Sopenharmony_ci 292cc1dc7a3Sopenharmony_ci def save_to_file(self, filePath): 293cc1dc7a3Sopenharmony_ci """ 294cc1dc7a3Sopenharmony_ci Save this result set to a CSV file. 295cc1dc7a3Sopenharmony_ci 296cc1dc7a3Sopenharmony_ci Args: 297cc1dc7a3Sopenharmony_ci filePath (str): The output file path. 298cc1dc7a3Sopenharmony_ci """ 299cc1dc7a3Sopenharmony_ci dirName = os.path.dirname(filePath) 300cc1dc7a3Sopenharmony_ci if not os.path.exists(dirName): 301cc1dc7a3Sopenharmony_ci os.makedirs(dirName) 302cc1dc7a3Sopenharmony_ci 303cc1dc7a3Sopenharmony_ci with open(filePath, "w", newline="") as csvfile: 304cc1dc7a3Sopenharmony_ci writer = csv.writer(csvfile) 305cc1dc7a3Sopenharmony_ci self._save_header(writer) 306cc1dc7a3Sopenharmony_ci for record in self.records: 307cc1dc7a3Sopenharmony_ci self._save_record(writer, record) 308cc1dc7a3Sopenharmony_ci 309cc1dc7a3Sopenharmony_ci @staticmethod 310cc1dc7a3Sopenharmony_ci def _save_header(writer): 311cc1dc7a3Sopenharmony_ci """ 312cc1dc7a3Sopenharmony_ci Write the header to the CSV file. 313cc1dc7a3Sopenharmony_ci 314cc1dc7a3Sopenharmony_ci Args: 315cc1dc7a3Sopenharmony_ci writer (csv.writer): The CSV writer. 316cc1dc7a3Sopenharmony_ci """ 317cc1dc7a3Sopenharmony_ci row = ["Image Set", "Block Size", "Name", 318cc1dc7a3Sopenharmony_ci "PSNR", "Total Time", "Coding Time", "Coding Rate"] 319cc1dc7a3Sopenharmony_ci writer.writerow(row) 320cc1dc7a3Sopenharmony_ci 321cc1dc7a3Sopenharmony_ci def _save_record(self, writer, record): 322cc1dc7a3Sopenharmony_ci """ 323cc1dc7a3Sopenharmony_ci Write a record to the CSV file. 324cc1dc7a3Sopenharmony_ci 325cc1dc7a3Sopenharmony_ci Args: 326cc1dc7a3Sopenharmony_ci writer (csv.writer): The CSV writer. 327cc1dc7a3Sopenharmony_ci record (Record): The record to write. 328cc1dc7a3Sopenharmony_ci """ 329cc1dc7a3Sopenharmony_ci row = [self.testSet, 330cc1dc7a3Sopenharmony_ci record.blkSz, 331cc1dc7a3Sopenharmony_ci record.name, 332cc1dc7a3Sopenharmony_ci "%0.4f" % record.psnr, 333cc1dc7a3Sopenharmony_ci "%0.4f" % record.tTime, 334cc1dc7a3Sopenharmony_ci "%0.4f" % record.cTime, 335cc1dc7a3Sopenharmony_ci "%0.4f" % record.cRate] 336cc1dc7a3Sopenharmony_ci writer.writerow(row) 337cc1dc7a3Sopenharmony_ci 338cc1dc7a3Sopenharmony_ci def load_from_file(self, filePath): 339cc1dc7a3Sopenharmony_ci """ 340cc1dc7a3Sopenharmony_ci Load a reference result set from a CSV file on disk. 341cc1dc7a3Sopenharmony_ci 342cc1dc7a3Sopenharmony_ci Args: 343cc1dc7a3Sopenharmony_ci filePath (str): The input file path. 344cc1dc7a3Sopenharmony_ci """ 345cc1dc7a3Sopenharmony_ci with open(filePath, "r") as csvfile: 346cc1dc7a3Sopenharmony_ci reader = csv.reader(csvfile) 347cc1dc7a3Sopenharmony_ci # Skip the header 348cc1dc7a3Sopenharmony_ci next(reader) 349cc1dc7a3Sopenharmony_ci for row in reader: 350cc1dc7a3Sopenharmony_ci assert row[0] == self.testSet 351cc1dc7a3Sopenharmony_ci record = Record(row[1], row[2], 352cc1dc7a3Sopenharmony_ci float(row[3]), float(row[4]), 353cc1dc7a3Sopenharmony_ci float(row[5]), float(row[6])) 354cc1dc7a3Sopenharmony_ci self.add_record(record) 355