Spaces:
Running
Running
add
Browse files- app.py +482 -0
- requirements.txt +8 -0
app.py
ADDED
|
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import cv2
|
| 3 |
+
import torch
|
| 4 |
+
from PIL import Image, ImageOps
|
| 5 |
+
import numpy as np
|
| 6 |
+
import gradio as gr
|
| 7 |
+
import math
|
| 8 |
+
import os
|
| 9 |
+
import zipfile
|
| 10 |
+
import trimesh
|
| 11 |
+
import pygltflib
|
| 12 |
+
from scipy.ndimage import median_filter
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
# Depth-Anything V2 model setup (assuming the repository is cloned and weights downloaded)
|
| 16 |
+
from depth_anything_v2.dpt import DepthAnythingV2
|
| 17 |
+
|
| 18 |
+
DEVICE = 'cuda' if torch.cuda.is_available() else 'mps' if torch.backends.mps.is_available() else 'cpu'
|
| 19 |
+
|
| 20 |
+
model_configs = {
|
| 21 |
+
'vits': {'encoder': 'vits', 'features': 64, 'out_channels': [48, 96, 192, 384]},
|
| 22 |
+
'vitb': {'encoder': 'vitb', 'features': 128, 'out_channels': [96, 192, 384, 768]},
|
| 23 |
+
'vitl': {'encoder': 'vitl', 'features': 256, 'out_channels': [256, 512, 1024, 1024]},
|
| 24 |
+
'vitg': {'encoder': 'vitg', 'features': 384, 'out_channels': [1536, 1536, 1536, 1536]}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
encoder = 'vitl' # or 'vits', 'vitb', 'vitg'
|
| 28 |
+
|
| 29 |
+
model = DepthAnythingV2(**model_configs[encoder])
|
| 30 |
+
model.load_state_dict(torch.load(f'depth_anything_v2_{encoder}.pth', map_location='cpu'))
|
| 31 |
+
model = model.to(DEVICE).eval()
|
| 32 |
+
|
| 33 |
+
# Helper functions (from your notebook)
|
| 34 |
+
def quaternion_multiply(q1, q2):
|
| 35 |
+
x1, y1, z1, w1 = q1
|
| 36 |
+
x2, y2, z2, w2 = q2
|
| 37 |
+
return [
|
| 38 |
+
w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2,
|
| 39 |
+
w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2,
|
| 40 |
+
w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2,
|
| 41 |
+
w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2,
|
| 42 |
+
]
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def glb_add_lights(path_input, path_output):
|
| 46 |
+
"""
|
| 47 |
+
Adds directional lights in the horizontal plane to the glb file.
|
| 48 |
+
:param path_input: path to input glb
|
| 49 |
+
:param path_output: path to output glb
|
| 50 |
+
:return: None
|
| 51 |
+
"""
|
| 52 |
+
glb = pygltflib.GLTF2().load(path_input)
|
| 53 |
+
|
| 54 |
+
N = 3 # default max num lights in Babylon.js is 4
|
| 55 |
+
angle_step = 2 * math.pi / N
|
| 56 |
+
elevation_angle = math.radians(75)
|
| 57 |
+
|
| 58 |
+
light_colors = [
|
| 59 |
+
[1.0, 0.0, 0.0],
|
| 60 |
+
[0.0, 1.0, 0.0],
|
| 61 |
+
[0.0, 0.0, 1.0],
|
| 62 |
+
]
|
| 63 |
+
|
| 64 |
+
lights_extension = {
|
| 65 |
+
"lights": [
|
| 66 |
+
{"type": "directional", "color": light_colors[i], "intensity": 2.0}
|
| 67 |
+
for i in range(N)
|
| 68 |
+
]
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
if "KHR_lights_punctual" not in glb.extensionsUsed:
|
| 72 |
+
glb.extensionsUsed.append("KHR_lights_punctual")
|
| 73 |
+
glb.extensions["KHR_lights_punctual"] = lights_extension
|
| 74 |
+
|
| 75 |
+
light_nodes = []
|
| 76 |
+
for i in range(N):
|
| 77 |
+
angle = i * angle_step
|
| 78 |
+
|
| 79 |
+
pos_rot = [0.0, 0.0, math.sin(angle / 2), math.cos(angle / 2)]
|
| 80 |
+
elev_rot = [
|
| 81 |
+
math.sin(elevation_angle / 2),
|
| 82 |
+
0.0,
|
| 83 |
+
0.0,
|
| 84 |
+
math.cos(elevation_angle / 2),
|
| 85 |
+
]
|
| 86 |
+
rotation = quaternion_multiply(pos_rot, elev_rot)
|
| 87 |
+
|
| 88 |
+
node = {
|
| 89 |
+
"rotation": rotation,
|
| 90 |
+
"extensions": {"KHR_lights_punctual": {"light": i}},
|
| 91 |
+
}
|
| 92 |
+
light_nodes.append(node)
|
| 93 |
+
|
| 94 |
+
light_node_indices = list(range(len(glb.nodes), len(glb.nodes) + N))
|
| 95 |
+
glb.nodes.extend(light_nodes)
|
| 96 |
+
|
| 97 |
+
root_node_index = glb.scenes[glb.scene].nodes[0]
|
| 98 |
+
root_node = glb.nodes[root_node_index]
|
| 99 |
+
if hasattr(root_node, "children"):
|
| 100 |
+
root_node.children.extend(light_node_indices)
|
| 101 |
+
else:
|
| 102 |
+
root_node.children = light_node_indices
|
| 103 |
+
|
| 104 |
+
glb.save(path_output)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def extrude_depth_3d(
|
| 108 |
+
path_rgb,
|
| 109 |
+
path_depth,
|
| 110 |
+
path_out_base="../",
|
| 111 |
+
alpha=1.0,
|
| 112 |
+
invert=0,
|
| 113 |
+
output_model_scale=100,
|
| 114 |
+
filter_size=3,
|
| 115 |
+
coef_near=0.0,
|
| 116 |
+
coef_far=1.0,
|
| 117 |
+
emboss=0.3,
|
| 118 |
+
f_thic=0.05,
|
| 119 |
+
f_near=-0.15,
|
| 120 |
+
f_back=0.01,
|
| 121 |
+
vertex_colors=True,
|
| 122 |
+
scene_lights=True,
|
| 123 |
+
prepare_for_3d_printing=False,
|
| 124 |
+
zip_outputs=False,
|
| 125 |
+
):
|
| 126 |
+
f_far_inner = -emboss
|
| 127 |
+
f_far_outer = f_far_inner - f_back
|
| 128 |
+
|
| 129 |
+
f_near = max(f_near, f_far_inner)
|
| 130 |
+
|
| 131 |
+
depth_image = Image.open(path_depth)
|
| 132 |
+
mono_image = Image.open(path_rgb).convert("L")
|
| 133 |
+
|
| 134 |
+
if invert==1:
|
| 135 |
+
mono_image = ImageOps.invert(mono_image)
|
| 136 |
+
|
| 137 |
+
w, h = depth_image.size
|
| 138 |
+
d_max = max(w, h)
|
| 139 |
+
depth_image = np.array(depth_image).astype(np.double)
|
| 140 |
+
mono_image = np.array(mono_image).astype(np.double)
|
| 141 |
+
z_min, z_max = np.min(depth_image), np.max(depth_image)
|
| 142 |
+
m_min, m_max = np.min(mono_image), np.max(mono_image)
|
| 143 |
+
depth_image = (depth_image.astype(np.double) - z_min) / (z_max - z_min)
|
| 144 |
+
depth_image[depth_image < coef_near] = coef_near
|
| 145 |
+
depth_image[depth_image > coef_far] = coef_far
|
| 146 |
+
z_min, z_max = np.min(depth_image), np.max(depth_image)
|
| 147 |
+
depth_image = (depth_image - z_min) / (z_max - z_min)
|
| 148 |
+
mono_image = median_filter(mono_image, size=5)
|
| 149 |
+
mono_image = (mono_image.astype(np.double) - m_min) / (m_max - m_min)
|
| 150 |
+
mono_image_new = np.where(depth_image == coef_far, 1, mono_image)
|
| 151 |
+
m_min=np.min(mono_image_new)
|
| 152 |
+
mono_image_new = np.where(depth_image == coef_far, 0, mono_image)
|
| 153 |
+
m_max=np.max(mono_image_new)
|
| 154 |
+
mono_image = np.where(depth_image == coef_far, m_min, mono_image)
|
| 155 |
+
mono_image = (mono_image - m_min) / (m_max - m_min)
|
| 156 |
+
depth_image = np.where(depth_image != 1.0, (1-alpha) * depth_image + alpha * mono_image, depth_image)
|
| 157 |
+
#depth_image_new[depth_image < coef_near] = 0
|
| 158 |
+
#depth_image_new[depth_image > coef_far] = 1
|
| 159 |
+
#depth_image_new[depth_image_new < 0] = 0
|
| 160 |
+
depth_image = median_filter(depth_image, size=filter_size)
|
| 161 |
+
depth_image = emboss*(depth_image - np.min(depth_image)) / (np.max(depth_image) - np.min(depth_image))
|
| 162 |
+
Image.fromarray((depth_image * 255).astype(np.uint8)).convert("L").save(path_out_base+".png")
|
| 163 |
+
rgb_image = np.array(
|
| 164 |
+
Image.open(path_rgb).convert("RGB").resize((w, h), Image.Resampling.LANCZOS)
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
w_norm = w / float(d_max - 1)
|
| 168 |
+
h_norm = h / float(d_max - 1)
|
| 169 |
+
w_half = w_norm / 2
|
| 170 |
+
h_half = h_norm / 2
|
| 171 |
+
|
| 172 |
+
x, y = np.meshgrid(np.arange(w), np.arange(h))
|
| 173 |
+
x = x / float(d_max - 1) - w_half # [-w_half, w_half]
|
| 174 |
+
y = -y / float(d_max - 1) + h_half # [-h_half, h_half]
|
| 175 |
+
z = -depth_image # -depth_emboss (far) - 0 (near)
|
| 176 |
+
vertices_2d = np.stack((x, y, z), axis=-1)
|
| 177 |
+
vertices = vertices_2d.reshape(-1, 3)
|
| 178 |
+
colors = rgb_image[:, :, :3].reshape(-1, 3) / 255.0
|
| 179 |
+
|
| 180 |
+
faces = []
|
| 181 |
+
for y in range(h - 1):
|
| 182 |
+
for x in range(w - 1):
|
| 183 |
+
idx = y * w + x
|
| 184 |
+
faces.append([idx, idx + w, idx + 1])
|
| 185 |
+
faces.append([idx + 1, idx + w, idx + 1 + w])
|
| 186 |
+
|
| 187 |
+
# OUTER frame
|
| 188 |
+
|
| 189 |
+
nv = len(vertices)
|
| 190 |
+
vertices = np.append(
|
| 191 |
+
vertices,
|
| 192 |
+
[
|
| 193 |
+
[-w_half - f_thic, -h_half - f_thic, f_near], # 00
|
| 194 |
+
[-w_half - f_thic, -h_half - f_thic, f_far_outer], # 01
|
| 195 |
+
[w_half + f_thic, -h_half - f_thic, f_near], # 02
|
| 196 |
+
[w_half + f_thic, -h_half - f_thic, f_far_outer], # 03
|
| 197 |
+
[w_half + f_thic, h_half + f_thic, f_near], # 04
|
| 198 |
+
[w_half + f_thic, h_half + f_thic, f_far_outer], # 05
|
| 199 |
+
[-w_half - f_thic, h_half + f_thic, f_near], # 06
|
| 200 |
+
[-w_half - f_thic, h_half + f_thic, f_far_outer], # 07
|
| 201 |
+
],
|
| 202 |
+
axis=0,
|
| 203 |
+
)
|
| 204 |
+
faces.extend(
|
| 205 |
+
[
|
| 206 |
+
[nv + 0, nv + 1, nv + 2],
|
| 207 |
+
[nv + 2, nv + 1, nv + 3],
|
| 208 |
+
[nv + 2, nv + 3, nv + 4],
|
| 209 |
+
[nv + 4, nv + 3, nv + 5],
|
| 210 |
+
[nv + 4, nv + 5, nv + 6],
|
| 211 |
+
[nv + 6, nv + 5, nv + 7],
|
| 212 |
+
[nv + 6, nv + 7, nv + 0],
|
| 213 |
+
[nv + 0, nv + 7, nv + 1],
|
| 214 |
+
]
|
| 215 |
+
)
|
| 216 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * 8, axis=0)
|
| 217 |
+
|
| 218 |
+
# INNER frame
|
| 219 |
+
|
| 220 |
+
nv = len(vertices)
|
| 221 |
+
vertices_left_data = vertices_2d[:, 0] # H x 3
|
| 222 |
+
vertices_left_frame = vertices_2d[:, 0].copy() # H x 3
|
| 223 |
+
vertices_left_frame[:, 2] = f_near
|
| 224 |
+
vertices = np.append(vertices, vertices_left_data, axis=0)
|
| 225 |
+
vertices = np.append(vertices, vertices_left_frame, axis=0)
|
| 226 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 * h), axis=0)
|
| 227 |
+
for i in range(h - 1):
|
| 228 |
+
nvi_d = nv + i
|
| 229 |
+
nvi_f = nvi_d + h
|
| 230 |
+
faces.append([nvi_d, nvi_f, nvi_d + 1])
|
| 231 |
+
faces.append([nvi_d + 1, nvi_f, nvi_f + 1])
|
| 232 |
+
|
| 233 |
+
nv = len(vertices)
|
| 234 |
+
vertices_right_data = vertices_2d[:, -1] # H x 3
|
| 235 |
+
vertices_right_frame = vertices_2d[:, -1].copy() # H x 3
|
| 236 |
+
vertices_right_frame[:, 2] = f_near
|
| 237 |
+
vertices = np.append(vertices, vertices_right_data, axis=0)
|
| 238 |
+
vertices = np.append(vertices, vertices_right_frame, axis=0)
|
| 239 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 * h), axis=0)
|
| 240 |
+
for i in range(h - 1):
|
| 241 |
+
nvi_d = nv + i
|
| 242 |
+
nvi_f = nvi_d + h
|
| 243 |
+
faces.append([nvi_d, nvi_d + 1, nvi_f])
|
| 244 |
+
faces.append([nvi_d + 1, nvi_f + 1, nvi_f])
|
| 245 |
+
|
| 246 |
+
nv = len(vertices)
|
| 247 |
+
vertices_top_data = vertices_2d[0, :] # H x 3
|
| 248 |
+
vertices_top_frame = vertices_2d[0, :].copy() # H x 3
|
| 249 |
+
vertices_top_frame[:, 2] = f_near
|
| 250 |
+
vertices = np.append(vertices, vertices_top_data, axis=0)
|
| 251 |
+
vertices = np.append(vertices, vertices_top_frame, axis=0)
|
| 252 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 * w), axis=0)
|
| 253 |
+
for i in range(w - 1):
|
| 254 |
+
nvi_d = nv + i
|
| 255 |
+
nvi_f = nvi_d + w
|
| 256 |
+
faces.append([nvi_d, nvi_d + 1, nvi_f])
|
| 257 |
+
faces.append([nvi_d + 1, nvi_f + 1, nvi_f])
|
| 258 |
+
|
| 259 |
+
nv = len(vertices)
|
| 260 |
+
vertices_bottom_data = vertices_2d[-1, :] # H x 3
|
| 261 |
+
vertices_bottom_frame = vertices_2d[-1, :].copy() # H x 3
|
| 262 |
+
vertices_bottom_frame[:, 2] = f_near
|
| 263 |
+
vertices = np.append(vertices, vertices_bottom_data, axis=0)
|
| 264 |
+
vertices = np.append(vertices, vertices_bottom_frame, axis=0)
|
| 265 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 * w), axis=0)
|
| 266 |
+
for i in range(w - 1):
|
| 267 |
+
nvi_d = nv + i
|
| 268 |
+
nvi_f = nvi_d + w
|
| 269 |
+
faces.append([nvi_d, nvi_f, nvi_d + 1])
|
| 270 |
+
faces.append([nvi_d + 1, nvi_f, nvi_f + 1])
|
| 271 |
+
|
| 272 |
+
# FRONT frame
|
| 273 |
+
|
| 274 |
+
nv = len(vertices)
|
| 275 |
+
vertices = np.append(
|
| 276 |
+
vertices,
|
| 277 |
+
[
|
| 278 |
+
[-w_half - f_thic, -h_half - f_thic, f_near],
|
| 279 |
+
[-w_half - f_thic, h_half + f_thic, f_near],
|
| 280 |
+
],
|
| 281 |
+
axis=0,
|
| 282 |
+
)
|
| 283 |
+
vertices = np.append(vertices, vertices_left_frame, axis=0)
|
| 284 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 + h), axis=0)
|
| 285 |
+
for i in range(h - 1):
|
| 286 |
+
faces.append([nv, nv + 2 + i + 1, nv + 2 + i])
|
| 287 |
+
faces.append([nv, nv + 2, nv + 1])
|
| 288 |
+
|
| 289 |
+
nv = len(vertices)
|
| 290 |
+
vertices = np.append(
|
| 291 |
+
vertices,
|
| 292 |
+
[
|
| 293 |
+
[w_half + f_thic, h_half + f_thic, f_near],
|
| 294 |
+
[w_half + f_thic, -h_half - f_thic, f_near],
|
| 295 |
+
],
|
| 296 |
+
axis=0,
|
| 297 |
+
)
|
| 298 |
+
vertices = np.append(vertices, vertices_right_frame, axis=0)
|
| 299 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 + h), axis=0)
|
| 300 |
+
for i in range(h - 1):
|
| 301 |
+
faces.append([nv, nv + 2 + i, nv + 2 + i + 1])
|
| 302 |
+
faces.append([nv, nv + h + 1, nv + 1])
|
| 303 |
+
|
| 304 |
+
nv = len(vertices)
|
| 305 |
+
vertices = np.append(
|
| 306 |
+
vertices,
|
| 307 |
+
[
|
| 308 |
+
[w_half + f_thic, h_half + f_thic, f_near],
|
| 309 |
+
[-w_half - f_thic, h_half + f_thic, f_near],
|
| 310 |
+
],
|
| 311 |
+
axis=0,
|
| 312 |
+
)
|
| 313 |
+
vertices = np.append(vertices, vertices_top_frame, axis=0)
|
| 314 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 + w), axis=0)
|
| 315 |
+
for i in range(w - 1):
|
| 316 |
+
faces.append([nv, nv + 2 + i, nv + 2 + i + 1])
|
| 317 |
+
faces.append([nv, nv + 1, nv + 2])
|
| 318 |
+
|
| 319 |
+
nv = len(vertices)
|
| 320 |
+
vertices = np.append(
|
| 321 |
+
vertices,
|
| 322 |
+
[
|
| 323 |
+
[-w_half - f_thic, -h_half - f_thic, f_near],
|
| 324 |
+
[w_half + f_thic, -h_half - f_thic, f_near],
|
| 325 |
+
],
|
| 326 |
+
axis=0,
|
| 327 |
+
)
|
| 328 |
+
vertices = np.append(vertices, vertices_bottom_frame, axis=0)
|
| 329 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * (2 + w), axis=0)
|
| 330 |
+
for i in range(w - 1):
|
| 331 |
+
faces.append([nv, nv + 2 + i + 1, nv + 2 + i])
|
| 332 |
+
faces.append([nv, nv + 1, nv + w + 1])
|
| 333 |
+
|
| 334 |
+
# BACK frame
|
| 335 |
+
|
| 336 |
+
nv = len(vertices)
|
| 337 |
+
vertices = np.append(
|
| 338 |
+
vertices,
|
| 339 |
+
[
|
| 340 |
+
[-w_half - f_thic, -h_half - f_thic, f_far_outer], # 00
|
| 341 |
+
[w_half + f_thic, -h_half - f_thic, f_far_outer], # 01
|
| 342 |
+
[w_half + f_thic, h_half + f_thic, f_far_outer], # 02
|
| 343 |
+
[-w_half - f_thic, h_half + f_thic, f_far_outer], # 03
|
| 344 |
+
],
|
| 345 |
+
axis=0,
|
| 346 |
+
)
|
| 347 |
+
faces.extend(
|
| 348 |
+
[
|
| 349 |
+
[nv + 0, nv + 2, nv + 1],
|
| 350 |
+
[nv + 2, nv + 0, nv + 3],
|
| 351 |
+
]
|
| 352 |
+
)
|
| 353 |
+
colors = np.append(colors, [[0.5, 0.5, 0.5]] * 4, axis=0)
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
trimesh_kwargs = {}
|
| 357 |
+
if vertex_colors:
|
| 358 |
+
trimesh_kwargs["vertex_colors"] = colors
|
| 359 |
+
mesh = trimesh.Trimesh(vertices=vertices, faces=faces, **trimesh_kwargs)
|
| 360 |
+
|
| 361 |
+
mesh.merge_vertices()
|
| 362 |
+
|
| 363 |
+
current_max_dimension = max(mesh.extents)
|
| 364 |
+
scaling_factor = output_model_scale / current_max_dimension
|
| 365 |
+
mesh.apply_scale(scaling_factor)
|
| 366 |
+
|
| 367 |
+
if prepare_for_3d_printing:
|
| 368 |
+
rotation_mat = trimesh.transformations.rotation_matrix(
|
| 369 |
+
np.radians(0), [0.5, 0, 0]
|
| 370 |
+
)
|
| 371 |
+
mesh.apply_transform(rotation_mat)
|
| 372 |
+
|
| 373 |
+
if path_out_base is None:
|
| 374 |
+
path_out_base = os.path.splitext(path_depth)[0].replace("_16bit", "")
|
| 375 |
+
path_out_glb = path_out_base + ".glb"
|
| 376 |
+
path_out_stl = path_out_base + ".stl"
|
| 377 |
+
path_out_obj = path_out_base + ".obj"
|
| 378 |
+
|
| 379 |
+
mesh.export(path_out_stl, file_type="stl")
|
| 380 |
+
"""
|
| 381 |
+
mesh.export(path_out_glb, file_type="glb")
|
| 382 |
+
if scene_lights:
|
| 383 |
+
glb_add_lights(path_out_glb, path_out_glb)
|
| 384 |
+
mesh.export(path_out_obj, file_type="obj")
|
| 385 |
+
|
| 386 |
+
if zip_outputs:
|
| 387 |
+
with zipfile.ZipFile(path_out_glb + ".zip", "w", zipfile.ZIP_DEFLATED) as zipf:
|
| 388 |
+
arcname = os.path.basename(os.path.splitext(path_out_glb)[0]) + ".glb"
|
| 389 |
+
zipf.write(path_out_glb, arcname=arcname)
|
| 390 |
+
path_out_glb = path_out_glb + ".zip"
|
| 391 |
+
with zipfile.ZipFile(path_out_stl + ".zip", "w", zipfile.ZIP_DEFLATED) as zipf:
|
| 392 |
+
arcname = os.path.basename(os.path.splitext(path_out_stl)[0]) + ".stl"
|
| 393 |
+
zipf.write(path_out_stl, arcname=arcname)
|
| 394 |
+
path_out_stl = path_out_stl + ".zip"
|
| 395 |
+
with zipfile.ZipFile(path_out_obj + ".zip", "w", zipfile.ZIP_DEFLATED) as zipf:
|
| 396 |
+
arcname = os.path.basename(os.path.splitext(path_out_obj)[0]) + ".obj"
|
| 397 |
+
zipf.write(path_out_obj, arcname=arcname)
|
| 398 |
+
path_out_obj = path_out_obj + ".zip"
|
| 399 |
+
"""
|
| 400 |
+
return path_out_glb, path_out_stl, path_out_obj
|
| 401 |
+
|
| 402 |
+
def scale_to_width(img, length):
|
| 403 |
+
if img.width < img.height:
|
| 404 |
+
width = length
|
| 405 |
+
height = round(img.height * length / img.width)
|
| 406 |
+
else:
|
| 407 |
+
width = round(img.width * length / img.height)
|
| 408 |
+
height = length
|
| 409 |
+
return (width,height)
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
# Gradio Interface function
|
| 413 |
+
def process_image_and_generate_stl(image_input, depth_near, depth_far, thickness, alpha):
|
| 414 |
+
# Depth Estimation
|
| 415 |
+
raw_img = cv2.imread(image_input)
|
| 416 |
+
depth = model.infer_image(raw_img) # HxW raw depth map in numpy
|
| 417 |
+
|
| 418 |
+
# Save depth map temporarily
|
| 419 |
+
depth_output_path = "output_depth.png"
|
| 420 |
+
cv2.imwrite(depth_output_path, depth)
|
| 421 |
+
|
| 422 |
+
# Prepare images for 3D model generation
|
| 423 |
+
img_rgb = image_input
|
| 424 |
+
img_depth = depth_output_path
|
| 425 |
+
inv = 0 # Assuming no inversion for now, based on previous code
|
| 426 |
+
# Image.open(img_rgb).convert("L").save("example_1_black.png") # This line might not be necessary for the final output
|
| 427 |
+
size = scale_to_width(Image.open(img_rgb), 512)
|
| 428 |
+
Image.open(img_rgb).resize(size, Image.Resampling.LANCZOS).save("one.png") # Use Resampling.LANCZOS
|
| 429 |
+
if inv == 1:
|
| 430 |
+
Image.open(img_depth).convert(mode="F").resize(size, Image.Resampling.BILINEAR).convert("I").save("two.png") # Use Resampling.BILINEAR
|
| 431 |
+
else:
|
| 432 |
+
img=Image.open(img_depth).convert(mode="F").resize(size, Image.Resampling.BILINEAR).convert("I") # Use Resampling.BILINEAR
|
| 433 |
+
img = np.array(img).astype(np.double)
|
| 434 |
+
im_max=np.max(img)
|
| 435 |
+
im_min=np.min(img)
|
| 436 |
+
img=(1-(img-im_min)/(im_max-im_min))*im_max
|
| 437 |
+
img=Image.fromarray(img)
|
| 438 |
+
img.convert("I").save("two.png")
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
# 3D Model Generation
|
| 442 |
+
output_path_base = "generated_relief"
|
| 443 |
+
glb_path, stl_path, obj_path = extrude_depth_3d(
|
| 444 |
+
"one.png",
|
| 445 |
+
"two.png",
|
| 446 |
+
alpha=alpha,
|
| 447 |
+
invert=inv,
|
| 448 |
+
path_out_base=output_path_base,
|
| 449 |
+
output_model_scale=100,
|
| 450 |
+
filter_size=5, # Using 5 based on previous code
|
| 451 |
+
coef_near=depth_near,
|
| 452 |
+
coef_far=depth_far,
|
| 453 |
+
emboss=thickness,
|
| 454 |
+
f_thic=0.0, # Using 0.0 based on previous code
|
| 455 |
+
f_near=-thickness, # Using -thickness based on previous code
|
| 456 |
+
f_back=0.01, # Using 0.01 based on previous code
|
| 457 |
+
vertex_colors=True,
|
| 458 |
+
scene_lights=True,
|
| 459 |
+
prepare_for_3d_printing=True,
|
| 460 |
+
)
|
| 461 |
+
|
| 462 |
+
return stl_path # Return the path to the generated STL file
|
| 463 |
+
|
| 464 |
+
|
| 465 |
+
# Gradio Interface definition
|
| 466 |
+
iface = gr.Interface(
|
| 467 |
+
fn=process_image_and_generate_stl,
|
| 468 |
+
inputs=[
|
| 469 |
+
gr.Image(type="filepath", label="Upload Image"),
|
| 470 |
+
gr.Slider(minimum=0, maximum=1.0, value=0, label="Depth Near"),
|
| 471 |
+
gr.Slider(minimum=0, maximum=1.0, value=1.0, label="Depth Far"),
|
| 472 |
+
gr.Slider(minimum=0.1, maximum=1.0, value=0.3, label="Thickness"),
|
| 473 |
+
gr.Slider(minimum=0, maximum=1.0, value=0.05, label="Alpha"),
|
| 474 |
+
],
|
| 475 |
+
outputs=gr.File(label="Download STL File"), # Use gr.File() for file downloads
|
| 476 |
+
title="Image to 2.5D Relief Model Generator",
|
| 477 |
+
description="Upload an image, set parameters, and generate a 2.5D relief model (.stl file)."
|
| 478 |
+
)
|
| 479 |
+
|
| 480 |
+
# Launch the interface (for local testing)
|
| 481 |
+
if __name__ == "__main__":
|
| 482 |
+
iface.launch(debug=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio
|
| 2 |
+
numpy
|
| 3 |
+
Pillow
|
| 4 |
+
trimesh
|
| 5 |
+
pygltflib
|
| 6 |
+
scipy
|
| 7 |
+
torch
|
| 8 |
+
opencv-python
|