NicolasG2523 commited on
Commit
08faf90
·
verified ·
1 Parent(s): 4da0f4f

Upload gradio_app.py

Browse files
Files changed (1) hide show
  1. gradio_app.py +96 -59
gradio_app.py CHANGED
@@ -27,17 +27,13 @@ from fastapi import FastAPI
27
  from fastapi.staticfiles import StaticFiles
28
  import uuid
29
 
30
- import base64
31
- import html
32
-
33
  from hy3dgen.shapegen.utils import logger
34
 
35
  MAX_SEED = 1e7
36
 
37
- import spaces
38
-
39
  if True:
40
- import os
 
41
  import subprocess
42
  import sys
43
  import shlex
@@ -46,9 +42,6 @@ if True:
46
  print('install custom')
47
  subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
48
 
49
- @spaces.GPU
50
- def my_gpu_function():
51
- pass
52
 
53
  def get_example_img_list():
54
  print('Loading example img list ...')
@@ -118,19 +111,16 @@ def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
118
  return seed
119
 
120
 
121
- def build_model_viewer_html(save_folder, path, height=660, width=790, textured=False):
122
  # Remove first folder from path to make relative path
123
  if textured:
124
  related_path = f"./textured_mesh.glb"
125
  template_name = './assets/modelviewer-textured-template.html'
126
  output_html_path = os.path.join(save_folder, f'textured_mesh.html')
127
- mesh_file_path = os.path.join(save_folder, f'textured_mesh.glb')
128
  else:
129
  related_path = f"./white_mesh.glb"
130
  template_name = './assets/modelviewer-template.html'
131
  output_html_path = os.path.join(save_folder, f'white_mesh.html')
132
- mesh_file_path = os.path.join(save_folder, f'white_mesh.glb')
133
-
134
  offset = 50 if textured else 10
135
  with open(os.path.join(CURRENT_DIR, template_name), 'r', encoding='utf-8') as f:
136
  template_html = f.read()
