Surn commited on
Commit
857fd4e
·
1 Parent(s): 65fc25d

Ensure diff_gaussian_rasterization is installed at runtime

Browse files

Added ensure_diff_gaussian utility to check/install diff_gaussian_rasterization from GitHub if missing. Updated app.py to call this before loading Trellis pipeline. requirements.txt now explicitly includes the package.

README.md CHANGED
@@ -4,7 +4,7 @@ emoji: 🐝
4
  colorFrom: yellow
5
  colorTo: purple
6
  sdk: gradio
7
- python_version: 3.10.13
8
  sdk_version: 6.10.0
9
  app_file: app.py
10
  pinned: true
 
4
  colorFrom: yellow
5
  colorTo: purple
6
  sdk: gradio
7
+ python_version: 3.12.12
8
  sdk_version: 6.10.0
9
  app_file: app.py
10
  pinned: true
app.py CHANGED
@@ -23,7 +23,7 @@ import accelerate
23
  from transformers import AutoTokenizer, DPTImageProcessor, DPTForDepthEstimation
24
  from trellis.pipelines import TrellisImageTo3DPipeline
25
  from trellis.representations import Gaussian, MeshExtractResult
26
- from trellis.utils import render_utils, postprocessing_utils
27
  from pathlib import Path
28
  import utils.hex_hura as hex_hura
