FastCDM / fastcdm /matcher.py
BinyangQiu
first commit
ae1d809
Raw
History Blame Contribute Delete
7.51 kB
import cv2
import numpy as np
from scipy.spatial.distance import cdist
from scipy.optimize import linear_sum_assignment
class SimpleAffineTransform:
def __init__(self, translation=(0, 0), scale=1.0):
self.translation = np.array(translation)
self.scale = scale
def estimate(self, src, dst):
src_center = np.mean(src, axis=0)
dst_center = np.mean(dst, axis=0)
self.translation = dst_center - src_center
src_dists = np.linalg.norm(src - src_center, axis=1)
dst_dists = np.linalg.norm(dst - dst_center, axis=1)
self.scale = np.mean(dst_dists) / (np.mean(src_dists) + 1e-10)
def inverse(self):
return SimpleAffineTransform(-self.translation, 1.0 / self.scale)
def __call__(self, coords):
return (
self.scale * (coords - np.mean(coords, axis=0))
+ np.mean(coords, axis=0)
+ self.translation
)
def residuals(self, src, dst):
return np.sqrt(np.sum((self(src) - dst) ** 2, axis=1))
def norm_coords(x, left, right):
if x < left:
return left
if x > right:
return right
return x
def norm_same_token(token):
special_map = {
"\\dot": ".",
"\\Dot": ".",
"\\cdot": ".",
"\\cdotp": ".",
"\\ldotp": ".",
"\\mid": "|",
"\\rightarrow": "\\to",
"\\top": "T",
"\\Tilde": "\\tilde",
"\\prime": "'",
"\\ast": "*",
"\\left<": "\\langle",
"\\right>": "\\rangle",
"\\lbrace": "\{",
"\\rbrace": "\}",
"\\lbrack": "[",
"\\rbrack": "]",
"\\blackslash": "/",
"\\slash": "/",
"\\leq": "\\le",
"\\geq": "\\ge",
"\\neq": "\\ne",
"\\Vert": "\\|",
"\\lVert": "\\|",
"\\rVert": "\\|",
"\\vert": "|",
"\\lvert": "|",
"\\rvert": "|",
"\\colon": ":",
"\\Ddot": "\\ddot",
"\\Bar": "\\bar",
"\\Vec": "\\vec",
"\\parallel": "\\|",
"\\dag": "\\dagger",
"\\ddag": "\\ddagger",
"\\textlangle": "<",
"\\textrangle": ">",
"\\textgreater": ">",
"\\textless": "<",
"\\textbackslash": "\\",
"\\textunderscore": "_",
"\\=": "=",
"\\neg": "\\lnot",
"\\neq": "\\not=",
}
if token.startswith("\\left") or token.startswith("\\right"):
if (
"arrow" not in token
and "<" not in token
and ">" not in token
and "harpoon" not in token
):
token = token.replace("\\left", "").replace("\\right", "")
if token.startswith("\\big") or token.startswith("\\Big"):
if "\\" in token[4:]:
token = "\\" + token[4:].split("\\")[-1]
else:
token = token[-1]
if token in special_map.keys():
token = special_map[token]
if token.startswith("\\wide"):
return token.replace("wide", "")
if token.startswith("\\var"):
return token.replace("var", "")
if token.startswith("\\string"):
return token.replace("\\string", "")
return token
class HungarianMatcher:
def __init__(
self,
cost_token: float = 1,
cost_position: float = 0.05,
cost_order: float = 0.15,
):
self.cost_token = cost_token
self.cost_position = cost_position
self.cost_order = cost_order
self.cost = {}
def calculate_token_cost(self, box_gt, box_pred):
all_tokens = [data["token"] for data in box_gt + box_pred]
unique_tokens = sorted(list(set(all_tokens)))
token2id = {token: i for i, token in enumerate(unique_tokens)}
num_classes = len(token2id)
all_norm_tokens = [norm_same_token(data["token"]) for data in box_gt + box_pred]
unique_norm_tokens = sorted(list(set(all_norm_tokens)))
token2id_norm = {token: i for i, token in enumerate(unique_norm_tokens)}
num_classes_norm = len(token2id_norm)
gt_token_array = np.array([token2id[data["token"]] for data in box_gt])
norm_gt_token_array = np.array(
[token2id_norm[norm_same_token(data["token"])] for data in box_gt]
)
pred_token_logits = np.zeros((len(box_pred), num_classes))
for i, data in enumerate(box_pred):
if data["token"] in token2id:
pred_token_logits[i, token2id[data["token"]]] = 1
norm_pred_token_logits = np.zeros((len(box_pred), num_classes_norm))
for i, data in enumerate(box_pred):
norm_token = norm_same_token(data["token"])
if norm_token in token2id_norm:
norm_pred_token_logits[i, token2id_norm[norm_token]] = 1
if gt_token_array.size == 0 or pred_token_logits.shape[0] == 0:
return np.empty((len(box_gt), len(box_pred)))
token_cost = 1.0 - pred_token_logits[:, gt_token_array]
norm_token_cost = 1.0 - norm_pred_token_logits[:, norm_gt_token_array]
token_cost[np.logical_and(token_cost == 1, norm_token_cost == 0)] = 0.005
return token_cost.T
def box2array(self, box_list, size):
W, H = size
box_array = []
for box in box_list:
x_min, y_min, x_max, y_max = box["bbox"]
box_array.append(
[
(x_min + x_max) / (2 * W),
(y_min + y_max) / (2 * H),
(x_max - x_min) / W,
(y_max - y_min) / H,
]
)
return np.array(box_array)
def order2array(self, box_list, max_token_lens=None):
if not max_token_lens:
max_token_lens = len(box_list)
return np.array([[idx / max_token_lens] for idx, _ in enumerate(box_list)])
def calculate_l1_cost(self, gt_array, pred_array):
if gt_array.shape[0] == 0 or pred_array.shape[0] == 0:
return np.empty((gt_array.shape[0], pred_array.shape[0]))
return cdist(gt_array, pred_array, "minkowski", p=1) / gt_array.shape[-1]
def __call__(self, box_gt, box_pred, gt_size, pred_size):
if not box_gt or not box_pred:
return []
gt_box_array = self.box2array(box_gt, gt_size)
pred_box_array = self.box2array(box_pred, pred_size)
max_token_lens = max(len(box_gt), len(box_pred))
gt_order_array = self.order2array(box_gt, max_token_lens)
pred_order_array = self.order2array(box_pred, max_token_lens)
token_cost = self.calculate_token_cost(box_gt, box_pred)
position_cost = self.calculate_l1_cost(gt_box_array, pred_box_array)
order_cost = self.calculate_l1_cost(gt_order_array, pred_order_array)
self.cost = {
"token": token_cost,
"position": position_cost,
"order": order_cost,
}
cost = (
self.cost_token * token_cost
+ self.cost_position * position_cost
+ self.cost_order * order_cost
)
cost[np.isnan(cost) | np.isinf(cost)] = 100
row_ind, col_ind = linear_sum_assignment(cost)
return list(zip(row_ind, col_ind))
def update_inliers(ori_inliers, sub_inliers):
inliers = np.copy(ori_inliers)
sub_idx = -1
for idx in range(len(ori_inliers)):
if ori_inliers[idx] == False:
sub_idx += 1
if sub_inliers[sub_idx] == True:
inliers[idx] = True
return inliers