File size: 9,755 Bytes
88b0dcb 4f25fa8 88b0dcb a5a934b 88b0dcb a5a934b 88b0dcb a5a934b 88b0dcb |
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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
"""
@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)
|