|
import numpy as np |
|
from numpy import sin, cos |
|
from math import pi as π |
|
from my3d import camera_pose |
|
from my.config import BaseConf |
|
|
|
|
|
def get_K(H, W, FoV_x): |
|
FoV_x = FoV_x / 180 * π |
|
f = 1 / np.tan(FoV_x / 2) * (W / 2) |
|
|
|
K = np.array([ |
|
[f, 0, -(W/2 - 0.5)], |
|
[0, -f, -(H/2 - 0.5)], |
|
[0, 0, -1] |
|
]) |
|
|
|
|
|
return K |
|
|
|
|
|
SIDEVIEW_PROMPTS = [ |
|
"front view of", "side view of", "backside view of", "side view of" |
|
] |
|
|
|
TOPVIEW_PROMPT = "overhead view of" |
|
|
|
|
|
def train_eye_with_prompts(r, n): |
|
hs = np.random.rand(n) * 360 |
|
vs = np.random.rand(n) * np.deg2rad(100) |
|
vs = np.clip(vs, 1e-2, π-1e-2) |
|
|
|
prompts = [] |
|
v_thresh = np.deg2rad(30) |
|
for i in range(n): |
|
_p = "" |
|
if vs[i] < v_thresh: |
|
_p = TOPVIEW_PROMPT |
|
else: |
|
_a = hs[i] |
|
_a = (_a + 45) % 360 |
|
_quad = int(_a // 90) |
|
_p = SIDEVIEW_PROMPTS[_quad] |
|
prompts.append(_p) |
|
|
|
θ = np.deg2rad(hs) |
|
|
|
φ = np.arccos(1 - 2 * (vs / π)) |
|
|
|
horz = hs |
|
elev = np.rad2deg(π / 2 - φ) |
|
|
|
eyes = np.zeros((n, 3)) |
|
|
|
eyes[:, 0] = r * sin(φ) * cos(π-θ) |
|
eyes[:, 2] = r * sin(φ) * sin(π-θ) |
|
eyes[:, 1] = r * cos(φ) |
|
|
|
return eyes, prompts, horz, elev |
|
|
|
|
|
def spiral_poses( |
|
radius, height, |
|
num_steps=20, num_rounds=1, |
|
center=np.array([0, 0, 0]), up=np.array([0, 1, 0]), |
|
): |
|
eyes = [] |
|
for i in range(num_steps): |
|
ratio = (i + 1) / num_steps |
|
Δz = height * (1 - ratio) |
|
|
|
θ = ratio * (360 * num_rounds) |
|
θ = θ / 180 * π |
|
|
|
_r = max(radius * sin(ratio * π / 2), 0.5) |
|
Δx, Δy = _r * np.array([np.cos(θ), np.sin(θ)]) |
|
eyes.append(center + [Δx, Δz, Δy]) |
|
|
|
poses = [ |
|
camera_pose(e, center - e, up) for e in eyes |
|
] |
|
return poses |
|
|
|
def circular_poses( |
|
radius, height, |
|
num_steps=36, num_rounds=1, |
|
center=np.array([0, 0, 0]), up=np.array([0, 1, 0]), |
|
): |
|
eyes = [] |
|
horz = [] |
|
for i in range(num_steps): |
|
ratio = (i + 1) / num_steps |
|
Δz = radius * (np.cos(np.deg2rad(60))) |
|
|
|
θ = ratio * (360 * num_rounds) |
|
θ = θ / 180 * π |
|
|
|
_r = max(radius * sin(ratio * π / 2), 0.5) |
|
Δx, Δy = radius * np.array([np.sin(np.deg2rad(60))*np.cos(θ), np.sin(np.deg2rad(60))*np.sin(θ)]) |
|
eyes.append(center + [Δx, Δz, Δy]) |
|
horz.append(np.rad2deg(θ)) |
|
poses = [ |
|
camera_pose(e, center - e, up) for e in eyes |
|
] |
|
horz=np.array(horz) |
|
return poses |
|
|
|
class PoseConfig(BaseConf): |
|
rend_hw: int = 64 |
|
FoV: float = 60.0 |
|
R: float = 1.5 |
|
|
|
def make(self): |
|
cfgs = self.dict() |
|
hw = cfgs.pop("rend_hw") |
|
cfgs["H"] = hw |
|
cfgs["W"] = hw |
|
return Poser(**cfgs) |
|
|
|
|
|
class Poser(): |
|
def __init__(self, H, W, FoV, R): |
|
self.H, self.W = H, W |
|
self.R = R |
|
self.K = get_K(H, W, FoV) |
|
self.FoV = FoV |
|
|
|
def sample_train(self, n, device): |
|
eyes, prompts, horz, elev = train_eye_with_prompts(r=self.R, n=n) |
|
up = np.array([0, 1, 0]) |
|
|
|
|
|
poses = [ |
|
camera_pose(e, -e, up) for e in eyes |
|
] |
|
poses = np.stack(poses, 0) |
|
|
|
|
|
|
|
FoV = np.random.rand(n) * 30 + 40 |
|
|
|
|
|
random_Ks = [ |
|
get_K(self.H, self.W, FoV[i]) |
|
for i in range(len(poses)) |
|
|
|
] |
|
|
|
|
|
angles_list = [] |
|
|
|
for horizontal, elevation, fov in zip(horz, elev, FoV): |
|
angles_list.append([horizontal, elevation, fov]) |
|
|
|
return random_Ks, poses, prompts, angles_list |
|
|
|
def sample_test(self, n): |
|
poses = circular_poses(self.R, self.R, n, num_rounds=3) |
|
poses = np.stack(poses, axis=0) |
|
return self.K, poses |