@@ -138,11 +128,19 @@ def build_model_viewer_html(save_folder, path, height=660, width=790, textured=F
138
  with open(output_html_path, 'w', encoding='utf-8') as f:
139
  template_html = template_html.replace('#height#', f'{height - offset}')
140
  template_html = template_html.replace('#width#', f'{width}')
141
- #template_html = template_html.replace('#src#', f'{related_path}/')
142
- template_html = template_html.replace('#src#', f'https://nicolasg2523-picsto3d.hf.space/gradio_api/file=/tmp/{path}')
143
  f.write(template_html)
144
 
145
- return output_html_path
 
 
 
 
 
 
 
 
 
146
 
147
  @spaces.GPU(duration=40)
148
  def _gen_shape(
@@ -304,7 +302,7 @@ def generation_all(
304
 
305
  textured_mesh.metadata['extras'] = stats
306
  path_textured = export_mesh(textured_mesh, save_folder, textured=True)
307
- model_viewer_html_textured = build_model_viewer_html(save_folder, path_textured, height=HTML_HEIGHT, width=HTML_WIDTH,
308
  textured=True)
309
  if args.low_vram_mode:
310
  torch.cuda.empty_cache()
@@ -352,11 +350,7 @@ def shape_generation(
352
  mesh.metadata['extras'] = stats
353
 
354
  path = export_mesh(mesh, save_folder, textured=False)
355
- print(path)
356
- filepath=gr.File(path)
357
- print(filepath)
358
-
359
- model_viewer_html = build_model_viewer_html(save_folder, path, height=HTML_HEIGHT, width=HTML_WIDTH)
360
  if args.low_vram_mode:
361
  torch.cuda.empty_cache()
362
  return (
@@ -364,20 +358,33 @@ def shape_generation(
364
  model_viewer_html,
365
  stats,
366
  seed,
367
- gr.update(value=path)
368
  )
369
 
370
- def js_update(file):
371
- return f"<script>updateModelViewer('https://nicolasg2523-picsto3d.hf.space/gradio_api/file={file}');</script>"
372
 
373
  def build_app():
374
- title = 'Generación de modelo basado en imágenes (de 1 a 4 vistas)'
 
 
 
 
 
 
375
 
376
  title_html = f"""
377
  <div style="font-size: 2em; font-weight: bold; text-align: center; margin-bottom: 5px">
378
 
379
  {title}
380
  </div>
 
 
 
 
 
 
 
 
 
 
381
  """
382
  custom_css = """
383
  .app.svelte-wpkpf6.svelte-wpkpf6:not(.fill_width) {
@@ -399,15 +406,14 @@ def build_app():
399
  with gr.Row():
400
  with gr.Column(scale=3):
401
  with gr.Tabs(selected='tab_img_prompt') as tabs_prompt:
402
- with gr.Tab('Image Prompt', id='tab_img_prompt', visible=False) as tab_ip:
403
  image = gr.Image(label='Image', type='pil', image_mode='RGBA', height=290)
404
 
405
- with gr.Tab('Text Prompt', id='tab_txt_prompt', visible=False) as tab_tp:
406
  caption = gr.Textbox(label='Text Prompt',
407
  placeholder='HunyuanDiT will be used to generate image.',
408
  info='Example: A 3D model of a cute cat, white background')
409
-
410
- with gr.Tab('MultiView Prompt', visible=True) as tab_mv:
411
  # gr.Label('Please upload at least one front image.')
412
  with gr.Row():
413
  mv_image_front = gr.Image(label='Front', type='pil', image_mode='RGBA', height=140,
@@ -476,18 +482,56 @@ def build_app():
476
  file_export = gr.DownloadButton(label="Download", variant='primary',
477
  interactive=False, min_width=100)
478
 
479
- with gr.Column(scale=9):
480
  with gr.Tabs(selected='gen_mesh_panel') as tabs_output:
481
  with gr.Tab('Generated Mesh', id='gen_mesh_panel'):
482
  html_gen_mesh = gr.HTML(HTML_OUTPUT_PLACEHOLDER, label='Output')
483
- file_output = gr.File(label="Generated GLB")
484
- file_output.change(fn=js_update, inputs=file_output, outputs=html_gen_mesh)
485
-
486
  with gr.Tab('Exporting Mesh', id='export_mesh_panel'):
487
  html_export_mesh = gr.HTML(HTML_OUTPUT_PLACEHOLDER, label='Output')
488
  with gr.Tab('Mesh Statistic', id='stats_panel'):
489
  stats = gr.Json({}, label='Mesh Stats')
490
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
 
492
  btn.click(
493
  shape_generation,
@@ -506,7 +550,7 @@ def build_app():
506
  num_chunks,
507
  randomize_seed,
508
  ],
509
- outputs=[file_out, html_gen_mesh, stats, seed, file_output]
510
  ).then(
511
  lambda: (gr.update(visible=False, value=False), gr.update(interactive=True), gr.update(interactive=True),
512
  gr.update(interactive=False)),
@@ -577,7 +621,7 @@ def build_app():
577
  # for preview
578
  save_folder = gen_save_folder()
579
  _ = export_mesh(mesh, save_folder, textured=True)
580
- model_viewer_html = build_model_viewer_html(save_folder, path, height=HTML_HEIGHT, width=HTML_WIDTH,
581
  textured=True)
582
  else:
583
  mesh = trimesh.load(file_out)
@@ -591,7 +635,7 @@ def build_app():
591
  # for preview
592
  save_folder = gen_save_folder()
593
  _ = export_mesh(mesh, save_folder, textured=False)
594
- model_viewer_html = build_model_viewer_html(save_folder, path, height=HTML_HEIGHT, width=HTML_WIDTH,
595
  textured=False)
596
  print(f'export to {path}')
597
  return model_viewer_html, gr.update(value=path, interactive=True)
@@ -627,18 +671,12 @@ if __name__ == '__main__':
627
  parser.add_argument('--low_vram_mode', action='store_true')
628
  args = parser.parse_args()
629
 
630
- try:
631
- port = int(args.port)
632
- except ValueError:
633
- print(f"Invalid port argument detected: {args.port} — using default 7860")
634
- port = 7860
635
-
636
  args.enable_flashvdm = True
637
  SAVE_DIR = args.cache_path
638
  os.makedirs(SAVE_DIR, exist_ok=True)
639
 
640
  CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
641
- MV_MODE = True
642
  TURBO_MODE = 'turbo' in args.subfolder
643
 
644
  HTML_HEIGHT = 690 if MV_MODE else 650
@@ -646,16 +684,8 @@ if __name__ == '__main__':
646
  HTML_OUTPUT_PLACEHOLDER = f"""
647
  <div style='height: {650}px; width: 100%; border-radius: 8px; border-color: #e5e7eb; border-style: solid; border-width: 1px; display: flex; justify-content: center; align-items: center;'>
648
  <div style='text-align: center; font-size: 16px; color: #6b7280;'>
649
- <p style="color: #8d8d8d;">Bienvenido a 3DMarket</p>
650
- <script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
651
- <model-viewer id="model_viewer" src="https://modelviewer.dev/shared-assets/models/DamagedHelmet.glb" camera-controls auto-rotate style="height:500px;width:500px;"></model-viewer>
652
- <script>
653
- function updateModelViewer(srcUrl) {{
654
- const modelViewer = document.querySelector('#model_viewer');
655
- modelViewer.setAttribute('src', srcUrl + '?v=' + Date.now());
656
- console.log('updating model viewer src);
657
- }}
658
- </script>
659
  </div>
660
  </div>
661
  """
@@ -665,8 +695,6 @@ if __name__ == '__main__':
665
  border-color: #e5e7eb; order-style: solid; border-width: 1px;'>
666
  </div>
667
  """
668
-
669
- #demo = gr.Interface(fn=my_gpu_function, inputs=[], outputs="text")
670
  example_is = get_example_img_list()
671
  example_ts = get_example_txt_list()
672
  example_mvs = get_example_mv_list()
@@ -681,7 +709,12 @@ if __name__ == '__main__':
681
  texgen_worker = Hunyuan3DPaintPipeline.from_pretrained(args.texgen_model_path)
682
  if args.low_vram_mode:
683
  texgen_worker.enable_model_cpu_offload()
684
-
 
 
 
 
 
685
  HAS_TEXTUREGEN = True
686
  except Exception as e:
687
  print(e)
@@ -718,13 +751,17 @@ if __name__ == '__main__':
718
  degenerate_face_remove_worker = DegenerateFaceRemover()
719
  face_reduce_worker = FaceReducer()
720
 
 
 
 
721
  # create a static directory to store the static files
722
  static_dir = Path(SAVE_DIR).absolute()
723
  static_dir.mkdir(parents=True, exist_ok=True)
 
724
  shutil.copytree('./assets/env_maps', os.path.join(static_dir, 'env_maps'), dirs_exist_ok=True)
725
 
726
-
727
  if args.low_vram_mode:
728
  torch.cuda.empty_cache()
729
  demo = build_app()
730
- demo.launch()
 
 
27
  from fastapi.staticfiles import StaticFiles
28
  import uuid
29
 
 
 
 
30
  from hy3dgen.shapegen.utils import logger
31
 
32
  MAX_SEED = 1e7
33
 
 
 
34
  if True:
35
+ import os
36
+ import spaces
37
  import subprocess
38
  import sys
39
  import shlex
 
42
  print('install custom')
43
  subprocess.run(shlex.split("pip install custom_rasterizer-0.1-cp310-cp310-linux_x86_64.whl"), check=True)
44
 
 
 
 
45
 
46
  def get_example_img_list():
47
  print('Loading example img list ...')
 
111
  return seed
112
 
113
 
114
+ def build_model_viewer_html(save_folder, height=660, width=790, textured=False):
115
  # Remove first folder from path to make relative path
116
  if textured:
117
  related_path = f"./textured_mesh.glb"
118
  template_name = './assets/modelviewer-textured-template.html'
119
  output_html_path = os.path.join(save_folder, f'textured_mesh.html')
 
120
  else:
121
  related_path = f"./white_mesh.glb"
122
  template_name = './assets/modelviewer-template.html'
123
  output_html_path = os.path.join(save_folder, f'white_mesh.html')
 
 
124
  offset = 50 if textured else 10
125
  with open(os.path.join(CURRENT_DIR, template_name), 'r', encoding='utf-8') as f:
126
  template_html = f.read()
 
128
  with open(output_html_path, 'w', encoding='utf-8') as f:
129
  template_html = template_html.replace('#height#', f'{height - offset}')
130
  template_html = template_html.replace('#width#', f'{width}')
131
+ template_html = template_html.replace('#src#', f'{related_path}/')
 
132
  f.write(template_html)
133
 
134
+ rel_path = os.path.relpath(output_html_path, SAVE_DIR)
135
+ iframe_tag = f'<iframe src="/static/{rel_path}" height="{height}" width="100%" frameborder="0"></iframe>'
136
+ print(
137
+ f'Find html file {output_html_path}, {os.path.exists(output_html_path)}, relative HTML path is /static/{rel_path}')
138
+
139
+ return f"""
140
+ <div style='height: {height}; width: 100%;'>
141
+ {iframe_tag}
142
+ </div>
143
+ """
144
 
145
  @spaces.GPU(duration=40)
146
  def _gen_shape(
 
302
 
303
  textured_mesh.metadata['extras'] = stats
304
  path_textured = export_mesh(textured_mesh, save_folder, textured=True)
305
+ model_viewer_html_textured = build_model_viewer_html(save_folder, height=HTML_HEIGHT, width=HTML_WIDTH,
306
  textured=True)
307
  if args.low_vram_mode:
308
  torch.cuda.empty_cache()
 
350
  mesh.metadata['extras'] = stats
351
 
352
  path = export_mesh(mesh, save_folder, textured=False)
353
+ model_viewer_html = build_model_viewer_html(save_folder, height=HTML_HEIGHT, width=HTML_WIDTH)
 
 
 
 
354
  if args.low_vram_mode:
355
  torch.cuda.empty_cache()
356
  return (
 
358
  model_viewer_html,
359
  stats,
360
  seed,
 
361
  )
362
 
 
 
363
 
364
  def build_app():
365
+ title = 'Hunyuan3D-2: High Resolution Textured 3D Assets Generation'
366
+ if MV_MODE:
367
+ title = 'Hunyuan3D-2mv: Image to 3D Generation with 1-4 Views'
368
+ if 'mini' in args.subfolder:
369
+ title = 'Hunyuan3D-2mini: Strong 0.6B Image to Shape Generator'
370
+ if TURBO_MODE:
371
+ title = title.replace(':', '-Turbo: Fast ')
372
 
373
  title_html = f"""
374
  <div style="font-size: 2em; font-weight: bold; text-align: center; margin-bottom: 5px">
375
 
376
  {title}
377
  </div>
378
+ <div align="center">
379
+ Tencent Hunyuan3D Team
380
+ </div>
381
+ <div align="center">
382
+ <a href="https://github.com/tencent/Hunyuan3D-2">Github</a> &ensp;
383
+ <a href="http://3d-models.hunyuan.tencent.com">Homepage</a> &ensp;
384
+ <a href="https://3d.hunyuan.tencent.com">Hunyuan3D Studio</a> &ensp;
385
+ <a href="#">Technical Report</a> &ensp;
386
+ <a href="https://huggingface.co/Tencent/Hunyuan3D-2"> Pretrained Models</a> &ensp;
387
+ </div>
388
  """
389
  custom_css = """
390
  .app.svelte-wpkpf6.svelte-wpkpf6:not(.fill_width) {
 
406
  with gr.Row():
407
  with gr.Column(scale=3):
408
  with gr.Tabs(selected='tab_img_prompt') as tabs_prompt:
409
+ with gr.Tab('Image Prompt', id='tab_img_prompt', visible=not MV_MODE) as tab_ip:
410
  image = gr.Image(label='Image', type='pil', image_mode='RGBA', height=290)
411
 
412
+ with gr.Tab('Text Prompt', id='tab_txt_prompt', visible=HAS_T2I and not MV_MODE) as tab_tp:
413
  caption = gr.Textbox(label='Text Prompt',
414
  placeholder='HunyuanDiT will be used to generate image.',
415
  info='Example: A 3D model of a cute cat, white background')
416
+ with gr.Tab('MultiView Prompt', visible=MV_MODE) as tab_mv:
 
417
  # gr.Label('Please upload at least one front image.')
418
  with gr.Row():
419
  mv_image_front = gr.Image(label='Front', type='pil', image_mode='RGBA', height=140,
 
482
  file_export = gr.DownloadButton(label="Download", variant='primary',
483
  interactive=False, min_width=100)
484
 
485
+ with gr.Column(scale=6):
486
  with gr.Tabs(selected='gen_mesh_panel') as tabs_output:
487
  with gr.Tab('Generated Mesh', id='gen_mesh_panel'):
488
  html_gen_mesh = gr.HTML(HTML_OUTPUT_PLACEHOLDER, label='Output')
 
 
 
489
  with gr.Tab('Exporting Mesh', id='export_mesh_panel'):
490
  html_export_mesh = gr.HTML(HTML_OUTPUT_PLACEHOLDER, label='Output')
491
  with gr.Tab('Mesh Statistic', id='stats_panel'):
492
  stats = gr.Json({}, label='Mesh Stats')
493
 
494
+ with gr.Column(scale=3 if MV_MODE else 2):
495
+ with gr.Tabs(selected='tab_img_gallery') as gallery:
496
+ with gr.Tab('Image to 3D Gallery', id='tab_img_gallery', visible=not MV_MODE) as tab_gi:
497
+ with gr.Row():
498
+ gr.Examples(examples=example_is, inputs=[image],
499
+ label=None, examples_per_page=18)
500
+
501
+ with gr.Tab('Text to 3D Gallery', id='tab_txt_gallery', visible=HAS_T2I and not MV_MODE) as tab_gt:
502
+ with gr.Row():
503
+ gr.Examples(examples=example_ts, inputs=[caption],
504
+ label=None, examples_per_page=18)
505
+ with gr.Tab('MultiView to 3D Gallery', id='tab_mv_gallery', visible=MV_MODE) as tab_mv:
506
+ with gr.Row():
507
+ gr.Examples(examples=example_mvs,
508
+ inputs=[mv_image_front, mv_image_back, mv_image_left, mv_image_right],
509
+ label=None, examples_per_page=6)
510
+
511
+ gr.HTML(f"""
512
+ <div align="center">
513
+ Activated Model - Shape Generation ({args.model_path}/{args.subfolder}) ; Texture Generation ({'Hunyuan3D-2' if HAS_TEXTUREGEN else 'Unavailable'})
514
+ </div>
515
+ """)
516
+ if not HAS_TEXTUREGEN:
517
+ gr.HTML("""
518
+ <div style="margin-top: 5px;" align="center">
519
+ <b>Warning: </b>
520
+ Texture synthesis is disable due to missing requirements,
521
+ please install requirements following <a href="https://github.com/Tencent/Hunyuan3D-2?tab=readme-ov-file#install-requirements">README.md</a>to activate it.
522
+ </div>
523
+ """)
524
+ if not args.enable_t23d:
525
+ gr.HTML("""
526
+ <div style="margin-top: 5px;" align="center">
527
+ <b>Warning: </b>
528
+ Text to 3D is disable. To activate it, please run `python gradio_app.py --enable_t23d`.
529
+ </div>
530
+ """)
531
+
532
+ tab_ip.select(fn=lambda: gr.update(selected='tab_img_gallery'), outputs=gallery)
533
+ if HAS_T2I:
534
+ tab_tp.select(fn=lambda: gr.update(selected='tab_txt_gallery'), outputs=gallery)
535
 
536
  btn.click(
537
  shape_generation,
 
550
  num_chunks,
551
  randomize_seed,
552
  ],
553
+ outputs=[file_out, html_gen_mesh, stats, seed]
554
  ).then(
555
  lambda: (gr.update(visible=False, value=False), gr.update(interactive=True), gr.update(interactive=True),
556
  gr.update(interactive=False)),
 
621
  # for preview
622
  save_folder = gen_save_folder()
623
  _ = export_mesh(mesh, save_folder, textured=True)
624
+ model_viewer_html = build_model_viewer_html(save_folder, height=HTML_HEIGHT, width=HTML_WIDTH,
625
  textured=True)
626
  else:
627
  mesh = trimesh.load(file_out)
 
635
  # for preview
636
  save_folder = gen_save_folder()
637
  _ = export_mesh(mesh, save_folder, textured=False)
638
+ model_viewer_html = build_model_viewer_html(save_folder, height=HTML_HEIGHT, width=HTML_WIDTH,
639
  textured=False)
640
  print(f'export to {path}')
641
  return model_viewer_html, gr.update(value=path, interactive=True)
 
671
  parser.add_argument('--low_vram_mode', action='store_true')
672
  args = parser.parse_args()
673
 
 
 
 
 
 
 
674
  args.enable_flashvdm = True
675
  SAVE_DIR = args.cache_path
676
  os.makedirs(SAVE_DIR, exist_ok=True)
677
 
678
  CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
679
+ MV_MODE = 'mv' in args.model_path
680
  TURBO_MODE = 'turbo' in args.subfolder
681
 
682
  HTML_HEIGHT = 690 if MV_MODE else 650
 
684
  HTML_OUTPUT_PLACEHOLDER = f"""
685
  <div style='height: {650}px; width: 100%; border-radius: 8px; border-color: #e5e7eb; border-style: solid; border-width: 1px; display: flex; justify-content: center; align-items: center;'>
686
  <div style='text-align: center; font-size: 16px; color: #6b7280;'>
687
+ <p style="color: #8d8d8d;">Welcome to Hunyuan3D!</p>
688
+ <p style="color: #8d8d8d;">No mesh here.</p>
 
 
 
 
 
 
 
 
689
  </div>
690
  </div>
691
  """
 
695
  border-color: #e5e7eb; order-style: solid; border-width: 1px;'>
696
  </div>
697
  """
 
 
698
  example_is = get_example_img_list()
699
  example_ts = get_example_txt_list()
700
  example_mvs = get_example_mv_list()
 
709
  texgen_worker = Hunyuan3DPaintPipeline.from_pretrained(args.texgen_model_path)
710
  if args.low_vram_mode:
711
  texgen_worker.enable_model_cpu_offload()
712
+ # Not help much, ignore for now.
713
+ # if args.compile:
714
+ # texgen_worker.models['delight_model'].pipeline.unet.compile()
715
+ # texgen_worker.models['delight_model'].pipeline.vae.compile()
716
+ # texgen_worker.models['multiview_model'].pipeline.unet.compile()
717
+ # texgen_worker.models['multiview_model'].pipeline.vae.compile()
718
  HAS_TEXTUREGEN = True
719
  except Exception as e:
720
  print(e)
 
751
  degenerate_face_remove_worker = DegenerateFaceRemover()
752
  face_reduce_worker = FaceReducer()
753
 
754
+ # https://discuss.huggingface.co/t/how-to-serve-an-html-file/33921/2
755
+ # create a FastAPI app
756
+ app = FastAPI()
757
  # create a static directory to store the static files
758
  static_dir = Path(SAVE_DIR).absolute()
759
  static_dir.mkdir(parents=True, exist_ok=True)
760
+ app.mount("/static", StaticFiles(directory=static_dir, html=True), name="static")
761
  shutil.copytree('./assets/env_maps', os.path.join(static_dir, 'env_maps'), dirs_exist_ok=True)
762
 
 
763
  if args.low_vram_mode:
764
  torch.cuda.empty_cache()
765
  demo = build_app()
766
+ app = gr.mount_gradio_app(app, demo, path="/")
767
+ uvicorn.run(app, host=args.host, port=args.port)