Spaces:
Build error
Build error
""" | |
@date: 2021/06/19 | |
@description: | |
Specification of 4 coordinate systems: | |
Pixel coordinates (used in panoramic images), the range is related to the image size, | |
generally converted to UV coordinates first, the first is horizontal coordinates, | |
increasing to the right, the second is column coordinates, increasing down | |
Uv coordinates (used in panoramic images), the range is [0~1], the upper left corner is the origin, | |
u is the abscissa and increases to the right, V is the column coordinate and increases to the right | |
Longitude and latitude coordinates (spherical), the range of longitude lon is [-pi~ PI], | |
and the range of dimension is [-pi/2~ PI /2]. The center of the panorama is the origin, | |
and the longitude increases to the right and the dimension increases to the down | |
Xyz coordinate (used in 3-dimensional space, of course, | |
it can also represent longitude and latitude coordinates on the sphere). | |
If on the sphere, the coordinate mode length is 1, when y is projected to the height of the camera, | |
the real position information of space points will be obtained | |
Correspondence between longitude and latitude coordinates and xyz coordinates: | |
| -pi/2 | |
| | |
lef _ _ _ _ _ |_ _ _ _ _ | |
-pi / | \ | |
pi | - - - - - -\ - z 0 mid | |
right \_ _ _ _ _ /_|_ _ _ _ _ _/ | |
/ | | |
/ | | |
x/ | y pi/2 | |
""" | |
import numpy as np | |
import torch | |
import functools | |
def get_u(w, is_np, b=None): | |
u = pixel2uv(np.array(range(w)) if is_np else torch.arange(0, w), w=w, axis=0) | |
if b is not None: | |
u = u[np.newaxis].repeat(b) if is_np else u.repeat(b, 1) | |
return u | |
def get_lon(w, is_np, b=None): | |
lon = pixel2lonlat(np.array(range(w)) if is_np else torch.arange(0, w), w=w, axis=0) | |
if b is not None: | |
lon = lon[np.newaxis].repeat(b, axis=0) if is_np else lon.repeat(b, 1) | |
return lon | |
def pixel2uv(pixel, w=1024, h=512, axis=None): | |
pixel = pixel.astype(np.float) if isinstance(pixel, np.ndarray) else pixel.float() | |
# +0.5 will make left/right and up/down coordinates symmetric | |
if axis is None: | |
u = (pixel[..., 0:1] + 0.5) / w | |
v = (pixel[..., 1:] + 0.5) / h | |
elif axis == 0: | |
u = (pixel + 0.5) / (w * 1.0) | |
return u | |
elif axis == 1: | |
v = (pixel + 0.5) / (h * 1.0) | |
return v | |
else: | |
assert False, "axis error" | |
lst = [u, v] | |
uv = np.concatenate(lst, axis=-1) if isinstance(pixel, np.ndarray) else torch.cat(lst, dim=-1) | |
return uv | |
def pixel2lonlat(pixel, w=1024, h=512, axis=None): | |
uv = pixel2uv(pixel, w, h, axis) | |
lonlat = uv2lonlat(uv, axis) | |
return lonlat | |
def pixel2xyz(pixel, w=1024, h=512): | |
lonlat = pixel2lonlat(pixel, w, h) | |
xyz = lonlat2xyz(lonlat) | |
return xyz | |
def uv2lonlat(uv, axis=None): | |
if axis is None: | |
lon = (uv[..., 0:1] - 0.5) * 2 * np.pi | |
lat = (uv[..., 1:] - 0.5) * np.pi | |
elif axis == 0: | |
lon = (uv - 0.5) * 2 * np.pi | |
return lon | |
elif axis == 1: | |
lat = (uv - 0.5) * np.pi | |
return lat | |
else: | |
assert False, "axis error" | |
lst = [lon, lat] | |
lonlat = np.concatenate(lst, axis=-1) if isinstance(uv, np.ndarray) else torch.cat(lst, dim=-1) | |
return lonlat | |
def uv2xyz(uv, plan_y=None, spherical=False): | |
lonlat = uv2lonlat(uv) | |
xyz = lonlat2xyz(lonlat) | |
if spherical: | |
# Projection onto the sphere | |
return xyz | |
if plan_y is None: | |
from utils.boundary import boundary_type | |
plan_y = boundary_type(uv) | |
# Projection onto the specified plane | |
xyz = xyz * (plan_y / xyz[..., 1])[..., None] | |
return xyz | |
def lonlat2xyz(lonlat, plan_y=None): | |
lon = lonlat[..., 0:1] | |
lat = lonlat[..., 1:] | |
cos = np.cos if isinstance(lonlat, np.ndarray) else torch.cos | |
sin = np.sin if isinstance(lonlat, np.ndarray) else torch.sin | |
x = cos(lat) * sin(lon) | |
y = sin(lat) | |
z = cos(lat) * cos(lon) | |
lst = [x, y, z] | |
xyz = np.concatenate(lst, axis=-1) if isinstance(lonlat, np.ndarray) else torch.cat(lst, dim=-1) | |
if plan_y is not None: | |
xyz = xyz * (plan_y / xyz[..., 1])[..., None] | |
return xyz | |
##################### | |
def xyz2lonlat(xyz): | |
atan2 = np.arctan2 if isinstance(xyz, np.ndarray) else torch.atan2 | |
asin = np.arcsin if isinstance(xyz, np.ndarray) else torch.asin | |
norm = np.linalg.norm(xyz, axis=-1) if isinstance(xyz, np.ndarray) else torch.norm(xyz, p=2, dim=-1) | |
xyz_norm = xyz / norm[..., None] | |
x = xyz_norm[..., 0:1] | |
y = xyz_norm[..., 1:2] | |
z = xyz_norm[..., 2:] | |
lon = atan2(x, z) | |
lat = asin(y) | |
lst = [lon, lat] | |
lonlat = np.concatenate(lst, axis=-1) if isinstance(xyz, np.ndarray) else torch.cat(lst, dim=-1) | |
return lonlat | |
def xyz2uv(xyz): | |
lonlat = xyz2lonlat(xyz) | |
uv = lonlat2uv(lonlat) | |
return uv | |
def xyz2pixel(xyz, w=1024, h=512): | |
uv = xyz2uv(xyz) | |
pixel = uv2pixel(uv, w, h) | |
return pixel | |
def lonlat2uv(lonlat, axis=None): | |
if axis is None: | |
u = lonlat[..., 0:1] / (2 * np.pi) + 0.5 | |
v = lonlat[..., 1:] / np.pi + 0.5 | |
elif axis == 0: | |
u = lonlat / (2 * np.pi) + 0.5 | |
return u | |
elif axis == 1: | |
v = lonlat / np.pi + 0.5 | |
return v | |
else: | |
assert False, "axis error" | |
lst = [u, v] | |
uv = np.concatenate(lst, axis=-1) if isinstance(lonlat, np.ndarray) else torch.cat(lst, dim=-1) | |
return uv | |
def lonlat2pixel(lonlat, w=1024, h=512, axis=None, need_round=True): | |
uv = lonlat2uv(lonlat, axis) | |
pixel = uv2pixel(uv, w, h, axis, need_round) | |
return pixel | |
def uv2pixel(uv, w=1024, h=512, axis=None, need_round=True): | |
""" | |
:param uv: [[u1, v1], [u2, v2] ...] | |
:param w: width of panorama image | |
:param h: height of panorama image | |
:param axis: sometimes the input data is only u(axis =0) or only v(axis=1) | |
:param need_round: | |
:return: | |
""" | |
if axis is None: | |
pu = uv[..., 0:1] * w - 0.5 | |
pv = uv[..., 1:] * h - 0.5 | |
elif axis == 0: | |
pu = uv * w - 0.5 | |
if need_round: | |
pu = pu.round().astype(np.int) if isinstance(uv, np.ndarray) else pu.round().int() | |
return pu | |
elif axis == 1: | |
pv = uv * h - 0.5 | |
if need_round: | |
pv = pv.round().astype(np.int) if isinstance(uv, np.ndarray) else pv.round().int() | |
return pv | |
else: | |
assert False, "axis error" | |
lst = [pu, pv] | |
if need_round: | |
pixel = np.concatenate(lst, axis=-1).round().astype(np.int) if isinstance(uv, np.ndarray) else torch.cat(lst, | |
dim=-1).round().int() | |
else: | |
pixel = np.concatenate(lst, axis=-1) if isinstance(uv, np.ndarray) else torch.cat(lst, dim=-1) | |
pixel[..., 0] = pixel[..., 0] % w | |
pixel[..., 1] = pixel[..., 1] % h | |
return pixel | |
##################### | |
def xyz2depth(xyz, plan_y=1): | |
""" | |
:param xyz: | |
:param plan_y: | |
:return: | |
""" | |
xyz = xyz * (plan_y / xyz[..., 1])[..., None] | |
xz = xyz[..., ::2] | |
depth = np.linalg.norm(xz, axis=-1) if isinstance(xz, np.ndarray) else torch.norm(xz, dim=-1) | |
return depth | |
def uv2depth(uv, plan_y=None): | |
if plan_y is None: | |
from utils.boundary import boundary_type | |
plan_y = boundary_type(uv) | |
xyz = uv2xyz(uv, plan_y) | |
depth = xyz2depth(xyz, plan_y) | |
return depth | |
def lonlat2depth(lonlat, plan_y=None): | |
if plan_y is None: | |
from utils.boundary import boundary_type | |
plan_y = boundary_type(lonlat2uv(lonlat)) | |
xyz = lonlat2xyz(lonlat, plan_y) | |
depth = xyz2depth(xyz, plan_y) | |
return depth | |
def depth2xyz(depth, plan_y=1): | |
""" | |
:param depth: [patch_num] or [b, patch_num] | |
:param plan_y: | |
:return: | |
""" | |
is_np = isinstance(depth, np.ndarray) | |
w = depth.shape[-1] | |
lon = get_lon(w, is_np, b=depth.shape[0] if len(depth.shape) == 2 else None) | |
if not is_np: | |
lon = lon.to(depth.device) | |
cos = np.cos if is_np else torch.cos | |
sin = np.sin if is_np else torch.sin | |
# polar covert to cartesian | |
if len(depth.shape) == 2: | |
b = depth.shape[0] | |
xyz = np.zeros((b, w, 3)) if is_np else torch.zeros((b, w, 3)) | |
else: | |
xyz = np.zeros((w, 3)) if is_np else torch.zeros((w, 3)) | |
if not is_np: | |
xyz = xyz.to(depth.device) | |
xyz[..., 0] = depth * sin(lon) | |
xyz[..., 1] = plan_y | |
xyz[..., 2] = depth * cos(lon) | |
return xyz | |
def depth2uv(depth, plan_y=1): | |
xyz = depth2xyz(depth, plan_y) | |
uv = xyz2uv(xyz) | |
return uv | |
def depth2pixel(depth, w=1024, h=512, need_round=True, plan_y=1): | |
uv = depth2uv(depth, plan_y) | |
pixel = uv2pixel(uv, w, h, need_round=need_round) | |
return pixel | |
if __name__ == '__main__': | |
a = np.array([[0.5, 1, 0.5]]) | |
a = xyz2pixel(a) | |
print(a) | |
if __name__ == '__main__1': | |
np.set_printoptions(suppress=True) | |
a = np.array([[0, 0], [1023, 511]]) | |
a = pixel2xyz(a) | |
a = xyz2pixel(a) | |
print(a) | |
########### | |
a = torch.tensor([[0, 0], [1023, 511]]) | |
a = pixel2xyz(a) | |
a = xyz2pixel(a) | |
print(a) | |
########### | |
u = np.array([0, 256, 512, 1023]) | |
lon = pixel2lonlat(u, axis=0) | |
u = lonlat2pixel(lon, axis=0) | |
print(u) | |
u = torch.tensor([0, 256, 512, 1023]) | |
lon = pixel2lonlat(u, axis=0) | |
u = lonlat2pixel(lon, axis=0) | |
print(u) | |
########### | |
v = np.array([0, 256, 511]) | |
lat = pixel2lonlat(v, axis=1) | |
v = lonlat2pixel(lat, axis=1) | |
print(v) | |
v = torch.tensor([0, 256, 511]) | |
lat = pixel2lonlat(v, axis=1) | |
v = lonlat2pixel(lat, axis=1) | |
print(v) | |