toshas commited on
Commit
50f7486
1 Parent(s): fb4c01b

add output caching

Browse files

lock requirements
move assets to git lfs
add comments in code

.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.glb filter=lfs diff=lfs merge=lfs -text
37
+ *.ply filter=lfs diff=lfs merge=lfs -text
files/horse.ply.output/output.glb ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6e57814fdca91e80b2dad1912093f94d348802be932e3b0560d73c895519a31
3
+ size 1700664
main.py CHANGED
@@ -27,6 +27,12 @@ def convert_formats(path_input, target_ext):
27
 
28
 
29
  def add_lights(path_input, path_output):
 
 
 
 
 
 
30
  glb = pygltflib.GLTF2().load(path_input)
31
 
32
  N = 3 # default max num lights in Babylon.js is 4
@@ -34,11 +40,7 @@ def add_lights(path_input, path_output):
34
 
35
  lights_extension = {
36
  "lights": [
37
- {
38
- "type": "directional",
39
- "color": [1.0, 1.0, 1.0],
40
- "intensity": 2.0
41
- }
42
  for _ in range(N)
43
  ]
44
  }
@@ -50,19 +52,10 @@ def add_lights(path_input, path_output):
50
  light_nodes = []
51
  for i in range(N):
52
  angle = i * angle_step
53
- rotation = [
54
- 0.0,
55
- math.sin(angle / 2),
56
- 0.0,
57
- math.cos(angle / 2)
58
- ]
59
  node = {
60
  "rotation": rotation,
61
- "extensions": {
62
- "KHR_lights_punctual": {
63
- "light": i
64
- }
65
- }
66
  }
67
  light_nodes.append(node)
68
 
@@ -71,7 +64,7 @@ def add_lights(path_input, path_output):
71
 
72
  root_node_index = glb.scenes[glb.scene].nodes[0]
73
  root_node = glb.nodes[root_node_index]
74
- if hasattr(root_node, 'children'):
75
  root_node.children.extend(light_node_indices)
76
  else:
77
  root_node.children = light_node_indices
@@ -85,14 +78,20 @@ class Model3D(gr.Model3D):
85
  """
86
 
87
  def postprocess(self, y: str | Path | None) -> dict[str, str] | None:
 
 
 
 
 
88
  if y is not None:
89
  y = convert_formats(y, "glb")
90
  out = super().postprocess(y)
91
  return out
92
 
93
 
94
- def breathe_new_life_into_3d_model(path_input, prompt):
95
  """
 
96
  @inproceedings{wang2023breathing,
97
  title={Breathing New Life into 3D Assets with Generative Repainting},
98
  author={Wang, Tianfu and Kanakis, Menelaos and Schindler, Konrad and Van Gool, Luc and Obukhov, Anton},
@@ -100,7 +99,17 @@ def breathe_new_life_into_3d_model(path_input, prompt):
100
  year={2023},
101
  publisher={BMVA Press}
102
  }
 
 
 
 
103
  """
 
 
 
 
 
 
104
  path_output_dir = path_input + ".output"
105
  os.makedirs(path_output_dir, exist_ok=True)
106
 
@@ -129,6 +138,9 @@ def breathe_new_life_into_3d_model(path_input, prompt):
129
 
130
 
131
  def run():
 
 
 
132
  desc = """
133
  <p align="center">
134
  <a title="Website" href="https://www.obukhov.ai/repainting_3d_assets" target="_blank" rel="noopener noreferrer" style="display: inline-block;">
@@ -162,7 +174,10 @@ def run():
162
  elem_classes="viewport",
163
  label="Input Model",
164
  ),
165
- gr.Textbox(label="Text Prompt"),
 
 
 
166
  ],
