|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r"""Evaluate the frame interpolation model from a tfrecord and store results. |
|
|
|
This script runs the inference on examples in a tfrecord and generates images |
|
and numeric results according to the gin config. For details, see the |
|
run_evaluation() function below. |
|
|
|
Usage example: |
|
python3 -m frame_interpolation.eval.eval_cli -- \ |
|
--gin_config <path to eval_dataset.gin> \ |
|
--base_folder <the root directory to all training sessions> \ |
|
--label < the foldername of the training session> |
|
|
|
or |
|
|
|
python3 -m frame_interpolation.eval.eval_cli -- \ |
|
--gin_config <path to eval_dataset.gin> \ |
|
--model_path <The filepath of the TF2 saved model> |
|
|
|
The output is saved at the parent directory of the `model_path`: |
|
<parent directory of model_path>/batch_eval. |
|
|
|
The evaluation is run on a GPU by default. Add the `--mode` argument for others. |
|
""" |
|
import collections |
|
import os |
|
from typing import Any, Dict |
|
|
|
from . import util |
|
from absl import app |
|
from absl import flags |
|
from absl import logging |
|
import gin.tf |
|
from ..losses import losses |
|
import numpy as np |
|
import tensorflow as tf |
|
from ..training import data_lib |
|
|
|
|
|
_GIN_CONFIG = flags.DEFINE_string('gin_config', None, 'Gin config file.') |
|
_LABEL = flags.DEFINE_string( |
|
'label', None, 'Descriptive label for the training session to eval.') |
|
_BASE_FOLDER = flags.DEFINE_string('base_folder', None, |
|
'Root folder of training sessions.') |
|
_MODEL_PATH = flags.DEFINE_string( |
|
name='model_path', |
|
default=None, |
|
help='The path of the TF2 saved model to use. If _MODEL_PATH argument is ' |
|
'directly specified, _LABEL and _BASE_FOLDER arguments will be ignored.') |
|
_OUTPUT_FRAMES = flags.DEFINE_boolean( |
|
name='output_frames', |
|
default=False, |
|
help='If true, saves the the inputs, groud-truth and interpolated frames.') |
|
_MODE = flags.DEFINE_enum('mode', 'gpu', ['cpu', 'gpu'], |
|
'Device to run evaluations.') |
|
|
|
|
|
@gin.configurable('experiment') |
|
def _get_experiment_config(name) -> Dict[str, Any]: |
|
"""Fetches the gin config.""" |
|
return { |
|
'name': name, |
|
} |
|
|
|
|
|
def _set_visible_devices(): |
|
"""Set the visible devices according to running mode.""" |
|
mode_devices = tf.config.list_physical_devices(_MODE.value.upper()) |
|
tf.config.set_visible_devices([], 'GPU') |
|
tf.config.set_visible_devices([], 'TPU') |
|
tf.config.set_visible_devices(mode_devices, _MODE.value.upper()) |
|
return |
|
|
|
|
|
@gin.configurable('evaluation') |
|
def run_evaluation(model_path, tfrecord, output_dir, max_examples, metrics): |
|
"""Runs the eval loop for examples in the tfrecord. |
|
|
|
The evaluation is run for the first 'max_examples' number of examples, and |
|
resulting images are stored into the given output_dir. Any tensor that |
|
appears like an image is stored with its name -- this may include intermediate |
|
results, depending on what the model outputs. |
|
|
|
Additionally, numeric results are stored into results.csv file within the same |
|
directory. This includes per-example metrics and the mean across the whole |
|
dataset. |
|
|
|
Args: |
|
model_path: Directory TF2 saved model. |
|
tfrecord: Directory to the tfrecord eval data. |
|
output_dir: Directory to store the results into. |
|
max_examples: Maximum examples to evaluate. |
|
metrics: The names of loss functions to use. |
|
""" |
|
model = tf.saved_model.load(model_path) |
|
|
|
|
|
with tf.io.gfile.GFile(os.path.join(output_dir, 'readme.txt'), mode='w') as f: |
|
print('Results for:', file=f) |
|
print(f' model: {model_path}', file=f) |
|
print(f' tfrecord: {tfrecord}', file=f) |
|
|
|
with tf.io.gfile.GFile( |
|
os.path.join(output_dir, 'results.csv'), mode='w') as csv_file: |
|
test_losses = losses.test_losses(metrics, [ |
|
1.0, |
|
] * len(metrics)) |
|
title_row = ['key'] + list(test_losses) |
|
print(', '.join(title_row), file=csv_file) |
|
|
|
datasets = data_lib.create_eval_datasets( |
|
batch_size=1, |
|
files=[tfrecord], |
|
names=[os.path.basename(output_dir)], |
|
max_examples=max_examples) |
|
dataset = datasets[os.path.basename(output_dir)] |
|
|
|
all_losses = collections.defaultdict(list) |
|
for example in dataset: |
|
inputs = { |
|
'x0': example['x0'], |
|
'x1': example['x1'], |
|
'time': example['time'][..., tf.newaxis], |
|
} |
|
prediction = model(inputs, training=False) |
|
|
|
|
|
path = example['path'][0].numpy().decode('utf-8') |
|
key = path.rsplit('.', 1)[0].rsplit(os.sep)[-1] |
|
|
|
|
|
combined = {**prediction, **example} if _OUTPUT_FRAMES.value else {} |
|
for name in combined: |
|
image = combined[name] |
|
if isinstance(image, tf.Tensor): |
|
|
|
|
|
|
|
|
|
if len(image.shape) == 4 and (image.shape[-1] == 1 or |
|
image.shape[-1] == 3): |
|
util.write_image( |
|
os.path.join(output_dir, f'{key}_{name}.png'), image[0].numpy()) |
|
|
|
|
|
|
|
if 'y' in example: |
|
loss_values = [] |
|
|
|
|
|
|
|
prediction['image'] = tf.clip_by_value(prediction['image'], 0., 1.) |
|
for loss_name, (loss_value_fn, loss_weight_fn) in test_losses.items(): |
|
loss_value = loss_value_fn(example, prediction) * loss_weight_fn(0) |
|
loss_values.append(loss_value.numpy()) |
|
all_losses[loss_name].append(loss_value.numpy()) |
|
print(f'{key}, {str(loss_values)[1:-1]}', file=csv_file) |
|
|
|
if all_losses: |
|
totals = [np.mean(all_losses[loss_name]) for loss_name in test_losses] |
|
print(f'mean, {str(totals)[1:-1]}', file=csv_file) |
|
totals_dict = { |
|
loss_name: np.mean(all_losses[loss_name]) for loss_name in test_losses |
|
} |
|
logging.info('mean, %s', totals_dict) |
|
|
|
|
|
def main(argv): |
|
if len(argv) > 1: |
|
raise app.UsageError('Too many command-line arguments.') |
|
|
|
if _MODEL_PATH.value is not None: |
|
model_path = _MODEL_PATH.value |
|
else: |
|
model_path = os.path.join(_BASE_FOLDER.value, _LABEL.value, 'saved_model') |
|
|
|
gin.parse_config_files_and_bindings( |
|
config_files=[_GIN_CONFIG.value], |
|
bindings=None, |
|
skip_unknown=True) |
|
|
|
config = _get_experiment_config() |
|
eval_name = config['name'] |
|
output_dir = os.path.join( |
|
os.path.dirname(model_path), 'batch_eval', eval_name) |
|
logging.info('Creating output_dir @ %s ...', output_dir) |
|
|
|
|
|
tf.io.gfile.makedirs(output_dir) |
|
tf.io.gfile.copy( |
|
_GIN_CONFIG.value, os.path.join(output_dir, 'config.gin'), overwrite=True) |
|
|
|
_set_visible_devices() |
|
logging.info('Evaluating %s on %s ...', eval_name, [ |
|
el.name.split('/physical_device:')[-1] |
|
for el in tf.config.get_visible_devices() |
|
]) |
|
run_evaluation(model_path=model_path, output_dir=output_dir) |
|
|
|
logging.info('Done. Evaluations saved @ %s.', output_dir) |
|
|
|
if __name__ == '__main__': |
|
app.run(main) |
|
|