# Copyright 2016 The TensorFlow Authors All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """Utilities for processing depth images. """ import numpy as np import src.rotation_utils as ru import src.utils as utils def get_camera_matrix(width, height, fov): """Returns a camera matrix from image size and fov.""" xc = (width-1.) / 2. zc = (height-1.) / 2. f = (width / 2.) / np.tan(np.deg2rad(fov / 2.)) camera_matrix = utils.Foo(xc=xc, zc=zc, f=f) return camera_matrix def get_point_cloud_from_z(Y, camera_matrix): """Projects the depth image Y into a 3D point cloud. Inputs: Y is ...xHxW camera_matrix Outputs: X is positive going right Y is positive into the image Z is positive up in the image XYZ is ...xHxWx3 """ x, z = np.meshgrid(np.arange(Y.shape[-1]), np.arange(Y.shape[-2]-1, -1, -1)) for i in range(Y.ndim-2): x = np.expand_dims(x, axis=0) z = np.expand_dims(z, axis=0) X = (x-camera_matrix.xc) * Y / camera_matrix.f Z = (z-camera_matrix.zc) * Y / camera_matrix.f XYZ = np.concatenate((X[...,np.newaxis], Y[...,np.newaxis], Z[...,np.newaxis]), axis=X.ndim) return XYZ def make_geocentric(XYZ, sensor_height, camera_elevation_degree): """Transforms the point cloud into geocentric coordinate frame. Input: XYZ : ...x3 sensor_height : height of the sensor camera_elevation_degree : camera elevation to rectify. Output: XYZ : ...x3 """ R = ru.get_r_matrix([1.,0.,0.], angle=np.deg2rad(camera_elevation_degree)) XYZ = np.matmul(XYZ.reshape(-1,3), R.T).reshape(XYZ.shape) XYZ[...,2] = XYZ[...,2] + sensor_height return XYZ def bin_points(XYZ_cms, map_size, z_bins, xy_resolution): """Bins points into xy-z bins XYZ_cms is ... x H x W x3 Outputs is ... x map_size x map_size x (len(z_bins)+1) """ sh = XYZ_cms.shape XYZ_cms = XYZ_cms.reshape([-1, sh[-3], sh[-2], sh[-1]]) n_z_bins = len(z_bins)+1 map_center = (map_size-1.)/2. counts = [] isvalids = [] for XYZ_cm in XYZ_cms: isnotnan = np.logical_not(np.isnan(XYZ_cm[:,:,0])) X_bin = np.round(XYZ_cm[:,:,0] / xy_resolution + map_center).astype(np.int32) Y_bin = np.round(XYZ_cm[:,:,1] / xy_resolution + map_center).astype(np.int32) Z_bin = np.digitize(XYZ_cm[:,:,2], bins=z_bins).astype(np.int32) isvalid = np.array([X_bin >= 0, X_bin < map_size, Y_bin >= 0, Y_bin < map_size, Z_bin >= 0, Z_bin < n_z_bins, isnotnan]) isvalid = np.all(isvalid, axis=0) ind = (Y_bin * map_size + X_bin) * n_z_bins + Z_bin ind[np.logical_not(isvalid)] = 0 count = np.bincount(ind.ravel(), isvalid.ravel().astype(np.int32), minlength=map_size*map_size*n_z_bins) count = np.reshape(count, [map_size, map_size, n_z_bins]) counts.append(count) isvalids.append(isvalid) counts = np.array(counts).reshape(list(sh[:-3]) + [map_size, map_size, n_z_bins]) isvalids = np.array(isvalids).reshape(list(sh[:-3]) + [sh[-3], sh[-2], 1]) return counts, isvalids