parlorsky commited on
Commit
e83c8d6
·
verified ·
1 Parent(s): 6c7b84e

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +6 -0
  2. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.github/FUNDING.yml +1 -0
  3. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.github/workflows/publish.yml +27 -0
  4. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.gitignore +26 -0
  5. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/CONTRIBUTING.md +34 -0
  6. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/LICENSE +201 -0
  7. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/README.md +1053 -0
  8. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/__init__.py +9 -0
  9. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/configs_3b/main.yaml +91 -0
  10. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/configs_7b/main.yaml +88 -0
  11. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_01.jpg +3 -0
  12. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_02.jpg +3 -0
  13. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/dit_model_loader.png +0 -0
  14. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/seedvr_logo.png +0 -0
  15. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/torch_compile_settings.png +0 -0
  16. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_01.png +3 -0
  17. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_02.png +3 -0
  18. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/vae_model_loader.png +0 -0
  19. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/video_upscaler.png +0 -0
  20. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_4K_image_upscale.jpg +0 -0
  21. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_4K_image_upscale.json +1 -0
  22. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_HD_video_upscale.jpg +0 -0
  23. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_HD_video_upscale.json +1 -0
  24. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_simple_image_upscale.jpg +0 -0
  25. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_simple_image_upscale.json +1 -0
  26. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Eyes_212x120.mp4 +0 -0
  27. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Mustache_640x360.mp4 +3 -0
  28. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Sadhu_320x478.png +3 -0
  29. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/inference_cli.py +1712 -0
  30. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/neg_emb.pt +3 -0
  31. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/pos_emb.pt +3 -0
  32. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/pyproject.toml +40 -0
  33. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/requirements.txt +14 -0
  34. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/__init__.py +0 -0
  35. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/__init__.py +0 -0
  36. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/cache.py +47 -0
  37. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/config.py +134 -0
  38. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/decorators.py +130 -0
  39. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/__init__.py +56 -0
  40. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/config.py +75 -0
  41. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/samplers/base.py +108 -0
  42. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/samplers/euler.py +99 -0
  43. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/schedules/base.py +131 -0
  44. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/schedules/lerp.py +55 -0
  45. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/timesteps/base.py +72 -0
  46. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/timesteps/sampling/trailing.py +50 -0
  47. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/types.py +59 -0
  48. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/utils.py +84 -0
  49. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/distributed/__init__.py +37 -0
  50. v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/distributed/advanced.py +223 -0
.gitattributes CHANGED
@@ -296,3 +296,9 @@ v3-nodes/ComfyUI-RMBG/example_workflows/V3.0.0_nodes.jpg filter=lfs diff=lfs mer
296
  v3-nodes/ComfyUI-RMBG/example_workflows/YOLO_Node.jpg filter=lfs diff=lfs merge=lfs -text
297
  v3-nodes/ComfyUI-RMBG/example_workflows/florence2_node.jpg filter=lfs diff=lfs merge=lfs -text
298
  v3-nodes/ComfyUI-RMBG/models/sam3/perflib/tests/assets/masks.tiff filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
296
  v3-nodes/ComfyUI-RMBG/example_workflows/YOLO_Node.jpg filter=lfs diff=lfs merge=lfs -text
297
  v3-nodes/ComfyUI-RMBG/example_workflows/florence2_node.jpg filter=lfs diff=lfs merge=lfs -text
298
  v3-nodes/ComfyUI-RMBG/models/sam3/perflib/tests/assets/masks.tiff filter=lfs diff=lfs merge=lfs -text
