TKOSEI commited on
Commit
d4d439e
·
verified ·
1 Parent(s): 5861530
Files changed (2) hide show
  1. app.py +482 -0
  2. 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