zhigangjiang's picture
Update utils/conversion.py
a5a934b
"""
@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
@functools.lru_cache()
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
@functools.lru_cache()
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.float32) 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.int32) 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.int32) 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.int32) 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)