diff --git a/README.md b/README.md
index c355fbc..e290043 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@
| Date | Description |
| --- | --- |
+| **2026-04-05** | Added node "Extract Images from Video"
Can be used with "Sparse Generator with ReconViaGen" |
| **2026-04-04** | Added node "Sparse Generator with ReconViaGen" |
| **2026-04-01** | Added node "Voxel to Mesh"
It replaces Remeshing to make watertight mesh |
| **2026-03-21** | Added node "Projection HighPoly to LowPoly"
Added node "Render MultiView" |
diff --git a/example_workflows/ReconViaGen_MeshOnly_FromVideo.json b/example_workflows/ReconViaGen_MeshOnly_FromVideo.json
new file mode 100644
index 0000000..28bc191
--- /dev/null
+++ b/example_workflows/ReconViaGen_MeshOnly_FromVideo.json
@@ -0,0 +1,1053 @@
+{
+ "id": "6eb9772a-caf3-4cd2-a1f3-8e21b2fcb356",
+ "revision": 0,
+ "last_node_id": 32,
+ "last_link_id": 66,
+ "nodes": [
+ {
+ "id": 8,
+ "type": "Trellis2ShapeCascadeGenerator",
+ "pos": [
+ 2144.5184531017253,
+ 89.08064130521795
+ ],
+ "size": [
+ 335.9791015625,
+ 338
+ ],
+ "flags": {},
+ "order": 7,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "link": 16
+ },
+ {
+ "name": "image_cond",
+ "type": "IMAGE_COND",
+ "link": 58
+ },
+ {
+ "name": "shape_slat",
+ "type": "SHAPE_SLAT",
+ "link": 18
+ },
+ {
+ "name": "from_resolution",
+ "type": "INT",
+ "widget": {
+ "name": "from_resolution"
+ },
+ "link": 19
+ },
+ {
+ "name": "sparse_structure_resolution",
+ "type": "INT",
+ "widget": {
+ "name": "sparse_structure_resolution"
+ },
+ "link": 34
+ }
+ ],
+ "outputs": [
+ {
+ "name": "shape_slat",
+ "type": "SHAPE_SLAT",
+ "links": [
+ 11
+ ]
+ },
+ {
+ "name": "resolution",
+ "type": "INT",
+ "links": [
+ 12
+ ]
+ },
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": [
+ 10
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "ef2286b47ce0ef0e681dffe0209aadd96de50f72",
+ "Node name for S&R": "Trellis2ShapeCascadeGenerator"
+ },
+ "widgets_values": [
+ 0,
+ 1024,
+ 0,
+ 999999,
+ 12,
+ 7.5,
+ 0.01,
+ 3,
+ "heun",
+ 0.1,
+ 1
+ ]
+ },
+ {
+ "id": 9,
+ "type": "Trellis2DecodeLatents",
+ "pos": [
+ 2550.5271755896983,
+ 85.2758108930415
+ ],
+ "size": [
+ 270,
+ 122
+ ],
+ "flags": {},
+ "order": 8,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "link": 10
+ },
+ {
+ "name": "shape_slat",
+ "type": "SHAPE_SLAT",
+ "link": 11
+ },
+ {
+ "name": "texture_slat",
+ "shape": 7,
+ "type": "TEXTURE_SLAT",
+ "link": null
+ },
+ {
+ "name": "resolution",
+ "type": "INT",
+ "widget": {
+ "name": "resolution"
+ },
+ "link": 12
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "links": [
+ 13
+ ]
+ },
+ {
+ "name": "bvh",
+ "type": "BVH",
+ "links": []
+ },
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": []
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "ef2286b47ce0ef0e681dffe0209aadd96de50f72",
+ "Node name for S&R": "Trellis2DecodeLatents"
+ },
+ "widgets_values": [
+ 0,
+ true
+ ]
+ },
+ {
+ "id": 10,
+ "type": "Trellis2FillHolesWithCuMesh",
+ "pos": [
+ 2861.8616589898866,
+ 88.51255217697037
+ ],
+ "size": [
+ 312.4361328125,
+ 58
+ ],
+ "flags": {},
+ "order": 9,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "link": 13
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "links": [
+ 14
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "dc66f263e832b103a81a021bf6cd53eec8ff28af",
+ "Node name for S&R": "Trellis2FillHolesWithCuMesh"
+ },
+ "widgets_values": [
+ 1
+ ]
+ },
+ {
+ "id": 11,
+ "type": "Trellis2ReconstructMeshWithQuad",
+ "pos": [
+ 2847.559963718577,
+ 257.899269352995
+ ],
+ "size": [
+ 331.5878996659427,
+ 142.11360851901668
+ ],
+ "flags": {},
+ "order": 10,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "link": 14
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "links": [
+ 15
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "d4d66cfc9f1ce5b1c3b9a2e504ee4b24ffad48a5",
+ "Node name for S&R": "Trellis2ReconstructMeshWithQuad",
+ "widget_ue_connectable": {}
+ },
+ "widgets_values": [
+ 1,
+ 1024,
+ true,
+ true
+ ]
+ },
+ {
+ "id": 12,
+ "type": "Trellis2SimplifyMesh",
+ "pos": [
+ 2886.0029502911757,
+ 486.99086892176337
+ ],
+ "size": [
+ 270,
+ 82
+ ],
+ "flags": {},
+ "order": 11,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "link": 15
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "links": [
+ 35
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "ef2286b47ce0ef0e681dffe0209aadd96de50f72",
+ "Node name for S&R": "Trellis2SimplifyMesh"
+ },
+ "widgets_values": [
+ 500000,
+ "Cumesh"
+ ]
+ },
+ {
+ "id": 13,
+ "type": "Trellis2MeshWithVoxelToTrimesh",
+ "pos": [
+ 2840.8121187391353,
+ 764.4960805898208
+ ],
+ "size": [
+ 349.41171875,
+ 58
+ ],
+ "flags": {},
+ "order": 13,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "link": 36
+ }
+ ],
+ "outputs": [
+ {
+ "name": "trimesh",
+ "type": "TRIMESH",
+ "links": [
+ 21
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "ef2286b47ce0ef0e681dffe0209aadd96de50f72",
+ "Node name for S&R": "Trellis2MeshWithVoxelToTrimesh"
+ },
+ "widgets_values": [
+ "90 degrees"
+ ]
+ },
+ {
+ "id": 19,
+ "type": "Trellis2FillHolesWithMeshlib",
+ "pos": [
+ 2889.6686029432403,
+ 637.1515820571416
+ ],
+ "size": [
+ 249.284765625,
+ 46
+ ],
+ "flags": {},
+ "order": 12,
+ "mode": 4,
+ "inputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "link": 35
+ }
+ ],
+ "outputs": [
+ {
+ "name": "mesh",
+ "type": "MESHWITHVOXEL",
+ "links": [
+ 36
+ ]
+ },
+ {
+ "name": "holes_filled",
+ "type": "INT",
+ "links": null
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "a5242473f68fe91d5ef0b188f8658ae0da540237",
+ "Node name for S&R": "Trellis2FillHolesWithMeshlib"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 15,
+ "type": "Preview3D",
+ "pos": [
+ 1271.3057615922355,
+ 522.7384991788329
+ ],
+ "size": [
+ 1051.7218022911484,
+ 1141.158091689083
+ ],
+ "flags": {},
+ "order": 15,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "camera_info",
+ "shape": 7,
+ "type": "LOAD3D_CAMERA",
+ "link": null
+ },
+ {
+ "name": "bg_image",
+ "shape": 7,
+ "type": "IMAGE",
+ "link": null
+ },
+ {
+ "name": "model_file",
+ "type": "STRING,FILE_3D_GLB,FILE_3D_GLTF,FILE_3D_FBX,FILE_3D_OBJ,FILE_3D_STL,FILE_3D_USDZ,FILE_3D",
+ "widget": {
+ "name": "model_file"
+ },
+ "link": 22
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.18.1",
+ "Node name for S&R": "Preview3D",
+ "Camera Config": {
+ "cameraType": "perspective",
+ "fov": 75,
+ "state": {
+ "position": {
+ "x": 3.1224925587544337,
+ "y": 3.9751518637490415,
+ "z": 2.9199690658797848
+ },
+ "target": {
+ "x": 0,
+ "y": 2.5,
+ "z": 0
+ },
+ "zoom": 1,
+ "cameraType": "perspective"
+ }
+ },
+ "Last Time Model File": "C:/Git/ComfyUI/output/VGGT_MV_00007_.glb",
+ "Resource Folder": "Git/ComfyUI/output",
+ "Scene Config": {
+ "showGrid": true,
+ "backgroundColor": "#282828",
+ "backgroundImage": "",
+ "backgroundRenderMode": "tiled"
+ },
+ "Light Config": {
+ "intensity": 3
+ },
+ "Model Config": {
+ "upDirection": "original",
+ "materialMode": "normal",
+ "showSkeleton": false
+ }
+ },
+ "widgets_values": [
+ "C:/Git/ComfyUI/output/VGGT_MV_00007_.glb",
+ ""
+ ]
+ },
+ {
+ "id": 14,
+ "type": "Trellis2ExportMesh",
+ "pos": [
+ 2878.801482335511,
+ 893.0809816705423
+ ],
+ "size": [
+ 270,
+ 102
+ ],
+ "flags": {},
+ "order": 14,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "trimesh",
+ "type": "TRIMESH",
+ "link": 21
+ }
+ ],
+ "outputs": [
+ {
+ "name": "glb_path",
+ "type": "STRING",
+ "links": [
+ 22
+ ]
+ },
+ {
+ "name": "relative_path",
+ "type": "STRING",
+ "links": null
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "ef2286b47ce0ef0e681dffe0209aadd96de50f72",
+ "Node name for S&R": "Trellis2ExportMesh"
+ },
+ "widgets_values": [
+ "VGGT_MV",
+ "glb"
+ ]
+ },
+ {
+ "id": 4,
+ "type": "Trellis2SparseGeneratorWithReconViaGen",
+ "pos": [
+ 1257.1961548750526,
+ 122.64558954014163
+ ],
+ "size": [
+ 402.6431640625,
+ 334
+ ],
+ "flags": {},
+ "order": 4,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "link": 2
+ },
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 63
+ }
+ ],
+ "outputs": [
+ {
+ "name": "coords",
+ "type": "COORDS",
+ "links": [
+ 32
+ ]
+ },
+ {
+ "name": "sparse_structure_resolution",
+ "type": "INT",
+ "links": [
+ 34
+ ]
+ },
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": [
+ 33
+ ]
+ },
+ {
+ "name": "image_cond",
+ "type": "IMAGE_COND",
+ "links": []
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "a5242473f68fe91d5ef0b188f8658ae0da540237",
+ "Node name for S&R": "Trellis2SparseGeneratorWithReconViaGen"
+ },
+ "widgets_values": [
+ 12345,
+ "fixed",
+ 25,
+ 7.5,
+ 0.01,
+ 5,
+ "euler",
+ 32,
+ 0.1,
+ 1
+ ]
+ },
+ {
+ "id": 5,
+ "type": "Trellis2ShapeGenerator",
+ "pos": [
+ 1728.3644132553936,
+ 149.205532716106
+ ],
+ "size": [
+ 335.24609375,
+ 266
+ ],
+ "flags": {},
+ "order": 6,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "link": 33
+ },
+ {
+ "name": "image_cond",
+ "type": "IMAGE_COND",
+ "link": 57
+ },
+ {
+ "name": "coords",
+ "type": "COORDS",
+ "link": 32
+ }
+ ],
+ "outputs": [
+ {
+ "name": "shape_slat",
+ "type": "SHAPE_SLAT",
+ "links": [
+ 18
+ ]
+ },
+ {
+ "name": "resolution",
+ "type": "INT",
+ "links": [
+ 19
+ ]
+ },
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": [
+ 16
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "a5242473f68fe91d5ef0b188f8658ae0da540237",
+ "Node name for S&R": "Trellis2ShapeGenerator"
+ },
+ "widgets_values": [
+ 512,
+ 25,
+ 7.5,
+ 0.01,
+ 3,
+ "heun",
+ 0.1,
+ 1
+ ]
+ },
+ {
+ "id": 1,
+ "type": "Trellis2LoadModel",
+ "pos": [
+ 614.8749036576971,
+ -262.37703626247117
+ ],
+ "size": [
+ 301.71875,
+ 226
+ ],
+ "flags": {},
+ "order": 0,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": [
+ 2,
+ 55
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "c5d66aa46bf1bbf903fd4fe9ff086ac774bb7e03",
+ "Node name for S&R": "Trellis2LoadModel",
+ "widget_ue_connectable": {}
+ },
+ "widgets_values": [
+ "microsoft/TRELLIS.2-4B",
+ "flash_attn",
+ "cuda",
+ true,
+ false,
+ "flex_gemm",
+ "flash_attn",
+ true
+ ]
+ },
+ {
+ "id": 23,
+ "type": "PreviewImage",
+ "pos": [
+ -23.66147835882571,
+ 524.1622190318901
+ ],
+ "size": [
+ 1207.3601737800832,
+ 1034.082524221429
+ ],
+ "flags": {},
+ "order": 2,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "link": 41
+ }
+ ],
+ "outputs": [],
+ "properties": {
+ "cnr_id": "comfy-core",
+ "ver": "0.18.1",
+ "Node name for S&R": "PreviewImage"
+ },
+ "widgets_values": []
+ },
+ {
+ "id": 20,
+ "type": "Trellis2ExtractImagesFromVideo",
+ "pos": [
+ 22.654179598652767,
+ 336.740539302737
+ ],
+ "size": [
+ 341.20859375,
+ 82
+ ],
+ "flags": {},
+ "order": 1,
+ "mode": 0,
+ "inputs": [],
+ "outputs": [
+ {
+ "name": "images",
+ "type": "IMAGE",
+ "links": [
+ 41,
+ 66
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "f791215b2a608f2fae67722702b06f871af9e5c4",
+ "Node name for S&R": "Trellis2ExtractImagesFromVideo"
+ },
+ "widgets_values": [
+ "C:\\Users\\visua\\Downloads\\model-recording-1775339461054.mp4",
+ 2
+ ]
+ },
+ {
+ "id": 31,
+ "type": "Trellis2ImageCondGenerator",
+ "pos": [
+ 1272.906859863932,
+ -90.65371968536492
+ ],
+ "size": [
+ 308.0748046875,
+ 98
+ ],
+ "flags": {},
+ "order": 5,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "link": 55
+ },
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 64
+ }
+ ],
+ "outputs": [
+ {
+ "name": "cond_512",
+ "type": "IMAGE_COND",
+ "links": [
+ 57
+ ]
+ },
+ {
+ "name": "cond_1024",
+ "type": "IMAGE_COND",
+ "links": [
+ 58
+ ]
+ },
+ {
+ "name": "pipeline",
+ "type": "TRELLIS2PIPELINE",
+ "links": null
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "f791215b2a608f2fae67722702b06f871af9e5c4",
+ "Node name for S&R": "Trellis2ImageCondGenerator"
+ },
+ "widgets_values": [
+ 16
+ ]
+ },
+ {
+ "id": 2,
+ "type": "Trellis2PreProcessImage",
+ "pos": [
+ 635.3919465809572,
+ 139.54969969071414
+ ],
+ "size": [
+ 281.8229166666667,
+ 106
+ ],
+ "flags": {},
+ "order": 3,
+ "mode": 0,
+ "inputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "link": 66
+ }
+ ],
+ "outputs": [
+ {
+ "name": "image",
+ "type": "IMAGE",
+ "links": [
+ 63,
+ 64
+ ]
+ }
+ ],
+ "properties": {
+ "aux_id": "visualbruno/ComfyUI-Trellis2",
+ "ver": "15854282a73cf231b81d52ada22e652f414a078b",
+ "Node name for S&R": "Trellis2PreProcessImage",
+ "widget_ue_connectable": {}
+ },
+ "widgets_values": [
+ 5,
+ true,
+ 1024
+ ]
+ }
+ ],
+ "links": [
+ [
+ 2,
+ 1,
+ 0,
+ 4,
+ 0,
+ "TRELLIS2PIPELINE"
+ ],
+ [
+ 10,
+ 8,
+ 2,
+ 9,
+ 0,
+ "TRELLIS2PIPELINE"
+ ],
+ [
+ 11,
+ 8,
+ 0,
+ 9,
+ 1,
+ "SHAPE_SLAT"
+ ],
+ [
+ 12,
+ 8,
+ 1,
+ 9,
+ 3,
+ "INT"
+ ],
+ [
+ 13,
+ 9,
+ 0,
+ 10,
+ 0,
+ "MESHWITHVOXEL"
+ ],
+ [
+ 14,
+ 10,
+ 0,
+ 11,
+ 0,
+ "MESHWITHVOXEL"
+ ],
+ [
+ 15,
+ 11,
+ 0,
+ 12,
+ 0,
+ "MESHWITHVOXEL"
+ ],
+ [
+ 16,
+ 5,
+ 2,
+ 8,
+ 0,
+ "TRELLIS2PIPELINE"
+ ],
+ [
+ 18,
+ 5,
+ 0,
+ 8,
+ 2,
+ "SHAPE_SLAT"
+ ],
+ [
+ 19,
+ 5,
+ 1,
+ 8,
+ 3,
+ "INT"
+ ],
+ [
+ 21,
+ 13,
+ 0,
+ 14,
+ 0,
+ "TRIMESH"
+ ],
+ [
+ 22,
+ 14,
+ 0,
+ 15,
+ 2,
+ "STRING"
+ ],
+ [
+ 32,
+ 4,
+ 0,
+ 5,
+ 2,
+ "COORDS"
+ ],
+ [
+ 33,
+ 4,
+ 2,
+ 5,
+ 0,
+ "TRELLIS2PIPELINE"
+ ],
+ [
+ 34,
+ 4,
+ 1,
+ 8,
+ 4,
+ "INT"
+ ],
+ [
+ 35,
+ 12,
+ 0,
+ 19,
+ 0,
+ "MESHWITHVOXEL"
+ ],
+ [
+ 36,
+ 19,
+ 0,
+ 13,
+ 0,
+ "MESHWITHVOXEL"
+ ],
+ [
+ 41,
+ 20,
+ 0,
+ 23,
+ 0,
+ "IMAGE"
+ ],
+ [
+ 55,
+ 1,
+ 0,
+ 31,
+ 0,
+ "TRELLIS2PIPELINE"
+ ],
+ [
+ 57,
+ 31,
+ 0,
+ 5,
+ 1,
+ "IMAGE_COND"
+ ],
+ [
+ 58,
+ 31,
+ 1,
+ 8,
+ 1,
+ "IMAGE_COND"
+ ],
+ [
+ 63,
+ 2,
+ 0,
+ 4,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 64,
+ 2,
+ 0,
+ 31,
+ 1,
+ "IMAGE"
+ ],
+ [
+ 66,
+ 20,
+ 0,
+ 2,
+ 0,
+ "IMAGE"
+ ]
+ ],
+ "groups": [],
+ "config": {},
+ "extra": {
+ "ds": {
+ "scale": 0.4240976183724851,
+ "offset": [
+ 525.3533201242849,
+ 457.9701069886444
+ ]
+ },
+ "frontendVersion": "1.42.8",
+ "VHS_latentpreview": false,
+ "VHS_latentpreviewrate": 0,
+ "VHS_MetadataImage": true,
+ "VHS_KeepIntermediate": true
+ },
+ "version": 0.4
+}
\ No newline at end of file
diff --git a/nodes.py b/nodes.py
index 3813f39..00b7cba 100644
--- a/nodes.py
+++ b/nodes.py
@@ -2383,20 +2383,58 @@ class Trellis2PreProcessImage:
CATEGORY = "Trellis2Wrapper"
def process(self, image, padding, remove_background, max_size):
- image = tensor2pil(image)
-
- if remove_background:
- from rembg import remove
- image = remove(image)
-
- image = self.preprocess_image(image, max_size)
-
- if padding>0:
- border = (int(padding), int(padding), int(padding), int(padding))
- fill_color = self.parse_fill_for_image("0,0,0,255", image)
- image = ImageOps.expand(image,border=border,fill=fill_color)
-
- image = pil2tensor(image)
+ if image.ndim == 3:
+ image = tensor2pil(image)
+
+ if remove_background:
+ from rembg import remove
+ image = remove(image)
+
+ image = self.preprocess_image(image, max_size)
+
+ if padding>0:
+ border = (int(padding), int(padding), int(padding), int(padding))
+ fill_color = self.parse_fill_for_image("0,0,0,255", image)
+ image = ImageOps.expand(image,border=border,fill=fill_color)
+
+ image = pil2tensor(image)
+ elif image.ndim == 4:
+ images = convert_tensor_images_to_pil(image)
+ tensor_list = []
+ for img in images:
+ if remove_background:
+ from rembg import remove
+ img = remove(img)
+
+ img = self.preprocess_image(img, max_size)
+
+ if padding>0:
+ border = (int(padding), int(padding), int(padding), int(padding))
+ fill_color = self.parse_fill_for_image("0,0,0,255", img)
+ img = ImageOps.expand(img,border=border,fill=fill_color)
+
+ tensor_list.append(pil2tensor(img))
+
+ max_h = max(t.shape[-3] for t in tensor_list)
+ max_w = max(t.shape[-2] for t in tensor_list)
+
+ resized_tensors = []
+
+ for t in tensor_list:
+ # Ensure tensor is [C, H, W] for PyTorch's interpolate function
+ # Current shape is likely [H, W, C] or [1, H, W, C]
+ temp_t = t.squeeze() # Get to [H, W, C]
+ temp_t = temp_t.permute(2, 0, 1).unsqueeze(0) # Becomes [1, C, H, W]
+
+ # 2. Resize to the max dimensions
+ # Using 'bicubic' or 'bilinear' for better quality than 'nearest'
+ temp_t = F.interpolate(temp_t, size=(max_h, max_w), mode='bicubic', align_corners=False)
+
+ # 3. Convert back to ComfyUI format [H, W, C]
+ temp_t = temp_t.squeeze(0).permute(1, 2, 0)
+ resized_tensors.append(temp_t)
+
+ image = torch.stack(resized_tensors)
return (image,)
@@ -3527,7 +3565,7 @@ class Trellis2ImageCondGenerator:
"required": {
"pipeline": ("TRELLIS2PIPELINE",),
"image": ("IMAGE",),
- "max_views": ("INT", {"default": 4, "min": 1, "max": 16}),
+ "max_views": ("INT", {"default": 1, "min": 1, "max": 999}),
},
}
@@ -3779,6 +3817,9 @@ class Trellis2ShapeCascadeGenerator:
print(f"Num Tokens: {num_tokens}")
hr_resolution = 512
break
+
+ if pipeline.low_vram:
+ cond = pipeline._cond_to(cond, pipeline.device)
coords_dev = coords.to(pipeline.device)
# Sample structured latent
@@ -4804,7 +4845,7 @@ class Trellis2SparseGeneratorWithReconViaGen:
pipeline.unload_sparse_structure_vggt_cond()
self.unload_vggt_model(pipeline)
- return (coords, sparse_structure_resolution, pipeline,)
+ return (coords, sparse_structure_resolution, pipeline)
def load_vggt_model(self, pipeline):
if pipeline.VGGT_model is None:
@@ -4900,7 +4941,7 @@ class Trellis2SparseGeneratorWithReconViaGen:
ss_cond = pipeline._cond_cpu(ss_cond)
torch.cuda.empty_cache()
- return coords
+ return coords
@torch.no_grad()
def _run_ss_stage(
@@ -4986,6 +5027,7 @@ class Trellis2SparseGeneratorWithReconViaGen:
Returns:
dict: The conditioning information
"""
+ pipeline.models['sparse_structure_vggt_cond'].to(pipeline.device)
cond = pipeline.models['sparse_structure_vggt_cond'](aggregated_tokens_list, image_cond)
neg_cond = torch.zeros_like(cond)
return {
@@ -5059,7 +5101,46 @@ class Trellis2SparseGeneratorWithReconViaGen:
transform = transforms.Compose([
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
- pipeline.image_cond_model_transform = transform
+ pipeline.image_cond_model_transform = transform
+
+class Trellis2ExtractImagesFromVideo:
+ @classmethod
+ def INPUT_TYPES(s):
+ return {
+ "required": {
+ "video_file": ("STRING",),
+ "frames_per_second": ("INT",{"default":1,"min":1,"max":50,"step":1}),
+ },
+ }
+
+ RETURN_TYPES = ("IMAGE",)
+ RETURN_NAMES = ("images",)
+ FUNCTION = "process"
+ CATEGORY = "Trellis2Wrapper"
+ OUTPUT_NODE = True
+
+ def process(self, video_file, frames_per_second):
+ import imageio
+
+ vid = imageio.get_reader(video_file, 'ffmpeg')
+ fps = vid.get_meta_data()['fps']
+ frames = []
+ for i, frame in enumerate(vid):
+ if i % max(int(fps/frames_per_second), 1) == 0:
+ img = Image.fromarray(frame)
+ W, H = img.size
+ img = img.resize((int(W / H * 1024), 1024))
+ frames.append(img)
+ vid.close()
+
+ tensor_list = [torch.from_numpy(np.array(img).astype(np.float32) / 255.0) for img in frames]
+
+ print(f"{len(frames)} frames extracted")
+
+ tensor_frames = torch.stack(tensor_list)
+ #tensor_frames = tensor_frames.permute(0, 2, 3, 1)
+
+ return (tensor_frames,)
NODE_CLASS_MAPPINGS = {
"Trellis2LoadModel": Trellis2LoadModel,
@@ -5118,6 +5199,7 @@ NODE_CLASS_MAPPINGS = {
"Trellis2VoxelToMesh": Trellis2VoxelToMesh,
"Trellis2UnloadAllModels": Trellis2UnloadAllModels,
"Trellis2SparseGeneratorWithReconViaGen": Trellis2SparseGeneratorWithReconViaGen,
+ "Trellis2ExtractImagesFromVideo": Trellis2ExtractImagesFromVideo,
}
@@ -5178,4 +5260,5 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"Trellis2VoxelToMesh": "Trellis2 - Voxel to Mesh",
"Trellis2UnloadAllModels": "Trellis2 - Unload All ComfyUI Models",
"Trellis2SparseGeneratorWithReconViaGen": "Trellis2 - Sparse Generator with ReconViaGen",
+ "Trellis2ExtractImagesFromVideo": "Trellis 2 - Extract Images from Video",
}
diff --git a/pyproject.toml b/pyproject.toml
index 570f346..acd35fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "trellis2"
description = "ComfyUI Wrapper for Microsoft Trellis.2 - Native and Compact Structured Latents for 3D Generation"
-version = "1.0.20"
+version = "1.0.21"
license = {file = "LICENSE"}
# classifiers = [
# # For OS-independent nodes (works on all operating systems)