Spaces:
Running
on
Zero
Running
on
Zero
File size: 8,789 Bytes
c28dddb |
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 |
"""
This file computes the IoU-based and centroid-distance-based metrics in a symmetric manner\n
"""
import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
import numpy as np
from copy import deepcopy
from objects.dict_utils import (
get_base_part_idx,
get_bbox_vertices,
remove_handles,
compute_overall_bbox_size,
rescale_object,
find_part_mapping,
zero_center_object,
)
from objects.motions import transform_all_parts
from metrics.giou import sampling_giou, sampling_cDist
def _get_scores(
src_dict,
tgt_dict,
original_src_bbox_vertices,
original_tgt_bbox_vertices,
mapping,
num_states,
rotation_fix_range,
num_samples,
iou_include_base,
):
# Record the indices of the base parts of the src objects
src_base_idx = get_base_part_idx(src_dict)
# Compute the sum of IoU between the generated object and the candidate object over a number of articulation states
num_parts_in_src = len(src_dict["diffuse_tree"])
iou_per_part_and_state = np.zeros((num_parts_in_src, num_states), dtype=np.float32)
cDist_per_part_and_state = np.zeros(
(num_parts_in_src, num_states), dtype=np.float32
)
states = np.linspace(0, 1, num_states)
for state_idx, state in enumerate(states):
# Get a fresh copy of the bounding box vertices in rest pose
src_bbox_vertices = deepcopy(original_src_bbox_vertices)
tgt_bbox_vertices = deepcopy(original_tgt_bbox_vertices)
# Transform the objects to the current state using the joints
src_part_transfomrations = transform_all_parts(
src_bbox_vertices,
src_dict,
state,
rotation_fix_range=rotation_fix_range,
)
tgt_part_transfomrations = transform_all_parts(
tgt_bbox_vertices,
tgt_dict,
state,
rotation_fix_range=rotation_fix_range,
)
# Compute the IoU between the two objects using the transformed bounding boxes and the part mapping
for src_part_idx in range(num_parts_in_src):
# Get the index of the corresponding part in the candidate object
tgt_part_idx = int(mapping[src_part_idx, 0])
# Always use a fresh copy of the bounding box vertices in rest pose in case dry_run=False is incorrectly set
src_part_bbox_vertices = deepcopy(original_src_bbox_vertices)[src_part_idx]
tgt_part_bbox_vertices = deepcopy(original_tgt_bbox_vertices)[tgt_part_idx]
# Compute the sampling-based IoU between the two parts
iou_per_part_and_state[src_part_idx, state_idx] = sampling_giou(
src_part_bbox_vertices,
tgt_part_bbox_vertices,
src_part_transfomrations[src_part_idx],
tgt_part_transfomrations[tgt_part_idx],
num_samples=num_samples,
)
# Compute the centriod distance between the two matched parts
cDist_per_part_and_state[src_part_idx, state_idx] = sampling_cDist(
src_dict["diffuse_tree"][src_part_idx],
tgt_dict["diffuse_tree"][tgt_part_idx],
src_part_transfomrations[src_part_idx],
tgt_part_transfomrations[tgt_part_idx],
)
# IoU and cDist at the resting state
per_part_iou_avg_at_rest = iou_per_part_and_state[:, 0]
per_part_cDist_avg_at_rest = cDist_per_part_and_state[:, 0]
# Average the IoU over the states
per_part_iou_avg_over_states = np.sum(iou_per_part_and_state, axis=1) / num_states
# Average the cDist over the states
per_part_cDist_avg_over_states = (
np.sum(cDist_per_part_and_state, axis=1) / num_states
)
# Remove the base part if specified
if not iou_include_base:
per_part_iou_avg_over_states = np.delete(
per_part_iou_avg_over_states, src_base_idx
)
per_part_iou_avg_at_rest = np.delete(per_part_iou_avg_at_rest, src_base_idx)
per_part_cDist_avg_over_states = np.delete(
per_part_cDist_avg_over_states, src_base_idx
)
per_part_cDist_avg_at_rest = np.delete(per_part_cDist_avg_at_rest, src_base_idx)
aid_iou = float(np.mean(per_part_iou_avg_over_states)) if len(per_part_iou_avg_over_states) > 0 else 0
aid_cdist = float(np.mean(per_part_cDist_avg_over_states)) if len(per_part_cDist_avg_over_states) > 0 else 1
rid_iou = float(np.mean(per_part_iou_avg_at_rest)) if len(per_part_iou_avg_at_rest) > 0 else 0
rid_cdist = float(np.mean(per_part_cDist_avg_at_rest)) if len(per_part_cDist_avg_at_rest) > 0 else 1
return {
"AS-IoU": 1. - aid_iou,
"AS-cDist": aid_cdist,
"RS-IoU": 1. - rid_iou,
"RS-cDist": rid_cdist
}
def IoU_cDist(
gen_obj_dict,
gt_obj_dict,
num_states=2,
compare_handles=False,
iou_include_base=False,
rotation_fix_range=True,
num_samples=10000,
):
"""
Compute the IoU-based and centroid-distance-based metrics\n
This metric is the average sum of IoU between parts in the two objects over the sampled articulation states and at the resting state\n
- gen_obj_dict: the dictionary of the generated object\n
- gt_obj_dict: the dictionary of the gt object\n
- num_states: the number of articulation states to compute the metric\n
- compare_handles (optional): whether to compare the handles\n
- iou_include_base (optional): whether to include the base part in the IoU computation\n
- rotation_fix_range (optional): whether to fix the rotation range to 90 degrees for revolute joints\n
- num_samples (optional): the number of samples to use\n
Return:\n
- scores: a dictionary of the computed scores\n
- "AS-IoU": the average IoU over the articulation states\n
- "AS-cDist": the average centroid distance over the articulation states\n
- "RS-IoU": the average IoU at the resting state\n
- "RS-cDist": the average centroid distance at the resting state\n
"""
# Make copies of the dictionaries to avoid modifying the original dictionaries
gen_dict = deepcopy(gen_obj_dict)
gt_dict = deepcopy(gt_obj_dict)
# Strip the handles from the object if not comparing them
if not compare_handles:
gen_dict = remove_handles(gen_dict)
gt_dict = remove_handles(gt_dict)
# Zero center the objects
zero_center_object(gen_dict)
zero_center_object(gt_dict)
# scale the generated object as a whole to match the size of the gt object
gen_bbox_size = compute_overall_bbox_size(gen_dict)
gt_bbox_size = compute_overall_bbox_size(gt_dict)
scale_factor = gt_bbox_size / gen_bbox_size
rescale_object(gen_dict, scale_factor)
mapping_gen2gt = find_part_mapping(gen_dict, gt_dict, use_hungarian=True)
# for i in range(mapping_gen2gt.shape[0]):
# if mapping_gen2gt[i][0] < 100:
# gen_dict['diffuse_tree'][i]["parent"] = gt_dict['diffuse_tree'][int(mapping_gen2gt[i][0])]["parent"]
# gen_dict['diffuse_tree'][i]["children"] = gt_dict['diffuse_tree'][int(mapping_gen2gt[i][0])]["children"]
# gen_dict['diffuse_tree'][i]["id"] = gt_dict['diffuse_tree'][int(mapping_gen2gt[i][0])]["id"]
# mapping_gen2gt = find_part_mapping(gen_dict, gt_dict, use_hungarian=True)
mapping_gt2gen = find_part_mapping(gt_dict, gen_dict, use_hungarian=True)
# Save the original bounding box vertices in rest pose
original_gen_bbox_vertices = np.array(
[get_bbox_vertices(gen_dict, i) for i in range(len(gen_dict["diffuse_tree"]))],
dtype=np.float32,
)
original_gt_bbox_vertices = np.array(
[get_bbox_vertices(gt_dict, i) for i in range(len(gt_dict["diffuse_tree"]))],
dtype=np.float32,
)
# import ipdb
# ipdb.set_trace()
scores_gen2gt = _get_scores(
gen_dict,
gt_dict,
original_gen_bbox_vertices,
original_gt_bbox_vertices,
mapping_gen2gt,
num_states,
rotation_fix_range,
num_samples,
iou_include_base,
)
scores_gt2gen = _get_scores(
gt_dict,
gen_dict,
original_gt_bbox_vertices,
original_gen_bbox_vertices,
mapping_gt2gen,
num_states,
rotation_fix_range,
num_samples,
iou_include_base,
)
scores = {
"AS-IoU": (scores_gen2gt["AS-IoU"] + scores_gt2gen["AS-IoU"]) / 2,
"AS-cDist": (scores_gen2gt["AS-cDist"] + scores_gt2gen["AS-cDist"]) / 2,
"RS-IoU": (scores_gen2gt["RS-IoU"] + scores_gt2gen["RS-IoU"]) / 2,
"RS-cDist": (scores_gen2gt["RS-cDist"] + scores_gt2gen["RS-cDist"]) / 2,
}
return scores
|