zhigangjiang's picture
no message
88b0dcb
"""
@date: 2021/6/30
@description:
"""
import numpy as np
from typing import List
from utils.boundary import *
from scipy.optimize import least_squares
from functools import partial
def lsq_fit(ceil_norm, floor_norm):
"""
Least Squares
:param ceil_norm:
:param floor_norm:
:return:
"""
def error_fun(ratio, ceil_norm, floor_norm):
error = np.abs(ratio * ceil_norm - floor_norm)
return error
init_ratio = np.mean(floor_norm / ceil_norm, axis=-1)
error_func = partial(error_fun, ceil_norm=ceil_norm, floor_norm=floor_norm)
ret = least_squares(error_func, init_ratio, verbose=0)
ratio = ret.x[0]
return ratio
def mean_percentile_fit(ceil_norm, floor_norm, p1=25, p2=75):
"""
:param ceil_norm:
:param floor_norm:
:param p1:
:param p2:
:return:
"""
ratio = floor_norm / ceil_norm
r_min = np.percentile(ratio, p1)
r_max = np.percentile(ratio, p2)
return ratio[(r_min <= ratio) & (ratio <= r_max)].mean()
def calc_ceil_ratio(boundaries: List[np.array], mode='lsq'):
"""
:param boundaries: [ [[cu1, cv1], [cu2, cv2], ...], [[fu1, fv1], [fu2, fv2], ...] ]
:param mode: 'lsq' or 'mean'
:return:
"""
assert len(boundaries[0].shape) < 4 and len(boundaries[1].shape) < 4, 'error shape'
if not is_normal_layout(boundaries):
return 0
ceil_boundary = boundaries[0]
floor_boundary = boundaries[1]
assert ceil_boundary.shape[-2] == floor_boundary.shape[-2], "boundary need same length"
ceil_xyz = uv2xyz(ceil_boundary, -1)
floor_xyz = uv2xyz(floor_boundary, 1)
ceil_xz = ceil_xyz[..., ::2]
floor_xz = floor_xyz[..., ::2]
ceil_norm = np.linalg.norm(ceil_xz, axis=-1)
floor_norm = np.linalg.norm(floor_xz, axis=-1)
if mode == "lsq":
if len(ceil_norm.shape) == 2:
ratio = np.array([lsq_fit(ceil_norm[i], floor_norm[i]) for i in range(ceil_norm.shape[0])])
else:
ratio = lsq_fit(ceil_norm, floor_norm)
else:
if len(ceil_norm.shape) == 2:
ratio = np.array([mean_percentile_fit(ceil_norm[i], floor_norm[i]) for i in range(ceil_norm.shape[0])])
else:
ratio = mean_percentile_fit(ceil_norm, floor_norm)
return ratio
def calc_ceil_height(boundaries: List[np.array], camera_height=1.6, mode='lsq') -> float:
"""
:param boundaries: [ [[cu1, cv1], [cu2, cv2], ...], [[fu1, fv1], [fu2, fv2], ...] ]
:param camera_height:
:param mode:
:return:
"""
ratio = calc_ceil_ratio(boundaries, mode)
ceil_height = camera_height * ratio
return ceil_height
def calc_room_height(boundaries: List[np.array], camera_height=1.6, mode='lsq') -> float:
"""
:param boundaries: also can corners,format: [ [[cu1, cv1], [cu2, cv2], ...], [[fu1, fv1], [fu2, fv2], ...] ],
0 denotes ceil, 1 denotes floor
:param camera_height: actual camera height determines the scale
:param mode: fitting method lsq or mean
:return:
"""
ceil_height = calc_ceil_height(boundaries, camera_height, mode)
room_height = camera_height + ceil_height
return room_height
def height2ratio(height, camera_height=1.6):
ceil_height = height - camera_height
ratio = ceil_height / camera_height
return ratio
def ratio2height(ratio, camera_height=1.6):
ceil_height = camera_height * ratio
room_height = camera_height + ceil_height
return room_height
if __name__ == '__main__':
from dataset.mp3d_dataset import MP3DDataset
dataset = MP3DDataset(root_dir="../src/dataset/mp3d", mode="train")
for data in dataset:
ceil_corners = data['corners'][::2]
floor_corners = data['corners'][1::2]
# ceil_boundary = corners2boundary(ceil_corners, length=1024)
# floor_boundary = corners2boundary(floor_corners, length=1024)
room_height1 = calc_room_height([ceil_corners, floor_corners], camera_height=1.6, mode='mean')
room_height2 = calc_room_height([ceil_corners, floor_corners], camera_height=1.6, mode='lsq')
print(room_height1, room_height2, data['cameraCeilingHeight'] + 1.6)