Spaces:
Build error
Build error
| # some tools developed for the vision class | |
| import numpy as np | |
| from numpy import cross, tan | |
| from numpy.linalg import norm, inv | |
| def normalize(v): | |
| return v / norm(v) | |
| def camera_pose(eye, front, up): | |
| z = normalize(-1 * front) | |
| x = normalize(cross(up, z)) | |
| y = normalize(cross(z, x)) | |
| # convert to col vector | |
| x = x.reshape(-1, 1) | |
| y = y.reshape(-1, 1) | |
| z = z.reshape(-1, 1) | |
| eye = eye.reshape(-1, 1) | |
| pose = np.block([ | |
| [x, y, z, eye], | |
| [0, 0, 0, 1] | |
| ]) | |
| return pose | |
| def compute_extrinsics(eye, front, up): | |
| pose = camera_pose(eye, front, up) | |
| world_2_cam = inv(pose) | |
| return world_2_cam | |
| def compute_intrinsics(aspect_ratio, fov, img_height_in_pix): | |
| # aspect ratio is w / h | |
| ndc = compute_proj_to_normalized(aspect_ratio, fov) | |
| # anything beyond [-1, 1] should be discarded | |
| # this did not mention how to do z-clipping; | |
| ndc_to_img = compute_normalized_to_img_trans(aspect_ratio, img_height_in_pix) | |
| intrinsic = ndc_to_img @ ndc | |
| return intrinsic | |
| def compute_proj_to_normalized(aspect, fov): | |
| # compared to standard OpenGL NDC intrinsic, | |
| # this skips the 3rd row treatment on z. hence the name partial_ndc | |
| fov_in_rad = fov / 180 * np.pi | |
| t = tan(fov_in_rad / 2) # tan half fov | |
| partial_ndc_intrinsic = np.array([ | |
| [1 / (t * aspect), 0, 0, 0], | |
| [0, 1 / t, 0, 0], | |
| [0, 0, -1, 0] # copy the negative distance for division | |
| ]) | |
| return partial_ndc_intrinsic | |
| def compute_normalized_to_img_trans(aspect, img_height_in_pix): | |
| img_h = img_height_in_pix | |
| img_w = img_height_in_pix * aspect | |
| # note the OpenGL convention that (0, 0) sits at the center of the pixel; | |
| # hence the extra -0.5 translation | |
| # this is useful when you shoot rays through a pixel to the scene | |
| ndc_to_img = np.array([ | |
| [img_w / 2, 0, img_w / 2 - 0.5], | |
| [0, img_h / 2, img_h / 2 - 0.5], | |
| [0, 0, 1] | |
| ]) | |
| img_y_coord_flip = np.array([ | |
| [1, 0, 0], | |
| [0, -1, img_h - 1], # note the -1 | |
| [0, 0, 1] | |
| ]) | |
| # the product of the above 2 matrices is equivalent to adding | |
| # - sign to the (1, 1) entry | |
| # you could have simply written | |
| # ndc_to_img = np.array([ | |
| # [img_w / 2, 0, img_w / 2 - 0.5], | |
| # [0, -img_h / 2, img_h / 2 - 0.5], | |
| # [0, 0, 1] | |
| # ]) | |
| ndc_to_img = img_y_coord_flip @ ndc_to_img | |
| return ndc_to_img | |
| def unproject(K, pixel_coords, depth=1.0): | |
| """sometimes also referred to as backproject | |
| pixel_coords: [n, 2] pixel locations | |
| depth: [n,] or [,] depth value. of a shape that is broadcastable with pix coords | |
| """ | |
| K = K[0:3, 0:3] | |
| pixel_coords = as_homogeneous(pixel_coords) | |
| pixel_coords = pixel_coords.T # [2+1, n], so that mat mult is on the left | |
| # this will give points with z = -1, which is exactly what you want since | |
| # your camera is facing the -ve z axis | |
| pts = inv(K) @ pixel_coords | |
| pts = pts * depth # [3, n] * [n,] broadcast | |
| pts = pts.T | |
| pts = as_homogeneous(pts) | |
| return pts | |
| """ | |
| these two functions are changed so that they can handle arbitrary number of | |
| dimensions >=1 | |
| """ | |
| def homogenize(pts): | |
| # pts: [..., d], where last dim of the d is the diviser | |
| *front, d = pts.shape | |
| pts = pts / pts[..., -1].reshape(*front, 1) | |
| return pts | |
| def as_homogeneous(pts, lib=np): | |
| # pts: [..., d] | |
| *front, d = pts.shape | |
| points = lib.ones((*front, d + 1)) | |
| points[..., :d] = pts | |
| return points | |
| def simple_point_render(pts, img_w, img_h, fov, eye, front, up): | |
| """ | |
| pts: [N, 3] | |
| """ | |
| canvas = np.ones((img_h, img_w, 3)) | |
| pts = as_homogeneous(pts) | |
| E = compute_extrinsics(eye, front, up) | |
| world_2_ndc = compute_proj_to_normalized(img_w / img_h, fov) | |
| ndc_to_img = compute_normalized_to_img_trans(img_w / img_h, img_h) | |
| pts = pts @ E.T | |
| pts = pts @ world_2_ndc.T | |
| pts = homogenize(pts) | |
| # now filter out outliers beyond [-1, 1] | |
| outlier_mask = (np.abs(pts) > 1.0).any(axis=1) | |
| pts = pts[~outlier_mask] | |
| pts = pts @ ndc_to_img.T | |
| # now draw each point | |
| pts = np.rint(pts).astype(np.int32) | |
| xs, ys, _ = pts.T | |
| canvas[ys, xs] = (1, 0, 0) | |
| return canvas | |