Spaces:
Paused
Paused
Commit
Β·
55083e9
1
Parent(s):
05ce3e0
fix(3d): Implement correct Hunyuan3D installation and usage
Browse files- Replaces the previous flawed dynamic loading with a robust setup process that follows the official documentation.
- After downloading the model, the code now programmatically runs pip install for the custom rasterizer and compiles the mesh painter.
- Updates the pipeline to use the correct `Hunyuan3DDiTFlowMatchingPipeline` and `Hunyuan3DPaintPipeline` classes.
- This should resolve the `FileNotFoundError` and enable the direct 3D generation model.
- models/model_3d_generator.py +85 -79
models/model_3d_generator.py
CHANGED
@@ -71,69 +71,75 @@ class Hunyuan3DGenerator:
|
|
71 |
return False
|
72 |
|
73 |
def load_model(self):
|
74 |
-
"""Load Hunyuan3D model
|
75 |
if self.model is None:
|
76 |
-
logger.info("π Starting Hunyuan3D model loading...")
|
77 |
|
78 |
try:
|
79 |
-
#
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
-
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
|
123 |
-
except (ImportError, FileNotFoundError) as e:
|
124 |
-
logger.warning(f"β οΈ Could not import Hunyuan3D modules dynamically: {e}")
|
125 |
-
logger.info("π Using simplified implementation...")
|
126 |
-
self.model = "simplified"
|
127 |
-
|
128 |
-
except Exception as e:
|
129 |
-
logger.error(f"β Failed to set up Hunyuan3D: {e}")
|
130 |
-
logger.info("π Using fallback mode...")
|
131 |
-
self.model = "fallback_mode"
|
132 |
-
|
133 |
except Exception as e:
|
134 |
-
logger.error(f"β Failed to
|
135 |
-
logger.
|
136 |
-
self.model = "
|
137 |
|
138 |
def image_to_3d(self,
|
139 |
image: Union[str, Image.Image, np.ndarray],
|
@@ -192,7 +198,7 @@ class Hunyuan3DGenerator:
|
|
192 |
return self._generate_fallback_3d(image)
|
193 |
|
194 |
def _generate_with_direct_model(self, image: Image.Image, remove_background: bool, texture_resolution: int) -> str:
|
195 |
-
"""Generate 3D model using
|
196 |
|
197 |
try:
|
198 |
# Remove background if requested
|
@@ -200,38 +206,38 @@ class Hunyuan3DGenerator:
|
|
200 |
logger.info("π Removing background...")
|
201 |
image = self._remove_background(image)
|
202 |
|
203 |
-
# Save image
|
204 |
temp_image_path = self._save_temp_image(image)
|
205 |
|
206 |
-
# Generate
|
207 |
-
logger.info("π² Generating 3D shape...")
|
208 |
-
|
209 |
-
|
|
|
|
|
210 |
guidance_scale=self.guidance_scale,
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
shape_path=shape_output,
|
220 |
image_path=temp_image_path,
|
221 |
guidance_scale=self.guidance_scale,
|
222 |
-
|
223 |
-
seed=random.randint(1, 10000),
|
224 |
-
texture_resolution=texture_resolution
|
225 |
)
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
|
|
230 |
|
231 |
return output_path
|
232 |
|
233 |
except Exception as e:
|
234 |
-
logger.error(f"β Direct model generation failed: {e}")
|
235 |
raise
|
236 |
|
237 |
def _generate_simplified_3d(self, image: Image.Image) -> str:
|
|
|
71 |
return False
|
72 |
|
73 |
def load_model(self):
|
74 |
+
"""Load Hunyuan3D model and run necessary setup"""
|
75 |
if self.model is None:
|
76 |
+
logger.info("π Starting Hunyuan3D model loading and setup...")
|
77 |
|
78 |
try:
|
79 |
+
# Download model repository if not already present
|
80 |
+
logger.info(f"π₯ Downloading Hunyuan3D repository from {self.model_id}...")
|
81 |
+
self.model_path = snapshot_download(
|
82 |
+
repo_id=self.model_id,
|
83 |
+
repo_type="space",
|
84 |
+
cache_dir="./models/hunyuan3d_cache"
|
85 |
+
)
|
86 |
+
logger.info(f"β
Model repository downloaded to: {self.model_path}")
|
87 |
+
|
88 |
+
# --- Installation and Compilation ---
|
89 |
+
logger.info("π§ Running Hunyuan3D setup scripts...")
|
90 |
+
import subprocess
|
91 |
+
import sys
|
92 |
+
import os
|
93 |
+
|
94 |
+
# 1. Install requirements from the model's specific requirements file
|
95 |
+
requirements_path = os.path.join(self.model_path, 'requirements.txt')
|
96 |
+
if os.path.exists(requirements_path):
|
97 |
+
logger.info(f"π¦ Installing requirements from {requirements_path}...")
|
98 |
+
subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', requirements_path], check=True, capture_output=True, text=True)
|
99 |
+
|
100 |
+
# 2. Install custom rasterizer
|
101 |
+
rasterizer_path = os.path.join(self.model_path, 'hy3dpaint', 'packages', 'custom_rasterizer')
|
102 |
+
if os.path.exists(rasterizer_path):
|
103 |
+
logger.info(f"π¦ Installing custom_rasterizer from {rasterizer_path}...")
|
104 |
+
subprocess.run([sys.executable, '-m', 'pip', 'install', '-e', '.'], cwd=rasterizer_path, check=True, capture_output=True, text=True)
|
105 |
+
|
106 |
+
# 3. Compile mesh painter
|
107 |
+
renderer_path = os.path.join(self.model_path, 'hy3dpaint', 'DifferentiableRenderer')
|
108 |
+
compile_script_path = os.path.join(renderer_path, 'compile_mesh_painter.sh')
|
109 |
+
if os.path.exists(compile_script_path):
|
110 |
+
logger.info(f"ποΈ Compiling mesh painter in {renderer_path}...")
|
111 |
+
subprocess.run(['bash', 'compile_mesh_painter.sh'], cwd=renderer_path, check=True, capture_output=True, text=True)
|
112 |
+
|
113 |
+
logger.info("β
Hunyuan3D setup completed successfully.")
|
114 |
|
115 |
+
# --- Pipeline Initialization ---
|
116 |
+
logger.info("βοΈ Initializing Hunyuan3D pipelines...")
|
117 |
+
|
118 |
+
# Add subdirectories to Python path
|
119 |
+
sys.path.insert(0, os.path.join(self.model_path, 'hy3dshape'))
|
120 |
+
sys.path.insert(0, os.path.join(self.model_path, 'hy3dpaint'))
|
121 |
+
|
122 |
+
# Import the correct pipelines
|
123 |
+
from hy3dshape.pipelines import Hunyuan3DDiTFlowMatchingPipeline
|
124 |
+
from textureGenPipeline import Hunyuan3DPaintPipeline, Hunyuan3DPaintConfig
|
125 |
|
126 |
+
# Instantiate pipelines
|
127 |
+
logger.info("Instantiating shape pipeline...")
|
128 |
+
self.shape_pipeline = Hunyuan3DDiTFlowMatchingPipeline.from_pretrained(
|
129 |
+
self.model_path, torch_dtype=torch.bfloat16
|
130 |
+
).to(self.device)
|
131 |
+
|
132 |
+
logger.info("Instantiating paint pipeline...")
|
133 |
+
paint_config = Hunyuan3DPaintConfig(max_num_view=8, resolution=1024, pbr_optimization=True)
|
134 |
+
self.paint_pipeline = Hunyuan3DPaintPipeline(paint_config)
|
135 |
+
|
136 |
+
self.model = "direct_model"
|
137 |
+
logger.info("β
Hunyuan3D pipelines loaded successfully.")
|
138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
except Exception as e:
|
140 |
+
logger.error(f"β Failed to set up Hunyuan3D pipeline: {e}", exc_info=True)
|
141 |
+
logger.warning("π Falling back to simplified 3D generation...")
|
142 |
+
self.model = "simplified"
|
143 |
|
144 |
def image_to_3d(self,
|
145 |
image: Union[str, Image.Image, np.ndarray],
|
|
|
198 |
return self._generate_fallback_3d(image)
|
199 |
|
200 |
def _generate_with_direct_model(self, image: Image.Image, remove_background: bool, texture_resolution: int) -> str:
|
201 |
+
"""Generate 3D model using the official Hunyuan3D pipelines"""
|
202 |
|
203 |
try:
|
204 |
# Remove background if requested
|
|
|
206 |
logger.info("π Removing background...")
|
207 |
image = self._remove_background(image)
|
208 |
|
209 |
+
# Save image to a temporary file, as pipelines expect a path
|
210 |
temp_image_path = self._save_temp_image(image)
|
211 |
|
212 |
+
# 1. Generate the untextured mesh
|
213 |
+
logger.info("π² Generating 3D shape with Hunyuan3DDiTFlowMatchingPipeline...")
|
214 |
+
# The pipeline returns a list of meshes, we take the first one
|
215 |
+
mesh_untextured_path = self.shape_pipeline(
|
216 |
+
image=temp_image_path,
|
217 |
+
num_inference_steps=self.num_inference_steps,
|
218 |
guidance_scale=self.guidance_scale,
|
219 |
+
seed=random.randint(1, 10000)
|
220 |
+
)[0]
|
221 |
+
logger.info(f"β
Untextured mesh saved to: {mesh_untextured_path}")
|
222 |
+
|
223 |
+
# 2. Generate the texture for the mesh
|
224 |
+
logger.info("π¨ Generating texture with Hunyuan3DPaintPipeline...")
|
225 |
+
mesh_textured_path = self.paint_pipeline(
|
226 |
+
mesh_path=mesh_untextured_path,
|
|
|
227 |
image_path=temp_image_path,
|
228 |
guidance_scale=self.guidance_scale,
|
229 |
+
seed=random.randint(1, 10000)
|
|
|
|
|
230 |
)
|
231 |
+
logger.info(f"β
Textured mesh saved to: {mesh_textured_path}")
|
232 |
+
|
233 |
+
# 3. Save the final output to a consistent location
|
234 |
+
output_path = self._save_output_mesh(mesh_textured_path)
|
235 |
+
logger.info(f"β
3D model generation successful. Final model at: {output_path}")
|
236 |
|
237 |
return output_path
|
238 |
|
239 |
except Exception as e:
|
240 |
+
logger.error(f"β Direct model generation failed: {e}", exc_info=True)
|
241 |
raise
|
242 |
|
243 |
def _generate_simplified_3d(self, image: Image.Image) -> str:
|