167
  outputs=[
168
  gr.Model3D(
@@ -175,6 +190,9 @@ def run():
175
  [
176
  os.path.join(os.path.dirname(__file__), "files/horse.ply"),
177
  "pastel superhero unicorn",
 
 
 
178
  ],
179
  ],
180
  cache_examples=True,
 
27
 
28
 
29
  def add_lights(path_input, path_output):
30
+ """
31
+ Adds directional lights in the horizontal plane to the glb file.
32
+ :param path_input: path to input glb
33
+ :param path_output: path to output glb
34
+ :return: None
35
+ """
36
  glb = pygltflib.GLTF2().load(path_input)
37
 
38
  N = 3 # default max num lights in Babylon.js is 4
 
40
 
41
  lights_extension = {
42
  "lights": [
43
+ {"type": "directional", "color": [1.0, 1.0, 1.0], "intensity": 2.0}
 
 
 
 
44
  for _ in range(N)
45
  ]
46
  }
 
52
  light_nodes = []
53
  for i in range(N):
54
  angle = i * angle_step
55
+ rotation = [0.0, math.sin(angle / 2), 0.0, math.cos(angle / 2)]
 
 
 
 
 
56
  node = {
57
  "rotation": rotation,
58
+ "extensions": {"KHR_lights_punctual": {"light": i}},
 
 
 
 
59
  }
60
  light_nodes.append(node)
61
 
 
64
 
65
  root_node_index = glb.scenes[glb.scene].nodes[0]
66
  root_node = glb.nodes[root_node_index]
67
+ if hasattr(root_node, "children"):
68
  root_node.children.extend(light_node_indices)
69
  else:
70
  root_node.children = light_node_indices
 
78
  """
79
 
80
  def postprocess(self, y: str | Path | None) -> dict[str, str] | None:
81
+ """
82
+ Converts user input 3D model in any format acceptable by trimesh to glb, required by the web component
83
+ :param y: path to input 3D model
84
+ :return: file name mapped to base64 url data
85
+ """
86
  if y is not None:
87
  y = convert_formats(y, "glb")
88
  out = super().postprocess(y)
89
  return out
90
 
91
 
92
+ def breathe_new_life_into_3d_model(path_input, prompt, path_output=None):
93
  """
94
+ Paints the input 3D model using the following method:
95
  @inproceedings{wang2023breathing,
96
  title={Breathing New Life into 3D Assets with Generative Repainting},
97
  author={Wang, Tianfu and Kanakis, Menelaos and Schindler, Konrad and Van Gool, Luc and Obukhov, Anton},
 
99
  year={2023},
100
  publisher={BMVA Press}
101
  }
102
+ :param path_input: path to input 3D model
103
+ :param prompt: text description of the expected output
104
+ :param path_output: path to precomputed (cached) output if available
105
+ :return: path to the painted 3D model in glb format with lights for Gradio web component
106
  """
107
+ if path_output is not None:
108
+ path_output = path_output.name
109
+ path_output_glb_vis = path_output[:-4] + "_vis.glb"
110
+ add_lights(path_output, path_output_glb_vis)
111
+ return path_output_glb_vis
112
+
113
  path_output_dir = path_input + ".output"
114
  os.makedirs(path_output_dir, exist_ok=True)
115
 
 
138
 
139
 
140
  def run():
141
+ """
142
+ Gradio entry point
143
+ """
144
  desc = """
145
  <p align="center">
146
  <a title="Website" href="https://www.obukhov.ai/repainting_3d_assets" target="_blank" rel="noopener noreferrer" style="display: inline-block;">
 
174
  elem_classes="viewport",
175
  label="Input Model",
176
  ),
177
+ gr.Textbox(
178
+ label="Text Prompt",
179
+ ),
180
+ gr.File(visible=False, label="Cached Output"),
181
  ],
182
  outputs=[
183
  gr.Model3D(
 
190
  [
191
  os.path.join(os.path.dirname(__file__), "files/horse.ply"),
192
  "pastel superhero unicorn",
193
+ os.path.join(
194
+ os.path.dirname(__file__), "files/horse.ply.output/output.glb"
195
+ ),
196
  ],
197
  ],
198
  cache_examples=True,
requirements.txt CHANGED
@@ -1,3 +1,4 @@
1
- gradio
2
- pygltflib
3
- trimesh
 
 
1
+ gradio==3.44.4
2
+ gradio_client==0.5.1
3
+ pygltflib==1.16.0
4
+ trimesh==3.23.5