ECON / lib /common /BNI_utils.py
Yuliang's picture
Support TEXTure
487ee6d
import os
import os.path as osp
import cupy as cp
import cv2
import numpy as np
import torch
import trimesh
from cupyx.scipy.sparse import (
coo_matrix,
csr_matrix,
diags,
hstack,
spdiags,
vstack,
)
from cupyx.scipy.sparse.linalg import cg
from PIL import Image
from tqdm.auto import tqdm
from lib.dataset.mesh_util import clean_floats
def find_max_list(lst):
list_len = [len(i) for i in lst]
max_id = np.argmax(np.array(list_len))
return lst[max_id]
def interpolate_pts(pts, diff_ids):
pts_extend = np.around((pts[diff_ids] + pts[diff_ids - 1]) * 0.5).astype(np.int32)
pts = np.insert(pts, diff_ids, pts_extend, axis=0)
return pts
def align_pts(pts1, pts2):
diff_num = abs(len(pts1) - len(pts2))
diff_ids = np.sort(np.random.choice(min(len(pts2), len(pts1)), diff_num, replace=True))
if len(pts1) > len(pts2):
pts2 = interpolate_pts(pts2, diff_ids)
elif len(pts2) > len(pts1):
pts1 = interpolate_pts(pts1, diff_ids)
else:
pass
return pts1, pts2
def repeat_pts(pts1, pts2):
coverage_mask = ((pts1[:, None, :] == pts2[None, :, :]).sum(axis=2) == 2.).any(axis=1)
return coverage_mask
def find_contour(mask, method='all'):
if method == 'all':
contours, _ = cv2.findContours(mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
else:
contours, _ = cv2.findContours(
mask.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
)
contour_cloth = np.array(find_max_list(contours))[:, 0, :]
return contour_cloth
def mean_value_cordinates(inner_pts, contour_pts):
body_edges_a = np.sqrt(((inner_pts[:, None] - contour_pts[None, :])**2).sum(axis=2))
body_edges_c = np.roll(body_edges_a, shift=-1, axis=1)
body_edges_b = np.sqrt(((contour_pts - np.roll(contour_pts, shift=-1, axis=0))**2).sum(axis=1))
body_edges = np.concatenate([
body_edges_a[..., None], body_edges_c[..., None],
np.repeat(body_edges_b[None, :, None], axis=0, repeats=len(inner_pts))
],
axis=-1)
body_cos = (body_edges[:, :, 0]**2 + body_edges[:, :, 1]**2 -
body_edges[:, :, 2]**2) / (2 * body_edges[:, :, 0] * body_edges[:, :, 1])
body_tan_half = np.sqrt(
(1. - np.clip(body_cos, a_max=1., a_min=-1.)) / np.clip(1. + body_cos, 1e-6, 2.)
)
w = (body_tan_half + np.roll(body_tan_half, shift=1, axis=1)) / body_edges_a
w /= w.sum(axis=1, keepdims=True)
return w
def get_dst_mat(contour_body, contour_cloth):
dst_mat = ((contour_body[:, None, :] - contour_cloth[None, :, :])**2).sum(axis=2)
return dst_mat
def dispCorres(img_size, contour1, contour2, phi, dir_path):
contour1 = contour1[None, :, None, :].astype(np.int32)
contour2 = contour2[None, :, None, :].astype(np.int32)
disp = np.zeros((img_size, img_size, 3), dtype=np.uint8)
cv2.drawContours(disp, contour1, -1, (0, 255, 0), 1) # green
cv2.drawContours(disp, contour2, -1, (255, 0, 0), 1) # blue
for i in range(contour1.shape[1]): # do not show all the points when display
# cv2.circle(disp, (contour1[0, i, 0, 0], contour1[0, i, 0, 1]), 1,
# (255, 0, 0), -1)
corresPoint = contour2[0, phi[i], 0]
# cv2.circle(disp, (corresPoint[0], corresPoint[1]), 1, (0, 255, 0), -1)
cv2.line(
disp, (contour1[0, i, 0, 0], contour1[0, i, 0, 1]), (corresPoint[0], corresPoint[1]),
(255, 255, 255), 1
)
cv2.imwrite(osp.join(dir_path, "corres.png"), disp)
def remove_stretched_faces(verts, faces):
mesh = trimesh.Trimesh(verts, faces)
camera_ray = np.array([0.0, 0.0, 1.0])
faces_cam_angles = np.dot(mesh.face_normals, camera_ray)
# cos(90-20)=0.34 cos(90-10)=0.17, 10~20 degree
faces_mask = np.abs(faces_cam_angles) > 2e-1
mesh.update_faces(faces_mask)
mesh.remove_unreferenced_vertices()
return mesh.vertices, mesh.faces
def tensor2arr(t, mask=False):
if not mask:
return t.squeeze(0).permute(1, 2, 0).detach().cpu().numpy()
else:
mask = t.squeeze(0).abs().sum(dim=0, keepdim=True)
return (mask != mask[:, 0, 0]).float().squeeze(0).detach().cpu().numpy()
def arr2png(t):
return ((t + 1.0) * 0.5 * 255.0).astype(np.uint8)
def depth2arr(t):
return t.float().detach().cpu().numpy()
def depth2png(t):
t_copy = t.copy()
t_bg = t_copy[0, 0]
valid_region = np.logical_and(t > -1.0, t != t_bg)
t_copy[valid_region] -= t_copy[valid_region].min()
t_copy[valid_region] /= t_copy[valid_region].max()
t_copy[valid_region] = (1. - t_copy[valid_region]) * 255.0
t_copy[~valid_region] = 0.0
return t_copy[..., None].astype(np.uint8)
def verts_transform(t, depth_scale):
t_copy = t.clone()
t_copy *= depth_scale * 0.5
t_copy += depth_scale * 0.5
t_copy = t_copy[:, [1, 0, 2]] * torch.Tensor([2.0, 2.0, -2.0]) + torch.Tensor([
0.0, 0.0, depth_scale
])
return t_copy
def verts_inverse_transform(t, depth_scale):
t_copy = t.clone()
t_copy -= torch.tensor([0.0, 0.0, depth_scale])
t_copy /= torch.tensor([2.0, 2.0, -2.0])
t_copy -= depth_scale * 0.5
t_copy /= depth_scale * 0.5
t_copy = t_copy[:, [1, 0, 2]]
return t_copy
def depth_inverse_transform(t, depth_scale):
t_copy = t.clone()
t_copy -= torch.tensor(depth_scale)
t_copy /= torch.tensor(-2.0)
t_copy -= depth_scale * 0.5
t_copy /= depth_scale * 0.5
return t_copy
# BNI related
def move_left(mask):
return cp.pad(mask, ((0, 0), (0, 1)), "constant", constant_values=0)[:, 1:]
def move_right(mask):
return cp.pad(mask, ((0, 0), (1, 0)), "constant", constant_values=0)[:, :-1]
def move_top(mask):
return cp.pad(mask, ((0, 1), (0, 0)), "constant", constant_values=0)[1:, :]
def move_bottom(mask):
return cp.pad(mask, ((1, 0), (0, 0)), "constant", constant_values=0)[:-1, :]
def move_top_left(mask):
return cp.pad(mask, ((0, 1), (0, 1)), "constant", constant_values=0)[1:, 1:]
def move_top_right(mask):
return cp.pad(mask, ((0, 1), (1, 0)), "constant", constant_values=0)[1:, :-1]
def move_bottom_left(mask):
return cp.pad(mask, ((1, 0), (0, 1)), "constant", constant_values=0)[:-1, 1:]
def move_bottom_right(mask):
return cp.pad(mask, ((1, 0), (1, 0)), "constant", constant_values=0)[:-1, :-1]
def generate_dx_dy_new(mask, nz_horizontal, nz_vertical, step_size=1):
# pixel coordinates
# ^ vertical positive
# |
# |
# |
# o ---> horizontal positive
num_pixel = cp.sum(mask)
pixel_idx = cp.zeros_like(mask, dtype=int)
pixel_idx[mask] = cp.arange(num_pixel)
has_left_mask = cp.logical_and(move_right(mask), mask)
has_right_mask = cp.logical_and(move_left(mask), mask)
has_bottom_mask = cp.logical_and(move_top(mask), mask)
has_top_mask = cp.logical_and(move_bottom(mask), mask)
nz_left = nz_horizontal[has_left_mask[mask]]
nz_right = nz_horizontal[has_right_mask[mask]]
nz_top = nz_vertical[has_top_mask[mask]]
nz_bottom = nz_vertical[has_bottom_mask[mask]]
data = cp.stack([-nz_left / step_size, nz_left / step_size], -1).flatten()
indices = cp.stack((pixel_idx[move_left(has_left_mask)], pixel_idx[has_left_mask]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_left_mask[mask].astype(int) * 2)])
D_horizontal_neg = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_right / step_size, nz_right / step_size], -1).flatten()
indices = cp.stack((pixel_idx[has_right_mask], pixel_idx[move_right(has_right_mask)]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_right_mask[mask].astype(int) * 2)])
D_horizontal_pos = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_top / step_size, nz_top / step_size], -1).flatten()
indices = cp.stack((pixel_idx[has_top_mask], pixel_idx[move_top(has_top_mask)]), -1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_top_mask[mask].astype(int) * 2)])
D_vertical_pos = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_bottom / step_size, nz_bottom / step_size], -1).flatten()
indices = cp.stack((pixel_idx[move_bottom(has_bottom_mask)], pixel_idx[has_bottom_mask]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_bottom_mask[mask].astype(int) * 2)])
D_vertical_neg = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
return D_horizontal_pos, D_horizontal_neg, D_vertical_pos, D_vertical_neg
def generate_dx_dy(mask, nz_horizontal, nz_vertical, step_size=1):
# pixel coordinates
# ^ vertical positive
# |
# |
# |
# o ---> horizontal positive
num_pixel = cp.sum(mask)
pixel_idx = cp.zeros_like(mask, dtype=int)
pixel_idx[mask] = cp.arange(num_pixel)
has_left_mask = cp.logical_and(move_right(mask), mask)
has_right_mask = cp.logical_and(move_left(mask), mask)
has_bottom_mask = cp.logical_and(move_top(mask), mask)
has_top_mask = cp.logical_and(move_bottom(mask), mask)
nz_left = nz_horizontal[has_left_mask[mask]]
nz_right = nz_horizontal[has_right_mask[mask]]
nz_top = nz_vertical[has_top_mask[mask]]
nz_bottom = nz_vertical[has_bottom_mask[mask]]
data = cp.stack([-nz_left / step_size, nz_left / step_size], -1).flatten()
indices = cp.stack((pixel_idx[move_left(has_left_mask)], pixel_idx[has_left_mask]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_left_mask[mask].astype(int) * 2)])
D_horizontal_neg = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_right / step_size, nz_right / step_size], -1).flatten()
indices = cp.stack((pixel_idx[has_right_mask], pixel_idx[move_right(has_right_mask)]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_right_mask[mask].astype(int) * 2)])
D_horizontal_pos = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_top / step_size, nz_top / step_size], -1).flatten()
indices = cp.stack((pixel_idx[has_top_mask], pixel_idx[move_top(has_top_mask)]), -1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_top_mask[mask].astype(int) * 2)])
D_vertical_pos = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
data = cp.stack([-nz_bottom / step_size, nz_bottom / step_size], -1).flatten()
indices = cp.stack((pixel_idx[move_bottom(has_bottom_mask)], pixel_idx[has_bottom_mask]),
-1).flatten()
indptr = cp.concatenate([cp.array([0]), cp.cumsum(has_bottom_mask[mask].astype(int) * 2)])
D_vertical_neg = csr_matrix((data, indices, indptr), shape=(num_pixel, num_pixel))
return D_horizontal_pos, D_horizontal_neg, D_vertical_pos, D_vertical_neg
def construct_facets_from(mask):
idx = cp.zeros_like(mask, dtype=int)
idx[mask] = cp.arange(cp.sum(mask))
facet_move_top_mask = move_top(mask)
facet_move_left_mask = move_left(mask)
facet_move_top_left_mask = move_top_left(mask)
facet_top_left_mask = (
facet_move_top_mask * facet_move_left_mask * facet_move_top_left_mask * mask
)
facet_top_right_mask = move_right(facet_top_left_mask)
facet_bottom_left_mask = move_bottom(facet_top_left_mask)
facet_bottom_right_mask = move_bottom_right(facet_top_left_mask)
return cp.hstack((
4 * cp.ones((cp.sum(facet_top_left_mask).item(), 1)),
idx[facet_top_left_mask][:, None],
idx[facet_bottom_left_mask][:, None],
idx[facet_bottom_right_mask][:, None],
idx[facet_top_right_mask][:, None],
)).astype(int)
def map_depth_map_to_point_clouds(depth_map, mask, K=None, step_size=1):
# y
# | z
# | /
# |/
# o ---x
H, W = mask.shape
yy, xx = cp.meshgrid(cp.arange(W), cp.arange(H))
xx = cp.flip(xx, axis=0)
if K is None:
vertices = cp.zeros((H, W, 3))
vertices[..., 0] = xx * step_size
vertices[..., 1] = yy * step_size
vertices[..., 2] = depth_map
vertices = vertices[mask]
else:
u = cp.zeros((H, W, 3))
u[..., 0] = xx
u[..., 1] = yy
u[..., 2] = 1
u = u[mask].T # 3 x m
vertices = (cp.linalg.inv(K) @ u).T * depth_map[mask, cp.newaxis] # m x 3
return vertices
def sigmoid(x, k=1):
return 1 / (1 + cp.exp(-k * x))
def boundary_excluded_mask(mask):
top_mask = cp.pad(mask, ((1, 0), (0, 0)), "constant", constant_values=0)[:-1, :]
bottom_mask = cp.pad(mask, ((0, 1), (0, 0)), "constant", constant_values=0)[1:, :]
left_mask = cp.pad(mask, ((0, 0), (1, 0)), "constant", constant_values=0)[:, :-1]
right_mask = cp.pad(mask, ((0, 0), (0, 1)), "constant", constant_values=0)[:, 1:]
be_mask = top_mask * bottom_mask * left_mask * right_mask * mask
# discard single point
top_mask = cp.pad(be_mask, ((1, 0), (0, 0)), "constant", constant_values=0)[:-1, :]
bottom_mask = cp.pad(be_mask, ((0, 1), (0, 0)), "constant", constant_values=0)[1:, :]
left_mask = cp.pad(be_mask, ((0, 0), (1, 0)), "constant", constant_values=0)[:, :-1]
right_mask = cp.pad(be_mask, ((0, 0), (0, 1)), "constant", constant_values=0)[:, 1:]
bes_mask = (top_mask + bottom_mask + left_mask + right_mask).astype(bool)
be_mask = cp.logical_and(be_mask, bes_mask)
return be_mask
def create_boundary_matrix(mask):
num_pixel = cp.sum(mask)
pixel_idx = cp.zeros_like(mask, dtype=int)
pixel_idx[mask] = cp.arange(num_pixel)
be_mask = boundary_excluded_mask(mask)
boundary_mask = cp.logical_xor(be_mask, mask)
diag_data_term = boundary_mask[mask].astype(int)
B = diags(diag_data_term)
num_boundary_pixel = cp.sum(boundary_mask).item()
data_term = cp.concatenate((cp.ones(num_boundary_pixel), -cp.ones(num_boundary_pixel)))
row_idx = cp.tile(cp.arange(num_boundary_pixel), 2)
col_idx = cp.concatenate((pixel_idx[boundary_mask], pixel_idx[boundary_mask] + num_pixel))
B_full = coo_matrix((data_term, (row_idx, col_idx)), shape=(num_boundary_pixel, 2 * num_pixel))
return B, B_full
def double_side_bilateral_normal_integration(
normal_front,
normal_back,
normal_mask,
depth_front=None,
depth_back=None,
depth_mask=None,
k=2,
lambda_normal_back=1,
lambda_depth_front=1e-4,
lambda_depth_back=1e-2,
lambda_boundary_consistency=1,
step_size=1,
max_iter=150,
tol=1e-4,
cg_max_iter=5000,
cg_tol=1e-3,
cut_intersection=True,
):
# To avoid confusion, we list the coordinate systems in this code as follows
#
# pixel coordinates camera coordinates normal coordinates (the main paper's Fig. 1 (a))
# u x y
# | | z |
# | | / o -- x
# | |/ /
# o --- v o --- y z
# (bottom left)
# (o is the optical center;
# xy-plane is parallel to the image plane;
# +z is the viewing direction.)
#
# The input normal map should be defined in the normal coordinates.
# The camera matrix K should be defined in the camera coordinates.
# K = [[fx, 0, cx],
# [0, fy, cy],
# [0, 0, 1]]
num_normals = cp.sum(normal_mask)
normal_map_front = cp.asarray(normal_front)
normal_map_back = cp.asarray(normal_back)
normal_mask = cp.asarray(normal_mask)
if depth_mask is not None:
depth_map_front = cp.asarray(depth_front)
depth_map_back = cp.asarray(depth_back)
depth_mask = cp.asarray(depth_mask)
# transfer the normal map from the normal coordinates to the camera coordinates
nx_front = normal_map_front[normal_mask, 1]
ny_front = normal_map_front[normal_mask, 0]
nz_front = -normal_map_front[normal_mask, 2]
del normal_map_front
nx_back = normal_map_back[normal_mask, 1]
ny_back = normal_map_back[normal_mask, 0]
nz_back = -normal_map_back[normal_mask, 2]
del normal_map_back
# right, left, top, bottom
A3_f, A4_f, A1_f, A2_f = generate_dx_dy(
normal_mask, nz_horizontal=nz_front, nz_vertical=nz_front, step_size=step_size
)
A3_b, A4_b, A1_b, A2_b = generate_dx_dy(
normal_mask, nz_horizontal=nz_back, nz_vertical=nz_back, step_size=step_size
)
has_left_mask = cp.logical_and(move_right(normal_mask), normal_mask)
has_right_mask = cp.logical_and(move_left(normal_mask), normal_mask)
has_bottom_mask = cp.logical_and(move_top(normal_mask), normal_mask)
has_top_mask = cp.logical_and(move_bottom(normal_mask), normal_mask)
top_boundnary_mask = cp.logical_xor(has_top_mask, normal_mask)[normal_mask]
bottom_boundary_mask = cp.logical_xor(has_bottom_mask, normal_mask)[normal_mask]
left_boundary_mask = cp.logical_xor(has_left_mask, normal_mask)[normal_mask]
right_boudnary_mask = cp.logical_xor(has_right_mask, normal_mask)[normal_mask]
A_front_data = vstack((A1_f, A2_f, A3_f, A4_f))
A_front_zero = csr_matrix(A_front_data.shape)
A_front = hstack([A_front_data, A_front_zero])
A_back_data = vstack((A1_b, A2_b, A3_b, A4_b))
A_back_zero = csr_matrix(A_back_data.shape)
A_back = hstack([A_back_zero, A_back_data])
b_front = cp.concatenate((-nx_front, -nx_front, -ny_front, -ny_front))
b_back = cp.concatenate((-nx_back, -nx_back, -ny_back, -ny_back))
# initialization
W_front = spdiags(
0.5 * cp.ones(4 * num_normals), 0, 4 * num_normals, 4 * num_normals, format="csr"
)
W_back = spdiags(
0.5 * cp.ones(4 * num_normals), 0, 4 * num_normals, 4 * num_normals, format="csr"
)
z_front = cp.zeros(num_normals, float)
z_back = cp.zeros(num_normals, float)
z_combined = cp.concatenate((z_front, z_back))
B, B_full = create_boundary_matrix(normal_mask)
B_mat = lambda_boundary_consistency * coo_matrix(B_full.get().T @ B_full.get()) #bug
energy_list = []
if depth_mask is not None:
depth_mask_flat = depth_mask[normal_mask].astype(bool) # shape: (num_normals,)
z_prior_front = depth_map_front[normal_mask] # shape: (num_normals,)
z_prior_front[~depth_mask_flat] = 0
z_prior_back = depth_map_back[normal_mask]
z_prior_back[~depth_mask_flat] = 0
m = depth_mask[normal_mask].astype(int)
M = diags(m)
energy = (A_front @ z_combined - b_front).T @ W_front @ (A_front @ z_combined - b_front) + \
lambda_normal_back * (A_back @ z_combined - b_back).T @ W_back @ (A_back @ z_combined - b_back) + \
lambda_depth_front * (z_front - z_prior_front).T @ M @ (z_front - z_prior_front) + \
lambda_depth_back * (z_back - z_prior_back).T @ M @ (z_back - z_prior_back) + \
lambda_boundary_consistency * (z_back - z_front).T @ B @ (z_back - z_front)
depth_map_front_est = cp.ones_like(normal_mask, float) * cp.nan
depth_map_back_est = cp.ones_like(normal_mask, float) * cp.nan
facets_back = cp.asnumpy(construct_facets_from(normal_mask))
faces_back = np.concatenate((facets_back[:, [1, 4, 3]], facets_back[:, [1, 3, 2]]), axis=0)
faces_front = np.concatenate((facets_back[:, [1, 2, 3]], facets_back[:, [1, 3, 4]]), axis=0)
for i in range(max_iter):
A_mat_front = A_front_data.T @ W_front @ A_front_data
b_vec_front = A_front_data.T @ W_front @ b_front
A_mat_back = A_back_data.T @ W_back @ A_back_data
b_vec_back = A_back_data.T @ W_back @ b_back
if depth_mask is not None:
b_vec_front += lambda_depth_front * M @ z_prior_front
b_vec_back += lambda_depth_back * M @ z_prior_back
A_mat_front += lambda_depth_front * M
A_mat_back += lambda_depth_back * M
offset_front = cp.mean((z_prior_front - z_combined[:num_normals])[depth_mask_flat])
offset_back = cp.mean((z_prior_back - z_combined[num_normals:])[depth_mask_flat])
z_combined[:num_normals] = z_combined[:num_normals] + offset_front
z_combined[num_normals:] = z_combined[num_normals:] + offset_back
A_mat_combined = hstack([vstack((A_mat_front, csr_matrix((num_normals, num_normals)))), \
vstack((csr_matrix((num_normals, num_normals)), A_mat_back))]) + B_mat
b_vec_combined = cp.concatenate((b_vec_front, b_vec_back))
D = spdiags(
1 / cp.clip(A_mat_combined.diagonal(), 1e-5, None), 0, 2 * num_normals, 2 * num_normals,
"csr"
) # Jacob preconditioner
z_combined, _ = cg(
A_mat_combined, b_vec_combined, M=D, x0=z_combined, maxiter=cg_max_iter, tol=cg_tol
)
z_front = z_combined[:num_normals]
z_back = z_combined[num_normals:]
wu_f = sigmoid((A2_f.dot(z_front))**2 - (A1_f.dot(z_front))**2, k) # top
wv_f = sigmoid((A4_f.dot(z_front))**2 - (A3_f.dot(z_front))**2, k) # right
wu_f[top_boundnary_mask] = 0.5
wu_f[bottom_boundary_mask] = 0.5
wv_f[left_boundary_mask] = 0.5
wv_f[right_boudnary_mask] = 0.5
W_front = spdiags(
cp.concatenate((wu_f, 1 - wu_f, wv_f, 1 - wv_f)),
0,
4 * num_normals,
4 * num_normals,
format="csr"
)
wu_b = sigmoid((A2_b.dot(z_back))**2 - (A1_b.dot(z_back))**2, k) # top
wv_b = sigmoid((A4_b.dot(z_back))**2 - (A3_b.dot(z_back))**2, k) # right
wu_b[top_boundnary_mask] = 0.5
wu_b[bottom_boundary_mask] = 0.5
wv_b[left_boundary_mask] = 0.5
wv_b[right_boudnary_mask] = 0.5
W_back = spdiags(
cp.concatenate((wu_b, 1 - wu_b, wv_b, 1 - wv_b)),
0,
4 * num_normals,
4 * num_normals,
format="csr"
)
energy_old = energy
energy = (A_front_data @ z_front - b_front).T @ W_front @ (A_front_data @ z_front - b_front) + \
lambda_normal_back * (A_back_data @ z_back - b_back).T @ W_back @ (A_back_data @ z_back - b_back) + \
lambda_depth_front * (z_front - z_prior_front).T @ M @ (z_front - z_prior_front) + \
lambda_depth_back * (z_back - z_prior_back).T @ M @ (z_back - z_prior_back) +\
lambda_boundary_consistency * (z_back - z_front).T @ B @ (z_back - z_front)
energy_list.append(energy)
relative_energy = cp.abs(energy - energy_old) / energy_old
# print(f"step {i + 1}/{max_iter} energy: {energy:.3e}"
# f" relative energy: {relative_energy:.3e}")
if False:
# intermediate results
depth_map_front_est[normal_mask] = z_front
depth_map_back_est[normal_mask] = z_back
vertices_front = cp.asnumpy(
map_depth_map_to_point_clouds(
depth_map_front_est, normal_mask, K=None, step_size=step_size
)
)
vertices_back = cp.asnumpy(
map_depth_map_to_point_clouds(
depth_map_back_est, normal_mask, K=None, step_size=step_size
)
)
vertices_front, faces_front_ = remove_stretched_faces(vertices_front, faces_front)
vertices_back, faces_back_ = remove_stretched_faces(vertices_back, faces_back)
F_verts = verts_inverse_transform(torch.as_tensor(vertices_front).float(), 256.0)
B_verts = verts_inverse_transform(torch.as_tensor(vertices_back).float(), 256.0)
F_B_verts = torch.cat((F_verts, B_verts), dim=0)
F_B_faces = torch.cat((
torch.as_tensor(faces_front_).long(),
torch.as_tensor(faces_back_).long() + faces_front_.max() + 1
),
dim=0)
front_surf = trimesh.Trimesh(F_verts, faces_front_)
back_surf = trimesh.Trimesh(B_verts, faces_back_)
double_surf = trimesh.Trimesh(F_B_verts, F_B_faces)
bini_dir = "/home/yxiu/Code/ECON/log/bini/OBJ"
front_surf.export(osp.join(bini_dir, f"{i:04d}_F.obj"))
back_surf.export(osp.join(bini_dir, f"{i:04d}_B.obj"))
double_surf.export(osp.join(bini_dir, f"{i:04d}_FB.obj"))
if relative_energy < tol:
break
# del A1, A2, A3, A4, nx, ny
depth_map_front_est[normal_mask] = z_front
depth_map_back_est[normal_mask] = z_back
if cut_intersection:
# manually cut the intersection
normal_mask[depth_map_front_est >= depth_map_back_est] = False
depth_map_front_est[~normal_mask] = cp.nan
depth_map_back_est[~normal_mask] = cp.nan
vertices_front = cp.asnumpy(
map_depth_map_to_point_clouds(
depth_map_front_est, normal_mask, K=None, step_size=step_size
)
)
vertices_back = cp.asnumpy(
map_depth_map_to_point_clouds(depth_map_back_est, normal_mask, K=None, step_size=step_size)
)
facets_back = cp.asnumpy(construct_facets_from(normal_mask))
faces_back = np.concatenate((facets_back[:, [1, 4, 3]], facets_back[:, [1, 3, 2]]), axis=0)
faces_front = np.concatenate((facets_back[:, [1, 2, 3]], facets_back[:, [1, 3, 4]]), axis=0)
vertices_front, faces_front = remove_stretched_faces(vertices_front, faces_front)
vertices_back, faces_back = remove_stretched_faces(vertices_back, faces_back)
front_mesh = clean_floats(trimesh.Trimesh(vertices_front, faces_front))
back_mesh = clean_floats(trimesh.Trimesh(vertices_back, faces_back))
result = {
"F_verts": torch.as_tensor(front_mesh.vertices).float(), "F_faces": torch.as_tensor(
front_mesh.faces
).long(), "B_verts": torch.as_tensor(back_mesh.vertices).float(), "B_faces":
torch.as_tensor(back_mesh.faces).long(), "F_depth":
torch.as_tensor(depth_map_front_est).float(), "B_depth":
torch.as_tensor(depth_map_back_est).float()
}
return result
def save_normal_tensor(in_tensor, idx, png_path, thickness=0.0):
os.makedirs(os.path.dirname(png_path), exist_ok=True)
normal_F_arr = tensor2arr(in_tensor["normal_F"][idx:idx + 1])
normal_B_arr = tensor2arr(in_tensor["normal_B"][idx:idx + 1])
mask_normal_arr = tensor2arr(in_tensor["image"][idx:idx + 1], True)
depth_F_arr = depth2arr(in_tensor["depth_F"][idx])
depth_B_arr = depth2arr(in_tensor["depth_B"][idx])
BNI_dict = {}
# clothed human
BNI_dict["normal_F"] = normal_F_arr
BNI_dict["normal_B"] = normal_B_arr
BNI_dict["mask"] = mask_normal_arr > 0.
BNI_dict["depth_F"] = depth_F_arr - 100. - thickness
BNI_dict["depth_B"] = 100. - depth_B_arr + thickness
BNI_dict["depth_mask"] = depth_F_arr != -1.0
np.save(png_path + ".npy", BNI_dict, allow_pickle=True)
return BNI_dict