blob: 8d6777a0fb434c131705768fe2d6b67d3705206e [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS. All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
"""Export the scores computed by the apm_quality_assessment.py script into an
HTML file.
"""
import argparse
import collections
import logging
import glob
import os
import re
import sys
import quality_assessment.audioproc_wrapper as audioproc_wrapper
import quality_assessment.data_access as data_access
import quality_assessment.export as export
# Regular expressions used to derive score descriptors from file paths.
RE_CONFIG_NAME = re.compile(r'cfg-(.+)')
RE_INPUT_NAME = re.compile(r'input-(.+)')
RE_TEST_DATA_GEN_NAME = re.compile(r'gen-(.+)')
RE_SCORE_NAME = re.compile(r'score-(.+)\.txt')
def _InstanceArgumentsParser():
"""Arguments parser factory.
"""
parser = argparse.ArgumentParser(description=(
'Exports pre-computed APM module quality assessment results into HTML '
'tables.'))
parser.add_argument('-o', '--output_dir', required=True,
help=('the same base path used with the '
'apm_quality_assessment tool'))
parser.add_argument('-f', '--filename_suffix',
help=('suffix of the exported file'))
parser.add_argument('-c', '--config_names', type=re.compile,
help=('regular expression to filter the APM configuration'
' names'))
parser.add_argument('-i', '--input_names', type=re.compile,
help=('regular expression to filter the probing signal '
'names'))
parser.add_argument('-t', '--test_data_generators', type=re.compile,
help=('regular expression to filter the test data '
'generator names'))
parser.add_argument('-e', '--eval_scores', type=re.compile,
help=('regular expression to filter the evaluation score '
'names'))
return parser
def _GetScoreDescriptors(score_filepath):
"""Extracts a score descriptor from the given score file path.
Args:
score_filepath: path to the score file.
Returns:
A tuple of strings (APM configuration name, input audio track name,
test data generator name, test data generator parameters name,
evaluation score name).
"""
(config_name, input_name, test_data_gen_name, test_data_gen_params,
score_name) = score_filepath.split(os.sep)[-5:]
config_name = RE_CONFIG_NAME.match(config_name).groups(0)[0]
input_name = RE_INPUT_NAME.match(input_name).groups(0)[0]
test_data_gen_name = RE_TEST_DATA_GEN_NAME.match(
test_data_gen_name).groups(0)[0]
score_name = RE_SCORE_NAME.match(score_name).groups(0)[0]
return (config_name, input_name, test_data_gen_name, test_data_gen_params,
score_name)
def _ExcludeScore(config_name, input_name, test_data_gen_name, score_name,
args):
"""Decides whether excluding a score.
Given a score descriptor, encoded in config_name, input_name,
test_data_gen_name and score_name, use the corresponding regular expressions
to determine if the score should be excluded.
Args:
config_name: APM configuration name.
input_name: input audio track name.
test_data_gen_name: test data generator name.
score_name: evaluation score name.
args: parsed arguments.
Returns:
A boolean.
"""
value_regexpr_pairs = [
(config_name, args.config_names),
(input_name, args.input_names),
(test_data_gen_name, args.test_data_generators),
(score_name, args.eval_scores),
]
# Score accepted if each value matches the corresponding regular expression.
for value, regexpr in value_regexpr_pairs:
if regexpr is None:
continue
if not regexpr.match(value):
return True
return False
def _BuildOutputFilename(filename_suffix):
"""Builds the filename for the exported file.
Args:
filename_suffix: suffix for the output file name.
Returns:
A string.
"""
if filename_suffix is None:
return 'results.html'
return 'results-{}.html'.format(filename_suffix)
def main():
# Init.
logging.basicConfig(level=logging.DEBUG) # TODO(alessio): INFO once debugged.
parser = _InstanceArgumentsParser()
nested_dict = lambda: collections.defaultdict(nested_dict)
scores = nested_dict() # Organize the scores in a nested dictionary.
# Parse command line arguments.
args = parser.parse_args()
# Find score files in the output path.
src_path = os.path.join(
args.output_dir, 'cfg-*', 'input-*', 'gen-*', '*', 'score-*.txt')
logging.debug(src_path)
for score_filepath in glob.iglob(src_path):
# Extract score descriptors from the path.
(config_name, input_name, test_data_gen_name, test_data_gen_params,
score_name) = _GetScoreDescriptors(score_filepath)
# Ignore the score if required.
if _ExcludeScore(
config_name, input_name, test_data_gen_name, score_name, args):
logging.info('ignored score: %s %s %s %s',
config_name, input_name, test_data_gen_name, score_name)
continue
# Get metadata.
score_path, _ = os.path.split(score_filepath)
audio_in_filepath, audio_ref_filepath = (
data_access.Metadata.LoadAudioTestDataPaths(score_path))
audio_out_filepath = os.path.abspath(os.path.join(
score_path, audioproc_wrapper.AudioProcWrapper.OUTPUT_FILENAME))
# Add the score to the nested dictionary.
scores[score_name][config_name][input_name][test_data_gen_name][
test_data_gen_params] = {
'score': data_access.ScoreFile.Load(score_filepath),
'audio_in_filepath': audio_in_filepath,
'audio_out_filepath': audio_out_filepath,
'audio_ref_filepath': audio_ref_filepath,
}
# Export.
output_filepath = os.path.join(args.output_dir, _BuildOutputFilename(
args.filename_suffix))
exporter = export.HtmlExport(output_filepath)
exporter.Export(scores)
logging.info('output file successfully written in %s', output_filepath)
sys.exit(0)
if __name__ == '__main__':
main()