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 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
# SPDX-License-Identifier: Apache-2.0
# -----------------------------------------------------------------------------
# Copyright 2020 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.
# -----------------------------------------------------------------------------
"""
A ResultSet stores a set of results about the performance of a TestSet. Each
set keeps result Records for each image and block size tested, that store the
PSNR and coding time.
ResultSets are often backed by a CSV file on disk, and a ResultSet can be
compared against a set of reference results created by an earlier test run.
"""
import csv
import enum
import numpy as np
import os
@enum.unique
class Result(enum.IntEnum):
"""
An enumeration of test result status values.
Attributes:
NOTRUN: The test has not been run.
PASS: The test passed.
WARN: The test image quality was below the pass threshold but above
the fail threshold.
FAIL: The test image quality was below the fail threshold.
"""
NOTRUN = 0
PASS = 1
WARN = 2
FAIL = 3
class ResultSummary():
"""
An result summary data container, storing number of results of each type.
Attributes:
notruns: The number of tests that did not run.
passes: The number of tests that passed.
warnings: The number of tests that produced a warning.
fails: The number of tests that failed.
tTimes: Total time speedup vs reference (<1 is slower, >1 is faster).
cTimes: Coding time speedup vs reference (<1 is slower, >1 is faster).
psnrs: Coding time quality vs reference (<0 is worse, >0 is better).
"""
def __init__(self):
"""
Create a new result summary.
"""
self.notruns = 0
self.passes = 0
self.warnings = 0
self.fails = 0
self.tTimes = []
self.cTimes = []
self.psnrs = []
def add_record(self, record):
"""
Add a record to this summary.
Args:
record (Record): The Record to add.
"""
if record.status == Result.PASS:
self.passes += 1
elif record.status == Result.WARN:
self.warnings += 1
elif record.status == Result.FAIL:
self.fails += 1
else:
self.notruns += 1
if record.tTimeRel is not None:
self.tTimes.append(record.tTimeRel)
self.cTimes.append(record.cTimeRel)
self.psnrs.append(record.psnrRel)
def get_worst_result(self):
"""
Get the worst result in this set.
Returns:
Result: The worst test result.
"""
if self.fails:
return Result.FAIL
if self.warnings:
return Result.WARN
if self.passes:
return Result.PASS
return Result.NOTRUN
def __str__(self):
# Overall pass/fail results
overall = self.get_worst_result().name
dat = (overall, self.passes, self.warnings, self.fails)
result = ["\nSet Status: %s (Pass: %u | Warn: %u | Fail: %u)" % dat]
if (self.tTimes):
# Performance summaries
dat = (np.mean(self.tTimes), np.std(self.tTimes))
result.append("\nTotal time: Mean: %+0.2fx Std: %0.2fx" % dat)
dat = (np.mean(self.cTimes), np.std(self.cTimes))
result.append("Coding time: Mean: %+0.2fx Std: %0.2fx" % dat)
dat = (np.mean(self.psnrs), np.std(self.psnrs))
result.append("Image quality: Mean: %+0.2f dB Std: %0.2f dB" % dat)
return "\n".join(result)
class Record():
"""
A single result record, sotring results for a singel image and block size.
Attributes:
blkSz: The block size.
name: The test image name.
psnr: The image quality (PSNR dB)
tTime: The total compression time.
cTime: The coding compression time.
cRate: The coding compression rate.
status: The test Result.
"""
def __init__(self, blkSz, name, psnr, tTime, cTime, cRate):
"""
Create a result record, initially in the NOTRUN status.
Args:
blkSz (str): The block size.
name (str): The test image name.
psnr (float): The image quality PSNR, in dB.
tTime (float): The total compression time, in seconds.
cTime (float): The coding compression time, in seconds.
cRate (float): The coding compression rate, in MPix/s.
tTimeRel (float): The relative total time speedup vs reference.
cTimeRel (float): The relative coding time speedup vs reference.
cRateRel (float): The relative rate speedup vs reference.
psnrRel (float): The relative PSNR dB vs reference.
"""
self.blkSz = blkSz
self.name = name
self.psnr = psnr
self.tTime = tTime
self.cTime = cTime
self.cRate = cRate
self.status = Result.NOTRUN
self.tTimeRel = None
self.cTimeRel = None
self.cRateRel = None
self.psnrRel = None
def set_status(self, result):
"""
Set the result status.
Args:
result (Result): The test result.
"""
self.status = result
def __str__(self):
return "'%s' / '%s'" % (self.blkSz, self.name)
class ResultSet():
"""
A set of results for a TestSet, across one or more block sizes.
Attributes:
testSet: The test set these results are linked to.
records: The list of test results.
"""
def __init__(self, testSet):
"""
Create a new empty ResultSet.
Args:
testSet (TestSet): The test set these results are linked to.
"""
self.testSet = testSet
self.records = []
def add_record(self, record):
"""
Add a new test record to this result set.
Args:
record (Record): The test record to add.
"""
self.records.append(record)
def get_record(self, testSet, blkSz, name):
"""
Get a record matching the arguments.
Args:
testSet (TestSet): The test set to get results from.
blkSz (str): The block size.
name (str): The test name.
Returns:
Record: The test result, if present.
Raises:
KeyError: No match could be found.
"""
if testSet != self.testSet:
raise KeyError()
for record in self.records:
if record.blkSz == blkSz and record.name == name:
return record
raise KeyError()
def get_matching_record(self, other):
"""
Get a record matching the config of another record.
Args:
other (Record): The pattern result record to match.
Returns:
Record: The result, if present.
Raises:
KeyError: No match could be found.
"""
for record in self.records:
if record.blkSz == other.blkSz and record.name == other.name:
return record
raise KeyError()
def get_results_summary(self):
"""
Get a results summary of all the records in this result set.
Returns:
ResultSummary: The result summary.
"""
summary = ResultSummary()
for record in self.records:
summary.add_record(record)
return summary
def save_to_file(self, filePath):
"""
Save this result set to a CSV file.
Args:
filePath (str): The output file path.
"""
dirName = os.path.dirname(filePath)
if not os.path.exists(dirName):
os.makedirs(dirName)
with open(filePath, "w") as csvfile:
writer = csv.writer(csvfile)
self._save_header(writer)
for record in self.records:
self._save_record(writer, record)
@staticmethod
def _save_header(writer):
"""
Write the header to the CSV file.
Args:
writer (csv.writer): The CSV writer.
"""
row = ["Image Set", "Block Size", "Name",
"PSNR", "Total Time", "Coding Time", "Coding Rate"]
writer.writerow(row)
def _save_record(self, writer, record):
"""
Write a record to the CSV file.
Args:
writer (csv.writer): The CSV writer.
record (Record): The record to write.
"""
row = [self.testSet,
record.blkSz,
record.name,
"%0.4f" % record.psnr,
"%0.4f" % record.tTime,
"%0.4f" % record.cTime,
"%0.4f" % record.cRate]
writer.writerow(row)
def load_from_file(self, filePath):
"""
Load a reference result set from a CSV file on disk.
Args:
filePath (str): The input file path.
"""
with open(filePath, "r") as csvfile:
reader = csv.reader(csvfile)
# Skip the header
next(reader)
for row in reader:
assert row[0] == self.testSet
record = Record(row[1], row[2],
float(row[3]), float(row[4]),
float(row[5]), float(row[6]))
self.add_record(record)
|