Spaces:
Runtime error
Runtime error
import torch | |
import numpy as np | |
import pdb | |
def ea_score(src_line, tgt_line): | |
""" | |
Implement a differentiable EAScore of two 2D lines. | |
Kai Zhao∗, Qi Han∗, Chang-Bin Zhang, Jun Xu, Ming-Ming Cheng. | |
Deep Hough Transform for Semantic Line Detection. | |
TPAMI 2021. | |
- src_line: tensor shape Nx4, XYXY format | |
- tgt_line: tensor shape Nx4, XYXY format | |
""" | |
# midpoint error | |
src_line_mid_x = (src_line[:, 0] + src_line[:, 2]) / 2 | |
tgt_line_mid_x = (tgt_line[:, 0] + tgt_line[:, 2]) / 2 | |
src_line_mid_y = (src_line[:, 1] + src_line[:, 3]) / 2 | |
tgt_line_mid_y = (tgt_line[:, 1] + tgt_line[:, 3]) / 2 | |
line_se = 1 - torch.sqrt((src_line_mid_x - tgt_line_mid_x)**2 + (src_line_mid_y - tgt_line_mid_y)**2) | |
line_se = line_se.clamp(min=0) | |
# angle error | |
src_line_angle = torch.atan((src_line[:, 1] - src_line[:, 3]) / (src_line[:, 0] - src_line[:, 2] + 1e-5)) | |
tgt_line_angle = torch.atan((tgt_line[:, 1] - tgt_line[:, 3]) / (tgt_line[:, 0] - tgt_line[:, 2] + 1e-5)) | |
d_angle = torch.abs(src_line_angle - tgt_line_angle) | |
d_angle = torch.min(d_angle, torch.pi - d_angle) | |
line_sa = 1 - d_angle / (torch.pi / 2) | |
line_eascore = (line_se * line_sa) ** 2 | |
line_eascore[torch.isnan(line_eascore)] = 0.0 | |
return line_eascore, line_se, line_sa | |
def sine_to_angle(sin, cos, r, eps=1e-5): | |
sin = sin + eps | |
cos = cos + eps | |
mag = torch.sqrt(sin ** 2 + cos ** 2) | |
sin = sin / mag | |
cos = cos / mag | |
r = r / mag | |
theta_cos = torch.acos(cos) | |
theta_sin = torch.asin(sin) | |
theta_cos[theta_sin < 0] = torch.pi * 2 - theta_cos[theta_sin < 0] | |
return theta_cos, r | |
def line_xyxy_to_angle(line_xyxy, center=[0.5, 0.5], debug=False): | |
""" | |
Convert [X1, Y1, X2, Y2] representation of a 2D line to | |
[sin(theta), cos(theta), offset] representation. | |
r = xcos(theta) + ysin(theta) | |
For two points (x1, y1) and (x2, y2) within image plane [0, 1], | |
- cos(theta) = y1 - y2 | |
- sin(theta) = x2 - x1 | |
- r = x2y1 - x1y2 | |
Shengyi Qian, Linyi Jin, Chris Rockwell, Siyi Chen, David Fouhey. | |
Understanding 3D Object Articulation in Internet Videos. | |
CVPR 2022. | |
""" | |
eps = 1e-5 | |
device = line_xyxy.device | |
if isinstance(center, list): | |
center_w, center_h = center | |
line_xyxy = line_xyxy - torch.as_tensor([center_w, center_h, center_w, center_h]).to(device) | |
elif isinstance(center, torch.Tensor): | |
line_xyxy = line_xyxy - center | |
else: | |
raise NotImplementedError | |
#line_xyxy = line_xyxy.clamp(min=-0.5, max=0.5) | |
line_xyxy = line_xyxy.clamp(min=-1.0, max=1.0) | |
x1, y1, x2, y2 = line_xyxy[:,:1], line_xyxy[:,1:2], line_xyxy[:,2:3], line_xyxy[:,3:4] | |
cos = y1 - y2 | |
sin = x2 - x1 | |
r = x2 * y1 - x1 * y2 | |
theta, r = sine_to_angle(sin, cos, r) | |
if debug: | |
pdb.set_trace() | |
pass | |
# normalize, and ensure | |
# sin(theta) in [0, 1] | |
# cos(theta) in [-1, 1] | |
# r in (-sqrt(2) / 2, sqrt(2) / 2) | |
theta *= 2 | |
sin = torch.sin(theta) | |
cos = torch.cos(theta) | |
# assert (r > (- np.sqrt(2) / 2)).all() | |
# assert (r < (np.sqrt(2) / 2)).all() | |
assert (r > (- np.sqrt(2))).all() | |
assert (r < (np.sqrt(2))).all() | |
assert (sin >= -1).all() | |
assert (sin <= 1).all() | |
assert (cos >= -1).all() | |
assert (cos <= 1).all() | |
return torch.cat((sin, cos, r), dim=1) | |
def line_angle_to_xyxy(line_angle, center=[0.5, 0.5], use_bins=False, debug=False): | |
""" | |
Convert [sin(theta), cos(theta), offset] representation of a 2D line to | |
[X1, Y1, X2, Y2] representation. | |
Shengyi Qian, Linyi Jin, Chris Rockwell, Siyi Chen, David Fouhey. | |
Understanding 3D Object Articulation in Internet Videos. | |
CVPR 2022. | |
""" | |
eps = 1e-5 | |
device = line_angle.device | |
if isinstance(center, list): | |
center_w, center_h = center | |
elif isinstance(center, torch.Tensor): | |
center_w = center[:, 0:1] | |
center_h = center[:, 1:2] | |
else: | |
raise NotImplementedError | |
sin = line_angle[:, :1] | |
cos = line_angle[:, 1:2] | |
r = line_angle[:, 2:] | |
# normalize sin and cos | |
# make sure r is not out of boundary | |
theta, r = sine_to_angle(sin, cos, r) | |
theta /= 2 | |
sin = torch.sin(theta) | |
cos = torch.cos(theta) | |
r = r.clamp(min=(- np.sqrt(2) / 2), max=(np.sqrt(2) / 2)) | |
# intersect line with four boundaries | |
y1 = (r - cos * (0.0 - center_w)) / sin + center_h | |
y2 = (r - cos * (1.0 - center_w)) / sin + center_h | |
x3 = (r - sin * (0.0 - center_h)) / cos + center_w | |
x4 = (r - sin * (1.0 - center_h)) / cos + center_w | |
line_xyxy = [] | |
for i in range(line_angle.shape[0]): | |
line = [] | |
if y1[i] > - eps and y1[i] < (1.0 + eps): | |
line.append([0.0, y1[i]]) | |
if y2[i] > - eps and y2[i] < (1.0 + eps): | |
line.append([1.0, y2[i]]) | |
if len(line) < 2 and x3[i] > - eps and x3[i] < (1.0 + eps): | |
line.append([x3[i], 0.0]) | |
if len(line) < 2 and x4[i] > - eps and x4[i] < (1.0 + eps): | |
line.append([x4[i], 1.0]) | |
# Mathematically, we should only have two boundary points. | |
# However, in training time, it is not guaranteed the represented | |
# line is within the image plane. Even if r < sqrt(2)/2, it | |
# can be out of boundary. But it's rare and we want to ignore it. | |
if len(line) != 2: | |
line = [[0.0, y1[i]], [x3[i], 0.0]] | |
line = torch.as_tensor(line, device=device) | |
if torch.isnan(line.mean()): | |
pdb.set_trace() | |
pass | |
# make sure it is sorted, so that model does not get confused. | |
# flat [[x1, y1], [x2, y2]] to [x1, y1, x2, y2] | |
sort_idx = torch.sort(line[:, 0], dim=0, descending=False)[1] | |
line = line[sort_idx] | |
line = line.flatten() | |
line_xyxy.append(line) | |
line_xyxy = torch.stack(line_xyxy) | |
if debug: | |
pdb.set_trace() | |
pass | |
return line_xyxy | |
# def line_xyxy_to_angle(line_xyxy): | |
# """ | |
# Convert [X1, Y1, X2, Y2] representation of a 2D line to | |
# [sin(theta), cos(theta), offset] representation. | |
# xcos(theta) + ysin(theta) = r | |
# For two points (x1, y1) and (x2, y2) within image plane [0, 1], | |
# - cos(theta) = y1 - y2 | |
# - sin(theta) = x2 - x1 | |
# - r = x2y1 - x1y2 | |
# Shengyi Qian, Linyi Jin, Chris Rockwell, Siyi Chen, David Fouhey. | |
# Understanding 3D Object Articulation in Internet Videos. | |
# CVPR 2022. | |
# """ | |
# eps = 1e-5 | |
# device = line_xyxy.device | |
# line_xyxy = line_xyxy - torch.as_tensor([0.5, 0.5, 0.5, 0.5]).to(device) | |
# line_xyxy = line_xyxy.clamp(min=-0.5, max=0.5) | |
# x1, y1, x2, y2 = line_xyxy[:,:1], line_xyxy[:,1:2], line_xyxy[:,2:3], line_xyxy[:,3:4] | |
# cos = y1 - y2 | |
# sin = x2 - x1 | |
# r = x2 * y1 - x1 * y2 | |
# # normalize, and ensure | |
# # sin(theta) in [0, 1] | |
# # cos(theta) in [-1, 1] | |
# # r in (-sqrt(2) / 2, sqrt(2) / 2) | |
# sign = torch.sign(r) | |
# mag = torch.sqrt(sin ** 2 + cos ** 2) + eps | |
# r = r / mag * sign | |
# sin = sin / mag * sign | |
# cos = cos / mag * sign | |
# #assert (r > (- torch.sqrt(2) / 2)).all() | |
# assert (r >= 0).all() | |
# assert (r < (np.sqrt(2) / 2)).all() | |
# assert (sin >= -1).all() | |
# assert (sin <= 1).all() | |
# assert (cos >= -1).all() | |
# assert (cos <= 1).all() | |
# return torch.cat((sin, cos, r), dim=1) | |
# def line_angle_to_xyxy(line_angle, use_bins=False): | |
# """ | |
# Convert [sin(theta), cos(theta), offset] representation of a 2D line to | |
# [X1, Y1, X2, Y2] representation. | |
# Shengyi Qian, Linyi Jin, Chris Rockwell, Siyi Chen, David Fouhey. | |
# Understanding 3D Object Articulation in Internet Videos. | |
# CVPR 2022. | |
# """ | |
# eps = 1e-5 | |
# device = line_angle.device | |
# sin = line_angle[:, :1] + eps | |
# cos = line_angle[:, 1:2] + eps | |
# r = line_angle[:, 2:] | |
# # normalize sin and cos | |
# # make sure r is not out of boundary | |
# mag = torch.sqrt(sin ** 2 + cos ** 2) | |
# sin = sin / mag | |
# cos = cos / mag | |
# r = r.clamp(min=0, max=(np.sqrt(2) / 2)) | |
# # intersect line with four boundaries | |
# y1 = (r - cos * (- 0.5)) / sin + 0.5 | |
# y2 = (r - cos * 0.5) / sin + 0.5 | |
# x3 = (r - sin * (- 0.5)) / cos + 0.5 | |
# x4 = (r - sin * 0.5) / cos + 0.5 | |
# line_xyxy = [] | |
# for i in range(line_angle.shape[0]): | |
# line = [] | |
# if y1[i] > - eps and y1[i] < (1.0 + eps): | |
# line.append([0.0, y1[i]]) | |
# if y2[i] > - eps and y2[i] < (1.0 + eps): | |
# line.append([1.0, y2[i]]) | |
# if len(line) < 2 and x3[i] > - eps and x3[i] < (1.0 + eps): | |
# line.append([x3[i], 0.0]) | |
# if len(line) < 2 and x4[i] > - eps and x4[i] < (1.0 + eps): | |
# line.append([x4[i], 1.0]) | |
# # Mathematically, we should only have two boundary points. | |
# # However, in training time, it is not guaranteed the represented | |
# # line is within the image plane. Even if r < sqrt(2)/2, it | |
# # can be out of boundary. But it's rare and we want to ignore it. | |
# if len(line) != 2: | |
# line = [[0.0, y1[i]], [x3[i], 0.0]] | |
# line = torch.as_tensor(line, device=device) | |
# if torch.isnan(line.mean()): | |
# pdb.set_trace() | |
# pass | |
# # make sure it is sorted, so that model does not get confused. | |
# # flat [[x1, y1], [x2, y2]] to [x1, y1, x2, y2] | |
# sort_idx = torch.sort(line[:, 0], dim=0, descending=False)[1] | |
# line = line[sort_idx] | |
# line = line.flatten() | |
# line_xyxy.append(line) | |
# line_xyxy = torch.stack(line_xyxy) | |
# return line_xyxy | |