@@ -988,7 +988,12 @@ def transform_points(
988988
989989@torch .jit .script  
990990def  unproject_depth (depth : torch .Tensor , intrinsics : torch .Tensor ) ->  torch .Tensor :
991-     r"""Unproject depth image into a pointcloud. 
991+     r"""Unproject depth image into a pointcloud. This method assumes that depth 
992+     is provided orthogonally relative to the image plane, as opposed to absolutely relative to the camera's 
993+     principal point (perspective depth). To unproject a perspective depth image, use 
994+     :meth:`convert_perspective_depth_to_orthogonal_depth` to convert 
995+     to an orthogonal depth image prior to calling this method. Otherwise, the 
996+     created point cloud will be distorted, especially around the edges. 
992997
993998    This function converts depth images into points given the calibration matrix of the camera. 
994999
@@ -1059,6 +1064,105 @@ def unproject_depth(depth: torch.Tensor, intrinsics: torch.Tensor) -> torch.Tens
10591064    return  points_xyz 
10601065
10611066
1067+ @torch .jit .script  
1068+ def  convert_perspective_depth_to_orthogonal_depth (
1069+     perspective_depth : torch .Tensor , intrinsics : torch .Tensor 
1070+ ) ->  torch .Tensor :
1071+     r"""Provided depth image(s) where depth is provided as the distance to the principal 
1072+     point of the camera (perspective depth), this function converts it so that depth 
1073+     is provided as the distance to the camera's image plane (orthogonal depth). 
1074+ 
1075+     This is helpful because `unproject_depth` assumes that depth is expressed in 
1076+     the orthogonal depth format. 
1077+ 
1078+     If `perspective_depth` is a batch of depth images and `intrinsics` is a single intrinsic matrix, 
1079+     the same calibration matrix is applied to all depth images in the batch. 
1080+ 
1081+     The function assumes that the width and height are both greater than 1. 
1082+ 
1083+     Args: 
1084+         perspective_depth: The depth measurement obtained with the distance_to_camera replicator. 
1085+             Shape is (H, W) or or (H, W, 1) or (N, H, W) or (N, H, W, 1). 
1086+         intrinsics: A tensor providing camera's calibration matrix. Shape is (3, 3) or (N, 3, 3). 
1087+ 
1088+     Returns: 
1089+         The depth image as if obtained by the distance_to_image_plane replicator. Shape 
1090+             matches the input shape of depth 
1091+ 
1092+     Raises: 
1093+         ValueError: When depth is not of shape (H, W) or (H, W, 1) or (N, H, W) or (N, H, W, 1). 
1094+         ValueError: When intrinsics is not of shape (3, 3) or (N, 3, 3). 
1095+     """ 
1096+ 
1097+     # Clone inputs to avoid in-place modifications 
1098+     perspective_depth_batch  =  perspective_depth .clone ()
1099+     intrinsics_batch  =  intrinsics .clone ()
1100+ 
1101+     # Check if inputs are batched 
1102+     is_batched  =  perspective_depth_batch .dim () ==  4  or  (
1103+         perspective_depth_batch .dim () ==  3  and  perspective_depth_batch .shape [- 1 ] !=  1 
1104+     )
1105+ 
1106+     # Track whether the last dimension was singleton 
1107+     add_last_dim  =  False 
1108+     if  perspective_depth_batch .dim () ==  4  and  perspective_depth_batch .shape [- 1 ] ==  1 :
1109+         add_last_dim  =  True 
1110+         perspective_depth_batch  =  perspective_depth_batch .squeeze (dim = 3 )  # (N, H, W, 1) -> (N, H, W) 
1111+     if  perspective_depth_batch .dim () ==  3  and  perspective_depth_batch .shape [- 1 ] ==  1 :
1112+         add_last_dim  =  True 
1113+         perspective_depth_batch  =  perspective_depth_batch .squeeze (dim = 2 )  # (H, W, 1) -> (H, W) 
1114+ 
1115+     if  perspective_depth_batch .dim () ==  2 :
1116+         perspective_depth_batch  =  perspective_depth_batch [None ]  # (H, W) -> (1, H, W) 
1117+ 
1118+     if  intrinsics_batch .dim () ==  2 :
1119+         intrinsics_batch  =  intrinsics_batch [None ]  # (3, 3) -> (1, 3, 3) 
1120+ 
1121+     if  is_batched  and  intrinsics_batch .shape [0 ] ==  1 :
1122+         intrinsics_batch  =  intrinsics_batch .expand (perspective_depth_batch .shape [0 ], - 1 , - 1 )  # (1, 3, 3) -> (N, 3, 3) 
1123+ 
1124+     # Validate input shapes 
1125+     if  perspective_depth_batch .dim () !=  3 :
1126+         raise  ValueError (f"Expected perspective_depth to have 2, 3, or 4 dimensions; got { perspective_depth .shape }  ." )
1127+     if  intrinsics_batch .dim () !=  3 :
1128+         raise  ValueError (f"Expected intrinsics to have shape (3, 3) or (N, 3, 3); got { intrinsics .shape }  ." )
1129+ 
1130+     # Image dimensions 
1131+     im_height , im_width  =  perspective_depth_batch .shape [1 :]
1132+ 
1133+     # Get the intrinsics parameters 
1134+     fx  =  intrinsics_batch [:, 0 , 0 ].view (- 1 , 1 , 1 )
1135+     fy  =  intrinsics_batch [:, 1 , 1 ].view (- 1 , 1 , 1 )
1136+     cx  =  intrinsics_batch [:, 0 , 2 ].view (- 1 , 1 , 1 )
1137+     cy  =  intrinsics_batch [:, 1 , 2 ].view (- 1 , 1 , 1 )
1138+ 
1139+     # Create meshgrid of pixel coordinates 
1140+     u_grid  =  torch .arange (im_width , device = perspective_depth .device , dtype = perspective_depth .dtype )
1141+     v_grid  =  torch .arange (im_height , device = perspective_depth .device , dtype = perspective_depth .dtype )
1142+     u_grid , v_grid  =  torch .meshgrid (u_grid , v_grid , indexing = "xy" )
1143+ 
1144+     # Expand the grids for batch processing 
1145+     u_grid  =  u_grid .unsqueeze (0 ).expand (perspective_depth_batch .shape [0 ], - 1 , - 1 )
1146+     v_grid  =  v_grid .unsqueeze (0 ).expand (perspective_depth_batch .shape [0 ], - 1 , - 1 )
1147+ 
1148+     # Compute the squared terms for efficiency 
1149+     x_term  =  ((u_grid  -  cx ) /  fx ) **  2 
1150+     y_term  =  ((v_grid  -  cy ) /  fy ) **  2 
1151+ 
1152+     # Calculate the orthogonal (normal) depth 
1153+     normal_depth  =  perspective_depth_batch  /  torch .sqrt (1  +  x_term  +  y_term )
1154+ 
1155+     # Restore the last dimension if it was present in the input 
1156+     if  add_last_dim :
1157+         normal_depth  =  normal_depth .unsqueeze (- 1 )
1158+ 
1159+     # Return to original shape if input was not batched 
1160+     if  not  is_batched :
1161+         normal_depth  =  normal_depth .squeeze (0 )
1162+ 
1163+     return  normal_depth 
1164+ 
1165+ 
10621166@torch .jit .script  
10631167def  project_points (points : torch .Tensor , intrinsics : torch .Tensor ) ->  torch .Tensor :
10641168    r"""Projects 3D points into 2D image plane. 
0 commit comments