29
  from utils.storage import (
@@ -1000,30 +1000,71 @@ def generate_3d_asset_part1(depth_image_source, randomize_seed, seed, input_imag
1000
 
1001
  return depth_img, image_path, output_name, final_seed
1002
 
1003
- @spaces.GPU(duration=150,progress=gr.Progress(track_tqdm=True))
1004
- def generate_3d_asset_part2(depth_img, image_path, output_name, seed, steps, model_resolution, video_resolution, depth_alpha: int, multi_mode: str, req: gr.Request, progress=gr.Progress(track_tqdm=True)):
 
 
 
 
 
 
 
 
 
 
 
 
 
1005
  # Open image using standardized defaults
1006
  image_raw = Image.open(image_path).convert("RGB")
1007
  resized_image = resize_image_with_aspect_ratio(image_raw, model_resolution, model_resolution)
1008
  depth_img = Image.open(depth_img).convert("RGBA")
1009
  resized_depth_image = resize_image_with_aspect_ratio(depth_img, model_resolution, model_resolution)
1010
  resized_depth_image_enhanced = apply_lut(resized_depth_image, "./LUT/Contrast.cube", 100)
1011
- combined_depth_img = combine_depth_map_with_Image(resized_image, resized_depth_image_enhanced, model_resolution, model_resolution, depth_alpha)
 
 
 
 
 
 
1012
 
1013
- use_multi = (multi_mode != "singleimage")
 
1014
  if use_multi:
1015
  images = [resized_image, combined_depth_img, resized_depth_image]
1016
- processed_images = [TRELLIS_PIPELINE.preprocess_image(image, max_resolution=model_resolution, remove_bg=False) for image in images]
 
 
 
1017
 
1018
  if TRELLIS_PIPELINE is None:
1019
- gr.Warning(f"Trellis Pipeline is not initialized: {TRELLIS_PIPELINE.device()}")
1020
- return [None, None, depth_img]
1021
- else:
1022
  # Preprocess and run the Trellis pipeline with fixed sampler settings
 
 
 
 
 
 
 
1023
  try:
1024
- TRELLIS_PIPELINE.cuda()
 
 
 
 
 
 
 
 
 
 
1025
  if not use_multi:
1026
- processed_image = TRELLIS_PIPELINE.preprocess_image(combined_depth_img, max_resolution=model_resolution, remove_bg = False)
 
 
 
 
1027
  outputs = TRELLIS_PIPELINE.run(
1028
  processed_image,
1029
  seed=seed,
@@ -1057,39 +1098,40 @@ def generate_3d_asset_part2(depth_img, image_path, output_name, seed, steps, mod
1057
 
1058
  # Validate the mesh
1059
  mesh = outputs['mesh'][0]
1060
- meshisdict = isinstance(mesh, dict)
1061
- if meshisdict:
1062
- vertices = mesh['vertices']
1063
- faces = mesh['faces']
1064
- else:
1065
- vertices = mesh.vertices
1066
- faces = mesh.faces
1067
 
1068
- print(f"Mesh vertices: {vertices.shape}, faces: {faces.shape}")
1069
- if faces.max() >= vertices.shape[0]:
1070
- raise ValueError(f"Invalid mesh: face index {faces.max()} exceeds vertex count {vertices.shape[0]}")
1071
- except Exception as e:
1072
- gr.Warning(f"Error generating 3D asset: {e}")
1073
- print(f"Error generating 3D asset: {e}")
1074
- torch.cuda.empty_cache()
1075
- torch.cuda.ipc_collect()
1076
- return None,None, depth_img
1077
 
1078
  # Ensure data is on GPU and has correct type
1079
- if not vertices.is_cuda or not faces.is_cuda:
1080
- raise ValueError("Mesh data must be on GPU")
1081
- if vertices.dtype != torch.float32 or faces.dtype != torch.int32:
1082
- if meshisdict:
1083
- mesh['faces'] = faces.to(torch.int32)
1084
- mesh['vertices'] = vertices.to(torch.float32)
1085
- else:
1086
- mesh.faces = faces.to(torch.int32)
1087
- mesh.vertices = vertices.to(torch.float32)
1088
 
1089
- user_dir = os.path.join(constants.TMPDIR, str(req.session_hash))
1090
- os.makedirs(user_dir, exist_ok=True)
1091
 
1092
- video = render_utils.render_video(outputs['gaussian'][0], resolution=video_resolution, num_frames=64, r=1, fov=45)['color']
 
1093
  try:
1094
  #video_rf = render_utils.render_video(outputs['radiance_field'][0], resolution=video_resolution, num_frames=64, r=1, fov=45)['color']
1095
  video_geo = render_utils.render_video(outputs['mesh'][0], resolution=video_resolution, num_frames=64, r=1, fov=45)['normal']
@@ -1097,21 +1139,19 @@ def generate_3d_asset_part2(depth_img, image_path, output_name, seed, steps, mod
1097
  except Exception as e:
1098
  gr.Info(f"Error rendering video: {e}")
1099
  print(f"Error rendering video: {e}")
1100
- video_path = os.path.join(user_dir, f'{output_name}.mp4')
1101
- imageio.mimsave(video_path, video, fps=8)
1102
 
1103
  #snapshot_results = render_utils.render_snapshot_depth(outputs['radiance_field'][0], resolution=1280, r=1, fov=80)
1104
  #depth_snapshot = Image.fromarray(snapshot_results['normal'][0]).convert("L")
1105
- depth_snapshot = depth_img
1106
-
1107
- state = pack_state(outputs['gaussian'][0], outputs['mesh'][0], output_name)
1108
 
1109
  if torch.cuda.is_available():
1110
  torch.cuda.empty_cache()
1111
  torch.cuda.ipc_collect()
1112
  return [state, video_path, depth_snapshot]
1113
 
1114
-
1115
  @spaces.GPU(duration=90,progress=gr.Progress(track_tqdm=True))
1116
  def extract_glb(
1117
  state: dict,
@@ -1130,6 +1170,7 @@ def extract_glb(
1130
  Returns:
1131
  str: The path to the extracted GLB file.
1132
  """
 
1133
  user_dir = os.path.join(constants.TMPDIR, str(req.session_hash))
1134
  gs, mesh, name = unpack_state(state)
1135
  glb = postprocessing_utils.to_glb(gs, mesh, simplify=mesh_simplify, texture_size=texture_size, verbose=False)
 
23
  from transformers import AutoTokenizer, DPTImageProcessor, DPTForDepthEstimation
24
  from trellis.pipelines import TrellisImageTo3DPipeline
25
  from trellis.representations import Gaussian, MeshExtractResult
26
+ #from trellis.utils import render_utils, postprocessing_utils
27
  from pathlib import Path
28
  import utils.hex_hura as hex_hura
29
  from utils.storage import (
 
1000
 
1001
  return depth_img, image_path, output_name, final_seed
1002
 
1003
+ @spaces.GPU(duration=150, progress=gr.Progress(track_tqdm=True))
1004
+ def generate_3d_asset_part2(
1005
+ depth_img,
1006
+ image_path,
1007
+ output_name,
1008
+ seed,
1009
+ steps,
1010
+ model_resolution,
1011
+ video_resolution,
1012
+ depth_alpha: int,
1013
+ multi_mode: str,
1014
+ req: gr.Request,
1015
+ progress=gr.Progress(track_tqdm=True),
1016
+ ):
1017
+ from trellis.utils import render_utils
1018
  # Open image using standardized defaults
1019
  image_raw = Image.open(image_path).convert("RGB")
1020
  resized_image = resize_image_with_aspect_ratio(image_raw, model_resolution, model_resolution)
1021
  depth_img = Image.open(depth_img).convert("RGBA")
1022
  resized_depth_image = resize_image_with_aspect_ratio(depth_img, model_resolution, model_resolution)
1023
  resized_depth_image_enhanced = apply_lut(resized_depth_image, "./LUT/Contrast.cube", 100)
1024
+ combined_depth_img = combine_depth_map_with_Image(
1025
+ resized_image,
1026
+ resized_depth_image_enhanced,
1027
+ model_resolution,
1028
+ model_resolution,
1029
+ depth_alpha,
1030
+ )
1031
 
1032
+ use_multi = multi_mode != "singleimage"
1033
+ processed_images = None
1034
  if use_multi:
1035
  images = [resized_image, combined_depth_img, resized_depth_image]
1036
+ processed_images = [
1037
+ TRELLIS_PIPELINE.preprocess_image(image, max_resolution=model_resolution, remove_bg=False)
1038
+ for image in images
1039
+ ]
1040
 
1041
  if TRELLIS_PIPELINE is None:
 
 
 
1042
  # Preprocess and run the Trellis pipeline with fixed sampler settings
1043
+ gr.Warning("Trellis Pipeline is not initialized")
1044
+ return [None, None, depth_img]
1045
+
1046
+ try:
1047
+ # Prefer backends that work on Blackwell sm_120
1048
+ os.environ["ATTN_BACKEND"] = "sdpa"
1049
+ os.environ["XFORMERS_DISABLED"] = "1"
1050
  try:
1051
+ from trellis.modules.attention import config as attn_config
1052
+ if hasattr(attn_config, "set_backend"):
1053
+ attn_config.set_backend("sdpa")
1054
+ except Exception:
1055
+ pass
1056
+
1057
+ TRELLIS_PIPELINE.cuda()
1058
+
1059
+ # no_grad (not inference_mode): diff_gaussian_rasterization uses autograd.Function
1060
+ with torch.no_grad():
1061
+ #with torch.autocast("cuda", dtype=torch.bfloat16):
1062
  if not use_multi:
1063
+ processed_image = TRELLIS_PIPELINE.preprocess_image(
1064
+ combined_depth_img,
1065
+ max_resolution=model_resolution,
1066
+ remove_bg=False,
1067
+ )
1068
  outputs = TRELLIS_PIPELINE.run(
1069
  processed_image,
1070
  seed=seed,
 
1098
 
1099
  # Validate the mesh
1100
  mesh = outputs['mesh'][0]
1101
+ meshisdict = isinstance(mesh, dict)
1102
+ if meshisdict:
1103
+ vertices = mesh["vertices"]
1104
+ faces = mesh["faces"]
1105
+ else:
1106
+ vertices = mesh.vertices
1107
+ faces = mesh.faces
1108
 
1109
+ print(f"Mesh vertices: {vertices.shape}, faces: {faces.shape}")
1110
+ if faces.max() >= vertices.shape[0]:
1111
+ raise ValueError(f"Invalid mesh: face index {faces.max()} exceeds vertex count {vertices.shape[0]}")
1112
+ except Exception as e:
1113
+ gr.Warning(f"Error generating 3D asset: {e}")
1114
+ print(f"Error generating 3D asset: {e}")
1115
+ torch.cuda.empty_cache()
1116
+ torch.cuda.ipc_collect()
1117
+ return None, None, depth_img
1118
 
1119
  # Ensure data is on GPU and has correct type
1120
+ if not vertices.is_cuda or not faces.is_cuda:
1121
+ raise ValueError("Mesh data must be on GPU")
1122
+ if vertices.dtype != torch.float32 or faces.dtype != torch.int32:
1123
+ if meshisdict:
1124
+ mesh["faces"] = faces.to(torch.int32)
1125
+ mesh["vertices"] = vertices.to(torch.float32)
1126
+ else:
1127
+ mesh.faces = faces.to(torch.int32)
1128
+ mesh.vertices = vertices.to(torch.float32)
1129
 
1130
+ user_dir = os.path.join(constants.TMPDIR, str(req.session_hash))
1131
+ os.makedirs(user_dir, exist_ok=True)
1132
 
1133
+ with torch.no_grad():
1134
+ video = render_utils.render_video(outputs["gaussian"][0], resolution=video_resolution, num_frames=64, r=1, fov=45)["color"]
1135
  try:
1136
  #video_rf = render_utils.render_video(outputs['radiance_field'][0], resolution=video_resolution, num_frames=64, r=1, fov=45)['color']
1137
  video_geo = render_utils.render_video(outputs['mesh'][0], resolution=video_resolution, num_frames=64, r=1, fov=45)['normal']
 
1139
  except Exception as e:
1140
  gr.Info(f"Error rendering video: {e}")
1141
  print(f"Error rendering video: {e}")
1142
+ video_path = os.path.join(user_dir, f"{output_name}.mp4")
1143
+ imageio.mimsave(video_path, video, fps=8)
1144
 
1145
  #snapshot_results = render_utils.render_snapshot_depth(outputs['radiance_field'][0], resolution=1280, r=1, fov=80)
1146
  #depth_snapshot = Image.fromarray(snapshot_results['normal'][0]).convert("L")
1147
+ depth_snapshot = depth_img
1148
+ state = pack_state(outputs["gaussian"][0], outputs["mesh"][0], output_name)
 
1149
 
1150
  if torch.cuda.is_available():
1151
  torch.cuda.empty_cache()
1152
  torch.cuda.ipc_collect()
1153
  return [state, video_path, depth_snapshot]
1154
 
 
1155
  @spaces.GPU(duration=90,progress=gr.Progress(track_tqdm=True))
1156
  def extract_glb(
1157
  state: dict,
 
1170
  Returns:
1171
  str: The path to the extracted GLB file.
1172
  """
1173
+ from trellis.utils import postprocessing_utils
1174
  user_dir = os.path.join(constants.TMPDIR, str(req.session_hash))
1175
  gs, mesh, name = unpack_state(state)
1176
  glb = postprocessing_utils.to_glb(gs, mesh, simplify=mesh_simplify, texture_size=texture_size, verbose=False)
pre-requirements.txt CHANGED
@@ -3,5 +3,5 @@ pip>=26.2
3
  # Official PyTorch + torchvision for CUDA 12.8 (sm_120 / ZeroGPU)
4
  --extra-index-url https://download.pytorch.org/whl/cu128
5
 
6
- torch==2.9.1
7
- torchvision==0.24.1
 
3
  # Official PyTorch + torchvision for CUDA 12.8 (sm_120 / ZeroGPU)
4
  --extra-index-url https://download.pytorch.org/whl/cu128
5
 
6
+ torch==2.10.0
7
+ torchvision==0.25.0
requirements.txt CHANGED
@@ -14,7 +14,7 @@ sentencepiece
14
  --extra-index-url https://download.pytorch.org/whl/cu128
15
 
16
  # xformers for torch 2.10+ (official cu128 wheels)
17
- xformers>=0.0.34
18
 
19
  # Other dependencies
20
  Haishoku
@@ -61,7 +61,12 @@ pyvista==0.44.2
61
  pymeshfix==0.17.0
62
  igraph==0.11.8
63
  git+https://github.com/EasternJournalist/utils3d.git@3913c65d81e05e47b9f367250cf8c0f7462a0900
 
 
 
 
64
  spconv-cu128==2.4.1
 
65
  #gradio_litmodel3d==0.0.1
66
  #linux only
67
  #https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.3/flash_attn-2.7.3+cu12torch2.6cxx11abiFALSE-cp310-cp310-linux_x86_64.whl
@@ -70,19 +75,34 @@ spconv-cu128==2.4.1
70
 
71
  # -------------------------------------------------
72
  # CUDA extensions for torch 2.9.1 + cu128
 
73
  # -------------------------------------------------
74
- flash-attn @ https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.9-cp310-cp310-linux_x86_64.whl
 
 
 
 
 
 
75
 
76
  ninja
77
 
78
- # nvdiffrast – ready wheel for 2.9.1 + cu128 (Python 3.10 Linux)
79
- --find-links https://pozzettiandrea.github.io/nvdiffrast-full-wheels/cu128-torch291/
80
- nvdiffrast
 
81
 
82
  # diff_gaussian_rasterization – no reliable 2.9.1 wheel, build from source
83
- git+https://github.com/graphdeco-inria/diff-gaussian-rasterization.git
 
 
84
 
85
  #Windows only
86
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/flash_attn-2.7.4.post1-cp312-cp312-win_amd64.whl?download=true
87
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/diff_gaussian_rasterization-0.0.0-cp312-cp312-win_amd64.whl?download=true
88
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/nvdiffrast-0.3.3-py3-none-any.whl
 
 
 
 
 
 
14
  --extra-index-url https://download.pytorch.org/whl/cu128
15
 
16
  # xformers for torch 2.10+ (official cu128 wheels)
17
+ xformers>=0.0.33.post2
18
 
19
  # Other dependencies
20
  Haishoku
 
61
  pymeshfix==0.17.0
62
  igraph==0.11.8
63
  git+https://github.com/EasternJournalist/utils3d.git@3913c65d81e05e47b9f367250cf8c0f7462a0900
64
+
65
+ # spconv + cumm (Python 3.12 has cu128 wheels)
66
+ --extra-index-url https://ratharog.github.io/cumm-spconv/
67
+ cumm-cu128==0.9.1
68
  spconv-cu128==2.4.1
69
+
70
  #gradio_litmodel3d==0.0.1
71
  #linux only
72
  #https://github.com/Dao-AILab/flash-attention/releases/download/v2.7.3/flash_attn-2.7.3+cu12torch2.6cxx11abiFALSE-cp310-cp310-linux_x86_64.whl
 
75
 
76
  # -------------------------------------------------
77
  # CUDA extensions for torch 2.9.1 + cu128
78
+ # CUDA extensions for torch 2.10.0 + cu128 + Python 3.12
79
  # -------------------------------------------------
80
+ # flash-attn @ https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.9-cp310-cp310-linux_x86_64.whl
81
+ # flash-attn @ https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.9-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
82
+ # flash-attn @ https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.9-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
83
+ # https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.9-cp312-cp312-linux_x86_64.whl
84
+ # https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.10-cp312-cp312-linux_x86_64.whl
85
+ https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3+cu128torch2.10-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
86
+ # temporarily disabled – ABI mismatch with current torch 2.9.1+cu128
87
 
88
  ninja
89
 
90
+ # nvdiffrast – direct wheel (GitHub Pages index is down)
91
+ #https://github.com/PozzettiAndrea/cuda-wheels/releases/download/nvdiffrast-latest/nvdiffrast-0.4.0%2Bcu128torch2.9-cp310-cp310-manylinux_2_34_x86_64.manylinux_2_35_x86_64.whl
92
+ #https://github.com/PozzettiAndrea/cuda-wheels/releases/download/nvdiffrast-latest/nvdiffrast-0.4.0%2Bcu128torch2.9-cp312-cp312-manylinux_2_34_x86_64.manylinux_2_35_x86_64.whl
93
+ https://github.com/PozzettiAndrea/cuda-wheels/releases/download/nvdiffrast-latest/nvdiffrast-0.4.0%2Bcu128torch2.10-cp312-cp312-manylinux_2_34_x86_64.manylinux_2_35_x86_64.whl
94
 
95
  # diff_gaussian_rasterization – no reliable 2.9.1 wheel, build from source
96
+ #git+https://github.com/graphdeco-inria/diff-gaussian-rasterization.git ###############run inside app.py to build from source in misc.py ##############
97
+ https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/diff_gaussian_rasterization-0.0.0-cp312-cp312-linux_x86_64.whl?download=true
98
+ # (do not put the git URL here if you use ensure_diff_gaussian())
99
 
100
  #Windows only
101
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/flash_attn-2.7.4.post1-cp312-cp312-win_amd64.whl?download=true
102
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/diff_gaussian_rasterization-0.0.0-cp312-cp312-win_amd64.whl?download=true
103
  #https://huggingface.co/spaces/Surn/HexaGrid/resolve/main/wheels/nvdiffrast-0.3.3-py3-none-any.whl
104
+
105
+ #https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.28/flash_attn-2.8.3+cu128torch2.9-cp312-cp312-win_amd64.whl
106
+ #https://github.com/PozzettiAndrea/cuda-wheels/releases/download/nvdiffrast-latest/nvdiffrast-0.4.0%2Bcu128torch2.9-cp312-cp312-win_amd64.whl
107
+
108
+ #https://github.com/PozzettiAndrea/cuda-wheels/releases/download/nvdiffrast-latest/nvdiffrast-0.4.0%2Bcu128torch2.10-cp312-cp312-win_amd64.whl
utils/misc.py CHANGED
@@ -4,6 +4,7 @@ import sys
4
  import logging
5
  import os
6
  import subprocess
 
7
  from utils.constants import TMPDIR, MAX_SEED
8
  import numpy as np
9
 
@@ -206,4 +207,4 @@ def number_to_letter(n: int, upper_case: bool = True):
206
  while abs(n) > 0:
207
  n, remainder = divmod(abs(n) - 1, 26)
208
  result = chr(a_char + remainder) + result
209
- return result
 
4
  import logging
5
  import os
6
  import subprocess
7
+ import sys
8
  from utils.constants import TMPDIR, MAX_SEED
9
  import numpy as np
10
 
 
207
  while abs(n) > 0:
208
  n, remainder = divmod(abs(n) - 1, 26)
209
  result = chr(a_char + remainder) + result
210
+ return result
wheels/diff_gaussian_rasterization-0.0.0-cp312-cp312-linux_x86_64.whl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7acb876ee0e0150cec13f3f9388d76410da40a2719a69c3c574343ef4ebba8d5
3
+ size 4159087