| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| <title>Gradio-Lite: Serverless Gradio Running Entirely in Your Browser</title> |
| <meta name="description" content="Gradio-Lite: Serverless Gradio Running Entirely in Your Browser"> |
|
|
| <script type="module" crossorigin src="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.js"></script> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gradio/lite/dist/lite.css" /> |
|
|
| <style> |
| html, body { |
| margin: 0; |
| padding: 0; |
| height: 100%; |
| } |
| </style> |
| </head> |
| <body> |
| <gradio-lite> |
| <gradio-file name="app.py" entrypoint> |
| import gradio as gr |
| import numpy as np |
| import PIL |
| import trimesh |
| from transformers_js import import_transformers_js, as_url |
|
|
|
|
| transformers = await import_transformers_js() |
| pipeline = transformers.pipeline |
| depth_estimator = await pipeline('depth-estimation', 'Xenova/depth-anything-small-hf'); |
|
|
|
|
| def depthmap_to_glb_trimesh(depth_map, rgb_image, file_path): |
| assert depth_map.shape[:2] == rgb_image.shape[:2], "Depth map and RGB image must have the same dimensions" |
|
|
| # Generate vertices and faces |
| vertices = [] |
| colors = [] |
| faces = [] |
|
|
| height, width = depth_map.shape |
| for y in range(height): |
| for x in range(width): |
| z = depth_map[y, x] |
| vertices.append([x, y, z]) |
| colors.append(rgb_image[y, x]) |
|
|
| # Create faces (2 triangles per pixel, except for edges) |
| for y in range(height - 1): |
| for x in range(width - 1): |
| top_left = y * width + x |
| top_right = top_left + 1 |
| bottom_left = top_left + width |
| bottom_right = bottom_left + 1 |
|
|
| faces.append([top_left, bottom_left, top_right]) |
| faces.append([top_right, bottom_left, bottom_right]) |
|
|
| # Convert to numpy arrays |
| vertices = np.array(vertices, dtype=np.float64) |
| colors = np.array(colors, dtype=np.uint8) |
| faces = np.array(faces, dtype=np.int32) |
|
|
| mesh = trimesh.Trimesh(vertices=vertices, faces=faces, vertex_colors=colors, process=False) |
|
|
| # Export to GLB |
| mesh.export(file_path, file_type='glb') |
|
|
|
|
| def invert_depth(depth_map): |
| max_depth = np.max(depth_map) |
| return max_depth - depth_map |
|
|
|
|
| def invert_xy(map): |
| return map[::-1, ::-1] |
|
|
|
|
| async def estimate(image_path, depth_scale): |
| image = PIL.Image.open(image_path) |
| image.thumbnail((384, 384)) # Resize the image keeping the aspect ratio |
|
|
| predictions = await depth_estimator(as_url(image_path)) |
|
|
| depth_image = predictions["depth"].to_pil() |
|
|
| tensor = predictions["predicted_depth"] |
| tensor_data = { |
| "dims": tensor.dims, |
| "type": tensor.type, |
| "size": tensor.size, |
| } |
|
|
| # Construct the 3D model from the depth map and the RGB image |
| depth = predictions["predicted_depth"].to_numpy() |
| depth = invert_depth(depth) |
| depth = invert_xy(depth) |
|
|
| depth = depth * depth_scale |
|
|
| # The model outputs the depth map in a different size than the input image. |
| # So we resize the depth map to match the original image size. |
| depth = np.array(PIL.Image.fromarray(depth).resize(image.size)) |
|
|
| image_array = np.asarray(image) |
| image_array = invert_xy(image_array) |
|
|
| glb_file_path = "output.glb" |
| depthmap_to_glb_trimesh(depth, image_array, glb_file_path) |
|
|
| return depth_image, glb_file_path, tensor_data |
|
|
|
|
| demo = gr.Interface( |
| fn=estimate, |
| inputs=[ |
| gr.Image(type="filepath"), |
| gr.Slider(minimum=1, maximum=100, value=10, label="Depth Scale") |
| ], |
| outputs=[ |
| gr.Image(label="Depth Image"), |
| gr.Model3D(label="3D Model"), |
| gr.JSON(label="Tensor"), |
| ], |
| examples=[ |
| ["bread_small.png"], |
| ["cats.jpg"], |
| ] |
| ) |
|
|
| demo.launch() |
| </gradio-file> |
|
|
| <gradio-file name="bread_small.png" url="https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/bread_small.png" /> |
| <gradio-file name="cats.jpg" url="https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/cats.jpg" /> |
|
|
| <gradio-requirements> |
| transformers_js_py |
| trimesh |
| </gradio-requirements> |
| </gradio-lite> |
| </body> |
| </html> |
|
|