Spaces:
Running
on
Zero
Running
on
Zero
# Copyright (c) 2025 Ye Liu. Licensed under the BSD-3-Clause License. | |
import argparse | |
import nncore | |
import torch | |
from nncore.ops import temporal_area, temporal_intersection, temporal_iof, temporal_iou | |
from tabulate import tabulate | |
class SafeInt(int): | |
def __truediv__(self, other): | |
try: | |
return SafeInt(super().__truediv__(other)) | |
except ZeroDivisionError: | |
return SafeInt(0) | |
def check_ans(options, ans, response): | |
a = ans.lower() | |
b = response.lower().split(' ')[0].replace('(', '').replace(')', '').replace('.', '') | |
if len(b) != 1: | |
b = b[0] | |
nncore.log(f'WARNING: {response} -> {b}') | |
if b not in [chr(ord('a') + i) for i in range(len(options))]: | |
nncore.log(f'ERROR: {response} -> {b}') | |
return | |
return a == b | |
def compute_iou(pred, span, conf, cgbench_mode, conf_thr): | |
pred_tensor = torch.Tensor(pred) | |
span_tensor = torch.Tensor(span) | |
if cgbench_mode: | |
if conf_thr > 0: | |
conf_tensor = torch.Tensor(conf) | |
keep = torch.cat((torch.LongTensor([0]), torch.where(conf_tensor > conf_thr)[0])).unique() | |
pred_tensor = pred_tensor[keep] | |
else: | |
pred_tensor = pred_tensor[:1] | |
pred_area = temporal_area(pred_tensor).sum() | |
span_area = temporal_area(span_tensor).sum() | |
inter = temporal_intersection(pred_tensor, span_tensor).sum() | |
iou = (inter / (pred_area + span_area - inter)).unsqueeze(0) | |
assert iou.numel() == 1 | |
else: | |
iou = temporal_iou(pred_tensor, span_tensor) | |
iou = torch.where(iou.isfinite(), iou, 0) | |
return iou | |
def parse_args(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('pred_path') | |
parser.add_argument('--dataset') | |
parser.add_argument('--out_name', default='metrics.log') | |
parser.add_argument('--conf_thr', type=float, default=-1) | |
args = parser.parse_args() | |
return args | |
if __name__ == '__main__': | |
args = parse_args() | |
assert nncore.is_dir(args.pred_path) | |
log_file = nncore.join(args.pred_path, args.out_name) | |
nncore.set_default_logger(logger='eval', fmt=None, log_file=log_file) | |
if args.dataset is not None: | |
cgbench_mode = args.dataset == 'cgbench' | |
nncore.log(f'CG-Bench mode: {cgbench_mode}') | |
else: | |
cgbench_mode = False | |
nncore.log('Dataset is unknown, using default mode', log_level='WARNING') | |
pred_paths = nncore.ls(args.pred_path, ext=['json', 'jsonl'], join_path=True) | |
nncore.log(f'Total number of files: {len(pred_paths)}') | |
if cgbench_mode: | |
top_k = [1] | |
thres = [0.1, 0.2, 0.3, 0.4, 0.5] | |
else: | |
top_k = [1, 3, 5] | |
thres = [0.3, 0.5, 0.7] | |
tab_iou, tab_iop, tab_ans = dict(), dict(), dict() | |
iou_raise, iou_lower, iop_raise, iop_lower = SafeInt(0), SafeInt(0), SafeInt(0), SafeInt(0) | |
tab_iou_all = [SafeInt(0) for _ in range(len(top_k) * len(thres) + 3)] | |
tab_iop_all = [SafeInt(0) for _ in range(len(top_k) * len(thres) + 3)] | |
tab_ans_all = [SafeInt(0) for _ in range(len(thres) + 5)] | |
for path in pred_paths: | |
data = nncore.load(path) | |
for sample in data: | |
task = sample.get('task', 'unknown') | |
# samples in lvbench might have multiple tasks | |
if isinstance(task, str): | |
task = [task] | |
for t in task: | |
if t not in tab_iou: | |
tab_iou[t] = [SafeInt(0) for _ in range(len(top_k) * len(thres) + 3)] | |
if t not in tab_iop: | |
tab_iop[t] = [SafeInt(0) for _ in range(len(top_k) * len(thres) + 3)] | |
if t not in tab_ans: | |
tab_ans[t] = [SafeInt(0) for _ in range(len(thres) + 5)] | |
iou_hit = [False for _ in range(len(thres) + 1)] | |
iop_hit = False | |
if 'pred' in sample and 'conf' in sample and 'span' in sample: | |
for t in task: | |
tab_iou[t][0] += 1 | |
tab_iop[t][0] += 1 | |
tab_iou_all[0] += 1 | |
tab_iop_all[0] += 1 | |
iou = compute_iou(sample['pred'], sample['span'], sample['conf'], cgbench_mode, args.conf_thr) | |
top = iou[0].max().item() | |
for t in task: | |
tab_iou[t][-1] += top | |
tab_iou_all[-1] += top | |
for i, k in enumerate(top_k): | |
for j, h in enumerate(thres): | |
if iou[:k].max() >= h: | |
for t in task: | |
tab_iou[t][i * len(thres) + j + 2] += 1 | |
tab_iou_all[i * len(thres) + j + 2] += 1 | |
if k == 1: | |
iou_hit[j + 1] = True | |
if h == 0.5: | |
iou_hit[0] = True | |
if sample.get('pred_ori') is not None: | |
iou = compute_iou(sample['pred_ori'], sample['span'], sample['conf_ori'], cgbench_mode, | |
args.conf_thr) | |
iou = iou[0].max().item() | |
if iou < top: | |
iou_raise += 1 | |
if iou > top: | |
iou_lower += 1 | |
iop = temporal_iof(torch.Tensor(sample['pred']), torch.Tensor(sample['span'])) | |
iop = torch.where(iop.isfinite(), iop, 0) | |
top = iop[0].max().item() | |
for t in task: | |
tab_iop[t][-1] += top | |
tab_iop_all[-1] += top | |
for i, k in enumerate(top_k): | |
for j, h in enumerate(thres): | |
if iop[:k].max() >= h: | |
for t in task: | |
tab_iop[t][i * len(thres) + j + 2] += 1 | |
tab_iop_all[i * len(thres) + j + 2] += 1 | |
if k == 1 and h == 0.5: | |
iop_hit = True | |
if sample.get('pred_ori') is not None: | |
iop = temporal_iof(torch.Tensor(sample['pred_ori']), torch.Tensor(sample['span'])) | |
iop = torch.where(iop.isfinite(), iop, 0) | |
iop = iop[0].max().item() | |
if iop < top: | |
iop_raise += 1 | |
if iop > top: | |
iop_lower += 1 | |
if not sample.get('grounder_success', True): | |
for t in task: | |
tab_iou[t][1] += 1 | |
tab_iop[t][1] += 1 | |
tab_iou_all[1] += 1 | |
tab_iop_all[1] += 1 | |
if 'question' in sample and 'response' in sample: | |
for t in task: | |
tab_ans[t][0] += 1 | |
tab_ans_all[0] += 1 | |
correct = check_ans(sample['options'], sample['ans'], sample['response']) | |
if correct: | |
for t in task: | |
tab_ans[t][2] += 1 | |
tab_ans_all[2] += 1 | |
if iou_hit[0]: | |
for t in task: | |
tab_ans[t][3] += 1 | |
tab_ans_all[3] += 1 | |
if iop_hit: | |
for t in task: | |
tab_ans[t][4] += 1 | |
tab_ans_all[4] += 1 | |
for i in range(1, len(iou_hit)): | |
if iou_hit[i]: | |
for t in task: | |
tab_ans[t][i + 4] += 1 | |
tab_ans_all[i + 4] += 1 | |
elif correct is None: | |
for t in task: | |
tab_ans[t][1] += 1 | |
tab_ans_all[1] += 1 | |
tasks = sorted(list(set(list(tab_iou.keys()) + list(tab_iop.keys()) + list(tab_ans.keys())))) | |
if cgbench_mode: | |
nncore.log('\nGrounding (IoU):') | |
tab = tabulate( | |
[[task, tab_iou[task][0], tab_iou[task][1]] + | |
[f'{tab_iou[task][i] / tab_iou[task][0] * 100:.2f}' for i in range(2, len(tab_iou[task]))] + | |
[f'{sum(tab_iou[task][i] / tab_iou[task][0] for i in range(2, 2 + len(thres))) / len(thres) * 100:.2f}'] | |
for task in tasks if task in tab_iou] + | |
[['all', tab_iou_all[0], tab_iou_all[1]] + | |
[f'{tab_iou_all[i] / tab_iou_all[0] * 100:.2f}' for i in range(2, len(tab_iou_all))] + | |
[f'{sum(tab_iou_all[i] / tab_iou_all[0] for i in range(2, 2 + len(thres))) / len(thres) * 100:.2f}']], | |
headers=['Task', '#Samples', 'Failed'] + [f'R{k}@{t}' for k in top_k for t in thres] + ['mIoU', 'rec.@IoU'], | |
tablefmt='pretty', | |
stralign='left') | |
nncore.log(tab) | |
nncore.log(f'\nIoU Raise ({tab_iou_all[0]} Samples): {iou_raise} ({iou_raise / tab_iou_all[0] * 100:.2f}%)') | |
nncore.log(f'IoU Lower ({tab_iou_all[0]} Samples): {iou_lower} ({iou_lower / tab_iou_all[0] * 100:.2f}%)') | |
nncore.log('\nQA:') | |
tab = tabulate( | |
[[task, tab_ans[task][0], tab_ans[task][1], f'{tab_ans[task][2] / tab_ans[task][0] * 100:.2f}'] + | |
[f'{sum(tab_ans[task][i] / tab_ans[task][0] for i in range(5, 5 + len(thres))) / len(thres) * 100:.2f}'] | |
for task in tasks if task in tab_ans] + | |
[['all', tab_ans_all[0], tab_ans_all[1], f'{tab_ans_all[2] / tab_ans_all[0] * 100:.2f}'] + | |
[f'{sum(tab_ans_all[i] / tab_ans_all[0] for i in range(5, 5 + len(thres))) / len(thres) * 100:.2f}']], | |
headers=['Task', '#Samples', 'Failed', 'long-acc.', 'acc.@IoU'], | |
tablefmt='pretty', | |
stralign='left') | |
nncore.log(tab) | |
else: | |
nncore.log('\nGrounding (IoU):') | |
tab = tabulate( | |
[[task, tab_iou[task][0], tab_iou[task][1]] + | |
[f'{tab_iou[task][i] / tab_iou[task][0] * 100:.2f}' for i in range(2, len(tab_iou[task]))] | |
for task in tasks if task in tab_iou] + | |
[['all', tab_iou_all[0], tab_iou_all[1]] + | |
[f'{tab_iou_all[i] / tab_iou_all[0] * 100:.2f}' for i in range(2, len(tab_iou_all))]], | |
headers=['Task', '#Samples', 'Failed'] + [f'R{k}@{t}' for k in top_k for t in thres] + ['mIoU'], | |
tablefmt='pretty', | |
stralign='left') | |
nncore.log(tab) | |
nncore.log(f'\nIoU Raise ({tab_iou_all[0]} Samples): {iou_raise} ({iou_raise / tab_iou_all[0] * 100:.2f}%)') | |
nncore.log(f'IoU Lower ({tab_iou_all[0]} Samples): {iou_lower} ({iou_lower / tab_iou_all[0] * 100:.2f}%)') | |
nncore.log('\nGrounding (IoP):') | |
tab = tabulate( | |
[[task, tab_iop[task][0], tab_iop[task][1]] + | |
[f'{tab_iop[task][i] / tab_iop[task][0] * 100:.2f}' for i in range(2, len(tab_iop[task]))] | |
for task in tasks if task in tab_iop] + | |
[['all', tab_iop_all[0], tab_iop_all[1]] + | |
[f'{tab_iop_all[i] / tab_iop_all[0] * 100:.2f}' for i in range(2, len(tab_iop_all))]], | |
headers=['Task', '#Samples', 'Failed'] + [f'R{k}@{t}' for k in top_k for t in thres] + ['mIoP'], | |
tablefmt='pretty', | |
stralign='left') | |
nncore.log(tab) | |
nncore.log(f'\nIoP Raise ({tab_iop_all[0]} Samples): {iop_raise} ({iop_raise / tab_iop_all[0] * 100:.2f}%)') | |
nncore.log(f'IoP Lower ({tab_iop_all[0]} Samples): {iop_lower} ({iop_lower / tab_iop_all[0] * 100:.2f}%)') | |
nncore.log('\nQA:') | |
tab = tabulate( | |
[[task, tab_ans[task][0], tab_ans[task][1]] + | |
[f'{tab_ans[task][i] / tab_ans[task][0] * 100:.2f}' for i in range(2, 5)] | |
for task in tasks if task in tab_ans] + | |
[['all', tab_ans_all[0], tab_ans_all[1]] + | |
[f'{tab_ans_all[i] / tab_ans_all[0] * 100:.2f}' for i in range(2, 5)]], | |
headers=['Task', '#Samples', 'Failed', 'Acc', 'Acc (IoU >= 0.5)', 'Acc (IoP >= 0.5)'], | |
tablefmt='pretty', | |
stralign='left') | |
nncore.log(tab) | |