1111import hashlib
1212import platform
1313import re
14+ from packaging import version
1415from PIL import Image
1516import numpy as np
1617
@@ -20,7 +21,13 @@ class Hunyuan3DImageTo3D:
2021 def INPUT_TYPES (s ):
2122 models = [
2223 'tencent/Hunyuan3D-2/hunyuan3d-dit-v2-0' ,
23- 'tencent/Hunyuan3D-2/hunyuan3d-dit-v2-0-fast[variant=fp16]' ,
24+ 'tencent/Hunyuan3D-2/hunyuan3d-dit-v2-0-fast' ,
25+ 'tencent/Hunyuan3D-2mini/hunyuan3d-dit-v2-mini' ,
26+ 'tencent/Hunyuan3D-2mini/hunyuan3d-dit-v2-mini-fast' ,
27+ 'tencent/Hunyuan3D-2mini/hunyuan3d-dit-v2-mini-turbo' ,
28+ 'tencent/Hunyuan3D-2mv/hunyuan3d-dit-v2-mv-fast' ,
29+ 'tencent/Hunyuan3D-2mv/hunyuan3d-dit-v2-mv-turbo' ,
30+ 'tencent/Hunyuan3D-2mv/hunyuan3d-dit-v2-mv' ,
2431 ]
2532 return {
2633 "required" : {
@@ -39,6 +46,12 @@ def INPUT_TYPES(s):
3946 "model" : (models , {
4047 "tooltip" : "huggingface id of model(author/name/subfolder)" , # noqa: E501
4148 }),
49+ "back_image" : ("IMAGE" ,),
50+ "back_mask" : ("MASK" ,),
51+ "left_image" : ("IMAGE" ,),
52+ "left_mask" : ("MASK" ,),
53+ "right_image" : ("IMAGE" ,),
54+ "right_mask" : ("MASK" ,),
4255 }
4356 }
4457 RETURN_TYPES = ("STRING" ,)
@@ -63,6 +76,45 @@ def popen_print_output(args, cwd=None, shell=False):
6376 "\n "
6477 )
6578
79+ @staticmethod
80+ def install_custom_rasterizer (this_path ):
81+ print ("Installing custom_rasterizer" )
82+ Hunyuan3DImageTo3D .popen_print_output (
83+ [sys .executable , 'setup.py' , 'install' ],
84+ os .path .join (
85+ this_path ,
86+ 'Hunyuan3D-2/hy3dgen/texgen/custom_rasterizer'
87+ )
88+ )
89+
90+ @staticmethod
91+ def install_hy3dgen (this_path ):
92+ print ("Installing hy3dgen" )
93+ Hunyuan3DImageTo3D .popen_print_output (
94+ [sys .executable , 'setup.py' , 'install' ],
95+ os .path .join (this_path , 'Hunyuan3D-2' )
96+ )
97+
98+ @staticmethod
99+ def install_mesh_processor (this_path ):
100+ renderer_dir = os .path .join (
101+ this_path ,
102+ 'Hunyuan3D-2/hy3dgen/texgen/differentiable_renderer'
103+ )
104+ if platform .system () == 'Windows' :
105+ if importlib .util .find_spec ('mesh_processor' ) is None :
106+ print ("Installing mesh_processor" )
107+ Hunyuan3DImageTo3D .popen_print_output (
108+ [sys .executable , 'setup.py' , 'install' ],
109+ renderer_dir
110+ )
111+ else : # Linux
112+ if len (glob .glob (f'{ renderer_dir } /mesh_processor*.so' )) == 0 :
113+ Hunyuan3DImageTo3D .popen_print_output (
114+ ['/bin/bash' , 'compile_mesh_painter.sh' ],
115+ renderer_dir
116+ )
117+
66118 @staticmethod
67119 def install_check ():
68120 this_path = os .path .dirname (os .path .realpath (__file__ ))
@@ -84,40 +136,20 @@ def install_check():
84136# shell=True,
85137# )
86138
139+ cr_version = version .parse ("0.1" )
87140 if importlib .util .find_spec ('custom_rasterizer' ) is None :
88- print ("Installing custom_rasterizer" )
89- Hunyuan3DImageTo3D .popen_print_output (
90- [sys .executable , 'setup.py' , 'install' ],
91- os .path .join (
92- this_path ,
93- 'Hunyuan3D-2/hy3dgen/texgen/custom_rasterizer'
94- )
95- )
141+ Hunyuan3DImageTo3D .install_custom_rasterizer (this_path )
142+ elif cr_version > version .parse (importlib .metadata .version ('custom_rasterizer' )): # noqa: E501
143+ Hunyuan3DImageTo3D .install_custom_rasterizer (this_path )
96144
145+ hy3dgen_version = version .parse ("2.0.2" )
97146 if importlib .util .find_spec ('hy3dgen' ) is None :
98- print ("Installing hy3dgen" )
99- Hunyuan3DImageTo3D .popen_print_output (
100- [sys .executable , 'setup.py' , 'install' ],
101- os .path .join (this_path , 'Hunyuan3D-2' )
102- )
147+ Hunyuan3DImageTo3D .install_hy3dgen (this_path )
148+ elif hy3dgen_version > version .parse (importlib .metadata .version ('hy3dgen' )): # noqa: E501
149+ Hunyuan3DImageTo3D .install_hy3dgen (this_path )
103150
104- renderer_dir = os .path .join (
105- this_path ,
106- 'Hunyuan3D-2/hy3dgen/texgen/differentiable_renderer'
107- )
108- if platform .system () == 'Windows' :
109- if importlib .util .find_spec ('mesh_processor' ) is None :
110- print ("Installing mesh_processor" )
111- Hunyuan3DImageTo3D .popen_print_output (
112- [sys .executable , 'setup.py' , 'install' ],
113- renderer_dir
114- )
115- else : # Linux
116- if len (glob .glob (f'{ renderer_dir } /mesh_processor*.so' )) == 0 :
117- Hunyuan3DImageTo3D .popen_print_output (
118- ['/bin/bash' , 'compile_mesh_painter.sh' ],
119- renderer_dir
120- )
151+ if importlib .util .find_spec ('mesh_processor' ) is None :
152+ Hunyuan3DImageTo3D .install_differentiable_renderer (this_path )
121153
122154 @staticmethod
123155 def get_spare_filename (filename_format ):
@@ -127,6 +159,19 @@ def get_spare_filename(filename_format):
127159 return filename
128160 return None
129161
162+ def comfy_img_to_file (self , image , mask , input_image_file ):
163+ i = 255. * image .cpu ().numpy ()
164+ img = Image .fromarray (np .clip (i , 0 , 255 ).astype (np .uint8 ))
165+ if mask is not None :
166+ m = 255. * mask .cpu ().numpy ()
167+ mask_pil = Image .fromarray (m )
168+ mask_pil = np .clip (mask_pil , 0 , 255 ).astype (np .uint8 )
169+ mask_pil = 255 - mask_pil
170+ # If it crashes here,
171+ # check that you have transparency in the image
172+ img .putalpha (Image .fromarray (mask_pil , mode = 'L' ))
173+ img .save (input_image_file )
174+
130175 def process (
131176 self , image ,
132177 mask ,
@@ -136,6 +181,12 @@ def process(
136181 face_reducer ,
137182 paint ,
138183 model = 'tencent/Hunyuan3D-2' ,
184+ back_image = None ,
185+ back_mask = None ,
186+ left_image = None ,
187+ left_mask = None ,
188+ right_image = None ,
189+ right_mask = None ,
139190 ):
140191 from hy3dgen .shapegen import Hunyuan3DDiTFlowMatchingPipeline
141192 import hy3dgen .shapegen
@@ -152,7 +203,7 @@ def process(
152203# if 'HY3DGEN_MODELS' not in os.environ:
153204# os.environ['HY3DGEN_MODELS'] = checkpoint_dir
154205
155- variant = None
206+ variant = 'fp16'
156207 variant_m = re .match (r"^(.*)\[variant=([^\]]+)\]$" , model )
157208 if variant_m is not None :
158209 model = variant_m .group (1 )
@@ -164,23 +215,72 @@ def process(
164215 model = subfolder_m .group (1 )
165216 subfolder = subfolder_m .group (2 )
166217
218+ isMV = model == 'tencent/Hunyuan3D-2mv'
167219 output_3d_file = None
168220 with tempfile .TemporaryDirectory () as temp_dir :
169- for img , mask1 in zip (image , mask ):
170- i = 255. * img .cpu ().numpy ()
171- img = Image .fromarray (np .clip (i , 0 , 255 ).astype (np .uint8 ))
172- if mask1 is not None :
173- m = 255. * mask1 .cpu ().numpy ()
174- mask = Image .fromarray (m )
175- mask = np .clip (mask , 0 , 255 ).astype (np .uint8 )
176- mask = 255 - mask
177- # If it crashes here,
178- # check that you have transparency in the image
179- img .putalpha (Image .fromarray (mask , mode = 'L' ))
180- input_image_file = os .path .join (temp_dir , "input.png" )
181- img .save (input_image_file )
182-
183- mask = i = img = None
221+ nth = 0
222+ front_images_list = []
223+ back_images_list = []
224+ left_images_list = []
225+ right_images_list = []
226+ if image is not None :
227+ front_images_list = list (zip (image , mask ))
228+ if back_image is not None :
229+ back_images_list = list (zip (back_image , back_mask ))
230+ if left_image is not None :
231+ left_images_list = list (zip (left_image , left_mask ))
232+ if right_image is not None :
233+ right_images_list = list (zip (right_image , right_mask ))
234+
235+ max_len = max (
236+ len (front_images_list ),
237+ len (back_images_list ),
238+ len (left_images_list ),
239+ len (right_images_list ),
240+ )
241+
242+ sides = {
243+ 'front' : front_images_list ,
244+ 'back' : back_images_list ,
245+ 'left' : left_images_list ,
246+ 'right' : right_images_list ,
247+ }
248+
249+ # front should always be first
250+ side_names = [
251+ 'front' ,
252+ 'back' ,
253+ 'left' ,
254+ 'right' ,
255+ ]
256+
257+ for nth in range (0 , max_len ):
258+ image_obj = None
259+ for side in side_names :
260+ side_list = sides [side ]
261+ if side_list is None :
262+ continue
263+
264+ if nth >= len (side_list ):
265+ continue
266+
267+ side_info = side_list [nth ]
268+ if side_info is None :
269+ continue
270+
271+ (side_image , side_mask ) = side_list [nth ]
272+ input_image_file = os .path .join (
273+ temp_dir , f"{ side } { nth } .png"
274+ )
275+ self .comfy_img_to_file (
276+ side_image , side_mask , input_image_file
277+ )
278+ image_obj = {}
279+ if isMV :
280+ image_obj [side ] = input_image_file
281+ else :
282+ image_obj = input_image_file
283+ break
184284
185285 pipeline = Hunyuan3DDiTFlowMatchingPipeline .from_pretrained (
186286 model ,
@@ -191,9 +291,11 @@ def process(
191291 output_3d_file = Hunyuan3DImageTo3D .get_spare_filename (
192292 os .path .join (output_dir , 'hunyuan3d_{:05X}.glb' )
193293 )
294+
194295 mesh = pipeline (
195- image = input_image_file , num_inference_steps = steps ,
196- generator = torch .manual_seed (2025 ))[0 ]
296+ image = image_obj , num_inference_steps = steps ,
297+ generator = torch .manual_seed (2025 )
298+ )[0 ]
197299 if floater_remover :
198300 mesh = hy3dgen .shapegen .FloaterRemover ()(mesh )
199301 if face_remover :
@@ -204,7 +306,8 @@ def process(
204306 if paint :
205307 from hy3dgen .texgen import Hunyuan3DPaintPipeline
206308 pipeline = Hunyuan3DPaintPipeline .from_pretrained (
207- model ,
309+ "tencent/Hunyuan3D-2" ,
310+ # model,
208311 )
209312 mesh = pipeline (mesh , image = input_image_file )
210313
@@ -222,12 +325,26 @@ def IS_CHANGED(
222325 face_reducer ,
223326 paint ,
224327 model ,
328+ back_image ,
329+ back_mask ,
330+ left_image ,
331+ left_mask ,
332+ right_image ,
333+ right_mask ,
225334 ):
226335 m = hashlib .sha256 ()
336+ m .update (image )
337+ m .update (mask )
227338 m .update (steps )
228339 m .update (floater_remover )
229340 m .update (face_remover )
230341 m .update (face_reducer )
231342 m .update (paint )
232343 m .update (model )
344+ m .update (back_image )
345+ m .update (back_mask )
346+ m .update (left_image )
347+ m .update (left_mask )
348+ m .update (right_image )
349+ m .update (right_mask )
233350 return m .digest ().hex ()
0 commit comments