inno / pose.py
anhduy412's picture
Duplicate from jyseo/3DFuse
1a14066
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 * π # to rad
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)
# φ = v
φ = np.arccos(1 - 2 * (vs / π))
horz = hs
elev = np.rad2deg(π / 2 - φ)
eyes = np.zeros((n, 3))
eyes[:, 0] = r * sin(φ) * cos(π-θ) # x
eyes[:, 2] = r * sin(φ) * sin(π-θ) # z
eyes[:, 1] = r * cos(φ) # y
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 * ratio, 0.5)
_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 * ratio, 0.5)
_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