|
import os |
|
import sys |
|
from pathlib import Path |
|
if (_package_root := str(Path(__file__).absolute().parents[2])) not in sys.path: |
|
sys.path.insert(0, _package_root) |
|
import json |
|
from typing import * |
|
import importlib |
|
import importlib.util |
|
|
|
import click |
|
|
|
|
|
@click.command(context_settings={"allow_extra_args": True, "ignore_unknown_options": True}, help='Evaluation script.') |
|
@click.option('--baseline', 'baseline_code_path', type=click.Path(), required=True, help='Path to the baseline model python code.') |
|
@click.option('--config', 'config_path', type=click.Path(), default='configs/eval/all_benchmarks.json', help='Path to the evaluation configurations. ' |
|
'Defaults to "configs/eval/all_benchmarks.json".') |
|
@click.option('--output', '-o', 'output_path', type=click.Path(), required=True, help='Path to the output json file.') |
|
@click.option('--oracle', 'oracle_mode', is_flag=True, help='Use oracle mode for evaluation, i.e., use the GT intrinsics input.') |
|
@click.option('--dump_pred', is_flag=True, help='Dump predition results.') |
|
@click.option('--dump_gt', is_flag=True, help='Dump ground truth.') |
|
@click.pass_context |
|
def main(ctx: click.Context, baseline_code_path: str, config_path: str, oracle_mode: bool, output_path: Union[str, Path], dump_pred: bool, dump_gt: bool): |
|
|
|
import cv2 |
|
import numpy as np |
|
from tqdm import tqdm |
|
import torch |
|
import torch.nn.functional as F |
|
import utils3d |
|
|
|
from moge.test.baseline import MGEBaselineInterface |
|
from moge.test.dataloader import EvalDataLoaderPipeline |
|
from moge.test.metrics import compute_metrics |
|
from moge.utils.geometry_torch import intrinsics_to_fov |
|
from moge.utils.vis import colorize_depth, colorize_normal |
|
from moge.utils.tools import key_average, flatten_nested_dict, timeit, import_file_as_module |
|
|
|
|
|
module = import_file_as_module(baseline_code_path, Path(baseline_code_path).stem) |
|
baseline_cls: Type[MGEBaselineInterface] = getattr(module, 'Baseline') |
|
baseline : MGEBaselineInterface = baseline_cls.load.main(ctx.args, standalone_mode=False) |
|
|
|
|
|
with open(config_path, 'r') as f: |
|
config = json.load(f) |
|
|
|
Path(output_path).parent.mkdir(parents=True, exist_ok=True) |
|
all_metrics = {} |
|
|
|
for benchmark_name, benchmark_config in tqdm(list(config.items()), desc='Benchmarks'): |
|
filenames, metrics_list = [], [] |
|
with ( |
|
EvalDataLoaderPipeline(**benchmark_config) as eval_data_pipe, |
|
tqdm(total=len(eval_data_pipe), desc=benchmark_name, leave=False) as pbar |
|
): |
|
|
|
for i in range(len(eval_data_pipe)): |
|
sample = eval_data_pipe.get() |
|
sample = {k: v.to(baseline.device) if isinstance(v, torch.Tensor) else v for k, v in sample.items()} |
|
image = sample['image'] |
|
gt_intrinsics = sample['intrinsics'] |
|
|
|
|
|
torch.cuda.synchronize() |
|
with torch.inference_mode(), timeit('_inference_timer', verbose=False) as timer: |
|
if oracle_mode: |
|
pred = baseline.infer_for_evaluation(image, gt_intrinsics) |
|
else: |
|
pred = baseline.infer_for_evaluation(image) |
|
torch.cuda.synchronize() |
|
|
|
|
|
metrics, misc = compute_metrics(pred, sample, vis=dump_pred or dump_gt) |
|
metrics['inference_time'] = timer.time |
|
metrics_list.append(metrics) |
|
|
|
|
|
dump_path = Path(output_path.replace(".json", f"_dump"), f'{benchmark_name}', sample['filename'].replace('.zip', '')) |
|
if dump_pred: |
|
dump_path.joinpath('pred').mkdir(parents=True, exist_ok=True) |
|
cv2.imwrite(str(dump_path / 'pred' / 'image.jpg'), cv2.cvtColor((image.cpu().numpy().transpose(1, 2, 0) * 255).astype(np.uint8), cv2.COLOR_RGB2BGR)) |
|
|
|
with Path(dump_path, 'pred', 'metrics.json').open('w') as f: |
|
json.dump(metrics, f, indent=4) |
|
|
|
if 'pred_points' in misc: |
|
points = misc['pred_points'].cpu().numpy() |
|
cv2.imwrite(str(dump_path / 'pred' / 'points.exr'), cv2.cvtColor(points.astype(np.float32), cv2.COLOR_RGB2BGR), [cv2.IMWRITE_EXR_TYPE, cv2.IMWRITE_EXR_TYPE_FLOAT]) |
|
|
|
if 'pred_depth' in misc: |
|
depth = misc['pred_depth'].cpu().numpy() |
|
if 'mask' in pred: |
|
mask = pred['mask'].cpu().numpy() |
|
depth = np.where(mask, depth, np.inf) |
|
cv2.imwrite(str(dump_path / 'pred' / 'depth.png'), cv2.cvtColor(colorize_depth(depth), cv2.COLOR_RGB2BGR)) |
|
|
|
if 'mask' in pred: |
|
mask = pred['mask'].cpu().numpy() |
|
cv2.imwrite(str(dump_path / 'pred' / 'mask.png'), (mask * 255).astype(np.uint8)) |
|
|
|
if 'normal' in pred: |
|
normal = pred['normal'].cpu().numpy() |
|
cv2.imwrite(str(dump_path / 'pred' / 'normal.png'), cv2.cvtColor(colorize_normal(normal), cv2.COLOR_RGB2BGR)) |
|
|
|
if 'intrinsics' in pred: |
|
intrinsics = pred['intrinsics'] |
|
fov_x, fov_y = intrinsics_to_fov(intrinsics) |
|
with open(dump_path / 'pred' / 'fov.json', 'w') as f: |
|
json.dump({ |
|
'fov_x': np.rad2deg(fov_x.item()), |
|
'fov_y': np.rad2deg(fov_y.item()), |
|
'intrinsics': intrinsics.cpu().numpy().tolist(), |
|
}, f) |
|
|
|
if dump_gt: |
|
dump_path.joinpath('gt').mkdir(parents=True, exist_ok=True) |
|
cv2.imwrite(str(dump_path / 'gt' / 'image.jpg'), cv2.cvtColor((image.cpu().numpy().transpose(1, 2, 0) * 255).astype(np.uint8), cv2.COLOR_RGB2BGR)) |
|
|
|
if 'points' in sample: |
|
points = sample['points'] |
|
cv2.imwrite(str(dump_path / 'gt' / 'points.exr'), cv2.cvtColor(points.cpu().numpy().astype(np.float32), cv2.COLOR_RGB2BGR), [cv2.IMWRITE_EXR_TYPE, cv2.IMWRITE_EXR_TYPE_FLOAT]) |
|
|
|
if 'depth' in sample: |
|
depth = sample['depth'] |
|
mask = sample['depth_mask'] |
|
cv2.imwrite(str(dump_path / 'gt' / 'depth.png'), cv2.cvtColor(colorize_depth(depth.cpu().numpy(), mask=mask.cpu().numpy()), cv2.COLOR_RGB2BGR)) |
|
|
|
if 'normal' in sample: |
|
normal = sample['normal'] |
|
cv2.imwrite(str(dump_path / 'gt' / 'normal.png'), cv2.cvtColor(colorize_normal(normal.cpu().numpy()), cv2.COLOR_RGB2BGR)) |
|
|
|
if 'depth_mask' in sample: |
|
mask = sample['depth_mask'] |
|
cv2.imwrite(str(dump_path / 'gt' /'mask.png'), (mask.cpu().numpy() * 255).astype(np.uint8)) |
|
|
|
if 'intrinsics' in sample: |
|
intrinsics = sample['intrinsics'] |
|
fov_x, fov_y = intrinsics_to_fov(intrinsics) |
|
with open(dump_path / 'gt' / 'info.json', 'w') as f: |
|
json.dump({ |
|
'fov_x': np.rad2deg(fov_x.item()), |
|
'fov_y': np.rad2deg(fov_y.item()), |
|
'intrinsics': intrinsics.cpu().numpy().tolist(), |
|
}, f) |
|
|
|
|
|
if i % 100 == 0 or i == len(eval_data_pipe) - 1: |
|
Path(output_path).write_text( |
|
json.dumps({ |
|
**all_metrics, |
|
benchmark_name: key_average(metrics_list) |
|
}, indent=4) |
|
) |
|
pbar.update(1) |
|
|
|
all_metrics[benchmark_name] = key_average(metrics_list) |
|
|
|
|
|
all_metrics['mean'] = key_average(list(all_metrics.values())) |
|
Path(output_path).write_text(json.dumps(all_metrics, indent=4)) |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |
|
|