from email.policy import default import gradio as gr from transformers import DPTFeatureExtractor, DPTForDepthEstimation import torch import numpy as np from PIL import Image import open3d as o3d from pathlib import Path import os feature_extractor = DPTFeatureExtractor.from_pretrained("Intel/dpt-large") model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large") def process_image(image_path, voxel_s): voxel_s = max(voxel_s/500, 0.0001) image_path = Path(image_path) image_raw = Image.open(image_path) image = image_raw.resize( (800, int(800 * image_raw.size[1] / image_raw.size[0])), Image.Resampling.LANCZOS) # prepare image for the model encoding = feature_extractor(image, return_tensors="pt") # forward pass with torch.no_grad(): outputs = model(**encoding) predicted_depth = outputs.predicted_depth # interpolate to original size prediction = torch.nn.functional.interpolate( predicted_depth.unsqueeze(1), size=image.size[::-1], mode="bicubic", align_corners=False, ).squeeze() output = prediction.cpu().numpy() depth_image = (output * 255 / np.max(output)).astype('uint8') try: gltf_path = create_3d_voxels_obj( np.array(image), depth_image, image_path, voxel_s) img = Image.fromarray(depth_image) return [img, gltf_path, gltf_path] except Exception as e: print("Error reconstructing 3D model") raise Exception("Error reconstructing 3D model") def create_3d_voxels_obj(rgb_image, depth_image, image_path, voxel_s): depth_o3d = o3d.geometry.Image(depth_image) image_o3d = o3d.geometry.Image(rgb_image) rgbd_image = o3d.geometry.RGBDImage.create_from_color_and_depth( image_o3d, depth_o3d, convert_rgb_to_intensity=False) w = int(depth_image.shape[1]) h = int(depth_image.shape[0]) camera_intrinsic = o3d.camera.PinholeCameraIntrinsic() camera_intrinsic.set_intrinsics(w, h, 500, 500, w/2, h/2) pcd = o3d.geometry.PointCloud.create_from_rgbd_image( rgbd_image, camera_intrinsic) print('normals') pcd.normals = o3d.utility.Vector3dVector( np.zeros((1, 3))) # invalidate existing normals pcd.estimate_normals( search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.01, max_nn=30)) pcd.orient_normals_towards_camera_location( camera_location=np.array([0., 0., 1000.])) pcd.transform([[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]]) pcd.transform([[-1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) print('voxels') # ref https://towardsdatascience.com/how-to-automate-voxel-modelling-of-3d-point-cloud-with-python-459f4d43a227 voxel_size = round( max(pcd.get_max_bound()-pcd.get_min_bound())*voxel_s, 10) print("Voxel size", voxel_size, "voxel_s", voxel_s) voxel_grid = o3d.geometry.VoxelGrid.create_from_point_cloud( pcd, voxel_size=voxel_size) voxels = voxel_grid.get_voxels() vox_mesh = o3d.geometry.TriangleMesh() for v in voxels: cube = o3d.geometry.TriangleMesh.create_box(width=1, height=1, depth=1) cube.paint_uniform_color(v.color) cube.translate(v.grid_index, relative=False) vox_mesh += cube print(voxel_grid, vox_mesh) gltf_path = f'./{image_path.stem}.gltf' o3d.io.write_triangle_mesh(gltf_path, vox_mesh, write_triangle_uvs=True) return gltf_path title = "Demo: zero-shot depth estimation with DPT + 3D Voxels reconstruction" description = "This demo is a variation from the original DPT Demo. It uses the DPT model to predict the depth of an image and then reconstruct the 3D model as voxels." examples = [["examples/" + img, 10] for img in os.listdir("examples/")] iface = gr.Interface(fn=process_image, inputs=[ gr.Image( type="filepath", label="Input Image"), gr.Slider(value=10, minimum=5, maximum=100, step=1, label="Voxel Size",) ], outputs=[ gr.Image(label="predicted depth", type="pil"), gr.Model3D(label="3d mesh reconstruction", clear_color=[ 1.0, 1.0, 1.0, 1.0]), gr.File(label="3d gLTF") ], title=title, description=description, examples=examples, allow_flagging="never", cache_examples=False) iface.launch(debug=True)