299
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_01.jpg filter=lfs diff=lfs merge=lfs -text
300
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_02.jpg filter=lfs diff=lfs merge=lfs -text
301
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_01.png filter=lfs diff=lfs merge=lfs -text
302
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_02.png filter=lfs diff=lfs merge=lfs -text
303
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Mustache_640x360.mp4 filter=lfs diff=lfs merge=lfs -text
304
+ v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Sadhu_320x478.png filter=lfs diff=lfs merge=lfs -text
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.github/FUNDING.yml ADDED
@@ -0,0 +1 @@
 
 
1
+ github: [adrientoupet, numz]
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.github/workflows/publish.yml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Publish to Comfy registry
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "pyproject.toml"
9
+
10
+ permissions:
11
+ issues: write
12
+
13
+ jobs:
14
+ publish-node:
15
+ name: Publish Custom Node to registry
16
+ runs-on: ubuntu-latest
17
+ if: ${{ github.repository_owner == 'numz' }}
18
+ steps:
19
+ - name: Check out code
20
+ uses: actions/checkout@v4
21
+ with:
22
+ submodules: true
23
+ - name: Publish Custom Node
24
+ uses: Comfy-Org/publish-node-action@v1
25
+ with:
26
+ ## Add your own personal access token to your Github Repository secrets and reference it here.
27
+ personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/.gitignore ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .git/*
2
+ **/__pycache__/
3
+ tests/
4
+ .vscode/
5
+ .cursor/
6
+ benchmark/
7
+ advanced_optimizations.py
8
+ BENCHMARK_*
9
+ environment.yml
10
+ install_safetensors.py
11
+ manage_quantized_models.py
12
+ quantization_config.py
13
+ quantize_model.py
14
+ quantize_vae.py
15
+ README_*.md
16
+ run_*.py
17
+ vram_diagnostic.py
18
+ test_*.py
19
+ VRAM_OPTIMIZATIONS_SUMMARY.md
20
+ seedvr2.py
21
+ src/core/isolated_generation.py
22
+ src/core/subprocess_runner.py
23
+ models/video_vae_v3_mine_bad/
24
+ src/processing/
25
+ TILE_VAE*
26
+ .DS_Store
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/CONTRIBUTING.md ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to ComfyUI-SeedVR2_VideoUpscaler
2
+
3
+ Thank you for your interest in contributing to ComfyUI-SeedVR2_VideoUpscaler! We appreciate your effort and to help us incorporate your contribution in the best way possible, please follow the following contribution guidelines.
4
+
5
+ ## Reporting Bugs
6
+
7
+ If you find a bug in the project, we encourage you to report it. Here's how:
8
+
9
+ 1. First, check the [existing Issues](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/issues) to see if the issue has already been reported. If it has, please add a comment to the existing issue rather than creating a new one.
10
+ 2. If you can't find an existing issue that matches your bug, create a new issue. Make sure to include as many details as possible so we can understand and reproduce the problem.
11
+
12
+ ## Proposing Changes
13
+
14
+ We welcome code contributions from the community. Here's how to propose changes:
15
+
16
+ 1. Fork this repository to your own GitHub account.
17
+ 2. Create a new branch on your fork for your changes.
18
+ 3. Make your changes in this branch.
19
+ 4. When you are ready, submit a pull request to the **`main`** branch.
20
+
21
+ We use the GitHub Flow workflow.
22
+
23
+ Before submitting a pull request, please make sure your code adheres to the project's coding conventions and it has passed all tests. If you are adding features, please also add appropriate tests.
24
+
25
+ ## Contact
26
+
27
+ If you have any questions or need help, please reach out to the developers:
28
+
29
+ - **NumZ**: Discord NumZ#7184
30
+ - **adrientoupet** from AInVFX: [YouTube Channel](https://www.youtube.com/@AInVFX)
31
+
32
+ You can also open an issue on GitHub for general questions and discussions.
33
+
34
+ Thank you again for your contribution !
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2025 seed
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/README.md ADDED
@@ -0,0 +1,1053 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ComfyUI-SeedVR2_VideoUpscaler
2
+
3
+ [![View Code](https://img.shields.io/badge/📂_View_Code-GitHub-181717?style=for-the-badge&logo=github)](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler)
4
+
5
+ Official release of [SeedVR2](https://github.com/ByteDance-Seed/SeedVR) for ComfyUI that enables high-quality video and image upscaling.
6
+
7
+ Can run as **Multi-GPU standalone CLI** too, see [🖥️ Run as Standalone](#-run-as-standalone-cli) section.
8
+
9
+ [![SeedVR2 v2.5 Deep Dive Tutorial](https://img.youtube.com/vi/MBtWYXq_r60/maxresdefault.jpg)](https://youtu.be/MBtWYXq_r60)
10
+
11
+ ![Usage Example](docs/usage_01.png)
12
+
13
+ ![Usage Example](docs/usage_02.png)
14
+
15
+ ## 📋 Quick Access
16
+
17
+ - [🆙 Future Work](#-future-work)
18
+ - [🚀 Release Notes](#-release-notes)
19
+ - [🎯 Features](#-features)
20
+ - [🔧 Requirements](#-requirements)
21
+ - [📦 Installation](#-installation)
22
+ - [📖 Usage](#-usage)
23
+ - [🖥️ Run as Standalone](#️-run-as-standalone-cli)
24
+ - [⚠️ Limitations](#️-limitations)
25
+ - [🤝 Contributing](#-contributing)
26
+ - [🙏 Credits](#-credits)
27
+ - [📜 License](#-license)
28
+
29
+ ## 🆙 Future Work
30
+
31
+ We're actively working on improvements and new features. To stay informed:
32
+
33
+ - **📌 Track Active Development**: Visit [Issues](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/issues) to see active development, report bugs, and request new features
34
+ - **💬 Join the Community**: Learn from others, share your workflows, and get help in the [Discussions](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/discussions)
35
+ - **🔮 Next Model Survey**: We're looking for community input on the next open-source super-powerful generic restoration model. Share your suggestions in [Issue #164](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/issues/164)
36
+
37
+ ## 🚀 Release Notes
38
+
39
+ **2025.12.24 - Version 2.5.24**
40
+
41
+ - **🍎 Fix: MPS memory leak regression** - Restored MPS cache clearing after VAE encode/decode operations that was accidentally removed during code cleanup in v2.5.23
42
+
43
+ **2025.12.24 - Version 2.5.23**
44
+
45
+ - **🔒 Security: Prevent code execution in model loading** - Added protection against malicious .pth files by restricting deserialization to tensors only
46
+ - **🎥 Fix: FFmpeg video writer reliability** - Resolved ffmpeg process hanging issues by redirecting stderr and adding buffer flush, with improved error messages for debugging *(thanks [@thehhmdb](https://github.com/thehhmdb))*
47
+ - **⚡ Fix: GGUF VAE model support** - Enabled automatic weight dequantization for convolution operations, making GGUF-quantized VAE models fully functional *(thanks [@naxci1](https://github.com/naxci1))*
48
+ - **🛡️ Fix: VAE slicing edge cases** - Protected against division by zero crashes when using small split sizes with high temporal downsampling *(thanks [@naxci1](https://github.com/naxci1))*
49
+ - **🎨 Fix: LAB color transfer precision** - Resolved dtype mismatch errors during video upscaling by ensuring consistent float types before matrix operations
50
+ - **🔧 Fix: PyTorch 2.9+ compatibility** - Extended Conv3d memory workaround to all PyTorch 2.9+ versions, fixing 3x VRAM usage on newer PyTorch releases
51
+ - **📦 Fix: Bitsandbytes compatibility** - Added ValueError exception handling for Intel Gaudi version detection failures on non-Gaudi systems
52
+ - **🍎 MPS: Memory optimization** - Reduced memory usage during encode/decode operations on Apple Silicon *(thanks [@s-cerevisiae](https://github.com/s-cerevisiae))*
53
+
54
+
55
+ **2025.12.13 - Version 2.5.22**
56
+
57
+ - **🎬 CLI: FFmpeg video backend with 10-bit support** - New `--video_backend ffmpeg` and `--10bit` flags enable x265 encoding with 10-bit color depth, reducing banding artifacts in gradients compared to 8-bit OpenCV output *(based on PR by [@thehhmdb](https://github.com/thehhmdb) - thank you!)*
58
+ - **🍎 Fix: MPS bicubic upscaling compatibility** - Added CPU fallback for bicubic+antialias interpolation on PyTorch versions before 2.8.0, resolving RGBA alpha upscaling errors on Apple Silicon
59
+ - **⚡ Fix: Cross-platform histogram matching** - Replaced scatter_ operation with argsort+index_select for improved reliability across CUDA, ROCm, and MPS backends
60
+ - **🧹 MPS: Remove sync overhead** - Reverted unnecessary `torch.mps.synchronize()` calls introduced in v2.5.21 for consistent behavior with CUDA pipeline
61
+
62
+ **2025.12.12 - Version 2.5.21**
63
+
64
+ - **🛠️ Fix: GGUF dequantization error on MPS** - Resolved shape mismatch error introduced in 2.5.20 by skipping GGUF quantized buffers in precision conversion - these must remain in packed format for on-the-fly dequantization during inference
65
+ - **🍎 MPS: Eliminate CPU sync overhead** - Skip unnecessary CPU tensor offload on Apple Silicon unified memory architecture, preventing sync stalls that caused slowdowns. Input images and output video now stay on MPS device throughout the pipeline
66
+ - **⚡ MPS: Preload text embeddings** - Load text embeddings before Phase 1 encoding to avoid sync stall at Phase 2 start, improving timing accuracy and throughput
67
+ - **🧹 MPS: Optimized model cleanup** - Skip redundant CPU movement before model deletion on unified memory
68
+
69
+ **2025.12.12 - Version 2.5.20**
70
+
71
+ - **⚡ Expanded attention backends** - Full support for Flash Attention 2 (Ampere+), Flash Attention 3 (Hopper+), SageAttention 2, and SageAttention 3 (Blackwell/RTX 50xx), with automatic fallback chains to PyTorch SDPA when unavailable *(based on PR by [@naxci1](https://github.com/naxci1) - thank you!)*
72
+ - **🍎 macOS/Apple Silicon compatibility** - Replaced MPS autocast with explicit dtype conversion throughout VAE and DiT pipelines, resolving hangs and crashes on M-series Macs. BlockSwap now auto-disables with warning (unified memory makes it meaningless)
73
+ - **🛡️ Flash Attention graceful fallback** - Added compatibility shims for corrupted or partially installed flash_attn/xformers DLLs, preventing startup crashes
74
+ - **🛡️ AMD ROCm: bitsandbytes conflict fix** - Prevent kernel registration errors when diffusers attempts to re-import broken bitsandbytes installations
75
+ - **📦 ComfyUI Manager: macOS classifier fix** - Removed NVIDIA CUDA classifier causing false "GPU not supported" warnings on macOS
76
+ - **📚 Documentation updates** - Updated README with attention backend details, BlockSwap macOS notes, and clarified model caching descriptions
77
+
78
+ **2025.12.10 - Version 2.5.19**
79
+
80
+ - **🎨 New header logo design** - Refreshed ASCII art banner *(thanks [@naxci1](https://github.com/naxci1))*
81
+ - **🧹 Remove dead flash attention wrapper** - Removed legacy code from FP8CompatibleDiT; FlashAttentionVarlen already handles backend switching via its `attention_mode` attribute
82
+ - **🛡️ Fix graceful fallback from flash-attn** - Add compatibility shims for corrupted flash_attn/xformers DLLs, preventing startup crashes when CUDA extensions are broken
83
+ - **📊 Improved VRAM tracking** - Separate allocated vs reserved memory tracking, Windows-only overflow detection (WDDM paging behavior)
84
+ - **♻️ Centralize backend detection** - Unified `is_mps_available()`, `is_cuda_available()`, `get_gpu_backend()` helpers across codebase
85
+ - **🔄 Revert 2.5.14 VRAM limit enforcement** - Removed `set_per_process_memory_fraction` call; Overflow detection and warnings remain.
86
+
87
+ **2025.12.09 - Version 2.5.18**
88
+
89
+ - **🚀 CLI: Streaming mode for long videos** - New `--chunk_size` flag processes videos in memory-bounded chunks, enabling arbitrarily long videos without RAM limits. Works with model caching (`--cache_dit`/`--cache_vae`) for chunk-to-chunk reuse *(inspired by [disk02](https://github.com/disk02) PR contribution)*
90
+ - **⚡ CLI: Multi-GPU streaming** - Each GPU now streams its segment internally with independent model caching, improving memory efficiency and enabling `--temporal_overlap` blending at GPU boundaries
91
+ - **🔧 CLI: Fix large video MemoryError** - Shared memory transfer replaces numpy pickling, preventing crashes on high-resolution/long video outputs *(inspired by [FurkanGozukara](https://github.com/FurkanGozukara) PR contribution)*
92
+
93
+ **2025.12.05 - Version 2.5.17**
94
+
95
+ - **🔧 Fix: Older GPU compatibility (GTX 970, etc.)** - Runtime bf16 CUBLAS probe replaces compute capability heuristics, correctly detecting unsupported GPUs without affecting RTX 20XX
96
+
97
+ **2025.12.05 - Version 2.5.16**
98
+
99
+ - **🔧 Fix: Older GPU compatibility (GTX 970, etc.)** - Automatic fallback for GPUs without bfloat16 support
100
+ - **🐛 Fix: Quality regression** - Reverted bfloat16 detection that was causing artifact issues
101
+ - **📋 Debug: Environment info display** - Shows system info in debug mode to help with issue reporting
102
+ - **📚 Docs: Simplified contribution workflow** - Streamlined to main branch only
103
+
104
+ **2025.12.03 - Version 2.5.15**
105
+
106
+ - **🍎 Fix: MPS compatibility** - Disable antialias for MPS tensors and fix bfloat16 arange issues
107
+ - **⚡ Fix: Autocast device type** - Use proper device type attribute to prevent autocast errors
108
+ - **📊 Memory: Accurate VRAM tracking** - Use max_memory_reserved for more precise peak reporting
109
+ - **🔧 Fix: Triton compatibility** - Add shim for bitsandbytes 0.45+ / triton 3.0+ (fixes PyTorch 2.7 installation errors)
110
+
111
+ **2025.12.01 - Version 2.5.14**
112
+
113
+ - **🍎 Fix: MPS device comparison** - Normalize device strings to prevent unnecessary tensor movements
114
+ - **📊 Memory: VRAM swap detection** - Peak stats now show GPU+swap breakdown when overflow occurs, with warning when swap detected
115
+ - **🛡️ Memory: Enforce physical VRAM limit** - PyTorch now OOMs instead of silently swapping to shared memory (prevents extreme slowdowns on Windows)
116
+
117
+ **2025.11.30 - Version 2.5.13**
118
+
119
+ - **🔧 Fix: PyTorch 2.7+ triton import error** - Resolved installation crash caused by triton.ops import chain on newer triton versions
120
+ - **💾 Fix: OOM on float32 conversion for long videos** - Graceful fallback to native dtype when insufficient memory for float32 conversion
121
+ - **🍎 Fix: CLI watermark error on macOS** - Resolved MPS-related watermark processing crash on Apple Silicon
122
+
123
+ **2025.11.28 - Version 2.5.12**
124
+
125
+ - **🐛 Fix: Color artifacts regression** - Reverted in-place tensor operations in video transform pipeline that caused color artifacts on some images
126
+
127
+ **2025.11.28 - Version 2.5.11**
128
+
129
+ - **⚡ Feature: CUDNN attention backend** - Added support for PyTorch 2.3+ CUDNN_ATTENTION backend with automatic fallback for older versions (thanks @eadwu)
130
+ - **💾 Fix: Memory spike for long videos** - VAE decode now streams directly to pre-allocated tensor, eliminating OOM errors during long video processing
131
+ - **🎨 Fix: LAB color correction artifacts** - Resolved tile boundary artifacts using wavelet reconstruction preprocessing
132
+ - **🎨 Fix: Color reference misalignment** - Fixed color correction frame alignment with temporal overlap
133
+ - **🍎 Fix: MPS detection reliability** - Switched to canonical `torch.backends.mps.is_available()` API for consistent Apple Silicon detection
134
+ - **🖥️ Fix: Mac subprocess error** - CLI now uses direct processing on Mac to avoid MPS allocator failures in child processes
135
+ - **🖥️ Fix: Multi-GPU device assignment** - CUDA_VISIBLE_DEVICES now set before spawn for proper worker inheritance
136
+ - **📊 Fix: BlockSwap logging** - Now shows effective/total blocks (e.g., 32/32) instead of raw requested value
137
+ - **🔧 Feature: Auto bfloat16 detection** - Automatically detects bfloat16 support to prevent CUBLAS errors on older GPUs
138
+ - **📊 Feature: Peak RAM tracking** - Added RAM usage alongside VRAM in debug summary
139
+ - **⚡ Performance: In-place tensor ops** - Reduced memory allocation overhead with in-place operations throughout pipeline
140
+ - **📖 Docs: Multi-GPU clarification** - Clarified frame-level parallelism behavior expectations for multi-GPU setups
141
+
142
+ **2025.11.13 - Version 2.5.10**
143
+
144
+ - **🎯 Fix: Deterministic generation** - Identical images with the same seed now produce identical results across different sessions and batch positions
145
+ - **🔧 Fix: Model caching with BlockSwap** - Resolved issue where cached DiT models wouldn't properly reload when VAE caching state changed
146
+ - **💾 Fix: Runner caching optimization** - Runner templates now correctly cache whenever both DiT and VAE are cached, regardless of caching order
147
+ - **📁 Fix: Case-insensitive model paths** - Extra model paths in YAML config now work regardless of case (seedvr2, SEEDVR2, SeedVR2, etc.)
148
+ - **🐛 Fix: High resolution tile debug crash** - Fixed "NoneType has no attribute log" error when using maximum resolution with VAE tiling
149
+ - **📊 Fix: Temporal overlap logging** - Corrected frame count reporting when temporal overlap is automatically adjusted
150
+ - **🔍 Feature: Enhanced model path debugging** - Added detailed logging to help troubleshoot model loading issues (visible in debug mode)
151
+
152
+ **2025.11.12 - Version 2.5.9**
153
+
154
+ - **🐛 Fix: Tile debug visualization crash** - Fixed OpenCV error when using VAE tile debug mode on certain systems.
155
+ - **🍎 Fix: macOS MPS loading error** - Added automatic CPU fallback for MPS allocator issues on certain PyTorch/macOS versions.
156
+ - **🖥️ Fix: Windows log buffering** - Added flush to print statements for real-time log visibility in ComfyUI on Windows
157
+ - **📦 Fix: ComfyUI Registry logo** - Updated icon URL to display properly in ComfyUI node registry
158
+ - **ℹ️ Feature: Version display** - Added version number to node name and CLI/ComfyUI header for better tracking
159
+ - **💝 Feature: GitHub Sponsors** - Added sponsor button to support project development. Thank you everyone for your support!
160
+ - **📜 License: Apache 2.0** - Reverted License from MIT to Apache 2.0 to match ByteDance Seed project
161
+
162
+ **2025.11.10 - Version 2.5.8**
163
+
164
+ - **🐛 Fix (CLI): Windows batch processing duplicate files** - Fixed CLI batch mode processing each file twice on Windows due to case-insensitive filesystem. Improved directory scanning performance by 2-3x
165
+ - **📁 Fix(CLI): Output folder location** - Output files now created in sensible locations: batch mode creates `{folder_name}_upscaled/` sibling folder with original filenames preserved; single file mode adds `_upscaled` suffix in same directory. All logs now show absolute paths for clarity
166
+ - **🎨 Fix(CLI): RGBA alpha channel support** - PNG images with transparency are now properly detected and preserved through the upscaling pipeline, matching ComfyUI behavior
167
+
168
+ **2025.11.10 - Version 2.5.7**
169
+
170
+ - **🔧 Fix: Conv3d workaround compatibility** - Enhanced platform detection and added graceful fallback to prevent errors on PyTorch dev builds and AMD ROCm systems
171
+
172
+ **2025.11.09 - Version 2.5.6**
173
+
174
+ - 🎨 **Fix: Restored natural look for 7b model** - Corrected torch.compile optimization that was causing overly plastic/ high-specular appearance in upscaled videos with 7b model.
175
+
176
+ - 💾 **Memory: Fixed RAM leak for long videos** - On-demand reconstruction with lightweight batch indices instead of storing full transformed videos, fixed release_tensor_memory to handle CPU/CUDA/MPS consistently, and refactored batch processing helpers
177
+
178
+ **2025.11.08 - Version 2.5.4**
179
+
180
+ - 🎨 **Fix: AdaIN color correction** - Replace `.view()` with `.reshape()` to handle non-contiguous tensors after spatial padding, resolving "view size is not compatible with input tensor's size and stride" error
181
+ - 🔴 **Fix: AMD ROCm compatibility** - Add cuDNN availability check in Conv3d workaround to prevent "ATen not compiled with cuDNN support" error on ROCm systems (AMD GPUs on Windows/Linux)
182
+
183
+ **2025.11.08 - Version 2.5.3**
184
+
185
+ - 🍎 **Fix: Apple Silicon MPS device handling** - Corrected MPS device enumeration to use `"mps"` instead of `"mps:0"`, resolving invalid device errors on M-series Macs
186
+ - 🪟 **Fix: torch.mps AttributeError on Windows** - Add defensive checks for `torch.mps.is_available()` to handle PyTorch versions where the method doesn't exist on non-Mac platforms
187
+
188
+ **2025.11.07 - Version 2.5.0** 🎉
189
+
190
+ ⚠️ **BREAKING CHANGE**: This is a major update requiring workflow recreation. All nodes and CLI parameters have been redesigned for better usability and consistency. Watch the latest video from [AInVFX](https://www.youtube.com/@AInVFX) for a deep dive and check out the [usage](#-usage) section.
191
+
192
+ **📦 Official Release**: Now available on main branch with ComfyUI Manager support for easy installation and automatic version tracking. Updated dependencies and local imports prevent conflicts with other ComfyUI custom nodes.
193
+
194
+ ### 🎨 ComfyUI Improvements
195
+
196
+ - **Four-Node Modular Architecture**: Split into dedicated nodes for DiT model, VAE model, torch.compile settings, and main upscaler for granular control
197
+ - **Global Model Cache**: Models now shared across multiple upscaler instances with automatic config updates - no more redundant loading
198
+ - **ComfyUI V3 Migration**: Full compatibility with ComfyUI V3 stateless node design
199
+ - **RGBA Support**: Native alpha channel processing with edge-guided upscaling for clean transparency
200
+ - **Improved Memory Management**: Streaming architecture prevents VRAM spikes regardless of video length
201
+ - **Flexible Resolution Support**: Upscale to any resolution divisible by 2 with lossless padding approach (replaced restrictive cropping)
202
+ - **Enhanced Parameters**: Added `uniform_batch_size`, `temporal_overlap`, `prepend_frames`, and `max_resolution` for better control
203
+
204
+ ### 🖥️ CLI Enhancements
205
+
206
+ - **Batch Directory Processing**: Process entire folders of videos/images with model caching for efficiency
207
+ - **Single Image Support**: Direct image upscaling without video conversion
208
+ - **Smart Output Detection**: Auto-detects output format (MP4/PNG) based on input type
209
+ - **Enhanced Multi-GPU**: Improved workload distribution with temporal overlap blending
210
+ - **Unified Parameters**: CLI and ComfyUI now use identical parameter names for consistency
211
+ - **Better UX**: Auto-display help, validation improvements, progress tracking, and cleaner output
212
+
213
+ ### ⚡ Performance & Optimization
214
+
215
+ - **torch.compile Support**: 20-40% DiT speedup and 15-25% VAE speedup with full graph compilation
216
+ - **Optimized BlockSwap**: Adaptive memory clearing (5% threshold), separate I/O component handling, reduced overhead
217
+ - **Enhanced VAE Tiling**: Tensor offload support for accumulation buffers, separate encode/decode configuration
218
+ - **Native Dtype Pipeline**: Eliminated unnecessary conversions, maintains bfloat16 precision throughout for speed and quality
219
+ - **Optimized Tensor Operations**: Replaced einops rearrange with native PyTorch ops for 2-5x faster transforms
220
+
221
+ ### 🎯 Quality Improvements
222
+
223
+ - **LAB Color Correction**: New perceptual color transfer method with superior color accuracy (now default)
224
+ - **Additional Color Methods**: HSV saturation matching, wavelet adaptive, and hybrid approaches
225
+ - **Deterministic Generation**: Seed-based reproducibility with phase-specific seeding strategy
226
+ - **Better Temporal Consistency**: Hann window blending for smooth transitions between batches
227
+
228
+ ### 💾 Memory Management
229
+
230
+ - **Smarter Offloading**: Independent device configuration for DiT, VAE, and tensors (CPU/GPU/none)
231
+ - **Four-Phase Pipeline**: Completes each phase (encode→upscale→decode→postprocess) for all batches before moving to next, minimizing model swaps
232
+ - **Better Cleanup**: Phase-specific resource management with proper tensor memory release
233
+ - **Peak VRAM Tracking**: Per-phase memory monitoring with summary display
234
+
235
+ ### 🔧 Technical Improvements
236
+
237
+ - **GGUF Quantization Support**: Added full GGUF support for 4-bit/8-bit inference on low-VRAM systems
238
+ - **Improved GGUF Handling**: Fixed VRAM leaks, torch.compile compatibility, non-persistent buffers
239
+ - **Apple Silicon Support**: Full MPS (Metal Performance Shaders) support for Apple Silicon Macs
240
+ - **AMD ROCm Compatibility**: Conditional FSDP imports for PyTorch ROCm 7+ support
241
+ - **Conv3d Memory Workaround**: Fixes PyTorch 2.9+ cuDNN memory bug (3x usage reduction)
242
+ - **Flash Attention Optional**: Graceful fallback to SDPA when flash-attn unavailable
243
+
244
+ ### 📚 Code Quality
245
+
246
+ - **Modular Architecture**: Split monolithic files into focused modules (generation_phases, model_configuration, etc.)
247
+ - **Comprehensive Documentation**: Extensive docstrings with type hints across all modules
248
+ - **Better Error Handling**: Early validation, clear error messages, installation instructions
249
+ - **Consistent Logging**: Unified indentation, better categorization, concise messages
250
+
251
+ **2025.08.07**
252
+
253
+ - 🎯 **Unified Debug System**: New structured logging with categories, timers, and memory tracking. `enable_debug` now available on main node
254
+ - ⚡ **Smart FP8 Optimization**: FP8 models now keep native FP8 storage, converting to BFloat16 only for arithmetic - faster and more memory efficient than FP16
255
+ - 📦 **Model Registry**: Multi-repo support (numz/ & AInVFX/), auto-discovery of user models, added mixed FP8 variants to fix 7B artifacts
256
+ - 💾 **Model Caching**: `cache_model` moved to main node, fixed memory leaks with proper RoPE/wrapper cleanup
257
+ - 🧹 **Code Cleanup**: New modular structure (`constants.py`, `model_registry.py`, `debug.py`), removed legacy code
258
+ - 🚀 **Performance**: Better memory management with `torch.cuda.ipc_collect()`, improved RoPE handling
259
+
260
+ **2025.07.17**
261
+
262
+ - 🛠️ Add 7B sharp Models: add 2 new 7B models with sharpen output
263
+
264
+ **2025.07.11**
265
+
266
+ - 🎬 Complete tutorial released: Adrien from [AInVFX](https://www.youtube.com/@AInVFX) created an in-depth ComfyUI SeedVR2 guide covering everything from basic setup to advanced BlockSwap techniques for running on consumer GPUs. Perfect for understanding memory optimization and upscaling of image sequences with alpha channel! [Watch the tutorial](#-usage)
267
+
268
+ **2025.09.07**
269
+
270
+ - 🛠️ Blockswap Integration: Big thanks to [Adrien Toupet](https://github.com/adrientoupet) from [AInVFX](https://www.youtube.com/@AInVFX) for this :), useful for low VRAM users (see [usage](#-usage) section)
271
+
272
+ **2025.07.03**
273
+
274
+ - 🛠️ Can run as **standalone mode** with **Multi GPU** see [🖥️ Run as Standalone](#run-as-standalone-cli)
275
+
276
+ **2025.06.30**
277
+
278
+ - 🚀 Speed Up the process and less VRAM used
279
+ - 🛠️ Fixed memory leak on 3B models
280
+ - ❌ Can now interrupt process if needed
281
+ - ✅ Refactored the code for better sharing with the community, feel free to propose pull requests
282
+ - 🛠️ Removed flash attention dependency (thanks to [luke2642](https://github.com/Luke2642) !!)
283
+
284
+ **2025.06.24**
285
+
286
+ - 🚀 Speed up the process until x4
287
+
288
+ **2025.06.22**
289
+
290
+ - 💪 FP8 compatibility !
291
+ - 🚀 Speed Up all Process
292
+ - 🚀 less VRAM consumption (Stay high, batch_size=1 for RTX4090 max, I'm trying to fix that)
293
+ - 🛠️ Better benchmark coming soon
294
+
295
+ **2025.06.20**
296
+
297
+ - 🛠️ Initial push
298
+
299
+ ## 🎯 Features
300
+
301
+ ### Core Capabilities
302
+ - **High-Quality Diffusion-Based Upscaling**: One-step diffusion model for video and image enhancement
303
+ - **Temporal Consistency**: Maintains coherence across video frames with configurable batch processing
304
+ - **Multi-Format Support**: Handles RGB and RGBA (alpha channel) for both videos and images
305
+ - **Any Video Length**: Suitable for any video length
306
+
307
+ ### Model Support
308
+ - **Multiple Model Variants**: 3B and 7B parameter models with different precision options
309
+ - **FP16, FP8, and GGUF Quantization**: Choose between full precision (FP16), mixed precision (FP8), or heavily quantized GGUF models for different VRAM requirements
310
+ - **Automatic Model Downloads**: Models are automatically downloaded from HuggingFace on first use
311
+
312
+ ### Memory Optimization
313
+ - **BlockSwap Technology**: Dynamically swap transformer blocks between GPU and CPU memory to run large models on limited VRAM
314
+ - **VAE Tiling**: Process large resolutions with tiled encoding/decoding to reduce VRAM usage
315
+ - **Intelligent Offloading**: Offload models and intermediate tensors to CPU or secondary GPUs between processing phases
316
+ - **GGUF Quantization Support**: Run models with 4-bit or 8-bit quantization for extreme VRAM savings
317
+
318
+ ### Performance Features
319
+ - **torch.compile Integration**: Optional 20-40% DiT speedup and 15-25% VAE speedup with PyTorch 2.0+ compilation
320
+ - **Multi-GPU CLI**: Distribute workload across multiple GPUs with automatic temporal overlap blending
321
+ - **Model Caching**: Keep models loaded between generations for single-GPU directory processing or multi-GPU streaming
322
+ - **Flexible Attention Backends**: Choose between PyTorch SDPA (stable, always available), Flash Attention 2/3, or SageAttention 2/3 for faster computation on supported hardware
323
+
324
+ ### Quality Control
325
+ - **Advanced Color Correction**: Five methods including LAB (recommended for highest fidelity), wavelet, wavelet adaptive, HSV, and AdaIN
326
+ - **Noise Injection Controls**: Fine-tune input and latent noise scales for artifact reduction at high resolutions
327
+ - **Configurable Resolution Limits**: Set target and maximum resolutions with automatic aspect ratio preservation
328
+
329
+ ### Workflow Features
330
+ - **ComfyUI Integration**: Four dedicated nodes for complete control over the upscaling pipeline
331
+ - **Standalone CLI**: Command-line interface for batch processing and automation
332
+ - **Debug Logging**: Comprehensive debug mode with memory tracking, timing information, and processing details
333
+ - **Progress Reporting**: Real-time progress updates during processing
334
+
335
+ ## 🔧 Requirements
336
+
337
+ ### Hardware
338
+
339
+ With the current optimizations (tiling, BlockSwap, GGUF quantization), SeedVR2 can run on a wide range of hardware:
340
+
341
+ - **Minimal VRAM** (8GB or less): Use GGUF Q4_K_M models with BlockSwap and VAE tiling enabled
342
+ - **Moderate VRAM** (12-16GB): Use FP8 models with BlockSwap or VAE tiling as needed
343
+ - **High VRAM** (24GB+): Use FP16 models for best quality and speed without memory optimizations
344
+
345
+ ### Software
346
+
347
+ - **ComfyUI**: Latest version recommended
348
+ - **Python**: 3.12+ (Python 3.12 and 3.13 tested and recommended)
349
+ - **PyTorch**: 2.0+ for torch.compile support (optional but recommended)
350
+ - **Triton**: Required for torch.compile with inductor backend (optional)
351
+ - **Flash Attention / SageAttention**: Flash Attention 2 (Ampere+), Flash Attention 3 (Hopper+), SageAttention 2 or SageAttention 3 (Blackwell) provide faster attention computation on supported hardware (optional, falls back to PyTorch SDPA)
352
+
353
+ ## 📦 Installation
354
+
355
+ ### Option 1: ComfyUI Manager (Recommended)
356
+
357
+ 1. Open ComfyUI Manager in your ComfyUI interface
358
+ 2. Click "Custom Nodes Manager"
359
+ 3. Search for "ComfyUI-SeedVR2_VideoUpscaler"
360
+ 4. Click "Install" and restart ComfyUI
361
+
362
+ **Registry Link**: [ComfyUI Registry - SeedVR2 Video Upscaler](https://registry.comfy.org/nodes/seedvr2_videoupscaler)
363
+
364
+ ### Option 2: Manual Installation
365
+
366
+ 1. **Clone the repository** into your ComfyUI custom nodes directory:
367
+ ```bash
368
+ cd ComfyUI
369
+ git clone https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler.git custom_nodes/seedvr2_videoupscaler
370
+ ```
371
+
372
+ 2. **Install dependencies using standalone Python**:
373
+ ```bash
374
+ # Install requirements (from same ComfyUI directory)
375
+ # Windows:
376
+ .venv\Scripts\python.exe -m pip install -r custom_nodes\seedvr2_videoupscaler\requirements.txt
377
+ # Linux/macOS:
378
+ .venv/bin/python -m pip install -r custom_nodes/seedvr2_videoupscaler/requirements.txt
379
+ ```
380
+
381
+ 3. **Restart ComfyUI**
382
+
383
+ ### Model Installation
384
+
385
+ Models will be **automatically downloaded** on first use and saved to `ComfyUI/models/SEEDVR2`.
386
+
387
+ You can also manually download models from:
388
+ - Main models available at [numz/SeedVR2_comfyUI](https://huggingface.co/numz/SeedVR2_comfyUI/tree/main) and [AInVFX/SeedVR2_comfyUI](https://huggingface.co/AInVFX/SeedVR2_comfyUI/tree/main)
389
+ - Additional GGUF models available at [cmeka/SeedVR2-GGUF](https://huggingface.co/cmeka/SeedVR2-GGUF/tree/main)
390
+
391
+ ## 📖 Usage
392
+
393
+ ### 🎬 Video Tutorials
394
+
395
+ #### Latest Version Deep Dive (Recommended)
396
+
397
+ Complete walkthrough of version 2.5 by Adrien from [AInVFX](https://www.youtube.com/@AInVFX), covering the new 4-node architecture, GGUF support, memory optimizations, and production workflows:
398
+
399
+ [![SeedVR2 v2.5 Deep Dive Tutorial](https://img.youtube.com/vi/MBtWYXq_r60/maxresdefault.jpg)](https://youtu.be/MBtWYXq_r60)
400
+
401
+ This comprehensive tutorial covers:
402
+ - Installing v2.5 through ComfyUI Manager and troubleshooting conflicts
403
+ - Understanding the new 4-node modular architecture and why we rebuilt it
404
+ - Running 7B models on 8GB VRAM with GGUF quantization
405
+ - Configuring BlockSwap, VAE tiling, and torch.compile for your hardware
406
+ - Image and video upscaling workflows with alpha channel support
407
+ - CLI for batch processing and multi-GPU rendering
408
+ - Memory optimization strategies for different VRAM levels
409
+ - Real production tips and the critical batch_size formula (4n+1)
410
+
411
+ #### Previous Version Tutorial
412
+
413
+ For reference, here's the original tutorial covering the initial release:
414
+
415
+ [![SeedVR2 Deep Dive Tutorial](https://img.youtube.com/vi/I0sl45GMqNg/maxresdefault.jpg)](https://youtu.be/I0sl45GMqNg)
416
+
417
+ *Note: This tutorial covers the previous single-node architecture. While the UI has changed significantly in v2.5, the core concepts about BlockSwap and memory management remain valuable.*
418
+
419
+ ### Node Setup
420
+
421
+ SeedVR2 uses a modular node architecture with four specialized nodes:
422
+
423
+ #### 1. SeedVR2 (Down)Load DiT Model
424
+
425
+ ![SeedVR2 (Down)Load DiT Model](docs/dit_model_loader.png)
426
+
427
+ Configure the DiT (Diffusion Transformer) model for video upscaling.
428
+
429
+ **Parameters:**
430
+
431
+ - **model**: Choose your DiT model
432
+ - **3B Models**: Faster, lower VRAM requirements
433
+ - `seedvr2_ema_3b_fp16.safetensors`: FP16 (best quality)
434
+ - `seedvr2_ema_3b_fp8_e4m3fn.safetensors`: FP8 8-bit (good quality)
435
+ - `seedvr2_ema_3b-Q4_K_M.gguf`: GGUF 4-bit quantized (acceptable quality)
436
+ - `seedvr2_ema_3b-Q8_0.gguf`: GGUF 8-bit quantized (good quality)
437
+ - **7B Models**: Higher quality, higher VRAM requirements
438
+ - `seedvr2_ema_7b_fp16.safetensors`: FP16 (best quality)
439
+ - `seedvr2_ema_7b_fp8_e4m3fn_mixed_block35_fp16.safetensors`: FP8 with last block in FP16 to reduce artifacts (good quality)
440
+ - `seedvr2_ema_7b-Q4_K_M.gguf`: GGUF 4-bit quantized (acceptable quality)
441
+ - `seedvr2_ema_7b_sharp_*`: Sharp variants for enhanced detail
442
+
443
+ - **device**: GPU device for DiT inference (e.g., `cuda:0`)
444
+
445
+ - **offload_device**: Device to offload DiT model when not actively processing
446
+ - `none`: Keep model on inference device (fastest, highest VRAM)
447
+ - `cpu`: Offload to system RAM (reduces VRAM)
448
+ - `cuda:X`: Offload to another GPU (good balance if available)
449
+
450
+ - **cache_model**: Keep DiT model loaded on offload_device between workflow runs
451
+ - Useful for batch processing to avoid repeated loading
452
+ - Requires offload_device to be set
453
+
454
+ - **blocks_to_swap**: BlockSwap memory optimization
455
+ - `0`: Disabled (default)
456
+ - `1-32`: Number of transformer blocks to swap for 3B model
457
+ - `1-36`: Number of transformer blocks to swap for 7B model
458
+ - Higher values = more VRAM savings but slower processing
459
+ - Requires offload_device to be set and different from device
460
+
461
+ - **swap_io_components**: Offload input/output embeddings and normalization layers
462
+ - Additional VRAM savings when combined with blocks_to_swap
463
+ - Requires offload_device to be set and different from device
464
+
465
+ - **attention_mode**: Attention computation backend
466
+ - `sdpa`: PyTorch scaled_dot_product_attention (default, always available)
467
+ - `flash_attn_2`: Flash Attention 2 (Ampere+, requires flash-attn package)
468
+ - `flash_attn_3`: Flash Attention 3 (Hopper+, requires flash-attn with FA3 support)
469
+ - `sageattn_2`: SageAttention 2 (requires sageattention package)
470
+ - `sageattn_3`: SageAttention 3 (Blackwell/RTX 50xx, requires sageattn3 package)
471
+
472
+ - **torch_compile_args**: Connect to SeedVR2 Torch Compile Settings node for 20-40% speedup
473
+
474
+ **BlockSwap Explained:**
475
+
476
+ BlockSwap enables running large models on GPUs with limited VRAM by dynamically swapping transformer blocks between GPU and CPU memory during inference.
477
+
478
+ > **Note:** BlockSwap is not available on macOS. Apple Silicon Macs use unified memory architecture where GPU and CPU share the same memory pool, making BlockSwap meaningless. The option will be automatically disabled with a warning if requested on macOS.
479
+
480
+ Here's how it works:
481
+
482
+ - **What it does**: Keeps only the currently-needed transformer blocks on the GPU, while storing the rest on CPU or another device
483
+ - **When to use it**: When you get OOM (Out of Memory) errors during the upscaling phase
484
+ - **How to configure**:
485
+ 1. Set `offload_device` to `cpu` or another GPU
486
+ 2. Start with `blocks_to_swap=16` (half the blocks)
487
+ 3. If still getting OOM, increase to 24 or 32 (3B) / 36 (7B)
488
+ 4. Enable `swap_io_components` for maximum VRAM savings
489
+ 5. If you have plenty of VRAM, decrease or set to 0 for faster processing
490
+
491
+ **Example Configuration for Low VRAM (8GB)**:
492
+ - model: `seedvr2_ema_3b-Q8_0.gguf`
493
+ - device: `cuda:0`
494
+ - offload_device: `cpu`
495
+ - blocks_to_swap: `32`
496
+ - swap_io_components: `True`
497
+
498
+ #### 2. SeedVR2 (Down)Load VAE Model
499
+
500
+ ![SeedVR2 (Down)Load VAE Model](docs/vae_model_loader.png)
501
+
502
+ Configure the VAE (Variational Autoencoder) model for encoding/decoding video frames.
503
+
504
+ **Parameters:**
505
+
506
+ - **model**: VAE model selection
507
+ - `ema_vae_fp16.safetensors`: Default and recommended
508
+
509
+ - **device**: GPU device for VAE inference (e.g., `cuda:0`)
510
+
511
+ - **offload_device**: Device to offload VAE model when not actively processing
512
+ - `none`: Keep model on inference device (default, fastest)
513
+ - `cpu`: Offload to system RAM (reduces VRAM)
514
+ - `cuda:X`: Offload to another GPU (good balance if available)
515
+
516
+ - **cache_model**: Keep VAE model loaded on offload_device between workflow runs
517
+ - Requires offload_device to be set
518
+
519
+ - **encode_tiled**: Enable tiled encoding to reduce VRAM usage during encoding phase
520
+ - Enable if you see OOM errors during the "Encoding" phase in debug logs
521
+
522
+ - **encode_tile_size**: Encoding tile size in pixels (default: 1024)
523
+ - Applied to both height and width
524
+ - Lower values reduce VRAM but may increase processing time
525
+
526
+ - **encode_tile_overlap**: Encoding tile overlap in pixels (default: 128)
527
+ - Reduces visible seams between tiles
528
+
529
+ - **decode_tiled**: Enable tiled decoding to reduce VRAM usage during decoding phase
530
+ - Enable if you see OOM errors during the "Decoding" phase in debug logs
531
+
532
+ - **decode_tile_size**: Decoding tile size in pixels (default: 1024)
533
+
534
+ - **decode_tile_overlap**: Decoding tile overlap in pixels (default: 128)
535
+
536
+ - **torch_compile_args**: Connect to SeedVR2 Torch Compile Settings node for 15-25% speedup
537
+
538
+ **VAE Tiling Explained:**
539
+
540
+ VAE tiling processes large resolutions in smaller tiles to reduce VRAM requirements. Here's how to use it:
541
+
542
+ 1. **Run without tiling first** and monitor the debug logs (enable `enable_debug` on main node)
543
+ 2. **If OOM during "Encoding" phase**:
544
+ - Enable `encode_tiled`
545
+ - If still OOM, reduce `encode_tile_size` (try 768, 512, etc.)
546
+ 3. **If OOM during "Decoding" phase**:
547
+ - Enable `decode_tiled`
548
+ - If still OOM, reduce `decode_tile_size`
549
+ 4. **Adjust overlap** (default 128) if you see visible seams in output (increase it) or processing times are too slow (decrease it).
550
+
551
+ **Example Configuration for High Resolution (4K)**:
552
+ - encode_tiled: `True`
553
+ - encode_tile_size: `1024`
554
+ - encode_tile_overlap: `128`
555
+ - decode_tiled: `True`
556
+ - decode_tile_size: `1024`
557
+ - decode_tile_overlap: `128`
558
+
559
+ #### 3. SeedVR2 Torch Compile Settings (Optional)
560
+
561
+ ![SeedVR2 Torch Compile Settings](docs/torch_compile_settings.png)
562
+
563
+ Configure torch.compile optimization for 20-40% DiT speedup and 15-25% VAE speedup.
564
+
565
+ **Requirements:**
566
+ - PyTorch 2.0+
567
+ - Triton (for inductor backend)
568
+
569
+ **Parameters:**
570
+
571
+ - **backend**: Compilation backend
572
+ - `inductor`: Full optimization with Triton kernel generation and fusion (recommended)
573
+ - `cudagraphs`: Lightweight wrapper using CUDA graphs, no kernel optimization
574
+
575
+ - **mode**: Optimization level (compilation time vs runtime performance)
576
+ - `default`: Fast compilation with good speedup (recommended for development)
577
+ - `reduce-overhead`: Lower overhead, optimized for smaller models
578
+ - `max-autotune`: Slowest compilation, best runtime performance (recommended for production)
579
+ - `max-autotune-no-cudagraphs`: Like max-autotune but without CUDA graphs
580
+
581
+ - **fullgraph**: Compile entire model as single graph without breaks
582
+ - `False`: Allow graph breaks for better compatibility (default, recommended)
583
+ - `True`: Enforce no breaks for maximum optimization (may fail with dynamic shapes)
584
+
585
+ - **dynamic**: Handle varying input shapes without recompilation
586
+ - `False`: Specialize for exact input shapes (default)
587
+ - `True`: Create dynamic kernels that adapt to shape variations (enable when processing different resolutions or batch sizes)
588
+
589
+ - **dynamo_cache_size_limit**: Max cached compiled versions per function (default: 64)
590
+ - Higher = more memory, lower = more recompilation
591
+
592
+ - **dynamo_recompile_limit**: Max recompilation attempts before falling back to eager mode (default: 128)
593
+ - Safety limit to prevent compilation loops
594
+
595
+ **Usage:**
596
+ 1. Add this node to your workflow
597
+ 2. Connect its output to the `torch_compile_args` input of DiT and/or VAE loader nodes
598
+ 3. First run will be slow (compilation), subsequent runs will be much faster
599
+
600
+ **When to use:**
601
+ - torch.compile only makes sense when processing **multiple batches, long videos, or many tiles**
602
+ - For single images or short clips, the compilation time outweighs the speed improvement
603
+ - Best suited for batch processing workflows or long videos
604
+
605
+ **Recommended Settings:**
606
+ - For development/testing: `mode=default`, `backend=inductor`, `fullgraph=False`
607
+ - For production: `mode=max-autotune`, `backend=inductor`, `fullgraph=False`
608
+
609
+ #### 4. SeedVR2 Video Upscaler (Main Node)
610
+
611
+ ![SeedVR2 Video Upscaler](docs/video_upscaler.png)
612
+
613
+ Main upscaling node that processes video frames using DiT and VAE models.
614
+
615
+ **Required Inputs:**
616
+
617
+ - **image**: Input video frames as image batch (RGB or RGBA format)
618
+ - **dit**: DiT model configuration from SeedVR2 (Down)Load DiT Model node
619
+ - **vae**: VAE model configuration from SeedVR2 (Down)Load VAE Model node
620
+
621
+ **Parameters:**
622
+
623
+ - **seed**: Random seed for reproducible generation (default: 42)
624
+ - Same seed with same inputs produces identical output
625
+
626
+ - **resolution**: Target resolution for shortest edge in pixels (default: 1080)
627
+ - Maintains aspect ratio automatically
628
+
629
+ - **max_resolution**: Maximum resolution for any edge (default: 0 = no limit)
630
+ - Automatically scales down if exceeded to prevent OOM
631
+
632
+ - **batch_size**: Frames per batch (default: 5)
633
+ - **CRITICAL REQUIREMENT**: Must follow the **4n+1 formula** (1, 5, 9, 13, 17, 21, 25, ...)
634
+ - **Why this matters**: The model uses these frames for temporal consistency calculations
635
+ - **Minimum 5 for temporal consistency**: Use 1 only for single images or when temporal consistency isn't needed
636
+ - **Match shot length ideally**: For best results, set batch_size to match your shot length (e.g., batch_size=21 for a 20-frame shot)
637
+ - **VRAM impact**: Higher batch_size = better quality and speed but requires more VRAM
638
+ - **If you get OOM with batch_size=5**: Try optimization techniques first (model offloading, BlockSwap, GGUF models...) before reducing batch_size or input resolution, as these directly impact quality
639
+
640
+ **uniform_batch_size** (default: False)
641
+ - Pads the final batch to match `batch_size` for uniform processing
642
+ - Prevents temporal artifacts when the last batch is significantly smaller than others
643
+ - Example: 45 frames with `batch_size=33` creates [33, 33] instead of [33, 12]
644
+ - Recommended when using large batch sizes and video length is not a multiple of `batch_size`
645
+ - Increases VRAM usage slightly but ensures consistent temporal coherence across all batches
646
+
647
+ - **temporal_overlap**: Overlapping frames between batches (default: 0)
648
+ - Used for blending between batches to reduce temporal artifacts
649
+ - Range: 0-16 frames
650
+
651
+ - **prepend_frames**: Frames to prepend (default: 0)
652
+ - Prepends reversed frames to reduce artifacts at video start
653
+ - Automatically removed after processing
654
+ - Range: 0-32 frames
655
+
656
+ - **color_correction**: Color correction method (default: "wavelet")
657
+ - **`lab`**: Full perceptual color matching with detail preservation (recommended for highest fidelity to original)
658
+ - **`wavelet`**: Frequency-based natural colors, preserves details well
659
+ - **`wavelet_adaptive`**: Wavelet base + targeted saturation correction
660
+ - **`hsv`**: Hue-conditional saturation matching
661
+ - **`adain`**: Statistical style transfer
662
+ - **`none`**: No color correction
663
+
664
+ - **input_noise_scale**: Input noise injection scale 0.0-1.0 (default: 0.0)
665
+ - Adds noise to input frames to reduce artifacts at very high resolutions
666
+ - Try 0.1-0.3 if you see artifacts with high output resolutions
667
+
668
+ - **latent_noise_scale**: Latent space noise scale 0.0-1.0 (default: 0.0)
669
+ - Adds noise during diffusion process, can soften excessive detail
670
+ - Use if input_noise doesn't help, try 0.05-0.15
671
+
672
+ - **offload_device**: Device for storing intermediate tensors between processing phases (default: "cpu")
673
+ - `none`: Keep all tensors on inference device (fastest but highest VRAM)
674
+ - `cpu`: Offload to system RAM (recommended for long videos, slower transfers)
675
+ - `cuda:X`: Offload to another GPU (good balance if available, faster than CPU)
676
+
677
+ - **enable_debug**: Enable detailed debug logging (default: False)
678
+ - Shows memory usage, timing information, and processing details
679
+ - **Highly recommended** for troubleshooting OOM issues
680
+
681
+ **Output:**
682
+ - Upscaled video frames with color correction applied
683
+ - Format (RGB/RGBA) matches input
684
+ - Range [0, 1] normalized for ComfyUI compatibility
685
+
686
+ ### Typical Workflow Setup
687
+
688
+ **Basic Workflow (High VRAM - 24GB+)**:
689
+ ```
690
+ Load Video Frames
691
+
692
+ SeedVR2 Load DiT Model
693
+ ├─ model: seedvr2_ema_3b_fp16.safetensors
694
+ └─ device: cuda:0
695
+
696
+ SeedVR2 Load VAE Model
697
+ ├─ model: ema_vae_fp16.safetensors
698
+ └─ device: cuda:0
699
+
700
+ SeedVR2 Video Upscaler
701
+ ├─ batch_size: 21
702
+ └─ resolution: 1080
703
+
704
+ Save Video/Frames
705
+ ```
706
+
707
+ **Low VRAM Workflow (8-12GB)**:
708
+ ```
709
+ Load Video Frames
710
+
711
+ SeedVR2 Load DiT Model
712
+ ├─ model: seedvr2_ema_3b-Q8_0.gguf
713
+ ├─ device: cuda:0
714
+ ├─ offload_device: cpu
715
+ ├─ blocks_to_swap: 32
716
+ └─ swap_io_components: True
717
+
718
+ SeedVR2 Load VAE Model
719
+ ├─ model: ema_vae_fp16.safetensors
720
+ ├─ device: cuda:0
721
+ ├─ encode_tiled: True
722
+ └─ decode_tiled: True
723
+
724
+ SeedVR2 Video Upscaler
725
+ ├─ batch_size: 5
726
+ └─ resolution: 720
727
+
728
+ Save Video/Frames
729
+ ```
730
+
731
+ **High Performance Workflow (24GB+ with torch.compile)**:
732
+ ```
733
+ Load Video Frames
734
+
735
+ SeedVR2 Torch Compile Settings
736
+ ├─ mode: max-autotune
737
+ └─ backend: inductor
738
+
739
+ SeedVR2 Load DiT Model
740
+ ├─ model: seedvr2_ema_7b_sharp_fp16.safetensors
741
+ ├─ device: cuda:0
742
+ └─ torch_compile_args: connected
743
+
744
+ SeedVR2 Load VAE Model
745
+ ├─ model: ema_vae_fp16.safetensors
746
+ ├─ device: cuda:0
747
+ └─ torch_compile_args: connected
748
+
749
+ SeedVR2 Video Upscaler
750
+ ├─ batch_size: 81
751
+ └─ resolution: 1080
752
+
753
+ Save Video/Frames
754
+ ```
755
+
756
+ ## 🖥️ Run as Standalone (CLI)
757
+
758
+ The standalone CLI provides powerful batch processing capabilities with multi-GPU support and sophisticated optimization options.
759
+
760
+ ### Prerequisites
761
+
762
+ Choose the appropriate setup based on your installation:
763
+
764
+ #### Option 1: Already Have ComfyUI with SeedVR2 Installed
765
+
766
+ If you've already installed SeedVR2 as part of ComfyUI (via [ComfyUI installation](#-installation)), you can use the CLI directly:
767
+
768
+ ```bash
769
+ # Navigate to your ComfyUI directory
770
+ cd ComfyUI
771
+
772
+ # Run the CLI using standalone Python (display help message)
773
+ # Windows:
774
+ .venv\Scripts\python.exe custom_nodes\seedvr2_videoupscaler\inference_cli.py --help
775
+ # Linux/macOS:
776
+ .venv/bin/python custom_nodes/seedvr2_videoupscaler/inference_cli.py --help
777
+ ```
778
+
779
+ **Skip to [Command Line Usage](#command-line-usage) below.**
780
+
781
+ #### Option 2: Standalone Installation (Without ComfyUI)
782
+
783
+ If you want to use the CLI without ComfyUI installation, follow these steps:
784
+
785
+ 1. **Install [uv](https://docs.astral.sh/uv/getting-started/installation/)** (modern Python package manager):
786
+ ```bash
787
+ # Windows
788
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
789
+
790
+ # macOS and Linux
791
+ curl -LsSf https://astral.sh/uv/install.sh | sh
792
+ ```
793
+
794
+ 2. **Clone the repository**:
795
+ ```bash
796
+ git clone https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler.git seedvr2_videoupscaler
797
+ cd seedvr2_videoupscaler
798
+ ```
799
+
800
+ 3. **Create virtual environment and install dependencies**:
801
+ ```bash
802
+ # Create virtual environment with Python 3.13
803
+ uv venv --python 3.13
804
+
805
+ # Activate virtual environment
806
+ # Windows:
807
+ .venv\Scripts\activate
808
+ # Linux/macOS:
809
+ source .venv/bin/activate
810
+
811
+ # Install PyTorch with CUDA support
812
+ # Check command line based on your environment: https://pytorch.org/get-started/locally/
813
+ uv pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/cu130
814
+
815
+ # Install SeedVR2 requirements
816
+ uv pip install -r requirements.txt
817
+
818
+ # Run the CLI (display help message)
819
+ # Windows:
820
+ .venv\Scripts\python.exe inference_cli.py --help
821
+ # Linux/macOS:
822
+ .venv/bin/python inference_cli.py --help
823
+ ```
824
+
825
+ ### Command Line Usage
826
+
827
+ The CLI provides comprehensive options for single-GPU, multi-GPU, and batch processing workflows.
828
+
829
+ **Basic Usage Examples:**
830
+
831
+ ```bash
832
+ # Basic image upscaling
833
+ python inference_cli.py image.jpg
834
+
835
+ # Basic video upscaling with temporal consistency
836
+ python inference_cli.py video.mp4 --resolution 720 --batch_size 33
837
+
838
+ # Streaming mode for long videos (memory-efficient) with 10-bit video output (requires FFMPEG)
839
+ # Processes video in chunks of 330 frames to avoid loading entire video into RAM
840
+ # Use --temporal_overlap to ensure smooth transitions between chunks
841
+ python inference_cli.py long_video.mp4 \
842
+ --resolution 1080 \
843
+ --batch_size 33 \
844
+ --chunk_size 330 \
845
+ --temporal_overlap 3 \
846
+ --video_backend ffmpeg \
847
+ --10bit
848
+
849
+ # Multi-GPU processing with temporal overlap
850
+ python inference_cli.py video.mp4 \
851
+ --cuda_device 0,1 \
852
+ --resolution 1080 \
853
+ --batch_size 81 \
854
+ --uniform_batch_size \
855
+ --temporal_overlap 3 \
856
+ --prepend_frames 4
857
+
858
+ # Memory-optimized for low VRAM (8GB)
859
+ python inference_cli.py image.png \
860
+ --dit_model seedvr2_ema_3b-Q8_0.gguf \
861
+ --resolution 1080 \
862
+ --blocks_to_swap 32 \
863
+ --swap_io_components \
864
+ --dit_offload_device cpu \
865
+ --vae_offload_device cpu
866
+
867
+ # High resolution with VAE tiling
868
+ python inference_cli.py video.mp4 \
869
+ --resolution 1440 \
870
+ --batch_size 31 \
871
+ --uniform_batch_size \
872
+ --temporal_overlap 3 \
873
+ --vae_encode_tiled \
874
+ --vae_decode_tiled
875
+
876
+ # Batch directory processing with model caching
877
+ python inference_cli.py media_folder/ \
878
+ --output processed/ \
879
+ --cuda_device 0 \
880
+ --cache_dit \
881
+ --cache_vae \
882
+ --dit_offload_device cpu \
883
+ --vae_offload_device cpu \
884
+ --resolution 1080 \
885
+ --max_resolution 1920
886
+ ```
887
+
888
+ ### Command Line Arguments
889
+
890
+ **Input/Output:**
891
+ - `<input>`: Input file (.mp4, .avi, .png, .jpg, etc.) or directory
892
+ - `--output`: Output path (default: auto-generated in 'output/' directory)
893
+ - `--output_format`: Output format: 'mp4' (video) or 'png' (image sequence). Default: auto-detect from input type
894
+ - `--video_backend`: Video encoder backend: 'opencv' (default) or 'ffmpeg' (requires ffmpeg in PATH)
895
+ - `--10bit`: Save 10-bit video with x265 codec and yuv420p10le pixel format (reduces banding in gradients). Without this flag, ffmpeg uses x264 (yuv420p) for maximum compatibility. Requires --video_backend ffmpeg
896
+ - `--model_dir`: Model directory (default: ./models/SEEDVR2)
897
+
898
+ **Model Selection:**
899
+ - `--dit_model`: DiT model to use. Options: 3B/7B with fp16/fp8/GGUF variants (default: 3B FP8)
900
+
901
+ **Processing Parameters:**
902
+ - `--resolution`: Target short-side resolution in pixels (default: 1080)
903
+ - `--max_resolution`: Maximum resolution for any edge. Scales down if exceeded. 0 = no limit (default: 0)
904
+ - `--batch_size`: Frames per batch (must follow 4n+1: 1, 5, 9, 13, 17, 21...). Ideally matches shot length for best temporal consistency (default: 5)
905
+ - `--seed`: Random seed for reproducibility (default: 42)
906
+ - `--skip_first_frames`: Skip N initial frames (default: 0)
907
+ - `--load_cap`: Maximum total frames to load from video. 0 = load all (default: 0)
908
+ - `--chunk_size`: Frames per chunk for streaming mode. When > 0, processes video in memory-bounded chunks of N frames, writing each chunk before loading the next. Essential for long videos that would otherwise exceed RAM. Use with `--temporal_overlap` for seamless chunk transitions. 0 = load all frames at once (default: 0)
909
+ - `--prepend_frames`: Prepend N reversed frames to reduce start artifacts (auto-removed) (default: 0)
910
+ - `--temporal_overlap`: Frames to overlap between batches/GPUs for smooth blending (default: 0)
911
+
912
+ **Quality Control:**
913
+ - `--color_correction`: Color correction method: 'lab' (perceptual, recommended), 'wavelet', 'wavelet_adaptive', 'hsv', 'adain', or 'none' (default: lab)
914
+ - `--input_noise_scale`: Input noise injection scale (0.0-1.0). Reduces artifacts at high resolutions (default: 0.0)
915
+ - `--latent_noise_scale`: Latent space noise scale (0.0-1.0). Softens details if needed (default: 0.0)
916
+
917
+ **Memory Management:**
918
+ - `--dit_offload_device`: Device to offload DiT model: 'none' (keep on GPU), 'cpu', or 'cuda:X' (default: none)
919
+ - `--vae_offload_device`: Device to offload VAE model: 'none', 'cpu', or 'cuda:X' (default: none)
920
+ - `--blocks_to_swap`: Number of transformer blocks to swap (0=disabled, 3B: 0-32, 7B: 0-36). Requires dit_offload_device (default: 0). Not available on macOS.
921
+ - `--swap_io_components`: Offload I/O components for additional VRAM savings. Requires dit_offload_device. Not available on macOS.
922
+
923
+ **VAE Tiling:**
924
+ - `--vae_encode_tiled`: Enable VAE encode tiling to reduce VRAM during encoding
925
+ - `--vae_encode_tile_size`: VAE encode tile size in pixels (default: 1024)
926
+ - `--vae_encode_tile_overlap`: VAE encode tile overlap in pixels (default: 128)
927
+ - `--vae_decode_tiled`: Enable VAE decode tiling to reduce VRAM during decoding
928
+ - `--vae_decode_tile_size`: VAE decode tile size in pixels (default: 1024)
929
+ - `--vae_decode_tile_overlap`: VAE decode tile overlap in pixels (default: 128)
930
+ - `--tile_debug`: Visualize tiles: 'false' (default), 'encode', or 'decode'
931
+
932
+ **Performance Optimization:**
933
+ - `--allow_vram_overflow`: Allow VRAM overflow to system RAM. Prevents OOM but may cause severe slowdown
934
+ - `--attention_mode`: Attention backend: 'sdpa' (default), 'flash_attn_2' (Ampere+), 'flash_attn_3' (Hopper+), 'sageattn_2', or 'sageattn_3' (Blackwell)
935
+ - `--compile_dit`: Enable torch.compile for DiT model (20-40% speedup, requires PyTorch 2.0+ and Triton)
936
+ - `--compile_vae`: Enable torch.compile for VAE model (15-25% speedup, requires PyTorch 2.0+ and Triton)
937
+ - `--compile_backend`: Compilation backend: 'inductor' (full optimization) or 'cudagraphs' (lightweight) (default: inductor)
938
+ - `--compile_mode`: Optimization level: 'default', 'reduce-overhead', 'max-autotune', 'max-autotune-no-cudagraphs' (default: default)
939
+ - `--compile_fullgraph`: Compile entire model as single graph (faster but less flexible) (default: False)
940
+ - `--compile_dynamic`: Handle varying input shapes without recompilation (default: False)
941
+ - `--compile_dynamo_cache_size_limit`: Max cached compiled versions per function (default: 64)
942
+ - `--compile_dynamo_recompile_limit`: Max recompilation attempts before fallback (default: 128)
943
+
944
+ **Model Caching (batch processing):**
945
+ - `--cache_dit`: Keep DiT model in memory between generations. Works with single-GPU directory processing or multi-GPU streaming (`--chunk_size`). Requires `--dit_offload_device`
946
+ - `--cache_vae`: Keep VAE model in memory between generations. Works with single-GPU directory processing or multi-GPU streaming (`--chunk_size`). Requires `--vae_offload_device`
947
+
948
+ **Multi-GPU:**
949
+ - `--cuda_device`: CUDA device id(s). Single id (e.g., '0') or comma-separated list '0,1' for multi-GPU
950
+
951
+ **Debugging:**
952
+ - `--debug`: Enable verbose debug logging
953
+
954
+ ### Multi-GPU Processing Explained
955
+
956
+ The CLI's multi-GPU mode uses **frame-level parallelism**: the video is split into chunks and each GPU processes its chunk independently through all 4 phases (encode → upscale → decode → postprocess). This is ideal for long videos where you want to reduce total processing time by dividing the workload.
957
+
958
+ **How it works:**
959
+ 1. Video frames are split evenly across GPUs (e.g., 100 frames on 2 GPUs → 50 frames each)
960
+ 2. Each GPU loads its own copy of the models and processes its chunk independently
961
+ 3. When `--temporal_overlap` is set, chunks include overlapping frames for seamless blending
962
+ 4. Results are concatenated (and blended at overlap regions) into the final video
963
+
964
+ **Example for 100 frames on 2 GPUs with temporal_overlap=4:**
965
+ ```
966
+ GPU 0: Frames 0-53 (50 base + 4 overlap at end, processed as independent video)
967
+ GPU 1: Frames 50-99 (50 frames, 4 overlap at start, processed as independent video)
968
+ Result: Frames 0-99 with smooth blending at the transition point
969
+ ```
970
+
971
+ **Important considerations:**
972
+ - Each GPU processes its chunk as a separate video with its own batch splitting
973
+ - `batch_size` controls batching *within* each GPU's chunk, not across GPUs
974
+ - For short videos (< 100 frames), single GPU is often more efficient due to model loading overhead
975
+ - Multi-GPU doubles VRAM usage (each GPU loads full models) but roughly halves processing time
976
+
977
+ **When to use multi-GPU:**
978
+ - Long videos (100+ frames) where splitting provides significant time savings
979
+ - When you have multiple GPUs with sufficient VRAM each
980
+
981
+ **When to use single GPU:**
982
+ - Short videos where model loading overhead outweighs parallel gains
983
+ - When you want all frames processed together for maximum temporal coherence
984
+
985
+ **Best practices:**
986
+ - Set `--temporal_overlap` to 2-4 frames for smooth blending between GPU chunks
987
+ - Higher overlap = smoother transitions but more redundant processing
988
+ - Use `--prepend_frames` to reduce artifacts at video start
989
+ - For optimal quality on short videos, use single GPU with `batch_size` matching your shot length
990
+
991
+ ## ⚠️ Limitations
992
+
993
+ ### Model Limitations
994
+
995
+ **Batch Size Constraint**: The model requires batch_size to follow the **4n+1 formula** (1, 5, 9, 13, 17, 21, 25, ...) due to temporal consistency architecture. All frames in a batch are processed together for temporal coherence, then batches can be blended using temporal_overlap. Ideally, set batch_size to match your shot length for optimal quality.
996
+
997
+ ### Performance Considerations
998
+
999
+ **VAE Bottleneck**: Even with optimized DiT upscaling (BlockSwap, GGUF, torch.compile), the VAE encoding/decoding stages can be the bottleneck, especially for high resolutions. The VAE is slow. Use large batch_size to mitigate this.
1000
+
1001
+ **VRAM Usage**: While the integration now supports low VRAM systems (8GB or less with proper optimization), VRAM usage varies based on:
1002
+ - Input/output resolution (larger = more VRAM)
1003
+ - Batch size (higher = more VRAM but better temporal consistency and speed)
1004
+ - Model choice (FP16 > FP8 > GGUF in VRAM usage)
1005
+ - Optimization settings (BlockSwap, VAE tiling significantly reduce VRAM)
1006
+
1007
+ **Speed**: Processing speed depends on:
1008
+ - GPU capabilities (compute performance, VRAM bandwidth, and architecture generation)
1009
+ - Model size (3B faster than 7B)
1010
+ - Batch size (larger batch sizes are faster per frame due to better GPU utilization)
1011
+ - Optimization settings (torch.compile provides significant speedup)
1012
+ - Resolution (higher resolutions are slower)
1013
+
1014
+ ### Best Practices
1015
+
1016
+ 1. **Start with debug enabled** to understand where VRAM is being used
1017
+ 2. **For OOM errors during encoding**: Enable VAE encode tiling and reduce tile size
1018
+ 3. **For OOM errors during upscaling**: Enable BlockSwap and increase blocks_to_swap
1019
+ 4. **For OOM errors during decoding**: Enable VAE decode tiling and reduce tile size
1020
+ - **If still getting OOM after trying all above**: Reduce batch_size or resolution
1021
+ 5. **For best quality**: Use higher batch_size matching your shot length, FP16 models, and LAB color correction
1022
+ 6. **For speed**: Use FP8/GGUF models, enable torch.compile, and use Flash Attention if available
1023
+ 7. **Test settings with a short clip first** before processing long videos
1024
+
1025
+ ## 🤝 Contributing
1026
+
1027
+ Contributions are welcome! We value community input and improvements.
1028
+
1029
+ For detailed contribution guidelines, see [CONTRIBUTING.md](CONTRIBUTING.md).
1030
+
1031
+ **Quick Start:**
1032
+
1033
+ 1. Fork the repository
1034
+ 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
1035
+ 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
1036
+ 4. Push to the branch (`git push origin feature/AmazingFeature`)
1037
+ 5. Open a Pull Request to the **main** branch
1038
+
1039
+ **Get Help:**
1040
+ - YouTube: [AInVFX Channel](https://www.youtube.com/@AInVFX)
1041
+ - GitHub [Issues](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/issues): For bug reports and feature requests
1042
+ - GitHub [Discussions](https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/discussions): For questions and community support
1043
+ - Discord: adrientoupet & NumZ#7184
1044
+
1045
+ ## 🙏 Credits
1046
+
1047
+ This ComfyUI implementation is a collaborative project by **[NumZ](https://github.com/numz)** and **[AInVFX](https://www.youtube.com/@AInVFX)** (Adrien Toupet), based on the original [SeedVR2](https://github.com/ByteDance-Seed/SeedVR) by ByteDance Seed Team.
1048
+
1049
+ Special thanks to our community contributors including [naxci1](https://github.com/naxci1), [thehhmdb](https://github.com/thehhmdb), [s-cerevisiae](https://github.com/s-cerevisiae), [benjaminherb](https://github.com/benjaminherb), [cmeka](https://github.com/cmeka), [FurkanGozukara](https://github.com/FurkanGozukara), [JohnAlcatraz](https://github.com/JohnAlcatraz), [lihaoyun6](https://github.com/lihaoyun6), [Luchuanzhao](https://github.com/Luchuanzhao), [Luke2642](https://github.com/Luke2642), [proxyid](https://github.com/proxyid), [q5sys](https://github.com/q5sys), and many others for their improvements, bug fixes, and testing.
1050
+
1051
+ ## 📜 License
1052
+
1053
+ The code in this repository is released under the Apache 2.0 license as found in the [LICENSE](LICENSE) file.
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ComfyUI-SeedVR2_VideoUpscaler
3
+ Official SeedVR2 integration for ComfyUI
4
+ """
5
+
6
+ from .src.optimization.compatibility import ensure_triton_compat # noqa: F401
7
+ from .src.interfaces import comfy_entrypoint, SeedVR2Extension
8
+
9
+ __all__ = ["comfy_entrypoint", "SeedVR2Extension"]
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/configs_3b/main.yaml ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __object__:
2
+ path: projects.video_diffusion_sr.train
3
+ name: VideoDiffusionTrainer
4
+
5
+ dit:
6
+ model:
7
+ __object__:
8
+ path: "dit_3b.nadit"
9
+ name: "NaDiT"
10
+ args: "as_params"
11
+ vid_in_channels: 33
12
+ vid_out_channels: 16
13
+ vid_dim: 2560
14
+ vid_out_norm: fusedrms
15
+ txt_in_dim: 5120
16
+ txt_in_norm: fusedln
17
+ txt_dim: ${.vid_dim}
18
+ emb_dim: ${eval:'6 * ${.vid_dim}'}
19
+ heads: 20
20
+ head_dim: 128 # llm-like
21
+ expand_ratio: 4
22
+ norm: fusedrms
23
+ norm_eps: 1.0e-05
24
+ ada: single
25
+ qk_bias: False
26
+ qk_norm: fusedrms
27
+ patch_size: [1, 2, 2]
28
+ num_layers: 32 # llm-like
29
+ mm_layers: 10
30
+ mlp_type: swiglu
31
+ msa_type: None
32
+ block_type: ${eval:'${.num_layers} * ["mmdit_sr"]'} # space-full
33
+ window: ${eval:'${.num_layers} * [(4,3,3)]'} # space-full
34
+ window_method: ${eval:'${.num_layers} // 2 * ["720pwin_by_size_bysize","720pswin_by_size_bysize"]'} # space-full
35
+ rope_type: mmrope3d
36
+ rope_dim: 128
37
+ compile: False
38
+ gradient_checkpoint: True
39
+ fsdp:
40
+ sharding_strategy: _HYBRID_SHARD_ZERO2
41
+
42
+ ema:
43
+ decay: 0.9998
44
+
45
+ vae:
46
+ model:
47
+ __object__:
48
+ path: "video_vae_v3.modules.attn_video_vae"
49
+ name: "VideoAutoencoderKLWrapper"
50
+ args: "as_params"
51
+ freeze_encoder: False
52
+ gradient_checkpoint: True # Disabled to prevent VRAM leaks in inference
53
+ slicing:
54
+ split_size: 4
55
+ memory_device: same
56
+ memory_limit:
57
+ conv_max_mem: 0.5
58
+ norm_max_mem: 0.5
59
+ checkpoint: ema_vae_fp16.safetensors
60
+ scaling_factor: 0.9152
61
+ compile: False
62
+ grouping: False
63
+ dtype: float16
64
+
65
+ diffusion:
66
+ schedule:
67
+ type: lerp
68
+ T: 1000.0
69
+ sampler:
70
+ type: euler
71
+ prediction_type: v_lerp
72
+ timesteps:
73
+ training:
74
+ type: logitnormal
75
+ loc: 0.0
76
+ scale: 1.0
77
+ sampling:
78
+ type: uniform_trailing
79
+ steps: 50
80
+ transform: True
81
+ loss:
82
+ type: v_lerp
83
+ cfg:
84
+ scale: 7.5
85
+ rescale: 0
86
+
87
+ condition:
88
+ i2v: 0.0
89
+ v2v: 0.0
90
+ sr: 1.0
91
+ noise_scale: 0.25
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/configs_7b/main.yaml ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __object__:
2
+ path: projects.video_diffusion_sr.train
3
+ name: VideoDiffusionTrainer
4
+
5
+ dit:
6
+ model:
7
+ __object__:
8
+ path: "dit_7b.nadit"
9
+ name: "NaDiT"
10
+ args: "as_params"
11
+ vid_in_channels: 33
12
+ vid_out_channels: 16
13
+ vid_dim: 3072
14
+ txt_in_dim: 5120
15
+ txt_dim: ${.vid_dim}
16
+ emb_dim: ${eval:'6 * ${.vid_dim}'}
17
+ heads: 24
18
+ head_dim: 128 # llm-like
19
+ expand_ratio: 4
20
+ norm: fusedrms
21
+ norm_eps: 1e-5
22
+ ada: single
23
+ qk_bias: False
24
+ qk_rope: True
25
+ qk_norm: fusedrms
26
+ patch_size: [1, 2, 2]
27
+ num_layers: 36 # llm-like
28
+ shared_mlp: False
29
+ shared_qkv: False
30
+ mlp_type: normal
31
+ block_type: ${eval:'${.num_layers} * ["mmdit_sr"]'} # space-full
32
+ window: ${eval:'${.num_layers} * [(4,3,3)]'} # space-full
33
+ window_method: ${eval:'${.num_layers} // 2 * ["720pwin_by_size_bysize","720pswin_by_size_bysize"]'} # space-full
34
+ compile: False
35
+ gradient_checkpoint: True
36
+ fsdp:
37
+ sharding_strategy: _HYBRID_SHARD_ZERO2
38
+
39
+ ema:
40
+ decay: 0.9998
41
+
42
+ vae:
43
+ model:
44
+ __object__:
45
+ path: "video_vae_v3.modules.attn_video_vae"
46
+ name: "VideoAutoencoderKLWrapper"
47
+ args: "as_params"
48
+ freeze_encoder: False
49
+ # gradient_checkpoint: True
50
+ slicing:
51
+ split_size: 4
52
+ memory_device: same
53
+ memory_limit:
54
+ conv_max_mem: 0.5
55
+ norm_max_mem: 0.5
56
+ checkpoint: ema_vae_fp16.safetensors
57
+ scaling_factor: 0.9152
58
+ compile: False
59
+ grouping: False
60
+ dtype: float16
61
+
62
+ diffusion:
63
+ schedule:
64
+ type: lerp
65
+ T: 1000.0
66
+ sampler:
67
+ type: euler
68
+ prediction_type: v_lerp
69
+ timesteps:
70
+ training:
71
+ type: logitnormal
72
+ loc: 0.0
73
+ scale: 1.0
74
+ sampling:
75
+ type: uniform_trailing
76
+ steps: 50
77
+ transform: True
78
+ loss:
79
+ type: v_lerp
80
+ cfg:
81
+ scale: 7.5
82
+ rescale: 0
83
+
84
+ condition:
85
+ i2v: 0.0
86
+ v2v: 0.0
87
+ sr: 1.0
88
+ noise_scale: 0.25
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_01.jpg ADDED

Git LFS Details

  • SHA256: 356496e9421e5af83c40916cc45a44122f935b770dd5b7c62f8ca6c2f3065b5a
  • Pointer size: 131 Bytes
  • Size of remote file: 453 kB
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/demo_02.jpg ADDED

Git LFS Details

  • SHA256: 50bcd1fac36961f979c01fea565dc6c55babcc16c0ef5b8bb1b4c8dfbd75edaf
  • Pointer size: 131 Bytes
  • Size of remote file: 152 kB
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/dit_model_loader.png ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/seedvr_logo.png ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/torch_compile_settings.png ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_01.png ADDED

Git LFS Details

  • SHA256: 927198f9c5119df8d4b126a8ec4cd367a8b0ab2f471f755636843ecac5ebcfff
  • Pointer size: 132 Bytes
  • Size of remote file: 1.37 MB
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/usage_02.png ADDED

Git LFS Details

  • SHA256: 73266574b70c31fc06cc990e6229471ddba373ea5e808f92a1214c422bfacc37
  • Pointer size: 131 Bytes
  • Size of remote file: 686 kB
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/vae_model_loader.png ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/docs/video_upscaler.png ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_4K_image_upscale.jpg ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_4K_image_upscale.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"id":"e2c8e6ec-9e8a-4a81-a240-7e7d2befc2bf","revision":0,"last_node_id":20,"last_link_id":19,"nodes":[{"id":19,"type":"SeedVR2TorchCompileSettings","pos":[-366.25065217175177,401.8303561599628],"size":[307.671484375,178],"flags":{},"order":0,"mode":4,"inputs":[{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"mode","name":"mode","type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"fullgraph","name":"fullgraph","type":"BOOLEAN","widget":{"name":"fullgraph"},"link":null},{"localized_name":"dynamic","name":"dynamic","type":"BOOLEAN","widget":{"name":"dynamic"},"link":null},{"localized_name":"dynamo_cache_size_limit","name":"dynamo_cache_size_limit","type":"INT","widget":{"name":"dynamo_cache_size_limit"},"link":null},{"localized_name":"dynamo_recompile_limit","name":"dynamo_recompile_limit","type":"INT","widget":{"name":"dynamo_recompile_limit"},"link":null}],"outputs":[{"localized_name":"TORCH_COMPILE_ARGS","name":"TORCH_COMPILE_ARGS","type":"TORCH_COMPILE_ARGS","links":[18,19]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2TorchCompileSettings"},"widgets_values":["inductor","default",false,false,64,128]},{"id":18,"type":"Note","pos":[420.71483854947945,-81.0108307454865],"size":[210,94.05179298404067],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable to upscale alpha/mask channel along with RGB channel (for RGBA inputs)"],"color":"#432","bgcolor":"#653"},{"id":16,"type":"LoadImage","pos":[80.5773583147188,-182.2697173004957],"size":[274.080078125,314],"flags":{},"order":3,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[14]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":[17]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"LoadImage"},"widgets_values":["Sadhu_320x478.png","image"]},{"id":14,"type":"SeedVR2LoadDiTModel","pos":[64.4234520647185,212.7072358245033],"size":[307.6646484375,202],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":18},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"blocks_to_swap","name":"blocks_to_swap","shape":7,"type":"INT","widget":{"name":"blocks_to_swap"},"link":null},{"localized_name":"swap_io_components","name":"swap_io_components","shape":7,"type":"BOOLEAN","widget":{"name":"swap_io_components"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null},{"localized_name":"attention_mode","name":"attention_mode","shape":7,"type":"COMBO","widget":{"name":"attention_mode"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_DIT","name":"SEEDVR2_DIT","type":"SEEDVR2_DIT","links":[11]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadDiTModel"},"widgets_values":["seedvr2_ema_7b_sharp_fp16.safetensors","cuda:0",36,false,"cpu",false,"sdpa"]},{"id":13,"type":"SeedVR2LoadVAEModel","pos":[60.33985831471847,499.8798920745057],"size":[312.866796875,298],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":19},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"encode_tiled","name":"encode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"encode_tiled"},"link":null},{"localized_name":"encode_tile_size","name":"encode_tile_size","shape":7,"type":"INT","widget":{"name":"encode_tile_size"},"link":null},{"localized_name":"encode_tile_overlap","name":"encode_tile_overlap","shape":7,"type":"INT","widget":{"name":"encode_tile_overlap"},"link":null},{"localized_name":"decode_tiled","name":"decode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"decode_tiled"},"link":null},{"localized_name":"decode_tile_size","name":"decode_tile_size","shape":7,"type":"INT","widget":{"name":"decode_tile_size"},"link":null},{"localized_name":"decode_tile_overlap","name":"decode_tile_overlap","shape":7,"type":"INT","widget":{"name":"decode_tile_overlap"},"link":null},{"localized_name":"tile_debug","name":"tile_debug","shape":7,"type":"COMBO","widget":{"name":"tile_debug"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_VAE","name":"SEEDVR2_VAE","type":"SEEDVR2_VAE","links":[10]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadVAEModel"},"widgets_values":["ema_vae_fp16.safetensors","cuda:0",true,1024,128,true,1024,128,"false","cpu",false]},{"id":10,"type":"SeedVR2VideoUpscaler","pos":[843.8992333147189,191.3224701995034],"size":[270,386],"flags":{},"order":7,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":16},{"localized_name":"dit","name":"dit","type":"SEEDVR2_DIT","link":11},{"localized_name":"vae","name":"vae","type":"SEEDVR2_VAE","link":10},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"resolution","name":"resolution","type":"INT","widget":{"name":"resolution"},"link":null},{"localized_name":"max_resolution","name":"max_resolution","type":"INT","widget":{"name":"max_resolution"},"link":null},{"localized_name":"batch_size","name":"batch_size","type":"INT","widget":{"name":"batch_size"},"link":null},{"localized_name":"uniform_batch_size","name":"uniform_batch_size","type":"BOOLEAN","widget":{"name":"uniform_batch_size"},"link":null},{"localized_name":"color_correction","name":"color_correction","type":"COMBO","widget":{"name":"color_correction"},"link":null},{"localized_name":"temporal_overlap","name":"temporal_overlap","shape":7,"type":"INT","widget":{"name":"temporal_overlap"},"link":null},{"localized_name":"prepend_frames","name":"prepend_frames","shape":7,"type":"INT","widget":{"name":"prepend_frames"},"link":null},{"localized_name":"input_noise_scale","name":"input_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"input_noise_scale"},"link":null},{"localized_name":"latent_noise_scale","name":"latent_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"latent_noise_scale"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"enable_debug","name":"enable_debug","shape":7,"type":"BOOLEAN","widget":{"name":"enable_debug"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[12]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2VideoUpscaler"},"widgets_values":[42,"fixed",4096,4096,1,false,"lab",0,0,0,0,"cpu",false]},{"id":17,"type":"JoinImageWithAlpha","pos":[449.4979840435381,-180.32741340177097],"size":[176.86484375,46],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":14},{"localized_name":"alpha","name":"alpha","type":"MASK","link":17}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[16]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"JoinImageWithAlpha"},"widgets_values":[]},{"id":15,"type":"SaveImage","pos":[1167.7234520647182,192.0920014495034],"size":[270,270],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":12},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.68"},"widgets_values":["ComfyUI"]},{"id":20,"type":"Note","pos":[-353.44461861972593,641.0883773827939],"size":[290.1922351462714,97.95592567265356],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable torch.compile optimization. First batch has compilation overhead, but speeds up subsequent batches. Best for long videos, not single images."],"color":"#432","bgcolor":"#653"}],"links":[[10,13,0,10,2,"SEEDVR2_VAE"],[11,14,0,10,1,"SEEDVR2_DIT"],[12,10,0,15,0,"IMAGE"],[14,16,0,17,0,"IMAGE"],[16,17,0,10,0,"IMAGE"],[17,16,1,17,1,"MASK"],[18,19,0,14,0,"TORCH_COMPILE_ARGS"],[19,19,0,13,0,"TORCH_COMPILE_ARGS"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.6934334949441353,"offset":[1011.3669791186508,462.16171611318566]}},"version":0.4}
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_HD_video_upscale.jpg ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_HD_video_upscale.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"id":"e2c8e6ec-9e8a-4a81-a240-7e7d2befc2bf","revision":0,"last_node_id":24,"last_link_id":25,"nodes":[{"id":17,"type":"JoinImageWithAlpha","pos":[415.46463925106394,-269.3527671179127],"size":[176.86484375,46],"flags":{},"order":7,"mode":4,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":21},{"localized_name":"alpha","name":"alpha","type":"MASK","link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[16]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"JoinImageWithAlpha"},"widgets_values":[]},{"id":19,"type":"SeedVR2TorchCompileSettings","pos":[-369.50847923647007,315.0949953354577],"size":[307.671484375,178],"flags":{},"order":0,"mode":4,"inputs":[{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"mode","name":"mode","type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"fullgraph","name":"fullgraph","type":"BOOLEAN","widget":{"name":"fullgraph"},"link":null},{"localized_name":"dynamic","name":"dynamic","type":"BOOLEAN","widget":{"name":"dynamic"},"link":null},{"localized_name":"dynamo_cache_size_limit","name":"dynamo_cache_size_limit","type":"INT","widget":{"name":"dynamo_cache_size_limit"},"link":null},{"localized_name":"dynamo_recompile_limit","name":"dynamo_recompile_limit","type":"INT","widget":{"name":"dynamo_recompile_limit"},"link":null}],"outputs":[{"localized_name":"TORCH_COMPILE_ARGS","name":"TORCH_COMPILE_ARGS","type":"TORCH_COMPILE_ARGS","links":[18,19]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2TorchCompileSettings"},"widgets_values":["inductor","default",false,false,64,128]},{"id":20,"type":"Note","pos":[-358.9923959032341,553.3187800066305],"size":[290.1922351462714,97.95592567265356],"flags":{},"order":1,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable torch.compile optimization. First batch has compilation overhead, but speeds up subsequent batches. Best for long videos, not single images."],"color":"#432","bgcolor":"#653"},{"id":21,"type":"LoadVideo","pos":[-201.66033140975628,-266.86977401462417],"size":[274.080078125,228.1700439453125],"flags":{},"order":2,"mode":0,"inputs":[{"localized_name":"file","name":"file","type":"COMBO","widget":{"name":"file"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[20]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"LoadVideo"},"widgets_values":["Mustache_640x360.mp4","image"]},{"id":18,"type":"Note","pos":[417.45701148476115,-167.7461915699906],"size":[210,94.05179298404067],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable + connect input alpha to upscale alpha/mask channel along with RGB channel (for RGBA inputs)"],"color":"#432","bgcolor":"#653"},{"id":24,"type":"CreateVideo","pos":[1171.8133710326213,102.33318716823825],"size":[270,78],"flags":{},"order":9,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":22},{"localized_name":"audio","name":"audio","shape":7,"type":"AUDIO","link":25},{"localized_name":"fps","name":"fps","type":"FLOAT","widget":{"name":"fps"},"link":24}],"outputs":[{"localized_name":"VIDEO","name":"VIDEO","type":"VIDEO","links":[23]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"CreateVideo"},"widgets_values":[30]},{"id":23,"type":"SaveVideo","pos":[1499.476781650457,101.92559527677722],"size":[270,249.875],"flags":{},"order":10,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":23},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null},{"localized_name":"format","name":"format","type":"COMBO","widget":{"name":"format"},"link":null},{"localized_name":"codec","name":"codec","type":"COMBO","widget":{"name":"codec"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"SaveVideo"},"widgets_values":["video/ComfyUI","auto","auto"]},{"id":22,"type":"GetVideoComponents","pos":[149.28727188425574,-267.41033914727126],"size":[185.17734375,66],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"video","name":"video","type":"VIDEO","link":20}],"outputs":[{"localized_name":"images","name":"images","type":"IMAGE","links":[21]},{"localized_name":"audio","name":"audio","type":"AUDIO","links":[25]},{"localized_name":"fps","name":"fps","type":"FLOAT","links":[24]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"GetVideoComponents"},"widgets_values":[]},{"id":13,"type":"SeedVR2LoadVAEModel","pos":[57.08203125,413.14453125],"size":[312.866796875,298],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":19},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"encode_tiled","name":"encode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"encode_tiled"},"link":null},{"localized_name":"encode_tile_size","name":"encode_tile_size","shape":7,"type":"INT","widget":{"name":"encode_tile_size"},"link":null},{"localized_name":"encode_tile_overlap","name":"encode_tile_overlap","shape":7,"type":"INT","widget":{"name":"encode_tile_overlap"},"link":null},{"localized_name":"decode_tiled","name":"decode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"decode_tiled"},"link":null},{"localized_name":"decode_tile_size","name":"decode_tile_size","shape":7,"type":"INT","widget":{"name":"decode_tile_size"},"link":null},{"localized_name":"decode_tile_overlap","name":"decode_tile_overlap","shape":7,"type":"INT","widget":{"name":"decode_tile_overlap"},"link":null},{"localized_name":"tile_debug","name":"tile_debug","shape":7,"type":"COMBO","widget":{"name":"tile_debug"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_VAE","name":"SEEDVR2_VAE","type":"SEEDVR2_VAE","links":[10]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadVAEModel"},"widgets_values":["ema_vae_fp16.safetensors","cuda:0",true,1024,128,true,768,128,"false","cpu",false]},{"id":10,"type":"SeedVR2VideoUpscaler","pos":[840.6414062499999,104.58710937499997],"size":[270,386],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":16},{"localized_name":"dit","name":"dit","type":"SEEDVR2_DIT","link":11},{"localized_name":"vae","name":"vae","type":"SEEDVR2_VAE","link":10},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"resolution","name":"resolution","type":"INT","widget":{"name":"resolution"},"link":null},{"localized_name":"max_resolution","name":"max_resolution","type":"INT","widget":{"name":"max_resolution"},"link":null},{"localized_name":"batch_size","name":"batch_size","type":"INT","widget":{"name":"batch_size"},"link":null},{"localized_name":"uniform_batch_size","name":"uniform_batch_size","type":"BOOLEAN","widget":{"name":"uniform_batch_size"},"link":null},{"localized_name":"color_correction","name":"color_correction","type":"COMBO","widget":{"name":"color_correction"},"link":null},{"localized_name":"temporal_overlap","name":"temporal_overlap","shape":7,"type":"INT","widget":{"name":"temporal_overlap"},"link":null},{"localized_name":"prepend_frames","name":"prepend_frames","shape":7,"type":"INT","widget":{"name":"prepend_frames"},"link":null},{"localized_name":"input_noise_scale","name":"input_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"input_noise_scale"},"link":null},{"localized_name":"latent_noise_scale","name":"latent_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"latent_noise_scale"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"enable_debug","name":"enable_debug","shape":7,"type":"BOOLEAN","widget":{"name":"enable_debug"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[22]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2VideoUpscaler"},"widgets_values":[42,"fixed",1080,0,33,true,"lab",3,0,0,0,"cpu",false]},{"id":14,"type":"SeedVR2LoadDiTModel","pos":[61.165625,125.97187500000004],"size":[307.6646484375,202],"flags":{},"order":4,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":18},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"blocks_to_swap","name":"blocks_to_swap","shape":7,"type":"INT","widget":{"name":"blocks_to_swap"},"link":null},{"localized_name":"swap_io_components","name":"swap_io_components","shape":7,"type":"BOOLEAN","widget":{"name":"swap_io_components"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null},{"localized_name":"attention_mode","name":"attention_mode","shape":7,"type":"COMBO","widget":{"name":"attention_mode"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_DIT","name":"SEEDVR2_DIT","type":"SEEDVR2_DIT","links":[11]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadDiTModel"},"widgets_values":["seedvr2_ema_3b_fp16.safetensors","cuda:0",32,false,"cpu",false,"sdpa"]}],"links":[[10,13,0,10,2,"SEEDVR2_VAE"],[11,14,0,10,1,"SEEDVR2_DIT"],[16,17,0,10,0,"IMAGE"],[18,19,0,14,0,"TORCH_COMPILE_ARGS"],[19,19,0,13,0,"TORCH_COMPILE_ARGS"],[20,21,0,22,0,"VIDEO"],[21,22,0,17,0,"IMAGE"],[22,10,0,24,0,"IMAGE"],[23,24,0,23,0,"VIDEO"],[24,22,2,24,2,"FLOAT"],[25,22,1,24,1,"AUDIO"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.8441465687687482,"offset":[739.339356050958,625.120941260255]}},"version":0.4}
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_simple_image_upscale.jpg ADDED
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/SeedVR2_simple_image_upscale.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"id":"e2c8e6ec-9e8a-4a81-a240-7e7d2befc2bf","revision":0,"last_node_id":20,"last_link_id":19,"nodes":[{"id":16,"type":"LoadImage","pos":[529.6515751584279,98.66389637266899],"size":[274.080078125,314],"flags":{},"order":0,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"COMBO","widget":{"name":"image"},"link":null},{"localized_name":"choose file to upload","name":"upload","type":"IMAGEUPLOAD","widget":{"name":"upload"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[14]},{"localized_name":"MASK","name":"MASK","type":"MASK","links":[17]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"LoadImage"},"widgets_values":["example.png","image"]},{"id":17,"type":"JoinImageWithAlpha","pos":[867.7966831594896,98.31620737975611],"size":[176.86484375,46],"flags":{},"order":4,"mode":4,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":14},{"localized_name":"alpha","name":"alpha","type":"MASK","link":17}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[16]}],"properties":{"cnr_id":"comfy-core","ver":"0.3.68","Node name for S&R":"JoinImageWithAlpha"},"widgets_values":[]},{"id":19,"type":"SeedVR2TorchCompileSettings","pos":[82.82356467195704,682.7639698331246],"size":[307.671484375,178],"flags":{},"order":1,"mode":4,"inputs":[{"localized_name":"backend","name":"backend","type":"COMBO","widget":{"name":"backend"},"link":null},{"localized_name":"mode","name":"mode","type":"COMBO","widget":{"name":"mode"},"link":null},{"localized_name":"fullgraph","name":"fullgraph","type":"BOOLEAN","widget":{"name":"fullgraph"},"link":null},{"localized_name":"dynamic","name":"dynamic","type":"BOOLEAN","widget":{"name":"dynamic"},"link":null},{"localized_name":"dynamo_cache_size_limit","name":"dynamo_cache_size_limit","type":"INT","widget":{"name":"dynamo_cache_size_limit"},"link":null},{"localized_name":"dynamo_recompile_limit","name":"dynamo_recompile_limit","type":"INT","widget":{"name":"dynamo_recompile_limit"},"link":null}],"outputs":[{"localized_name":"TORCH_COMPILE_ARGS","name":"TORCH_COMPILE_ARGS","type":"TORCH_COMPILE_ARGS","links":[18,19]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2TorchCompileSettings"},"widgets_values":["inductor","default",false,false,64,128]},{"id":13,"type":"SeedVR2LoadVAEModel","pos":[509.41407515842775,780.813505747667],"size":[312.866796875,298],"flags":{},"order":6,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":19},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"encode_tiled","name":"encode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"encode_tiled"},"link":null},{"localized_name":"encode_tile_size","name":"encode_tile_size","shape":7,"type":"INT","widget":{"name":"encode_tile_size"},"link":null},{"localized_name":"encode_tile_overlap","name":"encode_tile_overlap","shape":7,"type":"INT","widget":{"name":"encode_tile_overlap"},"link":null},{"localized_name":"decode_tiled","name":"decode_tiled","shape":7,"type":"BOOLEAN","widget":{"name":"decode_tiled"},"link":null},{"localized_name":"decode_tile_size","name":"decode_tile_size","shape":7,"type":"INT","widget":{"name":"decode_tile_size"},"link":null},{"localized_name":"decode_tile_overlap","name":"decode_tile_overlap","shape":7,"type":"INT","widget":{"name":"decode_tile_overlap"},"link":null},{"localized_name":"tile_debug","name":"tile_debug","shape":7,"type":"COMBO","widget":{"name":"tile_debug"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_VAE","name":"SEEDVR2_VAE","type":"SEEDVR2_VAE","links":[10]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadVAEModel"},"widgets_values":["ema_vae_fp16.safetensors","cuda:0",false,1024,128,false,1024,128,"false","none",false]},{"id":14,"type":"SeedVR2LoadDiTModel","pos":[513.4976689084277,493.64084949766976],"size":[307.6646484375,202],"flags":{},"order":5,"mode":0,"inputs":[{"localized_name":"torch_compile_args","name":"torch_compile_args","shape":7,"type":"TORCH_COMPILE_ARGS","link":18},{"localized_name":"model","name":"model","type":"COMBO","widget":{"name":"model"},"link":null},{"localized_name":"device","name":"device","type":"COMBO","widget":{"name":"device"},"link":null},{"localized_name":"blocks_to_swap","name":"blocks_to_swap","shape":7,"type":"INT","widget":{"name":"blocks_to_swap"},"link":null},{"localized_name":"swap_io_components","name":"swap_io_components","shape":7,"type":"BOOLEAN","widget":{"name":"swap_io_components"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"cache_model","name":"cache_model","shape":7,"type":"BOOLEAN","widget":{"name":"cache_model"},"link":null},{"localized_name":"attention_mode","name":"attention_mode","shape":7,"type":"COMBO","widget":{"name":"attention_mode"},"link":null}],"outputs":[{"localized_name":"SEEDVR2_DIT","name":"SEEDVR2_DIT","type":"SEEDVR2_DIT","links":[11]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2LoadDiTModel"},"widgets_values":["seedvr2_ema_3b_fp8_e4m3fn.safetensors","cuda:0",0,false,"none",false,"sdpa"]},{"id":18,"type":"Note","pos":[869.7890553931869,199.9227829276786],"size":[210,94.05179298404067],"flags":{},"order":2,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable to upscale alpha/mask channel along with RGB channel (for RGBA inputs)"],"color":"#432","bgcolor":"#653"},{"id":20,"type":"Note","pos":[93.33964800519301,920.9877545042978],"size":[290.1922351462714,97.95592567265356],"flags":{},"order":3,"mode":0,"inputs":[],"outputs":[],"properties":{},"widgets_values":["Enable torch.compile optimization. First batch has compilation overhead, but speeds up subsequent batches. Best for long videos, not single images."],"color":"#432","bgcolor":"#653"},{"id":10,"type":"SeedVR2VideoUpscaler","pos":[1292.9734501584262,472.2560838726697],"size":[270,386],"flags":{},"order":7,"mode":0,"inputs":[{"localized_name":"image","name":"image","type":"IMAGE","link":16},{"localized_name":"dit","name":"dit","type":"SEEDVR2_DIT","link":11},{"localized_name":"vae","name":"vae","type":"SEEDVR2_VAE","link":10},{"localized_name":"seed","name":"seed","type":"INT","widget":{"name":"seed"},"link":null},{"localized_name":"resolution","name":"resolution","type":"INT","widget":{"name":"resolution"},"link":null},{"localized_name":"max_resolution","name":"max_resolution","type":"INT","widget":{"name":"max_resolution"},"link":null},{"localized_name":"batch_size","name":"batch_size","type":"INT","widget":{"name":"batch_size"},"link":null},{"localized_name":"uniform_batch_size","name":"uniform_batch_size","type":"BOOLEAN","widget":{"name":"uniform_batch_size"},"link":null},{"localized_name":"color_correction","name":"color_correction","type":"COMBO","widget":{"name":"color_correction"},"link":null},{"localized_name":"temporal_overlap","name":"temporal_overlap","shape":7,"type":"INT","widget":{"name":"temporal_overlap"},"link":null},{"localized_name":"prepend_frames","name":"prepend_frames","shape":7,"type":"INT","widget":{"name":"prepend_frames"},"link":null},{"localized_name":"input_noise_scale","name":"input_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"input_noise_scale"},"link":null},{"localized_name":"latent_noise_scale","name":"latent_noise_scale","shape":7,"type":"FLOAT","widget":{"name":"latent_noise_scale"},"link":null},{"localized_name":"offload_device","name":"offload_device","shape":7,"type":"COMBO","widget":{"name":"offload_device"},"link":null},{"localized_name":"enable_debug","name":"enable_debug","shape":7,"type":"BOOLEAN","widget":{"name":"enable_debug"},"link":null}],"outputs":[{"localized_name":"IMAGE","name":"IMAGE","type":"IMAGE","links":[12]}],"properties":{"aux_id":"ainvfx/ComfyUI-SeedVR2_VideoUpscaler","ver":"690cc39379c1481159ddd451368dbf2295930fc6","Node name for S&R":"SeedVR2VideoUpscaler"},"widgets_values":[42,"randomize",1080,0,1,false,"lab",0,0,0,0,"cpu",false]},{"id":15,"type":"SaveImage","pos":[1616.797668908428,473.0256151226697],"size":[270,270],"flags":{},"order":8,"mode":0,"inputs":[{"localized_name":"images","name":"images","type":"IMAGE","link":12},{"localized_name":"filename_prefix","name":"filename_prefix","type":"STRING","widget":{"name":"filename_prefix"},"link":null}],"outputs":[],"properties":{"cnr_id":"comfy-core","ver":"0.3.68"},"widgets_values":["ComfyUI"]}],"links":[[10,13,0,10,2,"SEEDVR2_VAE"],[11,14,0,10,1,"SEEDVR2_DIT"],[12,10,0,15,0,"IMAGE"],[14,16,0,17,0,"IMAGE"],[16,17,0,10,0,"IMAGE"],[17,16,1,17,1,"MASK"],[18,19,0,14,0,"TORCH_COMPILE_ARGS"],[19,19,0,13,0,"TORCH_COMPILE_ARGS"]],"groups":[],"config":{},"extra":{"ds":{"scale":0.9229599817706443,"offset":[268.65279462308837,183.8749573202125]}},"version":0.4}
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Eyes_212x120.mp4 ADDED
Binary file (20.5 kB). View file
 
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Mustache_640x360.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:21581bc8454e234e3d3f833172bb358215f47d33cfcf5fc12f3dd0dff3319d1d
3
+ size 115496
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/example_workflows/example_inputs/Sadhu_320x478.png ADDED

Git LFS Details

  • SHA256: b7faf22b8911a49742b1577b461f2187e78942d5f15c3998254edadfee970084
  • Pointer size: 131 Bytes
  • Size of remote file: 417 kB
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/inference_cli.py ADDED
@@ -0,0 +1,1712 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ SeedVR2 Video Upscaler - Standalone CLI Interface
4
+
5
+ Command-line interface for high-quality upscaling using SeedVR2 diffusion models.
6
+ Supports single and multi-GPU processing with advanced memory optimization.
7
+
8
+ Key Features:
9
+ • Multi-GPU Processing: Automatic workload distribution across multiple GPUs with
10
+ temporal overlap blending for seamless transitions
11
+ • Streaming Mode: Memory-efficient processing of long videos in chunks, avoiding
12
+ full video loading into RAM while maintaining temporal consistency
13
+ • Memory Optimization: BlockSwap for limited VRAM, VAE tiling for large resolutions,
14
+ intelligent tensor offloading between processing phases
15
+ • Performance: Torch.compile integration, BFloat16 compute pipeline,
16
+ efficient model caching for batch and streaming processing
17
+ • Flexibility: Multiple output formats (MP4/PNG), advanced color correction methods,
18
+ directory batch processing with auto-format detection
19
+ • Quality Control: Temporal overlap blending, frame prepending for artifact reduction,
20
+ configurable noise scales for detail preservation
21
+
22
+ Architecture:
23
+ The CLI implements a 4-phase processing pipeline:
24
+ 1. Encode: VAE encoding with optional input noise and tiling
25
+ 2. Upscale: DiT transformer upscaling with latent space diffusion
26
+ 3. Decode: VAE decoding with optional tiling
27
+ 4. Postprocess: Color correction and temporal blending
28
+
29
+ Usage:
30
+ python inference_cli.py video.mp4 --resolution 1080
31
+ For complete usage examples, run: python inference_cli.py --help
32
+
33
+ Requirements:
34
+ • Python 3.10+
35
+ • PyTorch 2.4+ with CUDA 12.1+ (NVIDIA) or MPS (Apple Silicon)
36
+ • 16GB+ VRAM recommended (8GB minimum with BlockSwap)
37
+ • OpenCV, NumPy for video I/O
38
+
39
+ Model Support:
40
+ • 3B models: seedvr2_ema_3b_fp16.safetensors (default), _fp8_e4m3fn/GGUF variants
41
+ • 7B models: seedvr2_ema_7b_fp16.safetensors, _fp8_e4m3fn/GGUF variants
42
+ • VAE: ema_vae_fp16.safetensors (shared across all models)
43
+ • Auto-downloads from HuggingFace on first run with SHA256 validation
44
+ """
45
+
46
+ # Standard library imports
47
+ import sys
48
+ import os
49
+ import argparse
50
+ import time
51
+ import platform
52
+ import multiprocessing as mp
53
+ from typing import Dict, Any, List, Optional, Tuple, Literal, Generator
54
+ from datetime import datetime
55
+ from pathlib import Path
56
+
57
+ # Set up path before any other imports to fix module resolution
58
+ script_dir = os.path.dirname(os.path.abspath(__file__))
59
+ if script_dir not in sys.path:
60
+ sys.path.insert(0, script_dir)
61
+
62
+ # Set environment variable so all spawned processes can find modules
63
+ os.environ['PYTHONPATH'] = script_dir + ':' + os.environ.get('PYTHONPATH', '')
64
+
65
+ # Ensure safe CUDA usage with multiprocessing
66
+ if mp.get_start_method(allow_none=True) != 'spawn':
67
+ mp.set_start_method('spawn', force=True)
68
+
69
+ # Configure platform-specific memory management before heavy imports
70
+ # Must be set BEFORE import torch
71
+ if platform.system() == "Darwin":
72
+ # MPS allocator requires: low_watermark <= high_watermark
73
+ # Setting both to 0.0 disables PyTorch memory limits, letting macOS manage memory
74
+ os.environ.setdefault("PYTORCH_MPS_HIGH_WATERMARK_RATIO", "0.0")
75
+ os.environ.setdefault("PYTORCH_MPS_LOW_WATERMARK_RATIO", "0.0")
76
+ else:
77
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "backend:cudaMallocAsync")
78
+
79
+ # Pre-parse arguments that must be handled before torch import
80
+ _pre_parser = argparse.ArgumentParser(add_help=False)
81
+ _pre_parser.add_argument("--cuda_device", type=str, default=None)
82
+ _pre_args, _ = _pre_parser.parse_known_args()
83
+
84
+ if _pre_args.cuda_device is not None:
85
+ device_list_env = [x.strip() for x in _pre_args.cuda_device.split(',') if x.strip()!='']
86
+
87
+ # Skip validation if CUDA_VISIBLE_DEVICES is already set (worker process)
88
+ if os.environ.get("CUDA_VISIBLE_DEVICES") is None:
89
+ # Temporary torch import for CUDA device validation only
90
+ # Must happen before setting CUDA_VISIBLE_DEVICES and before main torch import
91
+ import torch as _torch_check
92
+ if _torch_check.cuda.is_available():
93
+ available_count = _torch_check.cuda.device_count()
94
+ invalid_devices = [d for d in device_list_env if not d.isdigit() or int(d) >= available_count]
95
+ if invalid_devices:
96
+ print(f"❌ [ERROR] Invalid CUDA device ID(s): {', '.join(invalid_devices)}. "
97
+ f"Available devices: 0-{available_count-1} (total: {available_count})")
98
+ sys.exit(1)
99
+ else:
100
+ print("❌ [ERROR] CUDA is not available on this system. Cannot use --cuda_device argument.")
101
+ sys.exit(1)
102
+
103
+ # Set CUDA_VISIBLE_DEVICES for single GPU after validation
104
+ if len(device_list_env) == 1:
105
+ os.environ["CUDA_VISIBLE_DEVICES"] = device_list_env[0]
106
+
107
+ # Heavy dependency imports after environment configuration
108
+ import torch
109
+ import cv2
110
+ import numpy as np
111
+ import subprocess
112
+ import shutil
113
+
114
+ # Project imports
115
+ from src.utils.downloads import download_weight
116
+ from src.utils.model_registry import get_available_dit_models, DEFAULT_DIT, DEFAULT_VAE
117
+ from src.utils.constants import SEEDVR2_FOLDER_NAME
118
+ from src.core.generation_utils import (
119
+ setup_generation_context,
120
+ prepare_runner,
121
+ compute_generation_info,
122
+ log_generation_start,
123
+ blend_overlapping_frames,
124
+ load_text_embeddings,
125
+ script_directory
126
+ )
127
+ from src.core.generation_phases import (
128
+ encode_all_batches,
129
+ upscale_all_batches,
130
+ decode_all_batches,
131
+ postprocess_all_batches
132
+ )
133
+ from src.utils.debug import Debug
134
+ from src.optimization.memory_manager import clear_memory, get_gpu_backend, is_cuda_available
135
+ debug = Debug(enabled=False) # Will be enabled via --debug CLI flag
136
+
137
+
138
+ # =============================================================================
139
+ # FFMPEG Class
140
+ # =============================================================================
141
+
142
+ class FFMPEGVideoWriter:
143
+ """
144
+ Video writer using ffmpeg subprocess for encoding with 10-bit support.
145
+
146
+ Provides cv2.VideoWriter-compatible interface (write, isOpened, release) while
147
+ using ffmpeg for encoding. Enables 10-bit output (yuv420p10le with x265) which
148
+ reduces banding artifacts in gradients compared to 8-bit opencv output.
149
+
150
+ Args:
151
+ path: Output video file path
152
+ width: Frame width in pixels
153
+ height: Frame height in pixels
154
+ fps: Frames per second
155
+ use_10bit: If True, uses x265 codec with yuv420p10le pixel format.
156
+ If False, uses x264 with yuv420p (default: False)
157
+
158
+ Raises:
159
+ RuntimeError: If ffmpeg is not found in system PATH
160
+
161
+ Note:
162
+ Frames must be passed to write() in BGR format (same as cv2.VideoWriter).
163
+ Internally converts to RGB for ffmpeg rawvideo input.
164
+ """
165
+
166
+ def __init__(self, path: str, width: int, height: int, fps: float, use_10bit: bool = False):
167
+ pix_fmt = 'yuv420p10le' if use_10bit else 'yuv420p'
168
+ codec = 'libx265' if use_10bit else 'libx264'
169
+
170
+ self.proc = subprocess.Popen(
171
+ ['ffmpeg', '-y', '-f', 'rawvideo', '-pix_fmt', 'rgb24',
172
+ '-s', f'{width}x{height}', '-r', str(fps), '-i', '-',
173
+ '-c:v', codec, '-pix_fmt', pix_fmt, '-preset', 'medium', '-crf', '12', path],
174
+ stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
175
+ )
176
+
177
+ def write(self, frame_bgr: np.ndarray):
178
+ if not self.isOpened():
179
+ raise RuntimeError("FFMPEGVideoWriter: ffmpeg process is not running")
180
+
181
+ frame_rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
182
+ try:
183
+ self.proc.stdin.write(frame_rgb.astype(np.uint8).tobytes())
184
+ self.proc.stdin.flush() # Critical: prevent buffering issues
185
+ except BrokenPipeError:
186
+ raise RuntimeError(
187
+ "FFMPEGVideoWriter: ffmpeg process terminated unexpectedly. "
188
+ "Check video path, codec support, and disk space."
189
+ )
190
+
191
+ def isOpened(self) -> bool:
192
+ return self.proc is not None and self.proc.poll() is None
193
+
194
+ def release(self):
195
+ if self.proc:
196
+ try:
197
+ self.proc.stdin.close()
198
+ except Exception:
199
+ pass # Ignore errors on close
200
+
201
+ self.proc.wait()
202
+
203
+ if self.proc.returncode != 0:
204
+ debug.log(
205
+ f"ffmpeg exited with code {self.proc.returncode}. "
206
+ "Check output file for corruption.",
207
+ level="WARNING", force=True, category="file"
208
+ )
209
+ self.proc = None
210
+
211
+
212
+ # =============================================================================
213
+ # Device Management Helpers
214
+ # =============================================================================
215
+
216
+ def _device_id_to_name(device_id: str, platform_type: str = None) -> str:
217
+ """
218
+ Convert device ID to full device name.
219
+
220
+ Args:
221
+ device_id: Device ID ("0", "1") or special value ("cpu", "none")
222
+ platform_type: Override platform type ("cuda", "mps", "cpu")
223
+
224
+ Returns:
225
+ Full device name ("cuda:0", "mps:0", "cpu", "none")
226
+ """
227
+ if device_id in ("cpu", "none"):
228
+ return device_id
229
+
230
+ if platform_type is None:
231
+ platform_type = get_gpu_backend()
232
+
233
+ # MPS typically doesn't use indices
234
+ if platform_type == "mps":
235
+ return "mps"
236
+
237
+ return f"{platform_type}:{device_id}"
238
+
239
+
240
+ def _parse_offload_device(offload_arg: str, platform_type: str = None, cache_enabled: bool = False) -> Optional[str]:
241
+ """
242
+ Parse offload device argument to full device name.
243
+
244
+ Args:
245
+ offload_arg: Offload device argument ("none", "cpu", "0", "1", or "cuda:1")
246
+ platform_type: Override platform type
247
+ cache_enabled: If True and offload_arg is "none", default to "cpu"
248
+
249
+ Returns:
250
+ Full device name or None
251
+ """
252
+ if offload_arg == "none":
253
+ # If caching enabled but no offload device specified, default to CPU
254
+ return "cpu" if cache_enabled else None
255
+
256
+ if offload_arg == "cpu":
257
+ return "cpu"
258
+
259
+ # If already a full device name (cuda:1, mps:0), return as-is
260
+ if ":" in offload_arg:
261
+ return offload_arg
262
+
263
+ # Otherwise treat as device ID
264
+ return _device_id_to_name(offload_arg, platform_type)
265
+
266
+
267
+ # =============================================================================
268
+ # Constants
269
+ # =============================================================================
270
+
271
+ # Supported file extensions
272
+ VIDEO_EXTENSIONS = {'.mp4', '.avi', '.mov', '.mkv', '.webm', '.flv', '.wmv', '.m4v'}
273
+ IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.bmp', '.tiff', '.tif', '.webp'}
274
+
275
+
276
+ # =============================================================================
277
+ # Video I/O Functions
278
+ # =============================================================================
279
+
280
+ def get_media_files(directory: str) -> List[str]:
281
+ """
282
+ Get all video and image files from directory, sorted alphabetically.
283
+
284
+ Args:
285
+ directory: Path to directory to scan
286
+
287
+ Returns:
288
+ Sorted list of file paths (strings) matching video or image extensions
289
+ """
290
+ valid_extensions = VIDEO_EXTENSIONS | IMAGE_EXTENSIONS
291
+ path = Path(directory)
292
+
293
+ # Get all files and filter by extension (case-insensitive)
294
+ files = [f for f in path.iterdir() if f.is_file() and f.suffix.lower() in valid_extensions]
295
+
296
+ return sorted([str(f) for f in files])
297
+
298
+
299
+ def extract_frames_from_image(image_path: str) -> Tuple[torch.Tensor, float]:
300
+ """
301
+ Extract single frame from image file and convert to tensor format.
302
+
303
+ Reads image using OpenCV, converts BGR to RGB, normalizes to [0,1] range,
304
+ and formats as single-frame video tensor for consistent processing.
305
+
306
+ Args:
307
+ image_path: Path to input image file
308
+
309
+ Returns:
310
+ Tuple containing:
311
+ - frames_tensor: Single frame as tensor [1, H, W, C], Float16, range [0,1] (C=3 for RGB, C=4 for RGBA)
312
+ - fps: Default FPS value (30.0) for image-to-video conversion
313
+
314
+ Raises:
315
+ FileNotFoundError: If image file doesn't exist
316
+ ValueError: If image cannot be opened
317
+ """
318
+ debug.log(f"Loading image: {image_path}", category="file")
319
+
320
+ if not os.path.exists(image_path):
321
+ raise FileNotFoundError(f"Image file not found: {image_path}")
322
+
323
+ # Read image with alpha channel preserved
324
+ frame = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
325
+ if frame is None:
326
+ raise ValueError(f"Cannot open image file: {image_path}")
327
+
328
+ # Convert BGR(A) to RGB(A) based on channel count
329
+ if frame.shape[2] == 4:
330
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGRA2RGBA)
331
+ debug.log(f"Detected RGBA image (alpha channel preserved)", category="file")
332
+ else:
333
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
334
+
335
+ # Convert to float32 and normalize
336
+ frame = frame.astype(np.float32) / 255.0
337
+
338
+ # Convert to tensor [1, H, W, C]
339
+ frames_tensor = torch.from_numpy(frame[None, ...]).to(torch.float16)
340
+
341
+ debug.log(f"Image tensor shape: {frames_tensor.shape}, dtype: {frames_tensor.dtype}", category="memory")
342
+
343
+ return frames_tensor, 30.0 # Default FPS for images
344
+
345
+
346
+ def get_input_type(input_path: str) -> Literal['video', 'image', 'directory', 'unknown']:
347
+ """
348
+ Determine input type from file path.
349
+
350
+ Args:
351
+ input_path: Path to input file or directory
352
+
353
+ Returns:
354
+ Input type: 'video', 'image', 'directory', or 'unknown'
355
+
356
+ Raises:
357
+ FileNotFoundError: If input path doesn't exist
358
+ """
359
+ path = Path(input_path)
360
+
361
+ if not path.exists():
362
+ raise FileNotFoundError(f"Input path not found: {input_path}")
363
+
364
+ if path.is_dir():
365
+ return 'directory'
366
+
367
+ ext = path.suffix.lower()
368
+ if ext in VIDEO_EXTENSIONS:
369
+ return "video"
370
+ elif ext in IMAGE_EXTENSIONS:
371
+ return "image"
372
+ else:
373
+ return "unknown"
374
+
375
+
376
+ def generate_output_path(input_path: str, output_format: str, output_dir: Optional[str] = None,
377
+ input_type: Optional[str] = None, from_directory: bool = False) -> str:
378
+ """
379
+ Generate output path based on input path and format.
380
+
381
+ Args:
382
+ input_path: Source file path
383
+ output_format: "mp4" or "png"
384
+ output_dir: Optional output directory (overrides default behavior)
385
+ input_type: Optional input type ("image", "video", "directory")
386
+ from_directory: True if processing files from a directory (batch mode)
387
+
388
+ Returns:
389
+ Absolute output path (file for single image/video, directory for sequences)
390
+ """
391
+ input_path_obj = Path(input_path)
392
+ input_name = input_path_obj.stem
393
+
394
+ # Determine base directory and whether to add suffix
395
+ if output_dir:
396
+ # User specified output directory - use as-is, no suffix
397
+ base_dir = Path(output_dir)
398
+ add_suffix = False
399
+ elif from_directory:
400
+ # Batch mode: create sibling folder with _upscaled, keep original filenames
401
+ original_dir = input_path_obj.parent
402
+ base_dir = original_dir.parent / f"{original_dir.name}_upscaled"
403
+ add_suffix = False
404
+ else:
405
+ # Single file mode: output to same directory with _upscaled suffix
406
+ base_dir = input_path_obj.parent
407
+ add_suffix = True
408
+
409
+ # Build filename with optional suffix
410
+ file_suffix = "_upscaled" if add_suffix else ""
411
+
412
+ # Generate output path based on format
413
+ if output_format == "png":
414
+ if input_type == "image":
415
+ output_path = base_dir / f"{input_name}{file_suffix}.png"
416
+ else:
417
+ output_path = base_dir / f"{input_name}{file_suffix}"
418
+ else:
419
+ output_path = base_dir / f"{input_name}{file_suffix}.mp4"
420
+
421
+ return str(output_path.resolve())
422
+
423
+
424
+ def process_single_file(input_path: str, args: argparse.Namespace, device_list: List[str],
425
+ output_path: Optional[str] = None, format_auto_detected: bool = False,
426
+ runner_cache: Optional[Dict[str, Any]] = None) -> int:
427
+ """
428
+ Process a single video or image file with optional model caching.
429
+
430
+ For videos, supports streaming mode (chunk_size > 0) which processes in memory-bounded
431
+ chunks with temporal overlap for seamless transitions between chunks.
432
+
433
+ Args:
434
+ input_path: Path to input file
435
+ args: Command-line arguments with all processing settings
436
+ device_list: List of GPU device IDs as strings
437
+ output_path: Optional explicit output path (auto-generated if None)
438
+ format_auto_detected: Whether output format was auto-detected
439
+ runner_cache: Optional cache dict for model reuse across multiple files
440
+
441
+ Returns:
442
+ Number of frames written to output
443
+ """
444
+ input_type = get_input_type(input_path)
445
+
446
+ if input_type == "unknown":
447
+ debug.log(f"Skipping unsupported file: {input_path}", level="WARNING", category="file", force=True)
448
+ return 0
449
+
450
+ debug.log(f"Processing {input_type}: {Path(input_path).name}", category="generation", force=True)
451
+
452
+ # Generate or validate output path
453
+ if output_path is None:
454
+ output_path = generate_output_path(input_path, args.output_format, input_type=input_type)
455
+ elif not Path(output_path).suffix or (args.output_format == "png" and input_type != "image"):
456
+ # No extension or PNG sequence → treat as directory, generate filename
457
+ output_path = generate_output_path(input_path, args.output_format,
458
+ output_dir=output_path, input_type=input_type)
459
+
460
+ # Show format with auto-detection indicator
461
+ format_prefix = "Auto-detected" if format_auto_detected else "Requested"
462
+ debug.log(f"{format_prefix} output format: {args.output_format}", category="info", force=True, indent_level=1)
463
+
464
+ # === VIDEO PROCESSING ===
465
+ if input_type == "video":
466
+ if not os.path.exists(input_path):
467
+ raise FileNotFoundError(f"Video file not found: {input_path}")
468
+
469
+ cap = cv2.VideoCapture(input_path)
470
+ if not cap.isOpened():
471
+ raise ValueError(f"Cannot open video file: {input_path}")
472
+
473
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30.0
474
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
475
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
476
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
477
+
478
+ debug.log(f"Video info: {total_frames} frames, {width}x{height}, {fps:.2f} FPS", category="info")
479
+
480
+ # Skip initial frames
481
+ if args.skip_first_frames > 0:
482
+ debug.log(f"Skipping first {args.skip_first_frames} frames", category="info")
483
+ cap.set(cv2.CAP_PROP_POS_FRAMES, args.skip_first_frames)
484
+
485
+ # Calculate frames to process (apply load_cap if set)
486
+ frames_to_process = total_frames - args.skip_first_frames
487
+ if args.load_cap > 0:
488
+ frames_to_process = min(frames_to_process, args.load_cap)
489
+
490
+ # Early exit for empty/exhausted video
491
+ if frames_to_process <= 0:
492
+ debug.log(f"No frames to process after skipping {args.skip_first_frames} of {total_frames}",
493
+ level="WARNING", category="file", force=True)
494
+ cap.release()
495
+ return 0
496
+
497
+ # Streaming mode: process in chunks
498
+ chunk_size = args.chunk_size if args.chunk_size > 0 else frames_to_process
499
+ streaming = args.chunk_size > 0
500
+ total_chunks = (frames_to_process + chunk_size - 1) // chunk_size # ceiling division
501
+
502
+ if streaming:
503
+ debug.log(f"Streaming mode: chunks of {chunk_size} frames, overlap={args.temporal_overlap}",
504
+ category="info", force=True, indent_level=1)
505
+
506
+ is_png = args.output_format == "png"
507
+ video_writer = None
508
+ overlap = args.temporal_overlap
509
+ frames_written = 0
510
+ chunk_idx = 0
511
+ base_name = Path(input_path).stem
512
+
513
+ # Multi-GPU: workers stream their own segments
514
+ if len(device_list) > 1:
515
+ cap.release() # Workers will reopen
516
+ video_info = {
517
+ 'video_path': input_path,
518
+ 'start_frame': args.skip_first_frames,
519
+ 'frames_to_process': frames_to_process,
520
+ }
521
+ result = _gpu_processing(None, device_list, args, video_info=video_info)
522
+
523
+ # Save result
524
+ if is_png:
525
+ save_frames_to_image(result, output_path, base_name)
526
+ else:
527
+ video_writer = save_frames_to_video(result, output_path, fps,
528
+ video_backend=args.video_backend, use_10bit=args.use_10bit)
529
+ if video_writer is not None:
530
+ video_writer.release()
531
+
532
+ frames_written = result.shape[0]
533
+
534
+ # Single GPU: stream in main process
535
+ else:
536
+ chunk_count = 0
537
+ for result in _stream_video_chunks(
538
+ cap=cap,
539
+ frames_to_process=frames_to_process,
540
+ chunk_size=chunk_size,
541
+ overlap=overlap,
542
+ args=args,
543
+ device_id=device_list[0],
544
+ debug=debug,
545
+ runner_cache=runner_cache,
546
+ log_progress=streaming,
547
+ total_chunks=total_chunks,
548
+ cleanup_timer_name="chunk_cleanup"
549
+ ):
550
+ chunk_count += 1
551
+
552
+ # Save output
553
+ if is_png:
554
+ save_frames_to_image(result, output_path, base_name, start_index=frames_written)
555
+ else:
556
+ video_writer = save_frames_to_video(result, output_path, fps, writer=video_writer,
557
+ video_backend=args.video_backend, use_10bit=args.use_10bit)
558
+
559
+ frames_written += result.shape[0]
560
+ del result
561
+
562
+ chunk_idx = chunk_count
563
+ cap.release()
564
+ if video_writer is not None:
565
+ video_writer.release()
566
+
567
+ if streaming:
568
+ debug.log("", category="none", force=True)
569
+ if len(device_list) > 1:
570
+ debug.log(f"Streaming complete: {frames_written} frames across {len(device_list)} GPUs", category="success", force=True)
571
+ else:
572
+ debug.log(f"Streaming complete: {frames_written} frames in {chunk_idx} chunks", category="success", force=True)
573
+
574
+ debug.log(f"Output saved to: {output_path}", category="file", force=True)
575
+ return frames_written
576
+
577
+ # === IMAGE PROCESSING ===
578
+ frames_tensor, _ = extract_frames_from_image(input_path)
579
+
580
+ processing_start = time.time()
581
+ # Process frames (multiprocessing only for multi-GPU)
582
+ if len(device_list) > 1:
583
+ result = _gpu_processing(frames_tensor, device_list, args)
584
+ else:
585
+ result = _single_gpu_direct_processing(frames_tensor, args, device_list[0], runner_cache)
586
+ debug.log(f"Processing time: {time.time() - processing_start:.2f}s", category="timing")
587
+
588
+ # Save single image
589
+ os.makedirs(Path(output_path).parent, exist_ok=True)
590
+ frame_np = (result[0].cpu().numpy() * 255.0).astype(np.uint8)
591
+ _save_image_bgr(frame_np, output_path)
592
+
593
+ debug.log(f"Output saved to: {output_path}", category="file", force=True)
594
+ return 1
595
+
596
+
597
+ def _read_frames_from_cap(cap: cv2.VideoCapture, max_frames: int) -> Optional[torch.Tensor]:
598
+ """
599
+ Read up to max_frames from an already-open VideoCapture.
600
+
601
+ Args:
602
+ cap: An already opened cv2.VideoCapture instance
603
+ max_frames: Maximum number of frames to read in this call
604
+
605
+ Returns:
606
+ Tensor [T, H, W, C] float32 [0,1], or None if no frames available
607
+ """
608
+ frames = []
609
+ for _ in range(max_frames):
610
+ ret, frame = cap.read()
611
+ if not ret:
612
+ break
613
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0
614
+ frames.append(frame)
615
+
616
+ if not frames:
617
+ return None
618
+ return torch.from_numpy(np.stack(frames)).to(torch.float32)
619
+
620
+
621
+ def _stream_video_chunks(
622
+ cap: cv2.VideoCapture,
623
+ frames_to_process: int,
624
+ chunk_size: int,
625
+ overlap: int,
626
+ args: argparse.Namespace,
627
+ device_id: str,
628
+ debug: 'Debug',
629
+ runner_cache: Optional[Dict[str, Any]],
630
+ log_progress: bool = False,
631
+ total_chunks: int = 0,
632
+ cleanup_timer_name: Optional[str] = None,
633
+ log_prefix: str = ""
634
+ ) -> Generator[torch.Tensor, None, None]:
635
+ """
636
+ Generator that streams and processes video chunks.
637
+
638
+ Handles frame reading, temporal context prepending, processing via
639
+ _process_frames_core, context removal from output, and memory cleanup.
640
+ Caller is responsible for VideoCapture lifecycle and result handling.
641
+
642
+ Args:
643
+ cap: Open VideoCapture positioned at start frame
644
+ frames_to_process: Total frames to read and process
645
+ chunk_size: Frames per chunk (use frames_to_process for single chunk)
646
+ overlap: Temporal overlap frames between chunks for blending
647
+ args: Processing arguments (copied internally, prepend_frames zeroed after first chunk)
648
+ device_id: GPU device ID for processing
649
+ debug: Debug instance for logging
650
+ runner_cache: Optional model cache dict for reuse across chunks
651
+ log_progress: If True, log chunk progress with separators
652
+ total_chunks: Total chunks for progress display (used if log_progress=True)
653
+ cleanup_timer_name: Optional timer name for memory cleanup logging
654
+ log_prefix: Optional prefix for log messages (e.g., "[GPU 0] " for worker identification)
655
+
656
+ Yields:
657
+ Processed frames tensor [T, H, W, C] for each chunk, context frames removed
658
+ """
659
+ chunk_args = argparse.Namespace(**vars(args))
660
+ frames_read = 0
661
+ prev_raw_tail = None
662
+ chunk_idx = 0
663
+ streaming = chunk_size < frames_to_process
664
+
665
+ while frames_read < frames_to_process:
666
+ read_count = min(chunk_size, frames_to_process - frames_read)
667
+ new_frames = _read_frames_from_cap(cap, read_count)
668
+ if new_frames is None:
669
+ break
670
+ frames_read += new_frames.shape[0]
671
+ chunk_idx += 1
672
+
673
+ # Disable prepend_frames after first chunk
674
+ if chunk_idx > 1:
675
+ chunk_args.prepend_frames = 0
676
+
677
+ # Prepend context from previous chunk
678
+ if prev_raw_tail is not None and overlap > 0:
679
+ context_count = min(overlap, prev_raw_tail.shape[0])
680
+ frames = torch.cat([prev_raw_tail[-context_count:], new_frames], dim=0)
681
+ else:
682
+ frames = new_frames
683
+ context_count = 0
684
+
685
+ # Log progress if enabled
686
+ if log_progress and streaming:
687
+ if chunk_idx > 1:
688
+ debug.log("", category="none", force=True)
689
+ debug.log("━" * 60, category="none", force=True)
690
+ debug.log("", category="none", force=True)
691
+ debug.log(f"{log_prefix}Chunk {chunk_idx}/{total_chunks}: {new_frames.shape[0]} new + {context_count} context frames",
692
+ category="generation", force=True)
693
+ debug.log("", category="none", force=True)
694
+
695
+ # Process chunk
696
+ result = _process_frames_core(
697
+ frames_tensor=frames.to(torch.float16),
698
+ args=chunk_args,
699
+ device_id=device_id,
700
+ debug=debug,
701
+ runner_cache=runner_cache
702
+ )
703
+
704
+ # Remove context frames from output
705
+ if context_count > 0:
706
+ result = result[context_count:]
707
+
708
+ # Save tail for next chunk context
709
+ prev_raw_tail = new_frames[-overlap:].clone() if overlap > 0 else None
710
+
711
+ # Cleanup before yield
712
+ del frames
713
+
714
+ yield result
715
+
716
+ # Memory cleanup between chunks
717
+ if streaming:
718
+ clear_memory(debug=debug, deep=True, force=True, timer_name=cleanup_timer_name)
719
+
720
+
721
+ def _save_image_bgr(frame_np: np.ndarray, file_path: str) -> None:
722
+ """
723
+ Save a single RGB(A) uint8 frame to disk, converting to BGR(A) for OpenCV.
724
+
725
+ Args:
726
+ frame_np: Frame as uint8 numpy array [H, W, C] where C is 3 (RGB) or 4 (RGBA)
727
+ file_path: Output file path
728
+ """
729
+ if frame_np.shape[2] == 4:
730
+ frame_bgr = cv2.cvtColor(frame_np, cv2.COLOR_RGBA2BGRA)
731
+ else:
732
+ frame_bgr = cv2.cvtColor(frame_np, cv2.COLOR_RGB2BGR)
733
+ cv2.imwrite(file_path, frame_bgr)
734
+
735
+
736
+ def save_frames_to_video(
737
+ frames_tensor: torch.Tensor,
738
+ output_path: str,
739
+ fps: float = 30.0,
740
+ writer: Optional[cv2.VideoWriter] = None,
741
+ video_backend: str = "opencv",
742
+ use_10bit: bool = False
743
+ ) -> Optional[cv2.VideoWriter]:
744
+ """
745
+ Save frames tensor to MP4 video file.
746
+
747
+ Converts tensor from Float32 [0,1] to uint8 [0,255], RGB to BGR for OpenCV,
748
+ and writes to video file using mp4v codec. Supports streaming mode where
749
+ an existing writer is passed and kept open for subsequent chunks.
750
+
751
+ Args:
752
+ frames_tensor: Frames in format [T, H, W, C], Float32, range [0,1]
753
+ output_path: Output video file path (directory created if doesn't exist)
754
+ fps: Frames per second for output video (default: 30.0)
755
+ writer: Existing VideoWriter for streaming (if None, creates new one)
756
+
757
+ Returns:
758
+ VideoWriter if streaming mode (caller must close), None if standalone mode
759
+
760
+ Raises:
761
+ ValueError: If video writer cannot be initialized
762
+ """
763
+ frames_np = (frames_tensor.cpu().numpy() * 255.0).astype(np.uint8)
764
+ T, H, W, C = frames_np.shape
765
+
766
+ if writer is None:
767
+ debug.log(f"Saving {T} frames to video: {output_path} (backend={video_backend})", category="file")
768
+ os.makedirs(Path(output_path).parent, exist_ok=True)
769
+ if video_backend == "ffmpeg":
770
+ writer = FFMPEGVideoWriter(output_path, W, H, fps, use_10bit)
771
+ else:
772
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
773
+ writer = cv2.VideoWriter(output_path, fourcc, fps, (W, H))
774
+ if not writer.isOpened():
775
+ raise ValueError(f"Cannot create video writer for: {output_path}")
776
+
777
+ for i, frame in enumerate(frames_np):
778
+ frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
779
+ writer.write(frame_bgr)
780
+ if debug.enabled and (i + 1) % 100 == 0:
781
+ debug.log(f"Written {i + 1}/{T} frames", category="file")
782
+
783
+ return writer # Caller always closes
784
+
785
+
786
+ def save_frames_to_image(
787
+ frames_tensor: torch.Tensor,
788
+ output_dir: str,
789
+ base_name: str,
790
+ start_index: int = 0
791
+ ) -> int:
792
+ """
793
+ Save frames tensor as sequential PNG image files.
794
+
795
+ Each frame saved as {base_name}_{index:0Nd}.png with zero-padded indices.
796
+ Converts Float32 [0,1] to uint8 [0,255] and RGB(A) to BGR(A) for OpenCV.
797
+
798
+ Args:
799
+ frames_tensor: Frames in format [T, H, W, C], Float32, range [0,1]
800
+ output_dir: Directory to save PNG files (created if doesn't exist)
801
+ base_name: Base name for output files (e.g., "frame" → "frame_00000.png")
802
+ start_index: Starting index for filenames (for streaming continuation)
803
+
804
+ Returns:
805
+ Number of frames saved
806
+ """
807
+ os.makedirs(output_dir, exist_ok=True)
808
+
809
+ frames_np = (frames_tensor.cpu().numpy() * 255.0).astype(np.uint8)
810
+ total = frames_np.shape[0]
811
+
812
+ if start_index == 0:
813
+ debug.log(f"Saving {total} frames as PNGs to directory: {output_dir}", category="file")
814
+ digits = 6 # Supports up to 999,999 frames (~11.5 hours at 24fps)
815
+
816
+ for idx, frame in enumerate(frames_np):
817
+ filename = f"{base_name}_{start_index + idx:0{digits}d}.png"
818
+ file_path = os.path.join(output_dir, filename)
819
+ _save_image_bgr(frame, file_path)
820
+ if debug.enabled and (idx + 1) % 100 == 0:
821
+ debug.log(f"Saved {idx + 1}/{total} images", category="file")
822
+
823
+ debug.log(f"Saved {total} images to '{output_dir}'", category="success")
824
+ return total
825
+
826
+
827
+ # =============================================================================
828
+ # Core Processing Logic
829
+ # =============================================================================
830
+
831
+ def _process_frames_core(
832
+ frames_tensor: torch.Tensor,
833
+ args: argparse.Namespace,
834
+ device_id: str,
835
+ debug: Debug,
836
+ runner_cache: Optional[Dict[str, Any]] = None
837
+ ) -> torch.Tensor:
838
+ """
839
+ Core frame processing logic shared between worker and direct processing.
840
+
841
+ Executes the complete 4-phase pipeline: encode → upscale → decode → postprocess.
842
+ Supports both cached (direct) and non-cached (worker) execution modes.
843
+
844
+ Args:
845
+ frames_tensor: Input frames [T, H, W, C], Float16/Float32, range [0,1]
846
+ args: Command-line arguments with all processing settings
847
+ device_id: Device ID for inference ("0", "1", etc.)
848
+ debug: Debug instance for logging
849
+ runner_cache: Optional cache dict for model reuse (direct mode only)
850
+
851
+ Returns:
852
+ Upscaled frames tensor [T', H', W', C], Float32, range [0,1]
853
+ """
854
+ # Determine platform and convert device IDs to full names
855
+ platform_type = get_gpu_backend()
856
+ inference_device = _device_id_to_name(device_id, platform_type)
857
+
858
+ # Parse offload devices (with caching defaults)
859
+ cache_dit = args.cache_dit if runner_cache is not None else False
860
+ cache_vae = args.cache_vae if runner_cache is not None else False
861
+
862
+ dit_offload = _parse_offload_device(args.dit_offload_device, platform_type, cache_dit)
863
+ vae_offload = _parse_offload_device(args.vae_offload_device, platform_type, cache_vae)
864
+ tensor_offload = _parse_offload_device(args.tensor_offload_device, platform_type, False)
865
+
866
+ # Setup or reuse generation context
867
+ if runner_cache is not None and 'ctx' in runner_cache:
868
+ ctx = runner_cache['ctx']
869
+ # Clear previous run data but keep device config
870
+ keys_to_keep = {'dit_device', 'vae_device', 'dit_offload_device',
871
+ 'vae_offload_device', 'tensor_offload_device', 'compute_dtype'}
872
+ for key in list(ctx.keys()):
873
+ if key not in keys_to_keep:
874
+ del ctx[key]
875
+ else:
876
+ ctx = setup_generation_context(
877
+ dit_device=inference_device,
878
+ vae_device=inference_device,
879
+ dit_offload_device=dit_offload,
880
+ vae_offload_device=vae_offload,
881
+ tensor_offload_device=tensor_offload,
882
+ debug=debug
883
+ )
884
+ if runner_cache is not None:
885
+ runner_cache['ctx'] = ctx
886
+
887
+ # Build torch compile args
888
+ torch_compile_args_dit = None
889
+ torch_compile_args_vae = None
890
+ if args.compile_dit:
891
+ torch_compile_args_dit = {
892
+ "backend": args.compile_backend,
893
+ "mode": args.compile_mode,
894
+ "fullgraph": args.compile_fullgraph,
895
+ "dynamic": args.compile_dynamic,
896
+ "dynamo_cache_size_limit": args.compile_dynamo_cache_size_limit,
897
+ "dynamo_recompile_limit": args.compile_dynamo_recompile_limit,
898
+ }
899
+ if args.compile_vae:
900
+ torch_compile_args_vae = {
901
+ "backend": args.compile_backend,
902
+ "mode": args.compile_mode,
903
+ "fullgraph": args.compile_fullgraph,
904
+ "dynamic": args.compile_dynamic,
905
+ "dynamo_cache_size_limit": args.compile_dynamo_cache_size_limit,
906
+ "dynamo_recompile_limit": args.compile_dynamo_recompile_limit,
907
+ }
908
+
909
+ # Prepare runner with caching support
910
+ model_dir = args.model_dir if args.model_dir is not None else f"./models/{SEEDVR2_FOLDER_NAME}"
911
+
912
+ # Use fixed IDs for CLI caching when enabled
913
+ dit_id = "cli_dit" if cache_dit else None
914
+ vae_id = "cli_vae" if cache_vae else None
915
+
916
+ runner, cache_context = prepare_runner(
917
+ dit_model=args.dit_model,
918
+ vae_model=DEFAULT_VAE,
919
+ model_dir=model_dir,
920
+ debug=debug,
921
+ ctx=ctx,
922
+ dit_cache=cache_dit,
923
+ vae_cache=cache_vae,
924
+ dit_id=dit_id,
925
+ vae_id=vae_id,
926
+ block_swap_config={
927
+ 'blocks_to_swap': args.blocks_to_swap,
928
+ 'swap_io_components': args.swap_io_components,
929
+ 'offload_device': dit_offload,
930
+ },
931
+ encode_tiled=args.vae_encode_tiled,
932
+ encode_tile_size=(args.vae_encode_tile_size, args.vae_encode_tile_size),
933
+ encode_tile_overlap=(args.vae_encode_tile_overlap, args.vae_encode_tile_overlap),
934
+ decode_tiled=args.vae_decode_tiled,
935
+ decode_tile_size=(args.vae_decode_tile_size, args.vae_decode_tile_size),
936
+ decode_tile_overlap=(args.vae_decode_tile_overlap, args.vae_decode_tile_overlap),
937
+ tile_debug=args.tile_debug.lower() if args.tile_debug else "false",
938
+ attention_mode=args.attention_mode,
939
+ torch_compile_args_dit=torch_compile_args_dit,
940
+ torch_compile_args_vae=torch_compile_args_vae
941
+ )
942
+
943
+ ctx['cache_context'] = cache_context
944
+ if runner_cache is not None:
945
+ runner_cache['runner'] = runner
946
+
947
+ # Preload text embeddings before Phase 1 to avoid sync stall in Phase 2
948
+ ctx['text_embeds'] = load_text_embeddings(script_directory, ctx['dit_device'], ctx['compute_dtype'], debug)
949
+ debug.log("Loaded text embeddings for DiT", category="dit")
950
+
951
+ # Compute generation info and log start (handles prepending internally)
952
+ frames_tensor, gen_info = compute_generation_info(
953
+ ctx=ctx,
954
+ images=frames_tensor,
955
+ resolution=args.resolution,
956
+ max_resolution=args.max_resolution,
957
+ batch_size=args.batch_size,
958
+ uniform_batch_size=args.uniform_batch_size,
959
+ seed=args.seed,
960
+ prepend_frames=args.prepend_frames,
961
+ temporal_overlap=args.temporal_overlap,
962
+ debug=debug
963
+ )
964
+ log_generation_start(gen_info, debug)
965
+
966
+ # Phase 1: Encode
967
+ ctx = encode_all_batches(
968
+ runner, ctx=ctx, images=frames_tensor,
969
+ debug=debug,
970
+ batch_size=args.batch_size,
971
+ uniform_batch_size=args.uniform_batch_size,
972
+ seed=args.seed,
973
+ progress_callback=None,
974
+ temporal_overlap=args.temporal_overlap,
975
+ resolution=args.resolution,
976
+ max_resolution=args.max_resolution,
977
+ input_noise_scale=args.input_noise_scale,
978
+ color_correction=args.color_correction
979
+ )
980
+
981
+ # Phase 2: Upscale
982
+ ctx = upscale_all_batches(
983
+ runner, ctx=ctx, debug=debug, progress_callback=None,
984
+ seed=args.seed,
985
+ latent_noise_scale=args.latent_noise_scale,
986
+ cache_model=cache_dit
987
+ )
988
+
989
+ # Phase 3: Decode
990
+ ctx = decode_all_batches(
991
+ runner, ctx=ctx, debug=debug, progress_callback=None,
992
+ cache_model=cache_vae
993
+ )
994
+
995
+ # Phase 4: Post-process
996
+ ctx = postprocess_all_batches(
997
+ ctx=ctx, debug=debug, progress_callback=None,
998
+ color_correction=args.color_correction,
999
+ prepend_frames=0, # Worker mode handles this in main process
1000
+ temporal_overlap=args.temporal_overlap,
1001
+ batch_size=args.batch_size
1002
+ )
1003
+
1004
+ result_tensor = ctx['final_video']
1005
+
1006
+ # Convert to CPU and compatible dtype
1007
+ if result_tensor.is_cuda or result_tensor.is_mps:
1008
+ result_tensor = result_tensor.cpu()
1009
+ if result_tensor.dtype in (torch.bfloat16, torch.float8_e4m3fn, torch.float8_e5m2):
1010
+ result_tensor = result_tensor.to(torch.float32)
1011
+
1012
+ return result_tensor
1013
+
1014
+
1015
+ def _worker_process(
1016
+ proc_idx: int,
1017
+ device_id: str,
1018
+ frames_np: Optional[np.ndarray],
1019
+ shared_args: Dict[str, Any],
1020
+ return_queue: mp.Queue,
1021
+ done_barrier: mp.Barrier,
1022
+ video_info: Optional[Dict[str, Any]] = None
1023
+ ) -> None:
1024
+ """
1025
+ Worker process for multi-GPU upscaling.
1026
+
1027
+ Supports two modes:
1028
+ 1. frames_np provided: Process pre-loaded frames (for images)
1029
+ 2. video_info provided: Stream video segment internally (for videos)
1030
+ - Each worker opens the video, seeks to its assigned range, and streams
1031
+ with internal chunking and model caching for memory efficiency
1032
+
1033
+ Args:
1034
+ proc_idx: Worker index for result ordering
1035
+ device_id: GPU device ID (used for CUDA_VISIBLE_DEVICES inheritance)
1036
+ frames_np: Pre-loaded frames as numpy array, or None for video streaming
1037
+ shared_args: Serialized args namespace as dict
1038
+ return_queue: Queue for returning results to parent
1039
+ done_barrier: Barrier for synchronizing shared memory handoff
1040
+ video_info: Optional dict with 'video_path', 'start_frame', 'end_frame'
1041
+ for video streaming mode
1042
+ """
1043
+ # Create debug instance for this worker
1044
+ worker_debug = Debug(enabled=shared_args["debug"])
1045
+
1046
+ args = argparse.Namespace(**shared_args)
1047
+
1048
+ # Video streaming mode: worker reads and processes its assigned segment
1049
+ if video_info is not None:
1050
+ cap = cv2.VideoCapture(video_info['video_path'])
1051
+ cap.set(cv2.CAP_PROP_POS_FRAMES, video_info['start_frame'])
1052
+
1053
+ segment_frames = video_info['end_frame'] - video_info['start_frame']
1054
+ chunk_size = args.chunk_size if args.chunk_size > 0 else segment_frames
1055
+
1056
+ worker_debug.log(f"GPU {proc_idx}: frames {video_info['start_frame']}-{video_info['end_frame']} "
1057
+ f"({segment_frames} frames, chunks of {chunk_size})",
1058
+ category="generation", force=True)
1059
+
1060
+ # Only GPU 0 uses prepend_frames (applies to video start only)
1061
+ worker_args = argparse.Namespace(**vars(args))
1062
+ if proc_idx != 0:
1063
+ worker_args.prepend_frames = 0
1064
+
1065
+ # Enable model caching within worker only if requested
1066
+ runner_cache = {} if (args.cache_dit or args.cache_vae) else None
1067
+
1068
+ total_chunks = (segment_frames + chunk_size - 1) // chunk_size
1069
+ results = []
1070
+ for result in _stream_video_chunks(
1071
+ cap=cap,
1072
+ frames_to_process=segment_frames,
1073
+ chunk_size=chunk_size,
1074
+ overlap=args.temporal_overlap,
1075
+ args=worker_args,
1076
+ device_id="0",
1077
+ debug=worker_debug,
1078
+ runner_cache=runner_cache,
1079
+ log_progress=total_chunks > 1,
1080
+ total_chunks=total_chunks,
1081
+ log_prefix=f"[GPU {proc_idx}] "
1082
+ ):
1083
+ results.append(result.cpu())
1084
+
1085
+ cap.release()
1086
+ result_tensor = torch.cat(results, dim=0) if results else torch.empty(0, dtype=torch.float32)
1087
+
1088
+ # Pre-loaded frames mode (original behavior)
1089
+ else:
1090
+ frames_tensor = torch.from_numpy(frames_np).to(torch.float16)
1091
+ result_tensor = _process_frames_core(
1092
+ frames_tensor=frames_tensor,
1093
+ args=args,
1094
+ device_id="0",
1095
+ debug=worker_debug,
1096
+ runner_cache=None
1097
+ )
1098
+
1099
+ # Share tensor memory for efficient cross-process transfer (avoids pickling large arrays)
1100
+ return_queue.put((proc_idx, result_tensor.share_memory_()))
1101
+
1102
+ # Wait for parent to copy shared tensors before exiting
1103
+ # (shared memory requires creating process to stay alive during access)
1104
+ done_barrier.wait()
1105
+
1106
+
1107
+ def _single_gpu_direct_processing(
1108
+ frames_tensor: torch.Tensor,
1109
+ args: argparse.Namespace,
1110
+ device_id: str,
1111
+ runner_cache: Optional[Dict[str, Any]]
1112
+ ) -> torch.Tensor:
1113
+ """
1114
+ Direct single-GPU processing with model caching support.
1115
+
1116
+ Uses main process and shared runner cache for efficient multi-file processing.
1117
+ """
1118
+ return _process_frames_core(
1119
+ frames_tensor=frames_tensor,
1120
+ args=args,
1121
+ device_id=device_id,
1122
+ debug=debug,
1123
+ runner_cache=runner_cache
1124
+ )
1125
+
1126
+
1127
+ def _gpu_processing(
1128
+ frames_tensor: Optional[torch.Tensor],
1129
+ device_list: List[str],
1130
+ args: argparse.Namespace,
1131
+ video_info: Optional[Dict[str, Any]] = None
1132
+ ) -> torch.Tensor:
1133
+ """
1134
+ Orchestrate multi-GPU parallel video upscaling with temporal overlap blending.
1135
+
1136
+ Supports two modes:
1137
+ 1. video_info provided: Workers stream their assigned video segments internally
1138
+ (each GPU reads and processes its frame range with internal chunking)
1139
+ 2. frames_tensor provided: Workers process pre-loaded frame chunks
1140
+ (non streaming behavior for images or pre-loaded videos)
1141
+
1142
+ Args:
1143
+ frames_tensor: Input frames [T, H, W, C] or None if using video_info mode
1144
+ device_list: List of device IDs as strings (e.g., ["0", "1"])
1145
+ args: Parsed command-line arguments containing all processing settings
1146
+ video_info: Optional dict with 'video_path', 'start_frame', 'frames_to_process'
1147
+ for streaming mode where workers read video directly
1148
+
1149
+ Returns:
1150
+ Upscaled frames tensor [T', H', W', C], Float32, range [0,1]
1151
+ """
1152
+ num_devices = len(device_list)
1153
+ overlap = args.temporal_overlap
1154
+
1155
+ return_queue = mp.Queue(maxsize=0)
1156
+ done_barrier = mp.Barrier(num_devices + 1)
1157
+ workers = []
1158
+ shared_args = vars(args).copy()
1159
+
1160
+ # Video streaming mode: distribute frame ranges to workers
1161
+ if video_info is not None:
1162
+ total_frames = video_info['frames_to_process']
1163
+ start_frame = video_info['start_frame']
1164
+ video_path = video_info['video_path']
1165
+
1166
+ base_per_gpu = total_frames // num_devices
1167
+ remainder = total_frames % num_devices
1168
+
1169
+ current_start = start_frame
1170
+ for idx, device_id in enumerate(device_list):
1171
+ gpu_frames = base_per_gpu + (1 if idx < remainder else 0)
1172
+ gpu_end = current_start + gpu_frames
1173
+
1174
+ # Add overlap frames for blending (except last GPU)
1175
+ if idx < num_devices - 1 and overlap > 0:
1176
+ gpu_end = min(gpu_end + overlap, start_frame + total_frames)
1177
+
1178
+ worker_video_info = {
1179
+ 'video_path': video_path,
1180
+ 'start_frame': current_start,
1181
+ 'end_frame': gpu_end,
1182
+ }
1183
+
1184
+ os.environ["CUDA_VISIBLE_DEVICES"] = device_id
1185
+ p = mp.Process(
1186
+ target=_worker_process,
1187
+ args=(idx, device_id, None, shared_args, return_queue, done_barrier),
1188
+ kwargs={'video_info': worker_video_info}
1189
+ )
1190
+ p.start()
1191
+ workers.append(p)
1192
+
1193
+ current_start += gpu_frames
1194
+
1195
+ # Pre-loaded frames mode (original behavior for images or non-streaming)
1196
+ else:
1197
+ total_frames = frames_tensor.shape[0]
1198
+
1199
+ if overlap > 0 and num_devices > 1:
1200
+ chunk_with_overlap = total_frames // num_devices + overlap
1201
+ if args.batch_size > 1:
1202
+ chunk_with_overlap = ((chunk_with_overlap + args.batch_size - 1) // args.batch_size) * args.batch_size
1203
+ base_chunk_size = chunk_with_overlap - overlap
1204
+
1205
+ chunks = []
1206
+ for i in range(num_devices):
1207
+ start_idx = i * base_chunk_size
1208
+ if i == num_devices - 1:
1209
+ end_idx = total_frames
1210
+ else:
1211
+ end_idx = min(start_idx + chunk_with_overlap, total_frames)
1212
+ chunks.append(frames_tensor[start_idx:end_idx])
1213
+ else:
1214
+ chunks = torch.chunk(frames_tensor, num_devices, dim=0)
1215
+
1216
+ for idx, (device_id, chunk_tensor) in enumerate(zip(device_list, chunks)):
1217
+ os.environ["CUDA_VISIBLE_DEVICES"] = device_id
1218
+ p = mp.Process(
1219
+ target=_worker_process,
1220
+ args=(idx, device_id, chunk_tensor.cpu().numpy(), shared_args, return_queue, done_barrier),
1221
+ )
1222
+ p.start()
1223
+ workers.append(p)
1224
+
1225
+ # Collect results before joining to prevent deadlock
1226
+ # Tensors arrive via shared memory - copy to numpy while workers still alive
1227
+ results_np = [None] * num_devices
1228
+ collected = 0
1229
+ while collected < num_devices:
1230
+ proc_idx, result_tensor = return_queue.get()
1231
+ results_np[proc_idx] = result_tensor.numpy()
1232
+ collected += 1
1233
+
1234
+ # Release workers now that shared tensors are copied
1235
+ done_barrier.wait()
1236
+
1237
+ # Now safe to join
1238
+ for p in workers:
1239
+ p.join()
1240
+
1241
+ # Concatenate results with overlap blending using shared function
1242
+ if args.temporal_overlap > 0 and num_devices > 1:
1243
+ overlap = args.temporal_overlap
1244
+ result_tensor = None
1245
+
1246
+ for idx, res_np in enumerate(results_np):
1247
+ chunk_tensor = torch.from_numpy(res_np).to(torch.float32)
1248
+
1249
+ if idx == 0:
1250
+ # First chunk: keep all frames
1251
+ result_tensor = chunk_tensor
1252
+ else:
1253
+ # Subsequent chunks: blend overlapping region with accumulated result
1254
+ if chunk_tensor.shape[0] > overlap and result_tensor.shape[0] >= overlap:
1255
+ # Get overlapping regions
1256
+ prev_tail = result_tensor[-overlap:] # Last N frames from accumulated result
1257
+ cur_head = chunk_tensor[:overlap] # First N frames from current chunk
1258
+
1259
+ # Blend using shared function
1260
+ blended = blend_overlapping_frames(prev_tail, cur_head, overlap)
1261
+
1262
+ # Replace tail of result with blended frames, then append rest of chunk
1263
+ result_tensor = torch.cat([
1264
+ result_tensor[:-overlap], # Everything except the tail
1265
+ blended, # Blended overlapping frames
1266
+ chunk_tensor[overlap:] # Non-overlapping part of current chunk
1267
+ ], dim=0)
1268
+ else:
1269
+ # Edge case: chunk too small, just append non-overlapping part
1270
+ if chunk_tensor.shape[0] > overlap:
1271
+ result_tensor = torch.cat([result_tensor, chunk_tensor[overlap:]], dim=0)
1272
+
1273
+ if result_tensor is None:
1274
+ result_tensor = torch.from_numpy(results_np[0]).to(torch.float32)
1275
+ else:
1276
+ # Simple concatenation without overlap
1277
+ result_tensor = torch.from_numpy(np.concatenate(results_np, axis=0)).to(torch.float32)
1278
+
1279
+ # Handle prepend_frames removal (multi-GPU safe - done after all workers complete)
1280
+ if args.prepend_frames > 0:
1281
+ if args.prepend_frames < result_tensor.shape[0]:
1282
+ debug.log(f"Removing {args.prepend_frames} prepended frames from output", category="generation")
1283
+ result_tensor = result_tensor[args.prepend_frames:]
1284
+ else:
1285
+ debug.log(f"prepend_frames ({args.prepend_frames}) >= total frames ({result_tensor.shape[0]}), skipping removal",
1286
+ level="WARNING", category="generation", force=True)
1287
+
1288
+ return result_tensor
1289
+
1290
+
1291
+ # =============================================================================
1292
+ # Argument Parsing
1293
+ # =============================================================================
1294
+
1295
+ def parse_arguments() -> argparse.Namespace:
1296
+ """
1297
+ Parse and validate command-line arguments for SeedVR2 CLI.
1298
+
1299
+ Configures all available options including model selection, processing parameters,
1300
+ memory optimization settings, and output configuration.
1301
+
1302
+ Returns:
1303
+ Parsed arguments namespace with all CLI parameters
1304
+
1305
+ Note:
1306
+ - cuda_device argument only available on non-macOS systems
1307
+ - Default model directory resolves to "models/SEEDVR2" if not specified
1308
+ """
1309
+
1310
+ # Get the actual invocation path for usage examples
1311
+ invocation = sys.argv[0]
1312
+
1313
+ # Multi-line usage examples for --help
1314
+ usage_examples = f"""
1315
+ Examples:
1316
+
1317
+ Basic image upscaling:
1318
+ python {invocation} image.jpg
1319
+
1320
+ Basic video upscaling with temporal consistency:
1321
+ python {invocation} video.mp4 --resolution 720 --batch_size 33
1322
+
1323
+ Streaming mode for long videos with 10-bit video output (requires FFMPEG):
1324
+ python {invocation} long_video.mp4 --resolution 1080 --batch_size 33 --chunk_size 330 --temporal_overlap 3 --video_backend ffmpeg --10bit
1325
+
1326
+ Multi-GPU processing with temporal overlap:
1327
+ python {invocation} video.mp4 --cuda_device 0,1 --resolution 1080 --batch_size 81 --uniform_batch_size --temporal_overlap 3 --prepend_frames 4
1328
+
1329
+ Memory-optimized for low VRAM (8GB):
1330
+ python {invocation} image.png --dit_model seedvr2_ema_3b-Q8_0.gguf --blocks_to_swap 32 --swap_io_components --dit_offload_device cpu --vae_offload_device cpu
1331
+
1332
+ High resolution with VAE tiling:
1333
+ python {invocation} video.mp4 --resolution 1440 --batch_size 31 --uniform_batch_size --temporal_overlap 3 --vae_encode_tiled --vae_decode_tiled
1334
+
1335
+ Batch directory processing:
1336
+ python {invocation} media_folder/ --output processed/ --cuda_device 0 --cache_dit --cache_vae --dit_offload_device cpu --vae_offload_device cpu --resolution 1080 --max_resolution 1920
1337
+ """
1338
+
1339
+ parser = argparse.ArgumentParser(
1340
+ description="SeedVR2 Video Upscaler - CLI for high-quality image/video upscaling and batch processing",
1341
+ epilog=usage_examples,
1342
+ formatter_class=argparse.RawDescriptionHelpFormatter,
1343
+ allow_abbrev=False
1344
+ )
1345
+
1346
+ # Input/Output
1347
+ io_group = parser.add_argument_group('Input/Output options')
1348
+ io_group.add_argument("input", type=str,
1349
+ help="Input: video file (.mp4, .avi, etc.), image file (.png, .jpg, etc.), or directory")
1350
+ io_group.add_argument("--output", type=str, default=None,
1351
+ help="Output path (default: auto-generated in 'output/' directory)")
1352
+ io_group.add_argument("--output_format", type=str, default=None, choices=["mp4", "png", None],
1353
+ help="Output format: 'mp4' (video) or 'png' (image sequence). Default: auto-detect from input type")
1354
+ io_group.add_argument("--video_backend", type=str, default="opencv", choices=["opencv", "ffmpeg"],
1355
+ help="Video encoder backend: 'opencv' (default) or 'ffmpeg' (requires ffmpeg in PATH)")
1356
+ io_group.add_argument("--10bit", dest="use_10bit", action="store_true",
1357
+ help="Save 10-bit video with x265 codec (reduces banding). Without this flag, "
1358
+ "ffmpeg uses x264 for maximum compatibility. Requires --video_backend ffmpeg")
1359
+ io_group.add_argument("--model_dir", type=str, default=None,
1360
+ help=f"Model directory (default: ./models/{SEEDVR2_FOLDER_NAME})")
1361
+
1362
+ # Model Selection
1363
+ model_group = parser.add_argument_group('Model selection')
1364
+ model_group.add_argument("--dit_model", type=str, default=DEFAULT_DIT,
1365
+ choices=get_available_dit_models(),
1366
+ help="DiT model to use. Options: 3B (fp16/fp8/GGUF) or 7B (fp16/fp8/GGUF). Default: 3B FP8")
1367
+
1368
+ # Processing Parameters
1369
+ process_group = parser.add_argument_group('Processing parameters')
1370
+ process_group.add_argument("--resolution", type=int, default=1080,
1371
+ help="Target short-side resolution in pixels (default: 1080)")
1372
+ process_group.add_argument("--max_resolution", type=int, default=0,
1373
+ help="Maximum resolution for any edge. Scales down if exceeded. 0 = no limit (default: 0)")
1374
+ process_group.add_argument("--batch_size", type=int, default=5,
1375
+ help="Frames per batch (must follow 4n+1: 1, 5, 9, 13, 17, 21,...). "
1376
+ "Ideally matches shot length for best temporal consistency. Higher values improve "
1377
+ "quality and speed but require more VRAM. Default: 5")
1378
+ process_group.add_argument("--uniform_batch_size", action="store_true",
1379
+ help="Pad final batch to match batch_size. Prevents temporal artifacts caused by small "
1380
+ "final batches. Add extra compute but recommended for optimal quality.")
1381
+ process_group.add_argument("--seed", type=int, default=42,
1382
+ help="Random seed for reproducibility (default: 42)")
1383
+ process_group.add_argument("--skip_first_frames", type=int, default=0,
1384
+ help="Skip N initial frames (default: 0)")
1385
+ process_group.add_argument("--load_cap", type=int, default=0,
1386
+ help="Load maximum N frames from video. 0 = load all (default: 0)")
1387
+ process_group.add_argument("--chunk_size", type=int, default=0,
1388
+ help="Frames per chunk for streaming mode. When > 0, processes video in "
1389
+ "memory-bounded chunks of N frames. 0 = load all frames at once (default: 0)")
1390
+ process_group.add_argument("--prepend_frames", type=int, default=0,
1391
+ help="Prepend N reversed frames to reduce start artifacts (auto-removed). Default: 0")
1392
+ process_group.add_argument("--temporal_overlap", type=int, default=0,
1393
+ help="Frames to overlap between batches/GPUs for smooth blending (default: 0)")
1394
+
1395
+ # Quality Control
1396
+ quality_group = parser.add_argument_group('Quality control')
1397
+ quality_group.add_argument("--color_correction", type=str, default="lab",
1398
+ choices=["lab", "wavelet", "wavelet_adaptive", "hsv", "adain", "none"],
1399
+ help="Color correction method: 'lab' (perceptual color matching, recommended), 'wavelet' (frequency-based), "
1400
+ "'wavelet_adaptive' (wavelet + saturation correction), 'hsv' (hue-conditional), 'adain' (statistical transfer), "
1401
+ "'none' (disabled) (default: lab)")
1402
+ quality_group.add_argument("--input_noise_scale", type=float, default=0.0,
1403
+ help="Input noise injection scale (0.0-1.0). Adds variation to input images (default: 0.0)")
1404
+ quality_group.add_argument("--latent_noise_scale", type=float, default=0.0,
1405
+ help="Latent noise injection scale (0.0-1.0). Adds variation to latent space (default: 0.0)")
1406
+
1407
+ # Device Management
1408
+ device_group = parser.add_argument_group('Device management')
1409
+ if platform.system() != "Darwin":
1410
+ device_group.add_argument("--cuda_device", type=str, default=None,
1411
+ help="CUDA device(s): single '0' or multi-GPU '0,1,2'. Default: device 0")
1412
+ device_group.add_argument("--dit_offload_device", type=str, default="none",
1413
+ help="DiT offload device when idle: 'none' (keep on GPU), 'cpu' (offload to RAM), or GPU ID. "
1414
+ "Frees VRAM between phases. Required for BlockSwap. Default: none")
1415
+ device_group.add_argument("--vae_offload_device", type=str, default="none",
1416
+ help="VAE offload device when idle: 'none', 'cpu', or GPU ID. Frees VRAM between phases. Default: none")
1417
+ device_group.add_argument("--tensor_offload_device", type=str, default="cpu",
1418
+ help="Intermediate tensor storage: 'cpu' (recommended), 'none' (keep on GPU), or GPU ID. Default: cpu")
1419
+
1420
+ # Memory Optimization (BlockSwap)
1421
+ blockswap_group = parser.add_argument_group('Memory optimization (BlockSwap)')
1422
+ blockswap_group.add_argument("--blocks_to_swap", type=int, default=0,
1423
+ help="Transformer blocks to swap for VRAM savings. 0-32 (3B) or 0-36 (7B). "
1424
+ "Requires --dit_offload_device. Not available on macOS. Default: 0 (disabled)")
1425
+ blockswap_group.add_argument("--swap_io_components", action="store_true",
1426
+ help="Offload DiT I/O layers for extra VRAM savings. Requires --dit_offload_device. "
1427
+ "Not available on macOS")
1428
+
1429
+ # VAE Tiling
1430
+ vae_group = parser.add_argument_group('VAE tiling (for high resolution upscale)')
1431
+ vae_group.add_argument("--vae_encode_tiled", action="store_true",
1432
+ help="Enable VAE encode tiling to reduce VRAM during encoding")
1433
+ vae_group.add_argument("--vae_encode_tile_size", type=int, default=1024,
1434
+ help="VAE encode tile size in pixels (default: 1024). Applied to both height and width. Only used if --vae_encode_tiled is set")
1435
+ vae_group.add_argument("--vae_encode_tile_overlap", type=int, default=128,
1436
+ help="VAE encode tile overlap in pixels (default: 128). Reduces visible seams between tiles. Only used if --vae_encode_tiled is set")
1437
+ vae_group.add_argument("--vae_decode_tiled", action="store_true",
1438
+ help="Enable VAE decode tiling to reduce VRAM during decoding")
1439
+ vae_group.add_argument("--vae_decode_tile_size", type=int, default=1024,
1440
+ help="VAE decode tile size in pixels (default: 1024). Applied to both height and width. Only used if --vae_decode_tiled is set")
1441
+ vae_group.add_argument("--vae_decode_tile_overlap", type=int, default=128,
1442
+ help="VAE decode tile overlap in pixels (default: 128). Reduces visible seams between tiles. Only used if --vae_decode_tiled is set")
1443
+ vae_group.add_argument("--tile_debug", type=str, default="false", choices=["false", "encode", "decode"],
1444
+ help="Visualize tiles: 'false' (default), 'encode', or 'decode'")
1445
+
1446
+ # Performance
1447
+ perf_group = parser.add_argument_group('Performance optimization')
1448
+ perf_group.add_argument("--attention_mode", type=str, default="sdpa",
1449
+ choices=["sdpa", "flash_attn_2", "flash_attn_3", "sageattn_2", "sageattn_3"],
1450
+ help="Attention backend: 'sdpa' (default), 'flash_attn_2', 'flash_attn_3', 'sageattn_2', or 'sageattn_3' (Blackwell GPUs)")
1451
+ perf_group.add_argument("--compile_dit", action="store_true",
1452
+ help="Enable torch.compile for DiT model (20-40%% speedup, requires PyTorch 2.0+ and Triton)")
1453
+ perf_group.add_argument("--compile_vae", action="store_true",
1454
+ help="Enable torch.compile for VAE model (15-25%% speedup, requires PyTorch 2.0+ and Triton)")
1455
+ perf_group.add_argument("--compile_backend", type=str, default="inductor", choices=["inductor", "cudagraphs"],
1456
+ help="Compilation backend: 'inductor' (full optimization with Triton) or 'cudagraphs' (lightweight, no kernel optimization) (default: inductor)")
1457
+ perf_group.add_argument("--compile_mode", type=str, default="default", choices=["default", "reduce-overhead", "max-autotune", "max-autotune-no-cudagraphs"],
1458
+ help="Optimization level: 'default' (fast compilation), 'reduce-overhead' (lower overhead), 'max-autotune' (best runtime, slow compilation), "
1459
+ "'max-autotune-no-cudagraphs' (like max-autotune without cudagraphs) (default: default)")
1460
+ perf_group.add_argument("--compile_fullgraph", action="store_true",
1461
+ help="Compile entire model as single graph (faster but less flexible). May fail with dynamic shapes (default: False)")
1462
+ perf_group.add_argument("--compile_dynamic", action="store_true",
1463
+ help="Handle varying input shapes without recompilation. Useful for different resolutions/batch sizes (default: False)")
1464
+ perf_group.add_argument("--compile_dynamo_cache_size_limit", type=int, default=64,
1465
+ help="Max cached compiled versions per function. Increase when using many different input shapes. Higher uses more memory (default: 64)")
1466
+ perf_group.add_argument("--compile_dynamo_recompile_limit", type=int, default=128,
1467
+ help="Max recompilation attempts before fallback to eager mode. Safety limit to prevent compilation loops (default: 128)")
1468
+
1469
+ # Model Caching (for batch processing)
1470
+ cache_group = parser.add_argument_group('Model caching (batch processing)')
1471
+ cache_group.add_argument("--cache_dit", action="store_true",
1472
+ help="Keep DiT model in memory between generations. Works with single-GPU directory processing "
1473
+ "or multi-GPU streaming (--chunk_size). Requires --dit_offload_device")
1474
+ cache_group.add_argument("--cache_vae", action="store_true",
1475
+ help="Keep VAE model in memory between generations. Works with single-GPU directory processing "
1476
+ "or multi-GPU streaming (--chunk_size). Requires --vae_offload_device")
1477
+
1478
+ # Debugging
1479
+ debug_group = parser.add_argument_group('Debugging')
1480
+ debug_group.add_argument("--debug", action="store_true",
1481
+ help="Enable verbose debug logging")
1482
+
1483
+ # Auto-show help if no arguments provided
1484
+ if len(sys.argv) == 1:
1485
+ sys.argv.append('--help')
1486
+
1487
+ return parser.parse_args()
1488
+
1489
+
1490
+ # =============================================================================
1491
+ # Main Entry Point
1492
+ # =============================================================================
1493
+
1494
+ def main() -> None:
1495
+ """
1496
+ Main entry point for SeedVR2 Video Upscaler CLI.
1497
+
1498
+ Orchestrates the complete upscaling workflow:
1499
+ 1. Parse and validate command-line arguments
1500
+ 2. Extract frames from input video/image(s)
1501
+ 3. Download required models if not cached
1502
+ 4. Process frames on single or multiple GPUs
1503
+ 5. Save results as video or PNG sequence
1504
+ 6. Report timing and FPS (calculated from total wall-clock time)
1505
+
1506
+ Error handling:
1507
+ - Validates tile configuration before processing
1508
+ - Provides detailed error messages with traceback
1509
+ - Ensures proper cleanup on exit (VRAM automatically freed)
1510
+
1511
+ Raises:
1512
+ SystemExit: On argument validation failure or processing error
1513
+ """
1514
+ # Parse arguments
1515
+ args = parse_arguments()
1516
+
1517
+ # Update debug instance with --debug flag
1518
+ debug.enabled = args.debug
1519
+
1520
+ # print header
1521
+ debug.print_header(cli=True)
1522
+
1523
+ debug.log("Arguments:", category="setup")
1524
+ for key, value in vars(args).items():
1525
+ debug.log(f"{key}: {value}", category="none", indent_level=1)
1526
+
1527
+ if args.vae_encode_tiled and args.vae_encode_tile_overlap >= args.vae_encode_tile_size:
1528
+ debug.log(f"VAE encode tile overlap ({args.vae_encode_tile_overlap}) must be smaller than tile size ({args.vae_encode_tile_size})", level="ERROR", category="vae", force=True)
1529
+ sys.exit(1)
1530
+
1531
+ if args.vae_decode_tiled and args.vae_decode_tile_overlap >= args.vae_decode_tile_size:
1532
+ debug.log(f"VAE decode tile overlap ({args.vae_decode_tile_overlap}) must be smaller than tile size ({args.vae_decode_tile_size})", level="ERROR", category="vae", force=True)
1533
+ sys.exit(1)
1534
+
1535
+ # Validate ffmpeg availability if selected
1536
+ if args.video_backend == "ffmpeg" and shutil.which("ffmpeg") is None:
1537
+ debug.log("--video_backend ffmpeg requires ffmpeg in PATH. Install ffmpeg or use --video_backend opencv",
1538
+ level="ERROR", category="setup", force=True)
1539
+ sys.exit(1)
1540
+
1541
+ # Inform about caching defaults
1542
+ if args.cache_dit and args.dit_offload_device == "none":
1543
+ offload_target = "system memory (CPU)" if get_gpu_backend() != "mps" else "unified memory"
1544
+ debug.log(
1545
+ f"DiT caching enabled: Using default {offload_target} for offload. "
1546
+ "Set --dit_offload_device explicitly to use a different device.",
1547
+ category="cache", force=True
1548
+ )
1549
+
1550
+ if args.cache_vae and args.vae_offload_device == "none":
1551
+ offload_target = "system memory (CPU)" if get_gpu_backend() != "mps" else "unified memory"
1552
+ debug.log(
1553
+ f"VAE caching enabled: Using default {offload_target} for offload. "
1554
+ "Set --vae_offload_device explicitly to use a different device.",
1555
+ category="cache", force=True
1556
+ )
1557
+
1558
+ if args.debug:
1559
+ if platform.system() == "Darwin":
1560
+ debug.log("You are running on macOS and will use the MPS backend!", category="info", force=True)
1561
+ else:
1562
+ # Show actual CUDA device visibility
1563
+ debug.log(f"CUDA_VISIBLE_DEVICES: {os.environ.get('CUDA_VISIBLE_DEVICES', 'Not set (all)')}", category="device")
1564
+ if is_cuda_available():
1565
+ debug.log(f"torch.cuda.device_count(): {torch.cuda.device_count()}", category="device")
1566
+ debug.log(f"Using device index 0 inside script (mapped to selected GPU)", category="device")
1567
+
1568
+ try:
1569
+ start_time = time.time()
1570
+
1571
+ # Parse GPU list
1572
+ if platform.system() == "Darwin":
1573
+ device_list = ["0"]
1574
+ else:
1575
+ if args.cuda_device:
1576
+ device_list = [d.strip() for d in str(args.cuda_device).split(',') if d.strip()]
1577
+ else:
1578
+ device_list = ["0"]
1579
+ if args.debug:
1580
+ debug.log(f"Using devices: {device_list}", category="device")
1581
+
1582
+ # Download models once before processing
1583
+ if not download_weight(dit_model=args.dit_model, vae_model=DEFAULT_VAE, model_dir=args.model_dir, debug=debug):
1584
+ debug.log("Failed to download required models. Check console output above.", level="ERROR", category="download", force=True)
1585
+ sys.exit(1)
1586
+
1587
+ # Determine input type and process accordingly
1588
+ input_type = get_input_type(args.input)
1589
+
1590
+ # Track total frames for FPS calculation (time tracked via start_time)
1591
+ total_frames_processed = 0
1592
+
1593
+ # Track if output format was user-specified or auto-detected
1594
+ format_auto_detected = args.output_format is None
1595
+
1596
+ if input_type == 'directory':
1597
+ media_files = get_media_files(args.input)
1598
+ if not media_files:
1599
+ debug.log(f"No video or image files found in directory: {args.input}",
1600
+ level="ERROR", category="file", force=True)
1601
+ sys.exit(1)
1602
+
1603
+ debug.log(f"Found {len(media_files)} media files to process", category="file", force=True)
1604
+
1605
+ # Multi-GPU caching requires streaming (workers cache within their chunk loops)
1606
+ if (args.cache_dit or args.cache_vae) and len(device_list) > 1 and args.chunk_size <= 0:
1607
+ debug.log(
1608
+ "Model caching requires streaming mode (--chunk_size > 0) for multi-GPU. "
1609
+ "Disabling caching for this run.",
1610
+ level="WARNING", category="cache", force=True
1611
+ )
1612
+ args.cache_dit = False
1613
+ args.cache_vae = False
1614
+
1615
+ # Single-GPU: runner_cache persists across files; multi-GPU: workers cache internally
1616
+ runner_cache = {} if (args.cache_dit or args.cache_vae) and len(device_list) == 1 else None
1617
+
1618
+ for idx, file_path in enumerate(media_files, 1):
1619
+ # Visual separation between files (except before first file)
1620
+ if idx > 1:
1621
+ debug.log("", category="none", force=True)
1622
+ debug.log("━" * 60, category="none", force=True)
1623
+ debug.log("", category="none", force=True)
1624
+
1625
+ debug.log(f"Processing file {idx}/{len(media_files)}", category="generation", force=True)
1626
+
1627
+ # Auto-detect format per file if not user-specified
1628
+ if format_auto_detected:
1629
+ file_type = get_input_type(file_path)
1630
+ file_output_format = "mp4" if file_type == "video" else "png"
1631
+ else:
1632
+ file_output_format = args.output_format
1633
+
1634
+ # Temporarily override args.output_format for this file
1635
+ original_format = args.output_format
1636
+ args.output_format = file_output_format
1637
+
1638
+ # generate_output_path handles None gracefully with "outputs" default
1639
+ output_path = generate_output_path(file_path, file_output_format, args.output,
1640
+ input_type=get_input_type(file_path), from_directory=True)
1641
+
1642
+ # Process with explicit output path and runner cache
1643
+ frames = process_single_file(file_path, args, device_list, output_path,
1644
+ format_auto_detected=format_auto_detected,
1645
+ runner_cache=runner_cache)
1646
+ total_frames_processed += frames
1647
+
1648
+ # Restore original format
1649
+ args.output_format = original_format
1650
+
1651
+ elif input_type in ("video", "image"):
1652
+ # Auto-detect output format for single file if not specified
1653
+ if format_auto_detected:
1654
+ args.output_format = "mp4" if input_type == "video" else "png"
1655
+
1656
+ # Caching: single-GPU streaming uses runner_cache, multi-GPU streaming workers cache internally
1657
+ runner_cache = None
1658
+ streaming = args.chunk_size > 0
1659
+
1660
+ if args.cache_dit or args.cache_vae:
1661
+ if len(device_list) > 1:
1662
+ if not streaming:
1663
+ debug.log(
1664
+ "Model caching requires streaming mode (--chunk_size > 0) for multi-GPU. "
1665
+ "Disabling caching for this run.",
1666
+ level="WARNING", category="cache", force=True
1667
+ )
1668
+ args.cache_dit = False
1669
+ args.cache_vae = False
1670
+ elif streaming:
1671
+ runner_cache = {}
1672
+ else:
1673
+ debug.log(
1674
+ "Model caching has no benefit for single file processing (only useful for directories or streaming mode). "
1675
+ "Consider removing --cache_dit/--cache_vae for single files.",
1676
+ category="tip", force=True
1677
+ )
1678
+
1679
+ frames = process_single_file(args.input, args, device_list, args.output,
1680
+ format_auto_detected=format_auto_detected,
1681
+ runner_cache=runner_cache)
1682
+ total_frames_processed += frames
1683
+
1684
+ else:
1685
+ debug.log(f"Unsupported input type: {args.input}", level="ERROR", category="file", force=True)
1686
+ sys.exit(1)
1687
+
1688
+ # Calculate total execution time
1689
+ total_time = time.time() - start_time
1690
+
1691
+ debug.log("", category="none", force=True)
1692
+ debug.log(f"All upscaling processes completed successfully in {total_time:.2f}s", category="success", force=True)
1693
+
1694
+ # Calculate and display FPS based on overall wall-clock time
1695
+ if total_time > 0 and total_frames_processed > 0:
1696
+ fps = total_frames_processed / total_time
1697
+ debug.log(f"Average FPS: {fps:.2f} frames/sec", category="timing", force=True)
1698
+
1699
+ except Exception as e:
1700
+ debug.log(f"Error during processing: {e}", level="ERROR", category="generation", force=True)
1701
+ import traceback
1702
+ traceback.print_exc()
1703
+ sys.exit(1)
1704
+
1705
+ finally:
1706
+ debug.log(f"Process {os.getpid()} terminating - VRAM will be automatically freed", category="cleanup", force=True)
1707
+
1708
+ # print footer
1709
+ debug.print_footer()
1710
+
1711
+ if __name__ == "__main__":
1712
+ main()
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/neg_emb.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6a43e5800ef2354f1c156d27535834da055cbec8248298b8923492bba2076581
3
+ size 656540
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/pos_emb.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fa07a14844314772266b66c3b95deb0027696d8fe7065721263db5176f45d799
3
+ size 595100
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/pyproject.toml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "seedvr2_videoupscaler"
3
+ description = "SeedVR2 official ComfyUI integration: ByteDance-Seed's one-step diffusion-based video/image upscaling with memory-efficient inference"
4
+ version = "2.5.24"
5
+ authors = [
6
+ {name = "numz"},
7
+ {name = "adrientoupet"}
8
+ ]
9
+ license = {file = "LICENSE"}
10
+ classifiers = [
11
+ "Operating System :: OS Independent"
12
+ ]
13
+ dependencies = [
14
+ "torch",
15
+ "torchvision",
16
+ "safetensors",
17
+ "numpy",
18
+ "tqdm",
19
+ "psutil",
20
+ "einops",
21
+ "omegaconf>=2.3.0",
22
+ "diffusers>=0.33.1",
23
+ "peft>=0.17.0",
24
+ "rotary_embedding_torch>=0.5.3",
25
+ "opencv-python",
26
+ "gguf",
27
+ "matplotlib"
28
+ ]
29
+
30
+ [project.urls]
31
+ Repository = "https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler"
32
+ Documentation = "https://www.youtube.com/@AInVFX"
33
+ "Bug Tracker" = "https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/issues"
34
+ Forum = "https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler/discussions"
35
+
36
+ [tool.comfy]
37
+ PublisherId = "ainvfx"
38
+ DisplayName = "ComfyUI-SeedVR2_VideoUpscaler"
39
+ Icon = "https://raw.githubusercontent.com/numz/ComfyUI-SeedVR2_VideoUpscaler/refs/heads/main/docs/seedvr_logo.png"
40
+ includes = []
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/requirements.txt ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch
2
+ torchvision
3
+ safetensors
4
+ numpy
5
+ tqdm
6
+ psutil
7
+ einops
8
+ omegaconf>=2.3.0
9
+ diffusers>=0.33.1
10
+ peft>=0.17.0
11
+ rotary_embedding_torch>=0.5.3
12
+ opencv-python
13
+ gguf
14
+ matplotlib
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/__init__.py ADDED
File without changes
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/__init__.py ADDED
File without changes
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/cache.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ from typing import Callable
16
+
17
+
18
+ class Cache:
19
+ """Caching reusable args for faster inference"""
20
+
21
+ def __init__(self, disable=False, prefix="", cache=None):
22
+ self.cache = cache if cache is not None else {}
23
+ self.disable = disable
24
+ self.prefix = prefix
25
+
26
+ def __call__(self, key: str, fn: Callable):
27
+ if self.disable:
28
+ return fn()
29
+
30
+ key = self.prefix + key
31
+ try:
32
+ result = self.cache[key]
33
+ except KeyError:
34
+ result = fn()
35
+ self.cache[key] = result
36
+ return result
37
+
38
+ def namespace(self, namespace: str):
39
+ return Cache(
40
+ disable=self.disable,
41
+ prefix=self.prefix + namespace + ".",
42
+ cache=self.cache,
43
+ )
44
+
45
+ def get(self, key: str):
46
+ key = self.prefix + key
47
+ return self.cache[key]
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/config.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Configuration utility functions
17
+ """
18
+
19
+ import importlib
20
+ from typing import Any, Callable, List, Union
21
+ from omegaconf import DictConfig, ListConfig, OmegaConf
22
+ from ..utils.model_registry import MODEL_CLASSES
23
+
24
+ try:
25
+ OmegaConf.register_new_resolver("eval", eval)
26
+ except Exception as e:
27
+ if "already registered" not in str(e):
28
+ raise
29
+
30
+
31
+
32
+ def load_config(path: str, argv: List[str] = None) -> Union[DictConfig, ListConfig]:
33
+ """
34
+ Load a configuration. Will resolve inheritance.
35
+ """
36
+
37
+ #print(path)
38
+ config = OmegaConf.load(path)
39
+ if argv is not None:
40
+ config_argv = OmegaConf.from_dotlist(argv)
41
+ config = OmegaConf.merge(config, config_argv)
42
+ config = resolve_recursive(config, resolve_inheritance)
43
+ return config
44
+
45
+
46
+ def resolve_recursive(
47
+ config: Any,
48
+ resolver: Callable[[Union[DictConfig, ListConfig]], Union[DictConfig, ListConfig]],
49
+ ) -> Any:
50
+ config = resolver(config)
51
+ if isinstance(config, DictConfig):
52
+ for k in config.keys():
53
+ v = config.get(k)
54
+ if isinstance(v, (DictConfig, ListConfig)):
55
+ config[k] = resolve_recursive(v, resolver)
56
+ if isinstance(config, ListConfig):
57
+ for i in range(len(config)):
58
+ v = config.get(i)
59
+ if isinstance(v, (DictConfig, ListConfig)):
60
+ config[i] = resolve_recursive(v, resolver)
61
+ return config
62
+
63
+
64
+ def resolve_inheritance(config: Union[DictConfig, ListConfig]) -> Any:
65
+ """
66
+ Recursively resolve inheritance if the config contains:
67
+ __inherit__: path/to/parent.yaml or a ListConfig of such paths.
68
+ """
69
+ if isinstance(config, DictConfig):
70
+ inherit = config.pop("__inherit__", None)
71
+
72
+ if inherit:
73
+ inherit_list = inherit if isinstance(inherit, ListConfig) else [inherit]
74
+
75
+ parent_config = None
76
+ for parent_path in inherit_list:
77
+ assert isinstance(parent_path, str)
78
+ parent_config = (
79
+ load_config(parent_path)
80
+ if parent_config is None
81
+ else OmegaConf.merge(parent_config, load_config(parent_path))
82
+ )
83
+
84
+ if len(config.keys()) > 0:
85
+ config = OmegaConf.merge(parent_config, config)
86
+ else:
87
+ config = parent_config
88
+ return config
89
+
90
+
91
+ def import_item(path: str, name: str) -> Any:
92
+ """
93
+ Import a python item, checking model registry first.
94
+
95
+ Args:
96
+ path: Module path
97
+ name: Class/function name to import
98
+
99
+ Returns:
100
+ Imported object
101
+ """
102
+ # Simple lookup with path as key
103
+ if path in MODEL_CLASSES:
104
+ return MODEL_CLASSES[path]
105
+
106
+ # Fallback to dynamic import for everything else
107
+ try:
108
+ return getattr(importlib.import_module(path), name)
109
+ except (ImportError, AttributeError) as e:
110
+ raise ImportError(f"Could not import '{name}' from '{path}': {e}")
111
+
112
+
113
+ def create_object(config: DictConfig) -> Any:
114
+ """
115
+ Create an object from config.
116
+ The config is expected to contains the following:
117
+ __object__:
118
+ path: path.to.module
119
+ name: MyClass
120
+ args: as_config | as_params (default to as_config)
121
+ """
122
+
123
+ item = import_item(
124
+ path=config.__object__.path,
125
+ name=config.__object__.name,
126
+ )
127
+ args = config.__object__.get("args", "as_config")
128
+ if args == "as_config":
129
+ return item(config)
130
+ if args == "as_params":
131
+ config = OmegaConf.to_object(config)
132
+ config.pop("__object__")
133
+ return item(**config)
134
+ raise NotImplementedError(f"Unknown args type: {args}")
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/decorators.py ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Decorators.
17
+ """
18
+
19
+ import functools
20
+ import threading
21
+ import time
22
+ from typing import Callable
23
+ import torch
24
+
25
+ from .distributed import barrier_if_distributed, get_global_rank, get_local_rank
26
+ from .logger import get_logger
27
+
28
+ logger = get_logger(__name__)
29
+
30
+
31
+ def log_on_entry(func: Callable) -> Callable:
32
+ """
33
+ Functions with this decorator will log the function name at entry.
34
+ When using multiple decorators, this must be applied innermost to properly capture the name.
35
+ """
36
+
37
+ def log_on_entry_wrapper(*args, **kwargs):
38
+ logger.info(f"Entering {func.__name__}")
39
+ return func(*args, **kwargs)
40
+
41
+ return log_on_entry_wrapper
42
+
43
+
44
+ def barrier_on_entry(func: Callable) -> Callable:
45
+ """
46
+ Functions with this decorator will start executing when all ranks are ready to enter.
47
+ """
48
+
49
+ def barrier_on_entry_wrapper(*args, **kwargs):
50
+ barrier_if_distributed()
51
+ return func(*args, **kwargs)
52
+
53
+ return barrier_on_entry_wrapper
54
+
55
+
56
+ def _conditional_execute_wrapper_factory(execute: bool, func: Callable) -> Callable:
57
+ """
58
+ Helper function for local_rank_zero_only and global_rank_zero_only.
59
+ """
60
+
61
+ def conditional_execute_wrapper(*args, **kwargs):
62
+ # Only execute if needed.
63
+ result = func(*args, **kwargs) if execute else None
64
+ # All GPUs must wait.
65
+ barrier_if_distributed()
66
+ # Return results.
67
+ return result
68
+
69
+ return conditional_execute_wrapper
70
+
71
+
72
+ def _asserted_wrapper_factory(condition: bool, func: Callable, err_msg: str = "") -> Callable:
73
+ """
74
+ Helper function for some functions with special constraints,
75
+ especially functions called by other global_rank_zero_only / local_rank_zero_only ones,
76
+ in case they are wrongly invoked in other scenarios.
77
+ """
78
+
79
+ def asserted_execute_wrapper(*args, **kwargs):
80
+ assert condition, err_msg
81
+ result = func(*args, **kwargs)
82
+ return result
83
+
84
+ return asserted_execute_wrapper
85
+
86
+
87
+ def local_rank_zero_only(func: Callable) -> Callable:
88
+ """
89
+ Functions with this decorator will only execute on local rank zero.
90
+ """
91
+ return _conditional_execute_wrapper_factory(get_local_rank() == 0, func)
92
+
93
+
94
+ def global_rank_zero_only(func: Callable) -> Callable:
95
+ """
96
+ Functions with this decorator will only execute on global rank zero.
97
+ """
98
+ return _conditional_execute_wrapper_factory(get_global_rank() == 0, func)
99
+
100
+
101
+ def assert_only_global_rank_zero(func: Callable) -> Callable:
102
+ """
103
+ Functions with this decorator are only accessible to processes with global rank zero.
104
+ """
105
+ return _asserted_wrapper_factory(
106
+ get_global_rank() == 0, func, err_msg="Not accessible to processes with global_rank != 0"
107
+ )
108
+
109
+
110
+ def assert_only_local_rank_zero(func: Callable) -> Callable:
111
+ """
112
+ Functions with this decorator are only accessible to processes with local rank zero.
113
+ """
114
+ return _asserted_wrapper_factory(
115
+ get_local_rank() == 0, func, err_msg="Not accessible to processes with local_rank != 0"
116
+ )
117
+
118
+
119
+ def new_thread(func: Callable) -> Callable:
120
+ """
121
+ Functions with this decorator will run in a new thread.
122
+ The function will return the thread, which can be joined to wait for completion.
123
+ """
124
+
125
+ def new_thread_wrapper(*args, **kwargs):
126
+ thread = threading.Thread(target=func, args=args, kwargs=kwargs)
127
+ thread.start()
128
+ return thread
129
+
130
+ return new_thread_wrapper
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/__init__.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Diffusion package.
17
+ """
18
+
19
+ from .config import (
20
+ create_sampler_from_config,
21
+ create_sampling_timesteps_from_config,
22
+ create_schedule_from_config,
23
+ )
24
+ from .samplers.base import Sampler
25
+ from .samplers.euler import EulerSampler
26
+ from .schedules.base import Schedule
27
+ from .schedules.lerp import LinearInterpolationSchedule
28
+ from .timesteps.base import SamplingTimesteps, Timesteps
29
+ from .timesteps.sampling.trailing import UniformTrailingSamplingTimesteps
30
+ from .types import PredictionType, SamplingDirection
31
+ from .utils import classifier_free_guidance, classifier_free_guidance_dispatcher, expand_dims
32
+
33
+ __all__ = [
34
+ # Configs
35
+ "create_sampler_from_config",
36
+ "create_sampling_timesteps_from_config",
37
+ "create_schedule_from_config",
38
+ # Schedules
39
+ "Schedule",
40
+ "DiscreteVariancePreservingSchedule",
41
+ "LinearInterpolationSchedule",
42
+ # Samplers
43
+ "Sampler",
44
+ "EulerSampler",
45
+ # Timesteps
46
+ "Timesteps",
47
+ "SamplingTimesteps",
48
+ # Types
49
+ "PredictionType",
50
+ "SamplingDirection",
51
+ "UniformTrailingSamplingTimesteps",
52
+ # Utils
53
+ "classifier_free_guidance",
54
+ "classifier_free_guidance_dispatcher",
55
+ "expand_dims",
56
+ ]
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/config.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Utility functions for creating schedules and samplers from config.
17
+ """
18
+
19
+ import torch
20
+ from omegaconf import DictConfig
21
+
22
+ from .samplers.base import Sampler
23
+ from .samplers.euler import EulerSampler
24
+ from .schedules.base import Schedule
25
+ from .schedules.lerp import LinearInterpolationSchedule
26
+ from .timesteps.base import SamplingTimesteps
27
+ from .timesteps.sampling.trailing import UniformTrailingSamplingTimesteps
28
+
29
+
30
+ def create_schedule_from_config(
31
+ config: DictConfig,
32
+ device: torch.device,
33
+ dtype: torch.dtype = torch.float32,
34
+ ) -> Schedule:
35
+ """
36
+ Create a schedule from configuration.
37
+ """
38
+ if config.type == "lerp":
39
+ return LinearInterpolationSchedule(T=config.get("T", 1.0))
40
+
41
+ raise NotImplementedError
42
+
43
+
44
+ def create_sampler_from_config(
45
+ config: DictConfig,
46
+ schedule: Schedule,
47
+ timesteps: SamplingTimesteps,
48
+ ) -> Sampler:
49
+ """
50
+ Create a sampler from configuration.
51
+ """
52
+ if config.type == "euler":
53
+ return EulerSampler(
54
+ schedule=schedule,
55
+ timesteps=timesteps,
56
+ prediction_type=config.prediction_type,
57
+ )
58
+ raise NotImplementedError
59
+
60
+
61
+ def create_sampling_timesteps_from_config(
62
+ config: DictConfig,
63
+ schedule: Schedule,
64
+ device: torch.device,
65
+ dtype: torch.dtype = torch.float32,
66
+ ) -> SamplingTimesteps:
67
+ if config.type == "uniform_trailing":
68
+ return UniformTrailingSamplingTimesteps(
69
+ T=schedule.T,
70
+ steps=config.steps,
71
+ shift=config.get("shift", 1.0),
72
+ device=device,
73
+ dtype=dtype,
74
+ )
75
+ raise NotImplementedError
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/samplers/base.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Sampler base class.
17
+ """
18
+
19
+ from abc import ABC, abstractmethod
20
+ from dataclasses import dataclass
21
+ from typing import Callable
22
+ import torch
23
+ from tqdm import tqdm
24
+
25
+ from ..schedules.base import Schedule
26
+ from ..timesteps.base import SamplingTimesteps
27
+ from ..types import PredictionType, SamplingDirection
28
+ from ..utils import assert_schedule_timesteps_compatible
29
+
30
+
31
+ @dataclass
32
+ class SamplerModelArgs:
33
+ x_t: torch.Tensor
34
+ t: torch.Tensor
35
+ i: int
36
+
37
+
38
+ class Sampler(ABC):
39
+ """
40
+ Samplers are ODE/SDE solvers.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ schedule: Schedule,
46
+ timesteps: SamplingTimesteps,
47
+ prediction_type: PredictionType,
48
+ return_endpoint: bool = True,
49
+ ):
50
+ assert_schedule_timesteps_compatible(
51
+ schedule=schedule,
52
+ timesteps=timesteps,
53
+ )
54
+ self.schedule = schedule
55
+ self.timesteps = timesteps
56
+ self.prediction_type = prediction_type
57
+ self.return_endpoint = return_endpoint
58
+
59
+ @abstractmethod
60
+ def sample(
61
+ self,
62
+ x: torch.Tensor,
63
+ f: Callable[[SamplerModelArgs], torch.Tensor],
64
+ ) -> torch.Tensor:
65
+ """
66
+ Generate a new sample given the the intial sample x and score function f.
67
+ """
68
+
69
+ def get_next_timestep(
70
+ self,
71
+ t: torch.Tensor,
72
+ ) -> torch.Tensor:
73
+ """
74
+ Get the next sample timestep.
75
+ Support multiple different timesteps t in a batch.
76
+ If no more steps, return out of bound value -1 or T+1.
77
+ """
78
+ T = self.timesteps.T
79
+ steps = len(self.timesteps)
80
+ curr_idx = self.timesteps.index(t)
81
+ next_idx = curr_idx + 1
82
+ bound = -1 if self.timesteps.direction == SamplingDirection.backward else T + 1
83
+
84
+ s = self.timesteps[next_idx.clamp_max(steps - 1)]
85
+ s = s.where(next_idx < steps, bound)
86
+ return s
87
+
88
+ def get_endpoint(
89
+ self,
90
+ pred: torch.Tensor,
91
+ x_t: torch.Tensor,
92
+ t: torch.Tensor,
93
+ ) -> torch.Tensor:
94
+ """
95
+ Get to the endpoint of the probability flow.
96
+ """
97
+ x_0, x_T = self.schedule.convert_from_pred(pred, self.prediction_type, x_t, t)
98
+ return x_0 if self.timesteps.direction == SamplingDirection.backward else x_T
99
+
100
+ def get_progress_bar(self):
101
+ """
102
+ Get progress bar for sampling.
103
+ """
104
+ return tqdm(
105
+ iterable=range(len(self.timesteps) - (0 if self.return_endpoint else 1)),
106
+ dynamic_ncols=True,
107
+ desc=self.__class__.__name__,
108
+ )
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/samplers/euler.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+
16
+ """
17
+ Euler ODE solver.
18
+ """
19
+
20
+ from typing import Callable
21
+ import torch
22
+ from einops import rearrange
23
+ from torch.nn import functional as F
24
+
25
+ from ..types import PredictionType
26
+ from ..utils import expand_dims
27
+ from .base import Sampler, SamplerModelArgs
28
+
29
+
30
+ class EulerSampler(Sampler):
31
+ """
32
+ The Euler method is the simplest ODE solver.
33
+ <https://en.wikipedia.org/wiki/Euler_method>
34
+ """
35
+
36
+ def sample(
37
+ self,
38
+ x: torch.Tensor,
39
+ f: Callable[[SamplerModelArgs], torch.Tensor],
40
+ ) -> torch.Tensor:
41
+ timesteps = self.timesteps.timesteps
42
+ progress = self.get_progress_bar()
43
+ i = 0
44
+
45
+ # Keep native dtype throughout sampling
46
+ # The DiT model already handles dtype internally via compatibility wrapper
47
+ for t, s in zip(timesteps[:-1], timesteps[1:]):
48
+ pred = f(SamplerModelArgs(x, t, i))
49
+
50
+ # Next step
51
+ x = self.step_to(pred, x, t, s)
52
+
53
+ # Clean up temporary tensors
54
+ del pred
55
+
56
+ i += 1
57
+ progress.update()
58
+
59
+ if self.return_endpoint:
60
+ t = timesteps[-1]
61
+ pred = f(SamplerModelArgs(x, t, i))
62
+ x = self.get_endpoint(pred, x, t)
63
+ del pred
64
+ progress.update()
65
+
66
+ return x
67
+
68
+ def step(
69
+ self,
70
+ pred: torch.Tensor,
71
+ x_t: torch.Tensor,
72
+ t: torch.Tensor,
73
+ ) -> torch.Tensor:
74
+ """
75
+ Step to the next timestep.
76
+ """
77
+ return self.step_to(pred, x_t, t, self.get_next_timestep(t))
78
+
79
+ def step_to(
80
+ self,
81
+ pred: torch.Tensor,
82
+ x_t: torch.Tensor,
83
+ t: torch.Tensor,
84
+ s: torch.Tensor,
85
+ ) -> torch.Tensor:
86
+ """
87
+ Steps from x_t at timestep t to x_s at timestep s. Returns x_s.
88
+ """
89
+ t = expand_dims(t, x_t.ndim)
90
+ s = expand_dims(s, x_t.ndim)
91
+ T = self.schedule.T
92
+ # Step from x_t to x_s.
93
+ pred_x_0, pred_x_T = self.schedule.convert_from_pred(pred, self.prediction_type, x_t, t)
94
+ pred_x_s = self.schedule.forward(pred_x_0, pred_x_T, s.clamp(0, T))
95
+ # Clamp x_s to x_0 and x_T if s is out of bound.
96
+ pred_x_s = pred_x_s.where(s >= 0, pred_x_0)
97
+ pred_x_s = pred_x_s.where(s <= T, pred_x_T)
98
+ return pred_x_s
99
+
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/schedules/base.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Schedule base class.
17
+ """
18
+
19
+ from abc import ABC, abstractmethod, abstractproperty
20
+ from typing import Tuple, Union
21
+ import torch
22
+
23
+ from ..types import PredictionType
24
+ from ..utils import expand_dims
25
+
26
+
27
+ class Schedule(ABC):
28
+ """
29
+ Diffusion schedules are uniquely defined by T, A, B:
30
+
31
+ x_t = A(t) * x_0 + B(t) * x_T, where t in [0, T]
32
+
33
+ Schedules can be continuous or discrete.
34
+ """
35
+
36
+ @abstractproperty
37
+ def T(self) -> Union[int, float]:
38
+ """
39
+ Maximum timestep inclusive.
40
+ Schedule is continuous if float, discrete if int.
41
+ """
42
+
43
+ @abstractmethod
44
+ def A(self, t: torch.Tensor) -> torch.Tensor:
45
+ """
46
+ Interpolation coefficient A.
47
+ Returns tensor with the same shape as t.
48
+ """
49
+
50
+ @abstractmethod
51
+ def B(self, t: torch.Tensor) -> torch.Tensor:
52
+ """
53
+ Interpolation coefficient B.
54
+ Returns tensor with the same shape as t.
55
+ """
56
+
57
+ # ----------------------------------------------------
58
+
59
+ def snr(self, t: torch.Tensor) -> torch.Tensor:
60
+ """
61
+ Signal to noise ratio.
62
+ Returns tensor with the same shape as t.
63
+ """
64
+ return (self.A(t) ** 2) / (self.B(t) ** 2)
65
+
66
+ def isnr(self, snr: torch.Tensor) -> torch.Tensor:
67
+ """
68
+ Inverse signal to noise ratio.
69
+ Returns tensor with the same shape as snr.
70
+ Subclass may implement.
71
+ """
72
+ raise NotImplementedError
73
+
74
+ # ----------------------------------------------------
75
+
76
+ def is_continuous(self) -> bool:
77
+ """
78
+ Whether the schedule is continuous.
79
+ """
80
+ return isinstance(self.T, float)
81
+
82
+ def forward(self, x_0: torch.Tensor, x_T: torch.Tensor, t: torch.Tensor) -> torch.Tensor:
83
+ """
84
+ Diffusion forward function.
85
+ """
86
+ t = expand_dims(t, x_0.ndim)
87
+ return self.A(t) * x_0 + self.B(t) * x_T
88
+
89
+ def convert_from_pred(
90
+ self, pred: torch.Tensor, pred_type: PredictionType, x_t: torch.Tensor, t: torch.Tensor
91
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
92
+ """
93
+ Convert from prediction. Return predicted x_0 and x_T.
94
+ """
95
+ t = expand_dims(t, x_t.ndim)
96
+ A_t = self.A(t)
97
+ B_t = self.B(t)
98
+
99
+ if pred_type == PredictionType.x_T:
100
+ pred_x_T = pred
101
+ pred_x_0 = (x_t - B_t * pred_x_T) / A_t
102
+ elif pred_type == PredictionType.x_0:
103
+ pred_x_0 = pred
104
+ pred_x_T = (x_t - A_t * pred_x_0) / B_t
105
+ elif pred_type == PredictionType.v_cos:
106
+ pred_x_0 = A_t * x_t - B_t * pred
107
+ pred_x_T = A_t * pred + B_t * x_t
108
+ elif pred_type == PredictionType.v_lerp:
109
+ pred_x_0 = (x_t - B_t * pred) / (A_t + B_t)
110
+ pred_x_T = (x_t + A_t * pred) / (A_t + B_t)
111
+ else:
112
+ raise NotImplementedError
113
+
114
+ return pred_x_0, pred_x_T
115
+
116
+ def convert_to_pred(
117
+ self, x_0: torch.Tensor, x_T: torch.Tensor, t: torch.Tensor, pred_type: PredictionType
118
+ ) -> torch.FloatTensor:
119
+ """
120
+ Convert to prediction target given x_0 and x_T.
121
+ """
122
+ if pred_type == PredictionType.x_T:
123
+ return x_T
124
+ if pred_type == PredictionType.x_0:
125
+ return x_0
126
+ if pred_type == PredictionType.v_cos:
127
+ t = expand_dims(t, x_0.ndim)
128
+ return self.A(t) * x_T - self.B(t) * x_0
129
+ if pred_type == PredictionType.v_lerp:
130
+ return x_T - x_0
131
+ raise NotImplementedError
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/schedules/lerp.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Linear interpolation schedule (lerp).
17
+ """
18
+
19
+ from typing import Union
20
+ import torch
21
+
22
+ from .base import Schedule
23
+
24
+
25
+ class LinearInterpolationSchedule(Schedule):
26
+ """
27
+ Linear interpolation schedule (lerp) is proposed by flow matching and rectified flow.
28
+ It leads to straighter probability flow theoretically. It is also used by Stable Diffusion 3.
29
+ <https://arxiv.org/abs/2209.03003>
30
+ <https://arxiv.org/abs/2210.02747>
31
+
32
+ x_t = (1 - t) * x_0 + t * x_T
33
+
34
+ Can be either continuous or discrete.
35
+ """
36
+
37
+ def __init__(self, T: Union[int, float] = 1.0):
38
+ self._T = T
39
+
40
+ @property
41
+ def T(self) -> Union[int, float]:
42
+ return self._T
43
+
44
+ def A(self, t: torch.Tensor) -> torch.Tensor:
45
+ return 1 - (t / self.T)
46
+
47
+ def B(self, t: torch.Tensor) -> torch.Tensor:
48
+ return t / self.T
49
+
50
+ # ----------------------------------------------------
51
+
52
+ def isnr(self, snr: torch.Tensor) -> torch.Tensor:
53
+ t = self.T / (1 + snr**0.5)
54
+ t = t if self.is_continuous() else t.round().int()
55
+ return t
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/timesteps/base.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from abc import ABC, abstractmethod
2
+ from typing import Sequence, Union
3
+ import torch
4
+
5
+ from ..types import SamplingDirection
6
+
7
+
8
+ class Timesteps(ABC):
9
+ """
10
+ Timesteps base class.
11
+ """
12
+
13
+ def __init__(self, T: Union[int, float]):
14
+ assert T > 0
15
+ self._T = T
16
+
17
+ @property
18
+ def T(self) -> Union[int, float]:
19
+ """
20
+ Maximum timestep inclusive.
21
+ int if discrete, float if continuous.
22
+ """
23
+ return self._T
24
+
25
+ def is_continuous(self) -> bool:
26
+ """
27
+ Whether the schedule is continuous.
28
+ """
29
+ return isinstance(self.T, float)
30
+
31
+
32
+ class SamplingTimesteps(Timesteps):
33
+ """
34
+ Sampling timesteps.
35
+ It defines the discretization of sampling steps.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ T: Union[int, float],
41
+ timesteps: torch.Tensor,
42
+ direction: SamplingDirection,
43
+ ):
44
+ assert timesteps.ndim == 1
45
+ super().__init__(T)
46
+ self.timesteps = timesteps
47
+ self.direction = direction
48
+
49
+ def __len__(self) -> int:
50
+ """
51
+ Number of sampling steps.
52
+ """
53
+ return len(self.timesteps)
54
+
55
+ def __getitem__(self, idx: Union[int, torch.IntTensor]) -> torch.Tensor:
56
+ """
57
+ The timestep at the sampling step.
58
+ Returns a scalar tensor if idx is int,
59
+ or tensor of the same size if idx is a tensor.
60
+ """
61
+ return self.timesteps[idx]
62
+
63
+ def index(self, t: torch.Tensor) -> torch.Tensor:
64
+ """
65
+ Find index by t.
66
+ Return index of the same shape as t.
67
+ Index is -1 if t not found in timesteps.
68
+ """
69
+ i, j = t.reshape(-1, 1).eq(self.timesteps).nonzero(as_tuple=True)
70
+ idx = torch.full_like(t, fill_value=-1, dtype=torch.int)
71
+ idx.view(-1)[i] = j.int()
72
+ return idx
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/timesteps/sampling/trailing.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ import torch
16
+
17
+ from ...types import SamplingDirection
18
+ from ..base import SamplingTimesteps
19
+
20
+
21
+ class UniformTrailingSamplingTimesteps(SamplingTimesteps):
22
+ """
23
+ Uniform trailing sampling timesteps.
24
+ Defined in (https://arxiv.org/abs/2305.08891)
25
+
26
+ Shift is proposed in SD3 for RF schedule.
27
+ Defined in (https://arxiv.org/pdf/2403.03206) eq.23
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ T: int,
33
+ steps: int,
34
+ shift: float = 1.0,
35
+ device: torch.device = "cpu",
36
+ dtype: torch.dtype = torch.float32,
37
+ ):
38
+ # Create trailing timesteps with specified dtype
39
+ timesteps = torch.arange(1.0, 0.0, -1.0 / steps, device='cpu').to(device=device, dtype=dtype)
40
+
41
+ # Shift timesteps.
42
+ timesteps = shift * timesteps / (1 + (shift - 1) * timesteps)
43
+
44
+ # Scale to T range.
45
+ if isinstance(T, float):
46
+ timesteps = timesteps * T
47
+ else:
48
+ timesteps = timesteps.mul(T + 1).sub(1).round().int()
49
+
50
+ super().__init__(T=T, timesteps=timesteps, direction=SamplingDirection.backward)
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/types.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Type definitions.
17
+ """
18
+
19
+ from enum import Enum
20
+
21
+
22
+ class PredictionType(str, Enum):
23
+ """
24
+ x_0:
25
+ Predict data sample.
26
+ x_T:
27
+ Predict noise sample.
28
+ Proposed by DDPM (https://arxiv.org/abs/2006.11239)
29
+ Proved problematic by zsnr paper (https://arxiv.org/abs/2305.08891)
30
+ v_cos:
31
+ Predict velocity dx/dt based on the cosine schedule (A_t * x_T - B_t * x_0).
32
+ Proposed by progressive distillation (https://arxiv.org/abs/2202.00512)
33
+ v_lerp:
34
+ Predict velocity dx/dt based on the lerp schedule (x_T - x_0).
35
+ Proposed by rectified flow (https://arxiv.org/abs/2209.03003)
36
+ """
37
+
38
+ x_0 = "x_0"
39
+ x_T = "x_T"
40
+ v_cos = "v_cos"
41
+ v_lerp = "v_lerp"
42
+
43
+
44
+ class SamplingDirection(str, Enum):
45
+ """
46
+ backward: Sample from x_T to x_0 for data generation.
47
+ forward: Sample from x_0 to x_T for noise inversion.
48
+ """
49
+
50
+ backward = "backward"
51
+ forward = "forward"
52
+
53
+ @staticmethod
54
+ def reverse(direction):
55
+ if direction == SamplingDirection.backward:
56
+ return SamplingDirection.forward
57
+ if direction == SamplingDirection.forward:
58
+ return SamplingDirection.backward
59
+ raise NotImplementedError
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/diffusion/utils.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Utility functions.
17
+ """
18
+
19
+ from typing import Callable
20
+ import torch
21
+
22
+
23
+ def expand_dims(tensor: torch.Tensor, ndim: int):
24
+ """
25
+ Expand tensor to target ndim. New dims are added to the right.
26
+ For example, if the tensor shape was (8,), target ndim is 4, return (8, 1, 1, 1).
27
+ """
28
+ shape = tensor.shape + (1,) * (ndim - tensor.ndim)
29
+ return tensor.reshape(shape)
30
+
31
+
32
+ def assert_schedule_timesteps_compatible(schedule, timesteps):
33
+ """
34
+ Check if schedule and timesteps are compatible.
35
+ """
36
+ if schedule.T != timesteps.T:
37
+ raise ValueError("Schedule and timesteps must have the same T.")
38
+ if schedule.is_continuous() != timesteps.is_continuous():
39
+ raise ValueError("Schedule and timesteps must have the same continuity.")
40
+
41
+
42
+ def classifier_free_guidance(
43
+ pos: torch.Tensor,
44
+ neg: torch.Tensor,
45
+ scale: float,
46
+ rescale: float = 0.0,
47
+ ):
48
+ """
49
+ Apply classifier-free guidance.
50
+ """
51
+ # Classifier-free guidance (https://arxiv.org/abs/2207.12598)
52
+ cfg = neg + scale * (pos - neg)
53
+
54
+ # Classifier-free guidance rescale (https://arxiv.org/pdf/2305.08891.pdf)
55
+ if rescale != 0.0:
56
+ pos_std = pos.std(dim=list(range(1, pos.ndim)), keepdim=True)
57
+ cfg_std = cfg.std(dim=list(range(1, cfg.ndim)), keepdim=True)
58
+ factor = pos_std / cfg_std
59
+ factor = rescale * factor + (1 - rescale)
60
+ cfg *= factor
61
+
62
+ return cfg
63
+
64
+
65
+ def classifier_free_guidance_dispatcher(
66
+ pos: Callable,
67
+ neg: Callable,
68
+ scale: float,
69
+ rescale: float = 0.0,
70
+ ):
71
+ """
72
+ Optionally execute models depending on classifer-free guidance scale.
73
+ """
74
+ # If scale is 1, no need to execute neg model.
75
+ if scale == 1.0:
76
+ return pos()
77
+
78
+ # Otherwise, execute both pos nad neg models and apply cfg.
79
+ return classifier_free_guidance(
80
+ pos=pos(),
81
+ neg=neg(),
82
+ scale=scale,
83
+ rescale=rescale,
84
+ )
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/distributed/__init__.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Distributed package.
17
+ """
18
+
19
+ from .basic import (
20
+ barrier_if_distributed,
21
+ convert_to_ddp,
22
+ get_device,
23
+ get_global_rank,
24
+ get_local_rank,
25
+ get_world_size,
26
+ init_torch,
27
+ )
28
+
29
+ __all__ = [
30
+ "barrier_if_distributed",
31
+ "convert_to_ddp",
32
+ "get_device",
33
+ "get_global_rank",
34
+ "get_local_rank",
35
+ "get_world_size",
36
+ "init_torch",
37
+ ]
v3-nodes/ComfyUI-SeedVR2_VideoUpscaler/src/common/distributed/advanced.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # // Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
2
+ # //
3
+ # // Licensed under the Apache License, Version 2.0 (the "License");
4
+ # // you may not use this file except in compliance with the License.
5
+ # // You may obtain a copy of the License at
6
+ # //
7
+ # // http://www.apache.org/licenses/LICENSE-2.0
8
+ # //
9
+ # // Unless required by applicable law or agreed to in writing, software
10
+ # // distributed under the License is distributed on an "AS IS" BASIS,
11
+ # // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # // See the License for the specific language governing permissions and
13
+ # // limitations under the License.
14
+
15
+ """
16
+ Advanced distributed functions for sequence parallel.
17
+ """
18
+
19
+ from typing import Optional, List, TYPE_CHECKING
20
+ import torch
21
+ import torch.distributed as dist
22
+
23
+ # Conditional imports for distributed training features (not needed for inference)
24
+ try:
25
+ from torch.distributed.device_mesh import DeviceMesh, init_device_mesh
26
+ from torch.distributed.fsdp import ShardingStrategy
27
+ _FSDP_AVAILABLE = True
28
+ except (ImportError, AttributeError):
29
+ # AMD ROCm and other builds may not have full FSDP support
30
+ DeviceMesh = None
31
+ init_device_mesh = None
32
+ ShardingStrategy = None
33
+ _FSDP_AVAILABLE = False
34
+
35
+ from .basic import get_global_rank, get_world_size
36
+
37
+
38
+ _DATA_PARALLEL_GROUP = None
39
+ _SEQUENCE_PARALLEL_GROUP = None
40
+ _SEQUENCE_PARALLEL_CPU_GROUP = None
41
+ _MODEL_SHARD_CPU_INTER_GROUP = None
42
+ _MODEL_SHARD_CPU_INTRA_GROUP = None
43
+ _MODEL_SHARD_INTER_GROUP = None
44
+ _MODEL_SHARD_INTRA_GROUP = None
45
+ _SEQUENCE_PARALLEL_GLOBAL_RANKS = None
46
+
47
+
48
+ def get_data_parallel_group() -> Optional[dist.ProcessGroup]:
49
+ """
50
+ Get data parallel process group.
51
+ """
52
+ return _DATA_PARALLEL_GROUP
53
+
54
+
55
+ def get_sequence_parallel_group() -> Optional[dist.ProcessGroup]:
56
+ """
57
+ Get sequence parallel process group.
58
+ """
59
+ return _SEQUENCE_PARALLEL_GROUP
60
+
61
+
62
+ def get_sequence_parallel_cpu_group() -> Optional[dist.ProcessGroup]:
63
+ """
64
+ Get sequence parallel CPU process group.
65
+ """
66
+ return _SEQUENCE_PARALLEL_CPU_GROUP
67
+
68
+
69
+ def get_data_parallel_rank() -> int:
70
+ """
71
+ Get data parallel rank.
72
+ """
73
+ group = get_data_parallel_group()
74
+ return dist.get_rank(group) if group else get_global_rank()
75
+
76
+
77
+ def get_data_parallel_world_size() -> int:
78
+ """
79
+ Get data parallel world size.
80
+ """
81
+ group = get_data_parallel_group()
82
+ return dist.get_world_size(group) if group else get_world_size()
83
+
84
+
85
+ def get_sequence_parallel_rank() -> int:
86
+ """
87
+ Get sequence parallel rank.
88
+ """
89
+ group = get_sequence_parallel_group()
90
+ return dist.get_rank(group) if group else 0
91
+
92
+
93
+ def get_sequence_parallel_world_size() -> int:
94
+ """
95
+ Get sequence parallel world size.
96
+ """
97
+ group = get_sequence_parallel_group()
98
+ return dist.get_world_size(group) if group else 1
99
+
100
+
101
+ def get_model_shard_cpu_intra_group() -> Optional[dist.ProcessGroup]:
102
+ """
103
+ Get the CPU intra process group of model sharding.
104
+ """
105
+ return _MODEL_SHARD_CPU_INTRA_GROUP
106
+
107
+
108
+ def get_model_shard_cpu_inter_group() -> Optional[dist.ProcessGroup]:
109
+ """
110
+ Get the CPU inter process group of model sharding.
111
+ """
112
+ return _MODEL_SHARD_CPU_INTER_GROUP
113
+
114
+
115
+ def get_model_shard_intra_group() -> Optional[dist.ProcessGroup]:
116
+ """
117
+ Get the GPU intra process group of model sharding.
118
+ """
119
+ return _MODEL_SHARD_INTRA_GROUP
120
+
121
+
122
+ def get_model_shard_inter_group() -> Optional[dist.ProcessGroup]:
123
+ """
124
+ Get the GPU inter process group of model sharding.
125
+ """
126
+ return _MODEL_SHARD_INTER_GROUP
127
+
128
+
129
+ def init_sequence_parallel(sequence_parallel_size: int):
130
+ """
131
+ Initialize sequence parallel.
132
+ """
133
+ global _DATA_PARALLEL_GROUP
134
+ global _SEQUENCE_PARALLEL_GROUP
135
+ global _SEQUENCE_PARALLEL_CPU_GROUP
136
+ global _SEQUENCE_PARALLEL_GLOBAL_RANKS
137
+ assert dist.is_initialized()
138
+ world_size = dist.get_world_size()
139
+ rank = dist.get_rank()
140
+ data_parallel_size = world_size // sequence_parallel_size
141
+ for i in range(data_parallel_size):
142
+ start_rank = i * sequence_parallel_size
143
+ end_rank = (i + 1) * sequence_parallel_size
144
+ ranks = range(start_rank, end_rank)
145
+ group = dist.new_group(ranks)
146
+ cpu_group = dist.new_group(ranks, backend="gloo")
147
+ if rank in ranks:
148
+ _SEQUENCE_PARALLEL_GROUP = group
149
+ _SEQUENCE_PARALLEL_CPU_GROUP = cpu_group
150
+ _SEQUENCE_PARALLEL_GLOBAL_RANKS = list(ranks)
151
+
152
+
153
+ def init_model_shard_group(
154
+ *,
155
+ sharding_strategy: ShardingStrategy,
156
+ device_mesh: Optional[DeviceMesh] = None,
157
+ ):
158
+ """
159
+ Initialize process group of model sharding.
160
+ """
161
+ if not _FSDP_AVAILABLE:
162
+ raise RuntimeError(
163
+ "FSDP features are not available in this PyTorch build. "
164
+ "Model sharding requires torch.distributed.fsdp support."
165
+ )
166
+ global _MODEL_SHARD_INTER_GROUP
167
+ global _MODEL_SHARD_INTRA_GROUP
168
+ global _MODEL_SHARD_CPU_INTER_GROUP
169
+ global _MODEL_SHARD_CPU_INTRA_GROUP
170
+ assert dist.is_initialized()
171
+ world_size = dist.get_world_size()
172
+ if device_mesh is not None:
173
+ num_shards_per_group = device_mesh.shape[1]
174
+ elif sharding_strategy == ShardingStrategy.NO_SHARD:
175
+ num_shards_per_group = 1
176
+ elif sharding_strategy in [
177
+ ShardingStrategy.HYBRID_SHARD,
178
+ ShardingStrategy._HYBRID_SHARD_ZERO2,
179
+ ]:
180
+ num_shards_per_group = torch.cuda.device_count()
181
+ else:
182
+ num_shards_per_group = world_size
183
+ num_groups = world_size // num_shards_per_group
184
+ device_mesh = (num_groups, num_shards_per_group)
185
+
186
+ gpu_mesh_2d = init_device_mesh("cuda", device_mesh, mesh_dim_names=("inter", "intra"))
187
+ cpu_mesh_2d = init_device_mesh("cpu", device_mesh, mesh_dim_names=("inter", "intra"))
188
+
189
+ _MODEL_SHARD_INTER_GROUP = gpu_mesh_2d.get_group("inter")
190
+ _MODEL_SHARD_INTRA_GROUP = gpu_mesh_2d.get_group("intra")
191
+ _MODEL_SHARD_CPU_INTER_GROUP = cpu_mesh_2d.get_group("inter")
192
+ _MODEL_SHARD_CPU_INTRA_GROUP = cpu_mesh_2d.get_group("intra")
193
+
194
+ def get_sequence_parallel_global_ranks() -> List[int]:
195
+ """
196
+ Get all global ranks of the sequence parallel process group
197
+ that the caller rank belongs to.
198
+ """
199
+ if _SEQUENCE_PARALLEL_GLOBAL_RANKS is None:
200
+ return [dist.get_rank()]
201
+ return _SEQUENCE_PARALLEL_GLOBAL_RANKS
202
+
203
+
204
+ def get_next_sequence_parallel_rank() -> int:
205
+ """
206
+ Get the next global rank of the sequence parallel process group
207
+ that the caller rank belongs to.
208
+ """
209
+ sp_global_ranks = get_sequence_parallel_global_ranks()
210
+ sp_rank = get_sequence_parallel_rank()
211
+ sp_size = get_sequence_parallel_world_size()
212
+ return sp_global_ranks[(sp_rank + 1) % sp_size]
213
+
214
+
215
+ def get_prev_sequence_parallel_rank() -> int:
216
+ """
217
+ Get the previous global rank of the sequence parallel process group
218
+ that the caller rank belongs to.
219
+ """
220
+ sp_global_ranks = get_sequence_parallel_global_ranks()
221
+ sp_rank = get_sequence_parallel_rank()
222
+ sp_size = get_sequence_parallel_world_size()
223
+ return sp_global_ranks[(sp_rank + sp_size - 1) % sp_size]