|
<!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, |
|
"data": tensor.data, |
|
"size": tensor.size, |
|
} |
|
|
|
# Construct the 3D model from the depth map and the RGB image |
|
depth = predictions["predicted_depth"].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"], |
|
] |
|
) |
|
|
|
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-requirements> |
|
transformers_js_py |
|
trimesh |
|
</gradio-requirements> |
|
</gradio-lite> |
|
</body> |
|
</html> |