Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- third_party/diffusers/LICENSE +201 -0
- third_party/diffusers/README.md +231 -0
- third_party/diffusers/pyproject.toml +32 -0
- third_party/diffusers/setup.py +308 -0
- third_party/diffusers/src/diffusers/__init__.py +1623 -0
- third_party/diffusers/src/diffusers/callbacks.py +244 -0
- third_party/diffusers/src/diffusers/configuration_utils.py +805 -0
- third_party/diffusers/src/diffusers/dependency_versions_check.py +34 -0
- third_party/diffusers/src/diffusers/dependency_versions_table.py +56 -0
- third_party/diffusers/src/diffusers/hooks/__init__.py +30 -0
- third_party/diffusers/src/diffusers/hooks/_common.py +61 -0
- third_party/diffusers/src/diffusers/hooks/_helpers.py +381 -0
- third_party/diffusers/src/diffusers/hooks/context_parallel.py +382 -0
- third_party/diffusers/src/diffusers/hooks/faster_cache.py +654 -0
- third_party/diffusers/src/diffusers/hooks/first_block_cache.py +258 -0
- third_party/diffusers/src/diffusers/hooks/group_offloading.py +966 -0
- third_party/diffusers/src/diffusers/hooks/hooks.py +310 -0
- third_party/diffusers/src/diffusers/hooks/layer_skip.py +263 -0
- third_party/diffusers/src/diffusers/hooks/layerwise_casting.py +240 -0
- third_party/diffusers/src/diffusers/hooks/mag_cache.py +468 -0
- third_party/diffusers/src/diffusers/hooks/pyramid_attention_broadcast.py +314 -0
- third_party/diffusers/src/diffusers/hooks/smoothed_energy_guidance_utils.py +166 -0
- third_party/diffusers/src/diffusers/hooks/taylorseer_cache.py +345 -0
- third_party/diffusers/src/diffusers/hooks/text_kv_cache.py +173 -0
- third_party/diffusers/src/diffusers/hooks/utils.py +43 -0
- third_party/diffusers/src/diffusers/image_processor.py +1468 -0
- third_party/diffusers/src/diffusers/optimization.py +360 -0
- third_party/diffusers/src/diffusers/pipelines/README.md +171 -0
- third_party/diffusers/src/diffusers/pipelines/__init__.py +963 -0
- third_party/diffusers/src/diffusers/pipelines/auto_pipeline.py +1237 -0
- third_party/diffusers/src/diffusers/pipelines/chroma/__init__.py +51 -0
- third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma.py +976 -0
- third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_img2img.py +1060 -0
- third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_inpainting.py +1184 -0
- third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_output.py +20 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/__init__.py +64 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_5_predict.py +888 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_text2image.py +679 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_text2world.py +670 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_video2world.py +832 -0
- third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_output.py +39 -0
- third_party/diffusers/src/diffusers/pipelines/free_init_utils.py +186 -0
- third_party/diffusers/src/diffusers/pipelines/free_noise_utils.py +597 -0
- third_party/diffusers/src/diffusers/pipelines/glm_image/__init__.py +59 -0
- third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_glm_image.py +1049 -0
- third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_output.py +20 -0
- third_party/diffusers/src/diffusers/pipelines/hidream_image/__init__.py +47 -0
- third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_hidream_image.py +1053 -0
- third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_output.py +34 -0
- third_party/diffusers/src/diffusers/pipelines/latte/__init__.py +48 -0
third_party/diffusers/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 [yyyy] [name of copyright owner]
|
| 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.
|
third_party/diffusers/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!---
|
| 2 |
+
Copyright 2022 - The HuggingFace Team. All rights reserved.
|
| 3 |
+
|
| 4 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
you may not use this file except in compliance with the License.
|
| 6 |
+
You may obtain a copy of the License at
|
| 7 |
+
|
| 8 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
|
| 10 |
+
Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
See the License for the specific language governing permissions and
|
| 14 |
+
limitations under the License.
|
| 15 |
+
-->
|
| 16 |
+
|
| 17 |
+
<p align="center">
|
| 18 |
+
<br>
|
| 19 |
+
<img src="https://raw.githubusercontent.com/huggingface/diffusers/main/docs/source/en/imgs/diffusers_library.jpg" width="400"/>
|
| 20 |
+
<br>
|
| 21 |
+
<p>
|
| 22 |
+
<p align="center">
|
| 23 |
+
<a href="https://github.com/huggingface/diffusers/blob/main/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/huggingface/datasets.svg?color=blue"></a>
|
| 24 |
+
<a href="https://github.com/huggingface/diffusers/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/diffusers.svg"></a>
|
| 25 |
+
<a href="https://pepy.tech/project/diffusers"><img alt="GitHub release" src="https://static.pepy.tech/badge/diffusers/month"></a>
|
| 26 |
+
<a href="CODE_OF_CONDUCT.md"><img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg"></a>
|
| 27 |
+
<a href="https://twitter.com/diffuserslib"><img alt="X account" src="https://img.shields.io/twitter/url/https/twitter.com/diffuserslib.svg?style=social&label=Follow%20%40diffuserslib"></a>
|
| 28 |
+
</p>
|
| 29 |
+
|
| 30 |
+
🤗 Diffusers is the go-to library for state-of-the-art pretrained diffusion models for generating images, audio, and even 3D structures of molecules. Whether you're looking for a simple inference solution or training your own diffusion models, 🤗 Diffusers is a modular toolbox that supports both. Our library is designed with a focus on [usability over performance](https://huggingface.co/docs/diffusers/conceptual/philosophy#usability-over-performance), [simple over easy](https://huggingface.co/docs/diffusers/conceptual/philosophy#simple-over-easy), and [customizability over abstractions](https://huggingface.co/docs/diffusers/conceptual/philosophy#tweakable-contributorfriendly-over-abstraction).
|
| 31 |
+
|
| 32 |
+
🤗 Diffusers offers three core components:
|
| 33 |
+
|
| 34 |
+
- State-of-the-art [diffusion pipelines](https://huggingface.co/docs/diffusers/api/pipelines/overview) that can be run in inference with just a few lines of code.
|
| 35 |
+
- Interchangeable noise [schedulers](https://huggingface.co/docs/diffusers/api/schedulers/overview) for different diffusion speeds and output quality.
|
| 36 |
+
- Pretrained [models](https://huggingface.co/docs/diffusers/api/models/overview) that can be used as building blocks, and combined with schedulers, for creating your own end-to-end diffusion systems.
|
| 37 |
+
|
| 38 |
+
## Installation
|
| 39 |
+
|
| 40 |
+
We recommend installing 🤗 Diffusers in a virtual environment from PyPI or Conda. For more details about installing [PyTorch](https://pytorch.org/get-started/locally/), please refer to their official documentation.
|
| 41 |
+
|
| 42 |
+
### PyTorch
|
| 43 |
+
|
| 44 |
+
With `pip` (official package):
|
| 45 |
+
|
| 46 |
+
```bash
|
| 47 |
+
pip install --upgrade diffusers[torch]
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
With `conda` (maintained by the community):
|
| 51 |
+
|
| 52 |
+
```sh
|
| 53 |
+
conda install -c conda-forge diffusers
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
### Apple Silicon (M1/M2) support
|
| 57 |
+
|
| 58 |
+
Please refer to the [How to use Stable Diffusion in Apple Silicon](https://huggingface.co/docs/diffusers/optimization/mps) guide.
|
| 59 |
+
|
| 60 |
+
## Quickstart
|
| 61 |
+
|
| 62 |
+
Generating outputs is super easy with 🤗 Diffusers. To generate an image from text, use the `from_pretrained` method to load any pretrained diffusion model (browse the [Hub](https://huggingface.co/models?library=diffusers&sort=downloads) for 30,000+ checkpoints):
|
| 63 |
+
|
| 64 |
+
```python
|
| 65 |
+
from diffusers import DiffusionPipeline
|
| 66 |
+
import torch
|
| 67 |
+
|
| 68 |
+
pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", torch_dtype=torch.float16)
|
| 69 |
+
pipeline.to("cuda")
|
| 70 |
+
pipeline("An image of a squirrel in Picasso style").images[0]
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
You can also dig into the models and schedulers toolbox to build your own diffusion system:
|
| 74 |
+
|
| 75 |
+
```python
|
| 76 |
+
from diffusers import DDPMScheduler, UNet2DModel
|
| 77 |
+
from PIL import Image
|
| 78 |
+
import torch
|
| 79 |
+
|
| 80 |
+
scheduler = DDPMScheduler.from_pretrained("google/ddpm-cat-256")
|
| 81 |
+
model = UNet2DModel.from_pretrained("google/ddpm-cat-256").to("cuda")
|
| 82 |
+
scheduler.set_timesteps(50)
|
| 83 |
+
|
| 84 |
+
sample_size = model.config.sample_size
|
| 85 |
+
noise = torch.randn((1, 3, sample_size, sample_size), device="cuda")
|
| 86 |
+
input = noise
|
| 87 |
+
|
| 88 |
+
for t in scheduler.timesteps:
|
| 89 |
+
with torch.no_grad():
|
| 90 |
+
noisy_residual = model(input, t).sample
|
| 91 |
+
prev_noisy_sample = scheduler.step(noisy_residual, t, input).prev_sample
|
| 92 |
+
input = prev_noisy_sample
|
| 93 |
+
|
| 94 |
+
image = (input / 2 + 0.5).clamp(0, 1)
|
| 95 |
+
image = image.cpu().permute(0, 2, 3, 1).numpy()[0]
|
| 96 |
+
image = Image.fromarray((image * 255).round().astype("uint8"))
|
| 97 |
+
image
|
| 98 |
+
```
|
| 99 |
+
|
| 100 |
+
Check out the [Quickstart](https://huggingface.co/docs/diffusers/quicktour) to launch your diffusion journey today!
|
| 101 |
+
|
| 102 |
+
## How to navigate the documentation
|
| 103 |
+
|
| 104 |
+
| **Documentation** | **What can I learn?** |
|
| 105 |
+
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
| 106 |
+
| [Tutorial](https://huggingface.co/docs/diffusers/tutorials/tutorial_overview) | A basic crash course for learning how to use the library's most important features like using models and schedulers to build your own diffusion system, and training your own diffusion model. |
|
| 107 |
+
| [Loading](https://huggingface.co/docs/diffusers/using-diffusers/loading) | Guides for how to load and configure all the components (pipelines, models, and schedulers) of the library, as well as how to use different schedulers. |
|
| 108 |
+
| [Pipelines for inference](https://huggingface.co/docs/diffusers/using-diffusers/overview_techniques) | Guides for how to use pipelines for different inference tasks, batched generation, controlling generated outputs and randomness, and how to contribute a pipeline to the library. |
|
| 109 |
+
| [Optimization](https://huggingface.co/docs/diffusers/optimization/fp16) | Guides for how to optimize your diffusion model to run faster and consume less memory. |
|
| 110 |
+
| [Training](https://huggingface.co/docs/diffusers/training/overview) | Guides for how to train a diffusion model for different tasks with different training techniques. |
|
| 111 |
+
## Contribution
|
| 112 |
+
|
| 113 |
+
We ❤️ contributions from the open-source community!
|
| 114 |
+
If you want to contribute to this library, please check out our [Contribution guide](https://github.com/huggingface/diffusers/blob/main/CONTRIBUTING.md).
|
| 115 |
+
You can look out for [issues](https://github.com/huggingface/diffusers/issues) you'd like to tackle to contribute to the library.
|
| 116 |
+
- See [Good first issues](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) for general opportunities to contribute
|
| 117 |
+
- See [New model/pipeline](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22New+pipeline%2Fmodel%22) to contribute exciting new diffusion models / diffusion pipelines
|
| 118 |
+
- See [New scheduler](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22New+scheduler%22)
|
| 119 |
+
|
| 120 |
+
Also, say 👋 in our public Discord channel <a href="https://discord.gg/G7tWnz98XR"><img alt="Join us on Discord" src="https://img.shields.io/discord/823813159592001537?color=5865F2&logo=discord&logoColor=white"></a>. We discuss the hottest trends about diffusion models, help each other with contributions, personal projects or just hang out ☕.
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
## Popular Tasks & Pipelines
|
| 124 |
+
|
| 125 |
+
<table>
|
| 126 |
+
<tr>
|
| 127 |
+
<th>Task</th>
|
| 128 |
+
<th>Pipeline</th>
|
| 129 |
+
<th>🤗 Hub</th>
|
| 130 |
+
</tr>
|
| 131 |
+
<tr style="border-top: 2px solid black">
|
| 132 |
+
<td>Unconditional Image Generation</td>
|
| 133 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/ddpm"> DDPM </a></td>
|
| 134 |
+
<td><a href="https://huggingface.co/google/ddpm-ema-church-256"> google/ddpm-ema-church-256 </a></td>
|
| 135 |
+
</tr>
|
| 136 |
+
<tr style="border-top: 2px solid black">
|
| 137 |
+
<td>Text-to-Image</td>
|
| 138 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/text2img">Stable Diffusion Text-to-Image</a></td>
|
| 139 |
+
<td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5"> stable-diffusion-v1-5/stable-diffusion-v1-5 </a></td>
|
| 140 |
+
</tr>
|
| 141 |
+
<tr>
|
| 142 |
+
<td>Text-to-Image</td>
|
| 143 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/unclip">unCLIP</a></td>
|
| 144 |
+
<td><a href="https://huggingface.co/kakaobrain/karlo-v1-alpha"> kakaobrain/karlo-v1-alpha </a></td>
|
| 145 |
+
</tr>
|
| 146 |
+
<tr>
|
| 147 |
+
<td>Text-to-Image</td>
|
| 148 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/deepfloyd_if">DeepFloyd IF</a></td>
|
| 149 |
+
<td><a href="https://huggingface.co/DeepFloyd/IF-I-XL-v1.0"> DeepFloyd/IF-I-XL-v1.0 </a></td>
|
| 150 |
+
</tr>
|
| 151 |
+
<tr>
|
| 152 |
+
<td>Text-to-Image</td>
|
| 153 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/kandinsky">Kandinsky</a></td>
|
| 154 |
+
<td><a href="https://huggingface.co/kandinsky-community/kandinsky-2-2-decoder"> kandinsky-community/kandinsky-2-2-decoder </a></td>
|
| 155 |
+
</tr>
|
| 156 |
+
<tr style="border-top: 2px solid black">
|
| 157 |
+
<td>Text-guided Image-to-Image</td>
|
| 158 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/controlnet">ControlNet</a></td>
|
| 159 |
+
<td><a href="https://huggingface.co/lllyasviel/sd-controlnet-canny"> lllyasviel/sd-controlnet-canny </a></td>
|
| 160 |
+
</tr>
|
| 161 |
+
<tr>
|
| 162 |
+
<td>Text-guided Image-to-Image</td>
|
| 163 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/pix2pix">InstructPix2Pix</a></td>
|
| 164 |
+
<td><a href="https://huggingface.co/timbrooks/instruct-pix2pix"> timbrooks/instruct-pix2pix </a></td>
|
| 165 |
+
</tr>
|
| 166 |
+
<tr>
|
| 167 |
+
<td>Text-guided Image-to-Image</td>
|
| 168 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/img2img">Stable Diffusion Image-to-Image</a></td>
|
| 169 |
+
<td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5"> stable-diffusion-v1-5/stable-diffusion-v1-5 </a></td>
|
| 170 |
+
</tr>
|
| 171 |
+
<tr style="border-top: 2px solid black">
|
| 172 |
+
<td>Text-guided Image Inpainting</td>
|
| 173 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/inpaint">Stable Diffusion Inpainting</a></td>
|
| 174 |
+
<td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-inpainting"> stable-diffusion-v1-5/stable-diffusion-inpainting </a></td>
|
| 175 |
+
</tr>
|
| 176 |
+
<tr style="border-top: 2px solid black">
|
| 177 |
+
<td>Image Variation</td>
|
| 178 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/image_variation">Stable Diffusion Image Variation</a></td>
|
| 179 |
+
<td><a href="https://huggingface.co/lambdalabs/sd-image-variations-diffusers"> lambdalabs/sd-image-variations-diffusers </a></td>
|
| 180 |
+
</tr>
|
| 181 |
+
<tr style="border-top: 2px solid black">
|
| 182 |
+
<td>Super Resolution</td>
|
| 183 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/upscale">Stable Diffusion Upscale</a></td>
|
| 184 |
+
<td><a href="https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler"> stabilityai/stable-diffusion-x4-upscaler </a></td>
|
| 185 |
+
</tr>
|
| 186 |
+
<tr>
|
| 187 |
+
<td>Super Resolution</td>
|
| 188 |
+
<td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/latent_upscale">Stable Diffusion Latent Upscale</a></td>
|
| 189 |
+
<td><a href="https://huggingface.co/stabilityai/sd-x2-latent-upscaler"> stabilityai/sd-x2-latent-upscaler </a></td>
|
| 190 |
+
</tr>
|
| 191 |
+
</table>
|
| 192 |
+
|
| 193 |
+
## Popular libraries using 🧨 Diffusers
|
| 194 |
+
|
| 195 |
+
- https://github.com/microsoft/TaskMatrix
|
| 196 |
+
- https://github.com/invoke-ai/InvokeAI
|
| 197 |
+
- https://github.com/InstantID/InstantID
|
| 198 |
+
- https://github.com/apple/ml-stable-diffusion
|
| 199 |
+
- https://github.com/Sanster/lama-cleaner
|
| 200 |
+
- https://github.com/IDEA-Research/Grounded-Segment-Anything
|
| 201 |
+
- https://github.com/ashawkey/stable-dreamfusion
|
| 202 |
+
- https://github.com/deep-floyd/IF
|
| 203 |
+
- https://github.com/bentoml/BentoML
|
| 204 |
+
- https://github.com/bmaltais/kohya_ss
|
| 205 |
+
- +14,000 other amazing GitHub repositories 💪
|
| 206 |
+
|
| 207 |
+
Thank you for using us ❤️.
|
| 208 |
+
|
| 209 |
+
## Credits
|
| 210 |
+
|
| 211 |
+
This library concretizes previous work by many different authors and would not have been possible without their great research and implementations. We'd like to thank, in particular, the following implementations which have helped us in our development and without which the API could not have been as polished today:
|
| 212 |
+
|
| 213 |
+
- @CompVis' latent diffusion models library, available [here](https://github.com/CompVis/latent-diffusion)
|
| 214 |
+
- @hojonathanho original DDPM implementation, available [here](https://github.com/hojonathanho/diffusion) as well as the extremely useful translation into PyTorch by @pesser, available [here](https://github.com/pesser/pytorch_diffusion)
|
| 215 |
+
- @ermongroup's DDIM implementation, available [here](https://github.com/ermongroup/ddim)
|
| 216 |
+
- @yang-song's Score-VE and Score-VP implementations, available [here](https://github.com/yang-song/score_sde_pytorch)
|
| 217 |
+
|
| 218 |
+
We also want to thank @heejkoo for the very helpful overview of papers, code and resources on diffusion models, available [here](https://github.com/heejkoo/Awesome-Diffusion-Models) as well as @crowsonkb and @rromb for useful discussions and insights.
|
| 219 |
+
|
| 220 |
+
## Citation
|
| 221 |
+
|
| 222 |
+
```bibtex
|
| 223 |
+
@misc{von-platen-etal-2022-diffusers,
|
| 224 |
+
author = {Patrick von Platen and Suraj Patil and Anton Lozhkov and Pedro Cuenca and Nathan Lambert and Kashif Rasul and Mishig Davaadorj and Dhruv Nair and Sayak Paul and William Berman and Yiyi Xu and Steven Liu and Thomas Wolf},
|
| 225 |
+
title = {Diffusers: State-of-the-art diffusion models},
|
| 226 |
+
year = {2022},
|
| 227 |
+
publisher = {GitHub},
|
| 228 |
+
journal = {GitHub repository},
|
| 229 |
+
howpublished = {\url{https://github.com/huggingface/diffusers}}
|
| 230 |
+
}
|
| 231 |
+
```
|
third_party/diffusers/pyproject.toml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[tool.ruff]
|
| 2 |
+
line-length = 119
|
| 3 |
+
extend-exclude = [
|
| 4 |
+
"src/diffusers/pipelines/flux2/system_messages.py",
|
| 5 |
+
]
|
| 6 |
+
|
| 7 |
+
[tool.ruff.lint]
|
| 8 |
+
# Never enforce `E501` (line length violations).
|
| 9 |
+
ignore = ["C901", "E501", "E721", "E741", "F402", "F823"]
|
| 10 |
+
select = ["C", "E", "F", "I", "W"]
|
| 11 |
+
|
| 12 |
+
# Ignore import violations in all `__init__.py` files.
|
| 13 |
+
[tool.ruff.lint.per-file-ignores]
|
| 14 |
+
"__init__.py" = ["E402", "F401", "F403", "F811"]
|
| 15 |
+
"src/diffusers/utils/dummy_*.py" = ["F401"]
|
| 16 |
+
|
| 17 |
+
[tool.ruff.lint.isort]
|
| 18 |
+
lines-after-imports = 2
|
| 19 |
+
known-first-party = ["diffusers"]
|
| 20 |
+
|
| 21 |
+
[tool.ruff.format]
|
| 22 |
+
# Like Black, use double quotes for strings.
|
| 23 |
+
quote-style = "double"
|
| 24 |
+
|
| 25 |
+
# Like Black, indent with spaces, rather than tabs.
|
| 26 |
+
indent-style = "space"
|
| 27 |
+
|
| 28 |
+
# Like Black, respect magic trailing commas.
|
| 29 |
+
skip-magic-trailing-comma = false
|
| 30 |
+
|
| 31 |
+
# Like Black, automatically detect the appropriate line ending.
|
| 32 |
+
line-ending = "auto"
|
third_party/diffusers/setup.py
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 |
+
Simple check list from AllenNLP repo: https://github.com/allenai/allennlp/blob/main/setup.py
|
| 17 |
+
|
| 18 |
+
To create the package for PyPI.
|
| 19 |
+
|
| 20 |
+
1. Run `make pre-release` (or `make pre-patch` for a patch release) then run `make fix-copies` to fix the index of the
|
| 21 |
+
documentation.
|
| 22 |
+
|
| 23 |
+
If releasing on a special branch, copy the updated README.md on the main branch for the commit you will make
|
| 24 |
+
for the post-release and run `make fix-copies` on the main branch as well.
|
| 25 |
+
|
| 26 |
+
2. Unpin specific versions from setup.py that use a git install.
|
| 27 |
+
|
| 28 |
+
3. Checkout the release branch (v<RELEASE>-release, for example v4.19-release), and commit these changes with the
|
| 29 |
+
message: "Release: <RELEASE>" and push.
|
| 30 |
+
|
| 31 |
+
4. Manually trigger the "Nightly and release tests on main/release branch" workflow from the release branch. Wait for
|
| 32 |
+
the tests to complete. We can safely ignore the known test failures.
|
| 33 |
+
|
| 34 |
+
5. Wait for the tests on main to be completed and be green (otherwise revert and fix bugs).
|
| 35 |
+
|
| 36 |
+
6. Add a tag in git to mark the release: "git tag v<RELEASE> -m 'Adds tag v<RELEASE> for PyPI'"
|
| 37 |
+
Push the tag to git: git push --tags origin v<RELEASE>-release
|
| 38 |
+
|
| 39 |
+
7. Build both the sources and the wheel. Do not change anything in setup.py between
|
| 40 |
+
creating the wheel and the source distribution (obviously).
|
| 41 |
+
|
| 42 |
+
For the wheel, run: "python setup.py bdist_wheel" in the top level directory
|
| 43 |
+
(This will build a wheel for the Python version you use to build it).
|
| 44 |
+
|
| 45 |
+
For the sources, run: "python setup.py sdist"
|
| 46 |
+
You should now have a /dist directory with both .whl and .tar.gz source versions.
|
| 47 |
+
|
| 48 |
+
Long story cut short, you need to run both before you can upload the distribution to the
|
| 49 |
+
test PyPI and the actual PyPI servers:
|
| 50 |
+
|
| 51 |
+
python setup.py bdist_wheel && python setup.py sdist
|
| 52 |
+
|
| 53 |
+
8. Check that everything looks correct by uploading the package to the PyPI test server:
|
| 54 |
+
|
| 55 |
+
twine upload dist/* -r pypitest
|
| 56 |
+
(pypi suggests using twine as other methods upload files via plaintext.)
|
| 57 |
+
You may have to specify the repository url, use the following command then:
|
| 58 |
+
twine upload dist/* -r pypitest --repository-url=https://test.pypi.org/legacy/
|
| 59 |
+
|
| 60 |
+
Check that you can install it in a virtualenv by running:
|
| 61 |
+
pip install -i https://testpypi.python.org/pypi diffusers
|
| 62 |
+
|
| 63 |
+
If you are testing from a Colab Notebook, for instance, then do:
|
| 64 |
+
pip install diffusers && pip uninstall diffusers
|
| 65 |
+
pip install -i https://testpypi.python.org/pypi diffusers
|
| 66 |
+
|
| 67 |
+
Check you can run the following commands:
|
| 68 |
+
python -c "from diffusers import __version__; print(__version__)"
|
| 69 |
+
python -c "from diffusers import DiffusionPipeline; pipe = DiffusionPipeline.from_pretrained('fusing/unet-ldm-dummy-update'); pipe()"
|
| 70 |
+
python -c "from diffusers import DiffusionPipeline; pipe = DiffusionPipeline.from_pretrained('hf-internal-testing/tiny-stable-diffusion-pipe', safety_checker=None); pipe('ah suh du')"
|
| 71 |
+
python -c "from diffusers import *"
|
| 72 |
+
|
| 73 |
+
9. Upload the final version to the actual PyPI:
|
| 74 |
+
twine upload dist/* -r pypi
|
| 75 |
+
|
| 76 |
+
10. Prepare the release notes and publish them on GitHub once everything is looking hunky-dory. You can use the following
|
| 77 |
+
Space to fetch all the commits applicable for the release: https://huggingface.co/spacmes/sayakpaul/auto-release-notes-diffusers.
|
| 78 |
+
It automatically fetches the correct tag and branch but also provides the option to configure them.
|
| 79 |
+
`tag` should be the previous release tag (v0.26.1, for example), and `branch` should be
|
| 80 |
+
the latest release branch (v0.27.0-release, for example). It denotes all commits that have happened on branch
|
| 81 |
+
v0.27.0-release after the tag v0.26.1 was created.
|
| 82 |
+
|
| 83 |
+
11. Run `make post-release` (or, for a patch release, `make post-patch`). If you were on a branch for the release,
|
| 84 |
+
you need to go back to main before executing this.
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
import os
|
| 88 |
+
import re
|
| 89 |
+
import sys
|
| 90 |
+
|
| 91 |
+
from setuptools import Command, find_packages, setup
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
# IMPORTANT:
|
| 95 |
+
# 1. all dependencies should be listed here with their version requirements if any
|
| 96 |
+
# 2. once modified, run: `make deps_table_update` to update src/diffusers/dependency_versions_table.py
|
| 97 |
+
_deps = [
|
| 98 |
+
"Pillow", # keep the PIL.Image.Resampling deprecation away
|
| 99 |
+
"accelerate>=0.31.0",
|
| 100 |
+
"compel==0.1.8",
|
| 101 |
+
"datasets",
|
| 102 |
+
"filelock",
|
| 103 |
+
"flax>=0.4.1",
|
| 104 |
+
"ftfy",
|
| 105 |
+
"hf-doc-builder>=0.3.0",
|
| 106 |
+
"httpx<1.0.0",
|
| 107 |
+
"huggingface-hub>=0.34.0,<2.0",
|
| 108 |
+
"requests-mock==1.10.0",
|
| 109 |
+
"importlib_metadata",
|
| 110 |
+
"invisible-watermark>=0.2.0",
|
| 111 |
+
"isort>=5.5.4",
|
| 112 |
+
"jax>=0.4.1",
|
| 113 |
+
"jaxlib>=0.4.1",
|
| 114 |
+
"Jinja2",
|
| 115 |
+
"torchsde",
|
| 116 |
+
"note_seq",
|
| 117 |
+
"librosa",
|
| 118 |
+
"numpy",
|
| 119 |
+
"parameterized",
|
| 120 |
+
"peft>=0.17.0",
|
| 121 |
+
"protobuf>=3.20.3,<4",
|
| 122 |
+
"pytest",
|
| 123 |
+
"pytest-timeout",
|
| 124 |
+
"pytest-xdist",
|
| 125 |
+
"python>=3.10.0",
|
| 126 |
+
"ruff==0.9.10",
|
| 127 |
+
"safetensors>=0.3.1",
|
| 128 |
+
"sentencepiece>=0.1.91,!=0.1.92",
|
| 129 |
+
"GitPython<3.1.19",
|
| 130 |
+
"scipy",
|
| 131 |
+
"onnx",
|
| 132 |
+
"optimum_quanto>=0.2.6",
|
| 133 |
+
"gguf>=0.10.0",
|
| 134 |
+
"torchao>=0.7.0",
|
| 135 |
+
"bitsandbytes>=0.43.3",
|
| 136 |
+
"nvidia_modelopt[hf]>=0.33.1",
|
| 137 |
+
"regex!=2019.12.17",
|
| 138 |
+
"requests",
|
| 139 |
+
"tensorboard",
|
| 140 |
+
"tiktoken>=0.7.0",
|
| 141 |
+
"torch>=1.4",
|
| 142 |
+
"torchvision",
|
| 143 |
+
"transformers>=4.41.2",
|
| 144 |
+
"urllib3<=2.0.0",
|
| 145 |
+
"black",
|
| 146 |
+
"phonemizer",
|
| 147 |
+
"opencv-python",
|
| 148 |
+
"timm",
|
| 149 |
+
]
|
| 150 |
+
|
| 151 |
+
# this is a lookup table with items like:
|
| 152 |
+
#
|
| 153 |
+
# tokenizers: "huggingface-hub==0.8.0"
|
| 154 |
+
# packaging: "packaging"
|
| 155 |
+
#
|
| 156 |
+
# some of the values are versioned whereas others aren't.
|
| 157 |
+
deps = {b: a for a, b in (re.findall(r"^(([^!=<>~]+)(?:[!=<>~].*)?$)", x)[0] for x in _deps)}
|
| 158 |
+
|
| 159 |
+
# since we save this data in src/diffusers/dependency_versions_table.py it can be easily accessed from
|
| 160 |
+
# anywhere. If you need to quickly access the data from this table in a shell, you can do so easily with:
|
| 161 |
+
#
|
| 162 |
+
# python -c 'import sys; from diffusers.dependency_versions_table import deps; \
|
| 163 |
+
# print(" ".join([deps[x] for x in sys.argv[1:]]))' tokenizers datasets
|
| 164 |
+
#
|
| 165 |
+
# Just pass the desired package names to that script as it's shown with 2 packages above.
|
| 166 |
+
#
|
| 167 |
+
# If diffusers is not yet installed and the work is done from the cloned repo remember to add `PYTHONPATH=src` to the script above
|
| 168 |
+
#
|
| 169 |
+
# You can then feed this for example to `pip`:
|
| 170 |
+
#
|
| 171 |
+
# pip install -U $(python -c 'import sys; from diffusers.dependency_versions_table import deps; \
|
| 172 |
+
# print(" ".join([deps[x] for x in sys.argv[1:]]))' tokenizers datasets)
|
| 173 |
+
#
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def deps_list(*pkgs):
|
| 177 |
+
return [deps[pkg] for pkg in pkgs]
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class DepsTableUpdateCommand(Command):
|
| 181 |
+
"""
|
| 182 |
+
A custom command that updates the dependency table.
|
| 183 |
+
usage: python setup.py deps_table_update
|
| 184 |
+
"""
|
| 185 |
+
|
| 186 |
+
description = "build runtime dependency table"
|
| 187 |
+
user_options = [
|
| 188 |
+
# format: (long option, short option, description).
|
| 189 |
+
(
|
| 190 |
+
"dep-table-update",
|
| 191 |
+
None,
|
| 192 |
+
"updates src/diffusers/dependency_versions_table.py",
|
| 193 |
+
),
|
| 194 |
+
]
|
| 195 |
+
|
| 196 |
+
def initialize_options(self):
|
| 197 |
+
pass
|
| 198 |
+
|
| 199 |
+
def finalize_options(self):
|
| 200 |
+
pass
|
| 201 |
+
|
| 202 |
+
def run(self):
|
| 203 |
+
entries = "\n".join([f' "{k}": "{v}",' for k, v in deps.items()])
|
| 204 |
+
content = [
|
| 205 |
+
"# THIS FILE HAS BEEN AUTOGENERATED. To update:",
|
| 206 |
+
"# 1. modify the `_deps` dict in setup.py",
|
| 207 |
+
"# 2. run `make deps_table_update`",
|
| 208 |
+
"deps = {",
|
| 209 |
+
entries,
|
| 210 |
+
"}",
|
| 211 |
+
"",
|
| 212 |
+
]
|
| 213 |
+
target = "src/diffusers/dependency_versions_table.py"
|
| 214 |
+
print(f"updating {target}")
|
| 215 |
+
with open(target, "w", encoding="utf-8", newline="\n") as f:
|
| 216 |
+
f.write("\n".join(content))
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
extras = {}
|
| 220 |
+
extras["quality"] = deps_list("urllib3", "isort", "ruff", "hf-doc-builder")
|
| 221 |
+
extras["docs"] = deps_list("hf-doc-builder")
|
| 222 |
+
extras["training"] = deps_list("accelerate", "datasets", "protobuf", "tensorboard", "Jinja2", "peft", "timm")
|
| 223 |
+
extras["test"] = deps_list(
|
| 224 |
+
"compel",
|
| 225 |
+
"ftfy",
|
| 226 |
+
"GitPython",
|
| 227 |
+
"datasets",
|
| 228 |
+
"Jinja2",
|
| 229 |
+
"invisible-watermark",
|
| 230 |
+
"librosa",
|
| 231 |
+
"parameterized",
|
| 232 |
+
"protobuf",
|
| 233 |
+
"pytest",
|
| 234 |
+
"pytest-timeout",
|
| 235 |
+
"pytest-xdist",
|
| 236 |
+
"requests-mock",
|
| 237 |
+
"safetensors",
|
| 238 |
+
"sentencepiece",
|
| 239 |
+
"scipy",
|
| 240 |
+
"tiktoken",
|
| 241 |
+
"torchsde",
|
| 242 |
+
"torchvision",
|
| 243 |
+
"transformers",
|
| 244 |
+
"phonemizer",
|
| 245 |
+
)
|
| 246 |
+
extras["torch"] = deps_list("torch", "accelerate")
|
| 247 |
+
|
| 248 |
+
extras["bitsandbytes"] = deps_list("bitsandbytes", "accelerate")
|
| 249 |
+
extras["gguf"] = deps_list("gguf", "accelerate")
|
| 250 |
+
extras["optimum_quanto"] = deps_list("optimum_quanto", "accelerate")
|
| 251 |
+
extras["torchao"] = deps_list("torchao", "accelerate")
|
| 252 |
+
extras["nvidia_modelopt"] = deps_list("nvidia_modelopt[hf]")
|
| 253 |
+
|
| 254 |
+
if os.name == "nt": # windows
|
| 255 |
+
extras["flax"] = [] # jax is not supported on windows
|
| 256 |
+
else:
|
| 257 |
+
extras["flax"] = deps_list("jax", "jaxlib", "flax")
|
| 258 |
+
|
| 259 |
+
extras["dev"] = (
|
| 260 |
+
extras["quality"] + extras["test"] + extras["training"] + extras["docs"] + extras["torch"] + extras["flax"]
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
install_requires = [
|
| 264 |
+
deps["importlib_metadata"],
|
| 265 |
+
deps["filelock"],
|
| 266 |
+
deps["httpx"],
|
| 267 |
+
deps["huggingface-hub"],
|
| 268 |
+
deps["numpy"],
|
| 269 |
+
deps["regex"],
|
| 270 |
+
deps["requests"],
|
| 271 |
+
deps["safetensors"],
|
| 272 |
+
deps["Pillow"],
|
| 273 |
+
]
|
| 274 |
+
|
| 275 |
+
version_range_max = max(sys.version_info[1], 10) + 1
|
| 276 |
+
|
| 277 |
+
setup(
|
| 278 |
+
name="diffusers",
|
| 279 |
+
version="0.38.0.dev0", # expected format is one of x.y.z.dev0, or x.y.z.rc1 or x.y.z (no to dashes, yes to dots)
|
| 280 |
+
description="State-of-the-art diffusion in PyTorch and JAX.",
|
| 281 |
+
long_description=open("README.md", "r", encoding="utf-8").read(),
|
| 282 |
+
long_description_content_type="text/markdown",
|
| 283 |
+
keywords="deep learning diffusion jax pytorch stable diffusion audioldm",
|
| 284 |
+
license="Apache 2.0 License",
|
| 285 |
+
author="The Hugging Face team (past and future) with the help of all our contributors (https://github.com/huggingface/diffusers/graphs/contributors)",
|
| 286 |
+
author_email="diffusers@huggingface.co",
|
| 287 |
+
url="https://github.com/huggingface/diffusers",
|
| 288 |
+
package_dir={"": "src"},
|
| 289 |
+
packages=find_packages("src"),
|
| 290 |
+
package_data={"diffusers": ["py.typed"]},
|
| 291 |
+
include_package_data=True,
|
| 292 |
+
python_requires=">=3.10.0",
|
| 293 |
+
install_requires=list(install_requires),
|
| 294 |
+
extras_require=extras,
|
| 295 |
+
entry_points={"console_scripts": ["diffusers-cli=diffusers.commands.diffusers_cli:main"]},
|
| 296 |
+
classifiers=[
|
| 297 |
+
"Development Status :: 5 - Production/Stable",
|
| 298 |
+
"Intended Audience :: Developers",
|
| 299 |
+
"Intended Audience :: Education",
|
| 300 |
+
"Intended Audience :: Science/Research",
|
| 301 |
+
"License :: OSI Approved :: Apache Software License",
|
| 302 |
+
"Operating System :: OS Independent",
|
| 303 |
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
| 304 |
+
"Programming Language :: Python :: 3",
|
| 305 |
+
]
|
| 306 |
+
+ [f"Programming Language :: Python :: 3.{i}" for i in range(8, version_range_max)],
|
| 307 |
+
cmdclass={"deps_table_update": DepsTableUpdateCommand},
|
| 308 |
+
)
|
third_party/diffusers/src/diffusers/__init__.py
ADDED
|
@@ -0,0 +1,1623 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__version__ = "0.38.0.dev0"
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING
|
| 4 |
+
|
| 5 |
+
from .utils import (
|
| 6 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 7 |
+
OptionalDependencyNotAvailable,
|
| 8 |
+
_LazyModule,
|
| 9 |
+
is_accelerate_available,
|
| 10 |
+
is_bitsandbytes_available,
|
| 11 |
+
is_flax_available,
|
| 12 |
+
is_gguf_available,
|
| 13 |
+
is_librosa_available,
|
| 14 |
+
is_note_seq_available,
|
| 15 |
+
is_nvidia_modelopt_available,
|
| 16 |
+
is_onnx_available,
|
| 17 |
+
is_opencv_available,
|
| 18 |
+
is_optimum_quanto_available,
|
| 19 |
+
is_scipy_available,
|
| 20 |
+
is_sentencepiece_available,
|
| 21 |
+
is_torch_available,
|
| 22 |
+
is_torchao_available,
|
| 23 |
+
is_torchsde_available,
|
| 24 |
+
is_transformers_available,
|
| 25 |
+
is_transformers_version,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# Lazy Import based on
|
| 30 |
+
# https://github.com/huggingface/transformers/blob/main/src/transformers/__init__.py
|
| 31 |
+
|
| 32 |
+
# When adding a new object to this init, please add it to `_import_structure`. The `_import_structure` is a dictionary submodule to list of object names,
|
| 33 |
+
# and is used to defer the actual importing for when the objects are requested.
|
| 34 |
+
# This way `import diffusers` provides the names in the namespace without actually importing anything (and especially none of the backends).
|
| 35 |
+
|
| 36 |
+
_import_structure = {
|
| 37 |
+
"configuration_utils": ["ConfigMixin"],
|
| 38 |
+
"guiders": [],
|
| 39 |
+
"hooks": [],
|
| 40 |
+
"loaders": ["FromOriginalModelMixin"],
|
| 41 |
+
"models": [],
|
| 42 |
+
"modular_pipelines": [],
|
| 43 |
+
"pipelines": [],
|
| 44 |
+
"quantizers.pipe_quant_config": ["PipelineQuantizationConfig"],
|
| 45 |
+
"quantizers.quantization_config": [],
|
| 46 |
+
"schedulers": [],
|
| 47 |
+
"utils": [
|
| 48 |
+
"OptionalDependencyNotAvailable",
|
| 49 |
+
"is_flax_available",
|
| 50 |
+
"is_inflect_available",
|
| 51 |
+
"is_invisible_watermark_available",
|
| 52 |
+
"is_librosa_available",
|
| 53 |
+
"is_note_seq_available",
|
| 54 |
+
"is_onnx_available",
|
| 55 |
+
"is_scipy_available",
|
| 56 |
+
"is_torch_available",
|
| 57 |
+
"is_torchsde_available",
|
| 58 |
+
"is_transformers_available",
|
| 59 |
+
"is_transformers_version",
|
| 60 |
+
"is_unidecode_available",
|
| 61 |
+
"logging",
|
| 62 |
+
],
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
try:
|
| 66 |
+
if not is_torch_available() and not is_accelerate_available() and not is_bitsandbytes_available():
|
| 67 |
+
raise OptionalDependencyNotAvailable()
|
| 68 |
+
except OptionalDependencyNotAvailable:
|
| 69 |
+
from .utils import dummy_bitsandbytes_objects
|
| 70 |
+
|
| 71 |
+
_import_structure["utils.dummy_bitsandbytes_objects"] = [
|
| 72 |
+
name for name in dir(dummy_bitsandbytes_objects) if not name.startswith("_")
|
| 73 |
+
]
|
| 74 |
+
else:
|
| 75 |
+
_import_structure["quantizers.quantization_config"].append("BitsAndBytesConfig")
|
| 76 |
+
|
| 77 |
+
try:
|
| 78 |
+
if not is_torch_available() and not is_accelerate_available() and not is_gguf_available():
|
| 79 |
+
raise OptionalDependencyNotAvailable()
|
| 80 |
+
except OptionalDependencyNotAvailable:
|
| 81 |
+
from .utils import dummy_gguf_objects
|
| 82 |
+
|
| 83 |
+
_import_structure["utils.dummy_gguf_objects"] = [
|
| 84 |
+
name for name in dir(dummy_gguf_objects) if not name.startswith("_")
|
| 85 |
+
]
|
| 86 |
+
else:
|
| 87 |
+
_import_structure["quantizers.quantization_config"].append("GGUFQuantizationConfig")
|
| 88 |
+
|
| 89 |
+
try:
|
| 90 |
+
if not is_torch_available() and not is_accelerate_available() and not is_torchao_available():
|
| 91 |
+
raise OptionalDependencyNotAvailable()
|
| 92 |
+
except OptionalDependencyNotAvailable:
|
| 93 |
+
from .utils import dummy_torchao_objects
|
| 94 |
+
|
| 95 |
+
_import_structure["utils.dummy_torchao_objects"] = [
|
| 96 |
+
name for name in dir(dummy_torchao_objects) if not name.startswith("_")
|
| 97 |
+
]
|
| 98 |
+
else:
|
| 99 |
+
_import_structure["quantizers.quantization_config"].append("TorchAoConfig")
|
| 100 |
+
|
| 101 |
+
try:
|
| 102 |
+
if not is_torch_available() and not is_accelerate_available() and not is_optimum_quanto_available():
|
| 103 |
+
raise OptionalDependencyNotAvailable()
|
| 104 |
+
except OptionalDependencyNotAvailable:
|
| 105 |
+
from .utils import dummy_optimum_quanto_objects
|
| 106 |
+
|
| 107 |
+
_import_structure["utils.dummy_optimum_quanto_objects"] = [
|
| 108 |
+
name for name in dir(dummy_optimum_quanto_objects) if not name.startswith("_")
|
| 109 |
+
]
|
| 110 |
+
else:
|
| 111 |
+
_import_structure["quantizers.quantization_config"].append("QuantoConfig")
|
| 112 |
+
|
| 113 |
+
try:
|
| 114 |
+
if not is_torch_available() and not is_accelerate_available() and not is_nvidia_modelopt_available():
|
| 115 |
+
raise OptionalDependencyNotAvailable()
|
| 116 |
+
except OptionalDependencyNotAvailable:
|
| 117 |
+
from .utils import dummy_nvidia_modelopt_objects
|
| 118 |
+
|
| 119 |
+
_import_structure["utils.dummy_nvidia_modelopt_objects"] = [
|
| 120 |
+
name for name in dir(dummy_nvidia_modelopt_objects) if not name.startswith("_")
|
| 121 |
+
]
|
| 122 |
+
else:
|
| 123 |
+
_import_structure["quantizers.quantization_config"].append("NVIDIAModelOptConfig")
|
| 124 |
+
|
| 125 |
+
try:
|
| 126 |
+
if not is_onnx_available():
|
| 127 |
+
raise OptionalDependencyNotAvailable()
|
| 128 |
+
except OptionalDependencyNotAvailable:
|
| 129 |
+
from .utils import dummy_onnx_objects # noqa F403
|
| 130 |
+
|
| 131 |
+
_import_structure["utils.dummy_onnx_objects"] = [
|
| 132 |
+
name for name in dir(dummy_onnx_objects) if not name.startswith("_")
|
| 133 |
+
]
|
| 134 |
+
|
| 135 |
+
else:
|
| 136 |
+
_import_structure["pipelines"].extend(["OnnxRuntimeModel"])
|
| 137 |
+
|
| 138 |
+
try:
|
| 139 |
+
if not is_torch_available():
|
| 140 |
+
raise OptionalDependencyNotAvailable()
|
| 141 |
+
except OptionalDependencyNotAvailable:
|
| 142 |
+
from .utils import dummy_pt_objects # noqa F403
|
| 143 |
+
|
| 144 |
+
_import_structure["utils.dummy_pt_objects"] = [name for name in dir(dummy_pt_objects) if not name.startswith("_")]
|
| 145 |
+
|
| 146 |
+
else:
|
| 147 |
+
_import_structure["guiders"].extend(
|
| 148 |
+
[
|
| 149 |
+
"AdaptiveProjectedGuidance",
|
| 150 |
+
"AdaptiveProjectedMixGuidance",
|
| 151 |
+
"AutoGuidance",
|
| 152 |
+
"BaseGuidance",
|
| 153 |
+
"ClassifierFreeGuidance",
|
| 154 |
+
"ClassifierFreeZeroStarGuidance",
|
| 155 |
+
"FrequencyDecoupledGuidance",
|
| 156 |
+
"PerturbedAttentionGuidance",
|
| 157 |
+
"SkipLayerGuidance",
|
| 158 |
+
"SmoothedEnergyGuidance",
|
| 159 |
+
"TangentialClassifierFreeGuidance",
|
| 160 |
+
]
|
| 161 |
+
)
|
| 162 |
+
_import_structure["hooks"].extend(
|
| 163 |
+
[
|
| 164 |
+
"FasterCacheConfig",
|
| 165 |
+
"FirstBlockCacheConfig",
|
| 166 |
+
"HookRegistry",
|
| 167 |
+
"LayerSkipConfig",
|
| 168 |
+
"MagCacheConfig",
|
| 169 |
+
"PyramidAttentionBroadcastConfig",
|
| 170 |
+
"SmoothedEnergyGuidanceConfig",
|
| 171 |
+
"TaylorSeerCacheConfig",
|
| 172 |
+
"TextKVCacheConfig",
|
| 173 |
+
"apply_faster_cache",
|
| 174 |
+
"apply_first_block_cache",
|
| 175 |
+
"apply_layer_skip",
|
| 176 |
+
"apply_mag_cache",
|
| 177 |
+
"apply_pyramid_attention_broadcast",
|
| 178 |
+
"apply_taylorseer_cache",
|
| 179 |
+
"apply_text_kv_cache",
|
| 180 |
+
]
|
| 181 |
+
)
|
| 182 |
+
_import_structure["image_processor"] = [
|
| 183 |
+
"InpaintProcessor",
|
| 184 |
+
"IPAdapterMaskProcessor",
|
| 185 |
+
"PixArtImageProcessor",
|
| 186 |
+
"VaeImageProcessor",
|
| 187 |
+
"VaeImageProcessorLDM3D",
|
| 188 |
+
]
|
| 189 |
+
_import_structure["models"].extend(
|
| 190 |
+
[
|
| 191 |
+
"AllegroTransformer3DModel",
|
| 192 |
+
"AsymmetricAutoencoderKL",
|
| 193 |
+
"AttentionBackendName",
|
| 194 |
+
"AuraFlowTransformer2DModel",
|
| 195 |
+
"AutoencoderDC",
|
| 196 |
+
"AutoencoderKL",
|
| 197 |
+
"AutoencoderKLAllegro",
|
| 198 |
+
"AutoencoderKLCogVideoX",
|
| 199 |
+
"AutoencoderKLCosmos",
|
| 200 |
+
"AutoencoderKLFlux2",
|
| 201 |
+
"AutoencoderKLHunyuanImage",
|
| 202 |
+
"AutoencoderKLHunyuanImageRefiner",
|
| 203 |
+
"AutoencoderKLHunyuanVideo",
|
| 204 |
+
"AutoencoderKLHunyuanVideo15",
|
| 205 |
+
"AutoencoderKLKVAE",
|
| 206 |
+
"AutoencoderKLKVAEVideo",
|
| 207 |
+
"AutoencoderKLLTX2Audio",
|
| 208 |
+
"AutoencoderKLLTX2Video",
|
| 209 |
+
"AutoencoderKLLTXVideo",
|
| 210 |
+
"AutoencoderKLMagvit",
|
| 211 |
+
"AutoencoderKLMochi",
|
| 212 |
+
"AutoencoderKLQwenImage",
|
| 213 |
+
"AutoencoderKLTemporalDecoder",
|
| 214 |
+
"AutoencoderKLWan",
|
| 215 |
+
"AutoencoderOobleck",
|
| 216 |
+
"AutoencoderRAE",
|
| 217 |
+
"AutoencoderTiny",
|
| 218 |
+
"AutoencoderVidTok",
|
| 219 |
+
"AutoModel",
|
| 220 |
+
"BriaFiboTransformer2DModel",
|
| 221 |
+
"BriaTransformer2DModel",
|
| 222 |
+
"CacheMixin",
|
| 223 |
+
"ChromaTransformer2DModel",
|
| 224 |
+
"ChronoEditTransformer3DModel",
|
| 225 |
+
"CogVideoXTransformer3DModel",
|
| 226 |
+
"CogView3PlusTransformer2DModel",
|
| 227 |
+
"CogView4Transformer2DModel",
|
| 228 |
+
"ConsisIDTransformer3DModel",
|
| 229 |
+
"ConsistencyDecoderVAE",
|
| 230 |
+
"ContextParallelConfig",
|
| 231 |
+
"ControlNetModel",
|
| 232 |
+
"ControlNetUnionModel",
|
| 233 |
+
"ControlNetXSAdapter",
|
| 234 |
+
"CosmosControlNetModel",
|
| 235 |
+
"CosmosTransformer3DModel",
|
| 236 |
+
"DiTTransformer2DModel",
|
| 237 |
+
"EasyAnimateTransformer3DModel",
|
| 238 |
+
"Flux2Transformer2DModel",
|
| 239 |
+
"FluxControlNetModel",
|
| 240 |
+
"FluxMultiControlNetModel",
|
| 241 |
+
"FluxTransformer2DModel",
|
| 242 |
+
"GlmImageTransformer2DModel",
|
| 243 |
+
"HeliosTransformer3DModel",
|
| 244 |
+
"HiDreamImageTransformer2DModel",
|
| 245 |
+
"HunyuanDiT2DControlNetModel",
|
| 246 |
+
"HunyuanDiT2DModel",
|
| 247 |
+
"HunyuanDiT2DMultiControlNetModel",
|
| 248 |
+
"HunyuanImageTransformer2DModel",
|
| 249 |
+
"HunyuanVideo15Transformer3DModel",
|
| 250 |
+
"HunyuanVideoFramepackTransformer3DModel",
|
| 251 |
+
"HunyuanVideoTransformer3DModel",
|
| 252 |
+
"I2VGenXLUNet",
|
| 253 |
+
"Kandinsky3UNet",
|
| 254 |
+
"Kandinsky5Transformer3DModel",
|
| 255 |
+
"LatteTransformer3DModel",
|
| 256 |
+
"LongCatImageTransformer2DModel",
|
| 257 |
+
"LTX2VideoTransformer3DModel",
|
| 258 |
+
"LTXVideoTransformer3DModel",
|
| 259 |
+
"Lumina2Transformer2DModel",
|
| 260 |
+
"LuminaNextDiT2DModel",
|
| 261 |
+
"MochiTransformer3DModel",
|
| 262 |
+
"ModelMixin",
|
| 263 |
+
"MotionAdapter",
|
| 264 |
+
"MultiAdapter",
|
| 265 |
+
"MultiControlNetModel",
|
| 266 |
+
"NucleusMoEImageTransformer2DModel",
|
| 267 |
+
"OmniGenTransformer2DModel",
|
| 268 |
+
"OvisImageTransformer2DModel",
|
| 269 |
+
"ParallelConfig",
|
| 270 |
+
"PixArtTransformer2DModel",
|
| 271 |
+
"PriorTransformer",
|
| 272 |
+
"PRXTransformer2DModel",
|
| 273 |
+
"QwenImageControlNetModel",
|
| 274 |
+
"QwenImageMultiControlNetModel",
|
| 275 |
+
"QwenImageTransformer2DModel",
|
| 276 |
+
"SanaControlNetModel",
|
| 277 |
+
"SanaTransformer2DModel",
|
| 278 |
+
"SanaVideoTransformer3DModel",
|
| 279 |
+
"SD3ControlNetModel",
|
| 280 |
+
"SD3MultiControlNetModel",
|
| 281 |
+
"SD3Transformer2DModel",
|
| 282 |
+
"SkyReelsV2Transformer3DModel",
|
| 283 |
+
"SparseControlNetModel",
|
| 284 |
+
"StableAudioDiTModel",
|
| 285 |
+
"StableCascadeUNet",
|
| 286 |
+
"T2IAdapter",
|
| 287 |
+
"T5FilmDecoder",
|
| 288 |
+
"Transformer2DModel",
|
| 289 |
+
"TransformerTemporalModel",
|
| 290 |
+
"UNet1DModel",
|
| 291 |
+
"UNet2DConditionModel",
|
| 292 |
+
"UNet2DModel",
|
| 293 |
+
"UNet3DConditionModel",
|
| 294 |
+
"UNetControlNetXSModel",
|
| 295 |
+
"UNetMotionModel",
|
| 296 |
+
"UNetSpatioTemporalConditionModel",
|
| 297 |
+
"UVit2DModel",
|
| 298 |
+
"VQModel",
|
| 299 |
+
"WanAnimateTransformer3DModel",
|
| 300 |
+
"WanTransformer3DModel",
|
| 301 |
+
"WanVACETransformer3DModel",
|
| 302 |
+
"ZImageControlNetModel",
|
| 303 |
+
"ZImageTransformer2DModel",
|
| 304 |
+
"attention_backend",
|
| 305 |
+
]
|
| 306 |
+
)
|
| 307 |
+
_import_structure["modular_pipelines"].extend(
|
| 308 |
+
[
|
| 309 |
+
"AutoPipelineBlocks",
|
| 310 |
+
"ComponentsManager",
|
| 311 |
+
"ComponentSpec",
|
| 312 |
+
"ConditionalPipelineBlocks",
|
| 313 |
+
"ConfigSpec",
|
| 314 |
+
"InputParam",
|
| 315 |
+
"LoopSequentialPipelineBlocks",
|
| 316 |
+
"ModularPipeline",
|
| 317 |
+
"ModularPipelineBlocks",
|
| 318 |
+
"OutputParam",
|
| 319 |
+
"SequentialPipelineBlocks",
|
| 320 |
+
]
|
| 321 |
+
)
|
| 322 |
+
_import_structure["optimization"] = [
|
| 323 |
+
"get_constant_schedule",
|
| 324 |
+
"get_constant_schedule_with_warmup",
|
| 325 |
+
"get_cosine_schedule_with_warmup",
|
| 326 |
+
"get_cosine_with_hard_restarts_schedule_with_warmup",
|
| 327 |
+
"get_linear_schedule_with_warmup",
|
| 328 |
+
"get_polynomial_decay_schedule_with_warmup",
|
| 329 |
+
"get_scheduler",
|
| 330 |
+
]
|
| 331 |
+
_import_structure["pipelines"].extend(
|
| 332 |
+
[
|
| 333 |
+
"AudioPipelineOutput",
|
| 334 |
+
"AutoPipelineForImage2Image",
|
| 335 |
+
"AutoPipelineForInpainting",
|
| 336 |
+
"AutoPipelineForText2Image",
|
| 337 |
+
"ConsistencyModelPipeline",
|
| 338 |
+
"DanceDiffusionPipeline",
|
| 339 |
+
"DDIMPipeline",
|
| 340 |
+
"DDPMPipeline",
|
| 341 |
+
"DiffusionPipeline",
|
| 342 |
+
"DiTPipeline",
|
| 343 |
+
"ImagePipelineOutput",
|
| 344 |
+
"KarrasVePipeline",
|
| 345 |
+
"LDMPipeline",
|
| 346 |
+
"LDMSuperResolutionPipeline",
|
| 347 |
+
"PNDMPipeline",
|
| 348 |
+
"RePaintPipeline",
|
| 349 |
+
"ScoreSdeVePipeline",
|
| 350 |
+
"StableDiffusionMixin",
|
| 351 |
+
]
|
| 352 |
+
)
|
| 353 |
+
_import_structure["quantizers"] = ["DiffusersQuantizer"]
|
| 354 |
+
_import_structure["schedulers"].extend(
|
| 355 |
+
[
|
| 356 |
+
"AmusedScheduler",
|
| 357 |
+
"BlockRefinementScheduler",
|
| 358 |
+
"BlockRefinementSchedulerOutput",
|
| 359 |
+
"CMStochasticIterativeScheduler",
|
| 360 |
+
"CogVideoXDDIMScheduler",
|
| 361 |
+
"CogVideoXDPMScheduler",
|
| 362 |
+
"DDIMInverseScheduler",
|
| 363 |
+
"DDIMParallelScheduler",
|
| 364 |
+
"DDIMScheduler",
|
| 365 |
+
"DDPMParallelScheduler",
|
| 366 |
+
"DDPMScheduler",
|
| 367 |
+
"DDPMWuerstchenScheduler",
|
| 368 |
+
"DEISMultistepScheduler",
|
| 369 |
+
"DPMSolverMultistepInverseScheduler",
|
| 370 |
+
"DPMSolverMultistepScheduler",
|
| 371 |
+
"DPMSolverSinglestepScheduler",
|
| 372 |
+
"EDMDPMSolverMultistepScheduler",
|
| 373 |
+
"EDMEulerScheduler",
|
| 374 |
+
"EulerAncestralDiscreteScheduler",
|
| 375 |
+
"EulerDiscreteScheduler",
|
| 376 |
+
"FlowMatchEulerDiscreteScheduler",
|
| 377 |
+
"FlowMatchHeunDiscreteScheduler",
|
| 378 |
+
"FlowMatchLCMScheduler",
|
| 379 |
+
"HeliosDMDScheduler",
|
| 380 |
+
"HeliosScheduler",
|
| 381 |
+
"HeunDiscreteScheduler",
|
| 382 |
+
"IPNDMScheduler",
|
| 383 |
+
"KarrasVeScheduler",
|
| 384 |
+
"KDPM2AncestralDiscreteScheduler",
|
| 385 |
+
"KDPM2DiscreteScheduler",
|
| 386 |
+
"LCMScheduler",
|
| 387 |
+
"LTXEulerAncestralRFScheduler",
|
| 388 |
+
"PNDMScheduler",
|
| 389 |
+
"RePaintScheduler",
|
| 390 |
+
"SASolverScheduler",
|
| 391 |
+
"SchedulerMixin",
|
| 392 |
+
"SCMScheduler",
|
| 393 |
+
"ScoreSdeVeScheduler",
|
| 394 |
+
"TCDScheduler",
|
| 395 |
+
"UnCLIPScheduler",
|
| 396 |
+
"UniPCMultistepScheduler",
|
| 397 |
+
"VQDiffusionScheduler",
|
| 398 |
+
]
|
| 399 |
+
)
|
| 400 |
+
_import_structure["training_utils"] = ["EMAModel"]
|
| 401 |
+
_import_structure["video_processor"] = ["VideoProcessor"]
|
| 402 |
+
|
| 403 |
+
try:
|
| 404 |
+
if not (is_torch_available() and is_scipy_available()):
|
| 405 |
+
raise OptionalDependencyNotAvailable()
|
| 406 |
+
except OptionalDependencyNotAvailable:
|
| 407 |
+
from .utils import dummy_torch_and_scipy_objects # noqa F403
|
| 408 |
+
|
| 409 |
+
_import_structure["utils.dummy_torch_and_scipy_objects"] = [
|
| 410 |
+
name for name in dir(dummy_torch_and_scipy_objects) if not name.startswith("_")
|
| 411 |
+
]
|
| 412 |
+
|
| 413 |
+
else:
|
| 414 |
+
_import_structure["schedulers"].extend(["LMSDiscreteScheduler"])
|
| 415 |
+
|
| 416 |
+
try:
|
| 417 |
+
if not (is_torch_available() and is_torchsde_available()):
|
| 418 |
+
raise OptionalDependencyNotAvailable()
|
| 419 |
+
except OptionalDependencyNotAvailable:
|
| 420 |
+
from .utils import dummy_torch_and_torchsde_objects # noqa F403
|
| 421 |
+
|
| 422 |
+
_import_structure["utils.dummy_torch_and_torchsde_objects"] = [
|
| 423 |
+
name for name in dir(dummy_torch_and_torchsde_objects) if not name.startswith("_")
|
| 424 |
+
]
|
| 425 |
+
|
| 426 |
+
else:
|
| 427 |
+
_import_structure["schedulers"].extend(["CosineDPMSolverMultistepScheduler", "DPMSolverSDEScheduler"])
|
| 428 |
+
|
| 429 |
+
try:
|
| 430 |
+
if not (is_torch_available() and is_transformers_available()):
|
| 431 |
+
raise OptionalDependencyNotAvailable()
|
| 432 |
+
except OptionalDependencyNotAvailable:
|
| 433 |
+
from .utils import dummy_torch_and_transformers_objects # noqa F403
|
| 434 |
+
|
| 435 |
+
_import_structure["utils.dummy_torch_and_transformers_objects"] = [
|
| 436 |
+
name for name in dir(dummy_torch_and_transformers_objects) if not name.startswith("_")
|
| 437 |
+
]
|
| 438 |
+
|
| 439 |
+
else:
|
| 440 |
+
_import_structure["modular_pipelines"].extend(
|
| 441 |
+
[
|
| 442 |
+
"Flux2AutoBlocks",
|
| 443 |
+
"Flux2KleinAutoBlocks",
|
| 444 |
+
"Flux2KleinBaseAutoBlocks",
|
| 445 |
+
"Flux2KleinBaseModularPipeline",
|
| 446 |
+
"Flux2KleinModularPipeline",
|
| 447 |
+
"Flux2ModularPipeline",
|
| 448 |
+
"FluxAutoBlocks",
|
| 449 |
+
"FluxKontextAutoBlocks",
|
| 450 |
+
"FluxKontextModularPipeline",
|
| 451 |
+
"FluxModularPipeline",
|
| 452 |
+
"HeliosAutoBlocks",
|
| 453 |
+
"HeliosModularPipeline",
|
| 454 |
+
"HeliosPyramidAutoBlocks",
|
| 455 |
+
"HeliosPyramidDistilledAutoBlocks",
|
| 456 |
+
"HeliosPyramidDistilledModularPipeline",
|
| 457 |
+
"HeliosPyramidModularPipeline",
|
| 458 |
+
"QwenImageAutoBlocks",
|
| 459 |
+
"QwenImageEditAutoBlocks",
|
| 460 |
+
"QwenImageEditModularPipeline",
|
| 461 |
+
"QwenImageEditPlusAutoBlocks",
|
| 462 |
+
"QwenImageEditPlusModularPipeline",
|
| 463 |
+
"QwenImageLayeredAutoBlocks",
|
| 464 |
+
"QwenImageLayeredModularPipeline",
|
| 465 |
+
"QwenImageModularPipeline",
|
| 466 |
+
"StableDiffusionXLAutoBlocks",
|
| 467 |
+
"StableDiffusionXLModularPipeline",
|
| 468 |
+
"Wan22Blocks",
|
| 469 |
+
"Wan22Image2VideoBlocks",
|
| 470 |
+
"Wan22Image2VideoModularPipeline",
|
| 471 |
+
"Wan22ModularPipeline",
|
| 472 |
+
"WanBlocks",
|
| 473 |
+
"WanImage2VideoAutoBlocks",
|
| 474 |
+
"WanImage2VideoModularPipeline",
|
| 475 |
+
"WanModularPipeline",
|
| 476 |
+
"ZImageAutoBlocks",
|
| 477 |
+
"ZImageModularPipeline",
|
| 478 |
+
]
|
| 479 |
+
)
|
| 480 |
+
_import_structure["pipelines"].extend(
|
| 481 |
+
[
|
| 482 |
+
"AllegroPipeline",
|
| 483 |
+
"AltDiffusionImg2ImgPipeline",
|
| 484 |
+
"AltDiffusionPipeline",
|
| 485 |
+
"AmusedImg2ImgPipeline",
|
| 486 |
+
"AmusedInpaintPipeline",
|
| 487 |
+
"AmusedPipeline",
|
| 488 |
+
"AnimateDiffControlNetPipeline",
|
| 489 |
+
"AnimateDiffPAGPipeline",
|
| 490 |
+
"AnimateDiffPipeline",
|
| 491 |
+
"AnimateDiffSDXLPipeline",
|
| 492 |
+
"AnimateDiffSparseControlNetPipeline",
|
| 493 |
+
"AnimateDiffVideoToVideoControlNetPipeline",
|
| 494 |
+
"AnimateDiffVideoToVideoPipeline",
|
| 495 |
+
"AudioLDM2Pipeline",
|
| 496 |
+
"AudioLDM2ProjectionModel",
|
| 497 |
+
"AudioLDM2UNet2DConditionModel",
|
| 498 |
+
"AudioLDMPipeline",
|
| 499 |
+
"AuraFlowPipeline",
|
| 500 |
+
"BlipDiffusionControlNetPipeline",
|
| 501 |
+
"BlipDiffusionPipeline",
|
| 502 |
+
"BriaFiboEditPipeline",
|
| 503 |
+
"BriaFiboPipeline",
|
| 504 |
+
"BriaPipeline",
|
| 505 |
+
"ChromaImg2ImgPipeline",
|
| 506 |
+
"ChromaInpaintPipeline",
|
| 507 |
+
"ChromaPipeline",
|
| 508 |
+
"ChronoEditPipeline",
|
| 509 |
+
"CLIPImageProjection",
|
| 510 |
+
"CogVideoXFunControlPipeline",
|
| 511 |
+
"CogVideoXImageToVideoPipeline",
|
| 512 |
+
"CogVideoXPipeline",
|
| 513 |
+
"CogVideoXVideoToVideoPipeline",
|
| 514 |
+
"CogView3PlusPipeline",
|
| 515 |
+
"CogView4ControlPipeline",
|
| 516 |
+
"CogView4Pipeline",
|
| 517 |
+
"ConsisIDPipeline",
|
| 518 |
+
"Cosmos2_5_PredictBasePipeline",
|
| 519 |
+
"Cosmos2_5_TransferPipeline",
|
| 520 |
+
"Cosmos2TextToImagePipeline",
|
| 521 |
+
"Cosmos2VideoToWorldPipeline",
|
| 522 |
+
"CosmosTextToWorldPipeline",
|
| 523 |
+
"CosmosVideoToWorldPipeline",
|
| 524 |
+
"CycleDiffusionPipeline",
|
| 525 |
+
"EasyAnimateControlPipeline",
|
| 526 |
+
"EasyAnimateInpaintPipeline",
|
| 527 |
+
"EasyAnimatePipeline",
|
| 528 |
+
"Flux2KleinKVPipeline",
|
| 529 |
+
"Flux2KleinPipeline",
|
| 530 |
+
"Flux2Pipeline",
|
| 531 |
+
"FluxControlImg2ImgPipeline",
|
| 532 |
+
"FluxControlInpaintPipeline",
|
| 533 |
+
"FluxControlNetImg2ImgPipeline",
|
| 534 |
+
"FluxControlNetInpaintPipeline",
|
| 535 |
+
"FluxControlNetPipeline",
|
| 536 |
+
"FluxControlPipeline",
|
| 537 |
+
"FluxFillPipeline",
|
| 538 |
+
"FluxImg2ImgPipeline",
|
| 539 |
+
"FluxInpaintPipeline",
|
| 540 |
+
"FluxKontextInpaintPipeline",
|
| 541 |
+
"FluxKontextPipeline",
|
| 542 |
+
"FluxPipeline",
|
| 543 |
+
"FluxPriorReduxPipeline",
|
| 544 |
+
"GlmImagePipeline",
|
| 545 |
+
"HeliosPipeline",
|
| 546 |
+
"HeliosPyramidPipeline",
|
| 547 |
+
"HiDreamImagePipeline",
|
| 548 |
+
"HunyuanDiTControlNetPipeline",
|
| 549 |
+
"HunyuanDiTPAGPipeline",
|
| 550 |
+
"HunyuanDiTPipeline",
|
| 551 |
+
"HunyuanImagePipeline",
|
| 552 |
+
"HunyuanImageRefinerPipeline",
|
| 553 |
+
"HunyuanSkyreelsImageToVideoPipeline",
|
| 554 |
+
"HunyuanVideo15ImageToVideoPipeline",
|
| 555 |
+
"HunyuanVideo15Pipeline",
|
| 556 |
+
"HunyuanVideoFramepackPipeline",
|
| 557 |
+
"HunyuanVideoImageToVideoPipeline",
|
| 558 |
+
"HunyuanVideoPipeline",
|
| 559 |
+
"I2VGenXLPipeline",
|
| 560 |
+
"IFImg2ImgPipeline",
|
| 561 |
+
"IFImg2ImgSuperResolutionPipeline",
|
| 562 |
+
"IFInpaintingPipeline",
|
| 563 |
+
"IFInpaintingSuperResolutionPipeline",
|
| 564 |
+
"IFPipeline",
|
| 565 |
+
"IFSuperResolutionPipeline",
|
| 566 |
+
"ImageTextPipelineOutput",
|
| 567 |
+
"Kandinsky3Img2ImgPipeline",
|
| 568 |
+
"Kandinsky3Pipeline",
|
| 569 |
+
"Kandinsky5I2IPipeline",
|
| 570 |
+
"Kandinsky5I2VPipeline",
|
| 571 |
+
"Kandinsky5T2IPipeline",
|
| 572 |
+
"Kandinsky5T2VPipeline",
|
| 573 |
+
"KandinskyCombinedPipeline",
|
| 574 |
+
"KandinskyImg2ImgCombinedPipeline",
|
| 575 |
+
"KandinskyImg2ImgPipeline",
|
| 576 |
+
"KandinskyInpaintCombinedPipeline",
|
| 577 |
+
"KandinskyInpaintPipeline",
|
| 578 |
+
"KandinskyPipeline",
|
| 579 |
+
"KandinskyPriorPipeline",
|
| 580 |
+
"KandinskyV22CombinedPipeline",
|
| 581 |
+
"KandinskyV22ControlnetImg2ImgPipeline",
|
| 582 |
+
"KandinskyV22ControlnetPipeline",
|
| 583 |
+
"KandinskyV22Img2ImgCombinedPipeline",
|
| 584 |
+
"KandinskyV22Img2ImgPipeline",
|
| 585 |
+
"KandinskyV22InpaintCombinedPipeline",
|
| 586 |
+
"KandinskyV22InpaintPipeline",
|
| 587 |
+
"KandinskyV22Pipeline",
|
| 588 |
+
"KandinskyV22PriorEmb2EmbPipeline",
|
| 589 |
+
"KandinskyV22PriorPipeline",
|
| 590 |
+
"LatentConsistencyModelImg2ImgPipeline",
|
| 591 |
+
"LatentConsistencyModelPipeline",
|
| 592 |
+
"LattePipeline",
|
| 593 |
+
"LDMTextToImagePipeline",
|
| 594 |
+
"LEditsPPPipelineStableDiffusion",
|
| 595 |
+
"LEditsPPPipelineStableDiffusionXL",
|
| 596 |
+
"LLaDA2Pipeline",
|
| 597 |
+
"LLaDA2PipelineOutput",
|
| 598 |
+
"LongCatImageEditPipeline",
|
| 599 |
+
"LongCatImagePipeline",
|
| 600 |
+
"LTX2ConditionPipeline",
|
| 601 |
+
"LTX2ImageToVideoPipeline",
|
| 602 |
+
"LTX2LatentUpsamplePipeline",
|
| 603 |
+
"LTX2Pipeline",
|
| 604 |
+
"LTXConditionPipeline",
|
| 605 |
+
"LTXI2VLongMultiPromptPipeline",
|
| 606 |
+
"LTXImageToVideoPipeline",
|
| 607 |
+
"LTXLatentUpsamplePipeline",
|
| 608 |
+
"LTXPipeline",
|
| 609 |
+
"LucyEditPipeline",
|
| 610 |
+
"Lumina2Pipeline",
|
| 611 |
+
"Lumina2Text2ImgPipeline",
|
| 612 |
+
"LuminaPipeline",
|
| 613 |
+
"LuminaText2ImgPipeline",
|
| 614 |
+
"MarigoldDepthPipeline",
|
| 615 |
+
"MarigoldIntrinsicsPipeline",
|
| 616 |
+
"MarigoldNormalsPipeline",
|
| 617 |
+
"MochiPipeline",
|
| 618 |
+
"MusicLDMPipeline",
|
| 619 |
+
"NucleusMoEImagePipeline",
|
| 620 |
+
"OmniGenPipeline",
|
| 621 |
+
"OvisImagePipeline",
|
| 622 |
+
"PaintByExamplePipeline",
|
| 623 |
+
"PIAPipeline",
|
| 624 |
+
"PixArtAlphaPipeline",
|
| 625 |
+
"PixArtSigmaPAGPipeline",
|
| 626 |
+
"PixArtSigmaPipeline",
|
| 627 |
+
"PRXPipeline",
|
| 628 |
+
"QwenImageControlNetInpaintPipeline",
|
| 629 |
+
"QwenImageControlNetPipeline",
|
| 630 |
+
"QwenImageEditInpaintPipeline",
|
| 631 |
+
"QwenImageEditPipeline",
|
| 632 |
+
"QwenImageEditPlusPipeline",
|
| 633 |
+
"QwenImageImg2ImgPipeline",
|
| 634 |
+
"QwenImageInpaintPipeline",
|
| 635 |
+
"QwenImageLayeredPipeline",
|
| 636 |
+
"QwenImagePipeline",
|
| 637 |
+
"ReduxImageEncoder",
|
| 638 |
+
"SanaControlNetPipeline",
|
| 639 |
+
"SanaImageToVideoPipeline",
|
| 640 |
+
"SanaPAGPipeline",
|
| 641 |
+
"SanaPipeline",
|
| 642 |
+
"SanaSprintImg2ImgPipeline",
|
| 643 |
+
"SanaSprintPipeline",
|
| 644 |
+
"SanaVideoPipeline",
|
| 645 |
+
"SanaVideoPipeline",
|
| 646 |
+
"SemanticStableDiffusionPipeline",
|
| 647 |
+
"ShapEImg2ImgPipeline",
|
| 648 |
+
"ShapEPipeline",
|
| 649 |
+
"SkyReelsV2DiffusionForcingImageToVideoPipeline",
|
| 650 |
+
"SkyReelsV2DiffusionForcingPipeline",
|
| 651 |
+
"SkyReelsV2DiffusionForcingVideoToVideoPipeline",
|
| 652 |
+
"SkyReelsV2ImageToVideoPipeline",
|
| 653 |
+
"SkyReelsV2Pipeline",
|
| 654 |
+
"StableAudioPipeline",
|
| 655 |
+
"StableAudioProjectionModel",
|
| 656 |
+
"StableCascadeCombinedPipeline",
|
| 657 |
+
"StableCascadeDecoderPipeline",
|
| 658 |
+
"StableCascadePriorPipeline",
|
| 659 |
+
"StableDiffusion3ControlNetInpaintingPipeline",
|
| 660 |
+
"StableDiffusion3ControlNetPipeline",
|
| 661 |
+
"StableDiffusion3Img2ImgPipeline",
|
| 662 |
+
"StableDiffusion3InpaintPipeline",
|
| 663 |
+
"StableDiffusion3PAGImg2ImgPipeline",
|
| 664 |
+
"StableDiffusion3PAGImg2ImgPipeline",
|
| 665 |
+
"StableDiffusion3PAGPipeline",
|
| 666 |
+
"StableDiffusion3Pipeline",
|
| 667 |
+
"StableDiffusionAdapterPipeline",
|
| 668 |
+
"StableDiffusionAttendAndExcitePipeline",
|
| 669 |
+
"StableDiffusionControlNetImg2ImgPipeline",
|
| 670 |
+
"StableDiffusionControlNetInpaintPipeline",
|
| 671 |
+
"StableDiffusionControlNetPAGInpaintPipeline",
|
| 672 |
+
"StableDiffusionControlNetPAGPipeline",
|
| 673 |
+
"StableDiffusionControlNetPipeline",
|
| 674 |
+
"StableDiffusionControlNetXSPipeline",
|
| 675 |
+
"StableDiffusionDepth2ImgPipeline",
|
| 676 |
+
"StableDiffusionDiffEditPipeline",
|
| 677 |
+
"StableDiffusionGLIGENPipeline",
|
| 678 |
+
"StableDiffusionGLIGENTextImagePipeline",
|
| 679 |
+
"StableDiffusionImageVariationPipeline",
|
| 680 |
+
"StableDiffusionImg2ImgPipeline",
|
| 681 |
+
"StableDiffusionInpaintPipeline",
|
| 682 |
+
"StableDiffusionInpaintPipelineLegacy",
|
| 683 |
+
"StableDiffusionInstructPix2PixPipeline",
|
| 684 |
+
"StableDiffusionLatentUpscalePipeline",
|
| 685 |
+
"StableDiffusionLDM3DPipeline",
|
| 686 |
+
"StableDiffusionModelEditingPipeline",
|
| 687 |
+
"StableDiffusionPAGImg2ImgPipeline",
|
| 688 |
+
"StableDiffusionPAGInpaintPipeline",
|
| 689 |
+
"StableDiffusionPAGPipeline",
|
| 690 |
+
"StableDiffusionPanoramaPipeline",
|
| 691 |
+
"StableDiffusionParadigmsPipeline",
|
| 692 |
+
"StableDiffusionPipeline",
|
| 693 |
+
"StableDiffusionPipelineSafe",
|
| 694 |
+
"StableDiffusionPix2PixZeroPipeline",
|
| 695 |
+
"StableDiffusionSAGPipeline",
|
| 696 |
+
"StableDiffusionUpscalePipeline",
|
| 697 |
+
"StableDiffusionXLAdapterPipeline",
|
| 698 |
+
"StableDiffusionXLControlNetImg2ImgPipeline",
|
| 699 |
+
"StableDiffusionXLControlNetInpaintPipeline",
|
| 700 |
+
"StableDiffusionXLControlNetPAGImg2ImgPipeline",
|
| 701 |
+
"StableDiffusionXLControlNetPAGPipeline",
|
| 702 |
+
"StableDiffusionXLControlNetPipeline",
|
| 703 |
+
"StableDiffusionXLControlNetUnionImg2ImgPipeline",
|
| 704 |
+
"StableDiffusionXLControlNetUnionInpaintPipeline",
|
| 705 |
+
"StableDiffusionXLControlNetUnionPipeline",
|
| 706 |
+
"StableDiffusionXLControlNetXSPipeline",
|
| 707 |
+
"StableDiffusionXLImg2ImgPipeline",
|
| 708 |
+
"StableDiffusionXLInpaintPipeline",
|
| 709 |
+
"StableDiffusionXLInstructPix2PixPipeline",
|
| 710 |
+
"StableDiffusionXLPAGImg2ImgPipeline",
|
| 711 |
+
"StableDiffusionXLPAGInpaintPipeline",
|
| 712 |
+
"StableDiffusionXLPAGPipeline",
|
| 713 |
+
"StableDiffusionXLPipeline",
|
| 714 |
+
"StableUnCLIPImg2ImgPipeline",
|
| 715 |
+
"StableUnCLIPPipeline",
|
| 716 |
+
"StableVideoDiffusionPipeline",
|
| 717 |
+
"TextToVideoSDPipeline",
|
| 718 |
+
"TextToVideoZeroPipeline",
|
| 719 |
+
"TextToVideoZeroSDXLPipeline",
|
| 720 |
+
"UnCLIPImageVariationPipeline",
|
| 721 |
+
"UnCLIPPipeline",
|
| 722 |
+
"UniDiffuserModel",
|
| 723 |
+
"UniDiffuserPipeline",
|
| 724 |
+
"UniDiffuserTextDecoder",
|
| 725 |
+
"VersatileDiffusionDualGuidedPipeline",
|
| 726 |
+
"VersatileDiffusionImageVariationPipeline",
|
| 727 |
+
"VersatileDiffusionPipeline",
|
| 728 |
+
"VersatileDiffusionTextToImagePipeline",
|
| 729 |
+
"VideoToVideoSDPipeline",
|
| 730 |
+
"VisualClozeGenerationPipeline",
|
| 731 |
+
"VisualClozePipeline",
|
| 732 |
+
"VQDiffusionPipeline",
|
| 733 |
+
"WanAnimatePipeline",
|
| 734 |
+
"WanImageToVideoPipeline",
|
| 735 |
+
"WanPipeline",
|
| 736 |
+
"WanVACEPipeline",
|
| 737 |
+
"WanVideoToVideoPipeline",
|
| 738 |
+
"WuerstchenCombinedPipeline",
|
| 739 |
+
"WuerstchenDecoderPipeline",
|
| 740 |
+
"WuerstchenPriorPipeline",
|
| 741 |
+
"ZImageControlNetInpaintPipeline",
|
| 742 |
+
"ZImageControlNetPipeline",
|
| 743 |
+
"ZImageImg2ImgPipeline",
|
| 744 |
+
"ZImageInpaintPipeline",
|
| 745 |
+
"ZImageOmniPipeline",
|
| 746 |
+
"ZImagePipeline",
|
| 747 |
+
]
|
| 748 |
+
)
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
try:
|
| 752 |
+
if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
|
| 753 |
+
raise OptionalDependencyNotAvailable()
|
| 754 |
+
except OptionalDependencyNotAvailable:
|
| 755 |
+
from .utils import dummy_torch_and_transformers_and_opencv_objects # noqa F403
|
| 756 |
+
|
| 757 |
+
_import_structure["utils.dummy_torch_and_transformers_and_opencv_objects"] = [
|
| 758 |
+
name for name in dir(dummy_torch_and_transformers_and_opencv_objects) if not name.startswith("_")
|
| 759 |
+
]
|
| 760 |
+
|
| 761 |
+
else:
|
| 762 |
+
_import_structure["pipelines"].extend(["ConsisIDPipeline"])
|
| 763 |
+
|
| 764 |
+
try:
|
| 765 |
+
if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
|
| 766 |
+
raise OptionalDependencyNotAvailable()
|
| 767 |
+
except OptionalDependencyNotAvailable:
|
| 768 |
+
from .utils import dummy_torch_and_transformers_and_sentencepiece_objects # noqa F403
|
| 769 |
+
|
| 770 |
+
_import_structure["utils.dummy_torch_and_transformers_and_sentencepiece_objects"] = [
|
| 771 |
+
name for name in dir(dummy_torch_and_transformers_and_sentencepiece_objects) if not name.startswith("_")
|
| 772 |
+
]
|
| 773 |
+
|
| 774 |
+
else:
|
| 775 |
+
_import_structure["pipelines"].extend(["KolorsImg2ImgPipeline", "KolorsPAGPipeline", "KolorsPipeline"])
|
| 776 |
+
|
| 777 |
+
try:
|
| 778 |
+
if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
|
| 779 |
+
raise OptionalDependencyNotAvailable()
|
| 780 |
+
except OptionalDependencyNotAvailable:
|
| 781 |
+
from .utils import dummy_torch_and_transformers_and_onnx_objects # noqa F403
|
| 782 |
+
|
| 783 |
+
_import_structure["utils.dummy_torch_and_transformers_and_onnx_objects"] = [
|
| 784 |
+
name for name in dir(dummy_torch_and_transformers_and_onnx_objects) if not name.startswith("_")
|
| 785 |
+
]
|
| 786 |
+
|
| 787 |
+
else:
|
| 788 |
+
_import_structure["pipelines"].extend(
|
| 789 |
+
[
|
| 790 |
+
"OnnxStableDiffusionImg2ImgPipeline",
|
| 791 |
+
"OnnxStableDiffusionInpaintPipeline",
|
| 792 |
+
"OnnxStableDiffusionInpaintPipelineLegacy",
|
| 793 |
+
"OnnxStableDiffusionPipeline",
|
| 794 |
+
"OnnxStableDiffusionUpscalePipeline",
|
| 795 |
+
"StableDiffusionOnnxPipeline",
|
| 796 |
+
]
|
| 797 |
+
)
|
| 798 |
+
|
| 799 |
+
try:
|
| 800 |
+
if not (is_torch_available() and is_librosa_available()):
|
| 801 |
+
raise OptionalDependencyNotAvailable()
|
| 802 |
+
except OptionalDependencyNotAvailable:
|
| 803 |
+
from .utils import dummy_torch_and_librosa_objects # noqa F403
|
| 804 |
+
|
| 805 |
+
_import_structure["utils.dummy_torch_and_librosa_objects"] = [
|
| 806 |
+
name for name in dir(dummy_torch_and_librosa_objects) if not name.startswith("_")
|
| 807 |
+
]
|
| 808 |
+
|
| 809 |
+
else:
|
| 810 |
+
_import_structure["pipelines"].extend(["AudioDiffusionPipeline", "Mel"])
|
| 811 |
+
|
| 812 |
+
try:
|
| 813 |
+
if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
|
| 814 |
+
raise OptionalDependencyNotAvailable()
|
| 815 |
+
except OptionalDependencyNotAvailable:
|
| 816 |
+
from .utils import dummy_transformers_and_torch_and_note_seq_objects # noqa F403
|
| 817 |
+
|
| 818 |
+
_import_structure["utils.dummy_transformers_and_torch_and_note_seq_objects"] = [
|
| 819 |
+
name for name in dir(dummy_transformers_and_torch_and_note_seq_objects) if not name.startswith("_")
|
| 820 |
+
]
|
| 821 |
+
|
| 822 |
+
|
| 823 |
+
else:
|
| 824 |
+
_import_structure["pipelines"].extend(["SpectrogramDiffusionPipeline"])
|
| 825 |
+
|
| 826 |
+
try:
|
| 827 |
+
if not is_flax_available():
|
| 828 |
+
raise OptionalDependencyNotAvailable()
|
| 829 |
+
except OptionalDependencyNotAvailable:
|
| 830 |
+
from .utils import dummy_flax_objects # noqa F403
|
| 831 |
+
|
| 832 |
+
_import_structure["utils.dummy_flax_objects"] = [
|
| 833 |
+
name for name in dir(dummy_flax_objects) if not name.startswith("_")
|
| 834 |
+
]
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
else:
|
| 838 |
+
_import_structure["models.controlnets.controlnet_flax"] = ["FlaxControlNetModel"]
|
| 839 |
+
_import_structure["models.modeling_flax_utils"] = ["FlaxModelMixin"]
|
| 840 |
+
_import_structure["models.unets.unet_2d_condition_flax"] = ["FlaxUNet2DConditionModel"]
|
| 841 |
+
_import_structure["models.vae_flax"] = ["FlaxAutoencoderKL"]
|
| 842 |
+
_import_structure["pipelines"].extend(["FlaxDiffusionPipeline"])
|
| 843 |
+
_import_structure["schedulers"].extend(
|
| 844 |
+
[
|
| 845 |
+
"FlaxDDIMScheduler",
|
| 846 |
+
"FlaxDDPMScheduler",
|
| 847 |
+
"FlaxDPMSolverMultistepScheduler",
|
| 848 |
+
"FlaxEulerDiscreteScheduler",
|
| 849 |
+
"FlaxKarrasVeScheduler",
|
| 850 |
+
"FlaxLMSDiscreteScheduler",
|
| 851 |
+
"FlaxPNDMScheduler",
|
| 852 |
+
"FlaxSchedulerMixin",
|
| 853 |
+
"FlaxScoreSdeVeScheduler",
|
| 854 |
+
]
|
| 855 |
+
)
|
| 856 |
+
|
| 857 |
+
|
| 858 |
+
try:
|
| 859 |
+
if not (is_flax_available() and is_transformers_available()):
|
| 860 |
+
raise OptionalDependencyNotAvailable()
|
| 861 |
+
except OptionalDependencyNotAvailable:
|
| 862 |
+
from .utils import dummy_flax_and_transformers_objects # noqa F403
|
| 863 |
+
|
| 864 |
+
_import_structure["utils.dummy_flax_and_transformers_objects"] = [
|
| 865 |
+
name for name in dir(dummy_flax_and_transformers_objects) if not name.startswith("_")
|
| 866 |
+
]
|
| 867 |
+
|
| 868 |
+
|
| 869 |
+
else:
|
| 870 |
+
_import_structure["pipelines"].extend(
|
| 871 |
+
[
|
| 872 |
+
"FlaxStableDiffusionControlNetPipeline",
|
| 873 |
+
"FlaxStableDiffusionImg2ImgPipeline",
|
| 874 |
+
"FlaxStableDiffusionInpaintPipeline",
|
| 875 |
+
"FlaxStableDiffusionPipeline",
|
| 876 |
+
"FlaxStableDiffusionXLPipeline",
|
| 877 |
+
]
|
| 878 |
+
)
|
| 879 |
+
|
| 880 |
+
try:
|
| 881 |
+
if not (is_note_seq_available()):
|
| 882 |
+
raise OptionalDependencyNotAvailable()
|
| 883 |
+
except OptionalDependencyNotAvailable:
|
| 884 |
+
from .utils import dummy_note_seq_objects # noqa F403
|
| 885 |
+
|
| 886 |
+
_import_structure["utils.dummy_note_seq_objects"] = [
|
| 887 |
+
name for name in dir(dummy_note_seq_objects) if not name.startswith("_")
|
| 888 |
+
]
|
| 889 |
+
|
| 890 |
+
|
| 891 |
+
else:
|
| 892 |
+
_import_structure["pipelines"].extend(["MidiProcessor"])
|
| 893 |
+
|
| 894 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 895 |
+
from .configuration_utils import ConfigMixin
|
| 896 |
+
from .quantizers import PipelineQuantizationConfig
|
| 897 |
+
|
| 898 |
+
try:
|
| 899 |
+
if not is_bitsandbytes_available():
|
| 900 |
+
raise OptionalDependencyNotAvailable()
|
| 901 |
+
except OptionalDependencyNotAvailable:
|
| 902 |
+
from .utils.dummy_bitsandbytes_objects import *
|
| 903 |
+
else:
|
| 904 |
+
from .quantizers.quantization_config import BitsAndBytesConfig
|
| 905 |
+
|
| 906 |
+
try:
|
| 907 |
+
if not is_gguf_available():
|
| 908 |
+
raise OptionalDependencyNotAvailable()
|
| 909 |
+
except OptionalDependencyNotAvailable:
|
| 910 |
+
from .utils.dummy_gguf_objects import *
|
| 911 |
+
else:
|
| 912 |
+
from .quantizers.quantization_config import GGUFQuantizationConfig
|
| 913 |
+
|
| 914 |
+
try:
|
| 915 |
+
if not is_torchao_available():
|
| 916 |
+
raise OptionalDependencyNotAvailable()
|
| 917 |
+
except OptionalDependencyNotAvailable:
|
| 918 |
+
from .utils.dummy_torchao_objects import *
|
| 919 |
+
else:
|
| 920 |
+
from .quantizers.quantization_config import TorchAoConfig
|
| 921 |
+
|
| 922 |
+
try:
|
| 923 |
+
if not is_optimum_quanto_available():
|
| 924 |
+
raise OptionalDependencyNotAvailable()
|
| 925 |
+
except OptionalDependencyNotAvailable:
|
| 926 |
+
from .utils.dummy_optimum_quanto_objects import *
|
| 927 |
+
else:
|
| 928 |
+
from .quantizers.quantization_config import QuantoConfig
|
| 929 |
+
|
| 930 |
+
try:
|
| 931 |
+
if not is_nvidia_modelopt_available():
|
| 932 |
+
raise OptionalDependencyNotAvailable()
|
| 933 |
+
except OptionalDependencyNotAvailable:
|
| 934 |
+
from .utils.dummy_nvidia_modelopt_objects import *
|
| 935 |
+
else:
|
| 936 |
+
from .quantizers.quantization_config import NVIDIAModelOptConfig
|
| 937 |
+
|
| 938 |
+
try:
|
| 939 |
+
if not is_onnx_available():
|
| 940 |
+
raise OptionalDependencyNotAvailable()
|
| 941 |
+
except OptionalDependencyNotAvailable:
|
| 942 |
+
from .utils.dummy_onnx_objects import * # noqa F403
|
| 943 |
+
else:
|
| 944 |
+
from .pipelines import OnnxRuntimeModel
|
| 945 |
+
|
| 946 |
+
try:
|
| 947 |
+
if not is_torch_available():
|
| 948 |
+
raise OptionalDependencyNotAvailable()
|
| 949 |
+
except OptionalDependencyNotAvailable:
|
| 950 |
+
from .utils.dummy_pt_objects import * # noqa F403
|
| 951 |
+
else:
|
| 952 |
+
from .guiders import (
|
| 953 |
+
AdaptiveProjectedGuidance,
|
| 954 |
+
AdaptiveProjectedMixGuidance,
|
| 955 |
+
AutoGuidance,
|
| 956 |
+
BaseGuidance,
|
| 957 |
+
ClassifierFreeGuidance,
|
| 958 |
+
ClassifierFreeZeroStarGuidance,
|
| 959 |
+
FrequencyDecoupledGuidance,
|
| 960 |
+
PerturbedAttentionGuidance,
|
| 961 |
+
SkipLayerGuidance,
|
| 962 |
+
SmoothedEnergyGuidance,
|
| 963 |
+
TangentialClassifierFreeGuidance,
|
| 964 |
+
)
|
| 965 |
+
from .hooks import (
|
| 966 |
+
FasterCacheConfig,
|
| 967 |
+
FirstBlockCacheConfig,
|
| 968 |
+
HookRegistry,
|
| 969 |
+
LayerSkipConfig,
|
| 970 |
+
MagCacheConfig,
|
| 971 |
+
PyramidAttentionBroadcastConfig,
|
| 972 |
+
SmoothedEnergyGuidanceConfig,
|
| 973 |
+
TaylorSeerCacheConfig,
|
| 974 |
+
TextKVCacheConfig,
|
| 975 |
+
apply_faster_cache,
|
| 976 |
+
apply_first_block_cache,
|
| 977 |
+
apply_layer_skip,
|
| 978 |
+
apply_mag_cache,
|
| 979 |
+
apply_pyramid_attention_broadcast,
|
| 980 |
+
apply_taylorseer_cache,
|
| 981 |
+
apply_text_kv_cache,
|
| 982 |
+
)
|
| 983 |
+
from .image_processor import (
|
| 984 |
+
InpaintProcessor,
|
| 985 |
+
IPAdapterMaskProcessor,
|
| 986 |
+
PixArtImageProcessor,
|
| 987 |
+
VaeImageProcessor,
|
| 988 |
+
VaeImageProcessorLDM3D,
|
| 989 |
+
)
|
| 990 |
+
from .models import (
|
| 991 |
+
AllegroTransformer3DModel,
|
| 992 |
+
AsymmetricAutoencoderKL,
|
| 993 |
+
AttentionBackendName,
|
| 994 |
+
AuraFlowTransformer2DModel,
|
| 995 |
+
AutoencoderDC,
|
| 996 |
+
AutoencoderKL,
|
| 997 |
+
AutoencoderKLAllegro,
|
| 998 |
+
AutoencoderKLCogVideoX,
|
| 999 |
+
AutoencoderKLCosmos,
|
| 1000 |
+
AutoencoderKLFlux2,
|
| 1001 |
+
AutoencoderKLHunyuanImage,
|
| 1002 |
+
AutoencoderKLHunyuanImageRefiner,
|
| 1003 |
+
AutoencoderKLHunyuanVideo,
|
| 1004 |
+
AutoencoderKLHunyuanVideo15,
|
| 1005 |
+
AutoencoderKLKVAE,
|
| 1006 |
+
AutoencoderKLKVAEVideo,
|
| 1007 |
+
AutoencoderKLLTX2Audio,
|
| 1008 |
+
AutoencoderKLLTX2Video,
|
| 1009 |
+
AutoencoderKLLTXVideo,
|
| 1010 |
+
AutoencoderKLMagvit,
|
| 1011 |
+
AutoencoderKLMochi,
|
| 1012 |
+
AutoencoderKLQwenImage,
|
| 1013 |
+
AutoencoderKLTemporalDecoder,
|
| 1014 |
+
AutoencoderKLWan,
|
| 1015 |
+
AutoencoderOobleck,
|
| 1016 |
+
AutoencoderRAE,
|
| 1017 |
+
AutoencoderTiny,
|
| 1018 |
+
AutoencoderVidTok,
|
| 1019 |
+
AutoModel,
|
| 1020 |
+
BriaFiboTransformer2DModel,
|
| 1021 |
+
BriaTransformer2DModel,
|
| 1022 |
+
CacheMixin,
|
| 1023 |
+
ChromaTransformer2DModel,
|
| 1024 |
+
ChronoEditTransformer3DModel,
|
| 1025 |
+
CogVideoXTransformer3DModel,
|
| 1026 |
+
CogView3PlusTransformer2DModel,
|
| 1027 |
+
CogView4Transformer2DModel,
|
| 1028 |
+
ConsisIDTransformer3DModel,
|
| 1029 |
+
ConsistencyDecoderVAE,
|
| 1030 |
+
ContextParallelConfig,
|
| 1031 |
+
ControlNetModel,
|
| 1032 |
+
ControlNetUnionModel,
|
| 1033 |
+
ControlNetXSAdapter,
|
| 1034 |
+
CosmosControlNetModel,
|
| 1035 |
+
CosmosTransformer3DModel,
|
| 1036 |
+
DiTTransformer2DModel,
|
| 1037 |
+
EasyAnimateTransformer3DModel,
|
| 1038 |
+
Flux2Transformer2DModel,
|
| 1039 |
+
FluxControlNetModel,
|
| 1040 |
+
FluxMultiControlNetModel,
|
| 1041 |
+
FluxTransformer2DModel,
|
| 1042 |
+
GlmImageTransformer2DModel,
|
| 1043 |
+
HeliosTransformer3DModel,
|
| 1044 |
+
HiDreamImageTransformer2DModel,
|
| 1045 |
+
HunyuanDiT2DControlNetModel,
|
| 1046 |
+
HunyuanDiT2DModel,
|
| 1047 |
+
HunyuanDiT2DMultiControlNetModel,
|
| 1048 |
+
HunyuanImageTransformer2DModel,
|
| 1049 |
+
HunyuanVideo15Transformer3DModel,
|
| 1050 |
+
HunyuanVideoFramepackTransformer3DModel,
|
| 1051 |
+
HunyuanVideoTransformer3DModel,
|
| 1052 |
+
I2VGenXLUNet,
|
| 1053 |
+
Kandinsky3UNet,
|
| 1054 |
+
Kandinsky5Transformer3DModel,
|
| 1055 |
+
LatteTransformer3DModel,
|
| 1056 |
+
LongCatImageTransformer2DModel,
|
| 1057 |
+
LTX2VideoTransformer3DModel,
|
| 1058 |
+
LTXVideoTransformer3DModel,
|
| 1059 |
+
Lumina2Transformer2DModel,
|
| 1060 |
+
LuminaNextDiT2DModel,
|
| 1061 |
+
MochiTransformer3DModel,
|
| 1062 |
+
ModelMixin,
|
| 1063 |
+
MotionAdapter,
|
| 1064 |
+
MultiAdapter,
|
| 1065 |
+
MultiControlNetModel,
|
| 1066 |
+
NucleusMoEImageTransformer2DModel,
|
| 1067 |
+
OmniGenTransformer2DModel,
|
| 1068 |
+
OvisImageTransformer2DModel,
|
| 1069 |
+
ParallelConfig,
|
| 1070 |
+
PixArtTransformer2DModel,
|
| 1071 |
+
PriorTransformer,
|
| 1072 |
+
PRXTransformer2DModel,
|
| 1073 |
+
QwenImageControlNetModel,
|
| 1074 |
+
QwenImageMultiControlNetModel,
|
| 1075 |
+
QwenImageTransformer2DModel,
|
| 1076 |
+
SanaControlNetModel,
|
| 1077 |
+
SanaTransformer2DModel,
|
| 1078 |
+
SanaVideoTransformer3DModel,
|
| 1079 |
+
SD3ControlNetModel,
|
| 1080 |
+
SD3MultiControlNetModel,
|
| 1081 |
+
SD3Transformer2DModel,
|
| 1082 |
+
SkyReelsV2Transformer3DModel,
|
| 1083 |
+
SparseControlNetModel,
|
| 1084 |
+
StableAudioDiTModel,
|
| 1085 |
+
T2IAdapter,
|
| 1086 |
+
T5FilmDecoder,
|
| 1087 |
+
Transformer2DModel,
|
| 1088 |
+
TransformerTemporalModel,
|
| 1089 |
+
UNet1DModel,
|
| 1090 |
+
UNet2DConditionModel,
|
| 1091 |
+
UNet2DModel,
|
| 1092 |
+
UNet3DConditionModel,
|
| 1093 |
+
UNetControlNetXSModel,
|
| 1094 |
+
UNetMotionModel,
|
| 1095 |
+
UNetSpatioTemporalConditionModel,
|
| 1096 |
+
UVit2DModel,
|
| 1097 |
+
VQModel,
|
| 1098 |
+
WanAnimateTransformer3DModel,
|
| 1099 |
+
WanTransformer3DModel,
|
| 1100 |
+
WanVACETransformer3DModel,
|
| 1101 |
+
ZImageControlNetModel,
|
| 1102 |
+
ZImageTransformer2DModel,
|
| 1103 |
+
attention_backend,
|
| 1104 |
+
)
|
| 1105 |
+
from .modular_pipelines import (
|
| 1106 |
+
AutoPipelineBlocks,
|
| 1107 |
+
ComponentsManager,
|
| 1108 |
+
ComponentSpec,
|
| 1109 |
+
ConditionalPipelineBlocks,
|
| 1110 |
+
ConfigSpec,
|
| 1111 |
+
InputParam,
|
| 1112 |
+
LoopSequentialPipelineBlocks,
|
| 1113 |
+
ModularPipeline,
|
| 1114 |
+
ModularPipelineBlocks,
|
| 1115 |
+
OutputParam,
|
| 1116 |
+
SequentialPipelineBlocks,
|
| 1117 |
+
)
|
| 1118 |
+
from .optimization import (
|
| 1119 |
+
get_constant_schedule,
|
| 1120 |
+
get_constant_schedule_with_warmup,
|
| 1121 |
+
get_cosine_schedule_with_warmup,
|
| 1122 |
+
get_cosine_with_hard_restarts_schedule_with_warmup,
|
| 1123 |
+
get_linear_schedule_with_warmup,
|
| 1124 |
+
get_polynomial_decay_schedule_with_warmup,
|
| 1125 |
+
get_scheduler,
|
| 1126 |
+
)
|
| 1127 |
+
from .pipelines import (
|
| 1128 |
+
AudioPipelineOutput,
|
| 1129 |
+
AutoPipelineForImage2Image,
|
| 1130 |
+
AutoPipelineForInpainting,
|
| 1131 |
+
AutoPipelineForText2Image,
|
| 1132 |
+
BlipDiffusionControlNetPipeline,
|
| 1133 |
+
BlipDiffusionPipeline,
|
| 1134 |
+
CLIPImageProjection,
|
| 1135 |
+
ConsistencyModelPipeline,
|
| 1136 |
+
DanceDiffusionPipeline,
|
| 1137 |
+
DDIMPipeline,
|
| 1138 |
+
DDPMPipeline,
|
| 1139 |
+
DiffusionPipeline,
|
| 1140 |
+
DiTPipeline,
|
| 1141 |
+
ImagePipelineOutput,
|
| 1142 |
+
KarrasVePipeline,
|
| 1143 |
+
LDMPipeline,
|
| 1144 |
+
LDMSuperResolutionPipeline,
|
| 1145 |
+
PNDMPipeline,
|
| 1146 |
+
RePaintPipeline,
|
| 1147 |
+
ScoreSdeVePipeline,
|
| 1148 |
+
StableDiffusionMixin,
|
| 1149 |
+
)
|
| 1150 |
+
from .quantizers import DiffusersQuantizer
|
| 1151 |
+
from .schedulers import (
|
| 1152 |
+
AmusedScheduler,
|
| 1153 |
+
BlockRefinementScheduler,
|
| 1154 |
+
BlockRefinementSchedulerOutput,
|
| 1155 |
+
CMStochasticIterativeScheduler,
|
| 1156 |
+
CogVideoXDDIMScheduler,
|
| 1157 |
+
CogVideoXDPMScheduler,
|
| 1158 |
+
DDIMInverseScheduler,
|
| 1159 |
+
DDIMParallelScheduler,
|
| 1160 |
+
DDIMScheduler,
|
| 1161 |
+
DDPMParallelScheduler,
|
| 1162 |
+
DDPMScheduler,
|
| 1163 |
+
DDPMWuerstchenScheduler,
|
| 1164 |
+
DEISMultistepScheduler,
|
| 1165 |
+
DPMSolverMultistepInverseScheduler,
|
| 1166 |
+
DPMSolverMultistepScheduler,
|
| 1167 |
+
DPMSolverSinglestepScheduler,
|
| 1168 |
+
EDMDPMSolverMultistepScheduler,
|
| 1169 |
+
EDMEulerScheduler,
|
| 1170 |
+
EulerAncestralDiscreteScheduler,
|
| 1171 |
+
EulerDiscreteScheduler,
|
| 1172 |
+
FlowMatchEulerDiscreteScheduler,
|
| 1173 |
+
FlowMatchHeunDiscreteScheduler,
|
| 1174 |
+
FlowMatchLCMScheduler,
|
| 1175 |
+
HeliosDMDScheduler,
|
| 1176 |
+
HeliosScheduler,
|
| 1177 |
+
HeunDiscreteScheduler,
|
| 1178 |
+
IPNDMScheduler,
|
| 1179 |
+
KarrasVeScheduler,
|
| 1180 |
+
KDPM2AncestralDiscreteScheduler,
|
| 1181 |
+
KDPM2DiscreteScheduler,
|
| 1182 |
+
LCMScheduler,
|
| 1183 |
+
LTXEulerAncestralRFScheduler,
|
| 1184 |
+
PNDMScheduler,
|
| 1185 |
+
RePaintScheduler,
|
| 1186 |
+
SASolverScheduler,
|
| 1187 |
+
SchedulerMixin,
|
| 1188 |
+
SCMScheduler,
|
| 1189 |
+
ScoreSdeVeScheduler,
|
| 1190 |
+
TCDScheduler,
|
| 1191 |
+
UnCLIPScheduler,
|
| 1192 |
+
UniPCMultistepScheduler,
|
| 1193 |
+
VQDiffusionScheduler,
|
| 1194 |
+
)
|
| 1195 |
+
from .training_utils import EMAModel
|
| 1196 |
+
from .video_processor import VideoProcessor
|
| 1197 |
+
|
| 1198 |
+
try:
|
| 1199 |
+
if not (is_torch_available() and is_scipy_available()):
|
| 1200 |
+
raise OptionalDependencyNotAvailable()
|
| 1201 |
+
except OptionalDependencyNotAvailable:
|
| 1202 |
+
from .utils.dummy_torch_and_scipy_objects import * # noqa F403
|
| 1203 |
+
else:
|
| 1204 |
+
from .schedulers import LMSDiscreteScheduler
|
| 1205 |
+
|
| 1206 |
+
try:
|
| 1207 |
+
if not (is_torch_available() and is_torchsde_available()):
|
| 1208 |
+
raise OptionalDependencyNotAvailable()
|
| 1209 |
+
except OptionalDependencyNotAvailable:
|
| 1210 |
+
from .utils.dummy_torch_and_torchsde_objects import * # noqa F403
|
| 1211 |
+
else:
|
| 1212 |
+
from .schedulers import CosineDPMSolverMultistepScheduler, DPMSolverSDEScheduler
|
| 1213 |
+
|
| 1214 |
+
try:
|
| 1215 |
+
if not (is_torch_available() and is_transformers_available()):
|
| 1216 |
+
raise OptionalDependencyNotAvailable()
|
| 1217 |
+
except OptionalDependencyNotAvailable:
|
| 1218 |
+
from .utils.dummy_torch_and_transformers_objects import * # noqa F403
|
| 1219 |
+
else:
|
| 1220 |
+
from .modular_pipelines import (
|
| 1221 |
+
Flux2AutoBlocks,
|
| 1222 |
+
Flux2KleinAutoBlocks,
|
| 1223 |
+
Flux2KleinBaseAutoBlocks,
|
| 1224 |
+
Flux2KleinBaseModularPipeline,
|
| 1225 |
+
Flux2KleinModularPipeline,
|
| 1226 |
+
Flux2ModularPipeline,
|
| 1227 |
+
FluxAutoBlocks,
|
| 1228 |
+
FluxKontextAutoBlocks,
|
| 1229 |
+
FluxKontextModularPipeline,
|
| 1230 |
+
FluxModularPipeline,
|
| 1231 |
+
HeliosAutoBlocks,
|
| 1232 |
+
HeliosModularPipeline,
|
| 1233 |
+
HeliosPyramidAutoBlocks,
|
| 1234 |
+
HeliosPyramidDistilledAutoBlocks,
|
| 1235 |
+
HeliosPyramidDistilledModularPipeline,
|
| 1236 |
+
HeliosPyramidModularPipeline,
|
| 1237 |
+
QwenImageAutoBlocks,
|
| 1238 |
+
QwenImageEditAutoBlocks,
|
| 1239 |
+
QwenImageEditModularPipeline,
|
| 1240 |
+
QwenImageEditPlusAutoBlocks,
|
| 1241 |
+
QwenImageEditPlusModularPipeline,
|
| 1242 |
+
QwenImageLayeredAutoBlocks,
|
| 1243 |
+
QwenImageLayeredModularPipeline,
|
| 1244 |
+
QwenImageModularPipeline,
|
| 1245 |
+
StableDiffusionXLAutoBlocks,
|
| 1246 |
+
StableDiffusionXLModularPipeline,
|
| 1247 |
+
Wan22Blocks,
|
| 1248 |
+
Wan22Image2VideoBlocks,
|
| 1249 |
+
Wan22Image2VideoModularPipeline,
|
| 1250 |
+
Wan22ModularPipeline,
|
| 1251 |
+
WanBlocks,
|
| 1252 |
+
WanImage2VideoAutoBlocks,
|
| 1253 |
+
WanImage2VideoModularPipeline,
|
| 1254 |
+
WanModularPipeline,
|
| 1255 |
+
ZImageAutoBlocks,
|
| 1256 |
+
ZImageModularPipeline,
|
| 1257 |
+
)
|
| 1258 |
+
from .pipelines import (
|
| 1259 |
+
AllegroPipeline,
|
| 1260 |
+
AltDiffusionImg2ImgPipeline,
|
| 1261 |
+
AltDiffusionPipeline,
|
| 1262 |
+
AmusedImg2ImgPipeline,
|
| 1263 |
+
AmusedInpaintPipeline,
|
| 1264 |
+
AmusedPipeline,
|
| 1265 |
+
AnimateDiffControlNetPipeline,
|
| 1266 |
+
AnimateDiffPAGPipeline,
|
| 1267 |
+
AnimateDiffPipeline,
|
| 1268 |
+
AnimateDiffSDXLPipeline,
|
| 1269 |
+
AnimateDiffSparseControlNetPipeline,
|
| 1270 |
+
AnimateDiffVideoToVideoControlNetPipeline,
|
| 1271 |
+
AnimateDiffVideoToVideoPipeline,
|
| 1272 |
+
AudioLDM2Pipeline,
|
| 1273 |
+
AudioLDM2ProjectionModel,
|
| 1274 |
+
AudioLDM2UNet2DConditionModel,
|
| 1275 |
+
AudioLDMPipeline,
|
| 1276 |
+
AuraFlowPipeline,
|
| 1277 |
+
BriaFiboEditPipeline,
|
| 1278 |
+
BriaFiboPipeline,
|
| 1279 |
+
BriaPipeline,
|
| 1280 |
+
ChromaImg2ImgPipeline,
|
| 1281 |
+
ChromaInpaintPipeline,
|
| 1282 |
+
ChromaPipeline,
|
| 1283 |
+
ChronoEditPipeline,
|
| 1284 |
+
CLIPImageProjection,
|
| 1285 |
+
CogVideoXFunControlPipeline,
|
| 1286 |
+
CogVideoXImageToVideoPipeline,
|
| 1287 |
+
CogVideoXPipeline,
|
| 1288 |
+
CogVideoXVideoToVideoPipeline,
|
| 1289 |
+
CogView3PlusPipeline,
|
| 1290 |
+
CogView4ControlPipeline,
|
| 1291 |
+
CogView4Pipeline,
|
| 1292 |
+
ConsisIDPipeline,
|
| 1293 |
+
Cosmos2_5_PredictBasePipeline,
|
| 1294 |
+
Cosmos2_5_TransferPipeline,
|
| 1295 |
+
Cosmos2TextToImagePipeline,
|
| 1296 |
+
Cosmos2VideoToWorldPipeline,
|
| 1297 |
+
CosmosTextToWorldPipeline,
|
| 1298 |
+
CosmosVideoToWorldPipeline,
|
| 1299 |
+
CycleDiffusionPipeline,
|
| 1300 |
+
EasyAnimateControlPipeline,
|
| 1301 |
+
EasyAnimateInpaintPipeline,
|
| 1302 |
+
EasyAnimatePipeline,
|
| 1303 |
+
Flux2KleinKVPipeline,
|
| 1304 |
+
Flux2KleinPipeline,
|
| 1305 |
+
Flux2Pipeline,
|
| 1306 |
+
FluxControlImg2ImgPipeline,
|
| 1307 |
+
FluxControlInpaintPipeline,
|
| 1308 |
+
FluxControlNetImg2ImgPipeline,
|
| 1309 |
+
FluxControlNetInpaintPipeline,
|
| 1310 |
+
FluxControlNetPipeline,
|
| 1311 |
+
FluxControlPipeline,
|
| 1312 |
+
FluxFillPipeline,
|
| 1313 |
+
FluxImg2ImgPipeline,
|
| 1314 |
+
FluxInpaintPipeline,
|
| 1315 |
+
FluxKontextInpaintPipeline,
|
| 1316 |
+
FluxKontextPipeline,
|
| 1317 |
+
FluxPipeline,
|
| 1318 |
+
FluxPriorReduxPipeline,
|
| 1319 |
+
GlmImagePipeline,
|
| 1320 |
+
HeliosPipeline,
|
| 1321 |
+
HeliosPyramidPipeline,
|
| 1322 |
+
HiDreamImagePipeline,
|
| 1323 |
+
HunyuanDiTControlNetPipeline,
|
| 1324 |
+
HunyuanDiTPAGPipeline,
|
| 1325 |
+
HunyuanDiTPipeline,
|
| 1326 |
+
HunyuanImagePipeline,
|
| 1327 |
+
HunyuanImageRefinerPipeline,
|
| 1328 |
+
HunyuanSkyreelsImageToVideoPipeline,
|
| 1329 |
+
HunyuanVideo15ImageToVideoPipeline,
|
| 1330 |
+
HunyuanVideo15Pipeline,
|
| 1331 |
+
HunyuanVideoFramepackPipeline,
|
| 1332 |
+
HunyuanVideoImageToVideoPipeline,
|
| 1333 |
+
HunyuanVideoPipeline,
|
| 1334 |
+
I2VGenXLPipeline,
|
| 1335 |
+
IFImg2ImgPipeline,
|
| 1336 |
+
IFImg2ImgSuperResolutionPipeline,
|
| 1337 |
+
IFInpaintingPipeline,
|
| 1338 |
+
IFInpaintingSuperResolutionPipeline,
|
| 1339 |
+
IFPipeline,
|
| 1340 |
+
IFSuperResolutionPipeline,
|
| 1341 |
+
ImageTextPipelineOutput,
|
| 1342 |
+
Kandinsky3Img2ImgPipeline,
|
| 1343 |
+
Kandinsky3Pipeline,
|
| 1344 |
+
Kandinsky5I2IPipeline,
|
| 1345 |
+
Kandinsky5I2VPipeline,
|
| 1346 |
+
Kandinsky5T2IPipeline,
|
| 1347 |
+
Kandinsky5T2VPipeline,
|
| 1348 |
+
KandinskyCombinedPipeline,
|
| 1349 |
+
KandinskyImg2ImgCombinedPipeline,
|
| 1350 |
+
KandinskyImg2ImgPipeline,
|
| 1351 |
+
KandinskyInpaintCombinedPipeline,
|
| 1352 |
+
KandinskyInpaintPipeline,
|
| 1353 |
+
KandinskyPipeline,
|
| 1354 |
+
KandinskyPriorPipeline,
|
| 1355 |
+
KandinskyV22CombinedPipeline,
|
| 1356 |
+
KandinskyV22ControlnetImg2ImgPipeline,
|
| 1357 |
+
KandinskyV22ControlnetPipeline,
|
| 1358 |
+
KandinskyV22Img2ImgCombinedPipeline,
|
| 1359 |
+
KandinskyV22Img2ImgPipeline,
|
| 1360 |
+
KandinskyV22InpaintCombinedPipeline,
|
| 1361 |
+
KandinskyV22InpaintPipeline,
|
| 1362 |
+
KandinskyV22Pipeline,
|
| 1363 |
+
KandinskyV22PriorEmb2EmbPipeline,
|
| 1364 |
+
KandinskyV22PriorPipeline,
|
| 1365 |
+
LatentConsistencyModelImg2ImgPipeline,
|
| 1366 |
+
LatentConsistencyModelPipeline,
|
| 1367 |
+
LattePipeline,
|
| 1368 |
+
LDMTextToImagePipeline,
|
| 1369 |
+
LEditsPPPipelineStableDiffusion,
|
| 1370 |
+
LEditsPPPipelineStableDiffusionXL,
|
| 1371 |
+
LLaDA2Pipeline,
|
| 1372 |
+
LLaDA2PipelineOutput,
|
| 1373 |
+
LongCatImageEditPipeline,
|
| 1374 |
+
LongCatImagePipeline,
|
| 1375 |
+
LTX2ConditionPipeline,
|
| 1376 |
+
LTX2ImageToVideoPipeline,
|
| 1377 |
+
LTX2LatentUpsamplePipeline,
|
| 1378 |
+
LTX2Pipeline,
|
| 1379 |
+
LTXConditionPipeline,
|
| 1380 |
+
LTXI2VLongMultiPromptPipeline,
|
| 1381 |
+
LTXImageToVideoPipeline,
|
| 1382 |
+
LTXLatentUpsamplePipeline,
|
| 1383 |
+
LTXPipeline,
|
| 1384 |
+
LucyEditPipeline,
|
| 1385 |
+
Lumina2Pipeline,
|
| 1386 |
+
Lumina2Text2ImgPipeline,
|
| 1387 |
+
LuminaPipeline,
|
| 1388 |
+
LuminaText2ImgPipeline,
|
| 1389 |
+
MarigoldDepthPipeline,
|
| 1390 |
+
MarigoldIntrinsicsPipeline,
|
| 1391 |
+
MarigoldNormalsPipeline,
|
| 1392 |
+
MochiPipeline,
|
| 1393 |
+
MusicLDMPipeline,
|
| 1394 |
+
NucleusMoEImagePipeline,
|
| 1395 |
+
OmniGenPipeline,
|
| 1396 |
+
OvisImagePipeline,
|
| 1397 |
+
PaintByExamplePipeline,
|
| 1398 |
+
PIAPipeline,
|
| 1399 |
+
PixArtAlphaPipeline,
|
| 1400 |
+
PixArtSigmaPAGPipeline,
|
| 1401 |
+
PixArtSigmaPipeline,
|
| 1402 |
+
PRXPipeline,
|
| 1403 |
+
QwenImageControlNetInpaintPipeline,
|
| 1404 |
+
QwenImageControlNetPipeline,
|
| 1405 |
+
QwenImageEditInpaintPipeline,
|
| 1406 |
+
QwenImageEditPipeline,
|
| 1407 |
+
QwenImageEditPlusPipeline,
|
| 1408 |
+
QwenImageImg2ImgPipeline,
|
| 1409 |
+
QwenImageInpaintPipeline,
|
| 1410 |
+
QwenImageLayeredPipeline,
|
| 1411 |
+
QwenImagePipeline,
|
| 1412 |
+
ReduxImageEncoder,
|
| 1413 |
+
SanaControlNetPipeline,
|
| 1414 |
+
SanaImageToVideoPipeline,
|
| 1415 |
+
SanaPAGPipeline,
|
| 1416 |
+
SanaPipeline,
|
| 1417 |
+
SanaSprintImg2ImgPipeline,
|
| 1418 |
+
SanaSprintPipeline,
|
| 1419 |
+
SanaVideoPipeline,
|
| 1420 |
+
SemanticStableDiffusionPipeline,
|
| 1421 |
+
ShapEImg2ImgPipeline,
|
| 1422 |
+
ShapEPipeline,
|
| 1423 |
+
SkyReelsV2DiffusionForcingImageToVideoPipeline,
|
| 1424 |
+
SkyReelsV2DiffusionForcingPipeline,
|
| 1425 |
+
SkyReelsV2DiffusionForcingVideoToVideoPipeline,
|
| 1426 |
+
SkyReelsV2ImageToVideoPipeline,
|
| 1427 |
+
SkyReelsV2Pipeline,
|
| 1428 |
+
StableAudioPipeline,
|
| 1429 |
+
StableAudioProjectionModel,
|
| 1430 |
+
StableCascadeCombinedPipeline,
|
| 1431 |
+
StableCascadeDecoderPipeline,
|
| 1432 |
+
StableCascadePriorPipeline,
|
| 1433 |
+
StableDiffusion3ControlNetInpaintingPipeline,
|
| 1434 |
+
StableDiffusion3ControlNetPipeline,
|
| 1435 |
+
StableDiffusion3Img2ImgPipeline,
|
| 1436 |
+
StableDiffusion3InpaintPipeline,
|
| 1437 |
+
StableDiffusion3PAGImg2ImgPipeline,
|
| 1438 |
+
StableDiffusion3PAGPipeline,
|
| 1439 |
+
StableDiffusion3Pipeline,
|
| 1440 |
+
StableDiffusionAdapterPipeline,
|
| 1441 |
+
StableDiffusionAttendAndExcitePipeline,
|
| 1442 |
+
StableDiffusionControlNetImg2ImgPipeline,
|
| 1443 |
+
StableDiffusionControlNetInpaintPipeline,
|
| 1444 |
+
StableDiffusionControlNetPAGInpaintPipeline,
|
| 1445 |
+
StableDiffusionControlNetPAGPipeline,
|
| 1446 |
+
StableDiffusionControlNetPipeline,
|
| 1447 |
+
StableDiffusionControlNetXSPipeline,
|
| 1448 |
+
StableDiffusionDepth2ImgPipeline,
|
| 1449 |
+
StableDiffusionDiffEditPipeline,
|
| 1450 |
+
StableDiffusionGLIGENPipeline,
|
| 1451 |
+
StableDiffusionGLIGENTextImagePipeline,
|
| 1452 |
+
StableDiffusionImageVariationPipeline,
|
| 1453 |
+
StableDiffusionImg2ImgPipeline,
|
| 1454 |
+
StableDiffusionInpaintPipeline,
|
| 1455 |
+
StableDiffusionInpaintPipelineLegacy,
|
| 1456 |
+
StableDiffusionInstructPix2PixPipeline,
|
| 1457 |
+
StableDiffusionLatentUpscalePipeline,
|
| 1458 |
+
StableDiffusionLDM3DPipeline,
|
| 1459 |
+
StableDiffusionModelEditingPipeline,
|
| 1460 |
+
StableDiffusionPAGImg2ImgPipeline,
|
| 1461 |
+
StableDiffusionPAGInpaintPipeline,
|
| 1462 |
+
StableDiffusionPAGPipeline,
|
| 1463 |
+
StableDiffusionPanoramaPipeline,
|
| 1464 |
+
StableDiffusionParadigmsPipeline,
|
| 1465 |
+
StableDiffusionPipeline,
|
| 1466 |
+
StableDiffusionPipelineSafe,
|
| 1467 |
+
StableDiffusionPix2PixZeroPipeline,
|
| 1468 |
+
StableDiffusionSAGPipeline,
|
| 1469 |
+
StableDiffusionUpscalePipeline,
|
| 1470 |
+
StableDiffusionXLAdapterPipeline,
|
| 1471 |
+
StableDiffusionXLControlNetImg2ImgPipeline,
|
| 1472 |
+
StableDiffusionXLControlNetInpaintPipeline,
|
| 1473 |
+
StableDiffusionXLControlNetPAGImg2ImgPipeline,
|
| 1474 |
+
StableDiffusionXLControlNetPAGPipeline,
|
| 1475 |
+
StableDiffusionXLControlNetPipeline,
|
| 1476 |
+
StableDiffusionXLControlNetUnionImg2ImgPipeline,
|
| 1477 |
+
StableDiffusionXLControlNetUnionInpaintPipeline,
|
| 1478 |
+
StableDiffusionXLControlNetUnionPipeline,
|
| 1479 |
+
StableDiffusionXLControlNetXSPipeline,
|
| 1480 |
+
StableDiffusionXLImg2ImgPipeline,
|
| 1481 |
+
StableDiffusionXLInpaintPipeline,
|
| 1482 |
+
StableDiffusionXLInstructPix2PixPipeline,
|
| 1483 |
+
StableDiffusionXLPAGImg2ImgPipeline,
|
| 1484 |
+
StableDiffusionXLPAGInpaintPipeline,
|
| 1485 |
+
StableDiffusionXLPAGPipeline,
|
| 1486 |
+
StableDiffusionXLPipeline,
|
| 1487 |
+
StableUnCLIPImg2ImgPipeline,
|
| 1488 |
+
StableUnCLIPPipeline,
|
| 1489 |
+
StableVideoDiffusionPipeline,
|
| 1490 |
+
TextToVideoSDPipeline,
|
| 1491 |
+
TextToVideoZeroPipeline,
|
| 1492 |
+
TextToVideoZeroSDXLPipeline,
|
| 1493 |
+
UnCLIPImageVariationPipeline,
|
| 1494 |
+
UnCLIPPipeline,
|
| 1495 |
+
UniDiffuserModel,
|
| 1496 |
+
UniDiffuserPipeline,
|
| 1497 |
+
UniDiffuserTextDecoder,
|
| 1498 |
+
VersatileDiffusionDualGuidedPipeline,
|
| 1499 |
+
VersatileDiffusionImageVariationPipeline,
|
| 1500 |
+
VersatileDiffusionPipeline,
|
| 1501 |
+
VersatileDiffusionTextToImagePipeline,
|
| 1502 |
+
VideoToVideoSDPipeline,
|
| 1503 |
+
VisualClozeGenerationPipeline,
|
| 1504 |
+
VisualClozePipeline,
|
| 1505 |
+
VQDiffusionPipeline,
|
| 1506 |
+
WanAnimatePipeline,
|
| 1507 |
+
WanImageToVideoPipeline,
|
| 1508 |
+
WanPipeline,
|
| 1509 |
+
WanVACEPipeline,
|
| 1510 |
+
WanVideoToVideoPipeline,
|
| 1511 |
+
WuerstchenCombinedPipeline,
|
| 1512 |
+
WuerstchenDecoderPipeline,
|
| 1513 |
+
WuerstchenPriorPipeline,
|
| 1514 |
+
ZImageControlNetInpaintPipeline,
|
| 1515 |
+
ZImageControlNetPipeline,
|
| 1516 |
+
ZImageImg2ImgPipeline,
|
| 1517 |
+
ZImageInpaintPipeline,
|
| 1518 |
+
ZImageOmniPipeline,
|
| 1519 |
+
ZImagePipeline,
|
| 1520 |
+
)
|
| 1521 |
+
|
| 1522 |
+
try:
|
| 1523 |
+
if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
|
| 1524 |
+
raise OptionalDependencyNotAvailable()
|
| 1525 |
+
except OptionalDependencyNotAvailable:
|
| 1526 |
+
from .utils.dummy_torch_and_transformers_and_sentencepiece_objects import * # noqa F403
|
| 1527 |
+
else:
|
| 1528 |
+
from .pipelines import KolorsImg2ImgPipeline, KolorsPAGPipeline, KolorsPipeline
|
| 1529 |
+
|
| 1530 |
+
try:
|
| 1531 |
+
if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
|
| 1532 |
+
raise OptionalDependencyNotAvailable()
|
| 1533 |
+
except OptionalDependencyNotAvailable:
|
| 1534 |
+
from .utils.dummy_torch_and_transformers_and_opencv_objects import * # noqa F403
|
| 1535 |
+
else:
|
| 1536 |
+
from .pipelines import ConsisIDPipeline
|
| 1537 |
+
|
| 1538 |
+
try:
|
| 1539 |
+
if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
|
| 1540 |
+
raise OptionalDependencyNotAvailable()
|
| 1541 |
+
except OptionalDependencyNotAvailable:
|
| 1542 |
+
from .utils.dummy_torch_and_transformers_and_onnx_objects import * # noqa F403
|
| 1543 |
+
else:
|
| 1544 |
+
from .pipelines import (
|
| 1545 |
+
OnnxStableDiffusionImg2ImgPipeline,
|
| 1546 |
+
OnnxStableDiffusionInpaintPipeline,
|
| 1547 |
+
OnnxStableDiffusionInpaintPipelineLegacy,
|
| 1548 |
+
OnnxStableDiffusionPipeline,
|
| 1549 |
+
OnnxStableDiffusionUpscalePipeline,
|
| 1550 |
+
StableDiffusionOnnxPipeline,
|
| 1551 |
+
)
|
| 1552 |
+
|
| 1553 |
+
try:
|
| 1554 |
+
if not (is_torch_available() and is_librosa_available()):
|
| 1555 |
+
raise OptionalDependencyNotAvailable()
|
| 1556 |
+
except OptionalDependencyNotAvailable:
|
| 1557 |
+
from .utils.dummy_torch_and_librosa_objects import * # noqa F403
|
| 1558 |
+
else:
|
| 1559 |
+
from .pipelines import AudioDiffusionPipeline, Mel
|
| 1560 |
+
|
| 1561 |
+
try:
|
| 1562 |
+
if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
|
| 1563 |
+
raise OptionalDependencyNotAvailable()
|
| 1564 |
+
except OptionalDependencyNotAvailable:
|
| 1565 |
+
from .utils.dummy_transformers_and_torch_and_note_seq_objects import * # noqa F403
|
| 1566 |
+
else:
|
| 1567 |
+
from .pipelines import SpectrogramDiffusionPipeline
|
| 1568 |
+
|
| 1569 |
+
try:
|
| 1570 |
+
if not is_flax_available():
|
| 1571 |
+
raise OptionalDependencyNotAvailable()
|
| 1572 |
+
except OptionalDependencyNotAvailable:
|
| 1573 |
+
from .utils.dummy_flax_objects import * # noqa F403
|
| 1574 |
+
else:
|
| 1575 |
+
from .models.controlnets.controlnet_flax import FlaxControlNetModel
|
| 1576 |
+
from .models.modeling_flax_utils import FlaxModelMixin
|
| 1577 |
+
from .models.unets.unet_2d_condition_flax import FlaxUNet2DConditionModel
|
| 1578 |
+
from .models.vae_flax import FlaxAutoencoderKL
|
| 1579 |
+
from .pipelines import FlaxDiffusionPipeline
|
| 1580 |
+
from .schedulers import (
|
| 1581 |
+
FlaxDDIMScheduler,
|
| 1582 |
+
FlaxDDPMScheduler,
|
| 1583 |
+
FlaxDPMSolverMultistepScheduler,
|
| 1584 |
+
FlaxEulerDiscreteScheduler,
|
| 1585 |
+
FlaxKarrasVeScheduler,
|
| 1586 |
+
FlaxLMSDiscreteScheduler,
|
| 1587 |
+
FlaxPNDMScheduler,
|
| 1588 |
+
FlaxSchedulerMixin,
|
| 1589 |
+
FlaxScoreSdeVeScheduler,
|
| 1590 |
+
)
|
| 1591 |
+
|
| 1592 |
+
try:
|
| 1593 |
+
if not (is_flax_available() and is_transformers_available()):
|
| 1594 |
+
raise OptionalDependencyNotAvailable()
|
| 1595 |
+
except OptionalDependencyNotAvailable:
|
| 1596 |
+
from .utils.dummy_flax_and_transformers_objects import * # noqa F403
|
| 1597 |
+
else:
|
| 1598 |
+
from .pipelines import (
|
| 1599 |
+
FlaxStableDiffusionControlNetPipeline,
|
| 1600 |
+
FlaxStableDiffusionImg2ImgPipeline,
|
| 1601 |
+
FlaxStableDiffusionInpaintPipeline,
|
| 1602 |
+
FlaxStableDiffusionPipeline,
|
| 1603 |
+
FlaxStableDiffusionXLPipeline,
|
| 1604 |
+
)
|
| 1605 |
+
|
| 1606 |
+
try:
|
| 1607 |
+
if not (is_note_seq_available()):
|
| 1608 |
+
raise OptionalDependencyNotAvailable()
|
| 1609 |
+
except OptionalDependencyNotAvailable:
|
| 1610 |
+
from .utils.dummy_note_seq_objects import * # noqa F403
|
| 1611 |
+
else:
|
| 1612 |
+
from .pipelines import MidiProcessor
|
| 1613 |
+
|
| 1614 |
+
else:
|
| 1615 |
+
import sys
|
| 1616 |
+
|
| 1617 |
+
sys.modules[__name__] = _LazyModule(
|
| 1618 |
+
__name__,
|
| 1619 |
+
globals()["__file__"],
|
| 1620 |
+
_import_structure,
|
| 1621 |
+
module_spec=__spec__,
|
| 1622 |
+
extra_objects={"__version__": __version__},
|
| 1623 |
+
)
|
third_party/diffusers/src/diffusers/callbacks.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any
|
| 2 |
+
|
| 3 |
+
from .configuration_utils import ConfigMixin, register_to_config
|
| 4 |
+
from .utils import CONFIG_NAME
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PipelineCallback(ConfigMixin):
|
| 8 |
+
"""
|
| 9 |
+
Base class for all the official callbacks used in a pipeline. This class provides a structure for implementing
|
| 10 |
+
custom callbacks and ensures that all callbacks have a consistent interface.
|
| 11 |
+
|
| 12 |
+
Please implement the following:
|
| 13 |
+
`tensor_inputs`: This should return a list of tensor inputs specific to your callback. You will only be able to
|
| 14 |
+
include
|
| 15 |
+
variables listed in the `._callback_tensor_inputs` attribute of your pipeline class.
|
| 16 |
+
`callback_fn`: This method defines the core functionality of your callback.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
config_name = CONFIG_NAME
|
| 20 |
+
|
| 21 |
+
@register_to_config
|
| 22 |
+
def __init__(self, cutoff_step_ratio=1.0, cutoff_step_index=None):
|
| 23 |
+
super().__init__()
|
| 24 |
+
|
| 25 |
+
if (cutoff_step_ratio is None and cutoff_step_index is None) or (
|
| 26 |
+
cutoff_step_ratio is not None and cutoff_step_index is not None
|
| 27 |
+
):
|
| 28 |
+
raise ValueError("Either cutoff_step_ratio or cutoff_step_index should be provided, not both or none.")
|
| 29 |
+
|
| 30 |
+
if cutoff_step_ratio is not None and (
|
| 31 |
+
not isinstance(cutoff_step_ratio, float) or not (0.0 <= cutoff_step_ratio <= 1.0)
|
| 32 |
+
):
|
| 33 |
+
raise ValueError("cutoff_step_ratio must be a float between 0.0 and 1.0.")
|
| 34 |
+
|
| 35 |
+
@property
|
| 36 |
+
def tensor_inputs(self) -> list[str]:
|
| 37 |
+
raise NotImplementedError(f"You need to set the attribute `tensor_inputs` for {self.__class__}")
|
| 38 |
+
|
| 39 |
+
def callback_fn(self, pipeline, step_index, timesteps, callback_kwargs) -> dict[str, Any]:
|
| 40 |
+
raise NotImplementedError(f"You need to implement the method `callback_fn` for {self.__class__}")
|
| 41 |
+
|
| 42 |
+
def __call__(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 43 |
+
return self.callback_fn(pipeline, step_index, timestep, callback_kwargs)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class MultiPipelineCallbacks:
|
| 47 |
+
"""
|
| 48 |
+
This class is designed to handle multiple pipeline callbacks. It accepts a list of PipelineCallback objects and
|
| 49 |
+
provides a unified interface for calling all of them.
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
def __init__(self, callbacks: list[PipelineCallback]):
|
| 53 |
+
self.callbacks = callbacks
|
| 54 |
+
|
| 55 |
+
@property
|
| 56 |
+
def tensor_inputs(self) -> list[str]:
|
| 57 |
+
return [input for callback in self.callbacks for input in callback.tensor_inputs]
|
| 58 |
+
|
| 59 |
+
def __call__(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 60 |
+
"""
|
| 61 |
+
Calls all the callbacks in order with the given arguments and returns the final callback_kwargs.
|
| 62 |
+
"""
|
| 63 |
+
for callback in self.callbacks:
|
| 64 |
+
callback_kwargs = callback(pipeline, step_index, timestep, callback_kwargs)
|
| 65 |
+
|
| 66 |
+
return callback_kwargs
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class SDCFGCutoffCallback(PipelineCallback):
|
| 70 |
+
"""
|
| 71 |
+
Callback function for Stable Diffusion Pipelines. After certain number of steps (set by `cutoff_step_ratio` or
|
| 72 |
+
`cutoff_step_index`), this callback will disable the CFG.
|
| 73 |
+
|
| 74 |
+
Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
|
| 75 |
+
"""
|
| 76 |
+
|
| 77 |
+
tensor_inputs = ["prompt_embeds"]
|
| 78 |
+
|
| 79 |
+
def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 80 |
+
cutoff_step_ratio = self.config.cutoff_step_ratio
|
| 81 |
+
cutoff_step_index = self.config.cutoff_step_index
|
| 82 |
+
|
| 83 |
+
# Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
|
| 84 |
+
cutoff_step = (
|
| 85 |
+
cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
if step_index == cutoff_step:
|
| 89 |
+
prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
|
| 90 |
+
prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
|
| 91 |
+
|
| 92 |
+
pipeline._guidance_scale = 0.0
|
| 93 |
+
|
| 94 |
+
callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
|
| 95 |
+
return callback_kwargs
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
class SDXLCFGCutoffCallback(PipelineCallback):
|
| 99 |
+
"""
|
| 100 |
+
Callback function for the base Stable Diffusion XL Pipelines. After certain number of steps (set by
|
| 101 |
+
`cutoff_step_ratio` or `cutoff_step_index`), this callback will disable the CFG.
|
| 102 |
+
|
| 103 |
+
Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
tensor_inputs = [
|
| 107 |
+
"prompt_embeds",
|
| 108 |
+
"add_text_embeds",
|
| 109 |
+
"add_time_ids",
|
| 110 |
+
]
|
| 111 |
+
|
| 112 |
+
def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 113 |
+
cutoff_step_ratio = self.config.cutoff_step_ratio
|
| 114 |
+
cutoff_step_index = self.config.cutoff_step_index
|
| 115 |
+
|
| 116 |
+
# Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
|
| 117 |
+
cutoff_step = (
|
| 118 |
+
cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
if step_index == cutoff_step:
|
| 122 |
+
prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
|
| 123 |
+
prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
|
| 124 |
+
|
| 125 |
+
add_text_embeds = callback_kwargs[self.tensor_inputs[1]]
|
| 126 |
+
add_text_embeds = add_text_embeds[-1:] # "-1" denotes the embeddings for conditional pooled text tokens
|
| 127 |
+
|
| 128 |
+
add_time_ids = callback_kwargs[self.tensor_inputs[2]]
|
| 129 |
+
add_time_ids = add_time_ids[-1:] # "-1" denotes the embeddings for conditional added time vector
|
| 130 |
+
|
| 131 |
+
pipeline._guidance_scale = 0.0
|
| 132 |
+
|
| 133 |
+
callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
|
| 134 |
+
callback_kwargs[self.tensor_inputs[1]] = add_text_embeds
|
| 135 |
+
callback_kwargs[self.tensor_inputs[2]] = add_time_ids
|
| 136 |
+
|
| 137 |
+
return callback_kwargs
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class SDXLControlnetCFGCutoffCallback(PipelineCallback):
|
| 141 |
+
"""
|
| 142 |
+
Callback function for the Controlnet Stable Diffusion XL Pipelines. After certain number of steps (set by
|
| 143 |
+
`cutoff_step_ratio` or `cutoff_step_index`), this callback will disable the CFG.
|
| 144 |
+
|
| 145 |
+
Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
|
| 146 |
+
"""
|
| 147 |
+
|
| 148 |
+
tensor_inputs = [
|
| 149 |
+
"prompt_embeds",
|
| 150 |
+
"add_text_embeds",
|
| 151 |
+
"add_time_ids",
|
| 152 |
+
"image",
|
| 153 |
+
]
|
| 154 |
+
|
| 155 |
+
def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 156 |
+
cutoff_step_ratio = self.config.cutoff_step_ratio
|
| 157 |
+
cutoff_step_index = self.config.cutoff_step_index
|
| 158 |
+
|
| 159 |
+
# Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
|
| 160 |
+
cutoff_step = (
|
| 161 |
+
cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
if step_index == cutoff_step:
|
| 165 |
+
prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
|
| 166 |
+
prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
|
| 167 |
+
|
| 168 |
+
add_text_embeds = callback_kwargs[self.tensor_inputs[1]]
|
| 169 |
+
add_text_embeds = add_text_embeds[-1:] # "-1" denotes the embeddings for conditional pooled text tokens
|
| 170 |
+
|
| 171 |
+
add_time_ids = callback_kwargs[self.tensor_inputs[2]]
|
| 172 |
+
add_time_ids = add_time_ids[-1:] # "-1" denotes the embeddings for conditional added time vector
|
| 173 |
+
|
| 174 |
+
# For Controlnet
|
| 175 |
+
image = callback_kwargs[self.tensor_inputs[3]]
|
| 176 |
+
image = image[-1:]
|
| 177 |
+
|
| 178 |
+
pipeline._guidance_scale = 0.0
|
| 179 |
+
|
| 180 |
+
callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
|
| 181 |
+
callback_kwargs[self.tensor_inputs[1]] = add_text_embeds
|
| 182 |
+
callback_kwargs[self.tensor_inputs[2]] = add_time_ids
|
| 183 |
+
callback_kwargs[self.tensor_inputs[3]] = image
|
| 184 |
+
|
| 185 |
+
return callback_kwargs
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
class IPAdapterScaleCutoffCallback(PipelineCallback):
|
| 189 |
+
"""
|
| 190 |
+
Callback function for any pipeline that inherits `IPAdapterMixin`. After certain number of steps (set by
|
| 191 |
+
`cutoff_step_ratio` or `cutoff_step_index`), this callback will set the IP Adapter scale to `0.0`.
|
| 192 |
+
|
| 193 |
+
Note: This callback mutates the IP Adapter attention processors by setting the scale to 0.0 after the cutoff step.
|
| 194 |
+
"""
|
| 195 |
+
|
| 196 |
+
tensor_inputs = []
|
| 197 |
+
|
| 198 |
+
def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 199 |
+
cutoff_step_ratio = self.config.cutoff_step_ratio
|
| 200 |
+
cutoff_step_index = self.config.cutoff_step_index
|
| 201 |
+
|
| 202 |
+
# Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
|
| 203 |
+
cutoff_step = (
|
| 204 |
+
cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
if step_index == cutoff_step:
|
| 208 |
+
pipeline.set_ip_adapter_scale(0.0)
|
| 209 |
+
return callback_kwargs
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class SD3CFGCutoffCallback(PipelineCallback):
|
| 213 |
+
"""
|
| 214 |
+
Callback function for Stable Diffusion 3 Pipelines. After certain number of steps (set by `cutoff_step_ratio` or
|
| 215 |
+
`cutoff_step_index`), this callback will disable the CFG.
|
| 216 |
+
|
| 217 |
+
Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
|
| 218 |
+
"""
|
| 219 |
+
|
| 220 |
+
tensor_inputs = ["prompt_embeds", "pooled_prompt_embeds"]
|
| 221 |
+
|
| 222 |
+
def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
|
| 223 |
+
cutoff_step_ratio = self.config.cutoff_step_ratio
|
| 224 |
+
cutoff_step_index = self.config.cutoff_step_index
|
| 225 |
+
|
| 226 |
+
# Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
|
| 227 |
+
cutoff_step = (
|
| 228 |
+
cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
|
| 229 |
+
)
|
| 230 |
+
|
| 231 |
+
if step_index == cutoff_step:
|
| 232 |
+
prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
|
| 233 |
+
prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
|
| 234 |
+
|
| 235 |
+
pooled_prompt_embeds = callback_kwargs[self.tensor_inputs[1]]
|
| 236 |
+
pooled_prompt_embeds = pooled_prompt_embeds[
|
| 237 |
+
-1:
|
| 238 |
+
] # "-1" denotes the embeddings for conditional pooled text tokens.
|
| 239 |
+
|
| 240 |
+
pipeline._guidance_scale = 0.0
|
| 241 |
+
|
| 242 |
+
callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
|
| 243 |
+
callback_kwargs[self.tensor_inputs[1]] = pooled_prompt_embeds
|
| 244 |
+
return callback_kwargs
|
third_party/diffusers/src/diffusers/configuration_utils.py
ADDED
|
@@ -0,0 +1,805 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2025 The HuggingFace Inc. team.
|
| 3 |
+
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
| 4 |
+
#
|
| 5 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 6 |
+
# you may not use this file except in compliance with the License.
|
| 7 |
+
# You may obtain a copy of the License at
|
| 8 |
+
#
|
| 9 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
+
#
|
| 11 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 12 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
+
# See the License for the specific language governing permissions and
|
| 15 |
+
# limitations under the License.
|
| 16 |
+
"""ConfigMixin base class and utilities."""
|
| 17 |
+
|
| 18 |
+
import dataclasses
|
| 19 |
+
import functools
|
| 20 |
+
import importlib
|
| 21 |
+
import inspect
|
| 22 |
+
import json
|
| 23 |
+
import os
|
| 24 |
+
import re
|
| 25 |
+
from collections import OrderedDict
|
| 26 |
+
from pathlib import Path
|
| 27 |
+
from typing import Any
|
| 28 |
+
|
| 29 |
+
import numpy as np
|
| 30 |
+
from huggingface_hub import DDUFEntry, create_repo, hf_hub_download
|
| 31 |
+
from huggingface_hub.utils import (
|
| 32 |
+
EntryNotFoundError,
|
| 33 |
+
HfHubHTTPError,
|
| 34 |
+
RepositoryNotFoundError,
|
| 35 |
+
RevisionNotFoundError,
|
| 36 |
+
validate_hf_hub_args,
|
| 37 |
+
)
|
| 38 |
+
from typing_extensions import Self
|
| 39 |
+
|
| 40 |
+
from . import __version__
|
| 41 |
+
from .utils import (
|
| 42 |
+
HUGGINGFACE_CO_RESOLVE_ENDPOINT,
|
| 43 |
+
DummyObject,
|
| 44 |
+
deprecate,
|
| 45 |
+
extract_commit_hash,
|
| 46 |
+
http_user_agent,
|
| 47 |
+
logging,
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
logger = logging.get_logger(__name__)
|
| 52 |
+
|
| 53 |
+
_re_configuration_file = re.compile(r"config\.(.*)\.json")
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
class FrozenDict(OrderedDict):
|
| 57 |
+
def __init__(self, *args, **kwargs):
|
| 58 |
+
super().__init__(*args, **kwargs)
|
| 59 |
+
|
| 60 |
+
for key, value in self.items():
|
| 61 |
+
setattr(self, key, value)
|
| 62 |
+
|
| 63 |
+
self.__frozen = True
|
| 64 |
+
|
| 65 |
+
def __delitem__(self, *args, **kwargs):
|
| 66 |
+
raise Exception(f"You cannot use ``__delitem__`` on a {self.__class__.__name__} instance.")
|
| 67 |
+
|
| 68 |
+
def setdefault(self, *args, **kwargs):
|
| 69 |
+
raise Exception(f"You cannot use ``setdefault`` on a {self.__class__.__name__} instance.")
|
| 70 |
+
|
| 71 |
+
def pop(self, *args, **kwargs):
|
| 72 |
+
raise Exception(f"You cannot use ``pop`` on a {self.__class__.__name__} instance.")
|
| 73 |
+
|
| 74 |
+
def update(self, *args, **kwargs):
|
| 75 |
+
raise Exception(f"You cannot use ``update`` on a {self.__class__.__name__} instance.")
|
| 76 |
+
|
| 77 |
+
def __setattr__(self, name, value):
|
| 78 |
+
if hasattr(self, "__frozen") and self.__frozen:
|
| 79 |
+
raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")
|
| 80 |
+
super().__setattr__(name, value)
|
| 81 |
+
|
| 82 |
+
def __setitem__(self, name, value):
|
| 83 |
+
if hasattr(self, "__frozen") and self.__frozen:
|
| 84 |
+
raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")
|
| 85 |
+
super().__setitem__(name, value)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class ConfigMixin:
|
| 89 |
+
r"""
|
| 90 |
+
Base class for all configuration classes. All configuration parameters are stored under `self.config`. Also
|
| 91 |
+
provides the [`~ConfigMixin.from_config`] and [`~ConfigMixin.save_config`] methods for loading, downloading, and
|
| 92 |
+
saving classes that inherit from [`ConfigMixin`].
|
| 93 |
+
|
| 94 |
+
Class attributes:
|
| 95 |
+
- **config_name** (`str`) -- A filename under which the config should stored when calling
|
| 96 |
+
[`~ConfigMixin.save_config`] (should be overridden by parent class).
|
| 97 |
+
- **ignore_for_config** (`list[str]`) -- A list of attributes that should not be saved in the config (should be
|
| 98 |
+
overridden by subclass).
|
| 99 |
+
- **has_compatibles** (`bool`) -- Whether the class has compatible classes (should be overridden by subclass).
|
| 100 |
+
- **_deprecated_kwargs** (`list[str]`) -- Keyword arguments that are deprecated. Note that the `init` function
|
| 101 |
+
should only have a `kwargs` argument if at least one argument is deprecated (should be overridden by
|
| 102 |
+
subclass).
|
| 103 |
+
"""
|
| 104 |
+
|
| 105 |
+
config_name = None
|
| 106 |
+
ignore_for_config = []
|
| 107 |
+
has_compatibles = False
|
| 108 |
+
|
| 109 |
+
_deprecated_kwargs = []
|
| 110 |
+
_auto_class = None
|
| 111 |
+
|
| 112 |
+
@classmethod
|
| 113 |
+
def register_for_auto_class(cls, auto_class="AutoModel"):
|
| 114 |
+
"""
|
| 115 |
+
Register this class with the given auto class so that it can be loaded with `AutoModel.from_pretrained(...,
|
| 116 |
+
trust_remote_code=True)`.
|
| 117 |
+
|
| 118 |
+
When the config is saved, the resulting `config.json` will include an `auto_map` entry mapping the auto class
|
| 119 |
+
to this class's module and class name.
|
| 120 |
+
|
| 121 |
+
Args:
|
| 122 |
+
auto_class (`str` or type, *optional*, defaults to `"AutoModel"`):
|
| 123 |
+
The auto class to register this class with. Can be a string (e.g. `"AutoModel"`) or the class itself.
|
| 124 |
+
Currently only `"AutoModel"` is supported.
|
| 125 |
+
|
| 126 |
+
Example:
|
| 127 |
+
|
| 128 |
+
```python
|
| 129 |
+
from diffusers import ModelMixin, ConfigMixin
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
class MyCustomModel(ModelMixin, ConfigMixin): ...
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
MyCustomModel.register_for_auto_class("AutoModel")
|
| 136 |
+
```
|
| 137 |
+
"""
|
| 138 |
+
if auto_class != "AutoModel":
|
| 139 |
+
raise ValueError(f"Only 'AutoModel' is supported, got '{auto_class}'.")
|
| 140 |
+
|
| 141 |
+
cls._auto_class = auto_class
|
| 142 |
+
|
| 143 |
+
def register_to_config(self, **kwargs):
|
| 144 |
+
if self.config_name is None:
|
| 145 |
+
raise NotImplementedError(f"Make sure that {self.__class__} has defined a class name `config_name`")
|
| 146 |
+
# Special case for `kwargs` used in deprecation warning added to schedulers
|
| 147 |
+
# TODO: remove this when we remove the deprecation warning, and the `kwargs` argument,
|
| 148 |
+
# or solve in a more general way.
|
| 149 |
+
kwargs.pop("kwargs", None)
|
| 150 |
+
|
| 151 |
+
if not hasattr(self, "_internal_dict"):
|
| 152 |
+
internal_dict = kwargs
|
| 153 |
+
else:
|
| 154 |
+
previous_dict = dict(self._internal_dict)
|
| 155 |
+
internal_dict = {**self._internal_dict, **kwargs}
|
| 156 |
+
logger.debug(f"Updating config from {previous_dict} to {internal_dict}")
|
| 157 |
+
|
| 158 |
+
self._internal_dict = FrozenDict(internal_dict)
|
| 159 |
+
|
| 160 |
+
def __getattr__(self, name: str) -> Any:
|
| 161 |
+
"""The only reason we overwrite `getattr` here is to gracefully deprecate accessing
|
| 162 |
+
config attributes directly. See https://github.com/huggingface/diffusers/pull/3129
|
| 163 |
+
|
| 164 |
+
This function is mostly copied from PyTorch's __getattr__ overwrite:
|
| 165 |
+
https://pytorch.org/docs/stable/_modules/torch/nn/modules/module.html#Module
|
| 166 |
+
"""
|
| 167 |
+
|
| 168 |
+
is_in_config = "_internal_dict" in self.__dict__ and hasattr(self.__dict__["_internal_dict"], name)
|
| 169 |
+
is_attribute = name in self.__dict__
|
| 170 |
+
|
| 171 |
+
if is_in_config and not is_attribute:
|
| 172 |
+
deprecation_message = f"Accessing config attribute `{name}` directly via '{type(self).__name__}' object attribute is deprecated. Please access '{name}' over '{type(self).__name__}'s config object instead, e.g. 'scheduler.config.{name}'."
|
| 173 |
+
deprecate("direct config name access", "1.0.0", deprecation_message, standard_warn=False)
|
| 174 |
+
return self._internal_dict[name]
|
| 175 |
+
|
| 176 |
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
| 177 |
+
|
| 178 |
+
def save_config(self, save_directory: str | os.PathLike, push_to_hub: bool = False, **kwargs):
|
| 179 |
+
"""
|
| 180 |
+
Save a configuration object to the directory specified in `save_directory` so that it can be reloaded using the
|
| 181 |
+
[`~ConfigMixin.from_config`] class method.
|
| 182 |
+
|
| 183 |
+
Args:
|
| 184 |
+
save_directory (`str` or `os.PathLike`):
|
| 185 |
+
Directory where the configuration JSON file is saved (will be created if it does not exist).
|
| 186 |
+
push_to_hub (`bool`, *optional*, defaults to `False`):
|
| 187 |
+
Whether or not to push your model to the Hugging Face Hub after saving it. You can specify the
|
| 188 |
+
repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
|
| 189 |
+
namespace).
|
| 190 |
+
kwargs (`dict[str, Any]`, *optional*):
|
| 191 |
+
Additional keyword arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
|
| 192 |
+
"""
|
| 193 |
+
if os.path.isfile(save_directory):
|
| 194 |
+
raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
|
| 195 |
+
|
| 196 |
+
os.makedirs(save_directory, exist_ok=True)
|
| 197 |
+
|
| 198 |
+
# If we save using the predefined names, we can load using `from_config`
|
| 199 |
+
output_config_file = os.path.join(save_directory, self.config_name)
|
| 200 |
+
|
| 201 |
+
self.to_json_file(output_config_file)
|
| 202 |
+
logger.info(f"Configuration saved in {output_config_file}")
|
| 203 |
+
|
| 204 |
+
if push_to_hub:
|
| 205 |
+
commit_message = kwargs.pop("commit_message", None)
|
| 206 |
+
private = kwargs.pop("private", None)
|
| 207 |
+
create_pr = kwargs.pop("create_pr", False)
|
| 208 |
+
token = kwargs.pop("token", None)
|
| 209 |
+
repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
|
| 210 |
+
repo_id = create_repo(repo_id, exist_ok=True, private=private, token=token).repo_id
|
| 211 |
+
subfolder = kwargs.pop("subfolder", None)
|
| 212 |
+
|
| 213 |
+
self._upload_folder(
|
| 214 |
+
save_directory,
|
| 215 |
+
repo_id,
|
| 216 |
+
token=token,
|
| 217 |
+
commit_message=commit_message,
|
| 218 |
+
create_pr=create_pr,
|
| 219 |
+
subfolder=subfolder,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
@classmethod
|
| 223 |
+
def from_config(
|
| 224 |
+
cls, config: FrozenDict | dict[str, Any] = None, return_unused_kwargs=False, **kwargs
|
| 225 |
+
) -> Self | tuple[Self, dict[str, Any]]:
|
| 226 |
+
r"""
|
| 227 |
+
Instantiate a Python class from a config dictionary.
|
| 228 |
+
|
| 229 |
+
Parameters:
|
| 230 |
+
config (`dict[str, Any]`):
|
| 231 |
+
A config dictionary from which the Python class is instantiated. Make sure to only load configuration
|
| 232 |
+
files of compatible classes.
|
| 233 |
+
return_unused_kwargs (`bool`, *optional*, defaults to `False`):
|
| 234 |
+
Whether kwargs that are not consumed by the Python class should be returned or not.
|
| 235 |
+
kwargs (remaining dictionary of keyword arguments, *optional*):
|
| 236 |
+
Can be used to update the configuration object (after it is loaded) and initiate the Python class.
|
| 237 |
+
`**kwargs` are passed directly to the underlying scheduler/model's `__init__` method and eventually
|
| 238 |
+
overwrite the same named arguments in `config`.
|
| 239 |
+
|
| 240 |
+
Returns:
|
| 241 |
+
[`ModelMixin`] or [`SchedulerMixin`]:
|
| 242 |
+
A model or scheduler object instantiated from a config dictionary.
|
| 243 |
+
|
| 244 |
+
Examples:
|
| 245 |
+
|
| 246 |
+
```python
|
| 247 |
+
>>> from diffusers import DDPMScheduler, DDIMScheduler, PNDMScheduler
|
| 248 |
+
|
| 249 |
+
>>> # Download scheduler from huggingface.co and cache.
|
| 250 |
+
>>> scheduler = DDPMScheduler.from_pretrained("google/ddpm-cifar10-32")
|
| 251 |
+
|
| 252 |
+
>>> # Instantiate DDIM scheduler class with same config as DDPM
|
| 253 |
+
>>> scheduler = DDIMScheduler.from_config(scheduler.config)
|
| 254 |
+
|
| 255 |
+
>>> # Instantiate PNDM scheduler class with same config as DDPM
|
| 256 |
+
>>> scheduler = PNDMScheduler.from_config(scheduler.config)
|
| 257 |
+
```
|
| 258 |
+
"""
|
| 259 |
+
# <===== TO BE REMOVED WITH DEPRECATION
|
| 260 |
+
# TODO(Patrick) - make sure to remove the following lines when config=="model_path" is deprecated
|
| 261 |
+
if "pretrained_model_name_or_path" in kwargs:
|
| 262 |
+
config = kwargs.pop("pretrained_model_name_or_path")
|
| 263 |
+
|
| 264 |
+
if config is None:
|
| 265 |
+
raise ValueError("Please make sure to provide a config as the first positional argument.")
|
| 266 |
+
# ======>
|
| 267 |
+
|
| 268 |
+
if not isinstance(config, dict):
|
| 269 |
+
deprecation_message = "It is deprecated to pass a pretrained model name or path to `from_config`."
|
| 270 |
+
if "Scheduler" in cls.__name__:
|
| 271 |
+
deprecation_message += (
|
| 272 |
+
f"If you were trying to load a scheduler, please use {cls}.from_pretrained(...) instead."
|
| 273 |
+
" Otherwise, please make sure to pass a configuration dictionary instead. This functionality will"
|
| 274 |
+
" be removed in v1.0.0."
|
| 275 |
+
)
|
| 276 |
+
elif "Model" in cls.__name__:
|
| 277 |
+
deprecation_message += (
|
| 278 |
+
f"If you were trying to load a model, please use {cls}.load_config(...) followed by"
|
| 279 |
+
f" {cls}.from_config(...) instead. Otherwise, please make sure to pass a configuration dictionary"
|
| 280 |
+
" instead. This functionality will be removed in v1.0.0."
|
| 281 |
+
)
|
| 282 |
+
deprecate("config-passed-as-path", "1.0.0", deprecation_message, standard_warn=False)
|
| 283 |
+
config, kwargs = cls.load_config(pretrained_model_name_or_path=config, return_unused_kwargs=True, **kwargs)
|
| 284 |
+
|
| 285 |
+
init_dict, unused_kwargs, hidden_dict = cls.extract_init_dict(config, **kwargs)
|
| 286 |
+
|
| 287 |
+
# Allow dtype to be specified on initialization
|
| 288 |
+
if "dtype" in unused_kwargs:
|
| 289 |
+
init_dict["dtype"] = unused_kwargs.pop("dtype")
|
| 290 |
+
|
| 291 |
+
# add possible deprecated kwargs
|
| 292 |
+
for deprecated_kwarg in cls._deprecated_kwargs:
|
| 293 |
+
if deprecated_kwarg in unused_kwargs:
|
| 294 |
+
init_dict[deprecated_kwarg] = unused_kwargs.pop(deprecated_kwarg)
|
| 295 |
+
|
| 296 |
+
# Return model and optionally state and/or unused_kwargs
|
| 297 |
+
model = cls(**init_dict)
|
| 298 |
+
|
| 299 |
+
# make sure to also save config parameters that might be used for compatible classes
|
| 300 |
+
# update _class_name
|
| 301 |
+
if "_class_name" in hidden_dict:
|
| 302 |
+
hidden_dict["_class_name"] = cls.__name__
|
| 303 |
+
|
| 304 |
+
model.register_to_config(**hidden_dict)
|
| 305 |
+
|
| 306 |
+
# add hidden kwargs of compatible classes to unused_kwargs
|
| 307 |
+
unused_kwargs = {**unused_kwargs, **hidden_dict}
|
| 308 |
+
|
| 309 |
+
if return_unused_kwargs:
|
| 310 |
+
return (model, unused_kwargs)
|
| 311 |
+
else:
|
| 312 |
+
return model
|
| 313 |
+
|
| 314 |
+
@classmethod
|
| 315 |
+
def get_config_dict(cls, *args, **kwargs):
|
| 316 |
+
deprecation_message = (
|
| 317 |
+
f" The function get_config_dict is deprecated. Please use {cls}.load_config instead. This function will be"
|
| 318 |
+
" removed in version v1.0.0"
|
| 319 |
+
)
|
| 320 |
+
deprecate("get_config_dict", "1.0.0", deprecation_message, standard_warn=False)
|
| 321 |
+
return cls.load_config(*args, **kwargs)
|
| 322 |
+
|
| 323 |
+
@classmethod
|
| 324 |
+
@validate_hf_hub_args
|
| 325 |
+
def load_config(
|
| 326 |
+
cls,
|
| 327 |
+
pretrained_model_name_or_path: str | os.PathLike,
|
| 328 |
+
return_unused_kwargs=False,
|
| 329 |
+
return_commit_hash=False,
|
| 330 |
+
**kwargs,
|
| 331 |
+
) -> tuple[dict[str, Any], dict[str, Any]]:
|
| 332 |
+
r"""
|
| 333 |
+
Load a model or scheduler configuration.
|
| 334 |
+
|
| 335 |
+
Parameters:
|
| 336 |
+
pretrained_model_name_or_path (`str` or `os.PathLike`, *optional*):
|
| 337 |
+
Can be either:
|
| 338 |
+
|
| 339 |
+
- A string, the *model id* (for example `google/ddpm-celebahq-256`) of a pretrained model hosted on
|
| 340 |
+
the Hub.
|
| 341 |
+
- A path to a *directory* (for example `./my_model_directory`) containing model weights saved with
|
| 342 |
+
[`~ConfigMixin.save_config`].
|
| 343 |
+
|
| 344 |
+
cache_dir (`str | os.PathLike`, *optional*):
|
| 345 |
+
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
| 346 |
+
is not used.
|
| 347 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 348 |
+
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
| 349 |
+
cached versions if they exist.
|
| 350 |
+
proxies (`dict[str, str]`, *optional*):
|
| 351 |
+
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
| 352 |
+
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
| 353 |
+
output_loading_info(`bool`, *optional*, defaults to `False`):
|
| 354 |
+
Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
|
| 355 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 356 |
+
Whether to only load local model weights and configuration files or not. If set to `True`, the model
|
| 357 |
+
won't be downloaded from the Hub.
|
| 358 |
+
token (`str` or *bool*, *optional*):
|
| 359 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
| 360 |
+
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
| 361 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 362 |
+
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
| 363 |
+
allowed by Git.
|
| 364 |
+
subfolder (`str`, *optional*, defaults to `""`):
|
| 365 |
+
The subfolder location of a model file within a larger model repository on the Hub or locally.
|
| 366 |
+
return_unused_kwargs (`bool`, *optional*, defaults to `False):
|
| 367 |
+
Whether unused keyword arguments of the config are returned.
|
| 368 |
+
return_commit_hash (`bool`, *optional*, defaults to `False):
|
| 369 |
+
Whether the `commit_hash` of the loaded configuration are returned.
|
| 370 |
+
|
| 371 |
+
Returns:
|
| 372 |
+
`dict`:
|
| 373 |
+
A dictionary of all the parameters stored in a JSON configuration file.
|
| 374 |
+
|
| 375 |
+
"""
|
| 376 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 377 |
+
local_dir = kwargs.pop("local_dir", None)
|
| 378 |
+
local_dir_use_symlinks = kwargs.pop("local_dir_use_symlinks", "auto")
|
| 379 |
+
force_download = kwargs.pop("force_download", False)
|
| 380 |
+
proxies = kwargs.pop("proxies", None)
|
| 381 |
+
token = kwargs.pop("token", None)
|
| 382 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 383 |
+
revision = kwargs.pop("revision", None)
|
| 384 |
+
_ = kwargs.pop("mirror", None)
|
| 385 |
+
subfolder = kwargs.pop("subfolder", None)
|
| 386 |
+
user_agent = kwargs.pop("user_agent", {})
|
| 387 |
+
dduf_entries: dict[str, DDUFEntry] | None = kwargs.pop("dduf_entries", None)
|
| 388 |
+
|
| 389 |
+
user_agent = {**user_agent, "file_type": "config"}
|
| 390 |
+
user_agent = http_user_agent(user_agent)
|
| 391 |
+
|
| 392 |
+
pretrained_model_name_or_path = str(pretrained_model_name_or_path)
|
| 393 |
+
|
| 394 |
+
if cls.config_name is None:
|
| 395 |
+
raise ValueError(
|
| 396 |
+
"`self.config_name` is not defined. Note that one should not load a config from "
|
| 397 |
+
"`ConfigMixin`. Please make sure to define `config_name` in a class inheriting from `ConfigMixin`"
|
| 398 |
+
)
|
| 399 |
+
# Custom path for now
|
| 400 |
+
if dduf_entries:
|
| 401 |
+
if subfolder is not None:
|
| 402 |
+
raise ValueError(
|
| 403 |
+
"DDUF file only allow for 1 level of directory (e.g transformer/model1/model.safetentors is not allowed). "
|
| 404 |
+
"Please check the DDUF structure"
|
| 405 |
+
)
|
| 406 |
+
config_file = cls._get_config_file_from_dduf(pretrained_model_name_or_path, dduf_entries)
|
| 407 |
+
elif os.path.isfile(pretrained_model_name_or_path):
|
| 408 |
+
config_file = pretrained_model_name_or_path
|
| 409 |
+
elif os.path.isdir(pretrained_model_name_or_path):
|
| 410 |
+
if subfolder is not None and os.path.isfile(
|
| 411 |
+
os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)
|
| 412 |
+
):
|
| 413 |
+
config_file = os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)
|
| 414 |
+
elif os.path.isfile(os.path.join(pretrained_model_name_or_path, cls.config_name)):
|
| 415 |
+
# Load from a PyTorch checkpoint
|
| 416 |
+
config_file = os.path.join(pretrained_model_name_or_path, cls.config_name)
|
| 417 |
+
else:
|
| 418 |
+
raise EnvironmentError(
|
| 419 |
+
f"Error no file named {cls.config_name} found in directory {pretrained_model_name_or_path}."
|
| 420 |
+
)
|
| 421 |
+
else:
|
| 422 |
+
try:
|
| 423 |
+
# Load from URL or cache if already cached
|
| 424 |
+
config_file = hf_hub_download(
|
| 425 |
+
pretrained_model_name_or_path,
|
| 426 |
+
filename=cls.config_name,
|
| 427 |
+
cache_dir=cache_dir,
|
| 428 |
+
force_download=force_download,
|
| 429 |
+
proxies=proxies,
|
| 430 |
+
local_files_only=local_files_only,
|
| 431 |
+
token=token,
|
| 432 |
+
user_agent=user_agent,
|
| 433 |
+
subfolder=subfolder,
|
| 434 |
+
revision=revision,
|
| 435 |
+
local_dir=local_dir,
|
| 436 |
+
local_dir_use_symlinks=local_dir_use_symlinks,
|
| 437 |
+
)
|
| 438 |
+
except RepositoryNotFoundError:
|
| 439 |
+
raise EnvironmentError(
|
| 440 |
+
f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier"
|
| 441 |
+
" listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a"
|
| 442 |
+
" token having permission to this repo with `token` or log in with `hf auth login`."
|
| 443 |
+
)
|
| 444 |
+
except RevisionNotFoundError:
|
| 445 |
+
raise EnvironmentError(
|
| 446 |
+
f"{revision} is not a valid git identifier (branch name, tag name or commit id) that exists for"
|
| 447 |
+
" this model name. Check the model page at"
|
| 448 |
+
f" 'https://huggingface.co/{pretrained_model_name_or_path}' for available revisions."
|
| 449 |
+
)
|
| 450 |
+
except EntryNotFoundError:
|
| 451 |
+
raise EnvironmentError(
|
| 452 |
+
f"{pretrained_model_name_or_path} does not appear to have a file named {cls.config_name}."
|
| 453 |
+
)
|
| 454 |
+
except HfHubHTTPError as err:
|
| 455 |
+
raise EnvironmentError(
|
| 456 |
+
"There was a specific connection error when trying to load"
|
| 457 |
+
f" {pretrained_model_name_or_path}:\n{err}"
|
| 458 |
+
)
|
| 459 |
+
except ValueError:
|
| 460 |
+
raise EnvironmentError(
|
| 461 |
+
f"We couldn't connect to '{HUGGINGFACE_CO_RESOLVE_ENDPOINT}' to load this model, couldn't find it"
|
| 462 |
+
f" in the cached files and it looks like {pretrained_model_name_or_path} is not the path to a"
|
| 463 |
+
f" directory containing a {cls.config_name} file.\nCheckout your internet connection or see how to"
|
| 464 |
+
" run the library in offline mode at"
|
| 465 |
+
" 'https://huggingface.co/docs/diffusers/installation#offline-mode'."
|
| 466 |
+
)
|
| 467 |
+
except EnvironmentError:
|
| 468 |
+
raise EnvironmentError(
|
| 469 |
+
f"Can't load config for '{pretrained_model_name_or_path}'. If you were trying to load it from "
|
| 470 |
+
"'https://huggingface.co/models', make sure you don't have a local directory with the same name. "
|
| 471 |
+
f"Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a directory "
|
| 472 |
+
f"containing a {cls.config_name} file"
|
| 473 |
+
)
|
| 474 |
+
try:
|
| 475 |
+
config_dict = cls._dict_from_json_file(config_file, dduf_entries=dduf_entries)
|
| 476 |
+
|
| 477 |
+
commit_hash = extract_commit_hash(config_file)
|
| 478 |
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
| 479 |
+
raise EnvironmentError(f"It looks like the config file at '{config_file}' is not a valid JSON file.")
|
| 480 |
+
|
| 481 |
+
if not (return_unused_kwargs or return_commit_hash):
|
| 482 |
+
return config_dict
|
| 483 |
+
|
| 484 |
+
outputs = (config_dict,)
|
| 485 |
+
|
| 486 |
+
if return_unused_kwargs:
|
| 487 |
+
outputs += (kwargs,)
|
| 488 |
+
|
| 489 |
+
if return_commit_hash:
|
| 490 |
+
outputs += (commit_hash,)
|
| 491 |
+
|
| 492 |
+
return outputs
|
| 493 |
+
|
| 494 |
+
@staticmethod
|
| 495 |
+
def _get_init_keys(input_class):
|
| 496 |
+
return set(dict(inspect.signature(input_class.__init__).parameters).keys())
|
| 497 |
+
|
| 498 |
+
@classmethod
|
| 499 |
+
def extract_init_dict(cls, config_dict, **kwargs):
|
| 500 |
+
# Skip keys that were not present in the original config, so default __init__ values were used
|
| 501 |
+
used_defaults = config_dict.get("_use_default_values", [])
|
| 502 |
+
config_dict = {k: v for k, v in config_dict.items() if k not in used_defaults and k != "_use_default_values"}
|
| 503 |
+
|
| 504 |
+
# 0. Copy origin config dict
|
| 505 |
+
original_dict = dict(config_dict.items())
|
| 506 |
+
|
| 507 |
+
# 1. Retrieve expected config attributes from __init__ signature
|
| 508 |
+
expected_keys = cls._get_init_keys(cls)
|
| 509 |
+
expected_keys.remove("self")
|
| 510 |
+
# remove general kwargs if present in dict
|
| 511 |
+
if "kwargs" in expected_keys:
|
| 512 |
+
expected_keys.remove("kwargs")
|
| 513 |
+
# remove flax internal keys
|
| 514 |
+
if hasattr(cls, "_flax_internal_args"):
|
| 515 |
+
for arg in cls._flax_internal_args:
|
| 516 |
+
expected_keys.remove(arg)
|
| 517 |
+
|
| 518 |
+
# 2. Remove attributes that cannot be expected from expected config attributes
|
| 519 |
+
# remove keys to be ignored
|
| 520 |
+
if len(cls.ignore_for_config) > 0:
|
| 521 |
+
expected_keys = expected_keys - set(cls.ignore_for_config)
|
| 522 |
+
|
| 523 |
+
# load diffusers library to import compatible and original scheduler
|
| 524 |
+
diffusers_library = importlib.import_module(__name__.split(".")[0])
|
| 525 |
+
|
| 526 |
+
if cls.has_compatibles:
|
| 527 |
+
compatible_classes = [c for c in cls._get_compatibles() if not isinstance(c, DummyObject)]
|
| 528 |
+
else:
|
| 529 |
+
compatible_classes = []
|
| 530 |
+
|
| 531 |
+
expected_keys_comp_cls = set()
|
| 532 |
+
for c in compatible_classes:
|
| 533 |
+
expected_keys_c = cls._get_init_keys(c)
|
| 534 |
+
expected_keys_comp_cls = expected_keys_comp_cls.union(expected_keys_c)
|
| 535 |
+
expected_keys_comp_cls = expected_keys_comp_cls - cls._get_init_keys(cls)
|
| 536 |
+
config_dict = {k: v for k, v in config_dict.items() if k not in expected_keys_comp_cls}
|
| 537 |
+
|
| 538 |
+
# remove attributes from orig class that cannot be expected
|
| 539 |
+
orig_cls_name = config_dict.pop("_class_name", cls.__name__)
|
| 540 |
+
if (
|
| 541 |
+
isinstance(orig_cls_name, str)
|
| 542 |
+
and orig_cls_name != cls.__name__
|
| 543 |
+
and hasattr(diffusers_library, orig_cls_name)
|
| 544 |
+
):
|
| 545 |
+
orig_cls = getattr(diffusers_library, orig_cls_name)
|
| 546 |
+
unexpected_keys_from_orig = cls._get_init_keys(orig_cls) - expected_keys
|
| 547 |
+
config_dict = {k: v for k, v in config_dict.items() if k not in unexpected_keys_from_orig}
|
| 548 |
+
elif not isinstance(orig_cls_name, str) and not isinstance(orig_cls_name, (list, tuple)):
|
| 549 |
+
raise ValueError(
|
| 550 |
+
"Make sure that the `_class_name` is of type string or list of string (for custom pipelines)."
|
| 551 |
+
)
|
| 552 |
+
|
| 553 |
+
# remove private attributes
|
| 554 |
+
config_dict = {k: v for k, v in config_dict.items() if not k.startswith("_")}
|
| 555 |
+
|
| 556 |
+
# remove quantization_config
|
| 557 |
+
config_dict = {k: v for k, v in config_dict.items() if k != "quantization_config"}
|
| 558 |
+
|
| 559 |
+
# 3. Create keyword arguments that will be passed to __init__ from expected keyword arguments
|
| 560 |
+
init_dict = {}
|
| 561 |
+
for key in expected_keys:
|
| 562 |
+
# if config param is passed to kwarg and is present in config dict
|
| 563 |
+
# it should overwrite existing config dict key
|
| 564 |
+
if key in kwargs and key in config_dict:
|
| 565 |
+
config_dict[key] = kwargs.pop(key)
|
| 566 |
+
|
| 567 |
+
if key in kwargs:
|
| 568 |
+
# overwrite key
|
| 569 |
+
init_dict[key] = kwargs.pop(key)
|
| 570 |
+
elif key in config_dict:
|
| 571 |
+
# use value from config dict
|
| 572 |
+
init_dict[key] = config_dict.pop(key)
|
| 573 |
+
|
| 574 |
+
# 4. Give nice warning if unexpected values have been passed
|
| 575 |
+
if len(config_dict) > 0:
|
| 576 |
+
logger.warning(
|
| 577 |
+
f"The config attributes {config_dict} were passed to {cls.__name__}, "
|
| 578 |
+
"but are not expected and will be ignored. Please verify your "
|
| 579 |
+
f"{cls.config_name} configuration file."
|
| 580 |
+
)
|
| 581 |
+
|
| 582 |
+
# 5. Give nice info if config attributes are initialized to default because they have not been passed
|
| 583 |
+
passed_keys = set(init_dict.keys())
|
| 584 |
+
if len(expected_keys - passed_keys) > 0:
|
| 585 |
+
logger.info(
|
| 586 |
+
f"{expected_keys - passed_keys} was not found in config. Values will be initialized to default values."
|
| 587 |
+
)
|
| 588 |
+
|
| 589 |
+
# 6. Define unused keyword arguments
|
| 590 |
+
unused_kwargs = {**config_dict, **kwargs}
|
| 591 |
+
|
| 592 |
+
# 7. Define "hidden" config parameters that were saved for compatible classes
|
| 593 |
+
hidden_config_dict = {k: v for k, v in original_dict.items() if k not in init_dict}
|
| 594 |
+
|
| 595 |
+
return init_dict, unused_kwargs, hidden_config_dict
|
| 596 |
+
|
| 597 |
+
@classmethod
|
| 598 |
+
def _dict_from_json_file(cls, json_file: str | os.PathLike, dduf_entries: dict[str, DDUFEntry] | None = None):
|
| 599 |
+
if dduf_entries:
|
| 600 |
+
text = dduf_entries[json_file].read_text()
|
| 601 |
+
else:
|
| 602 |
+
with open(json_file, "r", encoding="utf-8") as reader:
|
| 603 |
+
text = reader.read()
|
| 604 |
+
return json.loads(text)
|
| 605 |
+
|
| 606 |
+
def __repr__(self):
|
| 607 |
+
return f"{self.__class__.__name__} {self.to_json_string()}"
|
| 608 |
+
|
| 609 |
+
@property
|
| 610 |
+
def config(self) -> dict[str, Any]:
|
| 611 |
+
"""
|
| 612 |
+
Returns the config of the class as a frozen dictionary
|
| 613 |
+
|
| 614 |
+
Returns:
|
| 615 |
+
`dict[str, Any]`: Config of the class.
|
| 616 |
+
"""
|
| 617 |
+
return self._internal_dict
|
| 618 |
+
|
| 619 |
+
def to_json_string(self) -> str:
|
| 620 |
+
"""
|
| 621 |
+
Serializes the configuration instance to a JSON string.
|
| 622 |
+
|
| 623 |
+
Returns:
|
| 624 |
+
`str`:
|
| 625 |
+
String containing all the attributes that make up the configuration instance in JSON format.
|
| 626 |
+
"""
|
| 627 |
+
config_dict = self._internal_dict if hasattr(self, "_internal_dict") else {}
|
| 628 |
+
config_dict["_class_name"] = self.__class__.__name__
|
| 629 |
+
config_dict["_diffusers_version"] = __version__
|
| 630 |
+
|
| 631 |
+
def to_json_saveable(value):
|
| 632 |
+
if isinstance(value, np.ndarray):
|
| 633 |
+
value = value.tolist()
|
| 634 |
+
elif isinstance(value, Path):
|
| 635 |
+
value = value.as_posix()
|
| 636 |
+
elif hasattr(value, "to_dict") and callable(value.to_dict):
|
| 637 |
+
value = value.to_dict()
|
| 638 |
+
elif isinstance(value, list):
|
| 639 |
+
value = [to_json_saveable(v) for v in value]
|
| 640 |
+
return value
|
| 641 |
+
|
| 642 |
+
if "quantization_config" in config_dict:
|
| 643 |
+
config_dict["quantization_config"] = (
|
| 644 |
+
config_dict.quantization_config.to_dict()
|
| 645 |
+
if not isinstance(config_dict.quantization_config, dict)
|
| 646 |
+
else config_dict.quantization_config
|
| 647 |
+
)
|
| 648 |
+
|
| 649 |
+
config_dict = {k: to_json_saveable(v) for k, v in config_dict.items()}
|
| 650 |
+
# Don't save "_ignore_files" or "_use_default_values"
|
| 651 |
+
config_dict.pop("_ignore_files", None)
|
| 652 |
+
config_dict.pop("_use_default_values", None)
|
| 653 |
+
# pop the `_pre_quantization_dtype` as torch.dtypes are not serializable.
|
| 654 |
+
_ = config_dict.pop("_pre_quantization_dtype", None)
|
| 655 |
+
|
| 656 |
+
if getattr(self, "_auto_class", None) is not None:
|
| 657 |
+
module = self.__class__.__module__.split(".")[-1]
|
| 658 |
+
auto_map = config_dict.get("auto_map", {})
|
| 659 |
+
auto_map[self._auto_class] = f"{module}.{self.__class__.__name__}"
|
| 660 |
+
config_dict["auto_map"] = auto_map
|
| 661 |
+
|
| 662 |
+
return json.dumps(config_dict, indent=2, sort_keys=True) + "\n"
|
| 663 |
+
|
| 664 |
+
def to_json_file(self, json_file_path: str | os.PathLike):
|
| 665 |
+
"""
|
| 666 |
+
Save the configuration instance's parameters to a JSON file.
|
| 667 |
+
|
| 668 |
+
Args:
|
| 669 |
+
json_file_path (`str` or `os.PathLike`):
|
| 670 |
+
Path to the JSON file to save a configuration instance's parameters.
|
| 671 |
+
"""
|
| 672 |
+
with open(json_file_path, "w", encoding="utf-8") as writer:
|
| 673 |
+
writer.write(self.to_json_string())
|
| 674 |
+
|
| 675 |
+
@classmethod
|
| 676 |
+
def _get_config_file_from_dduf(cls, pretrained_model_name_or_path: str, dduf_entries: dict[str, DDUFEntry]):
|
| 677 |
+
# paths inside a DDUF file must always be "/"
|
| 678 |
+
config_file = (
|
| 679 |
+
cls.config_name
|
| 680 |
+
if pretrained_model_name_or_path == ""
|
| 681 |
+
else "/".join([pretrained_model_name_or_path, cls.config_name])
|
| 682 |
+
)
|
| 683 |
+
if config_file not in dduf_entries:
|
| 684 |
+
raise ValueError(
|
| 685 |
+
f"We did not manage to find the file {config_file} in the dduf file. We only have the following files {dduf_entries.keys()}"
|
| 686 |
+
)
|
| 687 |
+
return config_file
|
| 688 |
+
|
| 689 |
+
|
| 690 |
+
def register_to_config(init):
|
| 691 |
+
r"""
|
| 692 |
+
Decorator to apply on the init of classes inheriting from [`ConfigMixin`] so that all the arguments are
|
| 693 |
+
automatically sent to `self.register_for_config`. To ignore a specific argument accepted by the init but that
|
| 694 |
+
shouldn't be registered in the config, use the `ignore_for_config` class variable
|
| 695 |
+
|
| 696 |
+
Warning: Once decorated, all private arguments (beginning with an underscore) are trashed and not sent to the init!
|
| 697 |
+
"""
|
| 698 |
+
|
| 699 |
+
@functools.wraps(init)
|
| 700 |
+
def inner_init(self, *args, **kwargs):
|
| 701 |
+
# Ignore private kwargs in the init.
|
| 702 |
+
init_kwargs = {k: v for k, v in kwargs.items() if not k.startswith("_")}
|
| 703 |
+
config_init_kwargs = {k: v for k, v in kwargs.items() if k.startswith("_")}
|
| 704 |
+
if not isinstance(self, ConfigMixin):
|
| 705 |
+
raise RuntimeError(
|
| 706 |
+
f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "
|
| 707 |
+
"not inherit from `ConfigMixin`."
|
| 708 |
+
)
|
| 709 |
+
|
| 710 |
+
ignore = getattr(self, "ignore_for_config", [])
|
| 711 |
+
# Get positional arguments aligned with kwargs
|
| 712 |
+
new_kwargs = {}
|
| 713 |
+
signature = inspect.signature(init)
|
| 714 |
+
parameters = {
|
| 715 |
+
name: p.default for i, (name, p) in enumerate(signature.parameters.items()) if i > 0 and name not in ignore
|
| 716 |
+
}
|
| 717 |
+
for arg, name in zip(args, parameters.keys()):
|
| 718 |
+
new_kwargs[name] = arg
|
| 719 |
+
|
| 720 |
+
# Then add all kwargs
|
| 721 |
+
new_kwargs.update(
|
| 722 |
+
{
|
| 723 |
+
k: init_kwargs.get(k, default)
|
| 724 |
+
for k, default in parameters.items()
|
| 725 |
+
if k not in ignore and k not in new_kwargs
|
| 726 |
+
}
|
| 727 |
+
)
|
| 728 |
+
|
| 729 |
+
# Take note of the parameters that were not present in the loaded config
|
| 730 |
+
if len(set(new_kwargs.keys()) - set(init_kwargs)) > 0:
|
| 731 |
+
new_kwargs["_use_default_values"] = list(set(new_kwargs.keys()) - set(init_kwargs))
|
| 732 |
+
|
| 733 |
+
new_kwargs = {**config_init_kwargs, **new_kwargs}
|
| 734 |
+
getattr(self, "register_to_config")(**new_kwargs)
|
| 735 |
+
init(self, *args, **init_kwargs)
|
| 736 |
+
|
| 737 |
+
return inner_init
|
| 738 |
+
|
| 739 |
+
|
| 740 |
+
def flax_register_to_config(cls):
|
| 741 |
+
original_init = cls.__init__
|
| 742 |
+
|
| 743 |
+
@functools.wraps(original_init)
|
| 744 |
+
def init(self, *args, **kwargs):
|
| 745 |
+
if not isinstance(self, ConfigMixin):
|
| 746 |
+
raise RuntimeError(
|
| 747 |
+
f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "
|
| 748 |
+
"not inherit from `ConfigMixin`."
|
| 749 |
+
)
|
| 750 |
+
|
| 751 |
+
# Ignore private kwargs in the init. Retrieve all passed attributes
|
| 752 |
+
init_kwargs = dict(kwargs.items())
|
| 753 |
+
|
| 754 |
+
# Retrieve default values
|
| 755 |
+
fields = dataclasses.fields(self)
|
| 756 |
+
default_kwargs = {}
|
| 757 |
+
for field in fields:
|
| 758 |
+
# ignore flax specific attributes
|
| 759 |
+
if field.name in self._flax_internal_args:
|
| 760 |
+
continue
|
| 761 |
+
if type(field.default) == dataclasses._MISSING_TYPE:
|
| 762 |
+
default_kwargs[field.name] = None
|
| 763 |
+
else:
|
| 764 |
+
default_kwargs[field.name] = getattr(self, field.name)
|
| 765 |
+
|
| 766 |
+
# Make sure init_kwargs override default kwargs
|
| 767 |
+
new_kwargs = {**default_kwargs, **init_kwargs}
|
| 768 |
+
# dtype should be part of `init_kwargs`, but not `new_kwargs`
|
| 769 |
+
if "dtype" in new_kwargs:
|
| 770 |
+
new_kwargs.pop("dtype")
|
| 771 |
+
|
| 772 |
+
# Get positional arguments aligned with kwargs
|
| 773 |
+
for i, arg in enumerate(args):
|
| 774 |
+
name = fields[i].name
|
| 775 |
+
new_kwargs[name] = arg
|
| 776 |
+
|
| 777 |
+
# Take note of the parameters that were not present in the loaded config
|
| 778 |
+
if len(set(new_kwargs.keys()) - set(init_kwargs)) > 0:
|
| 779 |
+
new_kwargs["_use_default_values"] = list(set(new_kwargs.keys()) - set(init_kwargs))
|
| 780 |
+
|
| 781 |
+
getattr(self, "register_to_config")(**new_kwargs)
|
| 782 |
+
original_init(self, *args, **kwargs)
|
| 783 |
+
|
| 784 |
+
cls.__init__ = init
|
| 785 |
+
return cls
|
| 786 |
+
|
| 787 |
+
|
| 788 |
+
class LegacyConfigMixin(ConfigMixin):
|
| 789 |
+
r"""
|
| 790 |
+
A subclass of `ConfigMixin` to resolve class mapping from legacy classes (like `Transformer2DModel`) to more
|
| 791 |
+
pipeline-specific classes (like `DiTTransformer2DModel`).
|
| 792 |
+
"""
|
| 793 |
+
|
| 794 |
+
@classmethod
|
| 795 |
+
def from_config(cls, config: FrozenDict | dict[str, Any] = None, return_unused_kwargs=False, **kwargs):
|
| 796 |
+
# To prevent dependency import problem.
|
| 797 |
+
from .models.model_loading_utils import _fetch_remapped_cls_from_config
|
| 798 |
+
|
| 799 |
+
# resolve remapping
|
| 800 |
+
remapped_class = _fetch_remapped_cls_from_config(config, cls)
|
| 801 |
+
|
| 802 |
+
if remapped_class is cls:
|
| 803 |
+
return super(LegacyConfigMixin, remapped_class).from_config(config, return_unused_kwargs, **kwargs)
|
| 804 |
+
else:
|
| 805 |
+
return remapped_class.from_config(config, return_unused_kwargs, **kwargs)
|
third_party/diffusers/src/diffusers/dependency_versions_check.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 .dependency_versions_table import deps
|
| 16 |
+
from .utils.versions import require_version, require_version_core
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
# define which module versions we always want to check at run time
|
| 20 |
+
# (usually the ones defined in `install_requires` in setup.py)
|
| 21 |
+
#
|
| 22 |
+
# order specific notes:
|
| 23 |
+
# - tqdm must be checked before tokenizers
|
| 24 |
+
|
| 25 |
+
pkgs_to_check_at_runtime = "python requests filelock numpy".split()
|
| 26 |
+
for pkg in pkgs_to_check_at_runtime:
|
| 27 |
+
if pkg in deps:
|
| 28 |
+
require_version_core(deps[pkg])
|
| 29 |
+
else:
|
| 30 |
+
raise ValueError(f"can't find {pkg} in {deps.keys()}, check dependency_versions_table.py")
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def dep_version_check(pkg, hint=None):
|
| 34 |
+
require_version(deps[pkg], hint)
|
third_party/diffusers/src/diffusers/dependency_versions_table.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# THIS FILE HAS BEEN AUTOGENERATED. To update:
|
| 2 |
+
# 1. modify the `_deps` dict in setup.py
|
| 3 |
+
# 2. run `make deps_table_update`
|
| 4 |
+
deps = {
|
| 5 |
+
"Pillow": "Pillow",
|
| 6 |
+
"accelerate": "accelerate>=0.31.0",
|
| 7 |
+
"compel": "compel==0.1.8",
|
| 8 |
+
"datasets": "datasets",
|
| 9 |
+
"filelock": "filelock",
|
| 10 |
+
"flax": "flax>=0.4.1",
|
| 11 |
+
"ftfy": "ftfy",
|
| 12 |
+
"hf-doc-builder": "hf-doc-builder>=0.3.0",
|
| 13 |
+
"httpx": "httpx<1.0.0",
|
| 14 |
+
"huggingface-hub": "huggingface-hub>=0.34.0,<2.0",
|
| 15 |
+
"requests-mock": "requests-mock==1.10.0",
|
| 16 |
+
"importlib_metadata": "importlib_metadata",
|
| 17 |
+
"invisible-watermark": "invisible-watermark>=0.2.0",
|
| 18 |
+
"isort": "isort>=5.5.4",
|
| 19 |
+
"jax": "jax>=0.4.1",
|
| 20 |
+
"jaxlib": "jaxlib>=0.4.1",
|
| 21 |
+
"Jinja2": "Jinja2",
|
| 22 |
+
"torchsde": "torchsde",
|
| 23 |
+
"note_seq": "note_seq",
|
| 24 |
+
"librosa": "librosa",
|
| 25 |
+
"numpy": "numpy",
|
| 26 |
+
"parameterized": "parameterized",
|
| 27 |
+
"peft": "peft>=0.17.0",
|
| 28 |
+
"protobuf": "protobuf>=3.20.3,<4",
|
| 29 |
+
"pytest": "pytest",
|
| 30 |
+
"pytest-timeout": "pytest-timeout",
|
| 31 |
+
"pytest-xdist": "pytest-xdist",
|
| 32 |
+
"python": "python>=3.10.0",
|
| 33 |
+
"ruff": "ruff==0.9.10",
|
| 34 |
+
"safetensors": "safetensors>=0.3.1",
|
| 35 |
+
"sentencepiece": "sentencepiece>=0.1.91,!=0.1.92",
|
| 36 |
+
"GitPython": "GitPython<3.1.19",
|
| 37 |
+
"scipy": "scipy",
|
| 38 |
+
"onnx": "onnx",
|
| 39 |
+
"optimum_quanto": "optimum_quanto>=0.2.6",
|
| 40 |
+
"gguf": "gguf>=0.10.0",
|
| 41 |
+
"torchao": "torchao>=0.7.0",
|
| 42 |
+
"bitsandbytes": "bitsandbytes>=0.43.3",
|
| 43 |
+
"nvidia_modelopt[hf]": "nvidia_modelopt[hf]>=0.33.1",
|
| 44 |
+
"regex": "regex!=2019.12.17",
|
| 45 |
+
"requests": "requests",
|
| 46 |
+
"tensorboard": "tensorboard",
|
| 47 |
+
"tiktoken": "tiktoken>=0.7.0",
|
| 48 |
+
"torch": "torch>=1.4",
|
| 49 |
+
"torchvision": "torchvision",
|
| 50 |
+
"transformers": "transformers>=4.41.2",
|
| 51 |
+
"urllib3": "urllib3<=2.0.0",
|
| 52 |
+
"black": "black",
|
| 53 |
+
"phonemizer": "phonemizer",
|
| 54 |
+
"opencv-python": "opencv-python",
|
| 55 |
+
"timm": "timm",
|
| 56 |
+
}
|
third_party/diffusers/src/diffusers/hooks/__init__.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
| 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 ..utils import is_torch_available
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
if is_torch_available():
|
| 19 |
+
from .context_parallel import apply_context_parallel
|
| 20 |
+
from .faster_cache import FasterCacheConfig, apply_faster_cache
|
| 21 |
+
from .first_block_cache import FirstBlockCacheConfig, apply_first_block_cache
|
| 22 |
+
from .group_offloading import apply_group_offloading
|
| 23 |
+
from .hooks import HookRegistry, ModelHook
|
| 24 |
+
from .layer_skip import LayerSkipConfig, apply_layer_skip
|
| 25 |
+
from .layerwise_casting import apply_layerwise_casting, apply_layerwise_casting_hook
|
| 26 |
+
from .mag_cache import MagCacheConfig, apply_mag_cache
|
| 27 |
+
from .pyramid_attention_broadcast import PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
|
| 28 |
+
from .smoothed_energy_guidance_utils import SmoothedEnergyGuidanceConfig
|
| 29 |
+
from .taylorseer_cache import TaylorSeerCacheConfig, apply_taylorseer_cache
|
| 30 |
+
from .text_kv_cache import TextKVCacheConfig, apply_text_kv_cache
|
third_party/diffusers/src/diffusers/hooks/_common.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 ..models.attention import AttentionModuleMixin, FeedForward, LuminaFeedForward
|
| 18 |
+
from ..models.attention_processor import Attention, MochiAttention
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
_ATTENTION_CLASSES = (Attention, MochiAttention, AttentionModuleMixin)
|
| 22 |
+
_FEEDFORWARD_CLASSES = (FeedForward, LuminaFeedForward)
|
| 23 |
+
|
| 24 |
+
_SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS = (
|
| 25 |
+
"blocks",
|
| 26 |
+
"transformer_blocks",
|
| 27 |
+
"single_transformer_blocks",
|
| 28 |
+
"layers",
|
| 29 |
+
"visual_transformer_blocks",
|
| 30 |
+
)
|
| 31 |
+
_TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS = ("temporal_transformer_blocks",)
|
| 32 |
+
_CROSS_TRANSFORMER_BLOCK_IDENTIFIERS = ("blocks", "transformer_blocks", "layers")
|
| 33 |
+
|
| 34 |
+
_ALL_TRANSFORMER_BLOCK_IDENTIFIERS = tuple(
|
| 35 |
+
{
|
| 36 |
+
*_SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 37 |
+
*_TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 38 |
+
*_CROSS_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 39 |
+
}
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
# Layers supported for group offloading and layerwise casting
|
| 43 |
+
_GO_LC_SUPPORTED_PYTORCH_LAYERS = (
|
| 44 |
+
torch.nn.Conv1d,
|
| 45 |
+
torch.nn.Conv2d,
|
| 46 |
+
torch.nn.Conv3d,
|
| 47 |
+
torch.nn.ConvTranspose1d,
|
| 48 |
+
torch.nn.ConvTranspose2d,
|
| 49 |
+
torch.nn.ConvTranspose3d,
|
| 50 |
+
torch.nn.Linear,
|
| 51 |
+
torch.nn.Embedding,
|
| 52 |
+
# TODO(aryan): look into torch.nn.LayerNorm, torch.nn.GroupNorm later, seems to be causing some issues with CogVideoX
|
| 53 |
+
# because of double invocation of the same norm layer in CogVideoXLayerNorm
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def _get_submodule_from_fqn(module: torch.nn.Module, fqn: str) -> torch.nn.Module | None:
|
| 58 |
+
for submodule_name, submodule in module.named_modules():
|
| 59 |
+
if submodule_name == fqn:
|
| 60 |
+
return submodule
|
| 61 |
+
return None
|
third_party/diffusers/src/diffusers/hooks/_helpers.py
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
from typing import Any, Callable, Type
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
@dataclass
|
| 21 |
+
class AttentionProcessorMetadata:
|
| 22 |
+
skip_processor_output_fn: Callable[[Any], Any]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class TransformerBlockMetadata:
|
| 27 |
+
return_hidden_states_index: int = None
|
| 28 |
+
return_encoder_hidden_states_index: int = None
|
| 29 |
+
hidden_states_argument_name: str = "hidden_states"
|
| 30 |
+
|
| 31 |
+
_cls: Type = None
|
| 32 |
+
_cached_parameter_indices: dict[str, int] = None
|
| 33 |
+
|
| 34 |
+
def _get_parameter_from_args_kwargs(self, identifier: str, args=(), kwargs=None):
|
| 35 |
+
kwargs = kwargs or {}
|
| 36 |
+
if identifier in kwargs:
|
| 37 |
+
return kwargs[identifier]
|
| 38 |
+
if self._cached_parameter_indices is not None:
|
| 39 |
+
return args[self._cached_parameter_indices[identifier]]
|
| 40 |
+
if self._cls is None:
|
| 41 |
+
raise ValueError("Model class is not set for metadata.")
|
| 42 |
+
parameters = list(inspect.signature(self._cls.forward).parameters.keys())
|
| 43 |
+
parameters = parameters[1:] # skip `self`
|
| 44 |
+
self._cached_parameter_indices = {param: i for i, param in enumerate(parameters)}
|
| 45 |
+
if identifier not in self._cached_parameter_indices:
|
| 46 |
+
raise ValueError(f"Parameter '{identifier}' not found in function signature but was requested.")
|
| 47 |
+
index = self._cached_parameter_indices[identifier]
|
| 48 |
+
if index >= len(args):
|
| 49 |
+
raise ValueError(f"Expected {index} arguments but got {len(args)}.")
|
| 50 |
+
return args[index]
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class AttentionProcessorRegistry:
|
| 54 |
+
_registry = {}
|
| 55 |
+
# TODO(aryan): this is only required for the time being because we need to do the registrations
|
| 56 |
+
# for classes. If we do it eagerly, i.e. call the functions in global scope, we will get circular
|
| 57 |
+
# import errors because of the models imported in this file.
|
| 58 |
+
_is_registered = False
|
| 59 |
+
|
| 60 |
+
@classmethod
|
| 61 |
+
def register(cls, model_class: Type, metadata: AttentionProcessorMetadata):
|
| 62 |
+
cls._register()
|
| 63 |
+
cls._registry[model_class] = metadata
|
| 64 |
+
|
| 65 |
+
@classmethod
|
| 66 |
+
def get(cls, model_class: Type) -> AttentionProcessorMetadata:
|
| 67 |
+
cls._register()
|
| 68 |
+
if model_class not in cls._registry:
|
| 69 |
+
raise ValueError(f"Model class {model_class} not registered.")
|
| 70 |
+
return cls._registry[model_class]
|
| 71 |
+
|
| 72 |
+
@classmethod
|
| 73 |
+
def _register(cls):
|
| 74 |
+
if cls._is_registered:
|
| 75 |
+
return
|
| 76 |
+
cls._is_registered = True
|
| 77 |
+
_register_attention_processors_metadata()
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class TransformerBlockRegistry:
|
| 81 |
+
_registry = {}
|
| 82 |
+
# TODO(aryan): this is only required for the time being because we need to do the registrations
|
| 83 |
+
# for classes. If we do it eagerly, i.e. call the functions in global scope, we will get circular
|
| 84 |
+
# import errors because of the models imported in this file.
|
| 85 |
+
_is_registered = False
|
| 86 |
+
|
| 87 |
+
@classmethod
|
| 88 |
+
def register(cls, model_class: Type, metadata: TransformerBlockMetadata):
|
| 89 |
+
cls._register()
|
| 90 |
+
metadata._cls = model_class
|
| 91 |
+
cls._registry[model_class] = metadata
|
| 92 |
+
|
| 93 |
+
@classmethod
|
| 94 |
+
def get(cls, model_class: Type) -> TransformerBlockMetadata:
|
| 95 |
+
cls._register()
|
| 96 |
+
if model_class not in cls._registry:
|
| 97 |
+
raise ValueError(f"Model class {model_class} not registered.")
|
| 98 |
+
return cls._registry[model_class]
|
| 99 |
+
|
| 100 |
+
@classmethod
|
| 101 |
+
def _register(cls):
|
| 102 |
+
if cls._is_registered:
|
| 103 |
+
return
|
| 104 |
+
cls._is_registered = True
|
| 105 |
+
_register_transformer_blocks_metadata()
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def _register_attention_processors_metadata():
|
| 109 |
+
from ..models.attention_processor import AttnProcessor2_0
|
| 110 |
+
from ..models.transformers.transformer_cogview4 import CogView4AttnProcessor
|
| 111 |
+
from ..models.transformers.transformer_flux import FluxAttnProcessor
|
| 112 |
+
from ..models.transformers.transformer_hunyuanimage import HunyuanImageAttnProcessor
|
| 113 |
+
from ..models.transformers.transformer_qwenimage import QwenDoubleStreamAttnProcessor2_0
|
| 114 |
+
from ..models.transformers.transformer_wan import WanAttnProcessor2_0
|
| 115 |
+
from ..models.transformers.transformer_z_image import ZSingleStreamAttnProcessor
|
| 116 |
+
|
| 117 |
+
# AttnProcessor2_0
|
| 118 |
+
AttentionProcessorRegistry.register(
|
| 119 |
+
model_class=AttnProcessor2_0,
|
| 120 |
+
metadata=AttentionProcessorMetadata(
|
| 121 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_AttnProcessor2_0,
|
| 122 |
+
),
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
# CogView4AttnProcessor
|
| 126 |
+
AttentionProcessorRegistry.register(
|
| 127 |
+
model_class=CogView4AttnProcessor,
|
| 128 |
+
metadata=AttentionProcessorMetadata(
|
| 129 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_CogView4AttnProcessor,
|
| 130 |
+
),
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
# WanAttnProcessor2_0
|
| 134 |
+
AttentionProcessorRegistry.register(
|
| 135 |
+
model_class=WanAttnProcessor2_0,
|
| 136 |
+
metadata=AttentionProcessorMetadata(
|
| 137 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_WanAttnProcessor2_0,
|
| 138 |
+
),
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# FluxAttnProcessor
|
| 142 |
+
AttentionProcessorRegistry.register(
|
| 143 |
+
model_class=FluxAttnProcessor,
|
| 144 |
+
metadata=AttentionProcessorMetadata(skip_processor_output_fn=_skip_proc_output_fn_Attention_FluxAttnProcessor),
|
| 145 |
+
)
|
| 146 |
+
|
| 147 |
+
# QwenDoubleStreamAttnProcessor2
|
| 148 |
+
AttentionProcessorRegistry.register(
|
| 149 |
+
model_class=QwenDoubleStreamAttnProcessor2_0,
|
| 150 |
+
metadata=AttentionProcessorMetadata(
|
| 151 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_QwenDoubleStreamAttnProcessor2_0
|
| 152 |
+
),
|
| 153 |
+
)
|
| 154 |
+
|
| 155 |
+
# HunyuanImageAttnProcessor
|
| 156 |
+
AttentionProcessorRegistry.register(
|
| 157 |
+
model_class=HunyuanImageAttnProcessor,
|
| 158 |
+
metadata=AttentionProcessorMetadata(
|
| 159 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_HunyuanImageAttnProcessor,
|
| 160 |
+
),
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
# ZSingleStreamAttnProcessor
|
| 164 |
+
AttentionProcessorRegistry.register(
|
| 165 |
+
model_class=ZSingleStreamAttnProcessor,
|
| 166 |
+
metadata=AttentionProcessorMetadata(
|
| 167 |
+
skip_processor_output_fn=_skip_proc_output_fn_Attention_ZSingleStreamAttnProcessor,
|
| 168 |
+
),
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def _register_transformer_blocks_metadata():
|
| 173 |
+
from ..models.attention import BasicTransformerBlock, JointTransformerBlock
|
| 174 |
+
from ..models.transformers.cogvideox_transformer_3d import CogVideoXBlock
|
| 175 |
+
from ..models.transformers.transformer_bria import BriaTransformerBlock
|
| 176 |
+
from ..models.transformers.transformer_cogview4 import CogView4TransformerBlock
|
| 177 |
+
from ..models.transformers.transformer_flux import FluxSingleTransformerBlock, FluxTransformerBlock
|
| 178 |
+
from ..models.transformers.transformer_hunyuan_video import (
|
| 179 |
+
HunyuanVideoSingleTransformerBlock,
|
| 180 |
+
HunyuanVideoTokenReplaceSingleTransformerBlock,
|
| 181 |
+
HunyuanVideoTokenReplaceTransformerBlock,
|
| 182 |
+
HunyuanVideoTransformerBlock,
|
| 183 |
+
)
|
| 184 |
+
from ..models.transformers.transformer_hunyuanimage import (
|
| 185 |
+
HunyuanImageSingleTransformerBlock,
|
| 186 |
+
HunyuanImageTransformerBlock,
|
| 187 |
+
)
|
| 188 |
+
from ..models.transformers.transformer_kandinsky import Kandinsky5TransformerDecoderBlock
|
| 189 |
+
from ..models.transformers.transformer_ltx import LTXVideoTransformerBlock
|
| 190 |
+
from ..models.transformers.transformer_mochi import MochiTransformerBlock
|
| 191 |
+
from ..models.transformers.transformer_qwenimage import QwenImageTransformerBlock
|
| 192 |
+
from ..models.transformers.transformer_wan import WanTransformerBlock
|
| 193 |
+
from ..models.transformers.transformer_z_image import ZImageTransformerBlock
|
| 194 |
+
|
| 195 |
+
# BasicTransformerBlock
|
| 196 |
+
TransformerBlockRegistry.register(
|
| 197 |
+
model_class=BasicTransformerBlock,
|
| 198 |
+
metadata=TransformerBlockMetadata(
|
| 199 |
+
return_hidden_states_index=0,
|
| 200 |
+
return_encoder_hidden_states_index=None,
|
| 201 |
+
),
|
| 202 |
+
)
|
| 203 |
+
TransformerBlockRegistry.register(
|
| 204 |
+
model_class=BriaTransformerBlock,
|
| 205 |
+
metadata=TransformerBlockMetadata(
|
| 206 |
+
return_hidden_states_index=0,
|
| 207 |
+
return_encoder_hidden_states_index=None,
|
| 208 |
+
),
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
# CogVideoX
|
| 212 |
+
TransformerBlockRegistry.register(
|
| 213 |
+
model_class=CogVideoXBlock,
|
| 214 |
+
metadata=TransformerBlockMetadata(
|
| 215 |
+
return_hidden_states_index=0,
|
| 216 |
+
return_encoder_hidden_states_index=1,
|
| 217 |
+
),
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
# CogView4
|
| 221 |
+
TransformerBlockRegistry.register(
|
| 222 |
+
model_class=CogView4TransformerBlock,
|
| 223 |
+
metadata=TransformerBlockMetadata(
|
| 224 |
+
return_hidden_states_index=0,
|
| 225 |
+
return_encoder_hidden_states_index=1,
|
| 226 |
+
),
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
# Flux
|
| 230 |
+
TransformerBlockRegistry.register(
|
| 231 |
+
model_class=FluxTransformerBlock,
|
| 232 |
+
metadata=TransformerBlockMetadata(
|
| 233 |
+
return_hidden_states_index=1,
|
| 234 |
+
return_encoder_hidden_states_index=0,
|
| 235 |
+
),
|
| 236 |
+
)
|
| 237 |
+
TransformerBlockRegistry.register(
|
| 238 |
+
model_class=FluxSingleTransformerBlock,
|
| 239 |
+
metadata=TransformerBlockMetadata(
|
| 240 |
+
return_hidden_states_index=1,
|
| 241 |
+
return_encoder_hidden_states_index=0,
|
| 242 |
+
),
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
# HunyuanVideo
|
| 246 |
+
TransformerBlockRegistry.register(
|
| 247 |
+
model_class=HunyuanVideoTransformerBlock,
|
| 248 |
+
metadata=TransformerBlockMetadata(
|
| 249 |
+
return_hidden_states_index=0,
|
| 250 |
+
return_encoder_hidden_states_index=1,
|
| 251 |
+
),
|
| 252 |
+
)
|
| 253 |
+
TransformerBlockRegistry.register(
|
| 254 |
+
model_class=HunyuanVideoSingleTransformerBlock,
|
| 255 |
+
metadata=TransformerBlockMetadata(
|
| 256 |
+
return_hidden_states_index=0,
|
| 257 |
+
return_encoder_hidden_states_index=1,
|
| 258 |
+
),
|
| 259 |
+
)
|
| 260 |
+
TransformerBlockRegistry.register(
|
| 261 |
+
model_class=HunyuanVideoTokenReplaceTransformerBlock,
|
| 262 |
+
metadata=TransformerBlockMetadata(
|
| 263 |
+
return_hidden_states_index=0,
|
| 264 |
+
return_encoder_hidden_states_index=1,
|
| 265 |
+
),
|
| 266 |
+
)
|
| 267 |
+
TransformerBlockRegistry.register(
|
| 268 |
+
model_class=HunyuanVideoTokenReplaceSingleTransformerBlock,
|
| 269 |
+
metadata=TransformerBlockMetadata(
|
| 270 |
+
return_hidden_states_index=0,
|
| 271 |
+
return_encoder_hidden_states_index=1,
|
| 272 |
+
),
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
# LTXVideo
|
| 276 |
+
TransformerBlockRegistry.register(
|
| 277 |
+
model_class=LTXVideoTransformerBlock,
|
| 278 |
+
metadata=TransformerBlockMetadata(
|
| 279 |
+
return_hidden_states_index=0,
|
| 280 |
+
return_encoder_hidden_states_index=None,
|
| 281 |
+
),
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
# Mochi
|
| 285 |
+
TransformerBlockRegistry.register(
|
| 286 |
+
model_class=MochiTransformerBlock,
|
| 287 |
+
metadata=TransformerBlockMetadata(
|
| 288 |
+
return_hidden_states_index=0,
|
| 289 |
+
return_encoder_hidden_states_index=1,
|
| 290 |
+
),
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
# Wan
|
| 294 |
+
TransformerBlockRegistry.register(
|
| 295 |
+
model_class=WanTransformerBlock,
|
| 296 |
+
metadata=TransformerBlockMetadata(
|
| 297 |
+
return_hidden_states_index=0,
|
| 298 |
+
return_encoder_hidden_states_index=None,
|
| 299 |
+
),
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
# QwenImage
|
| 303 |
+
TransformerBlockRegistry.register(
|
| 304 |
+
model_class=QwenImageTransformerBlock,
|
| 305 |
+
metadata=TransformerBlockMetadata(
|
| 306 |
+
return_hidden_states_index=1,
|
| 307 |
+
return_encoder_hidden_states_index=0,
|
| 308 |
+
),
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
# HunyuanImage2.1
|
| 312 |
+
TransformerBlockRegistry.register(
|
| 313 |
+
model_class=HunyuanImageTransformerBlock,
|
| 314 |
+
metadata=TransformerBlockMetadata(
|
| 315 |
+
return_hidden_states_index=0,
|
| 316 |
+
return_encoder_hidden_states_index=1,
|
| 317 |
+
),
|
| 318 |
+
)
|
| 319 |
+
TransformerBlockRegistry.register(
|
| 320 |
+
model_class=HunyuanImageSingleTransformerBlock,
|
| 321 |
+
metadata=TransformerBlockMetadata(
|
| 322 |
+
return_hidden_states_index=0,
|
| 323 |
+
return_encoder_hidden_states_index=1,
|
| 324 |
+
),
|
| 325 |
+
)
|
| 326 |
+
|
| 327 |
+
# ZImage
|
| 328 |
+
TransformerBlockRegistry.register(
|
| 329 |
+
model_class=ZImageTransformerBlock,
|
| 330 |
+
metadata=TransformerBlockMetadata(
|
| 331 |
+
return_hidden_states_index=0,
|
| 332 |
+
return_encoder_hidden_states_index=None,
|
| 333 |
+
),
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
TransformerBlockRegistry.register(
|
| 337 |
+
model_class=JointTransformerBlock,
|
| 338 |
+
metadata=TransformerBlockMetadata(
|
| 339 |
+
return_hidden_states_index=1,
|
| 340 |
+
return_encoder_hidden_states_index=0,
|
| 341 |
+
),
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
# Kandinsky 5.0 (Kandinsky5TransformerDecoderBlock)
|
| 345 |
+
TransformerBlockRegistry.register(
|
| 346 |
+
model_class=Kandinsky5TransformerDecoderBlock,
|
| 347 |
+
metadata=TransformerBlockMetadata(
|
| 348 |
+
return_hidden_states_index=0,
|
| 349 |
+
return_encoder_hidden_states_index=None,
|
| 350 |
+
hidden_states_argument_name="visual_embed",
|
| 351 |
+
),
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
# fmt: off
|
| 356 |
+
def _skip_attention___ret___hidden_states(self, *args, **kwargs):
|
| 357 |
+
hidden_states = kwargs.get("hidden_states", None)
|
| 358 |
+
if hidden_states is None and len(args) > 0:
|
| 359 |
+
hidden_states = args[0]
|
| 360 |
+
return hidden_states
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def _skip_attention___ret___hidden_states___encoder_hidden_states(self, *args, **kwargs):
|
| 364 |
+
hidden_states = kwargs.get("hidden_states", None)
|
| 365 |
+
encoder_hidden_states = kwargs.get("encoder_hidden_states", None)
|
| 366 |
+
if hidden_states is None and len(args) > 0:
|
| 367 |
+
hidden_states = args[0]
|
| 368 |
+
if encoder_hidden_states is None and len(args) > 1:
|
| 369 |
+
encoder_hidden_states = args[1]
|
| 370 |
+
return hidden_states, encoder_hidden_states
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
_skip_proc_output_fn_Attention_AttnProcessor2_0 = _skip_attention___ret___hidden_states
|
| 374 |
+
_skip_proc_output_fn_Attention_CogView4AttnProcessor = _skip_attention___ret___hidden_states___encoder_hidden_states
|
| 375 |
+
_skip_proc_output_fn_Attention_WanAttnProcessor2_0 = _skip_attention___ret___hidden_states
|
| 376 |
+
# not sure what this is yet.
|
| 377 |
+
_skip_proc_output_fn_Attention_FluxAttnProcessor = _skip_attention___ret___hidden_states
|
| 378 |
+
_skip_proc_output_fn_Attention_QwenDoubleStreamAttnProcessor2_0 = _skip_attention___ret___hidden_states
|
| 379 |
+
_skip_proc_output_fn_Attention_HunyuanImageAttnProcessor = _skip_attention___ret___hidden_states
|
| 380 |
+
_skip_proc_output_fn_Attention_ZSingleStreamAttnProcessor = _skip_attention___ret___hidden_states
|
| 381 |
+
# fmt: on
|
third_party/diffusers/src/diffusers/hooks/context_parallel.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 |
+
import copy
|
| 15 |
+
import inspect
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
from typing import Type
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import torch.distributed as dist
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
if torch.distributed.is_available():
|
| 24 |
+
import torch.distributed._functional_collectives as funcol
|
| 25 |
+
|
| 26 |
+
from ..models._modeling_parallel import (
|
| 27 |
+
ContextParallelConfig,
|
| 28 |
+
ContextParallelInput,
|
| 29 |
+
ContextParallelModelPlan,
|
| 30 |
+
ContextParallelOutput,
|
| 31 |
+
gather_size_by_comm,
|
| 32 |
+
)
|
| 33 |
+
from ..utils import get_logger
|
| 34 |
+
from ..utils.torch_utils import lru_cache_unless_export, maybe_allow_in_graph, unwrap_module
|
| 35 |
+
from .hooks import HookRegistry, ModelHook
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 39 |
+
|
| 40 |
+
_CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE = "cp_input---{}"
|
| 41 |
+
_CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE = "cp_output---{}"
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# TODO(aryan): consolidate with ._helpers.TransformerBlockMetadata
|
| 45 |
+
@dataclass
|
| 46 |
+
class ModuleForwardMetadata:
|
| 47 |
+
cached_parameter_indices: dict[str, int] = None
|
| 48 |
+
_cls: Type = None
|
| 49 |
+
|
| 50 |
+
def _get_parameter_from_args_kwargs(self, identifier: str, args=(), kwargs=None):
|
| 51 |
+
kwargs = kwargs or {}
|
| 52 |
+
|
| 53 |
+
if identifier in kwargs:
|
| 54 |
+
return kwargs[identifier], True, None
|
| 55 |
+
|
| 56 |
+
if self.cached_parameter_indices is not None:
|
| 57 |
+
index = self.cached_parameter_indices.get(identifier, None)
|
| 58 |
+
if index is None:
|
| 59 |
+
raise ValueError(f"Parameter '{identifier}' not found in cached indices.")
|
| 60 |
+
return args[index], False, index
|
| 61 |
+
|
| 62 |
+
if self._cls is None:
|
| 63 |
+
raise ValueError("Model class is not set for metadata.")
|
| 64 |
+
|
| 65 |
+
parameters = list(inspect.signature(self._cls.forward).parameters.keys())
|
| 66 |
+
parameters = parameters[1:] # skip `self`
|
| 67 |
+
self.cached_parameter_indices = {param: i for i, param in enumerate(parameters)}
|
| 68 |
+
|
| 69 |
+
if identifier not in self.cached_parameter_indices:
|
| 70 |
+
raise ValueError(f"Parameter '{identifier}' not found in function signature but was requested.")
|
| 71 |
+
|
| 72 |
+
index = self.cached_parameter_indices[identifier]
|
| 73 |
+
|
| 74 |
+
if index >= len(args):
|
| 75 |
+
raise ValueError(f"Expected {index} arguments but got {len(args)}.")
|
| 76 |
+
|
| 77 |
+
return args[index], False, index
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def apply_context_parallel(
|
| 81 |
+
module: torch.nn.Module,
|
| 82 |
+
parallel_config: ContextParallelConfig,
|
| 83 |
+
plan: dict[str, ContextParallelModelPlan],
|
| 84 |
+
) -> None:
|
| 85 |
+
"""Apply context parallel on a model."""
|
| 86 |
+
logger.debug(f"Applying context parallel with CP mesh: {parallel_config._mesh} and plan: {plan}")
|
| 87 |
+
|
| 88 |
+
for module_id, cp_model_plan in plan.items():
|
| 89 |
+
submodule = _get_submodule_by_name(module, module_id)
|
| 90 |
+
if not isinstance(submodule, list):
|
| 91 |
+
submodule = [submodule]
|
| 92 |
+
|
| 93 |
+
logger.debug(f"Applying ContextParallelHook to {module_id=} identifying a total of {len(submodule)} modules")
|
| 94 |
+
|
| 95 |
+
for m in submodule:
|
| 96 |
+
if isinstance(cp_model_plan, dict):
|
| 97 |
+
hook = ContextParallelSplitHook(cp_model_plan, parallel_config)
|
| 98 |
+
hook_name = _CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE.format(module_id)
|
| 99 |
+
elif isinstance(cp_model_plan, (ContextParallelOutput, list, tuple)):
|
| 100 |
+
if isinstance(cp_model_plan, ContextParallelOutput):
|
| 101 |
+
cp_model_plan = [cp_model_plan]
|
| 102 |
+
if not all(isinstance(x, ContextParallelOutput) for x in cp_model_plan):
|
| 103 |
+
raise ValueError(f"Expected all elements of cp_model_plan to be CPOutput, but got {cp_model_plan}")
|
| 104 |
+
hook = ContextParallelGatherHook(cp_model_plan, parallel_config)
|
| 105 |
+
hook_name = _CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE.format(module_id)
|
| 106 |
+
else:
|
| 107 |
+
raise ValueError(f"Unsupported context parallel model plan type: {type(cp_model_plan)}")
|
| 108 |
+
registry = HookRegistry.check_if_exists_or_initialize(m)
|
| 109 |
+
registry.register_hook(hook, hook_name)
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def remove_context_parallel(module: torch.nn.Module, plan: dict[str, ContextParallelModelPlan]) -> None:
|
| 113 |
+
for module_id, cp_model_plan in plan.items():
|
| 114 |
+
submodule = _get_submodule_by_name(module, module_id)
|
| 115 |
+
if not isinstance(submodule, list):
|
| 116 |
+
submodule = [submodule]
|
| 117 |
+
|
| 118 |
+
for m in submodule:
|
| 119 |
+
registry = HookRegistry.check_if_exists_or_initialize(m)
|
| 120 |
+
if isinstance(cp_model_plan, dict):
|
| 121 |
+
hook_name = _CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE.format(module_id)
|
| 122 |
+
elif isinstance(cp_model_plan, (ContextParallelOutput, list, tuple)):
|
| 123 |
+
hook_name = _CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE.format(module_id)
|
| 124 |
+
else:
|
| 125 |
+
raise ValueError(f"Unsupported context parallel model plan type: {type(cp_model_plan)}")
|
| 126 |
+
registry.remove_hook(hook_name)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class ContextParallelSplitHook(ModelHook):
|
| 130 |
+
def __init__(self, metadata: ContextParallelModelPlan, parallel_config: ContextParallelConfig) -> None:
|
| 131 |
+
super().__init__()
|
| 132 |
+
self.metadata = metadata
|
| 133 |
+
self.parallel_config = parallel_config
|
| 134 |
+
self.module_forward_metadata = None
|
| 135 |
+
|
| 136 |
+
def initialize_hook(self, module):
|
| 137 |
+
cls = unwrap_module(module).__class__
|
| 138 |
+
self.module_forward_metadata = ModuleForwardMetadata(_cls=cls)
|
| 139 |
+
return module
|
| 140 |
+
|
| 141 |
+
def pre_forward(self, module, *args, **kwargs):
|
| 142 |
+
args_list = list(args)
|
| 143 |
+
|
| 144 |
+
for name, cpm in self.metadata.items():
|
| 145 |
+
if isinstance(cpm, ContextParallelInput) and cpm.split_output:
|
| 146 |
+
continue
|
| 147 |
+
|
| 148 |
+
# Maybe the parameter was passed as a keyword argument
|
| 149 |
+
input_val, is_kwarg, index = self.module_forward_metadata._get_parameter_from_args_kwargs(
|
| 150 |
+
name, args_list, kwargs
|
| 151 |
+
)
|
| 152 |
+
|
| 153 |
+
if input_val is None:
|
| 154 |
+
continue
|
| 155 |
+
|
| 156 |
+
# The input_val may be a tensor or list/tuple of tensors. In certain cases, user may specify to shard
|
| 157 |
+
# the output instead of input for a particular layer by setting split_output=True
|
| 158 |
+
if isinstance(input_val, torch.Tensor):
|
| 159 |
+
input_val = self._prepare_cp_input(input_val, cpm)
|
| 160 |
+
elif isinstance(input_val, (list, tuple)):
|
| 161 |
+
if len(input_val) != len(cpm):
|
| 162 |
+
raise ValueError(
|
| 163 |
+
f"Expected input model plan to have {len(input_val)} elements, but got {len(cpm)}."
|
| 164 |
+
)
|
| 165 |
+
sharded_input_val = []
|
| 166 |
+
for i, x in enumerate(input_val):
|
| 167 |
+
if torch.is_tensor(x) and not cpm[i].split_output:
|
| 168 |
+
x = self._prepare_cp_input(x, cpm[i])
|
| 169 |
+
sharded_input_val.append(x)
|
| 170 |
+
input_val = sharded_input_val
|
| 171 |
+
else:
|
| 172 |
+
raise ValueError(f"Unsupported input type: {type(input_val)}")
|
| 173 |
+
|
| 174 |
+
if is_kwarg:
|
| 175 |
+
kwargs[name] = input_val
|
| 176 |
+
elif index is not None and index < len(args_list):
|
| 177 |
+
args_list[index] = input_val
|
| 178 |
+
else:
|
| 179 |
+
raise ValueError(
|
| 180 |
+
f"An unexpected error occurred while processing the input '{name}'. Please open an "
|
| 181 |
+
f"issue at https://github.com/huggingface/diffusers/issues and provide a minimal reproducible "
|
| 182 |
+
f"example along with the full stack trace."
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
return tuple(args_list), kwargs
|
| 186 |
+
|
| 187 |
+
def post_forward(self, module, output):
|
| 188 |
+
is_tensor = isinstance(output, torch.Tensor)
|
| 189 |
+
is_tensor_list = isinstance(output, (list, tuple)) and all(isinstance(x, torch.Tensor) for x in output)
|
| 190 |
+
|
| 191 |
+
if not is_tensor and not is_tensor_list:
|
| 192 |
+
raise ValueError(f"Expected output to be a tensor or a list/tuple of tensors, but got {type(output)}.")
|
| 193 |
+
|
| 194 |
+
output = [output] if is_tensor else list(output)
|
| 195 |
+
for index, cpm in self.metadata.items():
|
| 196 |
+
if not isinstance(cpm, ContextParallelInput) or not cpm.split_output:
|
| 197 |
+
continue
|
| 198 |
+
if index >= len(output):
|
| 199 |
+
raise ValueError(f"Index {index} out of bounds for output of length {len(output)}.")
|
| 200 |
+
current_output = output[index]
|
| 201 |
+
current_output = self._prepare_cp_input(current_output, cpm)
|
| 202 |
+
output[index] = current_output
|
| 203 |
+
|
| 204 |
+
return output[0] if is_tensor else tuple(output)
|
| 205 |
+
|
| 206 |
+
def _prepare_cp_input(self, x: torch.Tensor, cp_input: ContextParallelInput) -> torch.Tensor:
|
| 207 |
+
if cp_input.expected_dims is not None and x.dim() != cp_input.expected_dims:
|
| 208 |
+
logger.warning_once(
|
| 209 |
+
f"Expected input tensor to have {cp_input.expected_dims} dimensions, but got {x.dim()} dimensions, split will not be applied."
|
| 210 |
+
)
|
| 211 |
+
return x
|
| 212 |
+
else:
|
| 213 |
+
if self.parallel_config.ulysses_anything:
|
| 214 |
+
return PartitionAnythingSharder.shard_anything(
|
| 215 |
+
x, cp_input.split_dim, self.parallel_config._flattened_mesh
|
| 216 |
+
)
|
| 217 |
+
return EquipartitionSharder.shard(x, cp_input.split_dim, self.parallel_config._flattened_mesh)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class ContextParallelGatherHook(ModelHook):
|
| 221 |
+
def __init__(self, metadata: ContextParallelModelPlan, parallel_config: ContextParallelConfig) -> None:
|
| 222 |
+
super().__init__()
|
| 223 |
+
self.metadata = metadata
|
| 224 |
+
self.parallel_config = parallel_config
|
| 225 |
+
|
| 226 |
+
def post_forward(self, module, output):
|
| 227 |
+
is_tensor = isinstance(output, torch.Tensor)
|
| 228 |
+
|
| 229 |
+
if is_tensor:
|
| 230 |
+
output = [output]
|
| 231 |
+
elif not (isinstance(output, (list, tuple)) and all(isinstance(x, torch.Tensor) for x in output)):
|
| 232 |
+
raise ValueError(f"Expected output to be a tensor or a list/tuple of tensors, but got {type(output)}.")
|
| 233 |
+
|
| 234 |
+
output = list(output)
|
| 235 |
+
|
| 236 |
+
if len(output) != len(self.metadata):
|
| 237 |
+
raise ValueError(f"Expected output to have {len(self.metadata)} elements, but got {len(output)}.")
|
| 238 |
+
|
| 239 |
+
for i, cpm in enumerate(self.metadata):
|
| 240 |
+
if cpm is None:
|
| 241 |
+
continue
|
| 242 |
+
if self.parallel_config.ulysses_anything:
|
| 243 |
+
output[i] = PartitionAnythingSharder.unshard_anything(
|
| 244 |
+
output[i], cpm.gather_dim, self.parallel_config._flattened_mesh
|
| 245 |
+
)
|
| 246 |
+
else:
|
| 247 |
+
output[i] = EquipartitionSharder.unshard(
|
| 248 |
+
output[i], cpm.gather_dim, self.parallel_config._flattened_mesh
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
return output[0] if is_tensor else tuple(output)
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
class AllGatherFunction(torch.autograd.Function):
|
| 255 |
+
@staticmethod
|
| 256 |
+
def forward(ctx, tensor, dim, group):
|
| 257 |
+
ctx.dim = dim
|
| 258 |
+
ctx.group = group
|
| 259 |
+
ctx.world_size = torch.distributed.get_world_size(group)
|
| 260 |
+
ctx.rank = torch.distributed.get_rank(group)
|
| 261 |
+
return funcol.all_gather_tensor(tensor, dim, group=group)
|
| 262 |
+
|
| 263 |
+
@staticmethod
|
| 264 |
+
def backward(ctx, grad_output):
|
| 265 |
+
grad_chunks = torch.chunk(grad_output, ctx.world_size, dim=ctx.dim)
|
| 266 |
+
return grad_chunks[ctx.rank], None, None
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
class EquipartitionSharder:
|
| 270 |
+
@classmethod
|
| 271 |
+
def shard(cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh) -> torch.Tensor:
|
| 272 |
+
# NOTE: the following assertion does not have to be true in general. We simply enforce it for now
|
| 273 |
+
# because the alternate case has not yet been tested/required for any model.
|
| 274 |
+
assert tensor.size()[dim] % mesh.size() == 0, (
|
| 275 |
+
"Tensor size along dimension to be sharded must be divisible by mesh size"
|
| 276 |
+
)
|
| 277 |
+
|
| 278 |
+
# The following is not fullgraph compatible with Dynamo (fails in DeviceMesh.get_rank)
|
| 279 |
+
# return tensor.chunk(mesh.size(), dim=dim)[mesh.get_rank()]
|
| 280 |
+
|
| 281 |
+
return tensor.chunk(mesh.size(), dim=dim)[torch.distributed.get_rank(mesh.get_group())]
|
| 282 |
+
|
| 283 |
+
@classmethod
|
| 284 |
+
def unshard(cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh) -> torch.Tensor:
|
| 285 |
+
tensor = tensor.contiguous()
|
| 286 |
+
tensor = AllGatherFunction.apply(tensor, dim, mesh.get_group())
|
| 287 |
+
return tensor
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class AllGatherAnythingFunction(torch.autograd.Function):
|
| 291 |
+
@staticmethod
|
| 292 |
+
def forward(ctx, tensor: torch.Tensor, dim: int, group: dist.device_mesh.DeviceMesh):
|
| 293 |
+
ctx.dim = dim
|
| 294 |
+
ctx.group = group
|
| 295 |
+
ctx.world_size = dist.get_world_size(group)
|
| 296 |
+
ctx.rank = dist.get_rank(group)
|
| 297 |
+
gathered_tensor = _all_gather_anything(tensor, dim, group)
|
| 298 |
+
return gathered_tensor
|
| 299 |
+
|
| 300 |
+
@staticmethod
|
| 301 |
+
def backward(ctx, grad_output):
|
| 302 |
+
# NOTE: We use `tensor_split` instead of chunk, because the `chunk`
|
| 303 |
+
# function may return fewer than the specified number of chunks!
|
| 304 |
+
grad_splits = torch.tensor_split(grad_output, ctx.world_size, dim=ctx.dim)
|
| 305 |
+
return grad_splits[ctx.rank], None, None
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
class PartitionAnythingSharder:
|
| 309 |
+
@classmethod
|
| 310 |
+
def shard_anything(
|
| 311 |
+
cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh
|
| 312 |
+
) -> torch.Tensor:
|
| 313 |
+
assert tensor.size()[dim] >= mesh.size(), (
|
| 314 |
+
f"Cannot shard tensor of size {tensor.size()} along dim {dim} across mesh of size {mesh.size()}."
|
| 315 |
+
)
|
| 316 |
+
# NOTE: We use `tensor_split` instead of chunk, because the `chunk`
|
| 317 |
+
# function may return fewer than the specified number of chunks!
|
| 318 |
+
return tensor.tensor_split(mesh.size(), dim=dim)[dist.get_rank(mesh.get_group())]
|
| 319 |
+
|
| 320 |
+
@classmethod
|
| 321 |
+
def unshard_anything(
|
| 322 |
+
cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh
|
| 323 |
+
) -> torch.Tensor:
|
| 324 |
+
tensor = tensor.contiguous()
|
| 325 |
+
tensor = AllGatherAnythingFunction.apply(tensor, dim, mesh.get_group())
|
| 326 |
+
return tensor
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
@lru_cache_unless_export(maxsize=64)
|
| 330 |
+
def _fill_gather_shapes(shape: tuple[int], gather_dims: tuple[int], dim: int, world_size: int) -> list[list[int]]:
|
| 331 |
+
gather_shapes = []
|
| 332 |
+
for i in range(world_size):
|
| 333 |
+
rank_shape = list(copy.deepcopy(shape))
|
| 334 |
+
rank_shape[dim] = gather_dims[i]
|
| 335 |
+
gather_shapes.append(rank_shape)
|
| 336 |
+
return gather_shapes
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
@maybe_allow_in_graph
|
| 340 |
+
def _all_gather_anything(tensor: torch.Tensor, dim: int, group: dist.device_mesh.DeviceMesh) -> torch.Tensor:
|
| 341 |
+
world_size = dist.get_world_size(group=group)
|
| 342 |
+
|
| 343 |
+
tensor = tensor.contiguous()
|
| 344 |
+
shape = tensor.shape
|
| 345 |
+
rank_dim = shape[dim]
|
| 346 |
+
gather_dims = gather_size_by_comm(rank_dim, group)
|
| 347 |
+
|
| 348 |
+
gather_shapes = _fill_gather_shapes(tuple(shape), tuple(gather_dims), dim, world_size)
|
| 349 |
+
|
| 350 |
+
gathered_tensors = [torch.empty(shape, device=tensor.device, dtype=tensor.dtype) for shape in gather_shapes]
|
| 351 |
+
|
| 352 |
+
dist.all_gather(gathered_tensors, tensor, group=group)
|
| 353 |
+
gathered_tensor = torch.cat(gathered_tensors, dim=dim)
|
| 354 |
+
return gathered_tensor
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
def _get_submodule_by_name(model: torch.nn.Module, name: str) -> torch.nn.Module | list[torch.nn.Module]:
|
| 358 |
+
if name.count("*") > 1:
|
| 359 |
+
raise ValueError("Wildcard '*' can only be used once in the name")
|
| 360 |
+
return _find_submodule_by_name(model, name)
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def _find_submodule_by_name(model: torch.nn.Module, name: str) -> torch.nn.Module | list[torch.nn.Module]:
|
| 364 |
+
if name == "":
|
| 365 |
+
return model
|
| 366 |
+
first_atom, remaining_name = name.split(".", 1) if "." in name else (name, "")
|
| 367 |
+
if first_atom == "*":
|
| 368 |
+
if not isinstance(model, torch.nn.ModuleList):
|
| 369 |
+
raise ValueError("Wildcard '*' can only be used with ModuleList")
|
| 370 |
+
submodules = []
|
| 371 |
+
for submodule in model:
|
| 372 |
+
subsubmodules = _find_submodule_by_name(submodule, remaining_name)
|
| 373 |
+
if not isinstance(subsubmodules, list):
|
| 374 |
+
subsubmodules = [subsubmodules]
|
| 375 |
+
submodules.extend(subsubmodules)
|
| 376 |
+
return submodules
|
| 377 |
+
else:
|
| 378 |
+
if hasattr(model, first_atom):
|
| 379 |
+
submodule = getattr(model, first_atom)
|
| 380 |
+
return _find_submodule_by_name(submodule, remaining_name)
|
| 381 |
+
else:
|
| 382 |
+
raise ValueError(f"'{first_atom}' is not a submodule of '{model.__class__.__name__}'")
|
third_party/diffusers/src/diffusers/hooks/faster_cache.py
ADDED
|
@@ -0,0 +1,654 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 re
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
from typing import Any, Callable
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from ..models.attention import AttentionModuleMixin
|
| 22 |
+
from ..models.modeling_outputs import Transformer2DModelOutput
|
| 23 |
+
from ..utils import logging
|
| 24 |
+
from ._common import _ATTENTION_CLASSES
|
| 25 |
+
from .hooks import HookRegistry, ModelHook
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
_FASTER_CACHE_DENOISER_HOOK = "faster_cache_denoiser"
|
| 32 |
+
_FASTER_CACHE_BLOCK_HOOK = "faster_cache_block"
|
| 33 |
+
_SPATIAL_ATTENTION_BLOCK_IDENTIFIERS = (
|
| 34 |
+
"^blocks.*attn",
|
| 35 |
+
"^transformer_blocks.*attn",
|
| 36 |
+
"^single_transformer_blocks.*attn",
|
| 37 |
+
)
|
| 38 |
+
_TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS = ("^temporal_transformer_blocks.*attn",)
|
| 39 |
+
_TRANSFORMER_BLOCK_IDENTIFIERS = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS + _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
|
| 40 |
+
_UNCOND_COND_INPUT_KWARGS_IDENTIFIERS = (
|
| 41 |
+
"hidden_states",
|
| 42 |
+
"encoder_hidden_states",
|
| 43 |
+
"timestep",
|
| 44 |
+
"attention_mask",
|
| 45 |
+
"encoder_attention_mask",
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
@dataclass
|
| 50 |
+
class FasterCacheConfig:
|
| 51 |
+
r"""
|
| 52 |
+
Configuration for [FasterCache](https://huggingface.co/papers/2410.19355).
|
| 53 |
+
|
| 54 |
+
Attributes:
|
| 55 |
+
spatial_attention_block_skip_range (`int`, defaults to `2`):
|
| 56 |
+
Calculate the attention states every `N` iterations. If this is set to `N`, the attention computation will
|
| 57 |
+
be skipped `N - 1` times (i.e., cached attention states will be reused) before computing the new attention
|
| 58 |
+
states again.
|
| 59 |
+
temporal_attention_block_skip_range (`int`, *optional*, defaults to `None`):
|
| 60 |
+
Calculate the attention states every `N` iterations. If this is set to `N`, the attention computation will
|
| 61 |
+
be skipped `N - 1` times (i.e., cached attention states will be reused) before computing the new attention
|
| 62 |
+
states again.
|
| 63 |
+
spatial_attention_timestep_skip_range (`tuple[float, float]`, defaults to `(-1, 681)`):
|
| 64 |
+
The timestep range within which the spatial attention computation can be skipped without a significant loss
|
| 65 |
+
in quality. This is to be determined by the user based on the underlying model. The first value in the
|
| 66 |
+
tuple is the lower bound and the second value is the upper bound. Typically, diffusion timesteps for
|
| 67 |
+
denoising are in the reversed range of 0 to 1000 (i.e. denoising starts at timestep 1000 and ends at
|
| 68 |
+
timestep 0). For the default values, this would mean that the spatial attention computation skipping will
|
| 69 |
+
be applicable only after denoising timestep 681 is reached, and continue until the end of the denoising
|
| 70 |
+
process.
|
| 71 |
+
temporal_attention_timestep_skip_range (`tuple[float, float]`, *optional*, defaults to `None`):
|
| 72 |
+
The timestep range within which the temporal attention computation can be skipped without a significant
|
| 73 |
+
loss in quality. This is to be determined by the user based on the underlying model. The first value in the
|
| 74 |
+
tuple is the lower bound and the second value is the upper bound. Typically, diffusion timesteps for
|
| 75 |
+
denoising are in the reversed range of 0 to 1000 (i.e. denoising starts at timestep 1000 and ends at
|
| 76 |
+
timestep 0).
|
| 77 |
+
low_frequency_weight_update_timestep_range (`tuple[int, int]`, defaults to `(99, 901)`):
|
| 78 |
+
The timestep range within which the low frequency weight scaling update is applied. The first value in the
|
| 79 |
+
tuple is the lower bound and the second value is the upper bound of the timestep range. The callback
|
| 80 |
+
function for the update is called only within this range.
|
| 81 |
+
high_frequency_weight_update_timestep_range (`tuple[int, int]`, defaults to `(-1, 301)`):
|
| 82 |
+
The timestep range within which the high frequency weight scaling update is applied. The first value in the
|
| 83 |
+
tuple is the lower bound and the second value is the upper bound of the timestep range. The callback
|
| 84 |
+
function for the update is called only within this range.
|
| 85 |
+
alpha_low_frequency (`float`, defaults to `1.1`):
|
| 86 |
+
The weight to scale the low frequency updates by. This is used to approximate the unconditional branch from
|
| 87 |
+
the conditional branch outputs.
|
| 88 |
+
alpha_high_frequency (`float`, defaults to `1.1`):
|
| 89 |
+
The weight to scale the high frequency updates by. This is used to approximate the unconditional branch
|
| 90 |
+
from the conditional branch outputs.
|
| 91 |
+
unconditional_batch_skip_range (`int`, defaults to `5`):
|
| 92 |
+
Process the unconditional branch every `N` iterations. If this is set to `N`, the unconditional branch
|
| 93 |
+
computation will be skipped `N - 1` times (i.e., cached unconditional branch states will be reused) before
|
| 94 |
+
computing the new unconditional branch states again.
|
| 95 |
+
unconditional_batch_timestep_skip_range (`tuple[float, float]`, defaults to `(-1, 641)`):
|
| 96 |
+
The timestep range within which the unconditional branch computation can be skipped without a significant
|
| 97 |
+
loss in quality. This is to be determined by the user based on the underlying model. The first value in the
|
| 98 |
+
tuple is the lower bound and the second value is the upper bound.
|
| 99 |
+
spatial_attention_block_identifiers (`tuple[str, ...]`, defaults to `("blocks.*attn1", "transformer_blocks.*attn1", "single_transformer_blocks.*attn1")`):
|
| 100 |
+
The identifiers to match the spatial attention blocks in the model. If the name of the block contains any
|
| 101 |
+
of these identifiers, FasterCache will be applied to that block. This can either be the full layer names,
|
| 102 |
+
partial layer names, or regex patterns. Matching will always be done using a regex match.
|
| 103 |
+
temporal_attention_block_identifiers (`tuple[str, ...]`, defaults to `("temporal_transformer_blocks.*attn1",)`):
|
| 104 |
+
The identifiers to match the temporal attention blocks in the model. If the name of the block contains any
|
| 105 |
+
of these identifiers, FasterCache will be applied to that block. This can either be the full layer names,
|
| 106 |
+
partial layer names, or regex patterns. Matching will always be done using a regex match.
|
| 107 |
+
attention_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
|
| 108 |
+
The callback function to determine the weight to scale the attention outputs by. This function should take
|
| 109 |
+
the attention module as input and return a float value. This is used to approximate the unconditional
|
| 110 |
+
branch from the conditional branch outputs. If not provided, the default weight is 0.5 for all timesteps.
|
| 111 |
+
Typically, as described in the paper, this weight should gradually increase from 0 to 1 as the inference
|
| 112 |
+
progresses. Users are encouraged to experiment and provide custom weight schedules that take into account
|
| 113 |
+
the number of inference steps and underlying model behaviour as denoising progresses.
|
| 114 |
+
low_frequency_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
|
| 115 |
+
The callback function to determine the weight to scale the low frequency updates by. If not provided, the
|
| 116 |
+
default weight is 1.1 for timesteps within the range specified (as described in the paper).
|
| 117 |
+
high_frequency_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
|
| 118 |
+
The callback function to determine the weight to scale the high frequency updates by. If not provided, the
|
| 119 |
+
default weight is 1.1 for timesteps within the range specified (as described in the paper).
|
| 120 |
+
tensor_format (`str`, defaults to `"BCFHW"`):
|
| 121 |
+
The format of the input tensors. This should be one of `"BCFHW"`, `"BFCHW"`, or `"BCHW"`. The format is
|
| 122 |
+
used to split individual latent frames in order for low and high frequency components to be computed.
|
| 123 |
+
is_guidance_distilled (`bool`, defaults to `False`):
|
| 124 |
+
Whether the model is guidance distilled or not. If the model is guidance distilled, FasterCache will not be
|
| 125 |
+
applied at the denoiser-level to skip the unconditional branch computation (as there is none).
|
| 126 |
+
_unconditional_conditional_input_kwargs_identifiers (`list[str]`, defaults to `("hidden_states", "encoder_hidden_states", "timestep", "attention_mask", "encoder_attention_mask")`):
|
| 127 |
+
The identifiers to match the input kwargs that contain the batchwise-concatenated unconditional and
|
| 128 |
+
conditional inputs. If the name of the input kwargs contains any of these identifiers, FasterCache will
|
| 129 |
+
split the inputs into unconditional and conditional branches. This must be a list of exact input kwargs
|
| 130 |
+
names that contain the batchwise-concatenated unconditional and conditional inputs.
|
| 131 |
+
"""
|
| 132 |
+
|
| 133 |
+
# In the paper and codebase, they hardcode these values to 2. However, it can be made configurable
|
| 134 |
+
# after some testing. We default to 2 if these parameters are not provided.
|
| 135 |
+
spatial_attention_block_skip_range: int = 2
|
| 136 |
+
temporal_attention_block_skip_range: int | None = None
|
| 137 |
+
|
| 138 |
+
spatial_attention_timestep_skip_range: tuple[int, int] = (-1, 681)
|
| 139 |
+
temporal_attention_timestep_skip_range: tuple[int, int] = (-1, 681)
|
| 140 |
+
|
| 141 |
+
# Indicator functions for low/high frequency as mentioned in Equation 11 of the paper
|
| 142 |
+
low_frequency_weight_update_timestep_range: tuple[int, int] = (99, 901)
|
| 143 |
+
high_frequency_weight_update_timestep_range: tuple[int, int] = (-1, 301)
|
| 144 |
+
|
| 145 |
+
# ⍺1 and ⍺2 as mentioned in Equation 11 of the paper
|
| 146 |
+
alpha_low_frequency: float = 1.1
|
| 147 |
+
alpha_high_frequency: float = 1.1
|
| 148 |
+
|
| 149 |
+
# n as described in CFG-Cache explanation in the paper - dependent on the model
|
| 150 |
+
unconditional_batch_skip_range: int = 5
|
| 151 |
+
unconditional_batch_timestep_skip_range: tuple[int, int] = (-1, 641)
|
| 152 |
+
|
| 153 |
+
spatial_attention_block_identifiers: tuple[str, ...] = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS
|
| 154 |
+
temporal_attention_block_identifiers: tuple[str, ...] = _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
|
| 155 |
+
|
| 156 |
+
attention_weight_callback: Callable[[torch.nn.Module], float] = None
|
| 157 |
+
low_frequency_weight_callback: Callable[[torch.nn.Module], float] = None
|
| 158 |
+
high_frequency_weight_callback: Callable[[torch.nn.Module], float] = None
|
| 159 |
+
|
| 160 |
+
tensor_format: str = "BCFHW"
|
| 161 |
+
is_guidance_distilled: bool = False
|
| 162 |
+
|
| 163 |
+
current_timestep_callback: Callable[[], int] = None
|
| 164 |
+
|
| 165 |
+
_unconditional_conditional_input_kwargs_identifiers: list[str] = _UNCOND_COND_INPUT_KWARGS_IDENTIFIERS
|
| 166 |
+
|
| 167 |
+
def __repr__(self) -> str:
|
| 168 |
+
return (
|
| 169 |
+
f"FasterCacheConfig(\n"
|
| 170 |
+
f" spatial_attention_block_skip_range={self.spatial_attention_block_skip_range},\n"
|
| 171 |
+
f" temporal_attention_block_skip_range={self.temporal_attention_block_skip_range},\n"
|
| 172 |
+
f" spatial_attention_timestep_skip_range={self.spatial_attention_timestep_skip_range},\n"
|
| 173 |
+
f" temporal_attention_timestep_skip_range={self.temporal_attention_timestep_skip_range},\n"
|
| 174 |
+
f" low_frequency_weight_update_timestep_range={self.low_frequency_weight_update_timestep_range},\n"
|
| 175 |
+
f" high_frequency_weight_update_timestep_range={self.high_frequency_weight_update_timestep_range},\n"
|
| 176 |
+
f" alpha_low_frequency={self.alpha_low_frequency},\n"
|
| 177 |
+
f" alpha_high_frequency={self.alpha_high_frequency},\n"
|
| 178 |
+
f" unconditional_batch_skip_range={self.unconditional_batch_skip_range},\n"
|
| 179 |
+
f" unconditional_batch_timestep_skip_range={self.unconditional_batch_timestep_skip_range},\n"
|
| 180 |
+
f" spatial_attention_block_identifiers={self.spatial_attention_block_identifiers},\n"
|
| 181 |
+
f" temporal_attention_block_identifiers={self.temporal_attention_block_identifiers},\n"
|
| 182 |
+
f" tensor_format={self.tensor_format},\n"
|
| 183 |
+
f")"
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
class FasterCacheDenoiserState:
|
| 188 |
+
r"""
|
| 189 |
+
State for [FasterCache](https://huggingface.co/papers/2410.19355) top-level denoiser module.
|
| 190 |
+
"""
|
| 191 |
+
|
| 192 |
+
def __init__(self) -> None:
|
| 193 |
+
self.iteration: int = 0
|
| 194 |
+
self.low_frequency_delta: torch.Tensor = None
|
| 195 |
+
self.high_frequency_delta: torch.Tensor = None
|
| 196 |
+
|
| 197 |
+
def reset(self):
|
| 198 |
+
self.iteration = 0
|
| 199 |
+
self.low_frequency_delta = None
|
| 200 |
+
self.high_frequency_delta = None
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
class FasterCacheBlockState:
|
| 204 |
+
r"""
|
| 205 |
+
State for [FasterCache](https://huggingface.co/papers/2410.19355). Every underlying block that FasterCache is
|
| 206 |
+
applied to will have an instance of this state.
|
| 207 |
+
"""
|
| 208 |
+
|
| 209 |
+
def __init__(self) -> None:
|
| 210 |
+
self.iteration: int = 0
|
| 211 |
+
self.batch_size: int = None
|
| 212 |
+
self.cache: tuple[torch.Tensor, torch.Tensor] = None
|
| 213 |
+
|
| 214 |
+
def reset(self):
|
| 215 |
+
self.iteration = 0
|
| 216 |
+
self.batch_size = None
|
| 217 |
+
self.cache = None
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class FasterCacheDenoiserHook(ModelHook):
|
| 221 |
+
_is_stateful = True
|
| 222 |
+
|
| 223 |
+
def __init__(
|
| 224 |
+
self,
|
| 225 |
+
unconditional_batch_skip_range: int,
|
| 226 |
+
unconditional_batch_timestep_skip_range: tuple[int, int],
|
| 227 |
+
tensor_format: str,
|
| 228 |
+
is_guidance_distilled: bool,
|
| 229 |
+
uncond_cond_input_kwargs_identifiers: list[str],
|
| 230 |
+
current_timestep_callback: Callable[[], int],
|
| 231 |
+
low_frequency_weight_callback: Callable[[torch.nn.Module], torch.Tensor],
|
| 232 |
+
high_frequency_weight_callback: Callable[[torch.nn.Module], torch.Tensor],
|
| 233 |
+
) -> None:
|
| 234 |
+
super().__init__()
|
| 235 |
+
|
| 236 |
+
self.unconditional_batch_skip_range = unconditional_batch_skip_range
|
| 237 |
+
self.unconditional_batch_timestep_skip_range = unconditional_batch_timestep_skip_range
|
| 238 |
+
# We can't easily detect what args are to be split in unconditional and conditional branches. We
|
| 239 |
+
# can only do it for kwargs, hence they are the only ones we split. The args are passed as-is.
|
| 240 |
+
# If a model is to be made compatible with FasterCache, the user must ensure that the inputs that
|
| 241 |
+
# contain batchwise-concatenated unconditional and conditional inputs are passed as kwargs.
|
| 242 |
+
self.uncond_cond_input_kwargs_identifiers = uncond_cond_input_kwargs_identifiers
|
| 243 |
+
self.tensor_format = tensor_format
|
| 244 |
+
self.is_guidance_distilled = is_guidance_distilled
|
| 245 |
+
|
| 246 |
+
self.current_timestep_callback = current_timestep_callback
|
| 247 |
+
self.low_frequency_weight_callback = low_frequency_weight_callback
|
| 248 |
+
self.high_frequency_weight_callback = high_frequency_weight_callback
|
| 249 |
+
|
| 250 |
+
def initialize_hook(self, module):
|
| 251 |
+
self.state = FasterCacheDenoiserState()
|
| 252 |
+
return module
|
| 253 |
+
|
| 254 |
+
@staticmethod
|
| 255 |
+
def _get_cond_input(input: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 256 |
+
# Note: this method assumes that the input tensor is batchwise-concatenated with unconditional inputs
|
| 257 |
+
# followed by conditional inputs.
|
| 258 |
+
_, cond = input.chunk(2, dim=0)
|
| 259 |
+
return cond
|
| 260 |
+
|
| 261 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
|
| 262 |
+
# Split the unconditional and conditional inputs. We only want to infer the conditional branch if the
|
| 263 |
+
# requirements for skipping the unconditional branch are met as described in the paper.
|
| 264 |
+
# We skip the unconditional branch only if the following conditions are met:
|
| 265 |
+
# 1. We have completed at least one iteration of the denoiser
|
| 266 |
+
# 2. The current timestep is within the range specified by the user. This is the optimal timestep range
|
| 267 |
+
# where approximating the unconditional branch from the computation of the conditional branch is possible
|
| 268 |
+
# without a significant loss in quality.
|
| 269 |
+
# 3. The current iteration is not a multiple of the unconditional batch skip range. This is done so that
|
| 270 |
+
# we compute the unconditional branch at least once every few iterations to ensure minimal quality loss.
|
| 271 |
+
is_within_timestep_range = (
|
| 272 |
+
self.unconditional_batch_timestep_skip_range[0]
|
| 273 |
+
< self.current_timestep_callback()
|
| 274 |
+
< self.unconditional_batch_timestep_skip_range[1]
|
| 275 |
+
)
|
| 276 |
+
should_skip_uncond = (
|
| 277 |
+
self.state.iteration > 0
|
| 278 |
+
and is_within_timestep_range
|
| 279 |
+
and self.state.iteration % self.unconditional_batch_skip_range != 0
|
| 280 |
+
and not self.is_guidance_distilled
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
if should_skip_uncond:
|
| 284 |
+
is_any_kwarg_uncond = any(k in self.uncond_cond_input_kwargs_identifiers for k in kwargs.keys())
|
| 285 |
+
if is_any_kwarg_uncond:
|
| 286 |
+
logger.debug("FasterCache - Skipping unconditional branch computation")
|
| 287 |
+
args = tuple([self._get_cond_input(arg) if torch.is_tensor(arg) else arg for arg in args])
|
| 288 |
+
kwargs = {
|
| 289 |
+
k: v if k not in self.uncond_cond_input_kwargs_identifiers else self._get_cond_input(v)
|
| 290 |
+
for k, v in kwargs.items()
|
| 291 |
+
}
|
| 292 |
+
|
| 293 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 294 |
+
|
| 295 |
+
if self.is_guidance_distilled:
|
| 296 |
+
self.state.iteration += 1
|
| 297 |
+
return output
|
| 298 |
+
|
| 299 |
+
if torch.is_tensor(output):
|
| 300 |
+
hidden_states = output
|
| 301 |
+
elif isinstance(output, (tuple, Transformer2DModelOutput)):
|
| 302 |
+
hidden_states = output[0]
|
| 303 |
+
|
| 304 |
+
batch_size = hidden_states.size(0)
|
| 305 |
+
|
| 306 |
+
if should_skip_uncond:
|
| 307 |
+
self.state.low_frequency_delta = self.state.low_frequency_delta * self.low_frequency_weight_callback(
|
| 308 |
+
module
|
| 309 |
+
)
|
| 310 |
+
self.state.high_frequency_delta = self.state.high_frequency_delta * self.high_frequency_weight_callback(
|
| 311 |
+
module
|
| 312 |
+
)
|
| 313 |
+
|
| 314 |
+
if self.tensor_format == "BCFHW":
|
| 315 |
+
hidden_states = hidden_states.permute(0, 2, 1, 3, 4)
|
| 316 |
+
if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
|
| 317 |
+
hidden_states = hidden_states.flatten(0, 1)
|
| 318 |
+
|
| 319 |
+
low_freq_cond, high_freq_cond = _split_low_high_freq(hidden_states.float())
|
| 320 |
+
|
| 321 |
+
# Approximate/compute the unconditional branch outputs as described in Equation 9 and 10 of the paper
|
| 322 |
+
low_freq_uncond = self.state.low_frequency_delta + low_freq_cond
|
| 323 |
+
high_freq_uncond = self.state.high_frequency_delta + high_freq_cond
|
| 324 |
+
uncond_freq = low_freq_uncond + high_freq_uncond
|
| 325 |
+
|
| 326 |
+
uncond_states = torch.fft.ifftshift(uncond_freq)
|
| 327 |
+
uncond_states = torch.fft.ifft2(uncond_states).real
|
| 328 |
+
|
| 329 |
+
if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
|
| 330 |
+
uncond_states = uncond_states.unflatten(0, (batch_size, -1))
|
| 331 |
+
hidden_states = hidden_states.unflatten(0, (batch_size, -1))
|
| 332 |
+
if self.tensor_format == "BCFHW":
|
| 333 |
+
uncond_states = uncond_states.permute(0, 2, 1, 3, 4)
|
| 334 |
+
hidden_states = hidden_states.permute(0, 2, 1, 3, 4)
|
| 335 |
+
|
| 336 |
+
# Concatenate the approximated unconditional and predicted conditional branches
|
| 337 |
+
uncond_states = uncond_states.to(hidden_states.dtype)
|
| 338 |
+
hidden_states = torch.cat([uncond_states, hidden_states], dim=0)
|
| 339 |
+
else:
|
| 340 |
+
uncond_states, cond_states = hidden_states.chunk(2, dim=0)
|
| 341 |
+
if self.tensor_format == "BCFHW":
|
| 342 |
+
uncond_states = uncond_states.permute(0, 2, 1, 3, 4)
|
| 343 |
+
cond_states = cond_states.permute(0, 2, 1, 3, 4)
|
| 344 |
+
if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
|
| 345 |
+
uncond_states = uncond_states.flatten(0, 1)
|
| 346 |
+
cond_states = cond_states.flatten(0, 1)
|
| 347 |
+
|
| 348 |
+
low_freq_uncond, high_freq_uncond = _split_low_high_freq(uncond_states.float())
|
| 349 |
+
low_freq_cond, high_freq_cond = _split_low_high_freq(cond_states.float())
|
| 350 |
+
self.state.low_frequency_delta = low_freq_uncond - low_freq_cond
|
| 351 |
+
self.state.high_frequency_delta = high_freq_uncond - high_freq_cond
|
| 352 |
+
|
| 353 |
+
self.state.iteration += 1
|
| 354 |
+
if torch.is_tensor(output):
|
| 355 |
+
output = hidden_states
|
| 356 |
+
elif isinstance(output, tuple):
|
| 357 |
+
output = (hidden_states, *output[1:])
|
| 358 |
+
else:
|
| 359 |
+
output.sample = hidden_states
|
| 360 |
+
|
| 361 |
+
return output
|
| 362 |
+
|
| 363 |
+
def reset_state(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 364 |
+
self.state.reset()
|
| 365 |
+
return module
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
class FasterCacheBlockHook(ModelHook):
|
| 369 |
+
_is_stateful = True
|
| 370 |
+
|
| 371 |
+
def __init__(
|
| 372 |
+
self,
|
| 373 |
+
block_skip_range: int,
|
| 374 |
+
timestep_skip_range: tuple[int, int],
|
| 375 |
+
is_guidance_distilled: bool,
|
| 376 |
+
weight_callback: Callable[[torch.nn.Module], float],
|
| 377 |
+
current_timestep_callback: Callable[[], int],
|
| 378 |
+
) -> None:
|
| 379 |
+
super().__init__()
|
| 380 |
+
|
| 381 |
+
self.block_skip_range = block_skip_range
|
| 382 |
+
self.timestep_skip_range = timestep_skip_range
|
| 383 |
+
self.is_guidance_distilled = is_guidance_distilled
|
| 384 |
+
|
| 385 |
+
self.weight_callback = weight_callback
|
| 386 |
+
self.current_timestep_callback = current_timestep_callback
|
| 387 |
+
|
| 388 |
+
def initialize_hook(self, module):
|
| 389 |
+
self.state = FasterCacheBlockState()
|
| 390 |
+
return module
|
| 391 |
+
|
| 392 |
+
def _compute_approximated_attention_output(
|
| 393 |
+
self, t_2_output: torch.Tensor, t_output: torch.Tensor, weight: float, batch_size: int
|
| 394 |
+
) -> torch.Tensor:
|
| 395 |
+
if t_2_output.size(0) != batch_size:
|
| 396 |
+
# The cache t_2_output contains both batchwise-concatenated unconditional-conditional branch outputs. Just
|
| 397 |
+
# take the conditional branch outputs.
|
| 398 |
+
assert t_2_output.size(0) == 2 * batch_size
|
| 399 |
+
t_2_output = t_2_output[batch_size:]
|
| 400 |
+
if t_output.size(0) != batch_size:
|
| 401 |
+
# The cache t_output contains both batchwise-concatenated unconditional-conditional branch outputs. Just
|
| 402 |
+
# take the conditional branch outputs.
|
| 403 |
+
assert t_output.size(0) == 2 * batch_size
|
| 404 |
+
t_output = t_output[batch_size:]
|
| 405 |
+
return t_output + (t_output - t_2_output) * weight
|
| 406 |
+
|
| 407 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
|
| 408 |
+
batch_size = [
|
| 409 |
+
*[arg.size(0) for arg in args if torch.is_tensor(arg)],
|
| 410 |
+
*[v.size(0) for v in kwargs.values() if torch.is_tensor(v)],
|
| 411 |
+
][0]
|
| 412 |
+
if self.state.batch_size is None:
|
| 413 |
+
# Will be updated on first forward pass through the denoiser
|
| 414 |
+
self.state.batch_size = batch_size
|
| 415 |
+
|
| 416 |
+
# If we have to skip due to the skip conditions, then let's skip as expected.
|
| 417 |
+
# But, we can't skip if the denoiser wants to infer both unconditional and conditional branches. This
|
| 418 |
+
# is because the expected output shapes of attention layer will not match if we only return values from
|
| 419 |
+
# the cache (which only caches conditional branch outputs). So, if state.batch_size (which is the true
|
| 420 |
+
# unconditional-conditional batch size) is same as the current batch size, we don't perform the layer
|
| 421 |
+
# skip. Otherwise, we conditionally skip the layer based on what state.skip_callback returns.
|
| 422 |
+
is_within_timestep_range = (
|
| 423 |
+
self.timestep_skip_range[0] < self.current_timestep_callback() < self.timestep_skip_range[1]
|
| 424 |
+
)
|
| 425 |
+
if not is_within_timestep_range:
|
| 426 |
+
should_skip_attention = False
|
| 427 |
+
else:
|
| 428 |
+
should_compute_attention = self.state.iteration > 0 and self.state.iteration % self.block_skip_range == 0
|
| 429 |
+
should_skip_attention = not should_compute_attention
|
| 430 |
+
if should_skip_attention:
|
| 431 |
+
should_skip_attention = self.is_guidance_distilled or self.state.batch_size != batch_size
|
| 432 |
+
|
| 433 |
+
if should_skip_attention:
|
| 434 |
+
logger.debug("FasterCache - Skipping attention and using approximation")
|
| 435 |
+
if torch.is_tensor(self.state.cache[-1]):
|
| 436 |
+
t_2_output, t_output = self.state.cache
|
| 437 |
+
weight = self.weight_callback(module)
|
| 438 |
+
output = self._compute_approximated_attention_output(t_2_output, t_output, weight, batch_size)
|
| 439 |
+
else:
|
| 440 |
+
# The cache contains multiple tensors from past N iterations (N=2 for FasterCache). We need to handle all of them.
|
| 441 |
+
# Diffusers blocks can return multiple tensors - let's call them [A, B, C, ...] for simplicity.
|
| 442 |
+
# In our cache, we would have [[A_1, B_1, C_1, ...], [A_2, B_2, C_2, ...], ...] where each list is the output from
|
| 443 |
+
# a forward pass of the block. We need to compute the approximated output for each of these tensors.
|
| 444 |
+
# The zip(*state.cache) operation will give us [(A_1, A_2, ...), (B_1, B_2, ...), (C_1, C_2, ...), ...] which
|
| 445 |
+
# allows us to compute the approximated attention output for each tensor in the cache.
|
| 446 |
+
output = ()
|
| 447 |
+
for t_2_output, t_output in zip(*self.state.cache):
|
| 448 |
+
result = self._compute_approximated_attention_output(
|
| 449 |
+
t_2_output, t_output, self.weight_callback(module), batch_size
|
| 450 |
+
)
|
| 451 |
+
output += (result,)
|
| 452 |
+
else:
|
| 453 |
+
logger.debug("FasterCache - Computing attention")
|
| 454 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 455 |
+
|
| 456 |
+
# Note that the following condition for getting hidden_states should suffice since Diffusers blocks either return
|
| 457 |
+
# a single hidden_states tensor, or a tuple of (hidden_states, encoder_hidden_states) tensors. We need to handle
|
| 458 |
+
# both cases.
|
| 459 |
+
if torch.is_tensor(output):
|
| 460 |
+
cache_output = output
|
| 461 |
+
if not self.is_guidance_distilled and cache_output.size(0) == self.state.batch_size:
|
| 462 |
+
# The output here can be both unconditional-conditional branch outputs or just conditional branch outputs.
|
| 463 |
+
# This is determined at the higher-level denoiser module. We only want to cache the conditional branch outputs.
|
| 464 |
+
cache_output = cache_output.chunk(2, dim=0)[1]
|
| 465 |
+
else:
|
| 466 |
+
# Cache all return values and perform the same operation as above
|
| 467 |
+
cache_output = ()
|
| 468 |
+
for out in output:
|
| 469 |
+
if not self.is_guidance_distilled and out.size(0) == self.state.batch_size:
|
| 470 |
+
out = out.chunk(2, dim=0)[1]
|
| 471 |
+
cache_output += (out,)
|
| 472 |
+
|
| 473 |
+
if self.state.cache is None:
|
| 474 |
+
self.state.cache = [cache_output, cache_output]
|
| 475 |
+
else:
|
| 476 |
+
self.state.cache = [self.state.cache[-1], cache_output]
|
| 477 |
+
|
| 478 |
+
self.state.iteration += 1
|
| 479 |
+
return output
|
| 480 |
+
|
| 481 |
+
def reset_state(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 482 |
+
self.state.reset()
|
| 483 |
+
return module
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def apply_faster_cache(module: torch.nn.Module, config: FasterCacheConfig) -> None:
|
| 487 |
+
r"""
|
| 488 |
+
Applies [FasterCache](https://huggingface.co/papers/2410.19355) to a given pipeline.
|
| 489 |
+
|
| 490 |
+
Args:
|
| 491 |
+
module (`torch.nn.Module`):
|
| 492 |
+
The pytorch module to apply FasterCache to. Typically, this should be a transformer architecture supported
|
| 493 |
+
in Diffusers, such as `CogVideoXTransformer3DModel`, but external implementations may also work.
|
| 494 |
+
config (`FasterCacheConfig`):
|
| 495 |
+
The configuration to use for FasterCache.
|
| 496 |
+
|
| 497 |
+
Example:
|
| 498 |
+
```python
|
| 499 |
+
>>> import torch
|
| 500 |
+
>>> from diffusers import CogVideoXPipeline, FasterCacheConfig, apply_faster_cache
|
| 501 |
+
|
| 502 |
+
>>> pipe = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
|
| 503 |
+
>>> pipe.to("cuda")
|
| 504 |
+
|
| 505 |
+
>>> config = FasterCacheConfig(
|
| 506 |
+
... spatial_attention_block_skip_range=2,
|
| 507 |
+
... spatial_attention_timestep_skip_range=(-1, 681),
|
| 508 |
+
... low_frequency_weight_update_timestep_range=(99, 641),
|
| 509 |
+
... high_frequency_weight_update_timestep_range=(-1, 301),
|
| 510 |
+
... spatial_attention_block_identifiers=["transformer_blocks"],
|
| 511 |
+
... attention_weight_callback=lambda _: 0.3,
|
| 512 |
+
... tensor_format="BFCHW",
|
| 513 |
+
... )
|
| 514 |
+
>>> apply_faster_cache(pipe.transformer, config)
|
| 515 |
+
```
|
| 516 |
+
"""
|
| 517 |
+
|
| 518 |
+
logger.warning(
|
| 519 |
+
"FasterCache is a purely experimental feature and may not work as expected. Not all models support FasterCache. "
|
| 520 |
+
"The API is subject to change in future releases, with no guarantee of backward compatibility. Please report any issues at "
|
| 521 |
+
"https://github.com/huggingface/diffusers/issues."
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
if config.attention_weight_callback is None:
|
| 525 |
+
# If the user has not provided a weight callback, we default to 0.5 for all timesteps.
|
| 526 |
+
# In the paper, they recommend using a gradually increasing weight from 0 to 1 as the inference progresses, but
|
| 527 |
+
# this depends from model-to-model. It is required by the user to provide a weight callback if they want to
|
| 528 |
+
# use a different weight function. Defaulting to 0.5 works well in practice for most cases.
|
| 529 |
+
logger.warning(
|
| 530 |
+
"No `attention_weight_callback` provided when enabling FasterCache. Defaulting to using a weight of 0.5 for all timesteps."
|
| 531 |
+
)
|
| 532 |
+
config.attention_weight_callback = lambda _: 0.5
|
| 533 |
+
|
| 534 |
+
if config.low_frequency_weight_callback is None:
|
| 535 |
+
logger.debug(
|
| 536 |
+
"Low frequency weight callback not provided when enabling FasterCache. Defaulting to behaviour described in the paper."
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
def low_frequency_weight_callback(module: torch.nn.Module) -> float:
|
| 540 |
+
is_within_range = (
|
| 541 |
+
config.low_frequency_weight_update_timestep_range[0]
|
| 542 |
+
< config.current_timestep_callback()
|
| 543 |
+
< config.low_frequency_weight_update_timestep_range[1]
|
| 544 |
+
)
|
| 545 |
+
return config.alpha_low_frequency if is_within_range else 1.0
|
| 546 |
+
|
| 547 |
+
config.low_frequency_weight_callback = low_frequency_weight_callback
|
| 548 |
+
|
| 549 |
+
if config.high_frequency_weight_callback is None:
|
| 550 |
+
logger.debug(
|
| 551 |
+
"High frequency weight callback not provided when enabling FasterCache. Defaulting to behaviour described in the paper."
|
| 552 |
+
)
|
| 553 |
+
|
| 554 |
+
def high_frequency_weight_callback(module: torch.nn.Module) -> float:
|
| 555 |
+
is_within_range = (
|
| 556 |
+
config.high_frequency_weight_update_timestep_range[0]
|
| 557 |
+
< config.current_timestep_callback()
|
| 558 |
+
< config.high_frequency_weight_update_timestep_range[1]
|
| 559 |
+
)
|
| 560 |
+
return config.alpha_high_frequency if is_within_range else 1.0
|
| 561 |
+
|
| 562 |
+
config.high_frequency_weight_callback = high_frequency_weight_callback
|
| 563 |
+
|
| 564 |
+
supported_tensor_formats = ["BCFHW", "BFCHW", "BCHW"] # TODO(aryan): Support BSC for LTX Video
|
| 565 |
+
if config.tensor_format not in supported_tensor_formats:
|
| 566 |
+
raise ValueError(f"`tensor_format` must be one of {supported_tensor_formats}, but got {config.tensor_format}.")
|
| 567 |
+
|
| 568 |
+
_apply_faster_cache_on_denoiser(module, config)
|
| 569 |
+
|
| 570 |
+
for name, submodule in module.named_modules():
|
| 571 |
+
if not isinstance(submodule, _ATTENTION_CLASSES):
|
| 572 |
+
continue
|
| 573 |
+
if any(re.search(identifier, name) is not None for identifier in _TRANSFORMER_BLOCK_IDENTIFIERS):
|
| 574 |
+
_apply_faster_cache_on_attention_class(name, submodule, config)
|
| 575 |
+
|
| 576 |
+
|
| 577 |
+
def _apply_faster_cache_on_denoiser(module: torch.nn.Module, config: FasterCacheConfig) -> None:
|
| 578 |
+
hook = FasterCacheDenoiserHook(
|
| 579 |
+
config.unconditional_batch_skip_range,
|
| 580 |
+
config.unconditional_batch_timestep_skip_range,
|
| 581 |
+
config.tensor_format,
|
| 582 |
+
config.is_guidance_distilled,
|
| 583 |
+
config._unconditional_conditional_input_kwargs_identifiers,
|
| 584 |
+
config.current_timestep_callback,
|
| 585 |
+
config.low_frequency_weight_callback,
|
| 586 |
+
config.high_frequency_weight_callback,
|
| 587 |
+
)
|
| 588 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 589 |
+
registry.register_hook(hook, _FASTER_CACHE_DENOISER_HOOK)
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def _apply_faster_cache_on_attention_class(name: str, module: AttentionModuleMixin, config: FasterCacheConfig) -> None:
|
| 593 |
+
is_spatial_self_attention = (
|
| 594 |
+
any(re.search(identifier, name) is not None for identifier in config.spatial_attention_block_identifiers)
|
| 595 |
+
and config.spatial_attention_block_skip_range is not None
|
| 596 |
+
and not getattr(module, "is_cross_attention", False)
|
| 597 |
+
)
|
| 598 |
+
is_temporal_self_attention = (
|
| 599 |
+
any(re.search(identifier, name) is not None for identifier in config.temporal_attention_block_identifiers)
|
| 600 |
+
and config.temporal_attention_block_skip_range is not None
|
| 601 |
+
and not module.is_cross_attention
|
| 602 |
+
)
|
| 603 |
+
|
| 604 |
+
block_skip_range, timestep_skip_range, block_type = None, None, None
|
| 605 |
+
if is_spatial_self_attention:
|
| 606 |
+
block_skip_range = config.spatial_attention_block_skip_range
|
| 607 |
+
timestep_skip_range = config.spatial_attention_timestep_skip_range
|
| 608 |
+
block_type = "spatial"
|
| 609 |
+
elif is_temporal_self_attention:
|
| 610 |
+
block_skip_range = config.temporal_attention_block_skip_range
|
| 611 |
+
timestep_skip_range = config.temporal_attention_timestep_skip_range
|
| 612 |
+
block_type = "temporal"
|
| 613 |
+
|
| 614 |
+
if block_skip_range is None or timestep_skip_range is None:
|
| 615 |
+
logger.debug(
|
| 616 |
+
f'Unable to apply FasterCache to the selected layer: "{name}" because it does '
|
| 617 |
+
f"not match any of the required criteria for spatial or temporal attention layers. Note, "
|
| 618 |
+
f"however, that this layer may still be valid for applying PAB. Please specify the correct "
|
| 619 |
+
f"block identifiers in the configuration or use the specialized `apply_faster_cache_on_module` "
|
| 620 |
+
f"function to apply FasterCache to this layer."
|
| 621 |
+
)
|
| 622 |
+
return
|
| 623 |
+
|
| 624 |
+
logger.debug(f"Enabling FasterCache ({block_type}) for layer: {name}")
|
| 625 |
+
hook = FasterCacheBlockHook(
|
| 626 |
+
block_skip_range,
|
| 627 |
+
timestep_skip_range,
|
| 628 |
+
config.is_guidance_distilled,
|
| 629 |
+
config.attention_weight_callback,
|
| 630 |
+
config.current_timestep_callback,
|
| 631 |
+
)
|
| 632 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 633 |
+
registry.register_hook(hook, _FASTER_CACHE_BLOCK_HOOK)
|
| 634 |
+
|
| 635 |
+
|
| 636 |
+
# Reference: https://github.com/Vchitect/FasterCache/blob/fab32c15014636dc854948319c0a9a8d92c7acb4/scripts/latte/faster_cache_sample_latte.py#L127C1-L143C39
|
| 637 |
+
@torch.no_grad()
|
| 638 |
+
def _split_low_high_freq(x):
|
| 639 |
+
fft = torch.fft.fft2(x)
|
| 640 |
+
fft_shifted = torch.fft.fftshift(fft)
|
| 641 |
+
height, width = x.shape[-2:]
|
| 642 |
+
radius = min(height, width) // 5
|
| 643 |
+
|
| 644 |
+
y_grid, x_grid = torch.meshgrid(torch.arange(height), torch.arange(width))
|
| 645 |
+
center_x, center_y = width // 2, height // 2
|
| 646 |
+
mask = (x_grid - center_x) ** 2 + (y_grid - center_y) ** 2 <= radius**2
|
| 647 |
+
|
| 648 |
+
low_freq_mask = mask.unsqueeze(0).unsqueeze(0).to(x.device)
|
| 649 |
+
high_freq_mask = ~low_freq_mask
|
| 650 |
+
|
| 651 |
+
low_freq_fft = fft_shifted * low_freq_mask
|
| 652 |
+
high_freq_fft = fft_shifted * high_freq_mask
|
| 653 |
+
|
| 654 |
+
return low_freq_fft, high_freq_fft
|
third_party/diffusers/src/diffusers/hooks/first_block_cache.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The HuggingFace Team. All rights reserved.
|
| 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 dataclasses import dataclass
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from ..utils import get_logger
|
| 20 |
+
from ..utils.torch_utils import unwrap_module
|
| 21 |
+
from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS
|
| 22 |
+
from ._helpers import TransformerBlockRegistry
|
| 23 |
+
from .hooks import BaseState, HookRegistry, ModelHook, StateManager
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 27 |
+
|
| 28 |
+
_FBC_LEADER_BLOCK_HOOK = "fbc_leader_block_hook"
|
| 29 |
+
_FBC_BLOCK_HOOK = "fbc_block_hook"
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
@dataclass
|
| 33 |
+
class FirstBlockCacheConfig:
|
| 34 |
+
r"""
|
| 35 |
+
Configuration for [First Block
|
| 36 |
+
Cache](https://github.com/chengzeyi/ParaAttention/blob/7a266123671b55e7e5a2fe9af3121f07a36afc78/README.md#first-block-cache-our-dynamic-caching).
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
threshold (`float`, defaults to `0.05`):
|
| 40 |
+
The threshold to determine whether or not a forward pass through all layers of the model is required. A
|
| 41 |
+
higher threshold usually results in a forward pass through a lower number of layers and faster inference,
|
| 42 |
+
but might lead to poorer generation quality. A lower threshold may not result in significant generation
|
| 43 |
+
speedup. The threshold is compared against the absmean difference of the residuals between the current and
|
| 44 |
+
cached outputs from the first transformer block. If the difference is below the threshold, the forward pass
|
| 45 |
+
is skipped.
|
| 46 |
+
"""
|
| 47 |
+
|
| 48 |
+
threshold: float = 0.05
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
class FBCSharedBlockState(BaseState):
|
| 52 |
+
def __init__(self) -> None:
|
| 53 |
+
super().__init__()
|
| 54 |
+
|
| 55 |
+
self.head_block_output: torch.Tensor | tuple[torch.Tensor, ...] = None
|
| 56 |
+
self.head_block_residual: torch.Tensor = None
|
| 57 |
+
self.tail_block_residuals: torch.Tensor | tuple[torch.Tensor, ...] = None
|
| 58 |
+
self.should_compute: bool = True
|
| 59 |
+
|
| 60 |
+
def reset(self):
|
| 61 |
+
self.tail_block_residuals = None
|
| 62 |
+
self.should_compute = True
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class FBCHeadBlockHook(ModelHook):
|
| 66 |
+
_is_stateful = True
|
| 67 |
+
|
| 68 |
+
def __init__(self, state_manager: StateManager, threshold: float):
|
| 69 |
+
self.state_manager = state_manager
|
| 70 |
+
self.threshold = threshold
|
| 71 |
+
self._metadata = None
|
| 72 |
+
|
| 73 |
+
def initialize_hook(self, module):
|
| 74 |
+
unwrapped_module = unwrap_module(module)
|
| 75 |
+
self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
|
| 76 |
+
return module
|
| 77 |
+
|
| 78 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 79 |
+
original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
|
| 80 |
+
|
| 81 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 82 |
+
is_output_tuple = isinstance(output, tuple)
|
| 83 |
+
|
| 84 |
+
if is_output_tuple:
|
| 85 |
+
hidden_states_residual = output[self._metadata.return_hidden_states_index] - original_hidden_states
|
| 86 |
+
else:
|
| 87 |
+
hidden_states_residual = output - original_hidden_states
|
| 88 |
+
|
| 89 |
+
shared_state: FBCSharedBlockState = self.state_manager.get_state()
|
| 90 |
+
hidden_states = encoder_hidden_states = None
|
| 91 |
+
should_compute = self._should_compute_remaining_blocks(hidden_states_residual)
|
| 92 |
+
shared_state.should_compute = should_compute
|
| 93 |
+
|
| 94 |
+
if not should_compute:
|
| 95 |
+
# Apply caching
|
| 96 |
+
if is_output_tuple:
|
| 97 |
+
hidden_states = (
|
| 98 |
+
shared_state.tail_block_residuals[0] + output[self._metadata.return_hidden_states_index]
|
| 99 |
+
)
|
| 100 |
+
else:
|
| 101 |
+
hidden_states = shared_state.tail_block_residuals[0] + output
|
| 102 |
+
|
| 103 |
+
if self._metadata.return_encoder_hidden_states_index is not None:
|
| 104 |
+
assert is_output_tuple
|
| 105 |
+
encoder_hidden_states = (
|
| 106 |
+
shared_state.tail_block_residuals[1] + output[self._metadata.return_encoder_hidden_states_index]
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
if is_output_tuple:
|
| 110 |
+
return_output = [None] * len(output)
|
| 111 |
+
return_output[self._metadata.return_hidden_states_index] = hidden_states
|
| 112 |
+
return_output[self._metadata.return_encoder_hidden_states_index] = encoder_hidden_states
|
| 113 |
+
return_output = tuple(return_output)
|
| 114 |
+
else:
|
| 115 |
+
return_output = hidden_states
|
| 116 |
+
output = return_output
|
| 117 |
+
else:
|
| 118 |
+
if is_output_tuple:
|
| 119 |
+
head_block_output = [None] * len(output)
|
| 120 |
+
head_block_output[0] = output[self._metadata.return_hidden_states_index]
|
| 121 |
+
head_block_output[1] = output[self._metadata.return_encoder_hidden_states_index]
|
| 122 |
+
else:
|
| 123 |
+
head_block_output = output
|
| 124 |
+
shared_state.head_block_output = head_block_output
|
| 125 |
+
shared_state.head_block_residual = hidden_states_residual
|
| 126 |
+
|
| 127 |
+
return output
|
| 128 |
+
|
| 129 |
+
def reset_state(self, module):
|
| 130 |
+
self.state_manager.reset()
|
| 131 |
+
return module
|
| 132 |
+
|
| 133 |
+
@torch.compiler.disable
|
| 134 |
+
def _should_compute_remaining_blocks(self, hidden_states_residual: torch.Tensor) -> bool:
|
| 135 |
+
shared_state = self.state_manager.get_state()
|
| 136 |
+
if shared_state.head_block_residual is None:
|
| 137 |
+
return True
|
| 138 |
+
prev_hidden_states_residual = shared_state.head_block_residual
|
| 139 |
+
absmean = (hidden_states_residual - prev_hidden_states_residual).abs().mean()
|
| 140 |
+
prev_hidden_states_absmean = prev_hidden_states_residual.abs().mean()
|
| 141 |
+
diff = (absmean / prev_hidden_states_absmean).item()
|
| 142 |
+
return diff > self.threshold
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
class FBCBlockHook(ModelHook):
|
| 146 |
+
def __init__(self, state_manager: StateManager, is_tail: bool = False):
|
| 147 |
+
super().__init__()
|
| 148 |
+
self.state_manager = state_manager
|
| 149 |
+
self.is_tail = is_tail
|
| 150 |
+
self._metadata = None
|
| 151 |
+
|
| 152 |
+
def initialize_hook(self, module):
|
| 153 |
+
unwrapped_module = unwrap_module(module)
|
| 154 |
+
self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
|
| 155 |
+
return module
|
| 156 |
+
|
| 157 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 158 |
+
original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
|
| 159 |
+
original_encoder_hidden_states = None
|
| 160 |
+
if self._metadata.return_encoder_hidden_states_index is not None:
|
| 161 |
+
original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
|
| 162 |
+
"encoder_hidden_states", args, kwargs
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
shared_state = self.state_manager.get_state()
|
| 166 |
+
|
| 167 |
+
if shared_state.should_compute:
|
| 168 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 169 |
+
if self.is_tail:
|
| 170 |
+
hidden_states_residual = encoder_hidden_states_residual = None
|
| 171 |
+
if isinstance(output, tuple):
|
| 172 |
+
hidden_states_residual = (
|
| 173 |
+
output[self._metadata.return_hidden_states_index] - shared_state.head_block_output[0]
|
| 174 |
+
)
|
| 175 |
+
encoder_hidden_states_residual = (
|
| 176 |
+
output[self._metadata.return_encoder_hidden_states_index] - shared_state.head_block_output[1]
|
| 177 |
+
)
|
| 178 |
+
else:
|
| 179 |
+
hidden_states_residual = output - shared_state.head_block_output
|
| 180 |
+
shared_state.tail_block_residuals = (hidden_states_residual, encoder_hidden_states_residual)
|
| 181 |
+
return output
|
| 182 |
+
|
| 183 |
+
if original_encoder_hidden_states is None:
|
| 184 |
+
return_output = original_hidden_states
|
| 185 |
+
else:
|
| 186 |
+
return_output = [None, None]
|
| 187 |
+
return_output[self._metadata.return_hidden_states_index] = original_hidden_states
|
| 188 |
+
return_output[self._metadata.return_encoder_hidden_states_index] = original_encoder_hidden_states
|
| 189 |
+
return_output = tuple(return_output)
|
| 190 |
+
return return_output
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def apply_first_block_cache(module: torch.nn.Module, config: FirstBlockCacheConfig) -> None:
|
| 194 |
+
"""
|
| 195 |
+
Applies [First Block
|
| 196 |
+
Cache](https://github.com/chengzeyi/ParaAttention/blob/4de137c5b96416489f06e43e19f2c14a772e28fd/README.md#first-block-cache-our-dynamic-caching)
|
| 197 |
+
to a given module.
|
| 198 |
+
|
| 199 |
+
First Block Cache builds on the ideas of [TeaCache](https://huggingface.co/papers/2411.19108). It is much simpler
|
| 200 |
+
to implement generically for a wide range of models and has been integrated first for experimental purposes.
|
| 201 |
+
|
| 202 |
+
Args:
|
| 203 |
+
module (`torch.nn.Module`):
|
| 204 |
+
The pytorch module to apply FBCache to. Typically, this should be a transformer architecture supported in
|
| 205 |
+
Diffusers, such as `CogVideoXTransformer3DModel`, but external implementations may also work.
|
| 206 |
+
config (`FirstBlockCacheConfig`):
|
| 207 |
+
The configuration to use for applying the FBCache method.
|
| 208 |
+
|
| 209 |
+
Example:
|
| 210 |
+
```python
|
| 211 |
+
>>> import torch
|
| 212 |
+
>>> from diffusers import CogView4Pipeline
|
| 213 |
+
>>> from diffusers.hooks import apply_first_block_cache, FirstBlockCacheConfig
|
| 214 |
+
|
| 215 |
+
>>> pipe = CogView4Pipeline.from_pretrained("THUDM/CogView4-6B", torch_dtype=torch.bfloat16)
|
| 216 |
+
>>> pipe.to("cuda")
|
| 217 |
+
|
| 218 |
+
>>> apply_first_block_cache(pipe.transformer, FirstBlockCacheConfig(threshold=0.2))
|
| 219 |
+
|
| 220 |
+
>>> prompt = "A photo of an astronaut riding a horse on mars"
|
| 221 |
+
>>> image = pipe(prompt, generator=torch.Generator().manual_seed(42)).images[0]
|
| 222 |
+
>>> image.save("output.png")
|
| 223 |
+
```
|
| 224 |
+
"""
|
| 225 |
+
|
| 226 |
+
state_manager = StateManager(FBCSharedBlockState, (), {})
|
| 227 |
+
remaining_blocks = []
|
| 228 |
+
|
| 229 |
+
for name, submodule in module.named_children():
|
| 230 |
+
if name not in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS or not isinstance(submodule, torch.nn.ModuleList):
|
| 231 |
+
continue
|
| 232 |
+
for index, block in enumerate(submodule):
|
| 233 |
+
remaining_blocks.append((f"{name}.{index}", block))
|
| 234 |
+
|
| 235 |
+
head_block_name, head_block = remaining_blocks.pop(0)
|
| 236 |
+
tail_block_name, tail_block = remaining_blocks.pop(-1)
|
| 237 |
+
|
| 238 |
+
logger.debug(f"Applying FBCHeadBlockHook to '{head_block_name}'")
|
| 239 |
+
_apply_fbc_head_block_hook(head_block, state_manager, config.threshold)
|
| 240 |
+
|
| 241 |
+
for name, block in remaining_blocks:
|
| 242 |
+
logger.debug(f"Applying FBCBlockHook to '{name}'")
|
| 243 |
+
_apply_fbc_block_hook(block, state_manager)
|
| 244 |
+
|
| 245 |
+
logger.debug(f"Applying FBCBlockHook to tail block '{tail_block_name}'")
|
| 246 |
+
_apply_fbc_block_hook(tail_block, state_manager, is_tail=True)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def _apply_fbc_head_block_hook(block: torch.nn.Module, state_manager: StateManager, threshold: float) -> None:
|
| 250 |
+
registry = HookRegistry.check_if_exists_or_initialize(block)
|
| 251 |
+
hook = FBCHeadBlockHook(state_manager, threshold)
|
| 252 |
+
registry.register_hook(hook, _FBC_LEADER_BLOCK_HOOK)
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def _apply_fbc_block_hook(block: torch.nn.Module, state_manager: StateManager, is_tail: bool = False) -> None:
|
| 256 |
+
registry = HookRegistry.check_if_exists_or_initialize(block)
|
| 257 |
+
hook = FBCBlockHook(state_manager, is_tail)
|
| 258 |
+
registry.register_hook(hook, _FBC_BLOCK_HOOK)
|
third_party/diffusers/src/diffusers/hooks/group_offloading.py
ADDED
|
@@ -0,0 +1,966 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 hashlib
|
| 16 |
+
import os
|
| 17 |
+
from contextlib import contextmanager, nullcontext
|
| 18 |
+
from dataclasses import dataclass, replace
|
| 19 |
+
from enum import Enum
|
| 20 |
+
from typing import Set
|
| 21 |
+
|
| 22 |
+
import safetensors.torch
|
| 23 |
+
import torch
|
| 24 |
+
|
| 25 |
+
from ..utils import get_logger, is_accelerate_available
|
| 26 |
+
from ._common import _GO_LC_SUPPORTED_PYTORCH_LAYERS
|
| 27 |
+
from .hooks import HookRegistry, ModelHook
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
if is_accelerate_available():
|
| 31 |
+
from accelerate.hooks import AlignDevicesHook, CpuOffload
|
| 32 |
+
from accelerate.utils import send_to_device
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# fmt: off
|
| 39 |
+
_GROUP_OFFLOADING = "group_offloading"
|
| 40 |
+
_LAYER_EXECUTION_TRACKER = "layer_execution_tracker"
|
| 41 |
+
_LAZY_PREFETCH_GROUP_OFFLOADING = "lazy_prefetch_group_offloading"
|
| 42 |
+
_GROUP_ID_LAZY_LEAF = "lazy_leafs"
|
| 43 |
+
# fmt: on
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class GroupOffloadingType(str, Enum):
|
| 47 |
+
BLOCK_LEVEL = "block_level"
|
| 48 |
+
LEAF_LEVEL = "leaf_level"
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
@dataclass
|
| 52 |
+
class GroupOffloadingConfig:
|
| 53 |
+
onload_device: torch.device
|
| 54 |
+
offload_device: torch.device
|
| 55 |
+
offload_type: GroupOffloadingType
|
| 56 |
+
non_blocking: bool
|
| 57 |
+
record_stream: bool
|
| 58 |
+
low_cpu_mem_usage: bool
|
| 59 |
+
num_blocks_per_group: int | None = None
|
| 60 |
+
offload_to_disk_path: str | None = None
|
| 61 |
+
stream: torch.cuda.Stream | torch.Stream | None = None
|
| 62 |
+
block_modules: list[str] | None = None
|
| 63 |
+
exclude_kwargs: list[str] | None = None
|
| 64 |
+
module_prefix: str = ""
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class ModuleGroup:
|
| 68 |
+
def __init__(
|
| 69 |
+
self,
|
| 70 |
+
modules: list[torch.nn.Module],
|
| 71 |
+
offload_device: torch.device,
|
| 72 |
+
onload_device: torch.device,
|
| 73 |
+
offload_leader: torch.nn.Module,
|
| 74 |
+
onload_leader: torch.nn.Module | None = None,
|
| 75 |
+
parameters: list[torch.nn.Parameter] | None = None,
|
| 76 |
+
buffers: list[torch.Tensor] | None = None,
|
| 77 |
+
non_blocking: bool = False,
|
| 78 |
+
stream: torch.cuda.Stream | torch.Stream | None = None,
|
| 79 |
+
record_stream: bool | None = False,
|
| 80 |
+
low_cpu_mem_usage: bool = False,
|
| 81 |
+
onload_self: bool = True,
|
| 82 |
+
offload_to_disk_path: str | None = None,
|
| 83 |
+
group_id: int | str | None = None,
|
| 84 |
+
) -> None:
|
| 85 |
+
self.modules = modules
|
| 86 |
+
self.offload_device = offload_device
|
| 87 |
+
self.onload_device = onload_device
|
| 88 |
+
self.offload_leader = offload_leader
|
| 89 |
+
self.onload_leader = onload_leader
|
| 90 |
+
self.parameters = parameters or []
|
| 91 |
+
self.buffers = buffers or []
|
| 92 |
+
self.non_blocking = non_blocking or stream is not None
|
| 93 |
+
self.stream = stream
|
| 94 |
+
self.record_stream = record_stream
|
| 95 |
+
self.onload_self = onload_self
|
| 96 |
+
self.low_cpu_mem_usage = low_cpu_mem_usage
|
| 97 |
+
|
| 98 |
+
self.offload_to_disk_path = offload_to_disk_path
|
| 99 |
+
self._is_offloaded_to_disk = False
|
| 100 |
+
|
| 101 |
+
if self.offload_to_disk_path is not None:
|
| 102 |
+
# Instead of `group_id or str(id(self))` we do this because `group_id` can be "" as well.
|
| 103 |
+
self.group_id = group_id if group_id is not None else str(id(self))
|
| 104 |
+
short_hash = _compute_group_hash(self.group_id)
|
| 105 |
+
self.safetensors_file_path = os.path.join(self.offload_to_disk_path, f"group_{short_hash}.safetensors")
|
| 106 |
+
|
| 107 |
+
all_tensors = []
|
| 108 |
+
for module in self.modules:
|
| 109 |
+
all_tensors.extend(list(module.parameters()))
|
| 110 |
+
all_tensors.extend(list(module.buffers()))
|
| 111 |
+
all_tensors.extend(self.parameters)
|
| 112 |
+
all_tensors.extend(self.buffers)
|
| 113 |
+
all_tensors = list(dict.fromkeys(all_tensors)) # Remove duplicates
|
| 114 |
+
|
| 115 |
+
self.tensor_to_key = {tensor: f"tensor_{i}" for i, tensor in enumerate(all_tensors)}
|
| 116 |
+
self.key_to_tensor = {v: k for k, v in self.tensor_to_key.items()}
|
| 117 |
+
self.cpu_param_dict = {}
|
| 118 |
+
else:
|
| 119 |
+
self.cpu_param_dict = self._init_cpu_param_dict()
|
| 120 |
+
|
| 121 |
+
self._torch_accelerator_module = (
|
| 122 |
+
getattr(torch, torch.accelerator.current_accelerator().type)
|
| 123 |
+
if hasattr(torch, "accelerator")
|
| 124 |
+
else torch.cuda
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
def _init_cpu_param_dict(self):
|
| 128 |
+
cpu_param_dict = {}
|
| 129 |
+
if self.stream is None:
|
| 130 |
+
return cpu_param_dict
|
| 131 |
+
|
| 132 |
+
for module in self.modules:
|
| 133 |
+
for param in module.parameters():
|
| 134 |
+
cpu_param_dict[param] = param.data.cpu() if self.low_cpu_mem_usage else param.data.cpu().pin_memory()
|
| 135 |
+
for buffer in module.buffers():
|
| 136 |
+
cpu_param_dict[buffer] = (
|
| 137 |
+
buffer.data.cpu() if self.low_cpu_mem_usage else buffer.data.cpu().pin_memory()
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
for param in self.parameters:
|
| 141 |
+
cpu_param_dict[param] = param.data.cpu() if self.low_cpu_mem_usage else param.data.cpu().pin_memory()
|
| 142 |
+
|
| 143 |
+
for buffer in self.buffers:
|
| 144 |
+
cpu_param_dict[buffer] = buffer.data.cpu() if self.low_cpu_mem_usage else buffer.data.cpu().pin_memory()
|
| 145 |
+
|
| 146 |
+
return cpu_param_dict
|
| 147 |
+
|
| 148 |
+
@contextmanager
|
| 149 |
+
def _pinned_memory_tensors(self):
|
| 150 |
+
try:
|
| 151 |
+
pinned_dict = {
|
| 152 |
+
param: tensor.pin_memory() if not tensor.is_pinned() else tensor
|
| 153 |
+
for param, tensor in self.cpu_param_dict.items()
|
| 154 |
+
}
|
| 155 |
+
yield pinned_dict
|
| 156 |
+
finally:
|
| 157 |
+
pinned_dict = None
|
| 158 |
+
|
| 159 |
+
def _transfer_tensor_to_device(self, tensor, source_tensor, default_stream):
|
| 160 |
+
tensor.data = source_tensor.to(self.onload_device, non_blocking=self.non_blocking)
|
| 161 |
+
if self.record_stream:
|
| 162 |
+
tensor.data.record_stream(default_stream)
|
| 163 |
+
|
| 164 |
+
def _process_tensors_from_modules(self, pinned_memory=None, default_stream=None):
|
| 165 |
+
for group_module in self.modules:
|
| 166 |
+
for param in group_module.parameters():
|
| 167 |
+
source = pinned_memory[param] if pinned_memory else param.data
|
| 168 |
+
self._transfer_tensor_to_device(param, source, default_stream)
|
| 169 |
+
for buffer in group_module.buffers():
|
| 170 |
+
source = pinned_memory[buffer] if pinned_memory else buffer.data
|
| 171 |
+
self._transfer_tensor_to_device(buffer, source, default_stream)
|
| 172 |
+
|
| 173 |
+
for param in self.parameters:
|
| 174 |
+
source = pinned_memory[param] if pinned_memory else param.data
|
| 175 |
+
self._transfer_tensor_to_device(param, source, default_stream)
|
| 176 |
+
|
| 177 |
+
for buffer in self.buffers:
|
| 178 |
+
source = pinned_memory[buffer] if pinned_memory else buffer.data
|
| 179 |
+
self._transfer_tensor_to_device(buffer, source, default_stream)
|
| 180 |
+
|
| 181 |
+
def _onload_from_disk(self):
|
| 182 |
+
if self.stream is not None:
|
| 183 |
+
# Wait for previous Host->Device transfer to complete
|
| 184 |
+
self.stream.synchronize()
|
| 185 |
+
|
| 186 |
+
context = nullcontext() if self.stream is None else self._torch_accelerator_module.stream(self.stream)
|
| 187 |
+
current_stream = self._torch_accelerator_module.current_stream() if self.record_stream else None
|
| 188 |
+
|
| 189 |
+
with context:
|
| 190 |
+
# Load to CPU (if using streams) or directly to target device, pin, and async copy to device
|
| 191 |
+
device = str(self.onload_device) if self.stream is None else "cpu"
|
| 192 |
+
loaded_tensors = safetensors.torch.load_file(self.safetensors_file_path, device=device)
|
| 193 |
+
|
| 194 |
+
if self.stream is not None:
|
| 195 |
+
for key, tensor_obj in self.key_to_tensor.items():
|
| 196 |
+
pinned_tensor = loaded_tensors[key].pin_memory()
|
| 197 |
+
tensor_obj.data = pinned_tensor.to(self.onload_device, non_blocking=self.non_blocking)
|
| 198 |
+
if self.record_stream:
|
| 199 |
+
tensor_obj.data.record_stream(current_stream)
|
| 200 |
+
else:
|
| 201 |
+
onload_device = (
|
| 202 |
+
self.onload_device.type if isinstance(self.onload_device, torch.device) else self.onload_device
|
| 203 |
+
)
|
| 204 |
+
loaded_tensors = safetensors.torch.load_file(self.safetensors_file_path, device=onload_device)
|
| 205 |
+
for key, tensor_obj in self.key_to_tensor.items():
|
| 206 |
+
tensor_obj.data = loaded_tensors[key]
|
| 207 |
+
|
| 208 |
+
def _onload_from_memory(self):
|
| 209 |
+
if self.stream is not None:
|
| 210 |
+
# Wait for previous Host->Device transfer to complete
|
| 211 |
+
self.stream.synchronize()
|
| 212 |
+
|
| 213 |
+
context = nullcontext() if self.stream is None else self._torch_accelerator_module.stream(self.stream)
|
| 214 |
+
default_stream = self._torch_accelerator_module.current_stream() if self.stream is not None else None
|
| 215 |
+
|
| 216 |
+
with context:
|
| 217 |
+
if self.stream is not None:
|
| 218 |
+
with self._pinned_memory_tensors() as pinned_memory:
|
| 219 |
+
self._process_tensors_from_modules(pinned_memory, default_stream=default_stream)
|
| 220 |
+
else:
|
| 221 |
+
self._process_tensors_from_modules(None)
|
| 222 |
+
|
| 223 |
+
def _offload_to_disk(self):
|
| 224 |
+
# TODO: we can potentially optimize this code path by checking if the _all_ the desired
|
| 225 |
+
# safetensor files exist on the disk and if so, skip this step entirely, reducing IO
|
| 226 |
+
# overhead. Currently, we just check if the given `safetensors_file_path` exists and if not
|
| 227 |
+
# we perform a write.
|
| 228 |
+
# Check if the file has been saved in this session or if it already exists on disk.
|
| 229 |
+
if not self._is_offloaded_to_disk and not os.path.exists(self.safetensors_file_path):
|
| 230 |
+
os.makedirs(os.path.dirname(self.safetensors_file_path), exist_ok=True)
|
| 231 |
+
tensors_to_save = {key: tensor.data.to(self.offload_device) for tensor, key in self.tensor_to_key.items()}
|
| 232 |
+
safetensors.torch.save_file(tensors_to_save, self.safetensors_file_path)
|
| 233 |
+
|
| 234 |
+
# The group is now considered offloaded to disk for the rest of the session.
|
| 235 |
+
self._is_offloaded_to_disk = True
|
| 236 |
+
|
| 237 |
+
# We do this to free up the RAM which is still holding the up tensor data.
|
| 238 |
+
for tensor_obj in self.tensor_to_key.keys():
|
| 239 |
+
tensor_obj.data = torch.empty_like(tensor_obj.data, device=self.offload_device)
|
| 240 |
+
|
| 241 |
+
def _offload_to_memory(self):
|
| 242 |
+
if self.stream is not None:
|
| 243 |
+
if not self.record_stream:
|
| 244 |
+
self._torch_accelerator_module.current_stream().synchronize()
|
| 245 |
+
|
| 246 |
+
for group_module in self.modules:
|
| 247 |
+
for param in group_module.parameters():
|
| 248 |
+
param.data = self.cpu_param_dict[param]
|
| 249 |
+
for param in self.parameters:
|
| 250 |
+
param.data = self.cpu_param_dict[param]
|
| 251 |
+
for buffer in self.buffers:
|
| 252 |
+
buffer.data = self.cpu_param_dict[buffer]
|
| 253 |
+
else:
|
| 254 |
+
for group_module in self.modules:
|
| 255 |
+
group_module.to(self.offload_device, non_blocking=False)
|
| 256 |
+
for param in self.parameters:
|
| 257 |
+
param.data = param.data.to(self.offload_device, non_blocking=False)
|
| 258 |
+
for buffer in self.buffers:
|
| 259 |
+
buffer.data = buffer.data.to(self.offload_device, non_blocking=False)
|
| 260 |
+
|
| 261 |
+
@torch.compiler.disable()
|
| 262 |
+
def onload_(self):
|
| 263 |
+
r"""Onloads the group of parameters to the onload_device."""
|
| 264 |
+
if self.offload_to_disk_path is not None:
|
| 265 |
+
self._onload_from_disk()
|
| 266 |
+
else:
|
| 267 |
+
self._onload_from_memory()
|
| 268 |
+
|
| 269 |
+
@torch.compiler.disable()
|
| 270 |
+
def offload_(self):
|
| 271 |
+
r"""Offloads the group of parameters to the offload_device."""
|
| 272 |
+
if self.offload_to_disk_path:
|
| 273 |
+
self._offload_to_disk()
|
| 274 |
+
else:
|
| 275 |
+
self._offload_to_memory()
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
class GroupOffloadingHook(ModelHook):
|
| 279 |
+
r"""
|
| 280 |
+
A hook that offloads groups of torch.nn.Module to the CPU for storage and onloads to accelerator device for
|
| 281 |
+
computation. Each group has one "onload leader" module that is responsible for onloading, and an "offload leader"
|
| 282 |
+
module that is responsible for offloading. If prefetching is enabled, the onload leader of the previous module
|
| 283 |
+
group is responsible for onloading the current module group.
|
| 284 |
+
"""
|
| 285 |
+
|
| 286 |
+
_is_stateful = False
|
| 287 |
+
|
| 288 |
+
def __init__(self, group: ModuleGroup, *, config: GroupOffloadingConfig) -> None:
|
| 289 |
+
self.group = group
|
| 290 |
+
self.next_group: ModuleGroup | None = None
|
| 291 |
+
self.config = config
|
| 292 |
+
|
| 293 |
+
def initialize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 294 |
+
if self.group.offload_leader == module:
|
| 295 |
+
self.group.offload_()
|
| 296 |
+
return module
|
| 297 |
+
|
| 298 |
+
def pre_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 299 |
+
# If there wasn't an onload_leader assigned, we assume that the submodule that first called its forward
|
| 300 |
+
# method is the onload_leader of the group.
|
| 301 |
+
if self.group.onload_leader is None:
|
| 302 |
+
self.group.onload_leader = module
|
| 303 |
+
|
| 304 |
+
# If the current module is the onload_leader of the group, we onload the group if it is supposed
|
| 305 |
+
# to onload itself. In the case of using prefetching with streams, we onload the next group if
|
| 306 |
+
# it is not supposed to onload itself.
|
| 307 |
+
if self.group.onload_leader == module:
|
| 308 |
+
if self.group.onload_self:
|
| 309 |
+
self.group.onload_()
|
| 310 |
+
else:
|
| 311 |
+
# onload_self=False means this group relies on prefetching from a previous group.
|
| 312 |
+
# However, for conditionally-executed modules (e.g. patch_short/patch_mid/patch_long in Helios),
|
| 313 |
+
# the prefetch chain may not cover them if they were absent during the first forward pass
|
| 314 |
+
# when the execution order was traced. In that case, their weights remain on offload_device,
|
| 315 |
+
# so we fall back to a synchronous onload here.
|
| 316 |
+
params = [p for m in self.group.modules for p in m.parameters()] + list(self.group.parameters)
|
| 317 |
+
if params and params[0].device == self.group.offload_device:
|
| 318 |
+
self.group.onload_()
|
| 319 |
+
if self.group.stream is not None:
|
| 320 |
+
self.group.stream.synchronize()
|
| 321 |
+
|
| 322 |
+
should_onload_next_group = self.next_group is not None and not self.next_group.onload_self
|
| 323 |
+
if should_onload_next_group:
|
| 324 |
+
self.next_group.onload_()
|
| 325 |
+
|
| 326 |
+
should_synchronize = (
|
| 327 |
+
not self.group.onload_self and self.group.stream is not None and not should_onload_next_group
|
| 328 |
+
)
|
| 329 |
+
if should_synchronize:
|
| 330 |
+
# If this group didn't onload itself, it means it was asynchronously onloaded by the
|
| 331 |
+
# previous group. We need to synchronize the side stream to ensure parameters
|
| 332 |
+
# are completely loaded to proceed with forward pass. Without this, uninitialized
|
| 333 |
+
# weights will be used in the computation, leading to incorrect results
|
| 334 |
+
# Also, we should only do this synchronization if we don't already do it from the sync call in
|
| 335 |
+
# self.next_group.onload_, hence the `not should_onload_next_group` check.
|
| 336 |
+
self.group.stream.synchronize()
|
| 337 |
+
|
| 338 |
+
args = send_to_device(args, self.group.onload_device, non_blocking=self.group.non_blocking)
|
| 339 |
+
|
| 340 |
+
# Some Autoencoder models use a feature cache that is passed through submodules
|
| 341 |
+
# and modified in place. The `send_to_device` call returns a copy of this feature cache object
|
| 342 |
+
# which breaks the inplace updates. Use `exclude_kwargs` to mark these cache features
|
| 343 |
+
exclude_kwargs = self.config.exclude_kwargs or []
|
| 344 |
+
if exclude_kwargs:
|
| 345 |
+
moved_kwargs = send_to_device(
|
| 346 |
+
{k: v for k, v in kwargs.items() if k not in exclude_kwargs},
|
| 347 |
+
self.group.onload_device,
|
| 348 |
+
non_blocking=self.group.non_blocking,
|
| 349 |
+
)
|
| 350 |
+
kwargs.update(moved_kwargs)
|
| 351 |
+
else:
|
| 352 |
+
kwargs = send_to_device(kwargs, self.group.onload_device, non_blocking=self.group.non_blocking)
|
| 353 |
+
|
| 354 |
+
return args, kwargs
|
| 355 |
+
|
| 356 |
+
def post_forward(self, module: torch.nn.Module, output):
|
| 357 |
+
if self.group.offload_leader == module:
|
| 358 |
+
self.group.offload_()
|
| 359 |
+
return output
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
class LazyPrefetchGroupOffloadingHook(ModelHook):
|
| 363 |
+
r"""
|
| 364 |
+
A hook, used in conjunction with GroupOffloadingHook, that applies lazy prefetching to groups of torch.nn.Module.
|
| 365 |
+
This hook is used to determine the order in which the layers are executed during the forward pass. Once the layer
|
| 366 |
+
invocation order is known, assignments of the next_group attribute for prefetching can be made, which allows
|
| 367 |
+
prefetching groups in the correct order.
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
_is_stateful = False
|
| 371 |
+
|
| 372 |
+
def __init__(self):
|
| 373 |
+
self.execution_order: list[tuple[str, torch.nn.Module]] = []
|
| 374 |
+
self._layer_execution_tracker_module_names = set()
|
| 375 |
+
|
| 376 |
+
def initialize_hook(self, module):
|
| 377 |
+
def make_execution_order_update_callback(current_name, current_submodule):
|
| 378 |
+
def callback():
|
| 379 |
+
if not torch.compiler.is_compiling():
|
| 380 |
+
logger.debug(f"Adding {current_name} to the execution order")
|
| 381 |
+
self.execution_order.append((current_name, current_submodule))
|
| 382 |
+
|
| 383 |
+
return callback
|
| 384 |
+
|
| 385 |
+
# To every submodule that contains a group offloading hook (at this point, no prefetching is enabled for any
|
| 386 |
+
# of the groups), we add a layer execution tracker hook that will be used to determine the order in which the
|
| 387 |
+
# layers are executed during the forward pass.
|
| 388 |
+
for name, submodule in module.named_modules():
|
| 389 |
+
if name == "" or not hasattr(submodule, "_diffusers_hook"):
|
| 390 |
+
continue
|
| 391 |
+
|
| 392 |
+
registry = HookRegistry.check_if_exists_or_initialize(submodule)
|
| 393 |
+
group_offloading_hook = registry.get_hook(_GROUP_OFFLOADING)
|
| 394 |
+
|
| 395 |
+
if group_offloading_hook is not None:
|
| 396 |
+
# For the first forward pass, we have to load in a blocking manner
|
| 397 |
+
group_offloading_hook.group.non_blocking = False
|
| 398 |
+
layer_tracker_hook = LayerExecutionTrackerHook(make_execution_order_update_callback(name, submodule))
|
| 399 |
+
registry.register_hook(layer_tracker_hook, _LAYER_EXECUTION_TRACKER)
|
| 400 |
+
self._layer_execution_tracker_module_names.add(name)
|
| 401 |
+
|
| 402 |
+
return module
|
| 403 |
+
|
| 404 |
+
def post_forward(self, module, output):
|
| 405 |
+
# At this point, for the current modules' submodules, we know the execution order of the layers. We can now
|
| 406 |
+
# remove the layer execution tracker hooks and apply prefetching by setting the next_group attribute for each
|
| 407 |
+
# group offloading hook.
|
| 408 |
+
num_executed = len(self.execution_order)
|
| 409 |
+
execution_order_module_names = {name for name, _ in self.execution_order}
|
| 410 |
+
|
| 411 |
+
# It may be possible that some layers were not executed during the forward pass. This can happen if the layer
|
| 412 |
+
# is not used in the forward pass, or if the layer is not executed due to some other reason. In such cases, we
|
| 413 |
+
# may not be able to apply prefetching in the correct order, which can lead to device-mismatch related errors
|
| 414 |
+
# if the missing layers end up being executed in the future.
|
| 415 |
+
if execution_order_module_names != self._layer_execution_tracker_module_names:
|
| 416 |
+
unexecuted_layers = list(self._layer_execution_tracker_module_names - execution_order_module_names)
|
| 417 |
+
if not torch.compiler.is_compiling():
|
| 418 |
+
logger.warning(
|
| 419 |
+
"It seems like some layers were not executed during the forward pass. This may lead to problems when "
|
| 420 |
+
"applying lazy prefetching with automatic tracing and lead to device-mismatch related errors. Please "
|
| 421 |
+
"make sure that all layers are executed during the forward pass. The following layers were not executed:\n"
|
| 422 |
+
f"{unexecuted_layers=}"
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# Remove the layer execution tracker hooks from the submodules
|
| 426 |
+
base_module_registry = module._diffusers_hook
|
| 427 |
+
registries = [submodule._diffusers_hook for _, submodule in self.execution_order]
|
| 428 |
+
group_offloading_hooks = [registry.get_hook(_GROUP_OFFLOADING) for registry in registries]
|
| 429 |
+
|
| 430 |
+
for i in range(num_executed):
|
| 431 |
+
registries[i].remove_hook(_LAYER_EXECUTION_TRACKER, recurse=False)
|
| 432 |
+
|
| 433 |
+
# Remove the current lazy prefetch group offloading hook so that it doesn't interfere with the next forward pass
|
| 434 |
+
base_module_registry.remove_hook(_LAZY_PREFETCH_GROUP_OFFLOADING, recurse=False)
|
| 435 |
+
|
| 436 |
+
# LazyPrefetchGroupOffloadingHook is only used with streams, so we know that non_blocking should be True.
|
| 437 |
+
# We disable non_blocking for the first forward pass, but need to enable it for the subsequent passes to
|
| 438 |
+
# see the benefits of prefetching.
|
| 439 |
+
for hook in group_offloading_hooks:
|
| 440 |
+
hook.group.non_blocking = True
|
| 441 |
+
|
| 442 |
+
# Set required attributes for prefetching
|
| 443 |
+
if num_executed > 0:
|
| 444 |
+
base_module_group_offloading_hook = base_module_registry.get_hook(_GROUP_OFFLOADING)
|
| 445 |
+
base_module_group_offloading_hook.next_group = group_offloading_hooks[0].group
|
| 446 |
+
base_module_group_offloading_hook.next_group.onload_self = False
|
| 447 |
+
|
| 448 |
+
for i in range(num_executed - 1):
|
| 449 |
+
name1, _ = self.execution_order[i]
|
| 450 |
+
name2, _ = self.execution_order[i + 1]
|
| 451 |
+
if not torch.compiler.is_compiling():
|
| 452 |
+
logger.debug(f"Applying lazy prefetch group offloading from {name1} to {name2}")
|
| 453 |
+
group_offloading_hooks[i].next_group = group_offloading_hooks[i + 1].group
|
| 454 |
+
group_offloading_hooks[i].next_group.onload_self = False
|
| 455 |
+
|
| 456 |
+
return output
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
class LayerExecutionTrackerHook(ModelHook):
|
| 460 |
+
r"""
|
| 461 |
+
A hook that tracks the order in which the layers are executed during the forward pass by calling back to the
|
| 462 |
+
LazyPrefetchGroupOffloadingHook to update the execution order.
|
| 463 |
+
"""
|
| 464 |
+
|
| 465 |
+
_is_stateful = False
|
| 466 |
+
|
| 467 |
+
def __init__(self, execution_order_update_callback):
|
| 468 |
+
self.execution_order_update_callback = execution_order_update_callback
|
| 469 |
+
|
| 470 |
+
def pre_forward(self, module, *args, **kwargs):
|
| 471 |
+
self.execution_order_update_callback()
|
| 472 |
+
return args, kwargs
|
| 473 |
+
|
| 474 |
+
|
| 475 |
+
def apply_group_offloading(
|
| 476 |
+
module: torch.nn.Module,
|
| 477 |
+
onload_device: str | torch.device,
|
| 478 |
+
offload_device: str | torch.device = torch.device("cpu"),
|
| 479 |
+
offload_type: str | GroupOffloadingType = "block_level",
|
| 480 |
+
num_blocks_per_group: int | None = None,
|
| 481 |
+
non_blocking: bool = False,
|
| 482 |
+
use_stream: bool = False,
|
| 483 |
+
record_stream: bool = False,
|
| 484 |
+
low_cpu_mem_usage: bool = False,
|
| 485 |
+
offload_to_disk_path: str | None = None,
|
| 486 |
+
block_modules: list[str] | None = None,
|
| 487 |
+
exclude_kwargs: list[str] | None = None,
|
| 488 |
+
) -> None:
|
| 489 |
+
r"""
|
| 490 |
+
Applies group offloading to the internal layers of a torch.nn.Module. To understand what group offloading is, and
|
| 491 |
+
where it is beneficial, we need to first provide some context on how other supported offloading methods work.
|
| 492 |
+
|
| 493 |
+
Typically, offloading is done at two levels:
|
| 494 |
+
- Module-level: In Diffusers, this can be enabled using the `ModelMixin::enable_model_cpu_offload()` method. It
|
| 495 |
+
works by offloading each component of a pipeline to the CPU for storage, and onloading to the accelerator device
|
| 496 |
+
when needed for computation. This method is more memory-efficient than keeping all components on the accelerator,
|
| 497 |
+
but the memory requirements are still quite high. For this method to work, one needs memory equivalent to size of
|
| 498 |
+
the model in runtime dtype + size of largest intermediate activation tensors to be able to complete the forward
|
| 499 |
+
pass.
|
| 500 |
+
- Leaf-level: In Diffusers, this can be enabled using the `ModelMixin::enable_sequential_cpu_offload()` method. It
|
| 501 |
+
works by offloading the lowest leaf-level parameters of the computation graph to the CPU for storage, and
|
| 502 |
+
onloading only the leafs to the accelerator device for computation. This uses the lowest amount of accelerator
|
| 503 |
+
memory, but can be slower due to the excessive number of device synchronizations.
|
| 504 |
+
|
| 505 |
+
Group offloading is a middle ground between the two methods. It works by offloading groups of internal layers,
|
| 506 |
+
(either `torch.nn.ModuleList` or `torch.nn.Sequential`). This method uses lower memory than module-level
|
| 507 |
+
offloading. It is also faster than leaf-level/sequential offloading, as the number of device synchronizations is
|
| 508 |
+
reduced.
|
| 509 |
+
|
| 510 |
+
Another supported feature (for CUDA devices with support for asynchronous data transfer streams) is the ability to
|
| 511 |
+
overlap data transfer and computation to reduce the overall execution time compared to sequential offloading. This
|
| 512 |
+
is enabled using layer prefetching with streams, i.e., the layer that is to be executed next starts onloading to
|
| 513 |
+
the accelerator device while the current layer is being executed - this increases the memory requirements slightly.
|
| 514 |
+
Note that this implementation also supports leaf-level offloading but can be made much faster when using streams.
|
| 515 |
+
|
| 516 |
+
Args:
|
| 517 |
+
module (`torch.nn.Module`):
|
| 518 |
+
The module to which group offloading is applied.
|
| 519 |
+
onload_device (`torch.device`):
|
| 520 |
+
The device to which the group of modules are onloaded.
|
| 521 |
+
offload_device (`torch.device`, defaults to `torch.device("cpu")`):
|
| 522 |
+
The device to which the group of modules are offloaded. This should typically be the CPU. Default is CPU.
|
| 523 |
+
offload_type (`str` or `GroupOffloadingType`, defaults to "block_level"):
|
| 524 |
+
The type of offloading to be applied. Can be one of "block_level" or "leaf_level". Default is
|
| 525 |
+
"block_level".
|
| 526 |
+
offload_to_disk_path (`str`, *optional*, defaults to `None`):
|
| 527 |
+
The path to the directory where parameters will be offloaded. Setting this option can be useful in limited
|
| 528 |
+
RAM environment settings where a reasonable speed-memory trade-off is desired.
|
| 529 |
+
num_blocks_per_group (`int`, *optional*):
|
| 530 |
+
The number of blocks per group when using offload_type="block_level". This is required when using
|
| 531 |
+
offload_type="block_level".
|
| 532 |
+
non_blocking (`bool`, defaults to `False`):
|
| 533 |
+
If True, offloading and onloading is done with non-blocking data transfer.
|
| 534 |
+
use_stream (`bool`, defaults to `False`):
|
| 535 |
+
If True, offloading and onloading is done asynchronously using a CUDA stream. This can be useful for
|
| 536 |
+
overlapping computation and data transfer.
|
| 537 |
+
record_stream (`bool`, defaults to `False`): When enabled with `use_stream`, it marks the current tensor
|
| 538 |
+
as having been used by this stream. It is faster at the expense of slightly more memory usage. Refer to the
|
| 539 |
+
[PyTorch official docs](https://pytorch.org/docs/stable/generated/torch.Tensor.record_stream.html) more
|
| 540 |
+
details.
|
| 541 |
+
low_cpu_mem_usage (`bool`, defaults to `False`):
|
| 542 |
+
If True, the CPU memory usage is minimized by pinning tensors on-the-fly instead of pre-pinning them. This
|
| 543 |
+
option only matters when using streamed CPU offloading (i.e. `use_stream=True`). This can be useful when
|
| 544 |
+
the CPU memory is a bottleneck but may counteract the benefits of using streams.
|
| 545 |
+
block_modules (`list[str]`, *optional*):
|
| 546 |
+
List of module names that should be treated as blocks for offloading. If provided, only these modules will
|
| 547 |
+
be considered for block-level offloading. If not provided, the default block detection logic will be used.
|
| 548 |
+
exclude_kwargs (`list[str]`, *optional*):
|
| 549 |
+
List of kwarg keys that should not be processed by send_to_device. This is useful for mutable state like
|
| 550 |
+
caching lists that need to maintain their object identity across forward passes. If not provided, will be
|
| 551 |
+
inferred from the module's `_skip_keys` attribute if it exists.
|
| 552 |
+
|
| 553 |
+
Example:
|
| 554 |
+
```python
|
| 555 |
+
>>> from diffusers import CogVideoXTransformer3DModel
|
| 556 |
+
>>> from diffusers.hooks import apply_group_offloading
|
| 557 |
+
|
| 558 |
+
>>> transformer = CogVideoXTransformer3DModel.from_pretrained(
|
| 559 |
+
... "THUDM/CogVideoX-5b", subfolder="transformer", torch_dtype=torch.bfloat16
|
| 560 |
+
... )
|
| 561 |
+
|
| 562 |
+
>>> apply_group_offloading(
|
| 563 |
+
... transformer,
|
| 564 |
+
... onload_device=torch.device("cuda"),
|
| 565 |
+
... offload_device=torch.device("cpu"),
|
| 566 |
+
... offload_type="block_level",
|
| 567 |
+
... num_blocks_per_group=2,
|
| 568 |
+
... use_stream=True,
|
| 569 |
+
... )
|
| 570 |
+
```
|
| 571 |
+
"""
|
| 572 |
+
|
| 573 |
+
onload_device = torch.device(onload_device) if isinstance(onload_device, str) else onload_device
|
| 574 |
+
offload_device = torch.device(offload_device) if isinstance(offload_device, str) else offload_device
|
| 575 |
+
offload_type = GroupOffloadingType(offload_type)
|
| 576 |
+
|
| 577 |
+
stream = None
|
| 578 |
+
if use_stream:
|
| 579 |
+
if torch.cuda.is_available():
|
| 580 |
+
stream = torch.cuda.Stream()
|
| 581 |
+
elif hasattr(torch, "xpu") and torch.xpu.is_available():
|
| 582 |
+
stream = torch.Stream()
|
| 583 |
+
else:
|
| 584 |
+
raise ValueError("Using streams for data transfer requires a CUDA device, or an Intel XPU device.")
|
| 585 |
+
|
| 586 |
+
if not use_stream and record_stream:
|
| 587 |
+
raise ValueError("`record_stream` cannot be True when `use_stream=False`.")
|
| 588 |
+
if offload_type == GroupOffloadingType.BLOCK_LEVEL and num_blocks_per_group is None:
|
| 589 |
+
raise ValueError("`num_blocks_per_group` must be provided when using `offload_type='block_level'.")
|
| 590 |
+
|
| 591 |
+
_raise_error_if_accelerate_model_or_sequential_hook_present(module)
|
| 592 |
+
|
| 593 |
+
if block_modules is None:
|
| 594 |
+
block_modules = getattr(module, "_group_offload_block_modules", None)
|
| 595 |
+
|
| 596 |
+
if exclude_kwargs is None:
|
| 597 |
+
exclude_kwargs = getattr(module, "_skip_keys", None)
|
| 598 |
+
|
| 599 |
+
config = GroupOffloadingConfig(
|
| 600 |
+
onload_device=onload_device,
|
| 601 |
+
offload_device=offload_device,
|
| 602 |
+
offload_type=offload_type,
|
| 603 |
+
num_blocks_per_group=num_blocks_per_group,
|
| 604 |
+
non_blocking=non_blocking,
|
| 605 |
+
stream=stream,
|
| 606 |
+
record_stream=record_stream,
|
| 607 |
+
low_cpu_mem_usage=low_cpu_mem_usage,
|
| 608 |
+
offload_to_disk_path=offload_to_disk_path,
|
| 609 |
+
block_modules=block_modules,
|
| 610 |
+
exclude_kwargs=exclude_kwargs,
|
| 611 |
+
)
|
| 612 |
+
_apply_group_offloading(module, config)
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
def _apply_group_offloading(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
|
| 616 |
+
if config.offload_type == GroupOffloadingType.BLOCK_LEVEL:
|
| 617 |
+
_apply_group_offloading_block_level(module, config)
|
| 618 |
+
elif config.offload_type == GroupOffloadingType.LEAF_LEVEL:
|
| 619 |
+
_apply_group_offloading_leaf_level(module, config)
|
| 620 |
+
else:
|
| 621 |
+
assert False
|
| 622 |
+
|
| 623 |
+
|
| 624 |
+
def _apply_group_offloading_block_level(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
|
| 625 |
+
r"""
|
| 626 |
+
This function applies offloading to groups of torch.nn.ModuleList or torch.nn.Sequential blocks, and explicitly
|
| 627 |
+
defined block modules. In comparison to the "leaf_level" offloading, which is more fine-grained, this offloading is
|
| 628 |
+
done at the top-level blocks and modules specified in block_modules.
|
| 629 |
+
|
| 630 |
+
When block_modules is provided, only those modules will be treated as blocks for offloading. For each specified
|
| 631 |
+
module, recursively apply block offloading to it.
|
| 632 |
+
"""
|
| 633 |
+
if config.stream is not None and config.num_blocks_per_group != 1:
|
| 634 |
+
logger.warning(
|
| 635 |
+
f"Using streams is only supported for num_blocks_per_group=1. Got {config.num_blocks_per_group=}. Setting it to 1."
|
| 636 |
+
)
|
| 637 |
+
config.num_blocks_per_group = 1
|
| 638 |
+
|
| 639 |
+
block_modules = set(config.block_modules) if config.block_modules is not None else set()
|
| 640 |
+
|
| 641 |
+
# Create module groups for ModuleList and Sequential blocks, and explicitly defined block modules
|
| 642 |
+
modules_with_group_offloading = set()
|
| 643 |
+
unmatched_modules = []
|
| 644 |
+
matched_module_groups = []
|
| 645 |
+
|
| 646 |
+
for name, submodule in module.named_children():
|
| 647 |
+
# Check if this is an explicitly defined block module
|
| 648 |
+
if name in block_modules:
|
| 649 |
+
# Track submodule using a prefix to avoid filename collisions during disk offload.
|
| 650 |
+
# Without this, submodules sharing the same model class would be assigned identical
|
| 651 |
+
# filenames (derived from the class name).
|
| 652 |
+
prefix = f"{config.module_prefix}{name}." if config.module_prefix else f"{name}."
|
| 653 |
+
submodule_config = replace(config, module_prefix=prefix)
|
| 654 |
+
|
| 655 |
+
_apply_group_offloading_block_level(submodule, submodule_config)
|
| 656 |
+
modules_with_group_offloading.add(name)
|
| 657 |
+
|
| 658 |
+
elif isinstance(submodule, (torch.nn.ModuleList, torch.nn.Sequential)):
|
| 659 |
+
# Handle ModuleList and Sequential blocks as before
|
| 660 |
+
for i in range(0, len(submodule), config.num_blocks_per_group):
|
| 661 |
+
current_modules = list(submodule[i : i + config.num_blocks_per_group])
|
| 662 |
+
if len(current_modules) == 0:
|
| 663 |
+
continue
|
| 664 |
+
|
| 665 |
+
group_id = f"{config.module_prefix}{name}_{i}_{i + len(current_modules) - 1}"
|
| 666 |
+
group = ModuleGroup(
|
| 667 |
+
modules=current_modules,
|
| 668 |
+
offload_device=config.offload_device,
|
| 669 |
+
onload_device=config.onload_device,
|
| 670 |
+
offload_to_disk_path=config.offload_to_disk_path,
|
| 671 |
+
offload_leader=current_modules[-1],
|
| 672 |
+
onload_leader=current_modules[0],
|
| 673 |
+
non_blocking=config.non_blocking,
|
| 674 |
+
stream=config.stream,
|
| 675 |
+
record_stream=config.record_stream,
|
| 676 |
+
low_cpu_mem_usage=config.low_cpu_mem_usage,
|
| 677 |
+
onload_self=True,
|
| 678 |
+
group_id=group_id,
|
| 679 |
+
)
|
| 680 |
+
matched_module_groups.append(group)
|
| 681 |
+
for j in range(i, i + len(current_modules)):
|
| 682 |
+
modules_with_group_offloading.add(f"{name}.{j}")
|
| 683 |
+
else:
|
| 684 |
+
# This is an unmatched module
|
| 685 |
+
unmatched_modules.append((name, submodule))
|
| 686 |
+
|
| 687 |
+
# Apply group offloading hooks to the module groups
|
| 688 |
+
for i, group in enumerate(matched_module_groups):
|
| 689 |
+
for group_module in group.modules:
|
| 690 |
+
_apply_group_offloading_hook(group_module, group, config=config)
|
| 691 |
+
|
| 692 |
+
# Parameters and Buffers of the top-level module need to be offloaded/onloaded separately
|
| 693 |
+
# when the forward pass of this module is called. This is because the top-level module is not
|
| 694 |
+
# part of any group (as doing so would lead to no VRAM savings).
|
| 695 |
+
parameters = _gather_parameters_with_no_group_offloading_parent(module, modules_with_group_offloading)
|
| 696 |
+
buffers = _gather_buffers_with_no_group_offloading_parent(module, modules_with_group_offloading)
|
| 697 |
+
parameters = [param for _, param in parameters]
|
| 698 |
+
buffers = [buffer for _, buffer in buffers]
|
| 699 |
+
|
| 700 |
+
# Create a group for the remaining unmatched submodules of the top-level
|
| 701 |
+
# module so that they are on the correct device when the forward pass is called.
|
| 702 |
+
unmatched_modules = [unmatched_module for _, unmatched_module in unmatched_modules]
|
| 703 |
+
if len(unmatched_modules) > 0 or len(parameters) > 0 or len(buffers) > 0:
|
| 704 |
+
unmatched_group = ModuleGroup(
|
| 705 |
+
modules=unmatched_modules,
|
| 706 |
+
offload_device=config.offload_device,
|
| 707 |
+
onload_device=config.onload_device,
|
| 708 |
+
offload_to_disk_path=config.offload_to_disk_path,
|
| 709 |
+
offload_leader=module,
|
| 710 |
+
onload_leader=module,
|
| 711 |
+
parameters=parameters,
|
| 712 |
+
buffers=buffers,
|
| 713 |
+
non_blocking=False,
|
| 714 |
+
stream=None,
|
| 715 |
+
record_stream=False,
|
| 716 |
+
onload_self=True,
|
| 717 |
+
group_id=f"{config.module_prefix}{module.__class__.__name__}_unmatched_group",
|
| 718 |
+
)
|
| 719 |
+
if config.stream is None:
|
| 720 |
+
_apply_group_offloading_hook(module, unmatched_group, config=config)
|
| 721 |
+
else:
|
| 722 |
+
_apply_lazy_group_offloading_hook(module, unmatched_group, config=config)
|
| 723 |
+
|
| 724 |
+
|
| 725 |
+
def _apply_group_offloading_leaf_level(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
|
| 726 |
+
r"""
|
| 727 |
+
This function applies offloading to groups of leaf modules in a torch.nn.Module. This method has minimal memory
|
| 728 |
+
requirements. However, it can be slower compared to other offloading methods due to the excessive number of device
|
| 729 |
+
synchronizations. When using devices that support streams to overlap data transfer and computation, this method can
|
| 730 |
+
reduce memory usage without any performance degradation.
|
| 731 |
+
"""
|
| 732 |
+
# Create module groups for leaf modules and apply group offloading hooks
|
| 733 |
+
modules_with_group_offloading = set()
|
| 734 |
+
for name, submodule in module.named_modules():
|
| 735 |
+
if not isinstance(submodule, _GO_LC_SUPPORTED_PYTORCH_LAYERS):
|
| 736 |
+
continue
|
| 737 |
+
group = ModuleGroup(
|
| 738 |
+
modules=[submodule],
|
| 739 |
+
offload_device=config.offload_device,
|
| 740 |
+
onload_device=config.onload_device,
|
| 741 |
+
offload_to_disk_path=config.offload_to_disk_path,
|
| 742 |
+
offload_leader=submodule,
|
| 743 |
+
onload_leader=submodule,
|
| 744 |
+
non_blocking=config.non_blocking,
|
| 745 |
+
stream=config.stream,
|
| 746 |
+
record_stream=config.record_stream,
|
| 747 |
+
low_cpu_mem_usage=config.low_cpu_mem_usage,
|
| 748 |
+
onload_self=True,
|
| 749 |
+
group_id=name,
|
| 750 |
+
)
|
| 751 |
+
_apply_group_offloading_hook(submodule, group, config=config)
|
| 752 |
+
modules_with_group_offloading.add(name)
|
| 753 |
+
|
| 754 |
+
# Parameters and Buffers at all non-leaf levels need to be offloaded/onloaded separately when the forward pass
|
| 755 |
+
# of the module is called
|
| 756 |
+
module_dict = dict(module.named_modules())
|
| 757 |
+
parameters = _gather_parameters_with_no_group_offloading_parent(module, modules_with_group_offloading)
|
| 758 |
+
buffers = _gather_buffers_with_no_group_offloading_parent(module, modules_with_group_offloading)
|
| 759 |
+
|
| 760 |
+
# Find closest module parent for each parameter and buffer, and attach group hooks
|
| 761 |
+
parent_to_parameters = {}
|
| 762 |
+
for name, param in parameters:
|
| 763 |
+
parent_name = _find_parent_module_in_module_dict(name, module_dict)
|
| 764 |
+
if parent_name in parent_to_parameters:
|
| 765 |
+
parent_to_parameters[parent_name].append(param)
|
| 766 |
+
else:
|
| 767 |
+
parent_to_parameters[parent_name] = [param]
|
| 768 |
+
|
| 769 |
+
parent_to_buffers = {}
|
| 770 |
+
for name, buffer in buffers:
|
| 771 |
+
parent_name = _find_parent_module_in_module_dict(name, module_dict)
|
| 772 |
+
if parent_name in parent_to_buffers:
|
| 773 |
+
parent_to_buffers[parent_name].append(buffer)
|
| 774 |
+
else:
|
| 775 |
+
parent_to_buffers[parent_name] = [buffer]
|
| 776 |
+
|
| 777 |
+
parent_names = set(parent_to_parameters.keys()) | set(parent_to_buffers.keys())
|
| 778 |
+
for name in parent_names:
|
| 779 |
+
parameters = parent_to_parameters.get(name, [])
|
| 780 |
+
buffers = parent_to_buffers.get(name, [])
|
| 781 |
+
parent_module = module_dict[name]
|
| 782 |
+
group = ModuleGroup(
|
| 783 |
+
modules=[],
|
| 784 |
+
offload_device=config.offload_device,
|
| 785 |
+
onload_device=config.onload_device,
|
| 786 |
+
offload_leader=parent_module,
|
| 787 |
+
onload_leader=parent_module,
|
| 788 |
+
offload_to_disk_path=config.offload_to_disk_path,
|
| 789 |
+
parameters=parameters,
|
| 790 |
+
buffers=buffers,
|
| 791 |
+
non_blocking=config.non_blocking,
|
| 792 |
+
stream=config.stream,
|
| 793 |
+
record_stream=config.record_stream,
|
| 794 |
+
low_cpu_mem_usage=config.low_cpu_mem_usage,
|
| 795 |
+
onload_self=True,
|
| 796 |
+
group_id=name,
|
| 797 |
+
)
|
| 798 |
+
_apply_group_offloading_hook(parent_module, group, config=config)
|
| 799 |
+
|
| 800 |
+
if config.stream is not None:
|
| 801 |
+
# When using streams, we need to know the layer execution order for applying prefetching (to overlap data transfer
|
| 802 |
+
# and computation). Since we don't know the order beforehand, we apply a lazy prefetching hook that will find the
|
| 803 |
+
# execution order and apply prefetching in the correct order.
|
| 804 |
+
unmatched_group = ModuleGroup(
|
| 805 |
+
modules=[],
|
| 806 |
+
offload_device=config.offload_device,
|
| 807 |
+
onload_device=config.onload_device,
|
| 808 |
+
offload_to_disk_path=config.offload_to_disk_path,
|
| 809 |
+
offload_leader=module,
|
| 810 |
+
onload_leader=module,
|
| 811 |
+
parameters=None,
|
| 812 |
+
buffers=None,
|
| 813 |
+
non_blocking=False,
|
| 814 |
+
stream=None,
|
| 815 |
+
record_stream=False,
|
| 816 |
+
low_cpu_mem_usage=config.low_cpu_mem_usage,
|
| 817 |
+
onload_self=True,
|
| 818 |
+
group_id=_GROUP_ID_LAZY_LEAF,
|
| 819 |
+
)
|
| 820 |
+
_apply_lazy_group_offloading_hook(module, unmatched_group, config=config)
|
| 821 |
+
|
| 822 |
+
|
| 823 |
+
def _apply_group_offloading_hook(
|
| 824 |
+
module: torch.nn.Module,
|
| 825 |
+
group: ModuleGroup,
|
| 826 |
+
*,
|
| 827 |
+
config: GroupOffloadingConfig,
|
| 828 |
+
) -> None:
|
| 829 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 830 |
+
|
| 831 |
+
# We may have already registered a group offloading hook if the module had a torch.nn.Parameter whose parent
|
| 832 |
+
# is the current module. In such cases, we don't want to overwrite the existing group offloading hook.
|
| 833 |
+
if registry.get_hook(_GROUP_OFFLOADING) is None:
|
| 834 |
+
hook = GroupOffloadingHook(group, config=config)
|
| 835 |
+
registry.register_hook(hook, _GROUP_OFFLOADING)
|
| 836 |
+
|
| 837 |
+
|
| 838 |
+
def _apply_lazy_group_offloading_hook(
|
| 839 |
+
module: torch.nn.Module,
|
| 840 |
+
group: ModuleGroup,
|
| 841 |
+
*,
|
| 842 |
+
config: GroupOffloadingConfig,
|
| 843 |
+
) -> None:
|
| 844 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 845 |
+
|
| 846 |
+
# We may have already registered a group offloading hook if the module had a torch.nn.Parameter whose parent
|
| 847 |
+
# is the current module. In such cases, we don't want to overwrite the existing group offloading hook.
|
| 848 |
+
if registry.get_hook(_GROUP_OFFLOADING) is None:
|
| 849 |
+
hook = GroupOffloadingHook(group, config=config)
|
| 850 |
+
registry.register_hook(hook, _GROUP_OFFLOADING)
|
| 851 |
+
|
| 852 |
+
lazy_prefetch_hook = LazyPrefetchGroupOffloadingHook()
|
| 853 |
+
registry.register_hook(lazy_prefetch_hook, _LAZY_PREFETCH_GROUP_OFFLOADING)
|
| 854 |
+
|
| 855 |
+
|
| 856 |
+
def _gather_parameters_with_no_group_offloading_parent(
|
| 857 |
+
module: torch.nn.Module, modules_with_group_offloading: Set[str]
|
| 858 |
+
) -> list[torch.nn.Parameter]:
|
| 859 |
+
parameters = []
|
| 860 |
+
for name, parameter in module.named_parameters():
|
| 861 |
+
has_parent_with_group_offloading = False
|
| 862 |
+
atoms = name.split(".")
|
| 863 |
+
while len(atoms) > 0:
|
| 864 |
+
parent_name = ".".join(atoms)
|
| 865 |
+
if parent_name in modules_with_group_offloading:
|
| 866 |
+
has_parent_with_group_offloading = True
|
| 867 |
+
break
|
| 868 |
+
atoms.pop()
|
| 869 |
+
if not has_parent_with_group_offloading:
|
| 870 |
+
parameters.append((name, parameter))
|
| 871 |
+
return parameters
|
| 872 |
+
|
| 873 |
+
|
| 874 |
+
def _gather_buffers_with_no_group_offloading_parent(
|
| 875 |
+
module: torch.nn.Module, modules_with_group_offloading: Set[str]
|
| 876 |
+
) -> list[torch.Tensor]:
|
| 877 |
+
buffers = []
|
| 878 |
+
for name, buffer in module.named_buffers():
|
| 879 |
+
has_parent_with_group_offloading = False
|
| 880 |
+
atoms = name.split(".")
|
| 881 |
+
while len(atoms) > 0:
|
| 882 |
+
parent_name = ".".join(atoms)
|
| 883 |
+
if parent_name in modules_with_group_offloading:
|
| 884 |
+
has_parent_with_group_offloading = True
|
| 885 |
+
break
|
| 886 |
+
atoms.pop()
|
| 887 |
+
if not has_parent_with_group_offloading:
|
| 888 |
+
buffers.append((name, buffer))
|
| 889 |
+
return buffers
|
| 890 |
+
|
| 891 |
+
|
| 892 |
+
def _find_parent_module_in_module_dict(name: str, module_dict: dict[str, torch.nn.Module]) -> str:
|
| 893 |
+
atoms = name.split(".")
|
| 894 |
+
while len(atoms) > 0:
|
| 895 |
+
parent_name = ".".join(atoms)
|
| 896 |
+
if parent_name in module_dict:
|
| 897 |
+
return parent_name
|
| 898 |
+
atoms.pop()
|
| 899 |
+
return ""
|
| 900 |
+
|
| 901 |
+
|
| 902 |
+
def _raise_error_if_accelerate_model_or_sequential_hook_present(module: torch.nn.Module) -> None:
|
| 903 |
+
if not is_accelerate_available():
|
| 904 |
+
return
|
| 905 |
+
for name, submodule in module.named_modules():
|
| 906 |
+
if not hasattr(submodule, "_hf_hook"):
|
| 907 |
+
continue
|
| 908 |
+
if isinstance(submodule._hf_hook, (AlignDevicesHook, CpuOffload)):
|
| 909 |
+
raise ValueError(
|
| 910 |
+
f"Cannot apply group offloading to a module that is already applying an alternative "
|
| 911 |
+
f"offloading strategy from Accelerate. If you want to apply group offloading, please "
|
| 912 |
+
f"disable the existing offloading strategy first. Offending module: {name} ({type(submodule)})"
|
| 913 |
+
)
|
| 914 |
+
|
| 915 |
+
|
| 916 |
+
def _get_top_level_group_offload_hook(module: torch.nn.Module) -> GroupOffloadingHook | None:
|
| 917 |
+
for submodule in module.modules():
|
| 918 |
+
if hasattr(submodule, "_diffusers_hook"):
|
| 919 |
+
group_offloading_hook = submodule._diffusers_hook.get_hook(_GROUP_OFFLOADING)
|
| 920 |
+
if group_offloading_hook is not None:
|
| 921 |
+
return group_offloading_hook
|
| 922 |
+
return None
|
| 923 |
+
|
| 924 |
+
|
| 925 |
+
def _is_group_offload_enabled(module: torch.nn.Module) -> bool:
|
| 926 |
+
top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
|
| 927 |
+
return top_level_group_offload_hook is not None
|
| 928 |
+
|
| 929 |
+
|
| 930 |
+
def _get_group_onload_device(module: torch.nn.Module) -> torch.device:
|
| 931 |
+
top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
|
| 932 |
+
if top_level_group_offload_hook is not None:
|
| 933 |
+
return top_level_group_offload_hook.config.onload_device
|
| 934 |
+
raise ValueError("Group offloading is not enabled for the provided module.")
|
| 935 |
+
|
| 936 |
+
|
| 937 |
+
def _compute_group_hash(group_id):
|
| 938 |
+
hashed_id = hashlib.sha256(group_id.encode("utf-8")).hexdigest()
|
| 939 |
+
# first 16 characters for a reasonably short but unique name
|
| 940 |
+
return hashed_id[:16]
|
| 941 |
+
|
| 942 |
+
|
| 943 |
+
def _maybe_remove_and_reapply_group_offloading(module: torch.nn.Module) -> None:
|
| 944 |
+
r"""
|
| 945 |
+
Removes the group offloading hook from the module and re-applies it. This is useful when the module has been
|
| 946 |
+
modified in-place and the group offloading hook references-to-tensors needs to be updated. The in-place
|
| 947 |
+
modification can happen in a number of ways, for example, fusing QKV or unloading/loading LoRAs on-the-fly.
|
| 948 |
+
|
| 949 |
+
In this implementation, we make an assumption that group offloading has only been applied at the top-level module,
|
| 950 |
+
and therefore all submodules have the same onload and offload devices. If this assumption is not true, say in the
|
| 951 |
+
case where user has applied group offloading at multiple levels, this function will not work as expected.
|
| 952 |
+
|
| 953 |
+
There is some performance penalty associated with doing this when non-default streams are used, because we need to
|
| 954 |
+
retrace the execution order of the layers with `LazyPrefetchGroupOffloadingHook`.
|
| 955 |
+
"""
|
| 956 |
+
top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
|
| 957 |
+
|
| 958 |
+
if top_level_group_offload_hook is None:
|
| 959 |
+
return
|
| 960 |
+
|
| 961 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 962 |
+
registry.remove_hook(_GROUP_OFFLOADING, recurse=True)
|
| 963 |
+
registry.remove_hook(_LAYER_EXECUTION_TRACKER, recurse=True)
|
| 964 |
+
registry.remove_hook(_LAZY_PREFETCH_GROUP_OFFLOADING, recurse=True)
|
| 965 |
+
|
| 966 |
+
_apply_group_offloading(module, top_level_group_offload_hook.config)
|
third_party/diffusers/src/diffusers/hooks/hooks.py
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 functools
|
| 16 |
+
from typing import Any
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
|
| 20 |
+
from ..utils.logging import get_logger
|
| 21 |
+
from ..utils.torch_utils import unwrap_module
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class BaseState:
|
| 28 |
+
def reset(self, *args, **kwargs) -> None:
|
| 29 |
+
raise NotImplementedError(
|
| 30 |
+
"BaseState::reset is not implemented. Please implement this method in the derived class."
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class StateManager:
|
| 35 |
+
def __init__(self, state_cls: BaseState, init_args=None, init_kwargs=None):
|
| 36 |
+
self._state_cls = state_cls
|
| 37 |
+
self._init_args = init_args if init_args is not None else ()
|
| 38 |
+
self._init_kwargs = init_kwargs if init_kwargs is not None else {}
|
| 39 |
+
self._state_cache = {}
|
| 40 |
+
self._current_context = None
|
| 41 |
+
|
| 42 |
+
def get_state(self):
|
| 43 |
+
if self._current_context is None:
|
| 44 |
+
raise ValueError("No context is set. Please set a context before retrieving the state.")
|
| 45 |
+
if self._current_context not in self._state_cache.keys():
|
| 46 |
+
self._state_cache[self._current_context] = self._state_cls(*self._init_args, **self._init_kwargs)
|
| 47 |
+
return self._state_cache[self._current_context]
|
| 48 |
+
|
| 49 |
+
def set_context(self, name: str) -> None:
|
| 50 |
+
self._current_context = name
|
| 51 |
+
|
| 52 |
+
def reset(self, *args, **kwargs) -> None:
|
| 53 |
+
for name, state in list(self._state_cache.items()):
|
| 54 |
+
state.reset(*args, **kwargs)
|
| 55 |
+
self._state_cache.pop(name)
|
| 56 |
+
self._current_context = None
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class ModelHook:
|
| 60 |
+
r"""
|
| 61 |
+
A hook that contains callbacks to be executed just before and after the forward method of a model.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
_is_stateful = False
|
| 65 |
+
|
| 66 |
+
def __init__(self):
|
| 67 |
+
self.fn_ref: "HookFunctionReference" = None
|
| 68 |
+
|
| 69 |
+
def initialize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 70 |
+
r"""
|
| 71 |
+
Hook that is executed when a model is initialized.
|
| 72 |
+
|
| 73 |
+
Args:
|
| 74 |
+
module (`torch.nn.Module`):
|
| 75 |
+
The module attached to this hook.
|
| 76 |
+
"""
|
| 77 |
+
return module
|
| 78 |
+
|
| 79 |
+
def deinitalize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 80 |
+
r"""
|
| 81 |
+
Hook that is executed when a model is deinitialized.
|
| 82 |
+
|
| 83 |
+
Args:
|
| 84 |
+
module (`torch.nn.Module`):
|
| 85 |
+
The module attached to this hook.
|
| 86 |
+
"""
|
| 87 |
+
return module
|
| 88 |
+
|
| 89 |
+
def pre_forward(self, module: torch.nn.Module, *args, **kwargs) -> tuple[tuple[Any], dict[str, Any]]:
|
| 90 |
+
r"""
|
| 91 |
+
Hook that is executed just before the forward method of the model.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
module (`torch.nn.Module`):
|
| 95 |
+
The module whose forward pass will be executed just after this event.
|
| 96 |
+
args (`tuple[Any]`):
|
| 97 |
+
The positional arguments passed to the module.
|
| 98 |
+
kwargs (`dict[Str, Any]`):
|
| 99 |
+
The keyword arguments passed to the module.
|
| 100 |
+
Returns:
|
| 101 |
+
`tuple[tuple[Any], dict[Str, Any]]`:
|
| 102 |
+
A tuple with the treated `args` and `kwargs`.
|
| 103 |
+
"""
|
| 104 |
+
return args, kwargs
|
| 105 |
+
|
| 106 |
+
def post_forward(self, module: torch.nn.Module, output: Any) -> Any:
|
| 107 |
+
r"""
|
| 108 |
+
Hook that is executed just after the forward method of the model.
|
| 109 |
+
|
| 110 |
+
Args:
|
| 111 |
+
module (`torch.nn.Module`):
|
| 112 |
+
The module whose forward pass been executed just before this event.
|
| 113 |
+
output (`Any`):
|
| 114 |
+
The output of the module.
|
| 115 |
+
Returns:
|
| 116 |
+
`Any`: The processed `output`.
|
| 117 |
+
"""
|
| 118 |
+
return output
|
| 119 |
+
|
| 120 |
+
def detach_hook(self, module: torch.nn.Module) -> torch.nn.Module:
|
| 121 |
+
r"""
|
| 122 |
+
Hook that is executed when the hook is detached from a module.
|
| 123 |
+
|
| 124 |
+
Args:
|
| 125 |
+
module (`torch.nn.Module`):
|
| 126 |
+
The module detached from this hook.
|
| 127 |
+
"""
|
| 128 |
+
return module
|
| 129 |
+
|
| 130 |
+
def reset_state(self, module: torch.nn.Module):
|
| 131 |
+
if self._is_stateful:
|
| 132 |
+
raise NotImplementedError("This hook is stateful and needs to implement the `reset_state` method.")
|
| 133 |
+
return module
|
| 134 |
+
|
| 135 |
+
def _set_context(self, module: torch.nn.Module, name: str) -> None:
|
| 136 |
+
# Iterate over all attributes of the hook to see if any of them have the type `StateManager`. If so, call `set_context` on them.
|
| 137 |
+
for attr_name in dir(self):
|
| 138 |
+
attr = getattr(self, attr_name)
|
| 139 |
+
if isinstance(attr, StateManager):
|
| 140 |
+
attr.set_context(name)
|
| 141 |
+
return module
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
class HookFunctionReference:
|
| 145 |
+
def __init__(self) -> None:
|
| 146 |
+
"""A container class that maintains mutable references to forward pass functions in a hook chain.
|
| 147 |
+
|
| 148 |
+
Its mutable nature allows the hook system to modify the execution chain dynamically without rebuilding the
|
| 149 |
+
entire forward pass structure.
|
| 150 |
+
|
| 151 |
+
Attributes:
|
| 152 |
+
pre_forward: A callable that processes inputs before the main forward pass.
|
| 153 |
+
post_forward: A callable that processes outputs after the main forward pass.
|
| 154 |
+
forward: The current forward function in the hook chain.
|
| 155 |
+
original_forward: The original forward function, stored when a hook provides a custom new_forward.
|
| 156 |
+
|
| 157 |
+
The class enables hook removal by allowing updates to the forward chain through reference modification rather
|
| 158 |
+
than requiring reconstruction of the entire chain. When a hook is removed, only the relevant references need to
|
| 159 |
+
be updated, preserving the execution order of the remaining hooks.
|
| 160 |
+
"""
|
| 161 |
+
self.pre_forward = None
|
| 162 |
+
self.post_forward = None
|
| 163 |
+
self.forward = None
|
| 164 |
+
self.original_forward = None
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class HookRegistry:
|
| 168 |
+
def __init__(self, module_ref: torch.nn.Module) -> None:
|
| 169 |
+
super().__init__()
|
| 170 |
+
|
| 171 |
+
self.hooks: dict[str, ModelHook] = {}
|
| 172 |
+
|
| 173 |
+
self._module_ref = module_ref
|
| 174 |
+
self._hook_order = []
|
| 175 |
+
self._fn_refs = []
|
| 176 |
+
|
| 177 |
+
def register_hook(self, hook: ModelHook, name: str) -> None:
|
| 178 |
+
if name in self.hooks.keys():
|
| 179 |
+
raise ValueError(
|
| 180 |
+
f"Hook with name {name} already exists in the registry. Please use a different name or "
|
| 181 |
+
f"first remove the existing hook and then add a new one."
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
self._module_ref = hook.initialize_hook(self._module_ref)
|
| 185 |
+
|
| 186 |
+
def create_new_forward(function_reference: HookFunctionReference):
|
| 187 |
+
def new_forward(module, *args, **kwargs):
|
| 188 |
+
args, kwargs = function_reference.pre_forward(module, *args, **kwargs)
|
| 189 |
+
output = function_reference.forward(*args, **kwargs)
|
| 190 |
+
return function_reference.post_forward(module, output)
|
| 191 |
+
|
| 192 |
+
return new_forward
|
| 193 |
+
|
| 194 |
+
forward = self._module_ref.forward
|
| 195 |
+
|
| 196 |
+
fn_ref = HookFunctionReference()
|
| 197 |
+
fn_ref.pre_forward = hook.pre_forward
|
| 198 |
+
fn_ref.post_forward = hook.post_forward
|
| 199 |
+
fn_ref.forward = forward
|
| 200 |
+
|
| 201 |
+
if hasattr(hook, "new_forward"):
|
| 202 |
+
fn_ref.original_forward = forward
|
| 203 |
+
fn_ref.forward = functools.update_wrapper(
|
| 204 |
+
functools.partial(hook.new_forward, self._module_ref), hook.new_forward
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
rewritten_forward = create_new_forward(fn_ref)
|
| 208 |
+
self._module_ref.forward = functools.update_wrapper(
|
| 209 |
+
functools.partial(rewritten_forward, self._module_ref), rewritten_forward
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
hook.fn_ref = fn_ref
|
| 213 |
+
self.hooks[name] = hook
|
| 214 |
+
self._hook_order.append(name)
|
| 215 |
+
self._fn_refs.append(fn_ref)
|
| 216 |
+
|
| 217 |
+
def get_hook(self, name: str) -> ModelHook | None:
|
| 218 |
+
return self.hooks.get(name, None)
|
| 219 |
+
|
| 220 |
+
def remove_hook(self, name: str, recurse: bool = True) -> None:
|
| 221 |
+
if name in self.hooks.keys():
|
| 222 |
+
num_hooks = len(self._hook_order)
|
| 223 |
+
hook = self.hooks[name]
|
| 224 |
+
index = self._hook_order.index(name)
|
| 225 |
+
fn_ref = self._fn_refs[index]
|
| 226 |
+
|
| 227 |
+
old_forward = fn_ref.forward
|
| 228 |
+
if fn_ref.original_forward is not None:
|
| 229 |
+
old_forward = fn_ref.original_forward
|
| 230 |
+
|
| 231 |
+
if index == num_hooks - 1:
|
| 232 |
+
self._module_ref.forward = old_forward
|
| 233 |
+
else:
|
| 234 |
+
self._fn_refs[index + 1].forward = old_forward
|
| 235 |
+
|
| 236 |
+
self._module_ref = hook.deinitalize_hook(self._module_ref)
|
| 237 |
+
del self.hooks[name]
|
| 238 |
+
self._hook_order.pop(index)
|
| 239 |
+
self._fn_refs.pop(index)
|
| 240 |
+
|
| 241 |
+
if recurse:
|
| 242 |
+
for module_name, module in self._module_ref.named_modules():
|
| 243 |
+
if module_name == "":
|
| 244 |
+
continue
|
| 245 |
+
if hasattr(module, "_diffusers_hook"):
|
| 246 |
+
module._diffusers_hook.remove_hook(name, recurse=False)
|
| 247 |
+
|
| 248 |
+
def reset_stateful_hooks(self, recurse: bool = True) -> None:
|
| 249 |
+
for hook_name in reversed(self._hook_order):
|
| 250 |
+
hook = self.hooks[hook_name]
|
| 251 |
+
if hook._is_stateful:
|
| 252 |
+
hook.reset_state(self._module_ref)
|
| 253 |
+
|
| 254 |
+
if recurse:
|
| 255 |
+
for module_name, module in unwrap_module(self._module_ref).named_modules():
|
| 256 |
+
if module_name == "":
|
| 257 |
+
continue
|
| 258 |
+
module = unwrap_module(module)
|
| 259 |
+
if hasattr(module, "_diffusers_hook"):
|
| 260 |
+
module._diffusers_hook.reset_stateful_hooks(recurse=False)
|
| 261 |
+
|
| 262 |
+
@classmethod
|
| 263 |
+
def check_if_exists_or_initialize(cls, module: torch.nn.Module) -> "HookRegistry":
|
| 264 |
+
if not hasattr(module, "_diffusers_hook"):
|
| 265 |
+
module._diffusers_hook = cls(module)
|
| 266 |
+
return module._diffusers_hook
|
| 267 |
+
|
| 268 |
+
def _set_context(self, name: str | None = None) -> None:
|
| 269 |
+
for hook_name in reversed(self._hook_order):
|
| 270 |
+
hook = self.hooks[hook_name]
|
| 271 |
+
if hook._is_stateful:
|
| 272 |
+
hook._set_context(self._module_ref, name)
|
| 273 |
+
|
| 274 |
+
for registry in self._get_child_registries():
|
| 275 |
+
registry._set_context(name)
|
| 276 |
+
|
| 277 |
+
def _get_child_registries(self) -> list["HookRegistry"]:
|
| 278 |
+
"""Return registries of child modules, using a cached list when available.
|
| 279 |
+
|
| 280 |
+
The cache is built on first call and reused for subsequent calls. This avoids the cost of walking the full
|
| 281 |
+
module tree via named_modules() on every _set_context call, which is significant for large models (e.g. ~2.7ms
|
| 282 |
+
per call on Flux2).
|
| 283 |
+
"""
|
| 284 |
+
if not hasattr(self, "_child_registries_cache"):
|
| 285 |
+
self._child_registries_cache = None
|
| 286 |
+
|
| 287 |
+
if self._child_registries_cache is not None:
|
| 288 |
+
return self._child_registries_cache
|
| 289 |
+
|
| 290 |
+
registries = []
|
| 291 |
+
for module_name, module in unwrap_module(self._module_ref).named_modules():
|
| 292 |
+
if module_name == "":
|
| 293 |
+
continue
|
| 294 |
+
module = unwrap_module(module)
|
| 295 |
+
if hasattr(module, "_diffusers_hook"):
|
| 296 |
+
registries.append(module._diffusers_hook)
|
| 297 |
+
self._child_registries_cache = registries
|
| 298 |
+
return registries
|
| 299 |
+
|
| 300 |
+
def __repr__(self) -> str:
|
| 301 |
+
registry_repr = ""
|
| 302 |
+
for i, hook_name in enumerate(self._hook_order):
|
| 303 |
+
if self.hooks[hook_name].__class__.__repr__ is not object.__repr__:
|
| 304 |
+
hook_repr = self.hooks[hook_name].__repr__()
|
| 305 |
+
else:
|
| 306 |
+
hook_repr = self.hooks[hook_name].__class__.__name__
|
| 307 |
+
registry_repr += f" ({i}) {hook_name} - {hook_repr}"
|
| 308 |
+
if i < len(self._hook_order) - 1:
|
| 309 |
+
registry_repr += "\n"
|
| 310 |
+
return f"HookRegistry(\n{registry_repr}\n)"
|
third_party/diffusers/src/diffusers/hooks/layer_skip.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 math
|
| 16 |
+
from dataclasses import asdict, dataclass
|
| 17 |
+
from typing import Callable
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from ..utils import get_logger
|
| 22 |
+
from ..utils.torch_utils import unwrap_module
|
| 23 |
+
from ._common import (
|
| 24 |
+
_ALL_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 25 |
+
_ATTENTION_CLASSES,
|
| 26 |
+
_FEEDFORWARD_CLASSES,
|
| 27 |
+
_get_submodule_from_fqn,
|
| 28 |
+
)
|
| 29 |
+
from ._helpers import AttentionProcessorRegistry, TransformerBlockRegistry
|
| 30 |
+
from .hooks import HookRegistry, ModelHook
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 34 |
+
|
| 35 |
+
_LAYER_SKIP_HOOK = "layer_skip_hook"
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# Aryan/YiYi TODO: we need to make guider class a config mixin so I think this is not needed
|
| 39 |
+
# either remove or make it serializable
|
| 40 |
+
@dataclass
|
| 41 |
+
class LayerSkipConfig:
|
| 42 |
+
r"""
|
| 43 |
+
Configuration for skipping internal transformer blocks when executing a transformer model.
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
indices (`list[int]`):
|
| 47 |
+
The indices of the layer to skip. This is typically the first layer in the transformer block.
|
| 48 |
+
fqn (`str`, defaults to `"auto"`):
|
| 49 |
+
The fully qualified name identifying the stack of transformer blocks. Typically, this is
|
| 50 |
+
`transformer_blocks`, `single_transformer_blocks`, `blocks`, `layers`, or `temporal_transformer_blocks`.
|
| 51 |
+
For automatic detection, set this to `"auto"`. "auto" only works on DiT models. For UNet models, you must
|
| 52 |
+
provide the correct fqn.
|
| 53 |
+
skip_attention (`bool`, defaults to `True`):
|
| 54 |
+
Whether to skip attention blocks.
|
| 55 |
+
skip_ff (`bool`, defaults to `True`):
|
| 56 |
+
Whether to skip feed-forward blocks.
|
| 57 |
+
skip_attention_scores (`bool`, defaults to `False`):
|
| 58 |
+
Whether to skip attention score computation in the attention blocks. This is equivalent to using `value`
|
| 59 |
+
projections as the output of scaled dot product attention.
|
| 60 |
+
dropout (`float`, defaults to `1.0`):
|
| 61 |
+
The dropout probability for dropping the outputs of the skipped layers. By default, this is set to `1.0`,
|
| 62 |
+
meaning that the outputs of the skipped layers are completely ignored. If set to `0.0`, the outputs of the
|
| 63 |
+
skipped layers are fully retained, which is equivalent to not skipping any layers.
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
indices: list[int]
|
| 67 |
+
fqn: str = "auto"
|
| 68 |
+
skip_attention: bool = True
|
| 69 |
+
skip_attention_scores: bool = False
|
| 70 |
+
skip_ff: bool = True
|
| 71 |
+
dropout: float = 1.0
|
| 72 |
+
|
| 73 |
+
def __post_init__(self):
|
| 74 |
+
if not (0 <= self.dropout <= 1):
|
| 75 |
+
raise ValueError(f"Expected `dropout` to be between 0.0 and 1.0, but got {self.dropout}.")
|
| 76 |
+
if not math.isclose(self.dropout, 1.0) and self.skip_attention_scores:
|
| 77 |
+
raise ValueError(
|
| 78 |
+
"Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
def to_dict(self):
|
| 82 |
+
return asdict(self)
|
| 83 |
+
|
| 84 |
+
@staticmethod
|
| 85 |
+
def from_dict(data: dict) -> "LayerSkipConfig":
|
| 86 |
+
return LayerSkipConfig(**data)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class AttentionScoreSkipFunctionMode(torch.overrides.TorchFunctionMode):
|
| 90 |
+
def __torch_function__(self, func, types, args=(), kwargs=None):
|
| 91 |
+
if kwargs is None:
|
| 92 |
+
kwargs = {}
|
| 93 |
+
if func is torch.nn.functional.scaled_dot_product_attention:
|
| 94 |
+
query = kwargs.get("query", None)
|
| 95 |
+
key = kwargs.get("key", None)
|
| 96 |
+
value = kwargs.get("value", None)
|
| 97 |
+
query = query if query is not None else args[0]
|
| 98 |
+
key = key if key is not None else args[1]
|
| 99 |
+
value = value if value is not None else args[2]
|
| 100 |
+
# If the Q sequence length does not match KV sequence length, methods like
|
| 101 |
+
# Perturbed Attention Guidance cannot be used (because the caller expects
|
| 102 |
+
# the same sequence length as Q, but if we return V here, it will not match).
|
| 103 |
+
# When Q.shape[2] != V.shape[2], PAG will essentially not be applied and
|
| 104 |
+
# the overall effect would that be of normal CFG with a scale of (guidance_scale + perturbed_guidance_scale).
|
| 105 |
+
if query.shape[2] == value.shape[2]:
|
| 106 |
+
return value
|
| 107 |
+
return func(*args, **kwargs)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class AttentionProcessorSkipHook(ModelHook):
|
| 111 |
+
def __init__(self, skip_processor_output_fn: Callable, skip_attention_scores: bool = False, dropout: float = 1.0):
|
| 112 |
+
self.skip_processor_output_fn = skip_processor_output_fn
|
| 113 |
+
self.skip_attention_scores = skip_attention_scores
|
| 114 |
+
self.dropout = dropout
|
| 115 |
+
|
| 116 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 117 |
+
if self.skip_attention_scores:
|
| 118 |
+
if not math.isclose(self.dropout, 1.0):
|
| 119 |
+
raise ValueError(
|
| 120 |
+
"Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
|
| 121 |
+
)
|
| 122 |
+
with AttentionScoreSkipFunctionMode():
|
| 123 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 124 |
+
else:
|
| 125 |
+
if math.isclose(self.dropout, 1.0):
|
| 126 |
+
output = self.skip_processor_output_fn(module, *args, **kwargs)
|
| 127 |
+
else:
|
| 128 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 129 |
+
output = torch.nn.functional.dropout(output, p=self.dropout)
|
| 130 |
+
return output
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
class FeedForwardSkipHook(ModelHook):
|
| 134 |
+
def __init__(self, dropout: float):
|
| 135 |
+
super().__init__()
|
| 136 |
+
self.dropout = dropout
|
| 137 |
+
|
| 138 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 139 |
+
if math.isclose(self.dropout, 1.0):
|
| 140 |
+
output = kwargs.get("hidden_states", None)
|
| 141 |
+
if output is None:
|
| 142 |
+
output = kwargs.get("x", None)
|
| 143 |
+
if output is None and len(args) > 0:
|
| 144 |
+
output = args[0]
|
| 145 |
+
else:
|
| 146 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 147 |
+
output = torch.nn.functional.dropout(output, p=self.dropout)
|
| 148 |
+
return output
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class TransformerBlockSkipHook(ModelHook):
|
| 152 |
+
def __init__(self, dropout: float):
|
| 153 |
+
super().__init__()
|
| 154 |
+
self.dropout = dropout
|
| 155 |
+
|
| 156 |
+
def initialize_hook(self, module):
|
| 157 |
+
self._metadata = TransformerBlockRegistry.get(unwrap_module(module).__class__)
|
| 158 |
+
return module
|
| 159 |
+
|
| 160 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 161 |
+
if math.isclose(self.dropout, 1.0):
|
| 162 |
+
original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
|
| 163 |
+
if self._metadata.return_encoder_hidden_states_index is None:
|
| 164 |
+
output = original_hidden_states
|
| 165 |
+
else:
|
| 166 |
+
original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
|
| 167 |
+
"encoder_hidden_states", args, kwargs
|
| 168 |
+
)
|
| 169 |
+
output = (original_hidden_states, original_encoder_hidden_states)
|
| 170 |
+
else:
|
| 171 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 172 |
+
output = torch.nn.functional.dropout(output, p=self.dropout)
|
| 173 |
+
return output
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def apply_layer_skip(module: torch.nn.Module, config: LayerSkipConfig) -> None:
|
| 177 |
+
r"""
|
| 178 |
+
Apply layer skipping to internal layers of a transformer.
|
| 179 |
+
|
| 180 |
+
Args:
|
| 181 |
+
module (`torch.nn.Module`):
|
| 182 |
+
The transformer model to which the layer skip hook should be applied.
|
| 183 |
+
config (`LayerSkipConfig`):
|
| 184 |
+
The configuration for the layer skip hook.
|
| 185 |
+
|
| 186 |
+
Example:
|
| 187 |
+
|
| 188 |
+
```python
|
| 189 |
+
>>> from diffusers import apply_layer_skip_hook, CogVideoXTransformer3DModel, LayerSkipConfig
|
| 190 |
+
|
| 191 |
+
>>> transformer = CogVideoXTransformer3DModel.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
|
| 192 |
+
>>> config = LayerSkipConfig(layer_index=[10, 20], fqn="transformer_blocks")
|
| 193 |
+
>>> apply_layer_skip_hook(transformer, config)
|
| 194 |
+
```
|
| 195 |
+
"""
|
| 196 |
+
_apply_layer_skip_hook(module, config)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def _apply_layer_skip_hook(module: torch.nn.Module, config: LayerSkipConfig, name: str | None = None) -> None:
|
| 200 |
+
name = name or _LAYER_SKIP_HOOK
|
| 201 |
+
|
| 202 |
+
if config.skip_attention and config.skip_attention_scores:
|
| 203 |
+
raise ValueError("Cannot set both `skip_attention` and `skip_attention_scores` to True. Please choose one.")
|
| 204 |
+
if not math.isclose(config.dropout, 1.0) and config.skip_attention_scores:
|
| 205 |
+
raise ValueError(
|
| 206 |
+
"Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
|
| 207 |
+
)
|
| 208 |
+
|
| 209 |
+
if config.fqn == "auto":
|
| 210 |
+
for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS:
|
| 211 |
+
if hasattr(module, identifier):
|
| 212 |
+
config.fqn = identifier
|
| 213 |
+
break
|
| 214 |
+
else:
|
| 215 |
+
raise ValueError(
|
| 216 |
+
"Could not find a suitable identifier for the transformer blocks automatically. Please provide a valid "
|
| 217 |
+
"`fqn` (fully qualified name) that identifies a stack of transformer blocks."
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
transformer_blocks = _get_submodule_from_fqn(module, config.fqn)
|
| 221 |
+
if transformer_blocks is None or not isinstance(transformer_blocks, torch.nn.ModuleList):
|
| 222 |
+
raise ValueError(
|
| 223 |
+
f"Could not find {config.fqn} in the provided module, or configured `fqn` (fully qualified name) does not identify "
|
| 224 |
+
f"a `torch.nn.ModuleList`. Please provide a valid `fqn` that identifies a stack of transformer blocks."
|
| 225 |
+
)
|
| 226 |
+
if len(config.indices) == 0:
|
| 227 |
+
raise ValueError("Layer index list is empty. Please provide a non-empty list of layer indices to skip.")
|
| 228 |
+
|
| 229 |
+
blocks_found = False
|
| 230 |
+
for i, block in enumerate(transformer_blocks):
|
| 231 |
+
if i not in config.indices:
|
| 232 |
+
continue
|
| 233 |
+
|
| 234 |
+
blocks_found = True
|
| 235 |
+
|
| 236 |
+
if config.skip_attention and config.skip_ff:
|
| 237 |
+
logger.debug(f"Applying TransformerBlockSkipHook to '{config.fqn}.{i}'")
|
| 238 |
+
registry = HookRegistry.check_if_exists_or_initialize(block)
|
| 239 |
+
hook = TransformerBlockSkipHook(config.dropout)
|
| 240 |
+
registry.register_hook(hook, name)
|
| 241 |
+
|
| 242 |
+
elif config.skip_attention or config.skip_attention_scores:
|
| 243 |
+
for submodule_name, submodule in block.named_modules():
|
| 244 |
+
if isinstance(submodule, _ATTENTION_CLASSES) and not submodule.is_cross_attention:
|
| 245 |
+
logger.debug(f"Applying AttentionProcessorSkipHook to '{config.fqn}.{i}.{submodule_name}'")
|
| 246 |
+
output_fn = AttentionProcessorRegistry.get(submodule.processor.__class__).skip_processor_output_fn
|
| 247 |
+
registry = HookRegistry.check_if_exists_or_initialize(submodule)
|
| 248 |
+
hook = AttentionProcessorSkipHook(output_fn, config.skip_attention_scores, config.dropout)
|
| 249 |
+
registry.register_hook(hook, name)
|
| 250 |
+
|
| 251 |
+
if config.skip_ff:
|
| 252 |
+
for submodule_name, submodule in block.named_modules():
|
| 253 |
+
if isinstance(submodule, _FEEDFORWARD_CLASSES):
|
| 254 |
+
logger.debug(f"Applying FeedForwardSkipHook to '{config.fqn}.{i}.{submodule_name}'")
|
| 255 |
+
registry = HookRegistry.check_if_exists_or_initialize(submodule)
|
| 256 |
+
hook = FeedForwardSkipHook(config.dropout)
|
| 257 |
+
registry.register_hook(hook, name)
|
| 258 |
+
|
| 259 |
+
if not blocks_found:
|
| 260 |
+
raise ValueError(
|
| 261 |
+
f"Could not find any transformer blocks matching the provided indices {config.indices} and "
|
| 262 |
+
f"fully qualified name '{config.fqn}'. Please check the indices and fqn for correctness."
|
| 263 |
+
)
|
third_party/diffusers/src/diffusers/hooks/layerwise_casting.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 re
|
| 16 |
+
from typing import Type
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
|
| 20 |
+
from ..utils import get_logger, is_peft_available, is_peft_version
|
| 21 |
+
from ._common import _GO_LC_SUPPORTED_PYTORCH_LAYERS
|
| 22 |
+
from .hooks import HookRegistry, ModelHook
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# fmt: off
|
| 29 |
+
_LAYERWISE_CASTING_HOOK = "layerwise_casting"
|
| 30 |
+
_PEFT_AUTOCAST_DISABLE_HOOK = "peft_autocast_disable"
|
| 31 |
+
DEFAULT_SKIP_MODULES_PATTERN = ("pos_embed", "patch_embed", "norm", "^proj_in$", "^proj_out$")
|
| 32 |
+
# fmt: on
|
| 33 |
+
|
| 34 |
+
_SHOULD_DISABLE_PEFT_INPUT_AUTOCAST = is_peft_available() and is_peft_version(">", "0.14.0")
|
| 35 |
+
if _SHOULD_DISABLE_PEFT_INPUT_AUTOCAST:
|
| 36 |
+
from peft.helpers import disable_input_dtype_casting
|
| 37 |
+
from peft.tuners.tuners_utils import BaseTunerLayer
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class LayerwiseCastingHook(ModelHook):
|
| 41 |
+
r"""
|
| 42 |
+
A hook that casts the weights of a module to a high precision dtype for computation, and to a low precision dtype
|
| 43 |
+
for storage. This process may lead to quality loss in the output, but can significantly reduce the memory
|
| 44 |
+
footprint.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
_is_stateful = False
|
| 48 |
+
|
| 49 |
+
def __init__(self, storage_dtype: torch.dtype, compute_dtype: torch.dtype, non_blocking: bool) -> None:
|
| 50 |
+
self.storage_dtype = storage_dtype
|
| 51 |
+
self.compute_dtype = compute_dtype
|
| 52 |
+
self.non_blocking = non_blocking
|
| 53 |
+
|
| 54 |
+
def initialize_hook(self, module: torch.nn.Module):
|
| 55 |
+
module.to(dtype=self.storage_dtype, non_blocking=self.non_blocking)
|
| 56 |
+
return module
|
| 57 |
+
|
| 58 |
+
def deinitalize_hook(self, module: torch.nn.Module):
|
| 59 |
+
raise NotImplementedError(
|
| 60 |
+
"LayerwiseCastingHook does not support deinitialization. A model once enabled with layerwise casting will "
|
| 61 |
+
"have casted its weights to a lower precision dtype for storage. Casting this back to the original dtype "
|
| 62 |
+
"will lead to precision loss, which might have an impact on the model's generation quality. The model should "
|
| 63 |
+
"be re-initialized and loaded in the original dtype."
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
def pre_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 67 |
+
module.to(dtype=self.compute_dtype, non_blocking=self.non_blocking)
|
| 68 |
+
return args, kwargs
|
| 69 |
+
|
| 70 |
+
def post_forward(self, module: torch.nn.Module, output):
|
| 71 |
+
module.to(dtype=self.storage_dtype, non_blocking=self.non_blocking)
|
| 72 |
+
return output
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
class PeftInputAutocastDisableHook(ModelHook):
|
| 76 |
+
r"""
|
| 77 |
+
A hook that disables the casting of inputs to the module weight dtype during the forward pass. By default, PEFT
|
| 78 |
+
casts the inputs to the weight dtype of the module, which can lead to precision loss.
|
| 79 |
+
|
| 80 |
+
The reasons for needing this are:
|
| 81 |
+
- If we don't add PEFT layers' weight names to `skip_modules_pattern` when applying layerwise casting, the
|
| 82 |
+
inputs will be casted to the, possibly lower precision, storage dtype. Reference:
|
| 83 |
+
https://github.com/huggingface/peft/blob/0facdebf6208139cbd8f3586875acb378813dd97/src/peft/tuners/lora/layer.py#L706
|
| 84 |
+
- We can, on our end, use something like accelerate's `send_to_device` but for dtypes. This way, we can ensure
|
| 85 |
+
that the inputs are casted to the computation dtype correctly always. However, there are two goals we are
|
| 86 |
+
hoping to achieve:
|
| 87 |
+
1. Making forward implementations independent of device/dtype casting operations as much as possible.
|
| 88 |
+
2. Performing inference without losing information from casting to different precisions. With the current
|
| 89 |
+
PEFT implementation (as linked in the reference above), and assuming running layerwise casting inference
|
| 90 |
+
with storage_dtype=torch.float8_e4m3fn and compute_dtype=torch.bfloat16, inputs are cast to
|
| 91 |
+
torch.float8_e4m3fn in the lora layer. We will then upcast back to torch.bfloat16 when we continue the
|
| 92 |
+
forward pass in PEFT linear forward or Diffusers layer forward, with a `send_to_dtype` operation from
|
| 93 |
+
LayerwiseCastingHook. This will be a lossy operation and result in poorer generation quality.
|
| 94 |
+
"""
|
| 95 |
+
|
| 96 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 97 |
+
with disable_input_dtype_casting(module):
|
| 98 |
+
return self.fn_ref.original_forward(*args, **kwargs)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def apply_layerwise_casting(
|
| 102 |
+
module: torch.nn.Module,
|
| 103 |
+
storage_dtype: torch.dtype,
|
| 104 |
+
compute_dtype: torch.dtype,
|
| 105 |
+
skip_modules_pattern: str | tuple[str, ...] = "auto",
|
| 106 |
+
skip_modules_classes: tuple[Type[torch.nn.Module], ...] | None = None,
|
| 107 |
+
non_blocking: bool = False,
|
| 108 |
+
) -> None:
|
| 109 |
+
r"""
|
| 110 |
+
Applies layerwise casting to a given module. The module expected here is a Diffusers ModelMixin but it can be any
|
| 111 |
+
nn.Module using diffusers layers or pytorch primitives.
|
| 112 |
+
|
| 113 |
+
Example:
|
| 114 |
+
|
| 115 |
+
```python
|
| 116 |
+
>>> import torch
|
| 117 |
+
>>> from diffusers import CogVideoXTransformer3DModel
|
| 118 |
+
|
| 119 |
+
>>> transformer = CogVideoXTransformer3DModel.from_pretrained(
|
| 120 |
+
... model_id, subfolder="transformer", torch_dtype=torch.bfloat16
|
| 121 |
+
... )
|
| 122 |
+
|
| 123 |
+
>>> apply_layerwise_casting(
|
| 124 |
+
... transformer,
|
| 125 |
+
... storage_dtype=torch.float8_e4m3fn,
|
| 126 |
+
... compute_dtype=torch.bfloat16,
|
| 127 |
+
... skip_modules_pattern=["patch_embed", "norm", "proj_out"],
|
| 128 |
+
... non_blocking=True,
|
| 129 |
+
... )
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
Args:
|
| 133 |
+
module (`torch.nn.Module`):
|
| 134 |
+
The module whose leaf modules will be cast to a high precision dtype for computation, and to a low
|
| 135 |
+
precision dtype for storage.
|
| 136 |
+
storage_dtype (`torch.dtype`):
|
| 137 |
+
The dtype to cast the module to before/after the forward pass for storage.
|
| 138 |
+
compute_dtype (`torch.dtype`):
|
| 139 |
+
The dtype to cast the module to during the forward pass for computation.
|
| 140 |
+
skip_modules_pattern (`tuple[str, ...]`, defaults to `"auto"`):
|
| 141 |
+
A list of patterns to match the names of the modules to skip during the layerwise casting process. If set
|
| 142 |
+
to `"auto"`, the default patterns are used. If set to `None`, no modules are skipped. If set to `None`
|
| 143 |
+
alongside `skip_modules_classes` being `None`, the layerwise casting is applied directly to the module
|
| 144 |
+
instead of its internal submodules.
|
| 145 |
+
skip_modules_classes (`tuple[Type[torch.nn.Module], ...]`, defaults to `None`):
|
| 146 |
+
A list of module classes to skip during the layerwise casting process.
|
| 147 |
+
non_blocking (`bool`, defaults to `False`):
|
| 148 |
+
If `True`, the weight casting operations are non-blocking.
|
| 149 |
+
"""
|
| 150 |
+
if skip_modules_pattern == "auto":
|
| 151 |
+
skip_modules_pattern = DEFAULT_SKIP_MODULES_PATTERN
|
| 152 |
+
|
| 153 |
+
if skip_modules_classes is None and skip_modules_pattern is None:
|
| 154 |
+
apply_layerwise_casting_hook(module, storage_dtype, compute_dtype, non_blocking)
|
| 155 |
+
return
|
| 156 |
+
|
| 157 |
+
_apply_layerwise_casting(
|
| 158 |
+
module,
|
| 159 |
+
storage_dtype,
|
| 160 |
+
compute_dtype,
|
| 161 |
+
skip_modules_pattern,
|
| 162 |
+
skip_modules_classes,
|
| 163 |
+
non_blocking,
|
| 164 |
+
)
|
| 165 |
+
_disable_peft_input_autocast(module)
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def _apply_layerwise_casting(
|
| 169 |
+
module: torch.nn.Module,
|
| 170 |
+
storage_dtype: torch.dtype,
|
| 171 |
+
compute_dtype: torch.dtype,
|
| 172 |
+
skip_modules_pattern: tuple[str, ...] | None = None,
|
| 173 |
+
skip_modules_classes: tuple[Type[torch.nn.Module], ...] | None = None,
|
| 174 |
+
non_blocking: bool = False,
|
| 175 |
+
_prefix: str = "",
|
| 176 |
+
) -> None:
|
| 177 |
+
should_skip = (skip_modules_classes is not None and isinstance(module, skip_modules_classes)) or (
|
| 178 |
+
skip_modules_pattern is not None and any(re.search(pattern, _prefix) for pattern in skip_modules_pattern)
|
| 179 |
+
)
|
| 180 |
+
if should_skip:
|
| 181 |
+
logger.debug(f'Skipping layerwise casting for layer "{_prefix}"')
|
| 182 |
+
return
|
| 183 |
+
|
| 184 |
+
if isinstance(module, _GO_LC_SUPPORTED_PYTORCH_LAYERS):
|
| 185 |
+
logger.debug(f'Applying layerwise casting to layer "{_prefix}"')
|
| 186 |
+
apply_layerwise_casting_hook(module, storage_dtype, compute_dtype, non_blocking)
|
| 187 |
+
return
|
| 188 |
+
|
| 189 |
+
for name, submodule in module.named_children():
|
| 190 |
+
layer_name = f"{_prefix}.{name}" if _prefix else name
|
| 191 |
+
_apply_layerwise_casting(
|
| 192 |
+
submodule,
|
| 193 |
+
storage_dtype,
|
| 194 |
+
compute_dtype,
|
| 195 |
+
skip_modules_pattern,
|
| 196 |
+
skip_modules_classes,
|
| 197 |
+
non_blocking,
|
| 198 |
+
_prefix=layer_name,
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def apply_layerwise_casting_hook(
|
| 203 |
+
module: torch.nn.Module, storage_dtype: torch.dtype, compute_dtype: torch.dtype, non_blocking: bool
|
| 204 |
+
) -> None:
|
| 205 |
+
r"""
|
| 206 |
+
Applies a `LayerwiseCastingHook` to a given module.
|
| 207 |
+
|
| 208 |
+
Args:
|
| 209 |
+
module (`torch.nn.Module`):
|
| 210 |
+
The module to attach the hook to.
|
| 211 |
+
storage_dtype (`torch.dtype`):
|
| 212 |
+
The dtype to cast the module to before the forward pass.
|
| 213 |
+
compute_dtype (`torch.dtype`):
|
| 214 |
+
The dtype to cast the module to during the forward pass.
|
| 215 |
+
non_blocking (`bool`):
|
| 216 |
+
If `True`, the weight casting operations are non-blocking.
|
| 217 |
+
"""
|
| 218 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 219 |
+
hook = LayerwiseCastingHook(storage_dtype, compute_dtype, non_blocking)
|
| 220 |
+
registry.register_hook(hook, _LAYERWISE_CASTING_HOOK)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def _is_layerwise_casting_active(module: torch.nn.Module) -> bool:
|
| 224 |
+
for submodule in module.modules():
|
| 225 |
+
if (
|
| 226 |
+
hasattr(submodule, "_diffusers_hook")
|
| 227 |
+
and submodule._diffusers_hook.get_hook(_LAYERWISE_CASTING_HOOK) is not None
|
| 228 |
+
):
|
| 229 |
+
return True
|
| 230 |
+
return False
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def _disable_peft_input_autocast(module: torch.nn.Module) -> None:
|
| 234 |
+
if not _SHOULD_DISABLE_PEFT_INPUT_AUTOCAST:
|
| 235 |
+
return
|
| 236 |
+
for submodule in module.modules():
|
| 237 |
+
if isinstance(submodule, BaseTunerLayer) and _is_layerwise_casting_active(submodule):
|
| 238 |
+
registry = HookRegistry.check_if_exists_or_initialize(submodule)
|
| 239 |
+
hook = PeftInputAutocastDisableHook()
|
| 240 |
+
registry.register_hook(hook, _PEFT_AUTOCAST_DISABLE_HOOK)
|
third_party/diffusers/src/diffusers/hooks/mag_cache.py
ADDED
|
@@ -0,0 +1,468 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 dataclasses import dataclass
|
| 16 |
+
from typing import List, Optional, Tuple, Union
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
|
| 20 |
+
from ..utils import get_logger
|
| 21 |
+
from ..utils.torch_utils import unwrap_module
|
| 22 |
+
from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS
|
| 23 |
+
from ._helpers import TransformerBlockRegistry
|
| 24 |
+
from .hooks import BaseState, HookRegistry, ModelHook, StateManager
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 28 |
+
|
| 29 |
+
_MAG_CACHE_LEADER_BLOCK_HOOK = "mag_cache_leader_block_hook"
|
| 30 |
+
_MAG_CACHE_BLOCK_HOOK = "mag_cache_block_hook"
|
| 31 |
+
|
| 32 |
+
# Default Mag Ratios for Flux models (Dev/Schnell) are provided for convenience.
|
| 33 |
+
# Users must explicitly pass these to the config if using Flux.
|
| 34 |
+
# Reference: https://github.com/Zehong-Ma/MagCache
|
| 35 |
+
FLUX_MAG_RATIOS = torch.tensor(
|
| 36 |
+
[1.0]
|
| 37 |
+
+ [
|
| 38 |
+
1.21094,
|
| 39 |
+
1.11719,
|
| 40 |
+
1.07812,
|
| 41 |
+
1.0625,
|
| 42 |
+
1.03906,
|
| 43 |
+
1.03125,
|
| 44 |
+
1.03906,
|
| 45 |
+
1.02344,
|
| 46 |
+
1.03125,
|
| 47 |
+
1.02344,
|
| 48 |
+
0.98047,
|
| 49 |
+
1.01562,
|
| 50 |
+
1.00781,
|
| 51 |
+
1.0,
|
| 52 |
+
1.00781,
|
| 53 |
+
1.0,
|
| 54 |
+
1.00781,
|
| 55 |
+
1.0,
|
| 56 |
+
1.0,
|
| 57 |
+
0.99609,
|
| 58 |
+
0.99609,
|
| 59 |
+
0.98047,
|
| 60 |
+
0.98828,
|
| 61 |
+
0.96484,
|
| 62 |
+
0.95703,
|
| 63 |
+
0.93359,
|
| 64 |
+
0.89062,
|
| 65 |
+
]
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def nearest_interp(src_array: torch.Tensor, target_length: int) -> torch.Tensor:
|
| 70 |
+
"""
|
| 71 |
+
Interpolate the source array to the target length using nearest neighbor interpolation.
|
| 72 |
+
"""
|
| 73 |
+
src_length = len(src_array)
|
| 74 |
+
if target_length == 1:
|
| 75 |
+
return src_array[-1:]
|
| 76 |
+
|
| 77 |
+
scale = (src_length - 1) / (target_length - 1)
|
| 78 |
+
grid = torch.arange(target_length, device=src_array.device, dtype=torch.float32)
|
| 79 |
+
mapped_indices = torch.round(grid * scale).long()
|
| 80 |
+
return src_array[mapped_indices]
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
@dataclass
|
| 84 |
+
class MagCacheConfig:
|
| 85 |
+
r"""
|
| 86 |
+
Configuration for [MagCache](https://github.com/Zehong-Ma/MagCache).
|
| 87 |
+
|
| 88 |
+
Args:
|
| 89 |
+
threshold (`float`, defaults to `0.06`):
|
| 90 |
+
The threshold for the accumulated error. If the accumulated error is below this threshold, the block
|
| 91 |
+
computation is skipped. A higher threshold allows for more aggressive skipping (faster) but may degrade
|
| 92 |
+
quality.
|
| 93 |
+
max_skip_steps (`int`, defaults to `3`):
|
| 94 |
+
The maximum number of consecutive steps that can be skipped (K in the paper).
|
| 95 |
+
retention_ratio (`float`, defaults to `0.2`):
|
| 96 |
+
The fraction of initial steps during which skipping is disabled to ensure stability. For example, if
|
| 97 |
+
`num_inference_steps` is 28 and `retention_ratio` is 0.2, the first 6 steps will never be skipped.
|
| 98 |
+
num_inference_steps (`int`, defaults to `28`):
|
| 99 |
+
The number of inference steps used in the pipeline. This is required to interpolate `mag_ratios` correctly.
|
| 100 |
+
mag_ratios (`torch.Tensor`, *optional*):
|
| 101 |
+
The pre-computed magnitude ratios for the model. These are checkpoint-dependent. If not provided, you must
|
| 102 |
+
set `calibrate=True` to calculate them for your specific model. For Flux models, you can use
|
| 103 |
+
`diffusers.hooks.mag_cache.FLUX_MAG_RATIOS`.
|
| 104 |
+
calibrate (`bool`, defaults to `False`):
|
| 105 |
+
If True, enables calibration mode. In this mode, no blocks are skipped. Instead, the hook calculates the
|
| 106 |
+
magnitude ratios for the current run and logs them at the end. Use this to obtain `mag_ratios` for new
|
| 107 |
+
models or schedulers.
|
| 108 |
+
"""
|
| 109 |
+
|
| 110 |
+
threshold: float = 0.06
|
| 111 |
+
max_skip_steps: int = 3
|
| 112 |
+
retention_ratio: float = 0.2
|
| 113 |
+
num_inference_steps: int = 28
|
| 114 |
+
mag_ratios: Optional[Union[torch.Tensor, List[float]]] = None
|
| 115 |
+
calibrate: bool = False
|
| 116 |
+
|
| 117 |
+
def __post_init__(self):
|
| 118 |
+
# User MUST provide ratios OR enable calibration.
|
| 119 |
+
if self.mag_ratios is None and not self.calibrate:
|
| 120 |
+
raise ValueError(
|
| 121 |
+
" `mag_ratios` must be provided for MagCache inference because these ratios are model-dependent.\n"
|
| 122 |
+
"To get them for your model:\n"
|
| 123 |
+
"1. Initialize `MagCacheConfig(calibrate=True, ...)`\n"
|
| 124 |
+
"2. Run inference on your model once.\n"
|
| 125 |
+
"3. Copy the printed ratios array and pass it to `mag_ratios` in the config.\n"
|
| 126 |
+
"For Flux models, you can import `FLUX_MAG_RATIOS` from `diffusers.hooks.mag_cache`."
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
if not self.calibrate and self.mag_ratios is not None:
|
| 130 |
+
if not torch.is_tensor(self.mag_ratios):
|
| 131 |
+
self.mag_ratios = torch.tensor(self.mag_ratios)
|
| 132 |
+
|
| 133 |
+
if len(self.mag_ratios) != self.num_inference_steps:
|
| 134 |
+
logger.debug(
|
| 135 |
+
f"Interpolating mag_ratios from length {len(self.mag_ratios)} to {self.num_inference_steps}"
|
| 136 |
+
)
|
| 137 |
+
self.mag_ratios = nearest_interp(self.mag_ratios, self.num_inference_steps)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class MagCacheState(BaseState):
|
| 141 |
+
def __init__(self) -> None:
|
| 142 |
+
super().__init__()
|
| 143 |
+
# Cache for the residual (output - input) from the *previous* timestep
|
| 144 |
+
self.previous_residual: torch.Tensor = None
|
| 145 |
+
|
| 146 |
+
# State inputs/outputs for the current forward pass
|
| 147 |
+
self.head_block_input: Union[torch.Tensor, Tuple[torch.Tensor, ...]] = None
|
| 148 |
+
self.should_compute: bool = True
|
| 149 |
+
|
| 150 |
+
# MagCache accumulators
|
| 151 |
+
self.accumulated_ratio: float = 1.0
|
| 152 |
+
self.accumulated_err: float = 0.0
|
| 153 |
+
self.accumulated_steps: int = 0
|
| 154 |
+
|
| 155 |
+
# Current step counter (timestep index)
|
| 156 |
+
self.step_index: int = 0
|
| 157 |
+
|
| 158 |
+
# Calibration storage
|
| 159 |
+
self.calibration_ratios: List[float] = []
|
| 160 |
+
|
| 161 |
+
def reset(self):
|
| 162 |
+
self.previous_residual = None
|
| 163 |
+
self.should_compute = True
|
| 164 |
+
self.accumulated_ratio = 1.0
|
| 165 |
+
self.accumulated_err = 0.0
|
| 166 |
+
self.accumulated_steps = 0
|
| 167 |
+
self.step_index = 0
|
| 168 |
+
self.calibration_ratios = []
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class MagCacheHeadHook(ModelHook):
|
| 172 |
+
_is_stateful = True
|
| 173 |
+
|
| 174 |
+
def __init__(self, state_manager: StateManager, config: MagCacheConfig):
|
| 175 |
+
self.state_manager = state_manager
|
| 176 |
+
self.config = config
|
| 177 |
+
self._metadata = None
|
| 178 |
+
|
| 179 |
+
def initialize_hook(self, module):
|
| 180 |
+
unwrapped_module = unwrap_module(module)
|
| 181 |
+
self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
|
| 182 |
+
return module
|
| 183 |
+
|
| 184 |
+
@torch.compiler.disable
|
| 185 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 186 |
+
if self.state_manager._current_context is None:
|
| 187 |
+
self.state_manager.set_context("inference")
|
| 188 |
+
|
| 189 |
+
arg_name = self._metadata.hidden_states_argument_name
|
| 190 |
+
hidden_states = self._metadata._get_parameter_from_args_kwargs(arg_name, args, kwargs)
|
| 191 |
+
|
| 192 |
+
state: MagCacheState = self.state_manager.get_state()
|
| 193 |
+
state.head_block_input = hidden_states
|
| 194 |
+
|
| 195 |
+
should_compute = True
|
| 196 |
+
|
| 197 |
+
if self.config.calibrate:
|
| 198 |
+
# Never skip during calibration
|
| 199 |
+
should_compute = True
|
| 200 |
+
else:
|
| 201 |
+
# MagCache Logic
|
| 202 |
+
current_step = state.step_index
|
| 203 |
+
if current_step >= len(self.config.mag_ratios):
|
| 204 |
+
current_scale = 1.0
|
| 205 |
+
else:
|
| 206 |
+
current_scale = self.config.mag_ratios[current_step]
|
| 207 |
+
|
| 208 |
+
retention_step = int(self.config.retention_ratio * self.config.num_inference_steps + 0.5)
|
| 209 |
+
|
| 210 |
+
if current_step >= retention_step:
|
| 211 |
+
state.accumulated_ratio *= current_scale
|
| 212 |
+
state.accumulated_steps += 1
|
| 213 |
+
state.accumulated_err += abs(1.0 - state.accumulated_ratio)
|
| 214 |
+
|
| 215 |
+
if (
|
| 216 |
+
state.previous_residual is not None
|
| 217 |
+
and state.accumulated_err <= self.config.threshold
|
| 218 |
+
and state.accumulated_steps <= self.config.max_skip_steps
|
| 219 |
+
):
|
| 220 |
+
should_compute = False
|
| 221 |
+
else:
|
| 222 |
+
state.accumulated_ratio = 1.0
|
| 223 |
+
state.accumulated_steps = 0
|
| 224 |
+
state.accumulated_err = 0.0
|
| 225 |
+
|
| 226 |
+
state.should_compute = should_compute
|
| 227 |
+
|
| 228 |
+
if not should_compute:
|
| 229 |
+
logger.debug(f"MagCache: Skipping step {state.step_index}")
|
| 230 |
+
# Apply MagCache: Output = Input + Previous Residual
|
| 231 |
+
|
| 232 |
+
output = hidden_states
|
| 233 |
+
res = state.previous_residual
|
| 234 |
+
|
| 235 |
+
if res.device != output.device:
|
| 236 |
+
res = res.to(output.device)
|
| 237 |
+
|
| 238 |
+
# Attempt to apply residual handling shape mismatches (e.g., text+image vs image only)
|
| 239 |
+
if res.shape == output.shape:
|
| 240 |
+
output = output + res
|
| 241 |
+
elif (
|
| 242 |
+
output.ndim == 3
|
| 243 |
+
and res.ndim == 3
|
| 244 |
+
and output.shape[0] == res.shape[0]
|
| 245 |
+
and output.shape[2] == res.shape[2]
|
| 246 |
+
):
|
| 247 |
+
# Assuming concatenation where image part is at the end (standard in Flux/SD3)
|
| 248 |
+
diff = output.shape[1] - res.shape[1]
|
| 249 |
+
if diff > 0:
|
| 250 |
+
output = output.clone()
|
| 251 |
+
output[:, diff:, :] = output[:, diff:, :] + res
|
| 252 |
+
else:
|
| 253 |
+
logger.warning(
|
| 254 |
+
f"MagCache: Dimension mismatch. Input {output.shape}, Residual {res.shape}. "
|
| 255 |
+
"Cannot apply residual safely. Returning input without residual."
|
| 256 |
+
)
|
| 257 |
+
else:
|
| 258 |
+
logger.warning(
|
| 259 |
+
f"MagCache: Dimension mismatch. Input {output.shape}, Residual {res.shape}. "
|
| 260 |
+
"Cannot apply residual safely. Returning input without residual."
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
if self._metadata.return_encoder_hidden_states_index is not None:
|
| 264 |
+
original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
|
| 265 |
+
"encoder_hidden_states", args, kwargs
|
| 266 |
+
)
|
| 267 |
+
max_idx = max(
|
| 268 |
+
self._metadata.return_hidden_states_index, self._metadata.return_encoder_hidden_states_index
|
| 269 |
+
)
|
| 270 |
+
ret_list = [None] * (max_idx + 1)
|
| 271 |
+
ret_list[self._metadata.return_hidden_states_index] = output
|
| 272 |
+
ret_list[self._metadata.return_encoder_hidden_states_index] = original_encoder_hidden_states
|
| 273 |
+
return tuple(ret_list)
|
| 274 |
+
else:
|
| 275 |
+
return output
|
| 276 |
+
|
| 277 |
+
else:
|
| 278 |
+
# Compute original forward
|
| 279 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 280 |
+
return output
|
| 281 |
+
|
| 282 |
+
def reset_state(self, module):
|
| 283 |
+
self.state_manager.reset()
|
| 284 |
+
return module
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
class MagCacheBlockHook(ModelHook):
|
| 288 |
+
def __init__(self, state_manager: StateManager, is_tail: bool = False, config: MagCacheConfig = None):
|
| 289 |
+
super().__init__()
|
| 290 |
+
self.state_manager = state_manager
|
| 291 |
+
self.is_tail = is_tail
|
| 292 |
+
self.config = config
|
| 293 |
+
self._metadata = None
|
| 294 |
+
|
| 295 |
+
def initialize_hook(self, module):
|
| 296 |
+
unwrapped_module = unwrap_module(module)
|
| 297 |
+
self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
|
| 298 |
+
return module
|
| 299 |
+
|
| 300 |
+
@torch.compiler.disable
|
| 301 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 302 |
+
if self.state_manager._current_context is None:
|
| 303 |
+
self.state_manager.set_context("inference")
|
| 304 |
+
state: MagCacheState = self.state_manager.get_state()
|
| 305 |
+
|
| 306 |
+
if not state.should_compute:
|
| 307 |
+
arg_name = self._metadata.hidden_states_argument_name
|
| 308 |
+
hidden_states = self._metadata._get_parameter_from_args_kwargs(arg_name, args, kwargs)
|
| 309 |
+
|
| 310 |
+
if self.is_tail:
|
| 311 |
+
# Still need to advance step index even if we skip
|
| 312 |
+
self._advance_step(state)
|
| 313 |
+
|
| 314 |
+
if self._metadata.return_encoder_hidden_states_index is not None:
|
| 315 |
+
encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
|
| 316 |
+
"encoder_hidden_states", args, kwargs
|
| 317 |
+
)
|
| 318 |
+
max_idx = max(
|
| 319 |
+
self._metadata.return_hidden_states_index, self._metadata.return_encoder_hidden_states_index
|
| 320 |
+
)
|
| 321 |
+
ret_list = [None] * (max_idx + 1)
|
| 322 |
+
ret_list[self._metadata.return_hidden_states_index] = hidden_states
|
| 323 |
+
ret_list[self._metadata.return_encoder_hidden_states_index] = encoder_hidden_states
|
| 324 |
+
return tuple(ret_list)
|
| 325 |
+
|
| 326 |
+
return hidden_states
|
| 327 |
+
|
| 328 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 329 |
+
|
| 330 |
+
if self.is_tail:
|
| 331 |
+
# Calculate residual for next steps
|
| 332 |
+
if isinstance(output, tuple):
|
| 333 |
+
out_hidden = output[self._metadata.return_hidden_states_index]
|
| 334 |
+
else:
|
| 335 |
+
out_hidden = output
|
| 336 |
+
|
| 337 |
+
in_hidden = state.head_block_input
|
| 338 |
+
|
| 339 |
+
if in_hidden is None:
|
| 340 |
+
return output
|
| 341 |
+
|
| 342 |
+
# Determine residual
|
| 343 |
+
if out_hidden.shape == in_hidden.shape:
|
| 344 |
+
residual = out_hidden - in_hidden
|
| 345 |
+
elif out_hidden.ndim == 3 and in_hidden.ndim == 3 and out_hidden.shape[2] == in_hidden.shape[2]:
|
| 346 |
+
diff = in_hidden.shape[1] - out_hidden.shape[1]
|
| 347 |
+
if diff == 0:
|
| 348 |
+
residual = out_hidden - in_hidden
|
| 349 |
+
else:
|
| 350 |
+
residual = out_hidden - in_hidden # Fallback to matching tail
|
| 351 |
+
else:
|
| 352 |
+
# Fallback for completely mismatched shapes
|
| 353 |
+
residual = out_hidden
|
| 354 |
+
|
| 355 |
+
if self.config.calibrate:
|
| 356 |
+
self._perform_calibration_step(state, residual)
|
| 357 |
+
|
| 358 |
+
state.previous_residual = residual
|
| 359 |
+
self._advance_step(state)
|
| 360 |
+
|
| 361 |
+
return output
|
| 362 |
+
|
| 363 |
+
def _perform_calibration_step(self, state: MagCacheState, current_residual: torch.Tensor):
|
| 364 |
+
if state.previous_residual is None:
|
| 365 |
+
# First step has no previous residual to compare against.
|
| 366 |
+
# log 1.0 as a neutral starting point.
|
| 367 |
+
ratio = 1.0
|
| 368 |
+
else:
|
| 369 |
+
# MagCache Calibration Formula: mean(norm(curr) / norm(prev))
|
| 370 |
+
# norm(dim=-1) gives magnitude of each token vector
|
| 371 |
+
curr_norm = torch.linalg.norm(current_residual.float(), dim=-1)
|
| 372 |
+
prev_norm = torch.linalg.norm(state.previous_residual.float(), dim=-1)
|
| 373 |
+
|
| 374 |
+
# Avoid division by zero
|
| 375 |
+
ratio = (curr_norm / (prev_norm + 1e-8)).mean().item()
|
| 376 |
+
|
| 377 |
+
state.calibration_ratios.append(ratio)
|
| 378 |
+
|
| 379 |
+
def _advance_step(self, state: MagCacheState):
|
| 380 |
+
state.step_index += 1
|
| 381 |
+
if state.step_index >= self.config.num_inference_steps:
|
| 382 |
+
# End of inference loop
|
| 383 |
+
if self.config.calibrate:
|
| 384 |
+
print("\n[MagCache] Calibration Complete. Copy these values to MagCacheConfig(mag_ratios=...):")
|
| 385 |
+
print(f"{state.calibration_ratios}\n")
|
| 386 |
+
logger.info(f"MagCache Calibration Results: {state.calibration_ratios}")
|
| 387 |
+
|
| 388 |
+
# Reset state
|
| 389 |
+
state.step_index = 0
|
| 390 |
+
state.accumulated_ratio = 1.0
|
| 391 |
+
state.accumulated_steps = 0
|
| 392 |
+
state.accumulated_err = 0.0
|
| 393 |
+
state.previous_residual = None
|
| 394 |
+
state.calibration_ratios = []
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def apply_mag_cache(module: torch.nn.Module, config: MagCacheConfig) -> None:
|
| 398 |
+
"""
|
| 399 |
+
Applies MagCache to a given module (typically a Transformer).
|
| 400 |
+
|
| 401 |
+
Args:
|
| 402 |
+
module (`torch.nn.Module`):
|
| 403 |
+
The module to apply MagCache to.
|
| 404 |
+
config (`MagCacheConfig`):
|
| 405 |
+
The configuration for MagCache.
|
| 406 |
+
"""
|
| 407 |
+
# Initialize registry on the root module so the Pipeline can set context.
|
| 408 |
+
HookRegistry.check_if_exists_or_initialize(module)
|
| 409 |
+
|
| 410 |
+
state_manager = StateManager(MagCacheState, (), {})
|
| 411 |
+
remaining_blocks = []
|
| 412 |
+
|
| 413 |
+
for name, submodule in module.named_children():
|
| 414 |
+
if name not in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS or not isinstance(submodule, torch.nn.ModuleList):
|
| 415 |
+
continue
|
| 416 |
+
for index, block in enumerate(submodule):
|
| 417 |
+
remaining_blocks.append((f"{name}.{index}", block))
|
| 418 |
+
|
| 419 |
+
if not remaining_blocks:
|
| 420 |
+
logger.warning("MagCache: No transformer blocks found to apply hooks.")
|
| 421 |
+
return
|
| 422 |
+
|
| 423 |
+
# Handle single-block models
|
| 424 |
+
if len(remaining_blocks) == 1:
|
| 425 |
+
name, block = remaining_blocks[0]
|
| 426 |
+
logger.info(f"MagCache: Applying Head+Tail Hooks to single block '{name}'")
|
| 427 |
+
_apply_mag_cache_block_hook(block, state_manager, config, is_tail=True)
|
| 428 |
+
_apply_mag_cache_head_hook(block, state_manager, config)
|
| 429 |
+
return
|
| 430 |
+
|
| 431 |
+
head_block_name, head_block = remaining_blocks.pop(0)
|
| 432 |
+
tail_block_name, tail_block = remaining_blocks.pop(-1)
|
| 433 |
+
|
| 434 |
+
logger.info(f"MagCache: Applying Head Hook to {head_block_name}")
|
| 435 |
+
_apply_mag_cache_head_hook(head_block, state_manager, config)
|
| 436 |
+
|
| 437 |
+
for name, block in remaining_blocks:
|
| 438 |
+
_apply_mag_cache_block_hook(block, state_manager, config)
|
| 439 |
+
|
| 440 |
+
logger.info(f"MagCache: Applying Tail Hook to {tail_block_name}")
|
| 441 |
+
_apply_mag_cache_block_hook(tail_block, state_manager, config, is_tail=True)
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
def _apply_mag_cache_head_hook(block: torch.nn.Module, state_manager: StateManager, config: MagCacheConfig) -> None:
|
| 445 |
+
registry = HookRegistry.check_if_exists_or_initialize(block)
|
| 446 |
+
|
| 447 |
+
# Automatically remove existing hook to allow re-application (e.g. switching modes)
|
| 448 |
+
if registry.get_hook(_MAG_CACHE_LEADER_BLOCK_HOOK) is not None:
|
| 449 |
+
registry.remove_hook(_MAG_CACHE_LEADER_BLOCK_HOOK)
|
| 450 |
+
|
| 451 |
+
hook = MagCacheHeadHook(state_manager, config)
|
| 452 |
+
registry.register_hook(hook, _MAG_CACHE_LEADER_BLOCK_HOOK)
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
def _apply_mag_cache_block_hook(
|
| 456 |
+
block: torch.nn.Module,
|
| 457 |
+
state_manager: StateManager,
|
| 458 |
+
config: MagCacheConfig,
|
| 459 |
+
is_tail: bool = False,
|
| 460 |
+
) -> None:
|
| 461 |
+
registry = HookRegistry.check_if_exists_or_initialize(block)
|
| 462 |
+
|
| 463 |
+
# Automatically remove existing hook to allow re-application
|
| 464 |
+
if registry.get_hook(_MAG_CACHE_BLOCK_HOOK) is not None:
|
| 465 |
+
registry.remove_hook(_MAG_CACHE_BLOCK_HOOK)
|
| 466 |
+
|
| 467 |
+
hook = MagCacheBlockHook(state_manager, is_tail, config)
|
| 468 |
+
registry.register_hook(hook, _MAG_CACHE_BLOCK_HOOK)
|
third_party/diffusers/src/diffusers/hooks/pyramid_attention_broadcast.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 re
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
from typing import Any, Callable
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
|
| 21 |
+
from ..models.attention import AttentionModuleMixin
|
| 22 |
+
from ..models.attention_processor import Attention, MochiAttention
|
| 23 |
+
from ..utils import logging
|
| 24 |
+
from ._common import (
|
| 25 |
+
_ATTENTION_CLASSES,
|
| 26 |
+
_CROSS_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 27 |
+
_SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 28 |
+
_TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS,
|
| 29 |
+
)
|
| 30 |
+
from .hooks import HookRegistry, ModelHook
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
_PYRAMID_ATTENTION_BROADCAST_HOOK = "pyramid_attention_broadcast"
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
@dataclass
|
| 40 |
+
class PyramidAttentionBroadcastConfig:
|
| 41 |
+
r"""
|
| 42 |
+
Configuration for Pyramid Attention Broadcast.
|
| 43 |
+
|
| 44 |
+
Args:
|
| 45 |
+
spatial_attention_block_skip_range (`int`, *optional*, defaults to `None`):
|
| 46 |
+
The number of times a specific spatial attention broadcast is skipped before computing the attention states
|
| 47 |
+
to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e.,
|
| 48 |
+
old attention states will be reused) before computing the new attention states again.
|
| 49 |
+
temporal_attention_block_skip_range (`int`, *optional*, defaults to `None`):
|
| 50 |
+
The number of times a specific temporal attention broadcast is skipped before computing the attention
|
| 51 |
+
states to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times
|
| 52 |
+
(i.e., old attention states will be reused) before computing the new attention states again.
|
| 53 |
+
cross_attention_block_skip_range (`int`, *optional*, defaults to `None`):
|
| 54 |
+
The number of times a specific cross-attention broadcast is skipped before computing the attention states
|
| 55 |
+
to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e.,
|
| 56 |
+
old attention states will be reused) before computing the new attention states again.
|
| 57 |
+
spatial_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
|
| 58 |
+
The range of timesteps to skip in the spatial attention layer. The attention computations will be
|
| 59 |
+
conditionally skipped if the current timestep is within the specified range.
|
| 60 |
+
temporal_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
|
| 61 |
+
The range of timesteps to skip in the temporal attention layer. The attention computations will be
|
| 62 |
+
conditionally skipped if the current timestep is within the specified range.
|
| 63 |
+
cross_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
|
| 64 |
+
The range of timesteps to skip in the cross-attention layer. The attention computations will be
|
| 65 |
+
conditionally skipped if the current timestep is within the specified range.
|
| 66 |
+
spatial_attention_block_identifiers (`tuple[str, ...]`):
|
| 67 |
+
The identifiers to match against the layer names to determine if the layer is a spatial attention layer.
|
| 68 |
+
temporal_attention_block_identifiers (`tuple[str, ...]`):
|
| 69 |
+
The identifiers to match against the layer names to determine if the layer is a temporal attention layer.
|
| 70 |
+
cross_attention_block_identifiers (`tuple[str, ...]`):
|
| 71 |
+
The identifiers to match against the layer names to determine if the layer is a cross-attention layer.
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
spatial_attention_block_skip_range: int | None = None
|
| 75 |
+
temporal_attention_block_skip_range: int | None = None
|
| 76 |
+
cross_attention_block_skip_range: int | None = None
|
| 77 |
+
|
| 78 |
+
spatial_attention_timestep_skip_range: tuple[int, int] = (100, 800)
|
| 79 |
+
temporal_attention_timestep_skip_range: tuple[int, int] = (100, 800)
|
| 80 |
+
cross_attention_timestep_skip_range: tuple[int, int] = (100, 800)
|
| 81 |
+
|
| 82 |
+
spatial_attention_block_identifiers: tuple[str, ...] = _SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS
|
| 83 |
+
temporal_attention_block_identifiers: tuple[str, ...] = _TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS
|
| 84 |
+
cross_attention_block_identifiers: tuple[str, ...] = _CROSS_TRANSFORMER_BLOCK_IDENTIFIERS
|
| 85 |
+
|
| 86 |
+
current_timestep_callback: Callable[[], int] = None
|
| 87 |
+
|
| 88 |
+
# TODO(aryan): add PAB for MLP layers (very limited speedup from testing with original codebase
|
| 89 |
+
# so not added for now)
|
| 90 |
+
|
| 91 |
+
def __repr__(self) -> str:
|
| 92 |
+
return (
|
| 93 |
+
f"PyramidAttentionBroadcastConfig(\n"
|
| 94 |
+
f" spatial_attention_block_skip_range={self.spatial_attention_block_skip_range},\n"
|
| 95 |
+
f" temporal_attention_block_skip_range={self.temporal_attention_block_skip_range},\n"
|
| 96 |
+
f" cross_attention_block_skip_range={self.cross_attention_block_skip_range},\n"
|
| 97 |
+
f" spatial_attention_timestep_skip_range={self.spatial_attention_timestep_skip_range},\n"
|
| 98 |
+
f" temporal_attention_timestep_skip_range={self.temporal_attention_timestep_skip_range},\n"
|
| 99 |
+
f" cross_attention_timestep_skip_range={self.cross_attention_timestep_skip_range},\n"
|
| 100 |
+
f" spatial_attention_block_identifiers={self.spatial_attention_block_identifiers},\n"
|
| 101 |
+
f" temporal_attention_block_identifiers={self.temporal_attention_block_identifiers},\n"
|
| 102 |
+
f" cross_attention_block_identifiers={self.cross_attention_block_identifiers},\n"
|
| 103 |
+
f" current_timestep_callback={self.current_timestep_callback}\n"
|
| 104 |
+
")"
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
class PyramidAttentionBroadcastState:
|
| 109 |
+
r"""
|
| 110 |
+
State for Pyramid Attention Broadcast.
|
| 111 |
+
|
| 112 |
+
Attributes:
|
| 113 |
+
iteration (`int`):
|
| 114 |
+
The current iteration of the Pyramid Attention Broadcast. It is necessary to ensure that `reset_state` is
|
| 115 |
+
called before starting a new inference forward pass for PAB to work correctly.
|
| 116 |
+
cache (`Any`):
|
| 117 |
+
The cached output from the previous forward pass. This is used to re-use the attention states when the
|
| 118 |
+
attention computation is skipped. It is either a tensor or a tuple of tensors, depending on the module.
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
def __init__(self) -> None:
|
| 122 |
+
self.iteration = 0
|
| 123 |
+
self.cache = None
|
| 124 |
+
|
| 125 |
+
def reset(self):
|
| 126 |
+
self.iteration = 0
|
| 127 |
+
self.cache = None
|
| 128 |
+
|
| 129 |
+
def __repr__(self):
|
| 130 |
+
cache_repr = ""
|
| 131 |
+
if self.cache is None:
|
| 132 |
+
cache_repr = "None"
|
| 133 |
+
else:
|
| 134 |
+
cache_repr = f"Tensor(shape={self.cache.shape}, dtype={self.cache.dtype})"
|
| 135 |
+
return f"PyramidAttentionBroadcastState(iteration={self.iteration}, cache={cache_repr})"
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class PyramidAttentionBroadcastHook(ModelHook):
|
| 139 |
+
r"""A hook that applies Pyramid Attention Broadcast to a given module."""
|
| 140 |
+
|
| 141 |
+
_is_stateful = True
|
| 142 |
+
|
| 143 |
+
def __init__(
|
| 144 |
+
self, timestep_skip_range: tuple[int, int], block_skip_range: int, current_timestep_callback: Callable[[], int]
|
| 145 |
+
) -> None:
|
| 146 |
+
super().__init__()
|
| 147 |
+
|
| 148 |
+
self.timestep_skip_range = timestep_skip_range
|
| 149 |
+
self.block_skip_range = block_skip_range
|
| 150 |
+
self.current_timestep_callback = current_timestep_callback
|
| 151 |
+
|
| 152 |
+
def initialize_hook(self, module):
|
| 153 |
+
self.state = PyramidAttentionBroadcastState()
|
| 154 |
+
return module
|
| 155 |
+
|
| 156 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
|
| 157 |
+
is_within_timestep_range = (
|
| 158 |
+
self.timestep_skip_range[0] < self.current_timestep_callback() < self.timestep_skip_range[1]
|
| 159 |
+
)
|
| 160 |
+
should_compute_attention = (
|
| 161 |
+
self.state.cache is None
|
| 162 |
+
or self.state.iteration == 0
|
| 163 |
+
or not is_within_timestep_range
|
| 164 |
+
or self.state.iteration % self.block_skip_range == 0
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
if should_compute_attention:
|
| 168 |
+
output = self.fn_ref.original_forward(*args, **kwargs)
|
| 169 |
+
else:
|
| 170 |
+
output = self.state.cache
|
| 171 |
+
|
| 172 |
+
self.state.cache = output
|
| 173 |
+
self.state.iteration += 1
|
| 174 |
+
return output
|
| 175 |
+
|
| 176 |
+
def reset_state(self, module: torch.nn.Module) -> None:
|
| 177 |
+
self.state.reset()
|
| 178 |
+
return module
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def apply_pyramid_attention_broadcast(module: torch.nn.Module, config: PyramidAttentionBroadcastConfig):
|
| 182 |
+
r"""
|
| 183 |
+
Apply [Pyramid Attention Broadcast](https://huggingface.co/papers/2408.12588) to a given pipeline.
|
| 184 |
+
|
| 185 |
+
PAB is an attention approximation method that leverages the similarity in attention states between timesteps to
|
| 186 |
+
reduce the computational cost of attention computation. The key takeaway from the paper is that the attention
|
| 187 |
+
similarity in the cross-attention layers between timesteps is high, followed by less similarity in the temporal and
|
| 188 |
+
spatial layers. This allows for the skipping of attention computation in the cross-attention layers more frequently
|
| 189 |
+
than in the temporal and spatial layers. Applying PAB will, therefore, speedup the inference process.
|
| 190 |
+
|
| 191 |
+
Args:
|
| 192 |
+
module (`torch.nn.Module`):
|
| 193 |
+
The module to apply Pyramid Attention Broadcast to.
|
| 194 |
+
config (`PyramidAttentionBroadcastConfig | None`, `optional`, defaults to `None`):
|
| 195 |
+
The configuration to use for Pyramid Attention Broadcast.
|
| 196 |
+
|
| 197 |
+
Example:
|
| 198 |
+
|
| 199 |
+
```python
|
| 200 |
+
>>> import torch
|
| 201 |
+
>>> from diffusers import CogVideoXPipeline, PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
|
| 202 |
+
>>> from diffusers.utils import export_to_video
|
| 203 |
+
|
| 204 |
+
>>> pipe = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
|
| 205 |
+
>>> pipe.to("cuda")
|
| 206 |
+
|
| 207 |
+
>>> config = PyramidAttentionBroadcastConfig(
|
| 208 |
+
... spatial_attention_block_skip_range=2,
|
| 209 |
+
... spatial_attention_timestep_skip_range=(100, 800),
|
| 210 |
+
... current_timestep_callback=lambda: pipe.current_timestep,
|
| 211 |
+
... )
|
| 212 |
+
>>> apply_pyramid_attention_broadcast(pipe.transformer, config)
|
| 213 |
+
```
|
| 214 |
+
"""
|
| 215 |
+
if config.current_timestep_callback is None:
|
| 216 |
+
raise ValueError(
|
| 217 |
+
"The `current_timestep_callback` function must be provided in the configuration to apply Pyramid Attention Broadcast."
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
if (
|
| 221 |
+
config.spatial_attention_block_skip_range is None
|
| 222 |
+
and config.temporal_attention_block_skip_range is None
|
| 223 |
+
and config.cross_attention_block_skip_range is None
|
| 224 |
+
):
|
| 225 |
+
logger.warning(
|
| 226 |
+
"Pyramid Attention Broadcast requires one or more of `spatial_attention_block_skip_range`, `temporal_attention_block_skip_range` "
|
| 227 |
+
"or `cross_attention_block_skip_range` parameters to be set to an integer, not `None`. Defaulting to using `spatial_attention_block_skip_range=2`. "
|
| 228 |
+
"To avoid this warning, please set one of the above parameters."
|
| 229 |
+
)
|
| 230 |
+
config.spatial_attention_block_skip_range = 2
|
| 231 |
+
|
| 232 |
+
for name, submodule in module.named_modules():
|
| 233 |
+
if not isinstance(submodule, (*_ATTENTION_CLASSES, AttentionModuleMixin)):
|
| 234 |
+
# PAB has been implemented specific to Diffusers' Attention classes. However, this does not mean that PAB
|
| 235 |
+
# cannot be applied to this layer. For custom layers, users can extend this functionality and implement
|
| 236 |
+
# their own PAB logic similar to `_apply_pyramid_attention_broadcast_on_attention_class`.
|
| 237 |
+
continue
|
| 238 |
+
_apply_pyramid_attention_broadcast_on_attention_class(name, submodule, config)
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def _apply_pyramid_attention_broadcast_on_attention_class(
|
| 242 |
+
name: str, module: Attention, config: PyramidAttentionBroadcastConfig
|
| 243 |
+
) -> bool:
|
| 244 |
+
is_spatial_self_attention = (
|
| 245 |
+
any(re.search(identifier, name) is not None for identifier in config.spatial_attention_block_identifiers)
|
| 246 |
+
and config.spatial_attention_block_skip_range is not None
|
| 247 |
+
and not getattr(module, "is_cross_attention", False)
|
| 248 |
+
)
|
| 249 |
+
is_temporal_self_attention = (
|
| 250 |
+
any(re.search(identifier, name) is not None for identifier in config.temporal_attention_block_identifiers)
|
| 251 |
+
and config.temporal_attention_block_skip_range is not None
|
| 252 |
+
and not getattr(module, "is_cross_attention", False)
|
| 253 |
+
)
|
| 254 |
+
is_cross_attention = (
|
| 255 |
+
any(re.search(identifier, name) is not None for identifier in config.cross_attention_block_identifiers)
|
| 256 |
+
and config.cross_attention_block_skip_range is not None
|
| 257 |
+
and getattr(module, "is_cross_attention", False)
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
block_skip_range, timestep_skip_range, block_type = None, None, None
|
| 261 |
+
if is_spatial_self_attention:
|
| 262 |
+
block_skip_range = config.spatial_attention_block_skip_range
|
| 263 |
+
timestep_skip_range = config.spatial_attention_timestep_skip_range
|
| 264 |
+
block_type = "spatial"
|
| 265 |
+
elif is_temporal_self_attention:
|
| 266 |
+
block_skip_range = config.temporal_attention_block_skip_range
|
| 267 |
+
timestep_skip_range = config.temporal_attention_timestep_skip_range
|
| 268 |
+
block_type = "temporal"
|
| 269 |
+
elif is_cross_attention:
|
| 270 |
+
block_skip_range = config.cross_attention_block_skip_range
|
| 271 |
+
timestep_skip_range = config.cross_attention_timestep_skip_range
|
| 272 |
+
block_type = "cross"
|
| 273 |
+
|
| 274 |
+
if block_skip_range is None or timestep_skip_range is None:
|
| 275 |
+
logger.info(
|
| 276 |
+
f'Unable to apply Pyramid Attention Broadcast to the selected layer: "{name}" because it does '
|
| 277 |
+
f"not match any of the required criteria for spatial, temporal or cross attention layers. Note, "
|
| 278 |
+
f"however, that this layer may still be valid for applying PAB. Please specify the correct "
|
| 279 |
+
f"block identifiers in the configuration."
|
| 280 |
+
)
|
| 281 |
+
return False
|
| 282 |
+
|
| 283 |
+
logger.debug(f"Enabling Pyramid Attention Broadcast ({block_type}) in layer: {name}")
|
| 284 |
+
_apply_pyramid_attention_broadcast_hook(
|
| 285 |
+
module, timestep_skip_range, block_skip_range, config.current_timestep_callback
|
| 286 |
+
)
|
| 287 |
+
return True
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def _apply_pyramid_attention_broadcast_hook(
|
| 291 |
+
module: Attention | MochiAttention,
|
| 292 |
+
timestep_skip_range: tuple[int, int],
|
| 293 |
+
block_skip_range: int,
|
| 294 |
+
current_timestep_callback: Callable[[], int],
|
| 295 |
+
):
|
| 296 |
+
r"""
|
| 297 |
+
Apply [Pyramid Attention Broadcast](https://huggingface.co/papers/2408.12588) to a given torch.nn.Module.
|
| 298 |
+
|
| 299 |
+
Args:
|
| 300 |
+
module (`torch.nn.Module`):
|
| 301 |
+
The module to apply Pyramid Attention Broadcast to.
|
| 302 |
+
timestep_skip_range (`tuple[int, int]`):
|
| 303 |
+
The range of timesteps to skip in the attention layer. The attention computations will be conditionally
|
| 304 |
+
skipped if the current timestep is within the specified range.
|
| 305 |
+
block_skip_range (`int`):
|
| 306 |
+
The number of times a specific attention broadcast is skipped before computing the attention states to
|
| 307 |
+
re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e., old
|
| 308 |
+
attention states will be reused) before computing the new attention states again.
|
| 309 |
+
current_timestep_callback (`Callable[[], int]`):
|
| 310 |
+
A callback function that returns the current inference timestep.
|
| 311 |
+
"""
|
| 312 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 313 |
+
hook = PyramidAttentionBroadcastHook(timestep_skip_range, block_skip_range, current_timestep_callback)
|
| 314 |
+
registry.register_hook(hook, _PYRAMID_ATTENTION_BROADCAST_HOOK)
|
third_party/diffusers/src/diffusers/hooks/smoothed_energy_guidance_utils.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 math
|
| 16 |
+
from dataclasses import asdict, dataclass
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
import torch.nn.functional as F
|
| 20 |
+
|
| 21 |
+
from ..utils import get_logger
|
| 22 |
+
from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS, _ATTENTION_CLASSES, _get_submodule_from_fqn
|
| 23 |
+
from .hooks import HookRegistry, ModelHook
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
logger = get_logger(__name__) # pylint: disable=invalid-name
|
| 27 |
+
|
| 28 |
+
_SMOOTHED_ENERGY_GUIDANCE_HOOK = "smoothed_energy_guidance_hook"
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class SmoothedEnergyGuidanceConfig:
|
| 33 |
+
r"""
|
| 34 |
+
Configuration for skipping internal transformer blocks when executing a transformer model.
|
| 35 |
+
|
| 36 |
+
Args:
|
| 37 |
+
indices (`list[int]`):
|
| 38 |
+
The indices of the layer to skip. This is typically the first layer in the transformer block.
|
| 39 |
+
fqn (`str`, defaults to `"auto"`):
|
| 40 |
+
The fully qualified name identifying the stack of transformer blocks. Typically, this is
|
| 41 |
+
`transformer_blocks`, `single_transformer_blocks`, `blocks`, `layers`, or `temporal_transformer_blocks`.
|
| 42 |
+
For automatic detection, set this to `"auto"`. "auto" only works on DiT models. For UNet models, you must
|
| 43 |
+
provide the correct fqn.
|
| 44 |
+
_query_proj_identifiers (`list[str]`, defaults to `None`):
|
| 45 |
+
The identifiers for the query projection layers. Typically, these are `to_q`, `query`, or `q_proj`. If
|
| 46 |
+
`None`, `to_q` is used by default.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
indices: list[int]
|
| 50 |
+
fqn: str = "auto"
|
| 51 |
+
_query_proj_identifiers: list[str] = None
|
| 52 |
+
|
| 53 |
+
def to_dict(self):
|
| 54 |
+
return asdict(self)
|
| 55 |
+
|
| 56 |
+
@staticmethod
|
| 57 |
+
def from_dict(data: dict) -> "SmoothedEnergyGuidanceConfig":
|
| 58 |
+
return SmoothedEnergyGuidanceConfig(**data)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class SmoothedEnergyGuidanceHook(ModelHook):
|
| 62 |
+
def __init__(self, blur_sigma: float = 1.0, blur_threshold_inf: float = 9999.9) -> None:
|
| 63 |
+
super().__init__()
|
| 64 |
+
self.blur_sigma = blur_sigma
|
| 65 |
+
self.blur_threshold_inf = blur_threshold_inf
|
| 66 |
+
|
| 67 |
+
def post_forward(self, module: torch.nn.Module, output: torch.Tensor) -> torch.Tensor:
|
| 68 |
+
# Copied from https://github.com/SusungHong/SEG-SDXL/blob/cf8256d640d5373541cfea3b3b6caf93272cf986/pipeline_seg.py#L172C31-L172C102
|
| 69 |
+
kernel_size = math.ceil(6 * self.blur_sigma) + 1 - math.ceil(6 * self.blur_sigma) % 2
|
| 70 |
+
smoothed_output = _gaussian_blur_2d(output, kernel_size, self.blur_sigma, self.blur_threshold_inf)
|
| 71 |
+
return smoothed_output
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def _apply_smoothed_energy_guidance_hook(
|
| 75 |
+
module: torch.nn.Module, config: SmoothedEnergyGuidanceConfig, blur_sigma: float, name: str | None = None
|
| 76 |
+
) -> None:
|
| 77 |
+
name = name or _SMOOTHED_ENERGY_GUIDANCE_HOOK
|
| 78 |
+
|
| 79 |
+
if config.fqn == "auto":
|
| 80 |
+
for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS:
|
| 81 |
+
if hasattr(module, identifier):
|
| 82 |
+
config.fqn = identifier
|
| 83 |
+
break
|
| 84 |
+
else:
|
| 85 |
+
raise ValueError(
|
| 86 |
+
"Could not find a suitable identifier for the transformer blocks automatically. Please provide a valid "
|
| 87 |
+
"`fqn` (fully qualified name) that identifies a stack of transformer blocks."
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
if config._query_proj_identifiers is None:
|
| 91 |
+
config._query_proj_identifiers = ["to_q"]
|
| 92 |
+
|
| 93 |
+
transformer_blocks = _get_submodule_from_fqn(module, config.fqn)
|
| 94 |
+
blocks_found = False
|
| 95 |
+
for i, block in enumerate(transformer_blocks):
|
| 96 |
+
if i not in config.indices:
|
| 97 |
+
continue
|
| 98 |
+
|
| 99 |
+
blocks_found = True
|
| 100 |
+
|
| 101 |
+
for submodule_name, submodule in block.named_modules():
|
| 102 |
+
if not isinstance(submodule, _ATTENTION_CLASSES) or submodule.is_cross_attention:
|
| 103 |
+
continue
|
| 104 |
+
for identifier in config._query_proj_identifiers:
|
| 105 |
+
query_proj = getattr(submodule, identifier, None)
|
| 106 |
+
if query_proj is None or not isinstance(query_proj, torch.nn.Linear):
|
| 107 |
+
continue
|
| 108 |
+
logger.debug(
|
| 109 |
+
f"Registering smoothed energy guidance hook on {config.fqn}.{i}.{submodule_name}.{identifier}"
|
| 110 |
+
)
|
| 111 |
+
registry = HookRegistry.check_if_exists_or_initialize(query_proj)
|
| 112 |
+
hook = SmoothedEnergyGuidanceHook(blur_sigma)
|
| 113 |
+
registry.register_hook(hook, name)
|
| 114 |
+
|
| 115 |
+
if not blocks_found:
|
| 116 |
+
raise ValueError(
|
| 117 |
+
f"Could not find any transformer blocks matching the provided indices {config.indices} and "
|
| 118 |
+
f"fully qualified name '{config.fqn}'. Please check the indices and fqn for correctness."
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
# Modified from https://github.com/SusungHong/SEG-SDXL/blob/cf8256d640d5373541cfea3b3b6caf93272cf986/pipeline_seg.py#L71
|
| 123 |
+
def _gaussian_blur_2d(query: torch.Tensor, kernel_size: int, sigma: float, sigma_threshold_inf: float) -> torch.Tensor:
|
| 124 |
+
"""
|
| 125 |
+
This implementation assumes that the input query is for visual (image/videos) tokens to apply the 2D gaussian blur.
|
| 126 |
+
However, some models use joint text-visual token attention for which this may not be suitable. Additionally, this
|
| 127 |
+
implementation also assumes that the visual tokens come from a square image/video. In practice, despite these
|
| 128 |
+
assumptions, applying the 2D square gaussian blur on the query projections generates reasonable results for
|
| 129 |
+
Smoothed Energy Guidance.
|
| 130 |
+
|
| 131 |
+
SEG is only supported as an experimental prototype feature for now, so the implementation may be modified in the
|
| 132 |
+
future without warning or guarantee of reproducibility.
|
| 133 |
+
"""
|
| 134 |
+
assert query.ndim == 3
|
| 135 |
+
|
| 136 |
+
is_inf = sigma > sigma_threshold_inf
|
| 137 |
+
batch_size, seq_len, embed_dim = query.shape
|
| 138 |
+
|
| 139 |
+
seq_len_sqrt = int(math.sqrt(seq_len))
|
| 140 |
+
num_square_tokens = seq_len_sqrt * seq_len_sqrt
|
| 141 |
+
query_slice = query[:, :num_square_tokens, :]
|
| 142 |
+
query_slice = query_slice.permute(0, 2, 1)
|
| 143 |
+
query_slice = query_slice.reshape(batch_size, embed_dim, seq_len_sqrt, seq_len_sqrt)
|
| 144 |
+
|
| 145 |
+
if is_inf:
|
| 146 |
+
kernel_size = min(kernel_size, seq_len_sqrt - (seq_len_sqrt % 2 - 1))
|
| 147 |
+
kernel_size_half = (kernel_size - 1) / 2
|
| 148 |
+
|
| 149 |
+
x = torch.linspace(-kernel_size_half, kernel_size_half, steps=kernel_size)
|
| 150 |
+
pdf = torch.exp(-0.5 * (x / sigma).pow(2))
|
| 151 |
+
kernel1d = pdf / pdf.sum()
|
| 152 |
+
kernel1d = kernel1d.to(query)
|
| 153 |
+
kernel2d = torch.matmul(kernel1d[:, None], kernel1d[None, :])
|
| 154 |
+
kernel2d = kernel2d.expand(embed_dim, 1, kernel2d.shape[0], kernel2d.shape[1])
|
| 155 |
+
|
| 156 |
+
padding = [kernel_size // 2, kernel_size // 2, kernel_size // 2, kernel_size // 2]
|
| 157 |
+
query_slice = F.pad(query_slice, padding, mode="reflect")
|
| 158 |
+
query_slice = F.conv2d(query_slice, kernel2d, groups=embed_dim)
|
| 159 |
+
else:
|
| 160 |
+
query_slice[:] = query_slice.mean(dim=(-2, -1), keepdim=True)
|
| 161 |
+
|
| 162 |
+
query_slice = query_slice.reshape(batch_size, embed_dim, num_square_tokens)
|
| 163 |
+
query_slice = query_slice.permute(0, 2, 1)
|
| 164 |
+
query[:, :num_square_tokens, :] = query_slice.clone()
|
| 165 |
+
|
| 166 |
+
return query
|
third_party/diffusers/src/diffusers/hooks/taylorseer_cache.py
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import re
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn as nn
|
| 7 |
+
|
| 8 |
+
from ..utils import logging
|
| 9 |
+
from .hooks import HookRegistry, ModelHook, StateManager
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
logger = logging.get_logger(__name__)
|
| 13 |
+
_TAYLORSEER_CACHE_HOOK = "taylorseer_cache"
|
| 14 |
+
_SPATIAL_ATTENTION_BLOCK_IDENTIFIERS = (
|
| 15 |
+
"^blocks.*attn",
|
| 16 |
+
"^transformer_blocks.*attn",
|
| 17 |
+
"^single_transformer_blocks.*attn",
|
| 18 |
+
)
|
| 19 |
+
_TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS = ("^temporal_transformer_blocks.*attn",)
|
| 20 |
+
_TRANSFORMER_BLOCK_IDENTIFIERS = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS + _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
|
| 21 |
+
_BLOCK_IDENTIFIERS = ("^[^.]*block[^.]*\\.[^.]+$",)
|
| 22 |
+
_PROJ_OUT_IDENTIFIERS = ("^proj_out$",)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class TaylorSeerCacheConfig:
|
| 27 |
+
"""
|
| 28 |
+
Configuration for TaylorSeer cache. See: https://huggingface.co/papers/2503.06923
|
| 29 |
+
|
| 30 |
+
Attributes:
|
| 31 |
+
cache_interval (`int`, defaults to `5`):
|
| 32 |
+
The interval between full computation steps. After a full computation, the cached (predicted) outputs are
|
| 33 |
+
reused for this many subsequent denoising steps before refreshing with a new full forward pass.
|
| 34 |
+
|
| 35 |
+
disable_cache_before_step (`int`, defaults to `3`):
|
| 36 |
+
The denoising step index before which caching is disabled, meaning full computation is performed for the
|
| 37 |
+
initial steps (0 to disable_cache_before_step - 1) to gather data for Taylor series approximations. During
|
| 38 |
+
these steps, Taylor factors are updated, but caching/predictions are not applied. Caching begins at this
|
| 39 |
+
step.
|
| 40 |
+
|
| 41 |
+
disable_cache_after_step (`int`, *optional*, defaults to `None`):
|
| 42 |
+
The denoising step index after which caching is disabled. If set, for steps >= this value, all modules run
|
| 43 |
+
full computations without predictions or state updates, ensuring accuracy in later stages if needed.
|
| 44 |
+
|
| 45 |
+
max_order (`int`, defaults to `1`):
|
| 46 |
+
The highest order in the Taylor series expansion for approximating module outputs. Higher orders provide
|
| 47 |
+
better approximations but increase computation and memory usage.
|
| 48 |
+
|
| 49 |
+
taylor_factors_dtype (`torch.dtype`, defaults to `torch.bfloat16`):
|
| 50 |
+
Data type used for storing and computing Taylor series factors. Lower precision reduces memory but may
|
| 51 |
+
affect stability; higher precision improves accuracy at the cost of more memory.
|
| 52 |
+
|
| 53 |
+
skip_predict_identifiers (`list[str]`, *optional*, defaults to `None`):
|
| 54 |
+
Regex patterns (using `re.fullmatch`) for module names to place as "skip" in "cache" mode. In this mode,
|
| 55 |
+
the module computes fully during initial or refresh steps but returns a zero tensor (matching recorded
|
| 56 |
+
shape) during prediction steps to skip computation cheaply.
|
| 57 |
+
|
| 58 |
+
cache_identifiers (`list[str]`, *optional*, defaults to `None`):
|
| 59 |
+
Regex patterns (using `re.fullmatch`) for module names to place in Taylor-series caching mode, where
|
| 60 |
+
outputs are approximated and cached for reuse.
|
| 61 |
+
|
| 62 |
+
use_lite_mode (`bool`, *optional*, defaults to `False`):
|
| 63 |
+
Enables a lightweight TaylorSeer variant that minimizes memory usage by applying predefined patterns for
|
| 64 |
+
skipping and caching (e.g., skipping blocks and caching projections). This overrides any custom
|
| 65 |
+
`inactive_identifiers` or `active_identifiers`.
|
| 66 |
+
|
| 67 |
+
Notes:
|
| 68 |
+
- Patterns are matched using `re.fullmatch` on the module name.
|
| 69 |
+
- If `skip_predict_identifiers` or `cache_identifiers` are provided, only matching modules are hooked.
|
| 70 |
+
- If neither is provided, all attention-like modules are hooked by default.
|
| 71 |
+
|
| 72 |
+
Example of inactive and active usage:
|
| 73 |
+
|
| 74 |
+
```py
|
| 75 |
+
def forward(x):
|
| 76 |
+
x = self.module1(x) # inactive module: returns zeros tensor based on shape recorded during full compute
|
| 77 |
+
x = self.module2(x) # active module: caches output here, avoiding recomputation of prior steps
|
| 78 |
+
return x
|
| 79 |
+
```
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
cache_interval: int = 5
|
| 83 |
+
disable_cache_before_step: int = 3
|
| 84 |
+
disable_cache_after_step: int | None = None
|
| 85 |
+
max_order: int = 1
|
| 86 |
+
taylor_factors_dtype: torch.dtype | None = torch.bfloat16
|
| 87 |
+
skip_predict_identifiers: list[str] | None = None
|
| 88 |
+
cache_identifiers: list[str] | None = None
|
| 89 |
+
use_lite_mode: bool = False
|
| 90 |
+
|
| 91 |
+
def __repr__(self) -> str:
|
| 92 |
+
return (
|
| 93 |
+
"TaylorSeerCacheConfig("
|
| 94 |
+
f"cache_interval={self.cache_interval}, "
|
| 95 |
+
f"disable_cache_before_step={self.disable_cache_before_step}, "
|
| 96 |
+
f"disable_cache_after_step={self.disable_cache_after_step}, "
|
| 97 |
+
f"max_order={self.max_order}, "
|
| 98 |
+
f"taylor_factors_dtype={self.taylor_factors_dtype}, "
|
| 99 |
+
f"skip_predict_identifiers={self.skip_predict_identifiers}, "
|
| 100 |
+
f"cache_identifiers={self.cache_identifiers}, "
|
| 101 |
+
f"use_lite_mode={self.use_lite_mode})"
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
class TaylorSeerState:
|
| 106 |
+
def __init__(
|
| 107 |
+
self,
|
| 108 |
+
taylor_factors_dtype: torch.dtype | None = torch.bfloat16,
|
| 109 |
+
max_order: int = 1,
|
| 110 |
+
is_inactive: bool = False,
|
| 111 |
+
):
|
| 112 |
+
self.taylor_factors_dtype = taylor_factors_dtype
|
| 113 |
+
self.max_order = max_order
|
| 114 |
+
self.is_inactive = is_inactive
|
| 115 |
+
|
| 116 |
+
self.module_dtypes: tuple[torch.dtype, ...] = ()
|
| 117 |
+
self.last_update_step: int | None = None
|
| 118 |
+
self.taylor_factors: dict[int, dict[int, torch.Tensor]] = {}
|
| 119 |
+
self.inactive_shapes: tuple[tuple[int, ...], ...] | None = None
|
| 120 |
+
self.device: torch.device | None = None
|
| 121 |
+
self.current_step: int = -1
|
| 122 |
+
|
| 123 |
+
def reset(self) -> None:
|
| 124 |
+
self.current_step = -1
|
| 125 |
+
self.last_update_step = None
|
| 126 |
+
self.taylor_factors = {}
|
| 127 |
+
self.inactive_shapes = None
|
| 128 |
+
self.device = None
|
| 129 |
+
|
| 130 |
+
def update(
|
| 131 |
+
self,
|
| 132 |
+
outputs: tuple[torch.Tensor, ...],
|
| 133 |
+
) -> None:
|
| 134 |
+
self.module_dtypes = tuple(output.dtype for output in outputs)
|
| 135 |
+
self.device = outputs[0].device
|
| 136 |
+
|
| 137 |
+
if self.is_inactive:
|
| 138 |
+
self.inactive_shapes = tuple(output.shape for output in outputs)
|
| 139 |
+
else:
|
| 140 |
+
for i, features in enumerate(outputs):
|
| 141 |
+
new_factors: dict[int, torch.Tensor] = {0: features}
|
| 142 |
+
is_first_update = self.last_update_step is None
|
| 143 |
+
if not is_first_update:
|
| 144 |
+
delta_step = self.current_step - self.last_update_step
|
| 145 |
+
if delta_step == 0:
|
| 146 |
+
raise ValueError("Delta step cannot be zero for TaylorSeer update.")
|
| 147 |
+
|
| 148 |
+
# Recursive divided differences up to max_order
|
| 149 |
+
prev_factors = self.taylor_factors.get(i, {})
|
| 150 |
+
for j in range(self.max_order):
|
| 151 |
+
prev = prev_factors.get(j)
|
| 152 |
+
if prev is None:
|
| 153 |
+
break
|
| 154 |
+
new_factors[j + 1] = (new_factors[j] - prev.to(features.dtype)) / delta_step
|
| 155 |
+
self.taylor_factors[i] = {
|
| 156 |
+
order: factor.to(self.taylor_factors_dtype) for order, factor in new_factors.items()
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
self.last_update_step = self.current_step
|
| 160 |
+
|
| 161 |
+
@torch.compiler.disable
|
| 162 |
+
def predict(self) -> list[torch.Tensor]:
|
| 163 |
+
if self.last_update_step is None:
|
| 164 |
+
raise ValueError("Cannot predict without prior initialization/update.")
|
| 165 |
+
|
| 166 |
+
step_offset = self.current_step - self.last_update_step
|
| 167 |
+
|
| 168 |
+
outputs = []
|
| 169 |
+
if self.is_inactive:
|
| 170 |
+
if self.inactive_shapes is None:
|
| 171 |
+
raise ValueError("Inactive shapes not set during prediction.")
|
| 172 |
+
for i in range(len(self.module_dtypes)):
|
| 173 |
+
outputs.append(
|
| 174 |
+
torch.zeros(
|
| 175 |
+
self.inactive_shapes[i],
|
| 176 |
+
dtype=self.module_dtypes[i],
|
| 177 |
+
device=self.device,
|
| 178 |
+
)
|
| 179 |
+
)
|
| 180 |
+
else:
|
| 181 |
+
if not self.taylor_factors:
|
| 182 |
+
raise ValueError("Taylor factors empty during prediction.")
|
| 183 |
+
num_outputs = len(self.taylor_factors)
|
| 184 |
+
num_orders = len(self.taylor_factors[0])
|
| 185 |
+
for i in range(num_outputs):
|
| 186 |
+
output_dtype = self.module_dtypes[i]
|
| 187 |
+
taylor_factors = self.taylor_factors[i]
|
| 188 |
+
output = torch.zeros_like(taylor_factors[0], dtype=output_dtype)
|
| 189 |
+
for order in range(num_orders):
|
| 190 |
+
coeff = (step_offset**order) / math.factorial(order)
|
| 191 |
+
factor = taylor_factors[order]
|
| 192 |
+
output = output + factor.to(output_dtype) * coeff
|
| 193 |
+
outputs.append(output)
|
| 194 |
+
return outputs
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
class TaylorSeerCacheHook(ModelHook):
|
| 198 |
+
_is_stateful = True
|
| 199 |
+
|
| 200 |
+
def __init__(
|
| 201 |
+
self,
|
| 202 |
+
cache_interval: int,
|
| 203 |
+
disable_cache_before_step: int,
|
| 204 |
+
taylor_factors_dtype: torch.dtype,
|
| 205 |
+
state_manager: StateManager,
|
| 206 |
+
disable_cache_after_step: int | None = None,
|
| 207 |
+
):
|
| 208 |
+
super().__init__()
|
| 209 |
+
self.cache_interval = cache_interval
|
| 210 |
+
self.disable_cache_before_step = disable_cache_before_step
|
| 211 |
+
self.disable_cache_after_step = disable_cache_after_step
|
| 212 |
+
self.taylor_factors_dtype = taylor_factors_dtype
|
| 213 |
+
self.state_manager = state_manager
|
| 214 |
+
|
| 215 |
+
def initialize_hook(self, module: torch.nn.Module):
|
| 216 |
+
return module
|
| 217 |
+
|
| 218 |
+
def reset_state(self, module: torch.nn.Module) -> None:
|
| 219 |
+
"""
|
| 220 |
+
Reset state between sampling runs.
|
| 221 |
+
"""
|
| 222 |
+
self.state_manager.reset()
|
| 223 |
+
|
| 224 |
+
@torch.compiler.disable
|
| 225 |
+
def _measure_should_compute(self) -> bool:
|
| 226 |
+
state: TaylorSeerState = self.state_manager.get_state()
|
| 227 |
+
state.current_step += 1
|
| 228 |
+
current_step = state.current_step
|
| 229 |
+
is_warmup_phase = current_step < self.disable_cache_before_step
|
| 230 |
+
is_compute_interval = (current_step - self.disable_cache_before_step - 1) % self.cache_interval == 0
|
| 231 |
+
is_cooldown_phase = self.disable_cache_after_step is not None and current_step >= self.disable_cache_after_step
|
| 232 |
+
should_compute = is_warmup_phase or is_compute_interval or is_cooldown_phase
|
| 233 |
+
return should_compute, state
|
| 234 |
+
|
| 235 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 236 |
+
should_compute, state = self._measure_should_compute()
|
| 237 |
+
if should_compute:
|
| 238 |
+
outputs = self.fn_ref.original_forward(*args, **kwargs)
|
| 239 |
+
wrapped_outputs = (outputs,) if isinstance(outputs, torch.Tensor) else outputs
|
| 240 |
+
state.update(wrapped_outputs)
|
| 241 |
+
return outputs
|
| 242 |
+
|
| 243 |
+
outputs_list = state.predict()
|
| 244 |
+
return outputs_list[0] if len(outputs_list) == 1 else tuple(outputs_list)
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def _resolve_patterns(config: TaylorSeerCacheConfig) -> tuple[list[str], list[str]]:
|
| 248 |
+
"""
|
| 249 |
+
Resolve effective inactive and active pattern lists from config + templates.
|
| 250 |
+
"""
|
| 251 |
+
|
| 252 |
+
inactive_patterns = config.skip_predict_identifiers if config.skip_predict_identifiers is not None else None
|
| 253 |
+
active_patterns = config.cache_identifiers if config.cache_identifiers is not None else None
|
| 254 |
+
|
| 255 |
+
return inactive_patterns or [], active_patterns or []
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def apply_taylorseer_cache(module: torch.nn.Module, config: TaylorSeerCacheConfig):
|
| 259 |
+
"""
|
| 260 |
+
Applies the TaylorSeer cache to a given pipeline (typically the transformer / UNet).
|
| 261 |
+
|
| 262 |
+
This function hooks selected modules in the model to enable caching or skipping based on the provided
|
| 263 |
+
configuration, reducing redundant computations in diffusion denoising loops.
|
| 264 |
+
|
| 265 |
+
Args:
|
| 266 |
+
module (torch.nn.Module): The model subtree to apply the hooks to.
|
| 267 |
+
config (TaylorSeerCacheConfig): Configuration for the cache.
|
| 268 |
+
|
| 269 |
+
Example:
|
| 270 |
+
```python
|
| 271 |
+
>>> import torch
|
| 272 |
+
>>> from diffusers import FluxPipeline, TaylorSeerCacheConfig
|
| 273 |
+
|
| 274 |
+
>>> pipe = FluxPipeline.from_pretrained(
|
| 275 |
+
... "black-forest-labs/FLUX.1-dev",
|
| 276 |
+
... torch_dtype=torch.bfloat16,
|
| 277 |
+
... )
|
| 278 |
+
>>> pipe.to("cuda")
|
| 279 |
+
|
| 280 |
+
>>> config = TaylorSeerCacheConfig(
|
| 281 |
+
... cache_interval=5,
|
| 282 |
+
... max_order=1,
|
| 283 |
+
... disable_cache_before_step=3,
|
| 284 |
+
... taylor_factors_dtype=torch.float32,
|
| 285 |
+
... )
|
| 286 |
+
>>> pipe.transformer.enable_cache(config)
|
| 287 |
+
```
|
| 288 |
+
"""
|
| 289 |
+
inactive_patterns, active_patterns = _resolve_patterns(config)
|
| 290 |
+
|
| 291 |
+
active_patterns = active_patterns or _TRANSFORMER_BLOCK_IDENTIFIERS
|
| 292 |
+
|
| 293 |
+
if config.use_lite_mode:
|
| 294 |
+
logger.info("Using TaylorSeer Lite variant for cache.")
|
| 295 |
+
active_patterns = _PROJ_OUT_IDENTIFIERS
|
| 296 |
+
inactive_patterns = _BLOCK_IDENTIFIERS
|
| 297 |
+
if config.skip_predict_identifiers or config.cache_identifiers:
|
| 298 |
+
logger.warning("Lite mode overrides user patterns.")
|
| 299 |
+
|
| 300 |
+
for name, submodule in module.named_modules():
|
| 301 |
+
matches_inactive = any(re.fullmatch(pattern, name) for pattern in inactive_patterns)
|
| 302 |
+
matches_active = any(re.fullmatch(pattern, name) for pattern in active_patterns)
|
| 303 |
+
if not (matches_inactive or matches_active):
|
| 304 |
+
continue
|
| 305 |
+
_apply_taylorseer_cache_hook(
|
| 306 |
+
module=submodule,
|
| 307 |
+
config=config,
|
| 308 |
+
is_inactive=matches_inactive,
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def _apply_taylorseer_cache_hook(
|
| 313 |
+
module: nn.Module,
|
| 314 |
+
config: TaylorSeerCacheConfig,
|
| 315 |
+
is_inactive: bool,
|
| 316 |
+
):
|
| 317 |
+
"""
|
| 318 |
+
Registers the TaylorSeer hook on the specified nn.Module.
|
| 319 |
+
|
| 320 |
+
Args:
|
| 321 |
+
name: Name of the module.
|
| 322 |
+
module: The nn.Module to be hooked.
|
| 323 |
+
config: Cache configuration.
|
| 324 |
+
is_inactive: Whether this module should operate in "inactive" mode.
|
| 325 |
+
"""
|
| 326 |
+
state_manager = StateManager(
|
| 327 |
+
TaylorSeerState,
|
| 328 |
+
init_kwargs={
|
| 329 |
+
"taylor_factors_dtype": config.taylor_factors_dtype,
|
| 330 |
+
"max_order": config.max_order,
|
| 331 |
+
"is_inactive": is_inactive,
|
| 332 |
+
},
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 336 |
+
|
| 337 |
+
hook = TaylorSeerCacheHook(
|
| 338 |
+
cache_interval=config.cache_interval,
|
| 339 |
+
disable_cache_before_step=config.disable_cache_before_step,
|
| 340 |
+
taylor_factors_dtype=config.taylor_factors_dtype,
|
| 341 |
+
disable_cache_after_step=config.disable_cache_after_step,
|
| 342 |
+
state_manager=state_manager,
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
registry.register_hook(hook, _TAYLORSEER_CACHE_HOOK)
|
third_party/diffusers/src/diffusers/hooks/text_kv_cache.py
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 dataclasses import dataclass
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from .hooks import BaseState, HookRegistry, ModelHook, StateManager
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
_TEXT_KV_CACHE_TRANSFORMER_HOOK = "text_kv_cache_transformer"
|
| 23 |
+
_TEXT_KV_CACHE_BLOCK_HOOK = "text_kv_cache_block"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
@dataclass
|
| 27 |
+
class TextKVCacheConfig:
|
| 28 |
+
"""Enable exact (lossless) text K/V caching for transformer models.
|
| 29 |
+
|
| 30 |
+
Pre-computes per-block text key and value projections once before the denoising loop and reuses them across all
|
| 31 |
+
steps. Positive and negative prompts are distinguished via a stable cache key captured by a transformer-level hook
|
| 32 |
+
before any intermediate tensor allocations.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
pass
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class TextKVCacheState(BaseState):
|
| 39 |
+
"""Shared state between the transformer-level and block-level hooks.
|
| 40 |
+
|
| 41 |
+
The transformer hook writes the stable ``encoder_hidden_states`` ``data_ptr()`` (captured *before* ``txt_norm``) so
|
| 42 |
+
that block hooks can use it as a reliable cache key across denoising steps.
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
def __init__(self):
|
| 46 |
+
self.key: int | None = None
|
| 47 |
+
|
| 48 |
+
def reset(self):
|
| 49 |
+
self.key = None
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class TextKVCacheBlockState(BaseState):
|
| 53 |
+
"""Per-block state holding cached text key/value projections."""
|
| 54 |
+
|
| 55 |
+
def __init__(self):
|
| 56 |
+
self.kv_cache: dict[int, tuple[torch.Tensor, torch.Tensor]] = {}
|
| 57 |
+
|
| 58 |
+
def reset(self):
|
| 59 |
+
self.kv_cache.clear()
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class TextKVCacheTransformerHook(ModelHook):
|
| 63 |
+
"""Captures ``encoder_hidden_states.data_ptr()`` before ``txt_norm``
|
| 64 |
+
and writes it to shared state for the block hooks to read."""
|
| 65 |
+
|
| 66 |
+
_is_stateful = True
|
| 67 |
+
|
| 68 |
+
def __init__(self, state_manager: StateManager):
|
| 69 |
+
super().__init__()
|
| 70 |
+
self.state_manager = state_manager
|
| 71 |
+
|
| 72 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 73 |
+
if self.state_manager._current_context is None:
|
| 74 |
+
self.state_manager.set_context("inference")
|
| 75 |
+
|
| 76 |
+
encoder_hidden_states = kwargs.get("encoder_hidden_states")
|
| 77 |
+
if encoder_hidden_states is not None:
|
| 78 |
+
state: TextKVCacheState = self.state_manager.get_state()
|
| 79 |
+
state.key = encoder_hidden_states.data_ptr()
|
| 80 |
+
return self.fn_ref.original_forward(*args, **kwargs)
|
| 81 |
+
|
| 82 |
+
def reset_state(self, module: torch.nn.Module):
|
| 83 |
+
self.state_manager.reset()
|
| 84 |
+
return module
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class TextKVCacheBlockHook(ModelHook):
|
| 88 |
+
"""Caches ``(txt_key, txt_value)`` per block per unique prompt using
|
| 89 |
+
the stable cache key from the shared state."""
|
| 90 |
+
|
| 91 |
+
_is_stateful = True
|
| 92 |
+
|
| 93 |
+
def __init__(self, state_manager: StateManager, block_state_manager: StateManager):
|
| 94 |
+
super().__init__()
|
| 95 |
+
self.state_manager = state_manager
|
| 96 |
+
self.block_state_manager = block_state_manager
|
| 97 |
+
|
| 98 |
+
def new_forward(self, module: torch.nn.Module, *args, **kwargs):
|
| 99 |
+
from ..models.transformers.transformer_nucleusmoe_image import _apply_rotary_emb_nucleus
|
| 100 |
+
|
| 101 |
+
if self.state_manager._current_context is None:
|
| 102 |
+
self.state_manager.set_context("inference")
|
| 103 |
+
|
| 104 |
+
if self.block_state_manager._current_context is None:
|
| 105 |
+
self.block_state_manager.set_context("inference")
|
| 106 |
+
|
| 107 |
+
if "encoder_hidden_states" in kwargs:
|
| 108 |
+
encoder_hidden_states = kwargs["encoder_hidden_states"]
|
| 109 |
+
else:
|
| 110 |
+
encoder_hidden_states = args[1]
|
| 111 |
+
|
| 112 |
+
if "image_rotary_emb" in kwargs:
|
| 113 |
+
image_rotary_emb = kwargs["image_rotary_emb"]
|
| 114 |
+
elif len(args) > 3:
|
| 115 |
+
image_rotary_emb = args[3]
|
| 116 |
+
else:
|
| 117 |
+
image_rotary_emb = None
|
| 118 |
+
|
| 119 |
+
state: TextKVCacheState = self.state_manager.get_state()
|
| 120 |
+
cache_key = state.key
|
| 121 |
+
|
| 122 |
+
block_state: TextKVCacheBlockState = self.block_state_manager.get_state()
|
| 123 |
+
|
| 124 |
+
if cache_key not in block_state.kv_cache:
|
| 125 |
+
context = module.encoder_proj(encoder_hidden_states)
|
| 126 |
+
|
| 127 |
+
attn = module.attn
|
| 128 |
+
head_dim = attn.inner_dim // attn.heads
|
| 129 |
+
num_kv_heads = attn.inner_kv_dim // head_dim
|
| 130 |
+
|
| 131 |
+
txt_key = attn.add_k_proj(context).unflatten(-1, (num_kv_heads, -1))
|
| 132 |
+
txt_value = attn.add_v_proj(context).unflatten(-1, (num_kv_heads, -1))
|
| 133 |
+
|
| 134 |
+
if attn.norm_added_k is not None:
|
| 135 |
+
txt_key = attn.norm_added_k(txt_key)
|
| 136 |
+
|
| 137 |
+
if image_rotary_emb is not None:
|
| 138 |
+
_, txt_freqs = image_rotary_emb
|
| 139 |
+
txt_key = _apply_rotary_emb_nucleus(txt_key, txt_freqs, use_real=False)
|
| 140 |
+
|
| 141 |
+
block_state.kv_cache[cache_key] = (txt_key, txt_value)
|
| 142 |
+
|
| 143 |
+
txt_key, txt_value = block_state.kv_cache[cache_key]
|
| 144 |
+
|
| 145 |
+
attn_kwargs = kwargs.get("attention_kwargs") or {}
|
| 146 |
+
attn_kwargs["cached_txt_key"] = txt_key
|
| 147 |
+
attn_kwargs["cached_txt_value"] = txt_value
|
| 148 |
+
kwargs["attention_kwargs"] = attn_kwargs
|
| 149 |
+
|
| 150 |
+
return self.fn_ref.original_forward(*args, **kwargs)
|
| 151 |
+
|
| 152 |
+
def reset_state(self, module: torch.nn.Module):
|
| 153 |
+
self.block_state_manager.reset()
|
| 154 |
+
return module
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def apply_text_kv_cache(module: torch.nn.Module, config: TextKVCacheConfig) -> None:
|
| 158 |
+
from ..models.transformers.transformer_nucleusmoe_image import NucleusMoEImageTransformerBlock
|
| 159 |
+
|
| 160 |
+
HookRegistry.check_if_exists_or_initialize(module)
|
| 161 |
+
|
| 162 |
+
state_manager = StateManager(TextKVCacheState)
|
| 163 |
+
|
| 164 |
+
transformer_hook = TextKVCacheTransformerHook(state_manager)
|
| 165 |
+
registry = HookRegistry.check_if_exists_or_initialize(module)
|
| 166 |
+
registry.register_hook(transformer_hook, _TEXT_KV_CACHE_TRANSFORMER_HOOK)
|
| 167 |
+
|
| 168 |
+
for _, submodule in module.named_modules():
|
| 169 |
+
if isinstance(submodule, NucleusMoEImageTransformerBlock):
|
| 170 |
+
block_state_manager = StateManager(TextKVCacheBlockState)
|
| 171 |
+
hook = TextKVCacheBlockHook(state_manager, block_state_manager)
|
| 172 |
+
block_registry = HookRegistry.check_if_exists_or_initialize(submodule)
|
| 173 |
+
block_registry.register_hook(hook, _TEXT_KV_CACHE_BLOCK_HOOK)
|
third_party/diffusers/src/diffusers/hooks/utils.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS, _ATTENTION_CLASSES, _FEEDFORWARD_CLASSES
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _get_identifiable_transformer_blocks_in_module(module: torch.nn.Module):
|
| 21 |
+
module_list_with_transformer_blocks = []
|
| 22 |
+
for name, submodule in module.named_modules():
|
| 23 |
+
name_endswith_identifier = any(name.endswith(identifier) for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS)
|
| 24 |
+
is_ModuleList = isinstance(submodule, torch.nn.ModuleList)
|
| 25 |
+
if name_endswith_identifier and is_ModuleList:
|
| 26 |
+
module_list_with_transformer_blocks.append((name, submodule))
|
| 27 |
+
return module_list_with_transformer_blocks
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _get_identifiable_attention_layers_in_module(module: torch.nn.Module):
|
| 31 |
+
attention_layers = []
|
| 32 |
+
for name, submodule in module.named_modules():
|
| 33 |
+
if isinstance(submodule, _ATTENTION_CLASSES):
|
| 34 |
+
attention_layers.append((name, submodule))
|
| 35 |
+
return attention_layers
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _get_identifiable_feedforward_layers_in_module(module: torch.nn.Module):
|
| 39 |
+
feedforward_layers = []
|
| 40 |
+
for name, submodule in module.named_modules():
|
| 41 |
+
if isinstance(submodule, _FEEDFORWARD_CLASSES):
|
| 42 |
+
feedforward_layers.append((name, submodule))
|
| 43 |
+
return feedforward_layers
|
third_party/diffusers/src/diffusers/image_processor.py
ADDED
|
@@ -0,0 +1,1468 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 math
|
| 16 |
+
import warnings
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import PIL.Image
|
| 20 |
+
import torch
|
| 21 |
+
import torch.nn.functional as F
|
| 22 |
+
from PIL import Image, ImageFilter, ImageOps
|
| 23 |
+
|
| 24 |
+
from .configuration_utils import ConfigMixin, register_to_config
|
| 25 |
+
from .utils import CONFIG_NAME, PIL_INTERPOLATION, deprecate
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
PipelineImageInput = (
|
| 29 |
+
PIL.Image.Image | np.ndarray | torch.Tensor | list[PIL.Image.Image] | list[np.ndarray] | list[torch.Tensor]
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
PipelineDepthInput = PipelineImageInput
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def is_valid_image(image) -> bool:
|
| 36 |
+
r"""
|
| 37 |
+
Checks if the input is a valid image.
|
| 38 |
+
|
| 39 |
+
A valid image can be:
|
| 40 |
+
- A `PIL.Image.Image`.
|
| 41 |
+
- A 2D or 3D `np.ndarray` or `torch.Tensor` (grayscale or color image).
|
| 42 |
+
|
| 43 |
+
Args:
|
| 44 |
+
image (`PIL.Image.Image | np.ndarray | torch.Tensor`):
|
| 45 |
+
The image to validate. It can be a PIL image, a NumPy array, or a torch tensor.
|
| 46 |
+
|
| 47 |
+
Returns:
|
| 48 |
+
`bool`:
|
| 49 |
+
`True` if the input is a valid image, `False` otherwise.
|
| 50 |
+
"""
|
| 51 |
+
return isinstance(image, PIL.Image.Image) or isinstance(image, (np.ndarray, torch.Tensor)) and image.ndim in (2, 3)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def is_valid_image_imagelist(images):
|
| 55 |
+
r"""
|
| 56 |
+
Checks if the input is a valid image or list of images.
|
| 57 |
+
|
| 58 |
+
The input can be one of the following formats:
|
| 59 |
+
- A 4D tensor or numpy array (batch of images).
|
| 60 |
+
- A valid single image: `PIL.Image.Image`, 2D `np.ndarray` or `torch.Tensor` (grayscale image), 3D `np.ndarray` or
|
| 61 |
+
`torch.Tensor`.
|
| 62 |
+
- A list of valid images.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
images (`np.ndarray | torch.Tensor | PIL.Image.Image | list`):
|
| 66 |
+
The image(s) to check. Can be a batch of images (4D tensor/array), a single image, or a list of valid
|
| 67 |
+
images.
|
| 68 |
+
|
| 69 |
+
Returns:
|
| 70 |
+
`bool`:
|
| 71 |
+
`True` if the input is valid, `False` otherwise.
|
| 72 |
+
"""
|
| 73 |
+
if isinstance(images, (np.ndarray, torch.Tensor)) and images.ndim == 4:
|
| 74 |
+
return True
|
| 75 |
+
elif is_valid_image(images):
|
| 76 |
+
return True
|
| 77 |
+
elif isinstance(images, list):
|
| 78 |
+
return all(is_valid_image(image) for image in images)
|
| 79 |
+
return False
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class VaeImageProcessor(ConfigMixin):
|
| 83 |
+
"""
|
| 84 |
+
Image processor for VAE.
|
| 85 |
+
|
| 86 |
+
Args:
|
| 87 |
+
do_resize (`bool`, *optional*, defaults to `True`):
|
| 88 |
+
Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`. Can accept
|
| 89 |
+
`height` and `width` arguments from [`image_processor.VaeImageProcessor.preprocess`] method.
|
| 90 |
+
vae_scale_factor (`int`, *optional*, defaults to `8`):
|
| 91 |
+
VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
|
| 92 |
+
resample (`str`, *optional*, defaults to `lanczos`):
|
| 93 |
+
Resampling filter to use when resizing the image.
|
| 94 |
+
do_normalize (`bool`, *optional*, defaults to `True`):
|
| 95 |
+
Whether to normalize the image to [-1,1].
|
| 96 |
+
do_binarize (`bool`, *optional*, defaults to `False`):
|
| 97 |
+
Whether to binarize the image to 0/1.
|
| 98 |
+
do_convert_rgb (`bool`, *optional*, defaults to be `False`):
|
| 99 |
+
Whether to convert the images to RGB format.
|
| 100 |
+
do_convert_grayscale (`bool`, *optional*, defaults to be `False`):
|
| 101 |
+
Whether to convert the images to grayscale format.
|
| 102 |
+
"""
|
| 103 |
+
|
| 104 |
+
config_name = CONFIG_NAME
|
| 105 |
+
|
| 106 |
+
@register_to_config
|
| 107 |
+
def __init__(
|
| 108 |
+
self,
|
| 109 |
+
do_resize: bool = True,
|
| 110 |
+
vae_scale_factor: int = 8,
|
| 111 |
+
vae_latent_channels: int = 4,
|
| 112 |
+
resample: str = "lanczos",
|
| 113 |
+
reducing_gap: int | None = None,
|
| 114 |
+
do_normalize: bool = True,
|
| 115 |
+
do_binarize: bool = False,
|
| 116 |
+
do_convert_rgb: bool = False,
|
| 117 |
+
do_convert_grayscale: bool = False,
|
| 118 |
+
):
|
| 119 |
+
super().__init__()
|
| 120 |
+
if do_convert_rgb and do_convert_grayscale:
|
| 121 |
+
raise ValueError(
|
| 122 |
+
"`do_convert_rgb` and `do_convert_grayscale` can not both be set to `True`,"
|
| 123 |
+
" if you intended to convert the image into RGB format, please set `do_convert_grayscale = False`.",
|
| 124 |
+
" if you intended to convert the image into grayscale format, please set `do_convert_rgb = False`",
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
@staticmethod
|
| 128 |
+
def numpy_to_pil(images: np.ndarray) -> list[PIL.Image.Image]:
|
| 129 |
+
r"""
|
| 130 |
+
Convert a numpy image or a batch of images to a PIL image.
|
| 131 |
+
|
| 132 |
+
Args:
|
| 133 |
+
images (`np.ndarray`):
|
| 134 |
+
The image array to convert to PIL format.
|
| 135 |
+
|
| 136 |
+
Returns:
|
| 137 |
+
`list[PIL.Image.Image]`:
|
| 138 |
+
A list of PIL images.
|
| 139 |
+
"""
|
| 140 |
+
if images.ndim == 3:
|
| 141 |
+
images = images[None, ...]
|
| 142 |
+
images = (images * 255).round().astype("uint8")
|
| 143 |
+
if images.shape[-1] == 1:
|
| 144 |
+
# special case for grayscale (single channel) images
|
| 145 |
+
pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
|
| 146 |
+
else:
|
| 147 |
+
pil_images = [Image.fromarray(image) for image in images]
|
| 148 |
+
|
| 149 |
+
return pil_images
|
| 150 |
+
|
| 151 |
+
@staticmethod
|
| 152 |
+
def pil_to_numpy(images: list[PIL.Image.Image] | PIL.Image.Image) -> np.ndarray:
|
| 153 |
+
r"""
|
| 154 |
+
Convert a PIL image or a list of PIL images to NumPy arrays.
|
| 155 |
+
|
| 156 |
+
Args:
|
| 157 |
+
images (`PIL.Image.Image` or `list[PIL.Image.Image]`):
|
| 158 |
+
The PIL image or list of images to convert to NumPy format.
|
| 159 |
+
|
| 160 |
+
Returns:
|
| 161 |
+
`np.ndarray`:
|
| 162 |
+
A NumPy array representation of the images.
|
| 163 |
+
"""
|
| 164 |
+
if not isinstance(images, list):
|
| 165 |
+
images = [images]
|
| 166 |
+
images = [np.array(image).astype(np.float32) / 255.0 for image in images]
|
| 167 |
+
images = np.stack(images, axis=0)
|
| 168 |
+
|
| 169 |
+
return images
|
| 170 |
+
|
| 171 |
+
@staticmethod
|
| 172 |
+
def numpy_to_pt(images: np.ndarray) -> torch.Tensor:
|
| 173 |
+
r"""
|
| 174 |
+
Convert a NumPy image to a PyTorch tensor.
|
| 175 |
+
|
| 176 |
+
Args:
|
| 177 |
+
images (`np.ndarray`):
|
| 178 |
+
The NumPy image array to convert to PyTorch format.
|
| 179 |
+
|
| 180 |
+
Returns:
|
| 181 |
+
`torch.Tensor`:
|
| 182 |
+
A PyTorch tensor representation of the images.
|
| 183 |
+
"""
|
| 184 |
+
if images.ndim == 3:
|
| 185 |
+
images = images[..., None]
|
| 186 |
+
|
| 187 |
+
images = torch.from_numpy(images.transpose(0, 3, 1, 2))
|
| 188 |
+
return images
|
| 189 |
+
|
| 190 |
+
@staticmethod
|
| 191 |
+
def pt_to_numpy(images: torch.Tensor) -> np.ndarray:
|
| 192 |
+
r"""
|
| 193 |
+
Convert a PyTorch tensor to a NumPy image.
|
| 194 |
+
|
| 195 |
+
Args:
|
| 196 |
+
images (`torch.Tensor`):
|
| 197 |
+
The PyTorch tensor to convert to NumPy format.
|
| 198 |
+
|
| 199 |
+
Returns:
|
| 200 |
+
`np.ndarray`:
|
| 201 |
+
A NumPy array representation of the images.
|
| 202 |
+
"""
|
| 203 |
+
images = images.cpu().permute(0, 2, 3, 1).float().numpy()
|
| 204 |
+
return images
|
| 205 |
+
|
| 206 |
+
@staticmethod
|
| 207 |
+
def normalize(images: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
|
| 208 |
+
r"""
|
| 209 |
+
Normalize an image array to [-1,1].
|
| 210 |
+
|
| 211 |
+
Args:
|
| 212 |
+
images (`np.ndarray` or `torch.Tensor`):
|
| 213 |
+
The image array to normalize.
|
| 214 |
+
|
| 215 |
+
Returns:
|
| 216 |
+
`np.ndarray` or `torch.Tensor`:
|
| 217 |
+
The normalized image array.
|
| 218 |
+
"""
|
| 219 |
+
return 2.0 * images - 1.0
|
| 220 |
+
|
| 221 |
+
@staticmethod
|
| 222 |
+
def denormalize(images: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
|
| 223 |
+
r"""
|
| 224 |
+
Denormalize an image array to [0,1].
|
| 225 |
+
|
| 226 |
+
Args:
|
| 227 |
+
images (`np.ndarray` or `torch.Tensor`):
|
| 228 |
+
The image array to denormalize.
|
| 229 |
+
|
| 230 |
+
Returns:
|
| 231 |
+
`np.ndarray` or `torch.Tensor`:
|
| 232 |
+
The denormalized image array.
|
| 233 |
+
"""
|
| 234 |
+
return (images * 0.5 + 0.5).clamp(0, 1)
|
| 235 |
+
|
| 236 |
+
@staticmethod
|
| 237 |
+
def convert_to_rgb(image: PIL.Image.Image) -> PIL.Image.Image:
|
| 238 |
+
r"""
|
| 239 |
+
Converts a PIL image to RGB format.
|
| 240 |
+
|
| 241 |
+
Args:
|
| 242 |
+
image (`PIL.Image.Image`):
|
| 243 |
+
The PIL image to convert to RGB.
|
| 244 |
+
|
| 245 |
+
Returns:
|
| 246 |
+
`PIL.Image.Image`:
|
| 247 |
+
The RGB-converted PIL image.
|
| 248 |
+
"""
|
| 249 |
+
image = image.convert("RGB")
|
| 250 |
+
|
| 251 |
+
return image
|
| 252 |
+
|
| 253 |
+
@staticmethod
|
| 254 |
+
def convert_to_grayscale(image: PIL.Image.Image) -> PIL.Image.Image:
|
| 255 |
+
r"""
|
| 256 |
+
Converts a given PIL image to grayscale.
|
| 257 |
+
|
| 258 |
+
Args:
|
| 259 |
+
image (`PIL.Image.Image`):
|
| 260 |
+
The input image to convert.
|
| 261 |
+
|
| 262 |
+
Returns:
|
| 263 |
+
`PIL.Image.Image`:
|
| 264 |
+
The image converted to grayscale.
|
| 265 |
+
"""
|
| 266 |
+
image = image.convert("L")
|
| 267 |
+
|
| 268 |
+
return image
|
| 269 |
+
|
| 270 |
+
@staticmethod
|
| 271 |
+
def blur(image: PIL.Image.Image, blur_factor: int = 4) -> PIL.Image.Image:
|
| 272 |
+
r"""
|
| 273 |
+
Applies Gaussian blur to an image.
|
| 274 |
+
|
| 275 |
+
Args:
|
| 276 |
+
image (`PIL.Image.Image`):
|
| 277 |
+
The PIL image to convert to grayscale.
|
| 278 |
+
|
| 279 |
+
Returns:
|
| 280 |
+
`PIL.Image.Image`:
|
| 281 |
+
The grayscale-converted PIL image.
|
| 282 |
+
"""
|
| 283 |
+
image = image.filter(ImageFilter.GaussianBlur(blur_factor))
|
| 284 |
+
|
| 285 |
+
return image
|
| 286 |
+
|
| 287 |
+
@staticmethod
|
| 288 |
+
def get_crop_region(mask_image: PIL.Image.Image, width: int, height: int, pad=0):
|
| 289 |
+
r"""
|
| 290 |
+
Finds a rectangular region that contains all masked ares in an image, and expands region to match the aspect
|
| 291 |
+
ratio of the original image; for example, if user drew mask in a 128x32 region, and the dimensions for
|
| 292 |
+
processing are 512x512, the region will be expanded to 128x128.
|
| 293 |
+
|
| 294 |
+
Args:
|
| 295 |
+
mask_image (PIL.Image.Image): Mask image.
|
| 296 |
+
width (int): Width of the image to be processed.
|
| 297 |
+
height (int): Height of the image to be processed.
|
| 298 |
+
pad (int, optional): Padding to be added to the crop region. Defaults to 0.
|
| 299 |
+
|
| 300 |
+
Returns:
|
| 301 |
+
tuple: (x1, y1, x2, y2) represent a rectangular region that contains all masked ares in an image and
|
| 302 |
+
matches the original aspect ratio.
|
| 303 |
+
"""
|
| 304 |
+
|
| 305 |
+
mask_image = mask_image.convert("L")
|
| 306 |
+
mask = np.array(mask_image)
|
| 307 |
+
|
| 308 |
+
# 1. find a rectangular region that contains all masked ares in an image
|
| 309 |
+
h, w = mask.shape
|
| 310 |
+
crop_left = 0
|
| 311 |
+
for i in range(w):
|
| 312 |
+
if not (mask[:, i] == 0).all():
|
| 313 |
+
break
|
| 314 |
+
crop_left += 1
|
| 315 |
+
|
| 316 |
+
crop_right = 0
|
| 317 |
+
for i in reversed(range(w)):
|
| 318 |
+
if not (mask[:, i] == 0).all():
|
| 319 |
+
break
|
| 320 |
+
crop_right += 1
|
| 321 |
+
|
| 322 |
+
crop_top = 0
|
| 323 |
+
for i in range(h):
|
| 324 |
+
if not (mask[i] == 0).all():
|
| 325 |
+
break
|
| 326 |
+
crop_top += 1
|
| 327 |
+
|
| 328 |
+
crop_bottom = 0
|
| 329 |
+
for i in reversed(range(h)):
|
| 330 |
+
if not (mask[i] == 0).all():
|
| 331 |
+
break
|
| 332 |
+
crop_bottom += 1
|
| 333 |
+
|
| 334 |
+
# 2. add padding to the crop region
|
| 335 |
+
x1, y1, x2, y2 = (
|
| 336 |
+
int(max(crop_left - pad, 0)),
|
| 337 |
+
int(max(crop_top - pad, 0)),
|
| 338 |
+
int(min(w - crop_right + pad, w)),
|
| 339 |
+
int(min(h - crop_bottom + pad, h)),
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
# 3. expands crop region to match the aspect ratio of the image to be processed
|
| 343 |
+
ratio_crop_region = (x2 - x1) / (y2 - y1)
|
| 344 |
+
ratio_processing = width / height
|
| 345 |
+
|
| 346 |
+
if ratio_crop_region > ratio_processing:
|
| 347 |
+
desired_height = (x2 - x1) / ratio_processing
|
| 348 |
+
desired_height_diff = int(desired_height - (y2 - y1))
|
| 349 |
+
y1 -= desired_height_diff // 2
|
| 350 |
+
y2 += desired_height_diff - desired_height_diff // 2
|
| 351 |
+
if y2 >= mask_image.height:
|
| 352 |
+
diff = y2 - mask_image.height
|
| 353 |
+
y2 -= diff
|
| 354 |
+
y1 -= diff
|
| 355 |
+
if y1 < 0:
|
| 356 |
+
y2 -= y1
|
| 357 |
+
y1 -= y1
|
| 358 |
+
if y2 >= mask_image.height:
|
| 359 |
+
y2 = mask_image.height
|
| 360 |
+
else:
|
| 361 |
+
desired_width = (y2 - y1) * ratio_processing
|
| 362 |
+
desired_width_diff = int(desired_width - (x2 - x1))
|
| 363 |
+
x1 -= desired_width_diff // 2
|
| 364 |
+
x2 += desired_width_diff - desired_width_diff // 2
|
| 365 |
+
if x2 >= mask_image.width:
|
| 366 |
+
diff = x2 - mask_image.width
|
| 367 |
+
x2 -= diff
|
| 368 |
+
x1 -= diff
|
| 369 |
+
if x1 < 0:
|
| 370 |
+
x2 -= x1
|
| 371 |
+
x1 -= x1
|
| 372 |
+
if x2 >= mask_image.width:
|
| 373 |
+
x2 = mask_image.width
|
| 374 |
+
|
| 375 |
+
return x1, y1, x2, y2
|
| 376 |
+
|
| 377 |
+
def _resize_and_fill(
|
| 378 |
+
self,
|
| 379 |
+
image: PIL.Image.Image,
|
| 380 |
+
width: int,
|
| 381 |
+
height: int,
|
| 382 |
+
) -> PIL.Image.Image:
|
| 383 |
+
r"""
|
| 384 |
+
Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center
|
| 385 |
+
the image within the dimensions, filling empty with data from image.
|
| 386 |
+
|
| 387 |
+
Args:
|
| 388 |
+
image (`PIL.Image.Image`):
|
| 389 |
+
The image to resize and fill.
|
| 390 |
+
width (`int`):
|
| 391 |
+
The width to resize the image to.
|
| 392 |
+
height (`int`):
|
| 393 |
+
The height to resize the image to.
|
| 394 |
+
|
| 395 |
+
Returns:
|
| 396 |
+
`PIL.Image.Image`:
|
| 397 |
+
The resized and filled image.
|
| 398 |
+
"""
|
| 399 |
+
|
| 400 |
+
ratio = width / height
|
| 401 |
+
src_ratio = image.width / image.height
|
| 402 |
+
|
| 403 |
+
src_w = width if ratio < src_ratio else image.width * height // image.height
|
| 404 |
+
src_h = height if ratio >= src_ratio else image.height * width // image.width
|
| 405 |
+
|
| 406 |
+
resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample])
|
| 407 |
+
res = Image.new("RGB", (width, height))
|
| 408 |
+
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
| 409 |
+
|
| 410 |
+
if ratio < src_ratio:
|
| 411 |
+
fill_height = height // 2 - src_h // 2
|
| 412 |
+
if fill_height > 0:
|
| 413 |
+
res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0))
|
| 414 |
+
res.paste(
|
| 415 |
+
resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)),
|
| 416 |
+
box=(0, fill_height + src_h),
|
| 417 |
+
)
|
| 418 |
+
elif ratio > src_ratio:
|
| 419 |
+
fill_width = width // 2 - src_w // 2
|
| 420 |
+
if fill_width > 0:
|
| 421 |
+
res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0))
|
| 422 |
+
res.paste(
|
| 423 |
+
resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)),
|
| 424 |
+
box=(fill_width + src_w, 0),
|
| 425 |
+
)
|
| 426 |
+
|
| 427 |
+
return res
|
| 428 |
+
|
| 429 |
+
def _resize_and_crop(
|
| 430 |
+
self,
|
| 431 |
+
image: PIL.Image.Image,
|
| 432 |
+
width: int,
|
| 433 |
+
height: int,
|
| 434 |
+
) -> PIL.Image.Image:
|
| 435 |
+
r"""
|
| 436 |
+
Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center
|
| 437 |
+
the image within the dimensions, cropping the excess.
|
| 438 |
+
|
| 439 |
+
Args:
|
| 440 |
+
image (`PIL.Image.Image`):
|
| 441 |
+
The image to resize and crop.
|
| 442 |
+
width (`int`):
|
| 443 |
+
The width to resize the image to.
|
| 444 |
+
height (`int`):
|
| 445 |
+
The height to resize the image to.
|
| 446 |
+
|
| 447 |
+
Returns:
|
| 448 |
+
`PIL.Image.Image`:
|
| 449 |
+
The resized and cropped image.
|
| 450 |
+
"""
|
| 451 |
+
ratio = width / height
|
| 452 |
+
src_ratio = image.width / image.height
|
| 453 |
+
|
| 454 |
+
src_w = width if ratio > src_ratio else image.width * height // image.height
|
| 455 |
+
src_h = height if ratio <= src_ratio else image.height * width // image.width
|
| 456 |
+
|
| 457 |
+
resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample])
|
| 458 |
+
res = Image.new("RGB", (width, height))
|
| 459 |
+
res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
|
| 460 |
+
return res
|
| 461 |
+
|
| 462 |
+
def resize(
|
| 463 |
+
self,
|
| 464 |
+
image: PIL.Image.Image | np.ndarray | torch.Tensor,
|
| 465 |
+
height: int,
|
| 466 |
+
width: int,
|
| 467 |
+
resize_mode: str = "default", # "default", "fill", "crop"
|
| 468 |
+
) -> PIL.Image.Image | np.ndarray | torch.Tensor:
|
| 469 |
+
"""
|
| 470 |
+
Resize image.
|
| 471 |
+
|
| 472 |
+
Args:
|
| 473 |
+
image (`PIL.Image.Image`, `np.ndarray` or `torch.Tensor`):
|
| 474 |
+
The image input, can be a PIL image, numpy array or pytorch tensor.
|
| 475 |
+
height (`int`):
|
| 476 |
+
The height to resize to.
|
| 477 |
+
width (`int`):
|
| 478 |
+
The width to resize to.
|
| 479 |
+
resize_mode (`str`, *optional*, defaults to `default`):
|
| 480 |
+
The resize mode to use, can be one of `default` or `fill`. If `default`, will resize the image to fit
|
| 481 |
+
within the specified width and height, and it may not maintaining the original aspect ratio. If `fill`,
|
| 482 |
+
will resize the image to fit within the specified width and height, maintaining the aspect ratio, and
|
| 483 |
+
then center the image within the dimensions, filling empty with data from image. If `crop`, will resize
|
| 484 |
+
the image to fit within the specified width and height, maintaining the aspect ratio, and then center
|
| 485 |
+
the image within the dimensions, cropping the excess. Note that resize_mode `fill` and `crop` are only
|
| 486 |
+
supported for PIL image input.
|
| 487 |
+
|
| 488 |
+
Returns:
|
| 489 |
+
`PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
|
| 490 |
+
The resized image.
|
| 491 |
+
"""
|
| 492 |
+
if resize_mode != "default" and not isinstance(image, PIL.Image.Image):
|
| 493 |
+
raise ValueError(f"Only PIL image input is supported for resize_mode {resize_mode}")
|
| 494 |
+
if isinstance(image, PIL.Image.Image):
|
| 495 |
+
if resize_mode == "default":
|
| 496 |
+
image = image.resize(
|
| 497 |
+
(width, height),
|
| 498 |
+
resample=PIL_INTERPOLATION[self.config.resample],
|
| 499 |
+
reducing_gap=self.config.reducing_gap,
|
| 500 |
+
)
|
| 501 |
+
elif resize_mode == "fill":
|
| 502 |
+
image = self._resize_and_fill(image, width, height)
|
| 503 |
+
elif resize_mode == "crop":
|
| 504 |
+
image = self._resize_and_crop(image, width, height)
|
| 505 |
+
else:
|
| 506 |
+
raise ValueError(f"resize_mode {resize_mode} is not supported")
|
| 507 |
+
|
| 508 |
+
elif isinstance(image, torch.Tensor):
|
| 509 |
+
image = torch.nn.functional.interpolate(
|
| 510 |
+
image,
|
| 511 |
+
size=(height, width),
|
| 512 |
+
)
|
| 513 |
+
elif isinstance(image, np.ndarray):
|
| 514 |
+
image = self.numpy_to_pt(image)
|
| 515 |
+
image = torch.nn.functional.interpolate(
|
| 516 |
+
image,
|
| 517 |
+
size=(height, width),
|
| 518 |
+
)
|
| 519 |
+
image = self.pt_to_numpy(image)
|
| 520 |
+
|
| 521 |
+
return image
|
| 522 |
+
|
| 523 |
+
def binarize(self, image: PIL.Image.Image) -> PIL.Image.Image:
|
| 524 |
+
"""
|
| 525 |
+
Create a mask.
|
| 526 |
+
|
| 527 |
+
Args:
|
| 528 |
+
image (`PIL.Image.Image`):
|
| 529 |
+
The image input, should be a PIL image.
|
| 530 |
+
|
| 531 |
+
Returns:
|
| 532 |
+
`PIL.Image.Image`:
|
| 533 |
+
The binarized image. Values less than 0.5 are set to 0, values greater than 0.5 are set to 1.
|
| 534 |
+
"""
|
| 535 |
+
image[image < 0.5] = 0
|
| 536 |
+
image[image >= 0.5] = 1
|
| 537 |
+
|
| 538 |
+
return image
|
| 539 |
+
|
| 540 |
+
def _denormalize_conditionally(
|
| 541 |
+
self, images: torch.Tensor, do_denormalize: list[bool] | None = None
|
| 542 |
+
) -> torch.Tensor:
|
| 543 |
+
r"""
|
| 544 |
+
Denormalize a batch of images based on a condition list.
|
| 545 |
+
|
| 546 |
+
Args:
|
| 547 |
+
images (`torch.Tensor`):
|
| 548 |
+
The input image tensor.
|
| 549 |
+
do_denormalize (`Optional[list[bool]`, *optional*, defaults to `None`):
|
| 550 |
+
A list of booleans indicating whether to denormalize each image in the batch. If `None`, will use the
|
| 551 |
+
value of `do_normalize` in the `VaeImageProcessor` config.
|
| 552 |
+
"""
|
| 553 |
+
if do_denormalize is None:
|
| 554 |
+
return self.denormalize(images) if self.config.do_normalize else images
|
| 555 |
+
|
| 556 |
+
return torch.stack(
|
| 557 |
+
[self.denormalize(images[i]) if do_denormalize[i] else images[i] for i in range(images.shape[0])]
|
| 558 |
+
)
|
| 559 |
+
|
| 560 |
+
def get_default_height_width(
|
| 561 |
+
self,
|
| 562 |
+
image: PIL.Image.Image | np.ndarray | torch.Tensor,
|
| 563 |
+
height: int | None = None,
|
| 564 |
+
width: int | None = None,
|
| 565 |
+
) -> tuple[int, int]:
|
| 566 |
+
r"""
|
| 567 |
+
Returns the height and width of the image, downscaled to the next integer multiple of `vae_scale_factor`.
|
| 568 |
+
|
| 569 |
+
Args:
|
| 570 |
+
image (`PIL.Image.Image | np.ndarray | torch.Tensor`):
|
| 571 |
+
The image input, which can be a PIL image, NumPy array, or PyTorch tensor. If it is a NumPy array, it
|
| 572 |
+
should have shape `[batch, height, width]` or `[batch, height, width, channels]`. If it is a PyTorch
|
| 573 |
+
tensor, it should have shape `[batch, channels, height, width]`.
|
| 574 |
+
height (`int | None`, *optional*, defaults to `None`):
|
| 575 |
+
The height of the preprocessed image. If `None`, the height of the `image` input will be used.
|
| 576 |
+
width (`int | None`, *optional*, defaults to `None`):
|
| 577 |
+
The width of the preprocessed image. If `None`, the width of the `image` input will be used.
|
| 578 |
+
|
| 579 |
+
Returns:
|
| 580 |
+
`tuple[int, int]`:
|
| 581 |
+
A tuple containing the height and width, both resized to the nearest integer multiple of
|
| 582 |
+
`vae_scale_factor`.
|
| 583 |
+
"""
|
| 584 |
+
|
| 585 |
+
if height is None:
|
| 586 |
+
if isinstance(image, PIL.Image.Image):
|
| 587 |
+
height = image.height
|
| 588 |
+
elif isinstance(image, torch.Tensor):
|
| 589 |
+
height = image.shape[2]
|
| 590 |
+
else:
|
| 591 |
+
height = image.shape[1]
|
| 592 |
+
|
| 593 |
+
if width is None:
|
| 594 |
+
if isinstance(image, PIL.Image.Image):
|
| 595 |
+
width = image.width
|
| 596 |
+
elif isinstance(image, torch.Tensor):
|
| 597 |
+
width = image.shape[3]
|
| 598 |
+
else:
|
| 599 |
+
width = image.shape[2]
|
| 600 |
+
|
| 601 |
+
width, height = (
|
| 602 |
+
x - x % self.config.vae_scale_factor for x in (width, height)
|
| 603 |
+
) # resize to integer multiple of vae_scale_factor
|
| 604 |
+
|
| 605 |
+
return height, width
|
| 606 |
+
|
| 607 |
+
def preprocess(
|
| 608 |
+
self,
|
| 609 |
+
image: PipelineImageInput,
|
| 610 |
+
height: int | None = None,
|
| 611 |
+
width: int | None = None,
|
| 612 |
+
resize_mode: str = "default", # "default", "fill", "crop"
|
| 613 |
+
crops_coords: tuple[int, int, int, int] | None = None,
|
| 614 |
+
) -> torch.Tensor:
|
| 615 |
+
"""
|
| 616 |
+
Preprocess the image input.
|
| 617 |
+
|
| 618 |
+
Args:
|
| 619 |
+
image (`PipelineImageInput`):
|
| 620 |
+
The image input, accepted formats are PIL images, NumPy arrays, PyTorch tensors; Also accept list of
|
| 621 |
+
supported formats.
|
| 622 |
+
height (`int`, *optional*):
|
| 623 |
+
The height in preprocessed image. If `None`, will use the `get_default_height_width()` to get default
|
| 624 |
+
height.
|
| 625 |
+
width (`int`, *optional*):
|
| 626 |
+
The width in preprocessed. If `None`, will use get_default_height_width()` to get the default width.
|
| 627 |
+
resize_mode (`str`, *optional*, defaults to `default`):
|
| 628 |
+
The resize mode, can be one of `default` or `fill`. If `default`, will resize the image to fit within
|
| 629 |
+
the specified width and height, and it may not maintaining the original aspect ratio. If `fill`, will
|
| 630 |
+
resize the image to fit within the specified width and height, maintaining the aspect ratio, and then
|
| 631 |
+
center the image within the dimensions, filling empty with data from image. If `crop`, will resize the
|
| 632 |
+
image to fit within the specified width and height, maintaining the aspect ratio, and then center the
|
| 633 |
+
image within the dimensions, cropping the excess. Note that resize_mode `fill` and `crop` are only
|
| 634 |
+
supported for PIL image input.
|
| 635 |
+
crops_coords (`list[tuple[int, int, int, int]]`, *optional*, defaults to `None`):
|
| 636 |
+
The crop coordinates for each image in the batch. If `None`, will not crop the image.
|
| 637 |
+
|
| 638 |
+
Returns:
|
| 639 |
+
`torch.Tensor`:
|
| 640 |
+
The preprocessed image.
|
| 641 |
+
"""
|
| 642 |
+
supported_formats = (PIL.Image.Image, np.ndarray, torch.Tensor)
|
| 643 |
+
|
| 644 |
+
# Expand the missing dimension for 3-dimensional pytorch tensor or numpy array that represents grayscale image
|
| 645 |
+
if self.config.do_convert_grayscale and isinstance(image, (torch.Tensor, np.ndarray)) and image.ndim == 3:
|
| 646 |
+
if isinstance(image, torch.Tensor):
|
| 647 |
+
# if image is a pytorch tensor could have 2 possible shapes:
|
| 648 |
+
# 1. batch x height x width: we should insert the channel dimension at position 1
|
| 649 |
+
# 2. channel x height x width: we should insert batch dimension at position 0,
|
| 650 |
+
# however, since both channel and batch dimension has same size 1, it is same to insert at position 1
|
| 651 |
+
# for simplicity, we insert a dimension of size 1 at position 1 for both cases
|
| 652 |
+
image = image.unsqueeze(1)
|
| 653 |
+
else:
|
| 654 |
+
# if it is a numpy array, it could have 2 possible shapes:
|
| 655 |
+
# 1. batch x height x width: insert channel dimension on last position
|
| 656 |
+
# 2. height x width x channel: insert batch dimension on first position
|
| 657 |
+
if image.shape[-1] == 1:
|
| 658 |
+
image = np.expand_dims(image, axis=0)
|
| 659 |
+
else:
|
| 660 |
+
image = np.expand_dims(image, axis=-1)
|
| 661 |
+
|
| 662 |
+
if isinstance(image, list) and isinstance(image[0], np.ndarray) and image[0].ndim == 4:
|
| 663 |
+
warnings.warn(
|
| 664 |
+
"Passing `image` as a list of 4d np.ndarray is deprecated."
|
| 665 |
+
"Please concatenate the list along the batch dimension and pass it as a single 4d np.ndarray",
|
| 666 |
+
FutureWarning,
|
| 667 |
+
)
|
| 668 |
+
image = np.concatenate(image, axis=0)
|
| 669 |
+
if isinstance(image, list) and isinstance(image[0], torch.Tensor) and image[0].ndim == 4:
|
| 670 |
+
warnings.warn(
|
| 671 |
+
"Passing `image` as a list of 4d torch.Tensor is deprecated."
|
| 672 |
+
"Please concatenate the list along the batch dimension and pass it as a single 4d torch.Tensor",
|
| 673 |
+
FutureWarning,
|
| 674 |
+
)
|
| 675 |
+
image = torch.cat(image, axis=0)
|
| 676 |
+
|
| 677 |
+
if not is_valid_image_imagelist(image):
|
| 678 |
+
raise ValueError(
|
| 679 |
+
f"Input is in incorrect format. Currently, we only support {', '.join(str(x) for x in supported_formats)}"
|
| 680 |
+
)
|
| 681 |
+
if not isinstance(image, list):
|
| 682 |
+
image = [image]
|
| 683 |
+
|
| 684 |
+
if isinstance(image[0], PIL.Image.Image):
|
| 685 |
+
if crops_coords is not None:
|
| 686 |
+
image = [i.crop(crops_coords) for i in image]
|
| 687 |
+
if self.config.do_resize:
|
| 688 |
+
height, width = self.get_default_height_width(image[0], height, width)
|
| 689 |
+
image = [self.resize(i, height, width, resize_mode=resize_mode) for i in image]
|
| 690 |
+
if self.config.do_convert_rgb:
|
| 691 |
+
image = [self.convert_to_rgb(i) for i in image]
|
| 692 |
+
elif self.config.do_convert_grayscale:
|
| 693 |
+
image = [self.convert_to_grayscale(i) for i in image]
|
| 694 |
+
image = self.pil_to_numpy(image) # to np
|
| 695 |
+
image = self.numpy_to_pt(image) # to pt
|
| 696 |
+
|
| 697 |
+
elif isinstance(image[0], np.ndarray):
|
| 698 |
+
image = np.concatenate(image, axis=0) if image[0].ndim == 4 else np.stack(image, axis=0)
|
| 699 |
+
|
| 700 |
+
image = self.numpy_to_pt(image)
|
| 701 |
+
|
| 702 |
+
height, width = self.get_default_height_width(image, height, width)
|
| 703 |
+
if self.config.do_resize:
|
| 704 |
+
image = self.resize(image, height, width)
|
| 705 |
+
|
| 706 |
+
elif isinstance(image[0], torch.Tensor):
|
| 707 |
+
image = torch.cat(image, axis=0) if image[0].ndim == 4 else torch.stack(image, axis=0)
|
| 708 |
+
|
| 709 |
+
if self.config.do_convert_grayscale and image.ndim == 3:
|
| 710 |
+
image = image.unsqueeze(1)
|
| 711 |
+
|
| 712 |
+
channel = image.shape[1]
|
| 713 |
+
# don't need any preprocess if the image is latents
|
| 714 |
+
if channel == self.config.vae_latent_channels:
|
| 715 |
+
return image
|
| 716 |
+
|
| 717 |
+
height, width = self.get_default_height_width(image, height, width)
|
| 718 |
+
if self.config.do_resize:
|
| 719 |
+
image = self.resize(image, height, width)
|
| 720 |
+
|
| 721 |
+
# expected range [0,1], normalize to [-1,1]
|
| 722 |
+
do_normalize = self.config.do_normalize
|
| 723 |
+
if do_normalize and image.min() < 0:
|
| 724 |
+
warnings.warn(
|
| 725 |
+
"Passing `image` as torch tensor with value range in [-1,1] is deprecated. The expected value range for image tensor is [0,1] "
|
| 726 |
+
f"when passing as pytorch tensor or numpy Array. You passed `image` with value range [{image.min()},{image.max()}]",
|
| 727 |
+
FutureWarning,
|
| 728 |
+
)
|
| 729 |
+
do_normalize = False
|
| 730 |
+
if do_normalize:
|
| 731 |
+
image = self.normalize(image)
|
| 732 |
+
|
| 733 |
+
if self.config.do_binarize:
|
| 734 |
+
image = self.binarize(image)
|
| 735 |
+
|
| 736 |
+
return image
|
| 737 |
+
|
| 738 |
+
def postprocess(
|
| 739 |
+
self,
|
| 740 |
+
image: torch.Tensor,
|
| 741 |
+
output_type: str = "pil",
|
| 742 |
+
do_denormalize: list[bool] | None = None,
|
| 743 |
+
) -> PIL.Image.Image | np.ndarray | torch.Tensor:
|
| 744 |
+
"""
|
| 745 |
+
Postprocess the image output from tensor to `output_type`.
|
| 746 |
+
|
| 747 |
+
Args:
|
| 748 |
+
image (`torch.Tensor`):
|
| 749 |
+
The image input, should be a pytorch tensor with shape `B x C x H x W`.
|
| 750 |
+
output_type (`str`, *optional*, defaults to `pil`):
|
| 751 |
+
The output type of the image, can be one of `pil`, `np`, `pt`, `latent`.
|
| 752 |
+
do_denormalize (`list[bool]`, *optional*, defaults to `None`):
|
| 753 |
+
Whether to denormalize the image to [0,1]. If `None`, will use the value of `do_normalize` in the
|
| 754 |
+
`VaeImageProcessor` config.
|
| 755 |
+
|
| 756 |
+
Returns:
|
| 757 |
+
`PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
|
| 758 |
+
The postprocessed image.
|
| 759 |
+
"""
|
| 760 |
+
if not isinstance(image, torch.Tensor):
|
| 761 |
+
raise ValueError(
|
| 762 |
+
f"Input for postprocessing is in incorrect format: {type(image)}. We only support pytorch tensor"
|
| 763 |
+
)
|
| 764 |
+
if output_type not in ["latent", "pt", "np", "pil"]:
|
| 765 |
+
deprecation_message = (
|
| 766 |
+
f"the output_type {output_type} is outdated and has been set to `np`. Please make sure to set it to one of these instead: "
|
| 767 |
+
"`pil`, `np`, `pt`, `latent`"
|
| 768 |
+
)
|
| 769 |
+
deprecate("Unsupported output_type", "1.0.0", deprecation_message, standard_warn=False)
|
| 770 |
+
output_type = "np"
|
| 771 |
+
|
| 772 |
+
if output_type == "latent":
|
| 773 |
+
return image
|
| 774 |
+
|
| 775 |
+
image = self._denormalize_conditionally(image, do_denormalize)
|
| 776 |
+
|
| 777 |
+
if output_type == "pt":
|
| 778 |
+
return image
|
| 779 |
+
|
| 780 |
+
image = self.pt_to_numpy(image)
|
| 781 |
+
|
| 782 |
+
if output_type == "np":
|
| 783 |
+
return image
|
| 784 |
+
|
| 785 |
+
if output_type == "pil":
|
| 786 |
+
return self.numpy_to_pil(image)
|
| 787 |
+
|
| 788 |
+
def apply_overlay(
|
| 789 |
+
self,
|
| 790 |
+
mask: PIL.Image.Image,
|
| 791 |
+
init_image: PIL.Image.Image,
|
| 792 |
+
image: PIL.Image.Image,
|
| 793 |
+
crop_coords: tuple[int, int, int, int] | None = None,
|
| 794 |
+
) -> PIL.Image.Image:
|
| 795 |
+
r"""
|
| 796 |
+
Applies an overlay of the mask and the inpainted image on the original image.
|
| 797 |
+
|
| 798 |
+
Args:
|
| 799 |
+
mask (`PIL.Image.Image`):
|
| 800 |
+
The mask image that highlights regions to overlay.
|
| 801 |
+
init_image (`PIL.Image.Image`):
|
| 802 |
+
The original image to which the overlay is applied.
|
| 803 |
+
image (`PIL.Image.Image`):
|
| 804 |
+
The image to overlay onto the original.
|
| 805 |
+
crop_coords (`tuple[int, int, int, int]`, *optional*):
|
| 806 |
+
Coordinates to crop the image. If provided, the image will be cropped accordingly.
|
| 807 |
+
|
| 808 |
+
Returns:
|
| 809 |
+
`PIL.Image.Image`:
|
| 810 |
+
The final image with the overlay applied.
|
| 811 |
+
"""
|
| 812 |
+
|
| 813 |
+
width, height = init_image.width, init_image.height
|
| 814 |
+
|
| 815 |
+
init_image_masked = PIL.Image.new("RGBa", (width, height))
|
| 816 |
+
init_image_masked.paste(init_image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert("L")))
|
| 817 |
+
|
| 818 |
+
init_image_masked = init_image_masked.convert("RGBA")
|
| 819 |
+
|
| 820 |
+
if crop_coords is not None:
|
| 821 |
+
x, y, x2, y2 = crop_coords
|
| 822 |
+
w = x2 - x
|
| 823 |
+
h = y2 - y
|
| 824 |
+
base_image = PIL.Image.new("RGBA", (width, height))
|
| 825 |
+
image = self.resize(image, height=h, width=w, resize_mode="crop")
|
| 826 |
+
base_image.paste(image, (x, y))
|
| 827 |
+
image = base_image.convert("RGB")
|
| 828 |
+
|
| 829 |
+
image = image.convert("RGBA")
|
| 830 |
+
image.alpha_composite(init_image_masked)
|
| 831 |
+
image = image.convert("RGB")
|
| 832 |
+
|
| 833 |
+
return image
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
class InpaintProcessor(ConfigMixin):
|
| 837 |
+
"""
|
| 838 |
+
Image processor for inpainting image and mask.
|
| 839 |
+
"""
|
| 840 |
+
|
| 841 |
+
config_name = CONFIG_NAME
|
| 842 |
+
|
| 843 |
+
@register_to_config
|
| 844 |
+
def __init__(
|
| 845 |
+
self,
|
| 846 |
+
do_resize: bool = True,
|
| 847 |
+
vae_scale_factor: int = 8,
|
| 848 |
+
vae_latent_channels: int = 4,
|
| 849 |
+
resample: str = "lanczos",
|
| 850 |
+
reducing_gap: int | None = None,
|
| 851 |
+
do_normalize: bool = True,
|
| 852 |
+
do_binarize: bool = False,
|
| 853 |
+
do_convert_grayscale: bool = False,
|
| 854 |
+
mask_do_normalize: bool = False,
|
| 855 |
+
mask_do_binarize: bool = True,
|
| 856 |
+
mask_do_convert_grayscale: bool = True,
|
| 857 |
+
):
|
| 858 |
+
super().__init__()
|
| 859 |
+
|
| 860 |
+
self._image_processor = VaeImageProcessor(
|
| 861 |
+
do_resize=do_resize,
|
| 862 |
+
vae_scale_factor=vae_scale_factor,
|
| 863 |
+
vae_latent_channels=vae_latent_channels,
|
| 864 |
+
resample=resample,
|
| 865 |
+
reducing_gap=reducing_gap,
|
| 866 |
+
do_normalize=do_normalize,
|
| 867 |
+
do_binarize=do_binarize,
|
| 868 |
+
do_convert_grayscale=do_convert_grayscale,
|
| 869 |
+
)
|
| 870 |
+
self._mask_processor = VaeImageProcessor(
|
| 871 |
+
do_resize=do_resize,
|
| 872 |
+
vae_scale_factor=vae_scale_factor,
|
| 873 |
+
vae_latent_channels=vae_latent_channels,
|
| 874 |
+
resample=resample,
|
| 875 |
+
reducing_gap=reducing_gap,
|
| 876 |
+
do_normalize=mask_do_normalize,
|
| 877 |
+
do_binarize=mask_do_binarize,
|
| 878 |
+
do_convert_grayscale=mask_do_convert_grayscale,
|
| 879 |
+
)
|
| 880 |
+
|
| 881 |
+
def preprocess(
|
| 882 |
+
self,
|
| 883 |
+
image: PIL.Image.Image,
|
| 884 |
+
mask: PIL.Image.Image | None = None,
|
| 885 |
+
height: int | None = None,
|
| 886 |
+
width: int | None = None,
|
| 887 |
+
padding_mask_crop: int | None = None,
|
| 888 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 889 |
+
"""
|
| 890 |
+
Preprocess the image and mask.
|
| 891 |
+
"""
|
| 892 |
+
if mask is None and padding_mask_crop is not None:
|
| 893 |
+
raise ValueError("mask must be provided if padding_mask_crop is provided")
|
| 894 |
+
|
| 895 |
+
# if mask is None, same behavior as regular image processor
|
| 896 |
+
if mask is None:
|
| 897 |
+
return self._image_processor.preprocess(image, height=height, width=width)
|
| 898 |
+
|
| 899 |
+
if padding_mask_crop is not None:
|
| 900 |
+
crops_coords = self._image_processor.get_crop_region(mask, width, height, pad=padding_mask_crop)
|
| 901 |
+
resize_mode = "fill"
|
| 902 |
+
else:
|
| 903 |
+
crops_coords = None
|
| 904 |
+
resize_mode = "default"
|
| 905 |
+
|
| 906 |
+
processed_image = self._image_processor.preprocess(
|
| 907 |
+
image,
|
| 908 |
+
height=height,
|
| 909 |
+
width=width,
|
| 910 |
+
crops_coords=crops_coords,
|
| 911 |
+
resize_mode=resize_mode,
|
| 912 |
+
)
|
| 913 |
+
|
| 914 |
+
processed_mask = self._mask_processor.preprocess(
|
| 915 |
+
mask,
|
| 916 |
+
height=height,
|
| 917 |
+
width=width,
|
| 918 |
+
resize_mode=resize_mode,
|
| 919 |
+
crops_coords=crops_coords,
|
| 920 |
+
)
|
| 921 |
+
|
| 922 |
+
if crops_coords is not None:
|
| 923 |
+
postprocessing_kwargs = {
|
| 924 |
+
"crops_coords": crops_coords,
|
| 925 |
+
"original_image": image,
|
| 926 |
+
"original_mask": mask,
|
| 927 |
+
}
|
| 928 |
+
else:
|
| 929 |
+
postprocessing_kwargs = {
|
| 930 |
+
"crops_coords": None,
|
| 931 |
+
"original_image": None,
|
| 932 |
+
"original_mask": None,
|
| 933 |
+
}
|
| 934 |
+
|
| 935 |
+
return processed_image, processed_mask, postprocessing_kwargs
|
| 936 |
+
|
| 937 |
+
def postprocess(
|
| 938 |
+
self,
|
| 939 |
+
image: torch.Tensor,
|
| 940 |
+
output_type: str = "pil",
|
| 941 |
+
original_image: PIL.Image.Image | None = None,
|
| 942 |
+
original_mask: PIL.Image.Image | None = None,
|
| 943 |
+
crops_coords: tuple[int, int, int, int] | None = None,
|
| 944 |
+
) -> tuple[PIL.Image.Image, PIL.Image.Image]:
|
| 945 |
+
"""
|
| 946 |
+
Postprocess the image, optionally apply mask overlay
|
| 947 |
+
"""
|
| 948 |
+
image = self._image_processor.postprocess(
|
| 949 |
+
image,
|
| 950 |
+
output_type=output_type,
|
| 951 |
+
)
|
| 952 |
+
# optionally apply the mask overlay
|
| 953 |
+
if crops_coords is not None and (original_image is None or original_mask is None):
|
| 954 |
+
raise ValueError("original_image and original_mask must be provided if crops_coords is provided")
|
| 955 |
+
|
| 956 |
+
elif crops_coords is not None and output_type != "pil":
|
| 957 |
+
raise ValueError("output_type must be 'pil' if crops_coords is provided")
|
| 958 |
+
|
| 959 |
+
elif crops_coords is not None:
|
| 960 |
+
image = [
|
| 961 |
+
self._image_processor.apply_overlay(original_mask, original_image, i, crops_coords) for i in image
|
| 962 |
+
]
|
| 963 |
+
|
| 964 |
+
return image
|
| 965 |
+
|
| 966 |
+
|
| 967 |
+
class VaeImageProcessorLDM3D(VaeImageProcessor):
|
| 968 |
+
"""
|
| 969 |
+
Image processor for VAE LDM3D.
|
| 970 |
+
|
| 971 |
+
Args:
|
| 972 |
+
do_resize (`bool`, *optional*, defaults to `True`):
|
| 973 |
+
Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`.
|
| 974 |
+
vae_scale_factor (`int`, *optional*, defaults to `8`):
|
| 975 |
+
VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
|
| 976 |
+
resample (`str`, *optional*, defaults to `lanczos`):
|
| 977 |
+
Resampling filter to use when resizing the image.
|
| 978 |
+
do_normalize (`bool`, *optional*, defaults to `True`):
|
| 979 |
+
Whether to normalize the image to [-1,1].
|
| 980 |
+
"""
|
| 981 |
+
|
| 982 |
+
config_name = CONFIG_NAME
|
| 983 |
+
|
| 984 |
+
@register_to_config
|
| 985 |
+
def __init__(
|
| 986 |
+
self,
|
| 987 |
+
do_resize: bool = True,
|
| 988 |
+
vae_scale_factor: int = 8,
|
| 989 |
+
resample: str = "lanczos",
|
| 990 |
+
do_normalize: bool = True,
|
| 991 |
+
):
|
| 992 |
+
super().__init__()
|
| 993 |
+
|
| 994 |
+
@staticmethod
|
| 995 |
+
def numpy_to_pil(images: np.ndarray) -> list[PIL.Image.Image]:
|
| 996 |
+
r"""
|
| 997 |
+
Convert a NumPy image or a batch of images to a list of PIL images.
|
| 998 |
+
|
| 999 |
+
Args:
|
| 1000 |
+
images (`np.ndarray`):
|
| 1001 |
+
The input NumPy array of images, which can be a single image or a batch.
|
| 1002 |
+
|
| 1003 |
+
Returns:
|
| 1004 |
+
`list[PIL.Image.Image]`:
|
| 1005 |
+
A list of PIL images converted from the input NumPy array.
|
| 1006 |
+
"""
|
| 1007 |
+
if images.ndim == 3:
|
| 1008 |
+
images = images[None, ...]
|
| 1009 |
+
images = (images * 255).round().astype("uint8")
|
| 1010 |
+
if images.shape[-1] == 1:
|
| 1011 |
+
# special case for grayscale (single channel) images
|
| 1012 |
+
pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
|
| 1013 |
+
else:
|
| 1014 |
+
pil_images = [Image.fromarray(image[:, :, :3]) for image in images]
|
| 1015 |
+
|
| 1016 |
+
return pil_images
|
| 1017 |
+
|
| 1018 |
+
@staticmethod
|
| 1019 |
+
def depth_pil_to_numpy(images: list[PIL.Image.Image] | PIL.Image.Image) -> np.ndarray:
|
| 1020 |
+
r"""
|
| 1021 |
+
Convert a PIL image or a list of PIL images to NumPy arrays.
|
| 1022 |
+
|
| 1023 |
+
Args:
|
| 1024 |
+
images (`list[PIL.Image.Image, PIL.Image.Image]`):
|
| 1025 |
+
The input image or list of images to be converted.
|
| 1026 |
+
|
| 1027 |
+
Returns:
|
| 1028 |
+
`np.ndarray`:
|
| 1029 |
+
A NumPy array of the converted images.
|
| 1030 |
+
"""
|
| 1031 |
+
if not isinstance(images, list):
|
| 1032 |
+
images = [images]
|
| 1033 |
+
|
| 1034 |
+
images = [np.array(image).astype(np.float32) / (2**16 - 1) for image in images]
|
| 1035 |
+
images = np.stack(images, axis=0)
|
| 1036 |
+
return images
|
| 1037 |
+
|
| 1038 |
+
@staticmethod
|
| 1039 |
+
def rgblike_to_depthmap(image: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
|
| 1040 |
+
r"""
|
| 1041 |
+
Convert an RGB-like depth image to a depth map.
|
| 1042 |
+
"""
|
| 1043 |
+
# 1. Cast the tensor to a larger integer type (e.g., int32)
|
| 1044 |
+
# to safely perform the multiplication by 256.
|
| 1045 |
+
# 2. Perform the 16-bit combination: High-byte * 256 + Low-byte.
|
| 1046 |
+
# 3. Cast the final result to the desired depth map type (uint16) if needed
|
| 1047 |
+
# before returning, though leaving it as int32/int64 is often safer
|
| 1048 |
+
# for return value from a library function.
|
| 1049 |
+
|
| 1050 |
+
if isinstance(image, torch.Tensor):
|
| 1051 |
+
# Cast to a safe dtype (e.g., int32 or int64) for the calculation
|
| 1052 |
+
original_dtype = image.dtype
|
| 1053 |
+
image_safe = image.to(torch.int32)
|
| 1054 |
+
|
| 1055 |
+
# Calculate the depth map
|
| 1056 |
+
depth_map = image_safe[:, :, 1] * 256 + image_safe[:, :, 2]
|
| 1057 |
+
|
| 1058 |
+
# You may want to cast the final result to uint16, but casting to a
|
| 1059 |
+
# larger int type (like int32) is sufficient to fix the overflow.
|
| 1060 |
+
# depth_map = depth_map.to(torch.uint16) # Uncomment if uint16 is strictly required
|
| 1061 |
+
return depth_map.to(original_dtype)
|
| 1062 |
+
|
| 1063 |
+
elif isinstance(image, np.ndarray):
|
| 1064 |
+
# NumPy equivalent: Cast to a safe dtype (e.g., np.int32)
|
| 1065 |
+
original_dtype = image.dtype
|
| 1066 |
+
image_safe = image.astype(np.int32)
|
| 1067 |
+
|
| 1068 |
+
# Calculate the depth map
|
| 1069 |
+
depth_map = image_safe[:, :, 1] * 256 + image_safe[:, :, 2]
|
| 1070 |
+
|
| 1071 |
+
# depth_map = depth_map.astype(np.uint16) # Uncomment if uint16 is strictly required
|
| 1072 |
+
return depth_map.astype(original_dtype)
|
| 1073 |
+
else:
|
| 1074 |
+
raise TypeError("Input image must be a torch.Tensor or np.ndarray")
|
| 1075 |
+
|
| 1076 |
+
def numpy_to_depth(self, images: np.ndarray) -> list[PIL.Image.Image]:
|
| 1077 |
+
r"""
|
| 1078 |
+
Convert a NumPy depth image or a batch of images to a list of PIL images.
|
| 1079 |
+
|
| 1080 |
+
Args:
|
| 1081 |
+
images (`np.ndarray`):
|
| 1082 |
+
The input NumPy array of depth images, which can be a single image or a batch.
|
| 1083 |
+
|
| 1084 |
+
Returns:
|
| 1085 |
+
`list[PIL.Image.Image]`:
|
| 1086 |
+
A list of PIL images converted from the input NumPy depth images.
|
| 1087 |
+
"""
|
| 1088 |
+
if images.ndim == 3:
|
| 1089 |
+
images = images[None, ...]
|
| 1090 |
+
images_depth = images[:, :, :, 3:]
|
| 1091 |
+
if images.shape[-1] == 6:
|
| 1092 |
+
images_depth = (images_depth * 255).round().astype("uint8")
|
| 1093 |
+
pil_images = [
|
| 1094 |
+
Image.fromarray(self.rgblike_to_depthmap(image_depth), mode="I;16") for image_depth in images_depth
|
| 1095 |
+
]
|
| 1096 |
+
elif images.shape[-1] == 4:
|
| 1097 |
+
images_depth = (images_depth * 65535.0).astype(np.uint16)
|
| 1098 |
+
pil_images = [Image.fromarray(image_depth, mode="I;16") for image_depth in images_depth]
|
| 1099 |
+
else:
|
| 1100 |
+
raise Exception("Not supported")
|
| 1101 |
+
|
| 1102 |
+
return pil_images
|
| 1103 |
+
|
| 1104 |
+
def postprocess(
|
| 1105 |
+
self,
|
| 1106 |
+
image: torch.Tensor,
|
| 1107 |
+
output_type: str = "pil",
|
| 1108 |
+
do_denormalize: list[bool] | None = None,
|
| 1109 |
+
) -> PIL.Image.Image | np.ndarray | torch.Tensor:
|
| 1110 |
+
"""
|
| 1111 |
+
Postprocess the image output from tensor to `output_type`.
|
| 1112 |
+
|
| 1113 |
+
Args:
|
| 1114 |
+
image (`torch.Tensor`):
|
| 1115 |
+
The image input, should be a pytorch tensor with shape `B x C x H x W`.
|
| 1116 |
+
output_type (`str`, *optional*, defaults to `pil`):
|
| 1117 |
+
The output type of the image, can be one of `pil`, `np`, `pt`, `latent`.
|
| 1118 |
+
do_denormalize (`list[bool]`, *optional*, defaults to `None`):
|
| 1119 |
+
Whether to denormalize the image to [0,1]. If `None`, will use the value of `do_normalize` in the
|
| 1120 |
+
`VaeImageProcessor` config.
|
| 1121 |
+
|
| 1122 |
+
Returns:
|
| 1123 |
+
`PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
|
| 1124 |
+
The postprocessed image.
|
| 1125 |
+
"""
|
| 1126 |
+
if not isinstance(image, torch.Tensor):
|
| 1127 |
+
raise ValueError(
|
| 1128 |
+
f"Input for postprocessing is in incorrect format: {type(image)}. We only support pytorch tensor"
|
| 1129 |
+
)
|
| 1130 |
+
if output_type not in ["latent", "pt", "np", "pil"]:
|
| 1131 |
+
deprecation_message = (
|
| 1132 |
+
f"the output_type {output_type} is outdated and has been set to `np`. Please make sure to set it to one of these instead: "
|
| 1133 |
+
"`pil`, `np`, `pt`, `latent`"
|
| 1134 |
+
)
|
| 1135 |
+
deprecate("Unsupported output_type", "1.0.0", deprecation_message, standard_warn=False)
|
| 1136 |
+
output_type = "np"
|
| 1137 |
+
|
| 1138 |
+
image = self._denormalize_conditionally(image, do_denormalize)
|
| 1139 |
+
|
| 1140 |
+
image = self.pt_to_numpy(image)
|
| 1141 |
+
|
| 1142 |
+
if output_type == "np":
|
| 1143 |
+
if image.shape[-1] == 6:
|
| 1144 |
+
image_depth = np.stack([self.rgblike_to_depthmap(im[:, :, 3:]) for im in image], axis=0)
|
| 1145 |
+
else:
|
| 1146 |
+
image_depth = image[:, :, :, 3:]
|
| 1147 |
+
return image[:, :, :, :3], image_depth
|
| 1148 |
+
|
| 1149 |
+
if output_type == "pil":
|
| 1150 |
+
return self.numpy_to_pil(image), self.numpy_to_depth(image)
|
| 1151 |
+
else:
|
| 1152 |
+
raise Exception(f"This type {output_type} is not supported")
|
| 1153 |
+
|
| 1154 |
+
def preprocess(
|
| 1155 |
+
self,
|
| 1156 |
+
rgb: torch.Tensor | PIL.Image.Image | np.ndarray,
|
| 1157 |
+
depth: torch.Tensor | PIL.Image.Image | np.ndarray,
|
| 1158 |
+
height: int | None = None,
|
| 1159 |
+
width: int | None = None,
|
| 1160 |
+
target_res: int | None = None,
|
| 1161 |
+
) -> torch.Tensor:
|
| 1162 |
+
r"""
|
| 1163 |
+
Preprocess the image input. Accepted formats are PIL images, NumPy arrays, or PyTorch tensors.
|
| 1164 |
+
|
| 1165 |
+
Args:
|
| 1166 |
+
rgb (`torch.Tensor | PIL.Image.Image | np.ndarray`):
|
| 1167 |
+
The RGB input image, which can be a single image or a batch.
|
| 1168 |
+
depth (`torch.Tensor | PIL.Image.Image | np.ndarray`):
|
| 1169 |
+
The depth input image, which can be a single image or a batch.
|
| 1170 |
+
height (`int | None`, *optional*, defaults to `None`):
|
| 1171 |
+
The desired height of the processed image. If `None`, defaults to the height of the input image.
|
| 1172 |
+
width (`int | None`, *optional*, defaults to `None`):
|
| 1173 |
+
The desired width of the processed image. If `None`, defaults to the width of the input image.
|
| 1174 |
+
target_res (`int | None`, *optional*, defaults to `None`):
|
| 1175 |
+
Target resolution for resizing the images. If specified, overrides height and width.
|
| 1176 |
+
|
| 1177 |
+
Returns:
|
| 1178 |
+
`tuple[torch.Tensor, torch.Tensor]`:
|
| 1179 |
+
A tuple containing the processed RGB and depth images as PyTorch tensors.
|
| 1180 |
+
"""
|
| 1181 |
+
supported_formats = (PIL.Image.Image, np.ndarray, torch.Tensor)
|
| 1182 |
+
|
| 1183 |
+
# Expand the missing dimension for 3-dimensional pytorch tensor or numpy array that represents grayscale image
|
| 1184 |
+
if self.config.do_convert_grayscale and isinstance(rgb, (torch.Tensor, np.ndarray)) and rgb.ndim == 3:
|
| 1185 |
+
raise Exception("This is not yet supported")
|
| 1186 |
+
|
| 1187 |
+
if isinstance(rgb, supported_formats):
|
| 1188 |
+
rgb = [rgb]
|
| 1189 |
+
depth = [depth]
|
| 1190 |
+
elif not (isinstance(rgb, list) and all(isinstance(i, supported_formats) for i in rgb)):
|
| 1191 |
+
raise ValueError(
|
| 1192 |
+
f"Input is in incorrect format: {[type(i) for i in rgb]}. Currently, we only support {', '.join(supported_formats)}"
|
| 1193 |
+
)
|
| 1194 |
+
|
| 1195 |
+
if isinstance(rgb[0], PIL.Image.Image):
|
| 1196 |
+
if self.config.do_convert_rgb:
|
| 1197 |
+
raise Exception("This is not yet supported")
|
| 1198 |
+
# rgb = [self.convert_to_rgb(i) for i in rgb]
|
| 1199 |
+
# depth = [self.convert_to_depth(i) for i in depth] #TODO define convert_to_depth
|
| 1200 |
+
if self.config.do_resize or target_res:
|
| 1201 |
+
height, width = self.get_default_height_width(rgb[0], height, width) if not target_res else target_res
|
| 1202 |
+
rgb = [self.resize(i, height, width) for i in rgb]
|
| 1203 |
+
depth = [self.resize(i, height, width) for i in depth]
|
| 1204 |
+
rgb = self.pil_to_numpy(rgb) # to np
|
| 1205 |
+
rgb = self.numpy_to_pt(rgb) # to pt
|
| 1206 |
+
|
| 1207 |
+
depth = self.depth_pil_to_numpy(depth) # to np
|
| 1208 |
+
depth = self.numpy_to_pt(depth) # to pt
|
| 1209 |
+
|
| 1210 |
+
elif isinstance(rgb[0], np.ndarray):
|
| 1211 |
+
rgb = np.concatenate(rgb, axis=0) if rgb[0].ndim == 4 else np.stack(rgb, axis=0)
|
| 1212 |
+
rgb = self.numpy_to_pt(rgb)
|
| 1213 |
+
height, width = self.get_default_height_width(rgb, height, width)
|
| 1214 |
+
if self.config.do_resize:
|
| 1215 |
+
rgb = self.resize(rgb, height, width)
|
| 1216 |
+
|
| 1217 |
+
depth = np.concatenate(depth, axis=0) if rgb[0].ndim == 4 else np.stack(depth, axis=0)
|
| 1218 |
+
depth = self.numpy_to_pt(depth)
|
| 1219 |
+
height, width = self.get_default_height_width(depth, height, width)
|
| 1220 |
+
if self.config.do_resize:
|
| 1221 |
+
depth = self.resize(depth, height, width)
|
| 1222 |
+
|
| 1223 |
+
elif isinstance(rgb[0], torch.Tensor):
|
| 1224 |
+
raise Exception("This is not yet supported")
|
| 1225 |
+
# rgb = torch.cat(rgb, axis=0) if rgb[0].ndim == 4 else torch.stack(rgb, axis=0)
|
| 1226 |
+
|
| 1227 |
+
# if self.config.do_convert_grayscale and rgb.ndim == 3:
|
| 1228 |
+
# rgb = rgb.unsqueeze(1)
|
| 1229 |
+
|
| 1230 |
+
# channel = rgb.shape[1]
|
| 1231 |
+
|
| 1232 |
+
# height, width = self.get_default_height_width(rgb, height, width)
|
| 1233 |
+
# if self.config.do_resize:
|
| 1234 |
+
# rgb = self.resize(rgb, height, width)
|
| 1235 |
+
|
| 1236 |
+
# depth = torch.cat(depth, axis=0) if depth[0].ndim == 4 else torch.stack(depth, axis=0)
|
| 1237 |
+
|
| 1238 |
+
# if self.config.do_convert_grayscale and depth.ndim == 3:
|
| 1239 |
+
# depth = depth.unsqueeze(1)
|
| 1240 |
+
|
| 1241 |
+
# channel = depth.shape[1]
|
| 1242 |
+
# # don't need any preprocess if the image is latents
|
| 1243 |
+
# if depth == 4:
|
| 1244 |
+
# return rgb, depth
|
| 1245 |
+
|
| 1246 |
+
# height, width = self.get_default_height_width(depth, height, width)
|
| 1247 |
+
# if self.config.do_resize:
|
| 1248 |
+
# depth = self.resize(depth, height, width)
|
| 1249 |
+
# expected range [0,1], normalize to [-1,1]
|
| 1250 |
+
do_normalize = self.config.do_normalize
|
| 1251 |
+
if rgb.min() < 0 and do_normalize:
|
| 1252 |
+
warnings.warn(
|
| 1253 |
+
"Passing `image` as torch tensor with value range in [-1,1] is deprecated. The expected value range for image tensor is [0,1] "
|
| 1254 |
+
f"when passing as pytorch tensor or numpy Array. You passed `image` with value range [{rgb.min()},{rgb.max()}]",
|
| 1255 |
+
FutureWarning,
|
| 1256 |
+
)
|
| 1257 |
+
do_normalize = False
|
| 1258 |
+
|
| 1259 |
+
if do_normalize:
|
| 1260 |
+
rgb = self.normalize(rgb)
|
| 1261 |
+
depth = self.normalize(depth)
|
| 1262 |
+
|
| 1263 |
+
if self.config.do_binarize:
|
| 1264 |
+
rgb = self.binarize(rgb)
|
| 1265 |
+
depth = self.binarize(depth)
|
| 1266 |
+
|
| 1267 |
+
return rgb, depth
|
| 1268 |
+
|
| 1269 |
+
|
| 1270 |
+
class IPAdapterMaskProcessor(VaeImageProcessor):
|
| 1271 |
+
"""
|
| 1272 |
+
Image processor for IP Adapter image masks.
|
| 1273 |
+
|
| 1274 |
+
Args:
|
| 1275 |
+
do_resize (`bool`, *optional*, defaults to `True`):
|
| 1276 |
+
Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`.
|
| 1277 |
+
vae_scale_factor (`int`, *optional*, defaults to `8`):
|
| 1278 |
+
VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
|
| 1279 |
+
resample (`str`, *optional*, defaults to `lanczos`):
|
| 1280 |
+
Resampling filter to use when resizing the image.
|
| 1281 |
+
do_normalize (`bool`, *optional*, defaults to `False`):
|
| 1282 |
+
Whether to normalize the image to [-1,1].
|
| 1283 |
+
do_binarize (`bool`, *optional*, defaults to `True`):
|
| 1284 |
+
Whether to binarize the image to 0/1.
|
| 1285 |
+
do_convert_grayscale (`bool`, *optional*, defaults to be `True`):
|
| 1286 |
+
Whether to convert the images to grayscale format.
|
| 1287 |
+
|
| 1288 |
+
"""
|
| 1289 |
+
|
| 1290 |
+
config_name = CONFIG_NAME
|
| 1291 |
+
|
| 1292 |
+
@register_to_config
|
| 1293 |
+
def __init__(
|
| 1294 |
+
self,
|
| 1295 |
+
do_resize: bool = True,
|
| 1296 |
+
vae_scale_factor: int = 8,
|
| 1297 |
+
resample: str = "lanczos",
|
| 1298 |
+
do_normalize: bool = False,
|
| 1299 |
+
do_binarize: bool = True,
|
| 1300 |
+
do_convert_grayscale: bool = True,
|
| 1301 |
+
):
|
| 1302 |
+
super().__init__(
|
| 1303 |
+
do_resize=do_resize,
|
| 1304 |
+
vae_scale_factor=vae_scale_factor,
|
| 1305 |
+
resample=resample,
|
| 1306 |
+
do_normalize=do_normalize,
|
| 1307 |
+
do_binarize=do_binarize,
|
| 1308 |
+
do_convert_grayscale=do_convert_grayscale,
|
| 1309 |
+
)
|
| 1310 |
+
|
| 1311 |
+
@staticmethod
|
| 1312 |
+
def downsample(mask: torch.Tensor, batch_size: int, num_queries: int, value_embed_dim: int):
|
| 1313 |
+
"""
|
| 1314 |
+
Downsamples the provided mask tensor to match the expected dimensions for scaled dot-product attention. If the
|
| 1315 |
+
aspect ratio of the mask does not match the aspect ratio of the output image, a warning is issued.
|
| 1316 |
+
|
| 1317 |
+
Args:
|
| 1318 |
+
mask (`torch.Tensor`):
|
| 1319 |
+
The input mask tensor generated with `IPAdapterMaskProcessor.preprocess()`.
|
| 1320 |
+
batch_size (`int`):
|
| 1321 |
+
The batch size.
|
| 1322 |
+
num_queries (`int`):
|
| 1323 |
+
The number of queries.
|
| 1324 |
+
value_embed_dim (`int`):
|
| 1325 |
+
The dimensionality of the value embeddings.
|
| 1326 |
+
|
| 1327 |
+
Returns:
|
| 1328 |
+
`torch.Tensor`:
|
| 1329 |
+
The downsampled mask tensor.
|
| 1330 |
+
|
| 1331 |
+
"""
|
| 1332 |
+
o_h = mask.shape[1]
|
| 1333 |
+
o_w = mask.shape[2]
|
| 1334 |
+
ratio = o_w / o_h
|
| 1335 |
+
mask_h = int(math.sqrt(num_queries / ratio))
|
| 1336 |
+
mask_h = int(mask_h) + int((num_queries % int(mask_h)) != 0)
|
| 1337 |
+
mask_w = num_queries // mask_h
|
| 1338 |
+
|
| 1339 |
+
mask_downsample = F.interpolate(mask.unsqueeze(0), size=(mask_h, mask_w), mode="bicubic").squeeze(0)
|
| 1340 |
+
|
| 1341 |
+
# Repeat batch_size times
|
| 1342 |
+
if mask_downsample.shape[0] < batch_size:
|
| 1343 |
+
mask_downsample = mask_downsample.repeat(batch_size, 1, 1)
|
| 1344 |
+
|
| 1345 |
+
mask_downsample = mask_downsample.view(mask_downsample.shape[0], -1)
|
| 1346 |
+
|
| 1347 |
+
downsampled_area = mask_h * mask_w
|
| 1348 |
+
# If the output image and the mask do not have the same aspect ratio, tensor shapes will not match
|
| 1349 |
+
# Pad tensor if downsampled_mask.shape[1] is smaller than num_queries
|
| 1350 |
+
if downsampled_area < num_queries:
|
| 1351 |
+
warnings.warn(
|
| 1352 |
+
"The aspect ratio of the mask does not match the aspect ratio of the output image. "
|
| 1353 |
+
"Please update your masks or adjust the output size for optimal performance.",
|
| 1354 |
+
UserWarning,
|
| 1355 |
+
)
|
| 1356 |
+
mask_downsample = F.pad(mask_downsample, (0, num_queries - mask_downsample.shape[1]), value=0.0)
|
| 1357 |
+
# Discard last embeddings if downsampled_mask.shape[1] is bigger than num_queries
|
| 1358 |
+
if downsampled_area > num_queries:
|
| 1359 |
+
warnings.warn(
|
| 1360 |
+
"The aspect ratio of the mask does not match the aspect ratio of the output image. "
|
| 1361 |
+
"Please update your masks or adjust the output size for optimal performance.",
|
| 1362 |
+
UserWarning,
|
| 1363 |
+
)
|
| 1364 |
+
mask_downsample = mask_downsample[:, :num_queries]
|
| 1365 |
+
|
| 1366 |
+
# Repeat last dimension to match SDPA output shape
|
| 1367 |
+
mask_downsample = mask_downsample.view(mask_downsample.shape[0], mask_downsample.shape[1], 1).repeat(
|
| 1368 |
+
1, 1, value_embed_dim
|
| 1369 |
+
)
|
| 1370 |
+
|
| 1371 |
+
return mask_downsample
|
| 1372 |
+
|
| 1373 |
+
|
| 1374 |
+
class PixArtImageProcessor(VaeImageProcessor):
|
| 1375 |
+
"""
|
| 1376 |
+
Image processor for PixArt image resize and crop.
|
| 1377 |
+
|
| 1378 |
+
Args:
|
| 1379 |
+
do_resize (`bool`, *optional*, defaults to `True`):
|
| 1380 |
+
Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`. Can accept
|
| 1381 |
+
`height` and `width` arguments from [`image_processor.VaeImageProcessor.preprocess`] method.
|
| 1382 |
+
vae_scale_factor (`int`, *optional*, defaults to `8`):
|
| 1383 |
+
VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
|
| 1384 |
+
resample (`str`, *optional*, defaults to `lanczos`):
|
| 1385 |
+
Resampling filter to use when resizing the image.
|
| 1386 |
+
do_normalize (`bool`, *optional*, defaults to `True`):
|
| 1387 |
+
Whether to normalize the image to [-1,1].
|
| 1388 |
+
do_binarize (`bool`, *optional*, defaults to `False`):
|
| 1389 |
+
Whether to binarize the image to 0/1.
|
| 1390 |
+
do_convert_rgb (`bool`, *optional*, defaults to be `False`):
|
| 1391 |
+
Whether to convert the images to RGB format.
|
| 1392 |
+
do_convert_grayscale (`bool`, *optional*, defaults to be `False`):
|
| 1393 |
+
Whether to convert the images to grayscale format.
|
| 1394 |
+
"""
|
| 1395 |
+
|
| 1396 |
+
@register_to_config
|
| 1397 |
+
def __init__(
|
| 1398 |
+
self,
|
| 1399 |
+
do_resize: bool = True,
|
| 1400 |
+
vae_scale_factor: int = 8,
|
| 1401 |
+
resample: str = "lanczos",
|
| 1402 |
+
do_normalize: bool = True,
|
| 1403 |
+
do_binarize: bool = False,
|
| 1404 |
+
do_convert_grayscale: bool = False,
|
| 1405 |
+
):
|
| 1406 |
+
super().__init__(
|
| 1407 |
+
do_resize=do_resize,
|
| 1408 |
+
vae_scale_factor=vae_scale_factor,
|
| 1409 |
+
resample=resample,
|
| 1410 |
+
do_normalize=do_normalize,
|
| 1411 |
+
do_binarize=do_binarize,
|
| 1412 |
+
do_convert_grayscale=do_convert_grayscale,
|
| 1413 |
+
)
|
| 1414 |
+
|
| 1415 |
+
@staticmethod
|
| 1416 |
+
def classify_height_width_bin(height: int, width: int, ratios: dict) -> tuple[int, int]:
|
| 1417 |
+
r"""
|
| 1418 |
+
Returns the binned height and width based on the aspect ratio.
|
| 1419 |
+
|
| 1420 |
+
Args:
|
| 1421 |
+
height (`int`): The height of the image.
|
| 1422 |
+
width (`int`): The width of the image.
|
| 1423 |
+
ratios (`dict`): A dictionary where keys are aspect ratios and values are tuples of (height, width).
|
| 1424 |
+
|
| 1425 |
+
Returns:
|
| 1426 |
+
`tuple[int, int]`: The closest binned height and width.
|
| 1427 |
+
"""
|
| 1428 |
+
ar = float(height / width)
|
| 1429 |
+
closest_ratio = min(ratios.keys(), key=lambda ratio: abs(float(ratio) - ar))
|
| 1430 |
+
default_hw = ratios[closest_ratio]
|
| 1431 |
+
return int(default_hw[0]), int(default_hw[1])
|
| 1432 |
+
|
| 1433 |
+
@staticmethod
|
| 1434 |
+
def resize_and_crop_tensor(samples: torch.Tensor, new_width: int, new_height: int) -> torch.Tensor:
|
| 1435 |
+
r"""
|
| 1436 |
+
Resizes and crops a tensor of images to the specified dimensions.
|
| 1437 |
+
|
| 1438 |
+
Args:
|
| 1439 |
+
samples (`torch.Tensor`):
|
| 1440 |
+
A tensor of shape (N, C, H, W) where N is the batch size, C is the number of channels, H is the height,
|
| 1441 |
+
and W is the width.
|
| 1442 |
+
new_width (`int`): The desired width of the output images.
|
| 1443 |
+
new_height (`int`): The desired height of the output images.
|
| 1444 |
+
|
| 1445 |
+
Returns:
|
| 1446 |
+
`torch.Tensor`: A tensor containing the resized and cropped images.
|
| 1447 |
+
"""
|
| 1448 |
+
orig_height, orig_width = samples.shape[2], samples.shape[3]
|
| 1449 |
+
|
| 1450 |
+
# Check if resizing is needed
|
| 1451 |
+
if orig_height != new_height or orig_width != new_width:
|
| 1452 |
+
ratio = max(new_height / orig_height, new_width / orig_width)
|
| 1453 |
+
resized_width = int(orig_width * ratio)
|
| 1454 |
+
resized_height = int(orig_height * ratio)
|
| 1455 |
+
|
| 1456 |
+
# Resize
|
| 1457 |
+
samples = F.interpolate(
|
| 1458 |
+
samples, size=(resized_height, resized_width), mode="bilinear", align_corners=False
|
| 1459 |
+
)
|
| 1460 |
+
|
| 1461 |
+
# Center Crop
|
| 1462 |
+
start_x = (resized_width - new_width) // 2
|
| 1463 |
+
end_x = start_x + new_width
|
| 1464 |
+
start_y = (resized_height - new_height) // 2
|
| 1465 |
+
end_y = start_y + new_height
|
| 1466 |
+
samples = samples[:, :, start_y:end_y, start_x:end_x]
|
| 1467 |
+
|
| 1468 |
+
return samples
|
third_party/diffusers/src/diffusers/optimization.py
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2025 The HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
"""PyTorch optimization for diffusion models."""
|
| 16 |
+
|
| 17 |
+
import math
|
| 18 |
+
from enum import Enum
|
| 19 |
+
|
| 20 |
+
from torch.optim import Optimizer
|
| 21 |
+
from torch.optim.lr_scheduler import LambdaLR
|
| 22 |
+
|
| 23 |
+
from .utils import logging
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
logger = logging.get_logger(__name__)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class SchedulerType(Enum):
|
| 30 |
+
LINEAR = "linear"
|
| 31 |
+
COSINE = "cosine"
|
| 32 |
+
COSINE_WITH_RESTARTS = "cosine_with_restarts"
|
| 33 |
+
POLYNOMIAL = "polynomial"
|
| 34 |
+
CONSTANT = "constant"
|
| 35 |
+
CONSTANT_WITH_WARMUP = "constant_with_warmup"
|
| 36 |
+
PIECEWISE_CONSTANT = "piecewise_constant"
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def get_constant_schedule(optimizer: Optimizer, last_epoch: int = -1) -> LambdaLR:
|
| 40 |
+
"""
|
| 41 |
+
Create a schedule with a constant learning rate, using the learning rate set in optimizer.
|
| 42 |
+
|
| 43 |
+
Args:
|
| 44 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 45 |
+
The optimizer for which to schedule the learning rate.
|
| 46 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 47 |
+
The index of the last epoch when resuming training.
|
| 48 |
+
|
| 49 |
+
Return:
|
| 50 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 51 |
+
"""
|
| 52 |
+
return LambdaLR(optimizer, lambda _: 1, last_epoch=last_epoch)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def get_constant_schedule_with_warmup(optimizer: Optimizer, num_warmup_steps: int, last_epoch: int = -1) -> LambdaLR:
|
| 56 |
+
"""
|
| 57 |
+
Create a schedule with a constant learning rate preceded by a warmup period during which the learning rate
|
| 58 |
+
increases linearly between 0 and the initial lr set in the optimizer.
|
| 59 |
+
|
| 60 |
+
Args:
|
| 61 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 62 |
+
The optimizer for which to schedule the learning rate.
|
| 63 |
+
num_warmup_steps (`int`):
|
| 64 |
+
The number of steps for the warmup phase.
|
| 65 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 66 |
+
The index of the last epoch when resuming training.
|
| 67 |
+
|
| 68 |
+
Return:
|
| 69 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 70 |
+
"""
|
| 71 |
+
|
| 72 |
+
def lr_lambda(current_step: int):
|
| 73 |
+
if current_step < num_warmup_steps:
|
| 74 |
+
return float(current_step) / float(max(1.0, num_warmup_steps))
|
| 75 |
+
return 1.0
|
| 76 |
+
|
| 77 |
+
return LambdaLR(optimizer, lr_lambda, last_epoch=last_epoch)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def get_piecewise_constant_schedule(optimizer: Optimizer, step_rules: str, last_epoch: int = -1) -> LambdaLR:
|
| 81 |
+
"""
|
| 82 |
+
Create a schedule with a constant learning rate, using the learning rate set in optimizer.
|
| 83 |
+
|
| 84 |
+
Args:
|
| 85 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 86 |
+
The optimizer for which to schedule the learning rate.
|
| 87 |
+
step_rules (`string`):
|
| 88 |
+
The rules for the learning rate. ex: rule_steps="1:10,0.1:20,0.01:30,0.005" it means that the learning rate
|
| 89 |
+
if multiple 1 for the first 10 steps, multiple 0.1 for the next 20 steps, multiple 0.01 for the next 30
|
| 90 |
+
steps and multiple 0.005 for the other steps.
|
| 91 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 92 |
+
The index of the last epoch when resuming training.
|
| 93 |
+
|
| 94 |
+
Return:
|
| 95 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
rules_dict = {}
|
| 99 |
+
rule_list = step_rules.split(",")
|
| 100 |
+
for rule_str in rule_list[:-1]:
|
| 101 |
+
value_str, steps_str = rule_str.split(":")
|
| 102 |
+
steps = int(steps_str)
|
| 103 |
+
value = float(value_str)
|
| 104 |
+
rules_dict[steps] = value
|
| 105 |
+
last_lr_multiple = float(rule_list[-1])
|
| 106 |
+
|
| 107 |
+
def create_rules_function(rules_dict, last_lr_multiple):
|
| 108 |
+
def rule_func(steps: int) -> float:
|
| 109 |
+
sorted_steps = sorted(rules_dict.keys())
|
| 110 |
+
for i, sorted_step in enumerate(sorted_steps):
|
| 111 |
+
if steps < sorted_step:
|
| 112 |
+
return rules_dict[sorted_steps[i]]
|
| 113 |
+
return last_lr_multiple
|
| 114 |
+
|
| 115 |
+
return rule_func
|
| 116 |
+
|
| 117 |
+
rules_func = create_rules_function(rules_dict, last_lr_multiple)
|
| 118 |
+
|
| 119 |
+
return LambdaLR(optimizer, rules_func, last_epoch=last_epoch)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def get_linear_schedule_with_warmup(
|
| 123 |
+
optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, last_epoch: int = -1
|
| 124 |
+
) -> LambdaLR:
|
| 125 |
+
"""
|
| 126 |
+
Create a schedule with a learning rate that decreases linearly from the initial lr set in the optimizer to 0, after
|
| 127 |
+
a warmup period during which it increases linearly from 0 to the initial lr set in the optimizer.
|
| 128 |
+
|
| 129 |
+
Args:
|
| 130 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 131 |
+
The optimizer for which to schedule the learning rate.
|
| 132 |
+
num_warmup_steps (`int`):
|
| 133 |
+
The number of steps for the warmup phase.
|
| 134 |
+
num_training_steps (`int`):
|
| 135 |
+
The total number of training steps.
|
| 136 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 137 |
+
The index of the last epoch when resuming training.
|
| 138 |
+
|
| 139 |
+
Return:
|
| 140 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 141 |
+
"""
|
| 142 |
+
|
| 143 |
+
def lr_lambda(current_step: int):
|
| 144 |
+
if current_step < num_warmup_steps:
|
| 145 |
+
return float(current_step) / float(max(1, num_warmup_steps))
|
| 146 |
+
return max(
|
| 147 |
+
0.0, float(num_training_steps - current_step) / float(max(1, num_training_steps - num_warmup_steps))
|
| 148 |
+
)
|
| 149 |
+
|
| 150 |
+
return LambdaLR(optimizer, lr_lambda, last_epoch)
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def get_cosine_schedule_with_warmup(
|
| 154 |
+
optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, num_cycles: float = 0.5, last_epoch: int = -1
|
| 155 |
+
) -> LambdaLR:
|
| 156 |
+
"""
|
| 157 |
+
Create a schedule with a learning rate that decreases following the values of the cosine function between the
|
| 158 |
+
initial lr set in the optimizer to 0, after a warmup period during which it increases linearly between 0 and the
|
| 159 |
+
initial lr set in the optimizer.
|
| 160 |
+
|
| 161 |
+
Args:
|
| 162 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 163 |
+
The optimizer for which to schedule the learning rate.
|
| 164 |
+
num_warmup_steps (`int`):
|
| 165 |
+
The number of steps for the warmup phase.
|
| 166 |
+
num_training_steps (`int`):
|
| 167 |
+
The total number of training steps.
|
| 168 |
+
num_periods (`float`, *optional*, defaults to 0.5):
|
| 169 |
+
The number of periods of the cosine function in a schedule (the default is to just decrease from the max
|
| 170 |
+
value to 0 following a half-cosine).
|
| 171 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 172 |
+
The index of the last epoch when resuming training.
|
| 173 |
+
|
| 174 |
+
Return:
|
| 175 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 176 |
+
"""
|
| 177 |
+
|
| 178 |
+
def lr_lambda(current_step):
|
| 179 |
+
if current_step < num_warmup_steps:
|
| 180 |
+
return float(current_step) / float(max(1, num_warmup_steps))
|
| 181 |
+
progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
|
| 182 |
+
return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress)))
|
| 183 |
+
|
| 184 |
+
return LambdaLR(optimizer, lr_lambda, last_epoch)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def get_cosine_with_hard_restarts_schedule_with_warmup(
|
| 188 |
+
optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, num_cycles: int = 1, last_epoch: int = -1
|
| 189 |
+
) -> LambdaLR:
|
| 190 |
+
"""
|
| 191 |
+
Create a schedule with a learning rate that decreases following the values of the cosine function between the
|
| 192 |
+
initial lr set in the optimizer to 0, with several hard restarts, after a warmup period during which it increases
|
| 193 |
+
linearly between 0 and the initial lr set in the optimizer.
|
| 194 |
+
|
| 195 |
+
Args:
|
| 196 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 197 |
+
The optimizer for which to schedule the learning rate.
|
| 198 |
+
num_warmup_steps (`int`):
|
| 199 |
+
The number of steps for the warmup phase.
|
| 200 |
+
num_training_steps (`int`):
|
| 201 |
+
The total number of training steps.
|
| 202 |
+
num_cycles (`int`, *optional*, defaults to 1):
|
| 203 |
+
The number of hard restarts to use.
|
| 204 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 205 |
+
The index of the last epoch when resuming training.
|
| 206 |
+
|
| 207 |
+
Return:
|
| 208 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 209 |
+
"""
|
| 210 |
+
|
| 211 |
+
def lr_lambda(current_step):
|
| 212 |
+
if current_step < num_warmup_steps:
|
| 213 |
+
return float(current_step) / float(max(1, num_warmup_steps))
|
| 214 |
+
progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
|
| 215 |
+
if progress >= 1.0:
|
| 216 |
+
return 0.0
|
| 217 |
+
return max(0.0, 0.5 * (1.0 + math.cos(math.pi * ((float(num_cycles) * progress) % 1.0))))
|
| 218 |
+
|
| 219 |
+
return LambdaLR(optimizer, lr_lambda, last_epoch)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def get_polynomial_decay_schedule_with_warmup(
|
| 223 |
+
optimizer: Optimizer,
|
| 224 |
+
num_warmup_steps: int,
|
| 225 |
+
num_training_steps: int,
|
| 226 |
+
lr_end: float = 1e-7,
|
| 227 |
+
power: float = 1.0,
|
| 228 |
+
last_epoch: int = -1,
|
| 229 |
+
) -> LambdaLR:
|
| 230 |
+
"""
|
| 231 |
+
Create a schedule with a learning rate that decreases as a polynomial decay from the initial lr set in the
|
| 232 |
+
optimizer to end lr defined by *lr_end*, after a warmup period during which it increases linearly from 0 to the
|
| 233 |
+
initial lr set in the optimizer.
|
| 234 |
+
|
| 235 |
+
Args:
|
| 236 |
+
optimizer ([`~torch.optim.Optimizer`]):
|
| 237 |
+
The optimizer for which to schedule the learning rate.
|
| 238 |
+
num_warmup_steps (`int`):
|
| 239 |
+
The number of steps for the warmup phase.
|
| 240 |
+
num_training_steps (`int`):
|
| 241 |
+
The total number of training steps.
|
| 242 |
+
lr_end (`float`, *optional*, defaults to 1e-7):
|
| 243 |
+
The end LR.
|
| 244 |
+
power (`float`, *optional*, defaults to 1.0):
|
| 245 |
+
Power factor.
|
| 246 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 247 |
+
The index of the last epoch when resuming training.
|
| 248 |
+
|
| 249 |
+
Note: *power* defaults to 1.0 as in the fairseq implementation, which in turn is based on the original BERT
|
| 250 |
+
implementation at
|
| 251 |
+
https://github.com/google-research/bert/blob/f39e881b169b9d53bea03d2d341b31707a6c052b/optimization.py#L37
|
| 252 |
+
|
| 253 |
+
Return:
|
| 254 |
+
`torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
|
| 255 |
+
|
| 256 |
+
"""
|
| 257 |
+
|
| 258 |
+
lr_init = optimizer.defaults["lr"]
|
| 259 |
+
if not (lr_init > lr_end):
|
| 260 |
+
raise ValueError(f"lr_end ({lr_end}) must be smaller than initial lr ({lr_init})")
|
| 261 |
+
|
| 262 |
+
def lr_lambda(current_step: int):
|
| 263 |
+
if current_step < num_warmup_steps:
|
| 264 |
+
return float(current_step) / float(max(1, num_warmup_steps))
|
| 265 |
+
elif current_step > num_training_steps:
|
| 266 |
+
return lr_end / lr_init # as LambdaLR multiplies by lr_init
|
| 267 |
+
else:
|
| 268 |
+
lr_range = lr_init - lr_end
|
| 269 |
+
decay_steps = num_training_steps - num_warmup_steps
|
| 270 |
+
pct_remaining = 1 - (current_step - num_warmup_steps) / decay_steps
|
| 271 |
+
decay = lr_range * pct_remaining**power + lr_end
|
| 272 |
+
return decay / lr_init # as LambdaLR multiplies by lr_init
|
| 273 |
+
|
| 274 |
+
return LambdaLR(optimizer, lr_lambda, last_epoch)
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
TYPE_TO_SCHEDULER_FUNCTION = {
|
| 278 |
+
SchedulerType.LINEAR: get_linear_schedule_with_warmup,
|
| 279 |
+
SchedulerType.COSINE: get_cosine_schedule_with_warmup,
|
| 280 |
+
SchedulerType.COSINE_WITH_RESTARTS: get_cosine_with_hard_restarts_schedule_with_warmup,
|
| 281 |
+
SchedulerType.POLYNOMIAL: get_polynomial_decay_schedule_with_warmup,
|
| 282 |
+
SchedulerType.CONSTANT: get_constant_schedule,
|
| 283 |
+
SchedulerType.CONSTANT_WITH_WARMUP: get_constant_schedule_with_warmup,
|
| 284 |
+
SchedulerType.PIECEWISE_CONSTANT: get_piecewise_constant_schedule,
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def get_scheduler(
|
| 289 |
+
name: str | SchedulerType,
|
| 290 |
+
optimizer: Optimizer,
|
| 291 |
+
step_rules: str | None = None,
|
| 292 |
+
num_warmup_steps: int | None = None,
|
| 293 |
+
num_training_steps: int | None = None,
|
| 294 |
+
num_cycles: int = 1,
|
| 295 |
+
power: float = 1.0,
|
| 296 |
+
last_epoch: int = -1,
|
| 297 |
+
) -> LambdaLR:
|
| 298 |
+
"""
|
| 299 |
+
Unified API to get any scheduler from its name.
|
| 300 |
+
|
| 301 |
+
Args:
|
| 302 |
+
name (`str` or `SchedulerType`):
|
| 303 |
+
The name of the scheduler to use.
|
| 304 |
+
optimizer (`torch.optim.Optimizer`):
|
| 305 |
+
The optimizer that will be used during training.
|
| 306 |
+
step_rules (`str`, *optional*):
|
| 307 |
+
A string representing the step rules to use. This is only used by the `PIECEWISE_CONSTANT` scheduler.
|
| 308 |
+
num_warmup_steps (`int`, *optional*):
|
| 309 |
+
The number of warmup steps to do. This is not required by all schedulers (hence the argument being
|
| 310 |
+
optional), the function will raise an error if it's unset and the scheduler type requires it.
|
| 311 |
+
num_training_steps (`int``, *optional*):
|
| 312 |
+
The number of training steps to do. This is not required by all schedulers (hence the argument being
|
| 313 |
+
optional), the function will raise an error if it's unset and the scheduler type requires it.
|
| 314 |
+
num_cycles (`int`, *optional*):
|
| 315 |
+
The number of hard restarts used in `COSINE_WITH_RESTARTS` scheduler.
|
| 316 |
+
power (`float`, *optional*, defaults to 1.0):
|
| 317 |
+
Power factor. See `POLYNOMIAL` scheduler
|
| 318 |
+
last_epoch (`int`, *optional*, defaults to -1):
|
| 319 |
+
The index of the last epoch when resuming training.
|
| 320 |
+
"""
|
| 321 |
+
name = SchedulerType(name)
|
| 322 |
+
schedule_func = TYPE_TO_SCHEDULER_FUNCTION[name]
|
| 323 |
+
if name == SchedulerType.CONSTANT:
|
| 324 |
+
return schedule_func(optimizer, last_epoch=last_epoch)
|
| 325 |
+
|
| 326 |
+
if name == SchedulerType.PIECEWISE_CONSTANT:
|
| 327 |
+
return schedule_func(optimizer, step_rules=step_rules, last_epoch=last_epoch)
|
| 328 |
+
|
| 329 |
+
# All other schedulers require `num_warmup_steps`
|
| 330 |
+
if num_warmup_steps is None:
|
| 331 |
+
raise ValueError(f"{name} requires `num_warmup_steps`, please provide that argument.")
|
| 332 |
+
|
| 333 |
+
if name == SchedulerType.CONSTANT_WITH_WARMUP:
|
| 334 |
+
return schedule_func(optimizer, num_warmup_steps=num_warmup_steps, last_epoch=last_epoch)
|
| 335 |
+
|
| 336 |
+
# All other schedulers require `num_training_steps`
|
| 337 |
+
if num_training_steps is None:
|
| 338 |
+
raise ValueError(f"{name} requires `num_training_steps`, please provide that argument.")
|
| 339 |
+
|
| 340 |
+
if name == SchedulerType.COSINE_WITH_RESTARTS:
|
| 341 |
+
return schedule_func(
|
| 342 |
+
optimizer,
|
| 343 |
+
num_warmup_steps=num_warmup_steps,
|
| 344 |
+
num_training_steps=num_training_steps,
|
| 345 |
+
num_cycles=num_cycles,
|
| 346 |
+
last_epoch=last_epoch,
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
if name == SchedulerType.POLYNOMIAL:
|
| 350 |
+
return schedule_func(
|
| 351 |
+
optimizer,
|
| 352 |
+
num_warmup_steps=num_warmup_steps,
|
| 353 |
+
num_training_steps=num_training_steps,
|
| 354 |
+
power=power,
|
| 355 |
+
last_epoch=last_epoch,
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
return schedule_func(
|
| 359 |
+
optimizer, num_warmup_steps=num_warmup_steps, num_training_steps=num_training_steps, last_epoch=last_epoch
|
| 360 |
+
)
|
third_party/diffusers/src/diffusers/pipelines/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 🧨 Diffusers Pipelines
|
| 2 |
+
|
| 3 |
+
Pipelines provide a simple way to run state-of-the-art diffusion models in inference.
|
| 4 |
+
Most diffusion systems consist of multiple independently-trained models and highly adaptable scheduler
|
| 5 |
+
components - all of which are needed to have a functioning end-to-end diffusion system.
|
| 6 |
+
|
| 7 |
+
As an example, [Stable Diffusion](https://huggingface.co/blog/stable_diffusion) has three independently trained models:
|
| 8 |
+
- [Autoencoder](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/vae.py#L392)
|
| 9 |
+
- [Conditional Unet](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/unet_2d_condition.py#L12)
|
| 10 |
+
- [CLIP text encoder](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPTextModel)
|
| 11 |
+
- a scheduler component, [scheduler](https://github.com/huggingface/diffusers/blob/main/src/diffusers/schedulers/scheduling_pndm.py),
|
| 12 |
+
- a [CLIPImageProcessor](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPImageProcessor),
|
| 13 |
+
- as well as a [safety checker](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/safety_checker.py).
|
| 14 |
+
All of these components are necessary to run stable diffusion in inference even though they were trained
|
| 15 |
+
or created independently from each other.
|
| 16 |
+
|
| 17 |
+
To that end, we strive to offer all open-sourced, state-of-the-art diffusion system under a unified API.
|
| 18 |
+
More specifically, we strive to provide pipelines that
|
| 19 |
+
- 1. can load the officially published weights and yield 1-to-1 the same outputs as the original implementation according to the corresponding paper (*e.g.* [LDMTextToImagePipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/latent_diffusion), uses the officially released weights of [High-Resolution Image Synthesis with Latent Diffusion Models](https://huggingface.co/papers/2112.10752)),
|
| 20 |
+
- 2. have a simple user interface to run the model in inference (see the [Pipelines API](#pipelines-api) section),
|
| 21 |
+
- 3. are easy to understand with code that is self-explanatory and can be read along-side the official paper (see [Pipelines summary](#pipelines-summary)),
|
| 22 |
+
- 4. can easily be contributed by the community (see the [Contribution](#contribution) section).
|
| 23 |
+
|
| 24 |
+
**Note** that pipelines do not (and should not) offer any training functionality.
|
| 25 |
+
If you are looking for *official* training examples, please have a look at [examples](https://github.com/huggingface/diffusers/tree/main/examples).
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
## Pipelines Summary
|
| 29 |
+
|
| 30 |
+
The following table summarizes all officially supported pipelines, their corresponding paper, and if
|
| 31 |
+
available a colab notebook to directly try them out.
|
| 32 |
+
|
| 33 |
+
| Pipeline | Source | Tasks | Colab
|
| 34 |
+
|-------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|:---:|:---:|
|
| 35 |
+
| [dance diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/dance_diffusion) | [**Dance Diffusion**](https://github.com/Harmonai-org/sample-generator) | *Unconditional Audio Generation* |
|
| 36 |
+
| [ddpm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddpm) | [**Denoising Diffusion Probabilistic Models**](https://huggingface.co/papers/2006.11239) | *Unconditional Image Generation* |
|
| 37 |
+
| [ddim](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddim) | [**Denoising Diffusion Implicit Models**](https://huggingface.co/papers/2010.02502) | *Unconditional Image Generation* | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/training_example.ipynb)
|
| 38 |
+
| [latent_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Text-to-Image Generation* |
|
| 39 |
+
| [latent_diffusion_uncond](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion_uncond) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Unconditional Image Generation* |
|
| 40 |
+
| [pndm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/pndm) | [**Pseudo Numerical Methods for Diffusion Models on Manifolds**](https://huggingface.co/papers/2202.09778) | *Unconditional Image Generation* |
|
| 41 |
+
| [score_sde_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_ve) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
|
| 42 |
+
| [score_sde_vp](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_vp) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
|
| 43 |
+
| [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-to-Image Generation* | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/stable_diffusion.ipynb)
|
| 44 |
+
| [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Image-to-Image Text-Guided Generation* | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
|
| 45 |
+
| [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-Guided Image Inpainting* | [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
|
| 46 |
+
| [stochastic_karras_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stochastic_karras_ve) | [**Elucidating the Design Space of Diffusion-Based Generative Models**](https://huggingface.co/papers/2206.00364) | *Unconditional Image Generation* |
|
| 47 |
+
|
| 48 |
+
**Note**: Pipelines are simple examples of how to play around with the diffusion systems as described in the corresponding papers.
|
| 49 |
+
However, most of them can be adapted to use different scheduler components or even different model components. Some pipeline examples are shown in the [Examples](#examples) below.
|
| 50 |
+
|
| 51 |
+
## Pipelines API
|
| 52 |
+
|
| 53 |
+
Diffusion models often consist of multiple independently-trained models or other previously existing components.
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
Each model has been trained independently on a different task and the scheduler can easily be swapped out and replaced with a different one.
|
| 57 |
+
During inference, we however want to be able to easily load all components and use them in inference - even if one component, *e.g.* CLIP's text encoder, originates from a different library, such as [Transformers](https://github.com/huggingface/transformers). To that end, all pipelines provide the following functionality:
|
| 58 |
+
|
| 59 |
+
- [`from_pretrained` method](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L139) that accepts a Hugging Face Hub repository id, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5) or a path to a local directory, *e.g.*
|
| 60 |
+
"./stable-diffusion". To correctly retrieve which models and components should be loaded, one has to provide a `model_index.json` file, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5/model_index.json](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/blob/main/model_index.json), which defines all components that should be
|
| 61 |
+
loaded into the pipelines. More specifically, for each model/component one needs to define the format `<name>: ["<library>", "<class name>"]`. `<name>` is the attribute name given to the loaded instance of `<class name>` which can be found in the library or pipeline folder called `"<library>"`.
|
| 62 |
+
- [`save_pretrained`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L90) that accepts a local path, *e.g.* `./stable-diffusion` under which all models/components of the pipeline will be saved. For each component/model a folder is created inside the local path that is named after the given attribute name, *e.g.* `./stable_diffusion/unet`.
|
| 63 |
+
In addition, a `model_index.json` file is created at the root of the local path, *e.g.* `./stable_diffusion/model_index.json` so that the complete pipeline can again be instantiated
|
| 64 |
+
from the local path.
|
| 65 |
+
- [`to`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L118) which accepts a `string` or `torch.device` to move all models that are of type `torch.nn.Module` to the passed device. The behavior is fully analogous to [PyTorch's `to` method](https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module.to).
|
| 66 |
+
- [`__call__`] method to use the pipeline in inference. `__call__` defines inference logic of the pipeline and should ideally encompass all aspects of it, from pre-processing to forwarding tensors to the different models and schedulers, as well as post-processing. The API of the `__call__` method can strongly vary from pipeline to pipeline. *E.g.* a text-to-image pipeline, such as [`StableDiffusionPipeline`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) should accept among other things the text prompt to generate the image. A pure image generation pipeline, such as [DDPMPipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/ddpm) on the other hand can be run without providing any inputs. To better understand what inputs can be adapted for
|
| 67 |
+
each pipeline, one should look directly into the respective pipeline.
|
| 68 |
+
|
| 69 |
+
**Note**: All pipelines have PyTorch's autograd disabled by decorating the `__call__` method with a [`torch.no_grad`](https://pytorch.org/docs/stable/generated/torch.no_grad.html) decorator because pipelines should
|
| 70 |
+
not be used for training. If you want to store the gradients during the forward pass, we recommend writing your own pipeline, see also our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community)
|
| 71 |
+
|
| 72 |
+
## Contribution
|
| 73 |
+
|
| 74 |
+
We are more than happy about any contribution to the officially supported pipelines 🤗. We aspire
|
| 75 |
+
all of our pipelines to be **self-contained**, **easy-to-tweak**, **beginner-friendly** and for **one-purpose-only**.
|
| 76 |
+
|
| 77 |
+
- **Self-contained**: A pipeline shall be as self-contained as possible. More specifically, this means that all functionality should be either directly defined in the pipeline file itself, should be inherited from (and only from) the [`DiffusionPipeline` class](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L56) or be directly attached to the model and scheduler components of the pipeline.
|
| 78 |
+
- **Easy-to-use**: Pipelines should be extremely easy to use - one should be able to load the pipeline and
|
| 79 |
+
use it for its designated task, *e.g.* text-to-image generation, in just a couple of lines of code. Most
|
| 80 |
+
logic including pre-processing, an unrolled diffusion loop, and post-processing should all happen inside the `__call__` method.
|
| 81 |
+
- **Easy-to-tweak**: Certain pipelines will not be able to handle all use cases and tasks that you might like them to. If you want to use a certain pipeline for a specific use case that is not yet supported, you might have to copy the pipeline file and tweak the code to your needs. We try to make the pipeline code as readable as possible so that each part –from pre-processing to diffusing to post-processing– can easily be adapted. If you would like the community to benefit from your customized pipeline, we would love to see a contribution to our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community). If you feel that an important pipeline should be part of the official pipelines but isn't, a contribution to the [official pipelines](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines) would be even better.
|
| 82 |
+
- **One-purpose-only**: Pipelines should be used for one task and one task only. Even if two tasks are very similar from a modeling point of view, *e.g.* image2image translation and in-painting, pipelines shall be used for one task only to keep them *easy-to-tweak* and *readable*.
|
| 83 |
+
|
| 84 |
+
## Examples
|
| 85 |
+
|
| 86 |
+
### Text-to-Image generation with Stable Diffusion
|
| 87 |
+
|
| 88 |
+
```python
|
| 89 |
+
# make sure you're logged in with `hf auth login`
|
| 90 |
+
from diffusers import StableDiffusionPipeline, LMSDiscreteScheduler
|
| 91 |
+
|
| 92 |
+
pipe = StableDiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
|
| 93 |
+
pipe = pipe.to("cuda")
|
| 94 |
+
|
| 95 |
+
prompt = "a photo of an astronaut riding a horse on mars"
|
| 96 |
+
image = pipe(prompt).images[0]
|
| 97 |
+
|
| 98 |
+
image.save("astronaut_rides_horse.png")
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### Image-to-Image text-guided generation with Stable Diffusion
|
| 102 |
+
|
| 103 |
+
The `StableDiffusionImg2ImgPipeline` lets you pass a text prompt and an initial image to condition the generation of new images.
|
| 104 |
+
|
| 105 |
+
```python
|
| 106 |
+
import requests
|
| 107 |
+
from PIL import Image
|
| 108 |
+
from io import BytesIO
|
| 109 |
+
|
| 110 |
+
from diffusers import StableDiffusionImg2ImgPipeline
|
| 111 |
+
|
| 112 |
+
# load the pipeline
|
| 113 |
+
device = "cuda"
|
| 114 |
+
pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
|
| 115 |
+
"stable-diffusion-v1-5/stable-diffusion-v1-5",
|
| 116 |
+
torch_dtype=torch.float16,
|
| 117 |
+
).to(device)
|
| 118 |
+
|
| 119 |
+
# let's download an initial image
|
| 120 |
+
url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
|
| 121 |
+
|
| 122 |
+
response = requests.get(url)
|
| 123 |
+
init_image = Image.open(BytesIO(response.content)).convert("RGB")
|
| 124 |
+
init_image = init_image.resize((768, 512))
|
| 125 |
+
|
| 126 |
+
prompt = "A fantasy landscape, trending on artstation"
|
| 127 |
+
|
| 128 |
+
images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images
|
| 129 |
+
|
| 130 |
+
images[0].save("fantasy_landscape.png")
|
| 131 |
+
```
|
| 132 |
+
You can also run this example on colab [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
|
| 133 |
+
|
| 134 |
+
### Tweak prompts reusing seeds and latents
|
| 135 |
+
|
| 136 |
+
You can generate your own latents to reproduce results, or tweak your prompt on a specific result you liked. [This notebook](https://github.com/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb) shows how to do it step by step. You can also run it in Google Colab [](https://colab.research.google.com/github/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb).
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
### In-painting using Stable Diffusion
|
| 140 |
+
|
| 141 |
+
The `StableDiffusionInpaintPipeline` lets you edit specific parts of an image by providing a mask and text prompt.
|
| 142 |
+
|
| 143 |
+
```python
|
| 144 |
+
import PIL
|
| 145 |
+
import requests
|
| 146 |
+
import torch
|
| 147 |
+
from io import BytesIO
|
| 148 |
+
|
| 149 |
+
from diffusers import StableDiffusionInpaintPipeline
|
| 150 |
+
|
| 151 |
+
def download_image(url):
|
| 152 |
+
response = requests.get(url)
|
| 153 |
+
return PIL.Image.open(BytesIO(response.content)).convert("RGB")
|
| 154 |
+
|
| 155 |
+
img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
|
| 156 |
+
mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
|
| 157 |
+
|
| 158 |
+
init_image = download_image(img_url).resize((512, 512))
|
| 159 |
+
mask_image = download_image(mask_url).resize((512, 512))
|
| 160 |
+
|
| 161 |
+
pipe = StableDiffusionInpaintPipeline.from_pretrained(
|
| 162 |
+
"stable-diffusion-v1-5/stable-diffusion-inpainting",
|
| 163 |
+
torch_dtype=torch.float16,
|
| 164 |
+
)
|
| 165 |
+
pipe = pipe.to("cuda")
|
| 166 |
+
|
| 167 |
+
prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
|
| 168 |
+
image = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images[0]
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
You can also run this example on colab [](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
|
third_party/diffusers/src/diffusers/pipelines/__init__.py
ADDED
|
@@ -0,0 +1,963 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ..utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_flax_available,
|
| 9 |
+
is_librosa_available,
|
| 10 |
+
is_note_seq_available,
|
| 11 |
+
is_onnx_available,
|
| 12 |
+
is_opencv_available,
|
| 13 |
+
is_sentencepiece_available,
|
| 14 |
+
is_torch_available,
|
| 15 |
+
is_torch_npu_available,
|
| 16 |
+
is_transformers_available,
|
| 17 |
+
is_transformers_version,
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
# These modules contain pipelines from multiple libraries/frameworks
|
| 22 |
+
_dummy_objects = {}
|
| 23 |
+
_import_structure = {
|
| 24 |
+
"controlnet": [],
|
| 25 |
+
"controlnet_hunyuandit": [],
|
| 26 |
+
"controlnet_sd3": [],
|
| 27 |
+
"deprecated": [],
|
| 28 |
+
"latent_diffusion": [],
|
| 29 |
+
"ledits_pp": [],
|
| 30 |
+
"marigold": [],
|
| 31 |
+
"pag": [],
|
| 32 |
+
"stable_diffusion": [],
|
| 33 |
+
"stable_diffusion_xl": [],
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
try:
|
| 37 |
+
if not is_torch_available():
|
| 38 |
+
raise OptionalDependencyNotAvailable()
|
| 39 |
+
except OptionalDependencyNotAvailable:
|
| 40 |
+
from ..utils import dummy_pt_objects # noqa F403
|
| 41 |
+
|
| 42 |
+
_dummy_objects.update(get_objects_from_module(dummy_pt_objects))
|
| 43 |
+
else:
|
| 44 |
+
_import_structure["auto_pipeline"] = [
|
| 45 |
+
"AutoPipelineForImage2Image",
|
| 46 |
+
"AutoPipelineForInpainting",
|
| 47 |
+
"AutoPipelineForText2Image",
|
| 48 |
+
]
|
| 49 |
+
_import_structure["consistency_models"] = ["ConsistencyModelPipeline"]
|
| 50 |
+
_import_structure["ddim"] = ["DDIMPipeline"]
|
| 51 |
+
_import_structure["ddpm"] = ["DDPMPipeline"]
|
| 52 |
+
_import_structure["dit"] = ["DiTPipeline"]
|
| 53 |
+
_import_structure["latent_diffusion"].extend(["LDMSuperResolutionPipeline"])
|
| 54 |
+
_import_structure["pipeline_utils"] = [
|
| 55 |
+
"AudioPipelineOutput",
|
| 56 |
+
"DiffusionPipeline",
|
| 57 |
+
"StableDiffusionMixin",
|
| 58 |
+
"ImagePipelineOutput",
|
| 59 |
+
]
|
| 60 |
+
_import_structure["deprecated"].extend(
|
| 61 |
+
[
|
| 62 |
+
"DanceDiffusionPipeline",
|
| 63 |
+
"PNDMPipeline",
|
| 64 |
+
"LDMPipeline",
|
| 65 |
+
"RePaintPipeline",
|
| 66 |
+
"ScoreSdeVePipeline",
|
| 67 |
+
"KarrasVePipeline",
|
| 68 |
+
]
|
| 69 |
+
)
|
| 70 |
+
try:
|
| 71 |
+
if not (is_torch_available() and is_librosa_available()):
|
| 72 |
+
raise OptionalDependencyNotAvailable()
|
| 73 |
+
except OptionalDependencyNotAvailable:
|
| 74 |
+
from ..utils import dummy_torch_and_librosa_objects # noqa F403
|
| 75 |
+
|
| 76 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_librosa_objects))
|
| 77 |
+
else:
|
| 78 |
+
_import_structure["deprecated"].extend(["AudioDiffusionPipeline", "Mel"])
|
| 79 |
+
|
| 80 |
+
try:
|
| 81 |
+
if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
|
| 82 |
+
raise OptionalDependencyNotAvailable()
|
| 83 |
+
except OptionalDependencyNotAvailable:
|
| 84 |
+
from ..utils import dummy_transformers_and_torch_and_note_seq_objects # noqa F403
|
| 85 |
+
|
| 86 |
+
_dummy_objects.update(get_objects_from_module(dummy_transformers_and_torch_and_note_seq_objects))
|
| 87 |
+
else:
|
| 88 |
+
_import_structure["deprecated"].extend(
|
| 89 |
+
[
|
| 90 |
+
"MidiProcessor",
|
| 91 |
+
"SpectrogramDiffusionPipeline",
|
| 92 |
+
]
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
try:
|
| 96 |
+
if not (is_torch_available() and is_transformers_available()):
|
| 97 |
+
raise OptionalDependencyNotAvailable()
|
| 98 |
+
except OptionalDependencyNotAvailable:
|
| 99 |
+
from ..utils import dummy_torch_and_transformers_objects # noqa F403
|
| 100 |
+
|
| 101 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 102 |
+
else:
|
| 103 |
+
_import_structure["deprecated"].extend(
|
| 104 |
+
[
|
| 105 |
+
"AmusedImg2ImgPipeline",
|
| 106 |
+
"AmusedInpaintPipeline",
|
| 107 |
+
"AmusedPipeline",
|
| 108 |
+
"AudioLDMPipeline",
|
| 109 |
+
"BlipDiffusionPipeline",
|
| 110 |
+
"I2VGenXLPipeline",
|
| 111 |
+
"ImageTextPipelineOutput",
|
| 112 |
+
"MusicLDMPipeline",
|
| 113 |
+
"PIAPipeline",
|
| 114 |
+
"PaintByExamplePipeline",
|
| 115 |
+
"SemanticStableDiffusionPipeline",
|
| 116 |
+
"StableDiffusionAttendAndExcitePipeline",
|
| 117 |
+
"StableDiffusionControlNetXSPipeline",
|
| 118 |
+
"StableDiffusionDiffEditPipeline",
|
| 119 |
+
"StableDiffusionGLIGENPipeline",
|
| 120 |
+
"StableDiffusionGLIGENTextImagePipeline",
|
| 121 |
+
"StableDiffusionLDM3DPipeline",
|
| 122 |
+
"StableDiffusionPanoramaPipeline",
|
| 123 |
+
"StableDiffusionPipelineSafe",
|
| 124 |
+
"StableDiffusionSAGPipeline",
|
| 125 |
+
"StableDiffusionXLControlNetXSPipeline",
|
| 126 |
+
"TextToVideoSDPipeline",
|
| 127 |
+
"TextToVideoZeroPipeline",
|
| 128 |
+
"TextToVideoZeroSDXLPipeline",
|
| 129 |
+
"UnCLIPImageVariationPipeline",
|
| 130 |
+
"UnCLIPPipeline",
|
| 131 |
+
"UniDiffuserModel",
|
| 132 |
+
"UniDiffuserPipeline",
|
| 133 |
+
"UniDiffuserTextDecoder",
|
| 134 |
+
"VQDiffusionPipeline",
|
| 135 |
+
"AltDiffusionPipeline",
|
| 136 |
+
"AltDiffusionImg2ImgPipeline",
|
| 137 |
+
"CycleDiffusionPipeline",
|
| 138 |
+
"StableDiffusionInpaintPipelineLegacy",
|
| 139 |
+
"StableDiffusionPix2PixZeroPipeline",
|
| 140 |
+
"StableDiffusionParadigmsPipeline",
|
| 141 |
+
"StableDiffusionModelEditingPipeline",
|
| 142 |
+
"VersatileDiffusionDualGuidedPipeline",
|
| 143 |
+
"VersatileDiffusionImageVariationPipeline",
|
| 144 |
+
"VersatileDiffusionPipeline",
|
| 145 |
+
"VersatileDiffusionTextToImagePipeline",
|
| 146 |
+
"VideoToVideoSDPipeline",
|
| 147 |
+
"WuerstchenCombinedPipeline",
|
| 148 |
+
"WuerstchenDecoderPipeline",
|
| 149 |
+
"WuerstchenPriorPipeline",
|
| 150 |
+
]
|
| 151 |
+
)
|
| 152 |
+
_import_structure["allegro"] = ["AllegroPipeline"]
|
| 153 |
+
_import_structure["animatediff"] = [
|
| 154 |
+
"AnimateDiffPipeline",
|
| 155 |
+
"AnimateDiffControlNetPipeline",
|
| 156 |
+
"AnimateDiffSDXLPipeline",
|
| 157 |
+
"AnimateDiffSparseControlNetPipeline",
|
| 158 |
+
"AnimateDiffVideoToVideoPipeline",
|
| 159 |
+
"AnimateDiffVideoToVideoControlNetPipeline",
|
| 160 |
+
]
|
| 161 |
+
_import_structure["bria"] = ["BriaPipeline"]
|
| 162 |
+
_import_structure["bria_fibo"] = ["BriaFiboPipeline", "BriaFiboEditPipeline"]
|
| 163 |
+
_import_structure["flux2"] = ["Flux2Pipeline", "Flux2KleinPipeline", "Flux2KleinKVPipeline"]
|
| 164 |
+
_import_structure["flux"] = [
|
| 165 |
+
"FluxControlPipeline",
|
| 166 |
+
"FluxControlInpaintPipeline",
|
| 167 |
+
"FluxControlImg2ImgPipeline",
|
| 168 |
+
"FluxControlNetPipeline",
|
| 169 |
+
"FluxControlNetImg2ImgPipeline",
|
| 170 |
+
"FluxControlNetInpaintPipeline",
|
| 171 |
+
"FluxImg2ImgPipeline",
|
| 172 |
+
"FluxInpaintPipeline",
|
| 173 |
+
"FluxPipeline",
|
| 174 |
+
"FluxFillPipeline",
|
| 175 |
+
"FluxPriorReduxPipeline",
|
| 176 |
+
"ReduxImageEncoder",
|
| 177 |
+
"FluxKontextPipeline",
|
| 178 |
+
"FluxKontextInpaintPipeline",
|
| 179 |
+
]
|
| 180 |
+
_import_structure["prx"] = ["PRXPipeline"]
|
| 181 |
+
_import_structure["audioldm2"] = [
|
| 182 |
+
"AudioLDM2Pipeline",
|
| 183 |
+
"AudioLDM2ProjectionModel",
|
| 184 |
+
"AudioLDM2UNet2DConditionModel",
|
| 185 |
+
]
|
| 186 |
+
_import_structure["chroma"] = ["ChromaPipeline", "ChromaImg2ImgPipeline", "ChromaInpaintPipeline"]
|
| 187 |
+
_import_structure["cogvideo"] = [
|
| 188 |
+
"CogVideoXPipeline",
|
| 189 |
+
"CogVideoXImageToVideoPipeline",
|
| 190 |
+
"CogVideoXVideoToVideoPipeline",
|
| 191 |
+
"CogVideoXFunControlPipeline",
|
| 192 |
+
]
|
| 193 |
+
_import_structure["cogview3"] = ["CogView3PlusPipeline"]
|
| 194 |
+
_import_structure["cogview4"] = ["CogView4Pipeline", "CogView4ControlPipeline"]
|
| 195 |
+
_import_structure["consisid"] = ["ConsisIDPipeline"]
|
| 196 |
+
_import_structure["cosmos"] = [
|
| 197 |
+
"Cosmos2_5_PredictBasePipeline",
|
| 198 |
+
"Cosmos2_5_TransferPipeline",
|
| 199 |
+
"Cosmos2TextToImagePipeline",
|
| 200 |
+
"CosmosTextToWorldPipeline",
|
| 201 |
+
"CosmosVideoToWorldPipeline",
|
| 202 |
+
"Cosmos2VideoToWorldPipeline",
|
| 203 |
+
]
|
| 204 |
+
_import_structure["controlnet"].extend(
|
| 205 |
+
[
|
| 206 |
+
"BlipDiffusionControlNetPipeline",
|
| 207 |
+
"StableDiffusionControlNetImg2ImgPipeline",
|
| 208 |
+
"StableDiffusionControlNetInpaintPipeline",
|
| 209 |
+
"StableDiffusionControlNetPipeline",
|
| 210 |
+
"StableDiffusionXLControlNetImg2ImgPipeline",
|
| 211 |
+
"StableDiffusionXLControlNetInpaintPipeline",
|
| 212 |
+
"StableDiffusionXLControlNetPipeline",
|
| 213 |
+
"StableDiffusionXLControlNetUnionPipeline",
|
| 214 |
+
"StableDiffusionXLControlNetUnionInpaintPipeline",
|
| 215 |
+
"StableDiffusionXLControlNetUnionImg2ImgPipeline",
|
| 216 |
+
]
|
| 217 |
+
)
|
| 218 |
+
_import_structure["pag"].extend(
|
| 219 |
+
[
|
| 220 |
+
"StableDiffusionControlNetPAGInpaintPipeline",
|
| 221 |
+
"AnimateDiffPAGPipeline",
|
| 222 |
+
"KolorsPAGPipeline",
|
| 223 |
+
"HunyuanDiTPAGPipeline",
|
| 224 |
+
"StableDiffusion3PAGPipeline",
|
| 225 |
+
"StableDiffusion3PAGImg2ImgPipeline",
|
| 226 |
+
"StableDiffusionPAGPipeline",
|
| 227 |
+
"StableDiffusionPAGImg2ImgPipeline",
|
| 228 |
+
"StableDiffusionPAGInpaintPipeline",
|
| 229 |
+
"StableDiffusionControlNetPAGPipeline",
|
| 230 |
+
"StableDiffusionXLPAGPipeline",
|
| 231 |
+
"StableDiffusionXLPAGInpaintPipeline",
|
| 232 |
+
"StableDiffusionXLControlNetPAGImg2ImgPipeline",
|
| 233 |
+
"StableDiffusionXLControlNetPAGPipeline",
|
| 234 |
+
"StableDiffusionXLPAGImg2ImgPipeline",
|
| 235 |
+
"PixArtSigmaPAGPipeline",
|
| 236 |
+
"SanaPAGPipeline",
|
| 237 |
+
]
|
| 238 |
+
)
|
| 239 |
+
_import_structure["controlnet_hunyuandit"].extend(
|
| 240 |
+
[
|
| 241 |
+
"HunyuanDiTControlNetPipeline",
|
| 242 |
+
]
|
| 243 |
+
)
|
| 244 |
+
_import_structure["controlnet_sd3"].extend(
|
| 245 |
+
[
|
| 246 |
+
"StableDiffusion3ControlNetPipeline",
|
| 247 |
+
"StableDiffusion3ControlNetInpaintingPipeline",
|
| 248 |
+
]
|
| 249 |
+
)
|
| 250 |
+
_import_structure["deepfloyd_if"] = [
|
| 251 |
+
"IFImg2ImgPipeline",
|
| 252 |
+
"IFImg2ImgSuperResolutionPipeline",
|
| 253 |
+
"IFInpaintingPipeline",
|
| 254 |
+
"IFInpaintingSuperResolutionPipeline",
|
| 255 |
+
"IFPipeline",
|
| 256 |
+
"IFSuperResolutionPipeline",
|
| 257 |
+
]
|
| 258 |
+
_import_structure["easyanimate"] = [
|
| 259 |
+
"EasyAnimatePipeline",
|
| 260 |
+
"EasyAnimateInpaintPipeline",
|
| 261 |
+
"EasyAnimateControlPipeline",
|
| 262 |
+
]
|
| 263 |
+
_import_structure["helios"] = ["HeliosPipeline", "HeliosPyramidPipeline"]
|
| 264 |
+
_import_structure["hidream_image"] = ["HiDreamImagePipeline"]
|
| 265 |
+
_import_structure["hunyuandit"] = ["HunyuanDiTPipeline"]
|
| 266 |
+
_import_structure["hunyuan_video"] = [
|
| 267 |
+
"HunyuanVideoPipeline",
|
| 268 |
+
"HunyuanSkyreelsImageToVideoPipeline",
|
| 269 |
+
"HunyuanVideoImageToVideoPipeline",
|
| 270 |
+
"HunyuanVideoFramepackPipeline",
|
| 271 |
+
]
|
| 272 |
+
_import_structure["hunyuan_video1_5"] = ["HunyuanVideo15Pipeline", "HunyuanVideo15ImageToVideoPipeline"]
|
| 273 |
+
_import_structure["hunyuan_image"] = ["HunyuanImagePipeline", "HunyuanImageRefinerPipeline"]
|
| 274 |
+
_import_structure["kandinsky"] = [
|
| 275 |
+
"KandinskyCombinedPipeline",
|
| 276 |
+
"KandinskyImg2ImgCombinedPipeline",
|
| 277 |
+
"KandinskyImg2ImgPipeline",
|
| 278 |
+
"KandinskyInpaintCombinedPipeline",
|
| 279 |
+
"KandinskyInpaintPipeline",
|
| 280 |
+
"KandinskyPipeline",
|
| 281 |
+
"KandinskyPriorPipeline",
|
| 282 |
+
]
|
| 283 |
+
_import_structure["kandinsky2_2"] = [
|
| 284 |
+
"KandinskyV22CombinedPipeline",
|
| 285 |
+
"KandinskyV22ControlnetImg2ImgPipeline",
|
| 286 |
+
"KandinskyV22ControlnetPipeline",
|
| 287 |
+
"KandinskyV22Img2ImgCombinedPipeline",
|
| 288 |
+
"KandinskyV22Img2ImgPipeline",
|
| 289 |
+
"KandinskyV22InpaintCombinedPipeline",
|
| 290 |
+
"KandinskyV22InpaintPipeline",
|
| 291 |
+
"KandinskyV22Pipeline",
|
| 292 |
+
"KandinskyV22PriorEmb2EmbPipeline",
|
| 293 |
+
"KandinskyV22PriorPipeline",
|
| 294 |
+
]
|
| 295 |
+
_import_structure["kandinsky3"] = [
|
| 296 |
+
"Kandinsky3Img2ImgPipeline",
|
| 297 |
+
"Kandinsky3Pipeline",
|
| 298 |
+
]
|
| 299 |
+
_import_structure["latent_consistency_models"] = [
|
| 300 |
+
"LatentConsistencyModelImg2ImgPipeline",
|
| 301 |
+
"LatentConsistencyModelPipeline",
|
| 302 |
+
]
|
| 303 |
+
_import_structure["latent_diffusion"].extend(["LDMTextToImagePipeline"])
|
| 304 |
+
_import_structure["ledits_pp"].extend(
|
| 305 |
+
[
|
| 306 |
+
"LEditsPPPipelineStableDiffusion",
|
| 307 |
+
"LEditsPPPipelineStableDiffusionXL",
|
| 308 |
+
]
|
| 309 |
+
)
|
| 310 |
+
_import_structure["latte"] = ["LattePipeline"]
|
| 311 |
+
_import_structure["llada2"] = ["LLaDA2Pipeline", "LLaDA2PipelineOutput"]
|
| 312 |
+
_import_structure["ltx"] = [
|
| 313 |
+
"LTXPipeline",
|
| 314 |
+
"LTXImageToVideoPipeline",
|
| 315 |
+
"LTXConditionPipeline",
|
| 316 |
+
"LTXLatentUpsamplePipeline",
|
| 317 |
+
"LTXI2VLongMultiPromptPipeline",
|
| 318 |
+
]
|
| 319 |
+
_import_structure["ltx2"] = [
|
| 320 |
+
"LTX2Pipeline",
|
| 321 |
+
"LTX2ConditionPipeline",
|
| 322 |
+
"LTX2ImageToVideoPipeline",
|
| 323 |
+
"LTX2LatentUpsamplePipeline",
|
| 324 |
+
]
|
| 325 |
+
_import_structure["lumina"] = ["LuminaPipeline", "LuminaText2ImgPipeline"]
|
| 326 |
+
_import_structure["lumina2"] = ["Lumina2Pipeline", "Lumina2Text2ImgPipeline"]
|
| 327 |
+
_import_structure["lucy"] = ["LucyEditPipeline"]
|
| 328 |
+
_import_structure["longcat_image"] = ["LongCatImagePipeline", "LongCatImageEditPipeline"]
|
| 329 |
+
_import_structure["marigold"].extend(
|
| 330 |
+
[
|
| 331 |
+
"MarigoldDepthPipeline",
|
| 332 |
+
"MarigoldIntrinsicsPipeline",
|
| 333 |
+
"MarigoldNormalsPipeline",
|
| 334 |
+
]
|
| 335 |
+
)
|
| 336 |
+
_import_structure["mochi"] = ["MochiPipeline"]
|
| 337 |
+
_import_structure["omnigen"] = ["OmniGenPipeline"]
|
| 338 |
+
_import_structure["ovis_image"] = ["OvisImagePipeline"]
|
| 339 |
+
_import_structure["visualcloze"] = ["VisualClozePipeline", "VisualClozeGenerationPipeline"]
|
| 340 |
+
_import_structure["pixart_alpha"] = ["PixArtAlphaPipeline", "PixArtSigmaPipeline"]
|
| 341 |
+
_import_structure["sana"] = [
|
| 342 |
+
"SanaPipeline",
|
| 343 |
+
"SanaSprintPipeline",
|
| 344 |
+
"SanaControlNetPipeline",
|
| 345 |
+
"SanaSprintImg2ImgPipeline",
|
| 346 |
+
]
|
| 347 |
+
_import_structure["sana_video"] = [
|
| 348 |
+
"SanaVideoPipeline",
|
| 349 |
+
"SanaImageToVideoPipeline",
|
| 350 |
+
]
|
| 351 |
+
_import_structure["shap_e"] = ["ShapEImg2ImgPipeline", "ShapEPipeline"]
|
| 352 |
+
_import_structure["stable_audio"] = [
|
| 353 |
+
"StableAudioProjectionModel",
|
| 354 |
+
"StableAudioPipeline",
|
| 355 |
+
]
|
| 356 |
+
_import_structure["stable_cascade"] = [
|
| 357 |
+
"StableCascadeCombinedPipeline",
|
| 358 |
+
"StableCascadeDecoderPipeline",
|
| 359 |
+
"StableCascadePriorPipeline",
|
| 360 |
+
]
|
| 361 |
+
_import_structure["stable_diffusion"].extend(
|
| 362 |
+
[
|
| 363 |
+
"CLIPImageProjection",
|
| 364 |
+
"StableDiffusionDepth2ImgPipeline",
|
| 365 |
+
"StableDiffusionImageVariationPipeline",
|
| 366 |
+
"StableDiffusionImg2ImgPipeline",
|
| 367 |
+
"StableDiffusionInpaintPipeline",
|
| 368 |
+
"StableDiffusionInstructPix2PixPipeline",
|
| 369 |
+
"StableDiffusionLatentUpscalePipeline",
|
| 370 |
+
"StableDiffusionPipeline",
|
| 371 |
+
"StableDiffusionUpscalePipeline",
|
| 372 |
+
"StableUnCLIPImg2ImgPipeline",
|
| 373 |
+
"StableUnCLIPPipeline",
|
| 374 |
+
]
|
| 375 |
+
)
|
| 376 |
+
_import_structure["aura_flow"] = ["AuraFlowPipeline"]
|
| 377 |
+
_import_structure["stable_diffusion_3"] = [
|
| 378 |
+
"StableDiffusion3Pipeline",
|
| 379 |
+
"StableDiffusion3Img2ImgPipeline",
|
| 380 |
+
"StableDiffusion3InpaintPipeline",
|
| 381 |
+
]
|
| 382 |
+
_import_structure["stable_video_diffusion"] = ["StableVideoDiffusionPipeline"]
|
| 383 |
+
_import_structure["stable_diffusion_xl"].extend(
|
| 384 |
+
[
|
| 385 |
+
"StableDiffusionXLImg2ImgPipeline",
|
| 386 |
+
"StableDiffusionXLInpaintPipeline",
|
| 387 |
+
"StableDiffusionXLInstructPix2PixPipeline",
|
| 388 |
+
"StableDiffusionXLPipeline",
|
| 389 |
+
]
|
| 390 |
+
)
|
| 391 |
+
_import_structure["t2i_adapter"] = [
|
| 392 |
+
"StableDiffusionAdapterPipeline",
|
| 393 |
+
"StableDiffusionXLAdapterPipeline",
|
| 394 |
+
]
|
| 395 |
+
_import_structure["wan"] = [
|
| 396 |
+
"WanPipeline",
|
| 397 |
+
"WanImageToVideoPipeline",
|
| 398 |
+
"WanVideoToVideoPipeline",
|
| 399 |
+
"WanVACEPipeline",
|
| 400 |
+
"WanAnimatePipeline",
|
| 401 |
+
]
|
| 402 |
+
_import_structure["kandinsky5"] = [
|
| 403 |
+
"Kandinsky5T2VPipeline",
|
| 404 |
+
"Kandinsky5I2VPipeline",
|
| 405 |
+
"Kandinsky5T2IPipeline",
|
| 406 |
+
"Kandinsky5I2IPipeline",
|
| 407 |
+
]
|
| 408 |
+
_import_structure["z_image"] = [
|
| 409 |
+
"ZImageControlNetInpaintPipeline",
|
| 410 |
+
"ZImageControlNetPipeline",
|
| 411 |
+
"ZImageImg2ImgPipeline",
|
| 412 |
+
"ZImageInpaintPipeline",
|
| 413 |
+
"ZImageOmniPipeline",
|
| 414 |
+
"ZImagePipeline",
|
| 415 |
+
]
|
| 416 |
+
_import_structure["skyreels_v2"] = [
|
| 417 |
+
"SkyReelsV2DiffusionForcingPipeline",
|
| 418 |
+
"SkyReelsV2DiffusionForcingImageToVideoPipeline",
|
| 419 |
+
"SkyReelsV2DiffusionForcingVideoToVideoPipeline",
|
| 420 |
+
"SkyReelsV2ImageToVideoPipeline",
|
| 421 |
+
"SkyReelsV2Pipeline",
|
| 422 |
+
]
|
| 423 |
+
_import_structure["nucleusmoe_image"] = ["NucleusMoEImagePipeline"]
|
| 424 |
+
_import_structure["qwenimage"] = [
|
| 425 |
+
"QwenImagePipeline",
|
| 426 |
+
"QwenImageImg2ImgPipeline",
|
| 427 |
+
"QwenImageInpaintPipeline",
|
| 428 |
+
"QwenImageEditPipeline",
|
| 429 |
+
"QwenImageEditPlusPipeline",
|
| 430 |
+
"QwenImageEditInpaintPipeline",
|
| 431 |
+
"QwenImageControlNetInpaintPipeline",
|
| 432 |
+
"QwenImageControlNetPipeline",
|
| 433 |
+
"QwenImageLayeredPipeline",
|
| 434 |
+
]
|
| 435 |
+
_import_structure["chronoedit"] = ["ChronoEditPipeline"]
|
| 436 |
+
_import_structure["glm_image"] = ["GlmImagePipeline"]
|
| 437 |
+
|
| 438 |
+
try:
|
| 439 |
+
if not is_onnx_available():
|
| 440 |
+
raise OptionalDependencyNotAvailable()
|
| 441 |
+
except OptionalDependencyNotAvailable:
|
| 442 |
+
from ..utils import dummy_onnx_objects # noqa F403
|
| 443 |
+
|
| 444 |
+
_dummy_objects.update(get_objects_from_module(dummy_onnx_objects))
|
| 445 |
+
else:
|
| 446 |
+
_import_structure["onnx_utils"] = ["OnnxRuntimeModel"]
|
| 447 |
+
try:
|
| 448 |
+
if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
|
| 449 |
+
raise OptionalDependencyNotAvailable()
|
| 450 |
+
except OptionalDependencyNotAvailable:
|
| 451 |
+
from ..utils import dummy_torch_and_transformers_and_onnx_objects # noqa F403
|
| 452 |
+
|
| 453 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_onnx_objects))
|
| 454 |
+
else:
|
| 455 |
+
_import_structure["stable_diffusion"].extend(
|
| 456 |
+
[
|
| 457 |
+
"OnnxStableDiffusionImg2ImgPipeline",
|
| 458 |
+
"OnnxStableDiffusionInpaintPipeline",
|
| 459 |
+
"OnnxStableDiffusionPipeline",
|
| 460 |
+
"OnnxStableDiffusionUpscalePipeline",
|
| 461 |
+
"StableDiffusionOnnxPipeline",
|
| 462 |
+
]
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
try:
|
| 466 |
+
if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
|
| 467 |
+
raise OptionalDependencyNotAvailable()
|
| 468 |
+
except OptionalDependencyNotAvailable:
|
| 469 |
+
from ..utils import (
|
| 470 |
+
dummy_torch_and_transformers_and_sentencepiece_objects,
|
| 471 |
+
)
|
| 472 |
+
|
| 473 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_sentencepiece_objects))
|
| 474 |
+
else:
|
| 475 |
+
_import_structure["kolors"] = [
|
| 476 |
+
"KolorsPipeline",
|
| 477 |
+
"KolorsImg2ImgPipeline",
|
| 478 |
+
]
|
| 479 |
+
|
| 480 |
+
try:
|
| 481 |
+
if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
|
| 482 |
+
raise OptionalDependencyNotAvailable()
|
| 483 |
+
except OptionalDependencyNotAvailable:
|
| 484 |
+
from ..utils import (
|
| 485 |
+
dummy_torch_and_transformers_and_opencv_objects,
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_opencv_objects))
|
| 489 |
+
else:
|
| 490 |
+
_import_structure["consisid"] = ["ConsisIDPipeline"]
|
| 491 |
+
|
| 492 |
+
try:
|
| 493 |
+
if not is_flax_available():
|
| 494 |
+
raise OptionalDependencyNotAvailable()
|
| 495 |
+
except OptionalDependencyNotAvailable:
|
| 496 |
+
from ..utils import dummy_flax_objects # noqa F403
|
| 497 |
+
|
| 498 |
+
_dummy_objects.update(get_objects_from_module(dummy_flax_objects))
|
| 499 |
+
else:
|
| 500 |
+
_import_structure["pipeline_flax_utils"] = ["FlaxDiffusionPipeline"]
|
| 501 |
+
try:
|
| 502 |
+
if not (is_flax_available() and is_transformers_available()):
|
| 503 |
+
raise OptionalDependencyNotAvailable()
|
| 504 |
+
except OptionalDependencyNotAvailable:
|
| 505 |
+
from ..utils import dummy_flax_and_transformers_objects # noqa F403
|
| 506 |
+
|
| 507 |
+
_dummy_objects.update(get_objects_from_module(dummy_flax_and_transformers_objects))
|
| 508 |
+
else:
|
| 509 |
+
_import_structure["controlnet"].extend(["FlaxStableDiffusionControlNetPipeline"])
|
| 510 |
+
_import_structure["stable_diffusion"].extend(
|
| 511 |
+
[
|
| 512 |
+
"FlaxStableDiffusionImg2ImgPipeline",
|
| 513 |
+
"FlaxStableDiffusionInpaintPipeline",
|
| 514 |
+
"FlaxStableDiffusionPipeline",
|
| 515 |
+
]
|
| 516 |
+
)
|
| 517 |
+
_import_structure["stable_diffusion_xl"].extend(
|
| 518 |
+
[
|
| 519 |
+
"FlaxStableDiffusionXLPipeline",
|
| 520 |
+
]
|
| 521 |
+
)
|
| 522 |
+
|
| 523 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 524 |
+
try:
|
| 525 |
+
if not is_torch_available():
|
| 526 |
+
raise OptionalDependencyNotAvailable()
|
| 527 |
+
except OptionalDependencyNotAvailable:
|
| 528 |
+
from ..utils.dummy_pt_objects import * # noqa F403
|
| 529 |
+
|
| 530 |
+
else:
|
| 531 |
+
from .auto_pipeline import (
|
| 532 |
+
AutoPipelineForImage2Image,
|
| 533 |
+
AutoPipelineForInpainting,
|
| 534 |
+
AutoPipelineForText2Image,
|
| 535 |
+
)
|
| 536 |
+
from .consistency_models import ConsistencyModelPipeline
|
| 537 |
+
from .ddim import DDIMPipeline
|
| 538 |
+
from .ddpm import DDPMPipeline
|
| 539 |
+
from .deprecated import (
|
| 540 |
+
DanceDiffusionPipeline,
|
| 541 |
+
KarrasVePipeline,
|
| 542 |
+
LDMPipeline,
|
| 543 |
+
PNDMPipeline,
|
| 544 |
+
RePaintPipeline,
|
| 545 |
+
ScoreSdeVePipeline,
|
| 546 |
+
)
|
| 547 |
+
from .dit import DiTPipeline
|
| 548 |
+
from .latent_diffusion import LDMSuperResolutionPipeline
|
| 549 |
+
from .pipeline_utils import (
|
| 550 |
+
AudioPipelineOutput,
|
| 551 |
+
DiffusionPipeline,
|
| 552 |
+
ImagePipelineOutput,
|
| 553 |
+
StableDiffusionMixin,
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
try:
|
| 557 |
+
if not (is_torch_available() and is_librosa_available()):
|
| 558 |
+
raise OptionalDependencyNotAvailable()
|
| 559 |
+
except OptionalDependencyNotAvailable:
|
| 560 |
+
from ..utils.dummy_torch_and_librosa_objects import *
|
| 561 |
+
else:
|
| 562 |
+
from .deprecated import AudioDiffusionPipeline, Mel
|
| 563 |
+
|
| 564 |
+
try:
|
| 565 |
+
if not (is_torch_available() and is_transformers_available()):
|
| 566 |
+
raise OptionalDependencyNotAvailable()
|
| 567 |
+
except OptionalDependencyNotAvailable:
|
| 568 |
+
from ..utils.dummy_torch_and_transformers_objects import *
|
| 569 |
+
else:
|
| 570 |
+
from .allegro import AllegroPipeline
|
| 571 |
+
from .animatediff import (
|
| 572 |
+
AnimateDiffControlNetPipeline,
|
| 573 |
+
AnimateDiffPipeline,
|
| 574 |
+
AnimateDiffSDXLPipeline,
|
| 575 |
+
AnimateDiffSparseControlNetPipeline,
|
| 576 |
+
AnimateDiffVideoToVideoControlNetPipeline,
|
| 577 |
+
AnimateDiffVideoToVideoPipeline,
|
| 578 |
+
)
|
| 579 |
+
from .audioldm2 import (
|
| 580 |
+
AudioLDM2Pipeline,
|
| 581 |
+
AudioLDM2ProjectionModel,
|
| 582 |
+
AudioLDM2UNet2DConditionModel,
|
| 583 |
+
)
|
| 584 |
+
from .aura_flow import AuraFlowPipeline
|
| 585 |
+
from .bria import BriaPipeline
|
| 586 |
+
from .bria_fibo import BriaFiboEditPipeline, BriaFiboPipeline
|
| 587 |
+
from .chroma import ChromaImg2ImgPipeline, ChromaInpaintPipeline, ChromaPipeline
|
| 588 |
+
from .chronoedit import ChronoEditPipeline
|
| 589 |
+
from .cogvideo import (
|
| 590 |
+
CogVideoXFunControlPipeline,
|
| 591 |
+
CogVideoXImageToVideoPipeline,
|
| 592 |
+
CogVideoXPipeline,
|
| 593 |
+
CogVideoXVideoToVideoPipeline,
|
| 594 |
+
)
|
| 595 |
+
from .cogview3 import CogView3PlusPipeline
|
| 596 |
+
from .cogview4 import CogView4ControlPipeline, CogView4Pipeline
|
| 597 |
+
from .controlnet import (
|
| 598 |
+
BlipDiffusionControlNetPipeline,
|
| 599 |
+
StableDiffusionControlNetImg2ImgPipeline,
|
| 600 |
+
StableDiffusionControlNetInpaintPipeline,
|
| 601 |
+
StableDiffusionControlNetPipeline,
|
| 602 |
+
StableDiffusionXLControlNetImg2ImgPipeline,
|
| 603 |
+
StableDiffusionXLControlNetInpaintPipeline,
|
| 604 |
+
StableDiffusionXLControlNetPipeline,
|
| 605 |
+
StableDiffusionXLControlNetUnionImg2ImgPipeline,
|
| 606 |
+
StableDiffusionXLControlNetUnionInpaintPipeline,
|
| 607 |
+
StableDiffusionXLControlNetUnionPipeline,
|
| 608 |
+
)
|
| 609 |
+
from .controlnet_hunyuandit import (
|
| 610 |
+
HunyuanDiTControlNetPipeline,
|
| 611 |
+
)
|
| 612 |
+
from .controlnet_sd3 import StableDiffusion3ControlNetInpaintingPipeline, StableDiffusion3ControlNetPipeline
|
| 613 |
+
from .cosmos import (
|
| 614 |
+
Cosmos2_5_PredictBasePipeline,
|
| 615 |
+
Cosmos2_5_TransferPipeline,
|
| 616 |
+
Cosmos2TextToImagePipeline,
|
| 617 |
+
Cosmos2VideoToWorldPipeline,
|
| 618 |
+
CosmosTextToWorldPipeline,
|
| 619 |
+
CosmosVideoToWorldPipeline,
|
| 620 |
+
)
|
| 621 |
+
from .deepfloyd_if import (
|
| 622 |
+
IFImg2ImgPipeline,
|
| 623 |
+
IFImg2ImgSuperResolutionPipeline,
|
| 624 |
+
IFInpaintingPipeline,
|
| 625 |
+
IFInpaintingSuperResolutionPipeline,
|
| 626 |
+
IFPipeline,
|
| 627 |
+
IFSuperResolutionPipeline,
|
| 628 |
+
)
|
| 629 |
+
from .deprecated import (
|
| 630 |
+
AltDiffusionImg2ImgPipeline,
|
| 631 |
+
AltDiffusionPipeline,
|
| 632 |
+
AmusedImg2ImgPipeline,
|
| 633 |
+
AmusedInpaintPipeline,
|
| 634 |
+
AmusedPipeline,
|
| 635 |
+
AudioLDMPipeline,
|
| 636 |
+
BlipDiffusionPipeline,
|
| 637 |
+
CycleDiffusionPipeline,
|
| 638 |
+
I2VGenXLPipeline,
|
| 639 |
+
ImageTextPipelineOutput,
|
| 640 |
+
MusicLDMPipeline,
|
| 641 |
+
PaintByExamplePipeline,
|
| 642 |
+
PIAPipeline,
|
| 643 |
+
SemanticStableDiffusionPipeline,
|
| 644 |
+
StableDiffusionAttendAndExcitePipeline,
|
| 645 |
+
StableDiffusionControlNetXSPipeline,
|
| 646 |
+
StableDiffusionDiffEditPipeline,
|
| 647 |
+
StableDiffusionGLIGENPipeline,
|
| 648 |
+
StableDiffusionGLIGENTextImagePipeline,
|
| 649 |
+
StableDiffusionInpaintPipelineLegacy,
|
| 650 |
+
StableDiffusionLDM3DPipeline,
|
| 651 |
+
StableDiffusionModelEditingPipeline,
|
| 652 |
+
StableDiffusionPanoramaPipeline,
|
| 653 |
+
StableDiffusionParadigmsPipeline,
|
| 654 |
+
StableDiffusionPipelineSafe,
|
| 655 |
+
StableDiffusionPix2PixZeroPipeline,
|
| 656 |
+
StableDiffusionSAGPipeline,
|
| 657 |
+
StableDiffusionXLControlNetXSPipeline,
|
| 658 |
+
TextToVideoSDPipeline,
|
| 659 |
+
TextToVideoZeroPipeline,
|
| 660 |
+
TextToVideoZeroSDXLPipeline,
|
| 661 |
+
UnCLIPImageVariationPipeline,
|
| 662 |
+
UnCLIPPipeline,
|
| 663 |
+
UniDiffuserModel,
|
| 664 |
+
UniDiffuserPipeline,
|
| 665 |
+
UniDiffuserTextDecoder,
|
| 666 |
+
VersatileDiffusionDualGuidedPipeline,
|
| 667 |
+
VersatileDiffusionImageVariationPipeline,
|
| 668 |
+
VersatileDiffusionPipeline,
|
| 669 |
+
VersatileDiffusionTextToImagePipeline,
|
| 670 |
+
VideoToVideoSDPipeline,
|
| 671 |
+
VQDiffusionPipeline,
|
| 672 |
+
WuerstchenCombinedPipeline,
|
| 673 |
+
WuerstchenDecoderPipeline,
|
| 674 |
+
WuerstchenPriorPipeline,
|
| 675 |
+
)
|
| 676 |
+
from .easyanimate import (
|
| 677 |
+
EasyAnimateControlPipeline,
|
| 678 |
+
EasyAnimateInpaintPipeline,
|
| 679 |
+
EasyAnimatePipeline,
|
| 680 |
+
)
|
| 681 |
+
from .flux import (
|
| 682 |
+
FluxControlImg2ImgPipeline,
|
| 683 |
+
FluxControlInpaintPipeline,
|
| 684 |
+
FluxControlNetImg2ImgPipeline,
|
| 685 |
+
FluxControlNetInpaintPipeline,
|
| 686 |
+
FluxControlNetPipeline,
|
| 687 |
+
FluxControlPipeline,
|
| 688 |
+
FluxFillPipeline,
|
| 689 |
+
FluxImg2ImgPipeline,
|
| 690 |
+
FluxInpaintPipeline,
|
| 691 |
+
FluxKontextInpaintPipeline,
|
| 692 |
+
FluxKontextPipeline,
|
| 693 |
+
FluxPipeline,
|
| 694 |
+
FluxPriorReduxPipeline,
|
| 695 |
+
ReduxImageEncoder,
|
| 696 |
+
)
|
| 697 |
+
from .flux2 import Flux2KleinKVPipeline, Flux2KleinPipeline, Flux2Pipeline
|
| 698 |
+
from .glm_image import GlmImagePipeline
|
| 699 |
+
from .helios import HeliosPipeline, HeliosPyramidPipeline
|
| 700 |
+
from .hidream_image import HiDreamImagePipeline
|
| 701 |
+
from .hunyuan_image import HunyuanImagePipeline, HunyuanImageRefinerPipeline
|
| 702 |
+
from .hunyuan_video import (
|
| 703 |
+
HunyuanSkyreelsImageToVideoPipeline,
|
| 704 |
+
HunyuanVideoFramepackPipeline,
|
| 705 |
+
HunyuanVideoImageToVideoPipeline,
|
| 706 |
+
HunyuanVideoPipeline,
|
| 707 |
+
)
|
| 708 |
+
from .hunyuan_video1_5 import HunyuanVideo15ImageToVideoPipeline, HunyuanVideo15Pipeline
|
| 709 |
+
from .hunyuandit import HunyuanDiTPipeline
|
| 710 |
+
from .kandinsky import (
|
| 711 |
+
KandinskyCombinedPipeline,
|
| 712 |
+
KandinskyImg2ImgCombinedPipeline,
|
| 713 |
+
KandinskyImg2ImgPipeline,
|
| 714 |
+
KandinskyInpaintCombinedPipeline,
|
| 715 |
+
KandinskyInpaintPipeline,
|
| 716 |
+
KandinskyPipeline,
|
| 717 |
+
KandinskyPriorPipeline,
|
| 718 |
+
)
|
| 719 |
+
from .kandinsky2_2 import (
|
| 720 |
+
KandinskyV22CombinedPipeline,
|
| 721 |
+
KandinskyV22ControlnetImg2ImgPipeline,
|
| 722 |
+
KandinskyV22ControlnetPipeline,
|
| 723 |
+
KandinskyV22Img2ImgCombinedPipeline,
|
| 724 |
+
KandinskyV22Img2ImgPipeline,
|
| 725 |
+
KandinskyV22InpaintCombinedPipeline,
|
| 726 |
+
KandinskyV22InpaintPipeline,
|
| 727 |
+
KandinskyV22Pipeline,
|
| 728 |
+
KandinskyV22PriorEmb2EmbPipeline,
|
| 729 |
+
KandinskyV22PriorPipeline,
|
| 730 |
+
)
|
| 731 |
+
from .kandinsky3 import (
|
| 732 |
+
Kandinsky3Img2ImgPipeline,
|
| 733 |
+
Kandinsky3Pipeline,
|
| 734 |
+
)
|
| 735 |
+
from .kandinsky5 import (
|
| 736 |
+
Kandinsky5I2IPipeline,
|
| 737 |
+
Kandinsky5I2VPipeline,
|
| 738 |
+
Kandinsky5T2IPipeline,
|
| 739 |
+
Kandinsky5T2VPipeline,
|
| 740 |
+
)
|
| 741 |
+
from .latent_consistency_models import (
|
| 742 |
+
LatentConsistencyModelImg2ImgPipeline,
|
| 743 |
+
LatentConsistencyModelPipeline,
|
| 744 |
+
)
|
| 745 |
+
from .latent_diffusion import LDMTextToImagePipeline
|
| 746 |
+
from .latte import LattePipeline
|
| 747 |
+
from .ledits_pp import (
|
| 748 |
+
LEditsPPDiffusionPipelineOutput,
|
| 749 |
+
LEditsPPInversionPipelineOutput,
|
| 750 |
+
LEditsPPPipelineStableDiffusion,
|
| 751 |
+
LEditsPPPipelineStableDiffusionXL,
|
| 752 |
+
)
|
| 753 |
+
from .llada2 import LLaDA2Pipeline, LLaDA2PipelineOutput
|
| 754 |
+
from .longcat_image import LongCatImageEditPipeline, LongCatImagePipeline
|
| 755 |
+
from .ltx import (
|
| 756 |
+
LTXConditionPipeline,
|
| 757 |
+
LTXI2VLongMultiPromptPipeline,
|
| 758 |
+
LTXImageToVideoPipeline,
|
| 759 |
+
LTXLatentUpsamplePipeline,
|
| 760 |
+
LTXPipeline,
|
| 761 |
+
)
|
| 762 |
+
from .ltx2 import LTX2ConditionPipeline, LTX2ImageToVideoPipeline, LTX2LatentUpsamplePipeline, LTX2Pipeline
|
| 763 |
+
from .lucy import LucyEditPipeline
|
| 764 |
+
from .lumina import LuminaPipeline, LuminaText2ImgPipeline
|
| 765 |
+
from .lumina2 import Lumina2Pipeline, Lumina2Text2ImgPipeline
|
| 766 |
+
from .marigold import (
|
| 767 |
+
MarigoldDepthPipeline,
|
| 768 |
+
MarigoldIntrinsicsPipeline,
|
| 769 |
+
MarigoldNormalsPipeline,
|
| 770 |
+
)
|
| 771 |
+
from .mochi import MochiPipeline
|
| 772 |
+
from .nucleusmoe_image import NucleusMoEImagePipeline
|
| 773 |
+
from .omnigen import OmniGenPipeline
|
| 774 |
+
from .ovis_image import OvisImagePipeline
|
| 775 |
+
from .pag import (
|
| 776 |
+
AnimateDiffPAGPipeline,
|
| 777 |
+
HunyuanDiTPAGPipeline,
|
| 778 |
+
KolorsPAGPipeline,
|
| 779 |
+
PixArtSigmaPAGPipeline,
|
| 780 |
+
SanaPAGPipeline,
|
| 781 |
+
StableDiffusion3PAGImg2ImgPipeline,
|
| 782 |
+
StableDiffusion3PAGPipeline,
|
| 783 |
+
StableDiffusionControlNetPAGInpaintPipeline,
|
| 784 |
+
StableDiffusionControlNetPAGPipeline,
|
| 785 |
+
StableDiffusionPAGImg2ImgPipeline,
|
| 786 |
+
StableDiffusionPAGInpaintPipeline,
|
| 787 |
+
StableDiffusionPAGPipeline,
|
| 788 |
+
StableDiffusionXLControlNetPAGImg2ImgPipeline,
|
| 789 |
+
StableDiffusionXLControlNetPAGPipeline,
|
| 790 |
+
StableDiffusionXLPAGImg2ImgPipeline,
|
| 791 |
+
StableDiffusionXLPAGInpaintPipeline,
|
| 792 |
+
StableDiffusionXLPAGPipeline,
|
| 793 |
+
)
|
| 794 |
+
from .pixart_alpha import PixArtAlphaPipeline, PixArtSigmaPipeline
|
| 795 |
+
from .prx import PRXPipeline
|
| 796 |
+
from .qwenimage import (
|
| 797 |
+
QwenImageControlNetInpaintPipeline,
|
| 798 |
+
QwenImageControlNetPipeline,
|
| 799 |
+
QwenImageEditInpaintPipeline,
|
| 800 |
+
QwenImageEditPipeline,
|
| 801 |
+
QwenImageEditPlusPipeline,
|
| 802 |
+
QwenImageImg2ImgPipeline,
|
| 803 |
+
QwenImageInpaintPipeline,
|
| 804 |
+
QwenImageLayeredPipeline,
|
| 805 |
+
QwenImagePipeline,
|
| 806 |
+
)
|
| 807 |
+
from .sana import (
|
| 808 |
+
SanaControlNetPipeline,
|
| 809 |
+
SanaPipeline,
|
| 810 |
+
SanaSprintImg2ImgPipeline,
|
| 811 |
+
SanaSprintPipeline,
|
| 812 |
+
)
|
| 813 |
+
from .sana_video import SanaImageToVideoPipeline, SanaVideoPipeline
|
| 814 |
+
from .shap_e import ShapEImg2ImgPipeline, ShapEPipeline
|
| 815 |
+
from .stable_audio import StableAudioPipeline, StableAudioProjectionModel
|
| 816 |
+
from .stable_cascade import (
|
| 817 |
+
StableCascadeCombinedPipeline,
|
| 818 |
+
StableCascadeDecoderPipeline,
|
| 819 |
+
StableCascadePriorPipeline,
|
| 820 |
+
)
|
| 821 |
+
from .stable_diffusion import (
|
| 822 |
+
CLIPImageProjection,
|
| 823 |
+
StableDiffusionDepth2ImgPipeline,
|
| 824 |
+
StableDiffusionImageVariationPipeline,
|
| 825 |
+
StableDiffusionImg2ImgPipeline,
|
| 826 |
+
StableDiffusionInpaintPipeline,
|
| 827 |
+
StableDiffusionInstructPix2PixPipeline,
|
| 828 |
+
StableDiffusionLatentUpscalePipeline,
|
| 829 |
+
StableDiffusionPipeline,
|
| 830 |
+
StableDiffusionUpscalePipeline,
|
| 831 |
+
StableUnCLIPImg2ImgPipeline,
|
| 832 |
+
StableUnCLIPPipeline,
|
| 833 |
+
)
|
| 834 |
+
from .stable_diffusion_3 import (
|
| 835 |
+
StableDiffusion3Img2ImgPipeline,
|
| 836 |
+
StableDiffusion3InpaintPipeline,
|
| 837 |
+
StableDiffusion3Pipeline,
|
| 838 |
+
)
|
| 839 |
+
from .stable_diffusion_xl import (
|
| 840 |
+
StableDiffusionXLImg2ImgPipeline,
|
| 841 |
+
StableDiffusionXLInpaintPipeline,
|
| 842 |
+
StableDiffusionXLInstructPix2PixPipeline,
|
| 843 |
+
StableDiffusionXLPipeline,
|
| 844 |
+
)
|
| 845 |
+
from .stable_video_diffusion import StableVideoDiffusionPipeline
|
| 846 |
+
from .t2i_adapter import (
|
| 847 |
+
StableDiffusionAdapterPipeline,
|
| 848 |
+
StableDiffusionXLAdapterPipeline,
|
| 849 |
+
)
|
| 850 |
+
from .visualcloze import VisualClozeGenerationPipeline, VisualClozePipeline
|
| 851 |
+
from .wan import (
|
| 852 |
+
WanAnimatePipeline,
|
| 853 |
+
WanImageToVideoPipeline,
|
| 854 |
+
WanPipeline,
|
| 855 |
+
WanVACEPipeline,
|
| 856 |
+
WanVideoToVideoPipeline,
|
| 857 |
+
)
|
| 858 |
+
from .z_image import (
|
| 859 |
+
ZImageControlNetInpaintPipeline,
|
| 860 |
+
ZImageControlNetPipeline,
|
| 861 |
+
ZImageImg2ImgPipeline,
|
| 862 |
+
ZImageInpaintPipeline,
|
| 863 |
+
ZImageOmniPipeline,
|
| 864 |
+
ZImagePipeline,
|
| 865 |
+
)
|
| 866 |
+
|
| 867 |
+
try:
|
| 868 |
+
if not is_onnx_available():
|
| 869 |
+
raise OptionalDependencyNotAvailable()
|
| 870 |
+
except OptionalDependencyNotAvailable:
|
| 871 |
+
from ..utils.dummy_onnx_objects import * # noqa F403
|
| 872 |
+
|
| 873 |
+
else:
|
| 874 |
+
from .onnx_utils import OnnxRuntimeModel
|
| 875 |
+
|
| 876 |
+
try:
|
| 877 |
+
if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
|
| 878 |
+
raise OptionalDependencyNotAvailable()
|
| 879 |
+
except OptionalDependencyNotAvailable:
|
| 880 |
+
from ..utils.dummy_torch_and_transformers_and_onnx_objects import *
|
| 881 |
+
else:
|
| 882 |
+
from .stable_diffusion import (
|
| 883 |
+
OnnxStableDiffusionImg2ImgPipeline,
|
| 884 |
+
OnnxStableDiffusionInpaintPipeline,
|
| 885 |
+
OnnxStableDiffusionPipeline,
|
| 886 |
+
OnnxStableDiffusionUpscalePipeline,
|
| 887 |
+
StableDiffusionOnnxPipeline,
|
| 888 |
+
)
|
| 889 |
+
|
| 890 |
+
try:
|
| 891 |
+
if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
|
| 892 |
+
raise OptionalDependencyNotAvailable()
|
| 893 |
+
except OptionalDependencyNotAvailable:
|
| 894 |
+
from ..utils.dummy_torch_and_transformers_and_sentencepiece_objects import *
|
| 895 |
+
else:
|
| 896 |
+
from .kolors import (
|
| 897 |
+
KolorsImg2ImgPipeline,
|
| 898 |
+
KolorsPipeline,
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
try:
|
| 902 |
+
if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
|
| 903 |
+
raise OptionalDependencyNotAvailable()
|
| 904 |
+
except OptionalDependencyNotAvailable:
|
| 905 |
+
from ..utils.dummy_torch_and_transformers_and_opencv_objects import *
|
| 906 |
+
else:
|
| 907 |
+
from .consisid import ConsisIDPipeline
|
| 908 |
+
|
| 909 |
+
try:
|
| 910 |
+
if not is_flax_available():
|
| 911 |
+
raise OptionalDependencyNotAvailable()
|
| 912 |
+
except OptionalDependencyNotAvailable:
|
| 913 |
+
from ..utils.dummy_flax_objects import * # noqa F403
|
| 914 |
+
else:
|
| 915 |
+
from .pipeline_flax_utils import FlaxDiffusionPipeline
|
| 916 |
+
|
| 917 |
+
try:
|
| 918 |
+
if not (is_flax_available() and is_transformers_available()):
|
| 919 |
+
raise OptionalDependencyNotAvailable()
|
| 920 |
+
except OptionalDependencyNotAvailable:
|
| 921 |
+
from ..utils.dummy_flax_and_transformers_objects import *
|
| 922 |
+
else:
|
| 923 |
+
from .controlnet import FlaxStableDiffusionControlNetPipeline
|
| 924 |
+
from .stable_diffusion import (
|
| 925 |
+
FlaxStableDiffusionImg2ImgPipeline,
|
| 926 |
+
FlaxStableDiffusionInpaintPipeline,
|
| 927 |
+
FlaxStableDiffusionPipeline,
|
| 928 |
+
)
|
| 929 |
+
from .stable_diffusion_xl import (
|
| 930 |
+
FlaxStableDiffusionXLPipeline,
|
| 931 |
+
)
|
| 932 |
+
|
| 933 |
+
try:
|
| 934 |
+
if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
|
| 935 |
+
raise OptionalDependencyNotAvailable()
|
| 936 |
+
except OptionalDependencyNotAvailable:
|
| 937 |
+
from ..utils.dummy_transformers_and_torch_and_note_seq_objects import * # noqa F403
|
| 938 |
+
|
| 939 |
+
else:
|
| 940 |
+
from .deprecated import (
|
| 941 |
+
MidiProcessor,
|
| 942 |
+
SpectrogramDiffusionPipeline,
|
| 943 |
+
)
|
| 944 |
+
|
| 945 |
+
from .skyreels_v2 import (
|
| 946 |
+
SkyReelsV2DiffusionForcingImageToVideoPipeline,
|
| 947 |
+
SkyReelsV2DiffusionForcingPipeline,
|
| 948 |
+
SkyReelsV2DiffusionForcingVideoToVideoPipeline,
|
| 949 |
+
SkyReelsV2ImageToVideoPipeline,
|
| 950 |
+
SkyReelsV2Pipeline,
|
| 951 |
+
)
|
| 952 |
+
|
| 953 |
+
else:
|
| 954 |
+
import sys
|
| 955 |
+
|
| 956 |
+
sys.modules[__name__] = _LazyModule(
|
| 957 |
+
__name__,
|
| 958 |
+
globals()["__file__"],
|
| 959 |
+
_import_structure,
|
| 960 |
+
module_spec=__spec__,
|
| 961 |
+
)
|
| 962 |
+
for name, value in _dummy_objects.items():
|
| 963 |
+
setattr(sys.modules[__name__], name, value)
|
third_party/diffusers/src/diffusers/pipelines/auto_pipeline.py
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# coding=utf-8
|
| 2 |
+
# Copyright 2025 The HuggingFace Inc. team.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
from collections import OrderedDict
|
| 17 |
+
|
| 18 |
+
from huggingface_hub.utils import validate_hf_hub_args
|
| 19 |
+
|
| 20 |
+
from ..configuration_utils import ConfigMixin
|
| 21 |
+
from ..models.controlnets import ControlNetUnionModel
|
| 22 |
+
from ..utils import is_sentencepiece_available
|
| 23 |
+
from .aura_flow import AuraFlowPipeline
|
| 24 |
+
from .chroma import ChromaPipeline
|
| 25 |
+
from .cogview3 import CogView3PlusPipeline
|
| 26 |
+
from .cogview4 import CogView4ControlPipeline, CogView4Pipeline
|
| 27 |
+
from .controlnet import (
|
| 28 |
+
StableDiffusionControlNetImg2ImgPipeline,
|
| 29 |
+
StableDiffusionControlNetInpaintPipeline,
|
| 30 |
+
StableDiffusionControlNetPipeline,
|
| 31 |
+
StableDiffusionXLControlNetImg2ImgPipeline,
|
| 32 |
+
StableDiffusionXLControlNetInpaintPipeline,
|
| 33 |
+
StableDiffusionXLControlNetPipeline,
|
| 34 |
+
StableDiffusionXLControlNetUnionImg2ImgPipeline,
|
| 35 |
+
StableDiffusionXLControlNetUnionInpaintPipeline,
|
| 36 |
+
StableDiffusionXLControlNetUnionPipeline,
|
| 37 |
+
)
|
| 38 |
+
from .controlnet_sd3 import (
|
| 39 |
+
StableDiffusion3ControlNetInpaintingPipeline,
|
| 40 |
+
StableDiffusion3ControlNetPipeline,
|
| 41 |
+
)
|
| 42 |
+
from .deepfloyd_if import IFImg2ImgPipeline, IFInpaintingPipeline, IFPipeline
|
| 43 |
+
from .deprecated.wuerstchen import WuerstchenCombinedPipeline, WuerstchenDecoderPipeline
|
| 44 |
+
from .flux import (
|
| 45 |
+
FluxControlImg2ImgPipeline,
|
| 46 |
+
FluxControlInpaintPipeline,
|
| 47 |
+
FluxControlNetImg2ImgPipeline,
|
| 48 |
+
FluxControlNetInpaintPipeline,
|
| 49 |
+
FluxControlNetPipeline,
|
| 50 |
+
FluxControlPipeline,
|
| 51 |
+
FluxImg2ImgPipeline,
|
| 52 |
+
FluxInpaintPipeline,
|
| 53 |
+
FluxKontextPipeline,
|
| 54 |
+
FluxPipeline,
|
| 55 |
+
)
|
| 56 |
+
from .flux2 import Flux2KleinPipeline, Flux2Pipeline
|
| 57 |
+
from .glm_image import GlmImagePipeline
|
| 58 |
+
from .helios import HeliosPipeline, HeliosPyramidPipeline
|
| 59 |
+
from .hunyuandit import HunyuanDiTPipeline
|
| 60 |
+
from .kandinsky import (
|
| 61 |
+
KandinskyCombinedPipeline,
|
| 62 |
+
KandinskyImg2ImgCombinedPipeline,
|
| 63 |
+
KandinskyImg2ImgPipeline,
|
| 64 |
+
KandinskyInpaintCombinedPipeline,
|
| 65 |
+
KandinskyInpaintPipeline,
|
| 66 |
+
KandinskyPipeline,
|
| 67 |
+
)
|
| 68 |
+
from .kandinsky2_2 import (
|
| 69 |
+
KandinskyV22CombinedPipeline,
|
| 70 |
+
KandinskyV22Img2ImgCombinedPipeline,
|
| 71 |
+
KandinskyV22Img2ImgPipeline,
|
| 72 |
+
KandinskyV22InpaintCombinedPipeline,
|
| 73 |
+
KandinskyV22InpaintPipeline,
|
| 74 |
+
KandinskyV22Pipeline,
|
| 75 |
+
)
|
| 76 |
+
from .kandinsky3 import Kandinsky3Img2ImgPipeline, Kandinsky3Pipeline
|
| 77 |
+
from .latent_consistency_models import LatentConsistencyModelImg2ImgPipeline, LatentConsistencyModelPipeline
|
| 78 |
+
from .lumina import LuminaPipeline
|
| 79 |
+
from .lumina2 import Lumina2Pipeline
|
| 80 |
+
from .nucleusmoe_image import NucleusMoEImagePipeline
|
| 81 |
+
from .ovis_image import OvisImagePipeline
|
| 82 |
+
from .pag import (
|
| 83 |
+
HunyuanDiTPAGPipeline,
|
| 84 |
+
PixArtSigmaPAGPipeline,
|
| 85 |
+
SanaPAGPipeline,
|
| 86 |
+
StableDiffusion3PAGImg2ImgPipeline,
|
| 87 |
+
StableDiffusion3PAGPipeline,
|
| 88 |
+
StableDiffusionControlNetPAGInpaintPipeline,
|
| 89 |
+
StableDiffusionControlNetPAGPipeline,
|
| 90 |
+
StableDiffusionPAGImg2ImgPipeline,
|
| 91 |
+
StableDiffusionPAGInpaintPipeline,
|
| 92 |
+
StableDiffusionPAGPipeline,
|
| 93 |
+
StableDiffusionXLControlNetPAGImg2ImgPipeline,
|
| 94 |
+
StableDiffusionXLControlNetPAGPipeline,
|
| 95 |
+
StableDiffusionXLPAGImg2ImgPipeline,
|
| 96 |
+
StableDiffusionXLPAGInpaintPipeline,
|
| 97 |
+
StableDiffusionXLPAGPipeline,
|
| 98 |
+
)
|
| 99 |
+
from .pixart_alpha import PixArtAlphaPipeline, PixArtSigmaPipeline
|
| 100 |
+
from .prx import PRXPipeline
|
| 101 |
+
from .qwenimage import (
|
| 102 |
+
QwenImageControlNetPipeline,
|
| 103 |
+
QwenImageEditInpaintPipeline,
|
| 104 |
+
QwenImageEditPipeline,
|
| 105 |
+
QwenImageEditPlusPipeline,
|
| 106 |
+
QwenImageImg2ImgPipeline,
|
| 107 |
+
QwenImageInpaintPipeline,
|
| 108 |
+
QwenImageLayeredPipeline,
|
| 109 |
+
QwenImagePipeline,
|
| 110 |
+
)
|
| 111 |
+
from .sana import SanaPipeline
|
| 112 |
+
from .stable_cascade import StableCascadeCombinedPipeline, StableCascadeDecoderPipeline
|
| 113 |
+
from .stable_diffusion import (
|
| 114 |
+
StableDiffusionImg2ImgPipeline,
|
| 115 |
+
StableDiffusionInpaintPipeline,
|
| 116 |
+
StableDiffusionPipeline,
|
| 117 |
+
)
|
| 118 |
+
from .stable_diffusion_3 import (
|
| 119 |
+
StableDiffusion3Img2ImgPipeline,
|
| 120 |
+
StableDiffusion3InpaintPipeline,
|
| 121 |
+
StableDiffusion3Pipeline,
|
| 122 |
+
)
|
| 123 |
+
from .stable_diffusion_xl import (
|
| 124 |
+
StableDiffusionXLImg2ImgPipeline,
|
| 125 |
+
StableDiffusionXLInpaintPipeline,
|
| 126 |
+
StableDiffusionXLPipeline,
|
| 127 |
+
)
|
| 128 |
+
from .wan import WanImageToVideoPipeline, WanPipeline, WanVideoToVideoPipeline
|
| 129 |
+
from .z_image import (
|
| 130 |
+
ZImageControlNetInpaintPipeline,
|
| 131 |
+
ZImageControlNetPipeline,
|
| 132 |
+
ZImageImg2ImgPipeline,
|
| 133 |
+
ZImageInpaintPipeline,
|
| 134 |
+
ZImageOmniPipeline,
|
| 135 |
+
ZImagePipeline,
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING = OrderedDict(
|
| 140 |
+
[
|
| 141 |
+
("stable-diffusion", StableDiffusionPipeline),
|
| 142 |
+
("stable-diffusion-xl", StableDiffusionXLPipeline),
|
| 143 |
+
("stable-diffusion-3", StableDiffusion3Pipeline),
|
| 144 |
+
("stable-diffusion-3-pag", StableDiffusion3PAGPipeline),
|
| 145 |
+
("if", IFPipeline),
|
| 146 |
+
("hunyuan", HunyuanDiTPipeline),
|
| 147 |
+
("hunyuan-pag", HunyuanDiTPAGPipeline),
|
| 148 |
+
("kandinsky", KandinskyCombinedPipeline),
|
| 149 |
+
("kandinsky22", KandinskyV22CombinedPipeline),
|
| 150 |
+
("kandinsky3", Kandinsky3Pipeline),
|
| 151 |
+
("stable-diffusion-controlnet", StableDiffusionControlNetPipeline),
|
| 152 |
+
("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetPipeline),
|
| 153 |
+
("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionPipeline),
|
| 154 |
+
("stable-diffusion-3-controlnet", StableDiffusion3ControlNetPipeline),
|
| 155 |
+
("wuerstchen", WuerstchenCombinedPipeline),
|
| 156 |
+
("cascade", StableCascadeCombinedPipeline),
|
| 157 |
+
("lcm", LatentConsistencyModelPipeline),
|
| 158 |
+
("pixart-alpha", PixArtAlphaPipeline),
|
| 159 |
+
("pixart-sigma", PixArtSigmaPipeline),
|
| 160 |
+
("sana", SanaPipeline),
|
| 161 |
+
("sana-pag", SanaPAGPipeline),
|
| 162 |
+
("stable-diffusion-pag", StableDiffusionPAGPipeline),
|
| 163 |
+
("stable-diffusion-controlnet-pag", StableDiffusionControlNetPAGPipeline),
|
| 164 |
+
("stable-diffusion-xl-pag", StableDiffusionXLPAGPipeline),
|
| 165 |
+
("stable-diffusion-xl-controlnet-pag", StableDiffusionXLControlNetPAGPipeline),
|
| 166 |
+
("pixart-sigma-pag", PixArtSigmaPAGPipeline),
|
| 167 |
+
("auraflow", AuraFlowPipeline),
|
| 168 |
+
("flux", FluxPipeline),
|
| 169 |
+
("flux-control", FluxControlPipeline),
|
| 170 |
+
("flux-controlnet", FluxControlNetPipeline),
|
| 171 |
+
("flux-kontext", FluxKontextPipeline),
|
| 172 |
+
("flux2-klein", Flux2KleinPipeline),
|
| 173 |
+
("flux2", Flux2Pipeline),
|
| 174 |
+
("lumina", LuminaPipeline),
|
| 175 |
+
("lumina2", Lumina2Pipeline),
|
| 176 |
+
("chroma", ChromaPipeline),
|
| 177 |
+
("cogview3", CogView3PlusPipeline),
|
| 178 |
+
("cogview4", CogView4Pipeline),
|
| 179 |
+
("glm_image", GlmImagePipeline),
|
| 180 |
+
("helios", HeliosPipeline),
|
| 181 |
+
("helios-pyramid", HeliosPyramidPipeline),
|
| 182 |
+
("cogview4-control", CogView4ControlPipeline),
|
| 183 |
+
("nucleusmoe-image", NucleusMoEImagePipeline),
|
| 184 |
+
("qwenimage", QwenImagePipeline),
|
| 185 |
+
("qwenimage-controlnet", QwenImageControlNetPipeline),
|
| 186 |
+
("z-image", ZImagePipeline),
|
| 187 |
+
("z-image-controlnet", ZImageControlNetPipeline),
|
| 188 |
+
("z-image-controlnet-inpaint", ZImageControlNetInpaintPipeline),
|
| 189 |
+
("z-image-omni", ZImageOmniPipeline),
|
| 190 |
+
("ovis", OvisImagePipeline),
|
| 191 |
+
("prx", PRXPipeline),
|
| 192 |
+
]
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING = OrderedDict(
|
| 196 |
+
[
|
| 197 |
+
("stable-diffusion", StableDiffusionImg2ImgPipeline),
|
| 198 |
+
("stable-diffusion-xl", StableDiffusionXLImg2ImgPipeline),
|
| 199 |
+
("stable-diffusion-3", StableDiffusion3Img2ImgPipeline),
|
| 200 |
+
("stable-diffusion-3-pag", StableDiffusion3PAGImg2ImgPipeline),
|
| 201 |
+
("if", IFImg2ImgPipeline),
|
| 202 |
+
("kandinsky", KandinskyImg2ImgCombinedPipeline),
|
| 203 |
+
("kandinsky22", KandinskyV22Img2ImgCombinedPipeline),
|
| 204 |
+
("kandinsky3", Kandinsky3Img2ImgPipeline),
|
| 205 |
+
("stable-diffusion-controlnet", StableDiffusionControlNetImg2ImgPipeline),
|
| 206 |
+
("stable-diffusion-pag", StableDiffusionPAGImg2ImgPipeline),
|
| 207 |
+
("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetImg2ImgPipeline),
|
| 208 |
+
("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionImg2ImgPipeline),
|
| 209 |
+
("stable-diffusion-xl-pag", StableDiffusionXLPAGImg2ImgPipeline),
|
| 210 |
+
("stable-diffusion-xl-controlnet-pag", StableDiffusionXLControlNetPAGImg2ImgPipeline),
|
| 211 |
+
("lcm", LatentConsistencyModelImg2ImgPipeline),
|
| 212 |
+
("flux", FluxImg2ImgPipeline),
|
| 213 |
+
("flux-controlnet", FluxControlNetImg2ImgPipeline),
|
| 214 |
+
("flux-control", FluxControlImg2ImgPipeline),
|
| 215 |
+
("flux-kontext", FluxKontextPipeline),
|
| 216 |
+
("flux2-klein", Flux2KleinPipeline),
|
| 217 |
+
("flux2", Flux2Pipeline),
|
| 218 |
+
("qwenimage", QwenImageImg2ImgPipeline),
|
| 219 |
+
("qwenimage-edit", QwenImageEditPipeline),
|
| 220 |
+
("qwenimage-edit-plus", QwenImageEditPlusPipeline),
|
| 221 |
+
("qwenimage-layered", QwenImageLayeredPipeline),
|
| 222 |
+
("z-image", ZImageImg2ImgPipeline),
|
| 223 |
+
]
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
AUTO_INPAINT_PIPELINES_MAPPING = OrderedDict(
|
| 227 |
+
[
|
| 228 |
+
("stable-diffusion", StableDiffusionInpaintPipeline),
|
| 229 |
+
("stable-diffusion-xl", StableDiffusionXLInpaintPipeline),
|
| 230 |
+
("stable-diffusion-3", StableDiffusion3InpaintPipeline),
|
| 231 |
+
("if", IFInpaintingPipeline),
|
| 232 |
+
("kandinsky", KandinskyInpaintCombinedPipeline),
|
| 233 |
+
("kandinsky22", KandinskyV22InpaintCombinedPipeline),
|
| 234 |
+
("stable-diffusion-controlnet", StableDiffusionControlNetInpaintPipeline),
|
| 235 |
+
("stable-diffusion-controlnet-pag", StableDiffusionControlNetPAGInpaintPipeline),
|
| 236 |
+
("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetInpaintPipeline),
|
| 237 |
+
("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionInpaintPipeline),
|
| 238 |
+
("stable-diffusion-3-controlnet", StableDiffusion3ControlNetInpaintingPipeline),
|
| 239 |
+
("stable-diffusion-xl-pag", StableDiffusionXLPAGInpaintPipeline),
|
| 240 |
+
("flux", FluxInpaintPipeline),
|
| 241 |
+
("flux-controlnet", FluxControlNetInpaintPipeline),
|
| 242 |
+
("flux-control", FluxControlInpaintPipeline),
|
| 243 |
+
("stable-diffusion-pag", StableDiffusionPAGInpaintPipeline),
|
| 244 |
+
("qwenimage", QwenImageInpaintPipeline),
|
| 245 |
+
("qwenimage-edit", QwenImageEditInpaintPipeline),
|
| 246 |
+
("z-image", ZImageInpaintPipeline),
|
| 247 |
+
]
|
| 248 |
+
)
|
| 249 |
+
|
| 250 |
+
AUTO_TEXT2VIDEO_PIPELINES_MAPPING = OrderedDict(
|
| 251 |
+
[
|
| 252 |
+
("wan", WanPipeline),
|
| 253 |
+
]
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
AUTO_IMAGE2VIDEO_PIPELINES_MAPPING = OrderedDict(
|
| 257 |
+
[
|
| 258 |
+
("wan-i2v", WanImageToVideoPipeline),
|
| 259 |
+
]
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
AUTO_VIDEO2VIDEO_PIPELINES_MAPPING = OrderedDict(
|
| 263 |
+
[
|
| 264 |
+
("wan", WanVideoToVideoPipeline),
|
| 265 |
+
]
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
+
_AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING = OrderedDict(
|
| 269 |
+
[
|
| 270 |
+
("kandinsky", KandinskyPipeline),
|
| 271 |
+
("kandinsky22", KandinskyV22Pipeline),
|
| 272 |
+
("wuerstchen", WuerstchenDecoderPipeline),
|
| 273 |
+
("cascade", StableCascadeDecoderPipeline),
|
| 274 |
+
]
|
| 275 |
+
)
|
| 276 |
+
_AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING = OrderedDict(
|
| 277 |
+
[
|
| 278 |
+
("kandinsky", KandinskyImg2ImgPipeline),
|
| 279 |
+
("kandinsky22", KandinskyV22Img2ImgPipeline),
|
| 280 |
+
]
|
| 281 |
+
)
|
| 282 |
+
_AUTO_INPAINT_DECODER_PIPELINES_MAPPING = OrderedDict(
|
| 283 |
+
[
|
| 284 |
+
("kandinsky", KandinskyInpaintPipeline),
|
| 285 |
+
("kandinsky22", KandinskyV22InpaintPipeline),
|
| 286 |
+
]
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
if is_sentencepiece_available():
|
| 290 |
+
from .kolors import KolorsImg2ImgPipeline, KolorsPipeline
|
| 291 |
+
from .pag import KolorsPAGPipeline
|
| 292 |
+
|
| 293 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING["kolors"] = KolorsPipeline
|
| 294 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING["kolors-pag"] = KolorsPAGPipeline
|
| 295 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["kolors"] = KolorsImg2ImgPipeline
|
| 296 |
+
|
| 297 |
+
SUPPORTED_TASKS_MAPPINGS = [
|
| 298 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
|
| 299 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
|
| 300 |
+
AUTO_INPAINT_PIPELINES_MAPPING,
|
| 301 |
+
AUTO_TEXT2VIDEO_PIPELINES_MAPPING,
|
| 302 |
+
AUTO_IMAGE2VIDEO_PIPELINES_MAPPING,
|
| 303 |
+
AUTO_VIDEO2VIDEO_PIPELINES_MAPPING,
|
| 304 |
+
_AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING,
|
| 305 |
+
_AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING,
|
| 306 |
+
_AUTO_INPAINT_DECODER_PIPELINES_MAPPING,
|
| 307 |
+
]
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def _get_connected_pipeline(pipeline_cls):
|
| 311 |
+
# for now connected pipelines can only be loaded from decoder pipelines, such as kandinsky-community/kandinsky-2-2-decoder
|
| 312 |
+
if pipeline_cls in _AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING.values():
|
| 313 |
+
return _get_task_class(
|
| 314 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False
|
| 315 |
+
)
|
| 316 |
+
if pipeline_cls in _AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING.values():
|
| 317 |
+
return _get_task_class(
|
| 318 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False
|
| 319 |
+
)
|
| 320 |
+
if pipeline_cls in _AUTO_INPAINT_DECODER_PIPELINES_MAPPING.values():
|
| 321 |
+
return _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False)
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def _get_model(pipeline_class_name):
|
| 325 |
+
for task_mapping in SUPPORTED_TASKS_MAPPINGS:
|
| 326 |
+
for model_name, pipeline in task_mapping.items():
|
| 327 |
+
if pipeline.__name__ == pipeline_class_name:
|
| 328 |
+
return model_name
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def _get_task_class(mapping, pipeline_class_name, throw_error_if_not_exist: bool = True):
|
| 332 |
+
model_name = _get_model(pipeline_class_name)
|
| 333 |
+
|
| 334 |
+
if model_name is not None:
|
| 335 |
+
task_class = mapping.get(model_name, None)
|
| 336 |
+
if task_class is not None:
|
| 337 |
+
return task_class
|
| 338 |
+
|
| 339 |
+
if throw_error_if_not_exist:
|
| 340 |
+
raise ValueError(f"AutoPipeline can't find a pipeline linked to {pipeline_class_name} for {model_name}")
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
class AutoPipelineForText2Image(ConfigMixin):
|
| 344 |
+
r"""
|
| 345 |
+
|
| 346 |
+
[`AutoPipelineForText2Image`] is a generic pipeline class that instantiates a text-to-image pipeline class. The
|
| 347 |
+
specific underlying pipeline class is automatically selected from either the
|
| 348 |
+
[`~AutoPipelineForText2Image.from_pretrained`] or [`~AutoPipelineForText2Image.from_pipe`] methods.
|
| 349 |
+
|
| 350 |
+
This class cannot be instantiated using `__init__()` (throws an error).
|
| 351 |
+
|
| 352 |
+
Class attributes:
|
| 353 |
+
|
| 354 |
+
- **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
|
| 355 |
+
diffusion pipeline's components.
|
| 356 |
+
|
| 357 |
+
"""
|
| 358 |
+
|
| 359 |
+
config_name = "model_index.json"
|
| 360 |
+
|
| 361 |
+
def __init__(self, *args, **kwargs):
|
| 362 |
+
raise EnvironmentError(
|
| 363 |
+
f"{self.__class__.__name__} is designed to be instantiated "
|
| 364 |
+
f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
|
| 365 |
+
f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
|
| 366 |
+
)
|
| 367 |
+
|
| 368 |
+
@classmethod
|
| 369 |
+
@validate_hf_hub_args
|
| 370 |
+
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
| 371 |
+
r"""
|
| 372 |
+
Instantiates a text-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
| 373 |
+
|
| 374 |
+
The from_pretrained() method takes care of returning the correct pipeline class instance by:
|
| 375 |
+
1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
|
| 376 |
+
config object
|
| 377 |
+
2. Find the text-to-image pipeline linked to the pipeline class using pattern matching on pipeline class
|
| 378 |
+
name.
|
| 379 |
+
|
| 380 |
+
If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetPipeline`] object.
|
| 381 |
+
|
| 382 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 383 |
+
|
| 384 |
+
If you get the error message below, you need to finetune the weights for your downstream task:
|
| 385 |
+
|
| 386 |
+
```
|
| 387 |
+
Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
|
| 388 |
+
- conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
|
| 389 |
+
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
| 390 |
+
```
|
| 391 |
+
|
| 392 |
+
Parameters:
|
| 393 |
+
pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
|
| 394 |
+
Can be either:
|
| 395 |
+
|
| 396 |
+
- A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
|
| 397 |
+
hosted on the Hub.
|
| 398 |
+
- A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
|
| 399 |
+
saved using
|
| 400 |
+
[`~DiffusionPipeline.save_pretrained`].
|
| 401 |
+
torch_dtype (`torch.dtype`, *optional*):
|
| 402 |
+
Override the default `torch.dtype` and load the model with another dtype.
|
| 403 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 404 |
+
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
| 405 |
+
cached versions if they exist.
|
| 406 |
+
cache_dir (`str | os.PathLike`, *optional*):
|
| 407 |
+
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
| 408 |
+
is not used.
|
| 409 |
+
|
| 410 |
+
proxies (`dict[str, str]`, *optional*):
|
| 411 |
+
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
| 412 |
+
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
| 413 |
+
output_loading_info(`bool`, *optional*, defaults to `False`):
|
| 414 |
+
Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
|
| 415 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 416 |
+
Whether to only load local model weights and configuration files or not. If set to `True`, the model
|
| 417 |
+
won't be downloaded from the Hub.
|
| 418 |
+
token (`str` or *bool*, *optional*):
|
| 419 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
| 420 |
+
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
| 421 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 422 |
+
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
| 423 |
+
allowed by Git.
|
| 424 |
+
custom_revision (`str`, *optional*, defaults to `"main"`):
|
| 425 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
|
| 426 |
+
`revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
|
| 427 |
+
custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
|
| 428 |
+
mirror (`str`, *optional*):
|
| 429 |
+
Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
|
| 430 |
+
guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
|
| 431 |
+
information.
|
| 432 |
+
device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
|
| 433 |
+
A map that specifies where each submodule should go. It doesn’t need to be defined for each
|
| 434 |
+
parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
|
| 435 |
+
same device.
|
| 436 |
+
|
| 437 |
+
Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
|
| 438 |
+
more information about each option see [designing a device
|
| 439 |
+
map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
|
| 440 |
+
max_memory (`Dict`, *optional*):
|
| 441 |
+
A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
|
| 442 |
+
each GPU and the available CPU RAM if unset.
|
| 443 |
+
offload_folder (`str` or `os.PathLike`, *optional*):
|
| 444 |
+
The path to offload weights if device_map contains the value `"disk"`.
|
| 445 |
+
offload_state_dict (`bool`, *optional*):
|
| 446 |
+
If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
|
| 447 |
+
the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
|
| 448 |
+
when there is some disk offload.
|
| 449 |
+
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
| 450 |
+
Speed up model loading only loading the pretrained weights and not initializing the weights. This also
|
| 451 |
+
tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
|
| 452 |
+
Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
|
| 453 |
+
argument to `True` will raise an error.
|
| 454 |
+
use_safetensors (`bool`, *optional*, defaults to `None`):
|
| 455 |
+
If set to `None`, the safetensors weights are downloaded if they're available **and** if the
|
| 456 |
+
safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
|
| 457 |
+
weights. If set to `False`, safetensors weights are not loaded.
|
| 458 |
+
kwargs (remaining dictionary of keyword arguments, *optional*):
|
| 459 |
+
Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
|
| 460 |
+
class). The overwritten components are passed directly to the pipelines `__init__` method. See example
|
| 461 |
+
below for more information.
|
| 462 |
+
variant (`str`, *optional*):
|
| 463 |
+
Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
|
| 464 |
+
loading `from_flax`.
|
| 465 |
+
|
| 466 |
+
> [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
|
| 467 |
+
with `hf > auth login`.
|
| 468 |
+
|
| 469 |
+
Examples:
|
| 470 |
+
|
| 471 |
+
```py
|
| 472 |
+
>>> from diffusers import AutoPipelineForText2Image
|
| 473 |
+
|
| 474 |
+
>>> pipeline = AutoPipelineForText2Image.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
|
| 475 |
+
>>> image = pipeline(prompt).images[0]
|
| 476 |
+
```
|
| 477 |
+
"""
|
| 478 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 479 |
+
force_download = kwargs.pop("force_download", False)
|
| 480 |
+
proxies = kwargs.pop("proxies", None)
|
| 481 |
+
token = kwargs.pop("token", None)
|
| 482 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 483 |
+
revision = kwargs.pop("revision", None)
|
| 484 |
+
|
| 485 |
+
load_config_kwargs = {
|
| 486 |
+
"cache_dir": cache_dir,
|
| 487 |
+
"force_download": force_download,
|
| 488 |
+
"proxies": proxies,
|
| 489 |
+
"token": token,
|
| 490 |
+
"local_files_only": local_files_only,
|
| 491 |
+
"revision": revision,
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
+
config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
|
| 495 |
+
orig_class_name = config["_class_name"]
|
| 496 |
+
if "ControlPipeline" in orig_class_name:
|
| 497 |
+
to_replace = "ControlPipeline"
|
| 498 |
+
else:
|
| 499 |
+
to_replace = "Pipeline"
|
| 500 |
+
|
| 501 |
+
if "controlnet" in kwargs:
|
| 502 |
+
if isinstance(kwargs["controlnet"], ControlNetUnionModel):
|
| 503 |
+
orig_class_name = config["_class_name"].replace(to_replace, "ControlNetUnionPipeline")
|
| 504 |
+
else:
|
| 505 |
+
orig_class_name = config["_class_name"].replace(to_replace, "ControlNetPipeline")
|
| 506 |
+
if "enable_pag" in kwargs:
|
| 507 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 508 |
+
if enable_pag:
|
| 509 |
+
orig_class_name = orig_class_name.replace(to_replace, "PAGPipeline")
|
| 510 |
+
|
| 511 |
+
text_2_image_cls = _get_task_class(AUTO_TEXT2IMAGE_PIPELINES_MAPPING, orig_class_name)
|
| 512 |
+
|
| 513 |
+
kwargs = {**load_config_kwargs, **kwargs}
|
| 514 |
+
return text_2_image_cls.from_pretrained(pretrained_model_or_path, **kwargs)
|
| 515 |
+
|
| 516 |
+
@classmethod
|
| 517 |
+
def from_pipe(cls, pipeline, **kwargs):
|
| 518 |
+
r"""
|
| 519 |
+
Instantiates a text-to-image Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
|
| 520 |
+
|
| 521 |
+
The from_pipe() method takes care of returning the correct pipeline class instance by finding the text-to-image
|
| 522 |
+
pipeline linked to the pipeline class using pattern matching on pipeline class name.
|
| 523 |
+
|
| 524 |
+
All the modules the pipeline contains will be used to initialize the new pipeline without reallocating
|
| 525 |
+
additional memory.
|
| 526 |
+
|
| 527 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 528 |
+
|
| 529 |
+
Parameters:
|
| 530 |
+
pipeline (`DiffusionPipeline`):
|
| 531 |
+
an instantiated `DiffusionPipeline` object
|
| 532 |
+
|
| 533 |
+
```py
|
| 534 |
+
>>> from diffusers import AutoPipelineForText2Image, AutoPipelineForImage2Image
|
| 535 |
+
|
| 536 |
+
>>> pipe_i2i = AutoPipelineForImage2Image.from_pretrained(
|
| 537 |
+
... "stable-diffusion-v1-5/stable-diffusion-v1-5", requires_safety_checker=False
|
| 538 |
+
... )
|
| 539 |
+
|
| 540 |
+
>>> pipe_t2i = AutoPipelineForText2Image.from_pipe(pipe_i2i)
|
| 541 |
+
>>> image = pipe_t2i(prompt).images[0]
|
| 542 |
+
```
|
| 543 |
+
"""
|
| 544 |
+
|
| 545 |
+
original_config = dict(pipeline.config)
|
| 546 |
+
original_cls_name = pipeline.__class__.__name__
|
| 547 |
+
|
| 548 |
+
# derive the pipeline class to instantiate
|
| 549 |
+
text_2_image_cls = _get_task_class(AUTO_TEXT2IMAGE_PIPELINES_MAPPING, original_cls_name)
|
| 550 |
+
|
| 551 |
+
if "controlnet" in kwargs:
|
| 552 |
+
if kwargs["controlnet"] is not None:
|
| 553 |
+
to_replace = "PAGPipeline" if "PAG" in text_2_image_cls.__name__ else "Pipeline"
|
| 554 |
+
text_2_image_cls = _get_task_class(
|
| 555 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
|
| 556 |
+
text_2_image_cls.__name__.replace("ControlNet", "").replace(to_replace, "ControlNet" + to_replace),
|
| 557 |
+
)
|
| 558 |
+
else:
|
| 559 |
+
text_2_image_cls = _get_task_class(
|
| 560 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
|
| 561 |
+
text_2_image_cls.__name__.replace("ControlNet", ""),
|
| 562 |
+
)
|
| 563 |
+
|
| 564 |
+
if "enable_pag" in kwargs:
|
| 565 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 566 |
+
if enable_pag:
|
| 567 |
+
text_2_image_cls = _get_task_class(
|
| 568 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
|
| 569 |
+
text_2_image_cls.__name__.replace("PAG", "").replace("Pipeline", "PAGPipeline"),
|
| 570 |
+
)
|
| 571 |
+
else:
|
| 572 |
+
text_2_image_cls = _get_task_class(
|
| 573 |
+
AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
|
| 574 |
+
text_2_image_cls.__name__.replace("PAG", ""),
|
| 575 |
+
)
|
| 576 |
+
|
| 577 |
+
# define expected module and optional kwargs given the pipeline signature
|
| 578 |
+
expected_modules, optional_kwargs = text_2_image_cls._get_signature_keys(text_2_image_cls)
|
| 579 |
+
|
| 580 |
+
pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
|
| 581 |
+
|
| 582 |
+
# allow users pass modules in `kwargs` to override the original pipeline's components
|
| 583 |
+
passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
|
| 584 |
+
original_class_obj = {
|
| 585 |
+
k: pipeline.components[k]
|
| 586 |
+
for k, v in pipeline.components.items()
|
| 587 |
+
if k in expected_modules and k not in passed_class_obj
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
# allow users pass optional kwargs to override the original pipelines config attribute
|
| 591 |
+
passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
|
| 592 |
+
original_pipe_kwargs = {
|
| 593 |
+
k: original_config[k]
|
| 594 |
+
for k, v in original_config.items()
|
| 595 |
+
if k in optional_kwargs and k not in passed_pipe_kwargs
|
| 596 |
+
}
|
| 597 |
+
|
| 598 |
+
# config that were not expected by original pipeline is stored as private attribute
|
| 599 |
+
# we will pass them as optional arguments if they can be accepted by the pipeline
|
| 600 |
+
additional_pipe_kwargs = [
|
| 601 |
+
k[1:]
|
| 602 |
+
for k in original_config.keys()
|
| 603 |
+
if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
|
| 604 |
+
]
|
| 605 |
+
for k in additional_pipe_kwargs:
|
| 606 |
+
original_pipe_kwargs[k] = original_config.pop(f"_{k}")
|
| 607 |
+
|
| 608 |
+
text_2_image_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
|
| 609 |
+
|
| 610 |
+
# store unused config as private attribute
|
| 611 |
+
unused_original_config = {
|
| 612 |
+
f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
|
| 613 |
+
for k, v in original_config.items()
|
| 614 |
+
if k not in text_2_image_kwargs
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
missing_modules = (
|
| 618 |
+
set(expected_modules) - set(text_2_image_cls._optional_components) - set(text_2_image_kwargs.keys())
|
| 619 |
+
)
|
| 620 |
+
|
| 621 |
+
if len(missing_modules) > 0:
|
| 622 |
+
raise ValueError(
|
| 623 |
+
f"Pipeline {text_2_image_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
model = text_2_image_cls(**text_2_image_kwargs)
|
| 627 |
+
model.register_to_config(_name_or_path=pretrained_model_name_or_path)
|
| 628 |
+
model.register_to_config(**unused_original_config)
|
| 629 |
+
|
| 630 |
+
return model
|
| 631 |
+
|
| 632 |
+
|
| 633 |
+
class AutoPipelineForImage2Image(ConfigMixin):
|
| 634 |
+
r"""
|
| 635 |
+
|
| 636 |
+
[`AutoPipelineForImage2Image`] is a generic pipeline class that instantiates an image-to-image pipeline class. The
|
| 637 |
+
specific underlying pipeline class is automatically selected from either the
|
| 638 |
+
[`~AutoPipelineForImage2Image.from_pretrained`] or [`~AutoPipelineForImage2Image.from_pipe`] methods.
|
| 639 |
+
|
| 640 |
+
This class cannot be instantiated using `__init__()` (throws an error).
|
| 641 |
+
|
| 642 |
+
Class attributes:
|
| 643 |
+
|
| 644 |
+
- **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
|
| 645 |
+
diffusion pipeline's components.
|
| 646 |
+
|
| 647 |
+
"""
|
| 648 |
+
|
| 649 |
+
config_name = "model_index.json"
|
| 650 |
+
|
| 651 |
+
def __init__(self, *args, **kwargs):
|
| 652 |
+
raise EnvironmentError(
|
| 653 |
+
f"{self.__class__.__name__} is designed to be instantiated "
|
| 654 |
+
f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
|
| 655 |
+
f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
|
| 656 |
+
)
|
| 657 |
+
|
| 658 |
+
@classmethod
|
| 659 |
+
@validate_hf_hub_args
|
| 660 |
+
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
| 661 |
+
r"""
|
| 662 |
+
Instantiates a image-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
|
| 663 |
+
|
| 664 |
+
The from_pretrained() method takes care of returning the correct pipeline class instance by:
|
| 665 |
+
1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
|
| 666 |
+
config object
|
| 667 |
+
2. Find the image-to-image pipeline linked to the pipeline class using pattern matching on pipeline class
|
| 668 |
+
name.
|
| 669 |
+
|
| 670 |
+
If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetImg2ImgPipeline`]
|
| 671 |
+
object.
|
| 672 |
+
|
| 673 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 674 |
+
|
| 675 |
+
If you get the error message below, you need to finetune the weights for your downstream task:
|
| 676 |
+
|
| 677 |
+
```
|
| 678 |
+
Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
|
| 679 |
+
- conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
|
| 680 |
+
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
| 681 |
+
```
|
| 682 |
+
|
| 683 |
+
Parameters:
|
| 684 |
+
pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
|
| 685 |
+
Can be either:
|
| 686 |
+
|
| 687 |
+
- A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
|
| 688 |
+
hosted on the Hub.
|
| 689 |
+
- A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
|
| 690 |
+
saved using
|
| 691 |
+
[`~DiffusionPipeline.save_pretrained`].
|
| 692 |
+
torch_dtype (`str` or `torch.dtype`, *optional*):
|
| 693 |
+
Override the default `torch.dtype` and load the model with another dtype.
|
| 694 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 695 |
+
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
| 696 |
+
cached versions if they exist.
|
| 697 |
+
cache_dir (`str | os.PathLike`, *optional*):
|
| 698 |
+
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
| 699 |
+
is not used.
|
| 700 |
+
|
| 701 |
+
proxies (`dict[str, str]`, *optional*):
|
| 702 |
+
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
| 703 |
+
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
| 704 |
+
output_loading_info(`bool`, *optional*, defaults to `False`):
|
| 705 |
+
Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
|
| 706 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 707 |
+
Whether to only load local model weights and configuration files or not. If set to `True`, the model
|
| 708 |
+
won't be downloaded from the Hub.
|
| 709 |
+
token (`str` or *bool*, *optional*):
|
| 710 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
| 711 |
+
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
| 712 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 713 |
+
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
| 714 |
+
allowed by Git.
|
| 715 |
+
custom_revision (`str`, *optional*, defaults to `"main"`):
|
| 716 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
|
| 717 |
+
`revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
|
| 718 |
+
custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
|
| 719 |
+
mirror (`str`, *optional*):
|
| 720 |
+
Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
|
| 721 |
+
guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
|
| 722 |
+
information.
|
| 723 |
+
device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
|
| 724 |
+
A map that specifies where each submodule should go. It doesn’t need to be defined for each
|
| 725 |
+
parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
|
| 726 |
+
same device.
|
| 727 |
+
|
| 728 |
+
Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
|
| 729 |
+
more information about each option see [designing a device
|
| 730 |
+
map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
|
| 731 |
+
max_memory (`Dict`, *optional*):
|
| 732 |
+
A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
|
| 733 |
+
each GPU and the available CPU RAM if unset.
|
| 734 |
+
offload_folder (`str` or `os.PathLike`, *optional*):
|
| 735 |
+
The path to offload weights if device_map contains the value `"disk"`.
|
| 736 |
+
offload_state_dict (`bool`, *optional*):
|
| 737 |
+
If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
|
| 738 |
+
the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
|
| 739 |
+
when there is some disk offload.
|
| 740 |
+
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
| 741 |
+
Speed up model loading only loading the pretrained weights and not initializing the weights. This also
|
| 742 |
+
tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
|
| 743 |
+
Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
|
| 744 |
+
argument to `True` will raise an error.
|
| 745 |
+
use_safetensors (`bool`, *optional*, defaults to `None`):
|
| 746 |
+
If set to `None`, the safetensors weights are downloaded if they're available **and** if the
|
| 747 |
+
safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
|
| 748 |
+
weights. If set to `False`, safetensors weights are not loaded.
|
| 749 |
+
kwargs (remaining dictionary of keyword arguments, *optional*):
|
| 750 |
+
Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
|
| 751 |
+
class). The overwritten components are passed directly to the pipelines `__init__` method. See example
|
| 752 |
+
below for more information.
|
| 753 |
+
variant (`str`, *optional*):
|
| 754 |
+
Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
|
| 755 |
+
loading `from_flax`.
|
| 756 |
+
|
| 757 |
+
> [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
|
| 758 |
+
with `hf > auth login`.
|
| 759 |
+
|
| 760 |
+
Examples:
|
| 761 |
+
|
| 762 |
+
```py
|
| 763 |
+
>>> from diffusers import AutoPipelineForImage2Image
|
| 764 |
+
|
| 765 |
+
>>> pipeline = AutoPipelineForImage2Image.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
|
| 766 |
+
>>> image = pipeline(prompt, image).images[0]
|
| 767 |
+
```
|
| 768 |
+
"""
|
| 769 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 770 |
+
force_download = kwargs.pop("force_download", False)
|
| 771 |
+
proxies = kwargs.pop("proxies", None)
|
| 772 |
+
token = kwargs.pop("token", None)
|
| 773 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 774 |
+
revision = kwargs.pop("revision", None)
|
| 775 |
+
|
| 776 |
+
load_config_kwargs = {
|
| 777 |
+
"cache_dir": cache_dir,
|
| 778 |
+
"force_download": force_download,
|
| 779 |
+
"proxies": proxies,
|
| 780 |
+
"token": token,
|
| 781 |
+
"local_files_only": local_files_only,
|
| 782 |
+
"revision": revision,
|
| 783 |
+
}
|
| 784 |
+
|
| 785 |
+
config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
|
| 786 |
+
orig_class_name = config["_class_name"]
|
| 787 |
+
|
| 788 |
+
# the `orig_class_name` can be:
|
| 789 |
+
# `- *Pipeline` (for regular text-to-image checkpoint)
|
| 790 |
+
# - `*ControlPipeline` (for Flux tools specific checkpoint)
|
| 791 |
+
# `- *Img2ImgPipeline` (for refiner checkpoint)
|
| 792 |
+
if "Img2Img" in orig_class_name:
|
| 793 |
+
to_replace = "Img2ImgPipeline"
|
| 794 |
+
elif "ControlPipeline" in orig_class_name:
|
| 795 |
+
to_replace = "ControlPipeline"
|
| 796 |
+
else:
|
| 797 |
+
to_replace = "Pipeline"
|
| 798 |
+
|
| 799 |
+
if "controlnet" in kwargs:
|
| 800 |
+
if isinstance(kwargs["controlnet"], ControlNetUnionModel):
|
| 801 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlNetUnion" + to_replace)
|
| 802 |
+
else:
|
| 803 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlNet" + to_replace)
|
| 804 |
+
if "enable_pag" in kwargs:
|
| 805 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 806 |
+
if enable_pag:
|
| 807 |
+
orig_class_name = orig_class_name.replace(to_replace, "PAG" + to_replace)
|
| 808 |
+
|
| 809 |
+
if to_replace == "ControlPipeline":
|
| 810 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlImg2ImgPipeline")
|
| 811 |
+
|
| 812 |
+
image_2_image_cls = _get_task_class(AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, orig_class_name)
|
| 813 |
+
|
| 814 |
+
kwargs = {**load_config_kwargs, **kwargs}
|
| 815 |
+
return image_2_image_cls.from_pretrained(pretrained_model_or_path, **kwargs)
|
| 816 |
+
|
| 817 |
+
@classmethod
|
| 818 |
+
def from_pipe(cls, pipeline, **kwargs):
|
| 819 |
+
r"""
|
| 820 |
+
Instantiates a image-to-image Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
|
| 821 |
+
|
| 822 |
+
The from_pipe() method takes care of returning the correct pipeline class instance by finding the
|
| 823 |
+
image-to-image pipeline linked to the pipeline class using pattern matching on pipeline class name.
|
| 824 |
+
|
| 825 |
+
All the modules the pipeline contains will be used to initialize the new pipeline without reallocating
|
| 826 |
+
additional memory.
|
| 827 |
+
|
| 828 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 829 |
+
|
| 830 |
+
Parameters:
|
| 831 |
+
pipeline (`DiffusionPipeline`):
|
| 832 |
+
an instantiated `DiffusionPipeline` object
|
| 833 |
+
|
| 834 |
+
Examples:
|
| 835 |
+
|
| 836 |
+
```py
|
| 837 |
+
>>> from diffusers import AutoPipelineForText2Image, AutoPipelineForImage2Image
|
| 838 |
+
|
| 839 |
+
>>> pipe_t2i = AutoPipelineForText2Image.from_pretrained(
|
| 840 |
+
... "stable-diffusion-v1-5/stable-diffusion-v1-5", requires_safety_checker=False
|
| 841 |
+
... )
|
| 842 |
+
|
| 843 |
+
>>> pipe_i2i = AutoPipelineForImage2Image.from_pipe(pipe_t2i)
|
| 844 |
+
>>> image = pipe_i2i(prompt, image).images[0]
|
| 845 |
+
```
|
| 846 |
+
"""
|
| 847 |
+
|
| 848 |
+
original_config = dict(pipeline.config)
|
| 849 |
+
original_cls_name = pipeline.__class__.__name__
|
| 850 |
+
|
| 851 |
+
# derive the pipeline class to instantiate
|
| 852 |
+
image_2_image_cls = _get_task_class(AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, original_cls_name)
|
| 853 |
+
|
| 854 |
+
if "controlnet" in kwargs:
|
| 855 |
+
if kwargs["controlnet"] is not None:
|
| 856 |
+
to_replace = "Img2ImgPipeline"
|
| 857 |
+
if "PAG" in image_2_image_cls.__name__:
|
| 858 |
+
to_replace = "PAG" + to_replace
|
| 859 |
+
image_2_image_cls = _get_task_class(
|
| 860 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
|
| 861 |
+
image_2_image_cls.__name__.replace("ControlNet", "").replace(
|
| 862 |
+
to_replace, "ControlNet" + to_replace
|
| 863 |
+
),
|
| 864 |
+
)
|
| 865 |
+
else:
|
| 866 |
+
image_2_image_cls = _get_task_class(
|
| 867 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
|
| 868 |
+
image_2_image_cls.__name__.replace("ControlNet", ""),
|
| 869 |
+
)
|
| 870 |
+
|
| 871 |
+
if "enable_pag" in kwargs:
|
| 872 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 873 |
+
if enable_pag:
|
| 874 |
+
image_2_image_cls = _get_task_class(
|
| 875 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
|
| 876 |
+
image_2_image_cls.__name__.replace("PAG", "").replace("Img2ImgPipeline", "PAGImg2ImgPipeline"),
|
| 877 |
+
)
|
| 878 |
+
else:
|
| 879 |
+
image_2_image_cls = _get_task_class(
|
| 880 |
+
AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
|
| 881 |
+
image_2_image_cls.__name__.replace("PAG", ""),
|
| 882 |
+
)
|
| 883 |
+
|
| 884 |
+
# define expected module and optional kwargs given the pipeline signature
|
| 885 |
+
expected_modules, optional_kwargs = image_2_image_cls._get_signature_keys(image_2_image_cls)
|
| 886 |
+
|
| 887 |
+
pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
|
| 888 |
+
|
| 889 |
+
# allow users pass modules in `kwargs` to override the original pipeline's components
|
| 890 |
+
passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
|
| 891 |
+
original_class_obj = {
|
| 892 |
+
k: pipeline.components[k]
|
| 893 |
+
for k, v in pipeline.components.items()
|
| 894 |
+
if k in expected_modules and k not in passed_class_obj
|
| 895 |
+
}
|
| 896 |
+
|
| 897 |
+
# allow users pass optional kwargs to override the original pipelines config attribute
|
| 898 |
+
passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
|
| 899 |
+
original_pipe_kwargs = {
|
| 900 |
+
k: original_config[k]
|
| 901 |
+
for k, v in original_config.items()
|
| 902 |
+
if k in optional_kwargs and k not in passed_pipe_kwargs
|
| 903 |
+
}
|
| 904 |
+
|
| 905 |
+
# config attribute that were not expected by original pipeline is stored as its private attribute
|
| 906 |
+
# we will pass them as optional arguments if they can be accepted by the pipeline
|
| 907 |
+
additional_pipe_kwargs = [
|
| 908 |
+
k[1:]
|
| 909 |
+
for k in original_config.keys()
|
| 910 |
+
if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
|
| 911 |
+
]
|
| 912 |
+
for k in additional_pipe_kwargs:
|
| 913 |
+
original_pipe_kwargs[k] = original_config.pop(f"_{k}")
|
| 914 |
+
|
| 915 |
+
image_2_image_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
|
| 916 |
+
|
| 917 |
+
# store unused config as private attribute
|
| 918 |
+
unused_original_config = {
|
| 919 |
+
f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
|
| 920 |
+
for k, v in original_config.items()
|
| 921 |
+
if k not in image_2_image_kwargs
|
| 922 |
+
}
|
| 923 |
+
|
| 924 |
+
missing_modules = (
|
| 925 |
+
set(expected_modules) - set(image_2_image_cls._optional_components) - set(image_2_image_kwargs.keys())
|
| 926 |
+
)
|
| 927 |
+
|
| 928 |
+
if len(missing_modules) > 0:
|
| 929 |
+
raise ValueError(
|
| 930 |
+
f"Pipeline {image_2_image_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
|
| 931 |
+
)
|
| 932 |
+
|
| 933 |
+
model = image_2_image_cls(**image_2_image_kwargs)
|
| 934 |
+
model.register_to_config(_name_or_path=pretrained_model_name_or_path)
|
| 935 |
+
model.register_to_config(**unused_original_config)
|
| 936 |
+
|
| 937 |
+
return model
|
| 938 |
+
|
| 939 |
+
|
| 940 |
+
class AutoPipelineForInpainting(ConfigMixin):
|
| 941 |
+
r"""
|
| 942 |
+
|
| 943 |
+
[`AutoPipelineForInpainting`] is a generic pipeline class that instantiates an inpainting pipeline class. The
|
| 944 |
+
specific underlying pipeline class is automatically selected from either the
|
| 945 |
+
[`~AutoPipelineForInpainting.from_pretrained`] or [`~AutoPipelineForInpainting.from_pipe`] methods.
|
| 946 |
+
|
| 947 |
+
This class cannot be instantiated using `__init__()` (throws an error).
|
| 948 |
+
|
| 949 |
+
Class attributes:
|
| 950 |
+
|
| 951 |
+
- **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
|
| 952 |
+
diffusion pipeline's components.
|
| 953 |
+
|
| 954 |
+
"""
|
| 955 |
+
|
| 956 |
+
config_name = "model_index.json"
|
| 957 |
+
|
| 958 |
+
def __init__(self, *args, **kwargs):
|
| 959 |
+
raise EnvironmentError(
|
| 960 |
+
f"{self.__class__.__name__} is designed to be instantiated "
|
| 961 |
+
f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
|
| 962 |
+
f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
|
| 963 |
+
)
|
| 964 |
+
|
| 965 |
+
@classmethod
|
| 966 |
+
@validate_hf_hub_args
|
| 967 |
+
def from_pretrained(cls, pretrained_model_or_path, **kwargs):
|
| 968 |
+
r"""
|
| 969 |
+
Instantiates a inpainting Pytorch diffusion pipeline from pretrained pipeline weight.
|
| 970 |
+
|
| 971 |
+
The from_pretrained() method takes care of returning the correct pipeline class instance by:
|
| 972 |
+
1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
|
| 973 |
+
config object
|
| 974 |
+
2. Find the inpainting pipeline linked to the pipeline class using pattern matching on pipeline class name.
|
| 975 |
+
|
| 976 |
+
If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetInpaintPipeline`]
|
| 977 |
+
object.
|
| 978 |
+
|
| 979 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 980 |
+
|
| 981 |
+
If you get the error message below, you need to finetune the weights for your downstream task:
|
| 982 |
+
|
| 983 |
+
```
|
| 984 |
+
Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
|
| 985 |
+
- conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
|
| 986 |
+
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
|
| 987 |
+
```
|
| 988 |
+
|
| 989 |
+
Parameters:
|
| 990 |
+
pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
|
| 991 |
+
Can be either:
|
| 992 |
+
|
| 993 |
+
- A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
|
| 994 |
+
hosted on the Hub.
|
| 995 |
+
- A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
|
| 996 |
+
saved using
|
| 997 |
+
[`~DiffusionPipeline.save_pretrained`].
|
| 998 |
+
torch_dtype (`str` or `torch.dtype`, *optional*):
|
| 999 |
+
Override the default `torch.dtype` and load the model with another dtype.
|
| 1000 |
+
force_download (`bool`, *optional*, defaults to `False`):
|
| 1001 |
+
Whether or not to force the (re-)download of the model weights and configuration files, overriding the
|
| 1002 |
+
cached versions if they exist.
|
| 1003 |
+
cache_dir (`str | os.PathLike`, *optional*):
|
| 1004 |
+
Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
|
| 1005 |
+
is not used.
|
| 1006 |
+
|
| 1007 |
+
proxies (`dict[str, str]`, *optional*):
|
| 1008 |
+
A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
|
| 1009 |
+
'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
|
| 1010 |
+
output_loading_info(`bool`, *optional*, defaults to `False`):
|
| 1011 |
+
Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
|
| 1012 |
+
local_files_only (`bool`, *optional*, defaults to `False`):
|
| 1013 |
+
Whether to only load local model weights and configuration files or not. If set to `True`, the model
|
| 1014 |
+
won't be downloaded from the Hub.
|
| 1015 |
+
token (`str` or *bool*, *optional*):
|
| 1016 |
+
The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
|
| 1017 |
+
`diffusers-cli login` (stored in `~/.huggingface`) is used.
|
| 1018 |
+
revision (`str`, *optional*, defaults to `"main"`):
|
| 1019 |
+
The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
|
| 1020 |
+
allowed by Git.
|
| 1021 |
+
custom_revision (`str`, *optional*, defaults to `"main"`):
|
| 1022 |
+
The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
|
| 1023 |
+
`revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
|
| 1024 |
+
custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
|
| 1025 |
+
mirror (`str`, *optional*):
|
| 1026 |
+
Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
|
| 1027 |
+
guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
|
| 1028 |
+
information.
|
| 1029 |
+
device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
|
| 1030 |
+
A map that specifies where each submodule should go. It doesn’t need to be defined for each
|
| 1031 |
+
parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
|
| 1032 |
+
same device.
|
| 1033 |
+
|
| 1034 |
+
Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
|
| 1035 |
+
more information about each option see [designing a device
|
| 1036 |
+
map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
|
| 1037 |
+
max_memory (`Dict`, *optional*):
|
| 1038 |
+
A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
|
| 1039 |
+
each GPU and the available CPU RAM if unset.
|
| 1040 |
+
offload_folder (`str` or `os.PathLike`, *optional*):
|
| 1041 |
+
The path to offload weights if device_map contains the value `"disk"`.
|
| 1042 |
+
offload_state_dict (`bool`, *optional*):
|
| 1043 |
+
If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
|
| 1044 |
+
the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
|
| 1045 |
+
when there is some disk offload.
|
| 1046 |
+
low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
|
| 1047 |
+
Speed up model loading only loading the pretrained weights and not initializing the weights. This also
|
| 1048 |
+
tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
|
| 1049 |
+
Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
|
| 1050 |
+
argument to `True` will raise an error.
|
| 1051 |
+
use_safetensors (`bool`, *optional*, defaults to `None`):
|
| 1052 |
+
If set to `None`, the safetensors weights are downloaded if they're available **and** if the
|
| 1053 |
+
safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
|
| 1054 |
+
weights. If set to `False`, safetensors weights are not loaded.
|
| 1055 |
+
kwargs (remaining dictionary of keyword arguments, *optional*):
|
| 1056 |
+
Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
|
| 1057 |
+
class). The overwritten components are passed directly to the pipelines `__init__` method. See example
|
| 1058 |
+
below for more information.
|
| 1059 |
+
variant (`str`, *optional*):
|
| 1060 |
+
Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
|
| 1061 |
+
loading `from_flax`.
|
| 1062 |
+
|
| 1063 |
+
> [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
|
| 1064 |
+
with `hf > auth login`.
|
| 1065 |
+
|
| 1066 |
+
Examples:
|
| 1067 |
+
|
| 1068 |
+
```py
|
| 1069 |
+
>>> from diffusers import AutoPipelineForInpainting
|
| 1070 |
+
|
| 1071 |
+
>>> pipeline = AutoPipelineForInpainting.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
|
| 1072 |
+
>>> image = pipeline(prompt, image=init_image, mask_image=mask_image).images[0]
|
| 1073 |
+
```
|
| 1074 |
+
"""
|
| 1075 |
+
cache_dir = kwargs.pop("cache_dir", None)
|
| 1076 |
+
force_download = kwargs.pop("force_download", False)
|
| 1077 |
+
proxies = kwargs.pop("proxies", None)
|
| 1078 |
+
token = kwargs.pop("token", None)
|
| 1079 |
+
local_files_only = kwargs.pop("local_files_only", False)
|
| 1080 |
+
revision = kwargs.pop("revision", None)
|
| 1081 |
+
|
| 1082 |
+
load_config_kwargs = {
|
| 1083 |
+
"cache_dir": cache_dir,
|
| 1084 |
+
"force_download": force_download,
|
| 1085 |
+
"proxies": proxies,
|
| 1086 |
+
"token": token,
|
| 1087 |
+
"local_files_only": local_files_only,
|
| 1088 |
+
"revision": revision,
|
| 1089 |
+
}
|
| 1090 |
+
|
| 1091 |
+
config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
|
| 1092 |
+
orig_class_name = config["_class_name"]
|
| 1093 |
+
|
| 1094 |
+
# The `orig_class_name`` can be:
|
| 1095 |
+
# `- *InpaintPipeline` (for inpaint-specific checkpoint)
|
| 1096 |
+
# - `*ControlPipeline` (for Flux tools specific checkpoint)
|
| 1097 |
+
# - or *Pipeline (for regular text-to-image checkpoint)
|
| 1098 |
+
if "Inpaint" in orig_class_name:
|
| 1099 |
+
to_replace = "InpaintPipeline"
|
| 1100 |
+
elif "ControlPipeline" in orig_class_name:
|
| 1101 |
+
to_replace = "ControlPipeline"
|
| 1102 |
+
else:
|
| 1103 |
+
to_replace = "Pipeline"
|
| 1104 |
+
|
| 1105 |
+
if "controlnet" in kwargs:
|
| 1106 |
+
if isinstance(kwargs["controlnet"], ControlNetUnionModel):
|
| 1107 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlNetUnion" + to_replace)
|
| 1108 |
+
else:
|
| 1109 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlNet" + to_replace)
|
| 1110 |
+
if "enable_pag" in kwargs:
|
| 1111 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 1112 |
+
if enable_pag:
|
| 1113 |
+
orig_class_name = orig_class_name.replace(to_replace, "PAG" + to_replace)
|
| 1114 |
+
if to_replace == "ControlPipeline":
|
| 1115 |
+
orig_class_name = orig_class_name.replace(to_replace, "ControlInpaintPipeline")
|
| 1116 |
+
inpainting_cls = _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, orig_class_name)
|
| 1117 |
+
|
| 1118 |
+
kwargs = {**load_config_kwargs, **kwargs}
|
| 1119 |
+
return inpainting_cls.from_pretrained(pretrained_model_or_path, **kwargs)
|
| 1120 |
+
|
| 1121 |
+
@classmethod
|
| 1122 |
+
def from_pipe(cls, pipeline, **kwargs):
|
| 1123 |
+
r"""
|
| 1124 |
+
Instantiates a inpainting Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
|
| 1125 |
+
|
| 1126 |
+
The from_pipe() method takes care of returning the correct pipeline class instance by finding the inpainting
|
| 1127 |
+
pipeline linked to the pipeline class using pattern matching on pipeline class name.
|
| 1128 |
+
|
| 1129 |
+
All the modules the pipeline class contain will be used to initialize the new pipeline without reallocating
|
| 1130 |
+
additional memory.
|
| 1131 |
+
|
| 1132 |
+
The pipeline is set in evaluation mode (`model.eval()`) by default.
|
| 1133 |
+
|
| 1134 |
+
Parameters:
|
| 1135 |
+
pipeline (`DiffusionPipeline`):
|
| 1136 |
+
an instantiated `DiffusionPipeline` object
|
| 1137 |
+
|
| 1138 |
+
Examples:
|
| 1139 |
+
|
| 1140 |
+
```py
|
| 1141 |
+
>>> from diffusers import AutoPipelineForText2Image, AutoPipelineForInpainting
|
| 1142 |
+
|
| 1143 |
+
>>> pipe_t2i = AutoPipelineForText2Image.from_pretrained(
|
| 1144 |
+
... "DeepFloyd/IF-I-XL-v1.0", requires_safety_checker=False
|
| 1145 |
+
... )
|
| 1146 |
+
|
| 1147 |
+
>>> pipe_inpaint = AutoPipelineForInpainting.from_pipe(pipe_t2i)
|
| 1148 |
+
>>> image = pipe_inpaint(prompt, image=init_image, mask_image=mask_image).images[0]
|
| 1149 |
+
```
|
| 1150 |
+
"""
|
| 1151 |
+
original_config = dict(pipeline.config)
|
| 1152 |
+
original_cls_name = pipeline.__class__.__name__
|
| 1153 |
+
|
| 1154 |
+
# derive the pipeline class to instantiate
|
| 1155 |
+
inpainting_cls = _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, original_cls_name)
|
| 1156 |
+
|
| 1157 |
+
if "controlnet" in kwargs:
|
| 1158 |
+
if kwargs["controlnet"] is not None:
|
| 1159 |
+
inpainting_cls = _get_task_class(
|
| 1160 |
+
AUTO_INPAINT_PIPELINES_MAPPING,
|
| 1161 |
+
inpainting_cls.__name__.replace("ControlNet", "").replace(
|
| 1162 |
+
"InpaintPipeline", "ControlNetInpaintPipeline"
|
| 1163 |
+
),
|
| 1164 |
+
)
|
| 1165 |
+
else:
|
| 1166 |
+
inpainting_cls = _get_task_class(
|
| 1167 |
+
AUTO_INPAINT_PIPELINES_MAPPING,
|
| 1168 |
+
inpainting_cls.__name__.replace("ControlNetInpaintPipeline", "InpaintPipeline"),
|
| 1169 |
+
)
|
| 1170 |
+
|
| 1171 |
+
if "enable_pag" in kwargs:
|
| 1172 |
+
enable_pag = kwargs.pop("enable_pag")
|
| 1173 |
+
if enable_pag:
|
| 1174 |
+
inpainting_cls = _get_task_class(
|
| 1175 |
+
AUTO_INPAINT_PIPELINES_MAPPING,
|
| 1176 |
+
inpainting_cls.__name__.replace("PAG", "").replace("InpaintPipeline", "PAGInpaintPipeline"),
|
| 1177 |
+
)
|
| 1178 |
+
else:
|
| 1179 |
+
inpainting_cls = _get_task_class(
|
| 1180 |
+
AUTO_INPAINT_PIPELINES_MAPPING,
|
| 1181 |
+
inpainting_cls.__name__.replace("PAGInpaintPipeline", "InpaintPipeline"),
|
| 1182 |
+
)
|
| 1183 |
+
|
| 1184 |
+
# define expected module and optional kwargs given the pipeline signature
|
| 1185 |
+
expected_modules, optional_kwargs = inpainting_cls._get_signature_keys(inpainting_cls)
|
| 1186 |
+
|
| 1187 |
+
pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
|
| 1188 |
+
|
| 1189 |
+
# allow users pass modules in `kwargs` to override the original pipeline's components
|
| 1190 |
+
passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
|
| 1191 |
+
original_class_obj = {
|
| 1192 |
+
k: pipeline.components[k]
|
| 1193 |
+
for k, v in pipeline.components.items()
|
| 1194 |
+
if k in expected_modules and k not in passed_class_obj
|
| 1195 |
+
}
|
| 1196 |
+
|
| 1197 |
+
# allow users pass optional kwargs to override the original pipelines config attribute
|
| 1198 |
+
passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
|
| 1199 |
+
original_pipe_kwargs = {
|
| 1200 |
+
k: original_config[k]
|
| 1201 |
+
for k, v in original_config.items()
|
| 1202 |
+
if k in optional_kwargs and k not in passed_pipe_kwargs
|
| 1203 |
+
}
|
| 1204 |
+
|
| 1205 |
+
# config that were not expected by original pipeline is stored as private attribute
|
| 1206 |
+
# we will pass them as optional arguments if they can be accepted by the pipeline
|
| 1207 |
+
additional_pipe_kwargs = [
|
| 1208 |
+
k[1:]
|
| 1209 |
+
for k in original_config.keys()
|
| 1210 |
+
if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
|
| 1211 |
+
]
|
| 1212 |
+
for k in additional_pipe_kwargs:
|
| 1213 |
+
original_pipe_kwargs[k] = original_config.pop(f"_{k}")
|
| 1214 |
+
|
| 1215 |
+
inpainting_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
|
| 1216 |
+
|
| 1217 |
+
# store unused config as private attribute
|
| 1218 |
+
unused_original_config = {
|
| 1219 |
+
f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
|
| 1220 |
+
for k, v in original_config.items()
|
| 1221 |
+
if k not in inpainting_kwargs
|
| 1222 |
+
}
|
| 1223 |
+
|
| 1224 |
+
missing_modules = (
|
| 1225 |
+
set(expected_modules) - set(inpainting_cls._optional_components) - set(inpainting_kwargs.keys())
|
| 1226 |
+
)
|
| 1227 |
+
|
| 1228 |
+
if len(missing_modules) > 0:
|
| 1229 |
+
raise ValueError(
|
| 1230 |
+
f"Pipeline {inpainting_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
|
| 1231 |
+
)
|
| 1232 |
+
|
| 1233 |
+
model = inpainting_cls(**inpainting_kwargs)
|
| 1234 |
+
model.register_to_config(_name_or_path=pretrained_model_name_or_path)
|
| 1235 |
+
model.register_to_config(**unused_original_config)
|
| 1236 |
+
|
| 1237 |
+
return model
|
third_party/diffusers/src/diffusers/pipelines/chroma/__init__.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ...utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_torch_available,
|
| 9 |
+
is_transformers_available,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_dummy_objects = {}
|
| 14 |
+
_additional_imports = {}
|
| 15 |
+
_import_structure = {"pipeline_output": ["ChromaPipelineOutput"]}
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 19 |
+
raise OptionalDependencyNotAvailable()
|
| 20 |
+
except OptionalDependencyNotAvailable:
|
| 21 |
+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
| 22 |
+
|
| 23 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 24 |
+
else:
|
| 25 |
+
_import_structure["pipeline_chroma"] = ["ChromaPipeline"]
|
| 26 |
+
_import_structure["pipeline_chroma_img2img"] = ["ChromaImg2ImgPipeline"]
|
| 27 |
+
_import_structure["pipeline_chroma_inpainting"] = ["ChromaInpaintPipeline"]
|
| 28 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 29 |
+
try:
|
| 30 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 31 |
+
raise OptionalDependencyNotAvailable()
|
| 32 |
+
except OptionalDependencyNotAvailable:
|
| 33 |
+
from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
|
| 34 |
+
else:
|
| 35 |
+
from .pipeline_chroma import ChromaPipeline
|
| 36 |
+
from .pipeline_chroma_img2img import ChromaImg2ImgPipeline
|
| 37 |
+
from .pipeline_chroma_inpainting import ChromaInpaintPipeline
|
| 38 |
+
else:
|
| 39 |
+
import sys
|
| 40 |
+
|
| 41 |
+
sys.modules[__name__] = _LazyModule(
|
| 42 |
+
__name__,
|
| 43 |
+
globals()["__file__"],
|
| 44 |
+
_import_structure,
|
| 45 |
+
module_spec=__spec__,
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
for name, value in _dummy_objects.items():
|
| 49 |
+
setattr(sys.modules[__name__], name, value)
|
| 50 |
+
for name, value in _additional_imports.items():
|
| 51 |
+
setattr(sys.modules[__name__], name, value)
|
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma.py
ADDED
|
@@ -0,0 +1,976 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from typing import Any, Callable
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, T5EncoderModel, T5TokenizerFast
|
| 21 |
+
|
| 22 |
+
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
| 23 |
+
from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
|
| 24 |
+
from ...models import AutoencoderKL, ChromaTransformer2DModel
|
| 25 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 26 |
+
from ...utils import (
|
| 27 |
+
USE_PEFT_BACKEND,
|
| 28 |
+
deprecate,
|
| 29 |
+
is_torch_xla_available,
|
| 30 |
+
logging,
|
| 31 |
+
replace_example_docstring,
|
| 32 |
+
scale_lora_layers,
|
| 33 |
+
unscale_lora_layers,
|
| 34 |
+
)
|
| 35 |
+
from ...utils.torch_utils import randn_tensor
|
| 36 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 37 |
+
from .pipeline_output import ChromaPipelineOutput
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if is_torch_xla_available():
|
| 41 |
+
import torch_xla.core.xla_model as xm
|
| 42 |
+
|
| 43 |
+
XLA_AVAILABLE = True
|
| 44 |
+
else:
|
| 45 |
+
XLA_AVAILABLE = False
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 49 |
+
|
| 50 |
+
EXAMPLE_DOC_STRING = """
|
| 51 |
+
Examples:
|
| 52 |
+
```py
|
| 53 |
+
>>> import torch
|
| 54 |
+
>>> from diffusers import ChromaPipeline
|
| 55 |
+
|
| 56 |
+
>>> model_id = "lodestones/Chroma1-HD"
|
| 57 |
+
>>> ckpt_path = "https://huggingface.co/lodestones/Chroma1-HD/blob/main/Chroma1-HD.safetensors"
|
| 58 |
+
>>> transformer = ChromaTransformer2DModel.from_single_file(ckpt_path, torch_dtype=torch.bfloat16)
|
| 59 |
+
>>> pipe = ChromaPipeline.from_pretrained(
|
| 60 |
+
... model_id,
|
| 61 |
+
... transformer=transformer,
|
| 62 |
+
... torch_dtype=torch.bfloat16,
|
| 63 |
+
... )
|
| 64 |
+
>>> pipe.enable_model_cpu_offload()
|
| 65 |
+
>>> prompt = [
|
| 66 |
+
... "A high-fashion close-up portrait of a blonde woman in clear sunglasses. The image uses a bold teal and red color split for dramatic lighting. The background is a simple teal-green. The photo is sharp and well-composed, and is designed for viewing with anaglyph 3D glasses for optimal effect. It looks professionally done."
|
| 67 |
+
... ]
|
| 68 |
+
>>> negative_prompt = [
|
| 69 |
+
... "low quality, ugly, unfinished, out of focus, deformed, disfigure, blurry, smudged, restricted palette, flat colors"
|
| 70 |
+
... ]
|
| 71 |
+
>>> image = pipe(prompt, negative_prompt=negative_prompt).images[0]
|
| 72 |
+
>>> image.save("chroma.png")
|
| 73 |
+
```
|
| 74 |
+
"""
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
|
| 78 |
+
def calculate_shift(
|
| 79 |
+
image_seq_len,
|
| 80 |
+
base_seq_len: int = 256,
|
| 81 |
+
max_seq_len: int = 4096,
|
| 82 |
+
base_shift: float = 0.5,
|
| 83 |
+
max_shift: float = 1.15,
|
| 84 |
+
):
|
| 85 |
+
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
| 86 |
+
b = base_shift - m * base_seq_len
|
| 87 |
+
mu = image_seq_len * m + b
|
| 88 |
+
return mu
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 92 |
+
def retrieve_timesteps(
|
| 93 |
+
scheduler,
|
| 94 |
+
num_inference_steps: int | None = None,
|
| 95 |
+
device: str | torch.device | None = None,
|
| 96 |
+
timesteps: list[int] | None = None,
|
| 97 |
+
sigmas: list[float] | None = None,
|
| 98 |
+
**kwargs,
|
| 99 |
+
):
|
| 100 |
+
r"""
|
| 101 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 102 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
scheduler (`SchedulerMixin`):
|
| 106 |
+
The scheduler to get timesteps from.
|
| 107 |
+
num_inference_steps (`int`):
|
| 108 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 109 |
+
must be `None`.
|
| 110 |
+
device (`str` or `torch.device`, *optional*):
|
| 111 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 112 |
+
timesteps (`list[int]`, *optional*):
|
| 113 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 114 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 115 |
+
sigmas (`list[float]`, *optional*):
|
| 116 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 117 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 118 |
+
|
| 119 |
+
Returns:
|
| 120 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 121 |
+
second element is the number of inference steps.
|
| 122 |
+
"""
|
| 123 |
+
if timesteps is not None and sigmas is not None:
|
| 124 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 125 |
+
if timesteps is not None:
|
| 126 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 127 |
+
if not accepts_timesteps:
|
| 128 |
+
raise ValueError(
|
| 129 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 130 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 131 |
+
)
|
| 132 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 133 |
+
timesteps = scheduler.timesteps
|
| 134 |
+
num_inference_steps = len(timesteps)
|
| 135 |
+
elif sigmas is not None:
|
| 136 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 137 |
+
if not accept_sigmas:
|
| 138 |
+
raise ValueError(
|
| 139 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 140 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 141 |
+
)
|
| 142 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 143 |
+
timesteps = scheduler.timesteps
|
| 144 |
+
num_inference_steps = len(timesteps)
|
| 145 |
+
else:
|
| 146 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 147 |
+
timesteps = scheduler.timesteps
|
| 148 |
+
return timesteps, num_inference_steps
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class ChromaPipeline(
|
| 152 |
+
DiffusionPipeline,
|
| 153 |
+
FluxLoraLoaderMixin,
|
| 154 |
+
FromSingleFileMixin,
|
| 155 |
+
TextualInversionLoaderMixin,
|
| 156 |
+
FluxIPAdapterMixin,
|
| 157 |
+
):
|
| 158 |
+
r"""
|
| 159 |
+
The Chroma pipeline for text-to-image generation.
|
| 160 |
+
|
| 161 |
+
Reference: https://huggingface.co/lodestones/Chroma1-HD/
|
| 162 |
+
|
| 163 |
+
Args:
|
| 164 |
+
transformer ([`ChromaTransformer2DModel`]):
|
| 165 |
+
Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
|
| 166 |
+
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
| 167 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 168 |
+
vae ([`AutoencoderKL`]):
|
| 169 |
+
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representation
|
| 170 |
+
text_encoder ([`T5EncoderModel`]):
|
| 171 |
+
[T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
|
| 172 |
+
the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
|
| 173 |
+
tokenizer (`T5TokenizerFast`):
|
| 174 |
+
Second Tokenizer of class
|
| 175 |
+
[T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
|
| 176 |
+
"""
|
| 177 |
+
|
| 178 |
+
model_cpu_offload_seq = "text_encoder->image_encoder->transformer->vae"
|
| 179 |
+
_optional_components = ["image_encoder", "feature_extractor"]
|
| 180 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds"]
|
| 181 |
+
|
| 182 |
+
def __init__(
|
| 183 |
+
self,
|
| 184 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 185 |
+
vae: AutoencoderKL,
|
| 186 |
+
text_encoder: T5EncoderModel,
|
| 187 |
+
tokenizer: T5TokenizerFast,
|
| 188 |
+
transformer: ChromaTransformer2DModel,
|
| 189 |
+
image_encoder: CLIPVisionModelWithProjection = None,
|
| 190 |
+
feature_extractor: CLIPImageProcessor = None,
|
| 191 |
+
):
|
| 192 |
+
super().__init__()
|
| 193 |
+
|
| 194 |
+
self.register_modules(
|
| 195 |
+
vae=vae,
|
| 196 |
+
text_encoder=text_encoder,
|
| 197 |
+
tokenizer=tokenizer,
|
| 198 |
+
transformer=transformer,
|
| 199 |
+
scheduler=scheduler,
|
| 200 |
+
image_encoder=image_encoder,
|
| 201 |
+
feature_extractor=feature_extractor,
|
| 202 |
+
)
|
| 203 |
+
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
|
| 204 |
+
# Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
|
| 205 |
+
# by the patch size. So the vae scale factor is multiplied by the patch size to account for this
|
| 206 |
+
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
|
| 207 |
+
self.default_sample_size = 128
|
| 208 |
+
|
| 209 |
+
def _get_t5_prompt_embeds(
|
| 210 |
+
self,
|
| 211 |
+
prompt: str | list[str] = None,
|
| 212 |
+
num_images_per_prompt: int = 1,
|
| 213 |
+
max_sequence_length: int = 512,
|
| 214 |
+
device: torch.device | None = None,
|
| 215 |
+
dtype: torch.dtype | None = None,
|
| 216 |
+
):
|
| 217 |
+
device = device or self._execution_device
|
| 218 |
+
dtype = dtype or self.text_encoder.dtype
|
| 219 |
+
|
| 220 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 221 |
+
batch_size = len(prompt)
|
| 222 |
+
|
| 223 |
+
if isinstance(self, TextualInversionLoaderMixin):
|
| 224 |
+
prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
|
| 225 |
+
|
| 226 |
+
text_inputs = self.tokenizer(
|
| 227 |
+
prompt,
|
| 228 |
+
padding="max_length",
|
| 229 |
+
max_length=max_sequence_length,
|
| 230 |
+
truncation=True,
|
| 231 |
+
return_length=False,
|
| 232 |
+
return_overflowing_tokens=False,
|
| 233 |
+
return_tensors="pt",
|
| 234 |
+
)
|
| 235 |
+
text_input_ids = text_inputs.input_ids
|
| 236 |
+
tokenizer_mask = text_inputs.attention_mask
|
| 237 |
+
|
| 238 |
+
tokenizer_mask_device = tokenizer_mask.to(device)
|
| 239 |
+
|
| 240 |
+
# unlike FLUX, Chroma uses the attention mask when generating the T5 embedding
|
| 241 |
+
prompt_embeds = self.text_encoder(
|
| 242 |
+
text_input_ids.to(device),
|
| 243 |
+
output_hidden_states=False,
|
| 244 |
+
attention_mask=tokenizer_mask_device,
|
| 245 |
+
)[0]
|
| 246 |
+
|
| 247 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 248 |
+
|
| 249 |
+
# for the text tokens, chroma requires that all except the first padding token are masked out during the forward pass through the transformer
|
| 250 |
+
seq_lengths = tokenizer_mask_device.sum(dim=1)
|
| 251 |
+
mask_indices = torch.arange(tokenizer_mask_device.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
|
| 252 |
+
attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
|
| 253 |
+
|
| 254 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 255 |
+
|
| 256 |
+
# duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
|
| 257 |
+
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
| 258 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 259 |
+
|
| 260 |
+
attention_mask = attention_mask.repeat(1, num_images_per_prompt)
|
| 261 |
+
attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
|
| 262 |
+
|
| 263 |
+
return prompt_embeds, attention_mask
|
| 264 |
+
|
| 265 |
+
def encode_prompt(
|
| 266 |
+
self,
|
| 267 |
+
prompt: str | list[str],
|
| 268 |
+
negative_prompt: str | list[str] = None,
|
| 269 |
+
device: torch.device | None = None,
|
| 270 |
+
num_images_per_prompt: int = 1,
|
| 271 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 272 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 273 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 274 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 275 |
+
do_classifier_free_guidance: bool = True,
|
| 276 |
+
max_sequence_length: int = 512,
|
| 277 |
+
lora_scale: float | None = None,
|
| 278 |
+
):
|
| 279 |
+
r"""
|
| 280 |
+
|
| 281 |
+
Args:
|
| 282 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 283 |
+
prompt to be encoded
|
| 284 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 285 |
+
The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
|
| 286 |
+
instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
|
| 287 |
+
device: (`torch.device`):
|
| 288 |
+
torch device
|
| 289 |
+
num_images_per_prompt (`int`):
|
| 290 |
+
number of images that should be generated per prompt
|
| 291 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 292 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 293 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 294 |
+
lora_scale (`float`, *optional*):
|
| 295 |
+
A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
|
| 296 |
+
"""
|
| 297 |
+
device = device or self._execution_device
|
| 298 |
+
|
| 299 |
+
# set lora scale so that monkey patched LoRA
|
| 300 |
+
# function of text encoder can correctly access it
|
| 301 |
+
if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
|
| 302 |
+
self._lora_scale = lora_scale
|
| 303 |
+
|
| 304 |
+
# dynamically adjust the LoRA scale
|
| 305 |
+
if self.text_encoder is not None and USE_PEFT_BACKEND:
|
| 306 |
+
scale_lora_layers(self.text_encoder, lora_scale)
|
| 307 |
+
|
| 308 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 309 |
+
|
| 310 |
+
if prompt is not None:
|
| 311 |
+
batch_size = len(prompt)
|
| 312 |
+
else:
|
| 313 |
+
batch_size = prompt_embeds.shape[0]
|
| 314 |
+
|
| 315 |
+
if prompt_embeds is None:
|
| 316 |
+
prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 317 |
+
prompt=prompt,
|
| 318 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 319 |
+
max_sequence_length=max_sequence_length,
|
| 320 |
+
device=device,
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
|
| 324 |
+
text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
|
| 325 |
+
negative_text_ids = None
|
| 326 |
+
|
| 327 |
+
if do_classifier_free_guidance:
|
| 328 |
+
if negative_prompt_embeds is None:
|
| 329 |
+
negative_prompt = negative_prompt or ""
|
| 330 |
+
negative_prompt = (
|
| 331 |
+
batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 335 |
+
raise TypeError(
|
| 336 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 337 |
+
f" {type(prompt)}."
|
| 338 |
+
)
|
| 339 |
+
elif batch_size != len(negative_prompt):
|
| 340 |
+
raise ValueError(
|
| 341 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 342 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 343 |
+
" the batch size of `prompt`."
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 347 |
+
prompt=negative_prompt,
|
| 348 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 349 |
+
max_sequence_length=max_sequence_length,
|
| 350 |
+
device=device,
|
| 351 |
+
)
|
| 352 |
+
|
| 353 |
+
negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
|
| 354 |
+
|
| 355 |
+
if self.text_encoder is not None:
|
| 356 |
+
if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
|
| 357 |
+
# Retrieve the original scale by scaling back the LoRA layers
|
| 358 |
+
unscale_lora_layers(self.text_encoder, lora_scale)
|
| 359 |
+
|
| 360 |
+
return (
|
| 361 |
+
prompt_embeds,
|
| 362 |
+
text_ids,
|
| 363 |
+
prompt_attention_mask,
|
| 364 |
+
negative_prompt_embeds,
|
| 365 |
+
negative_text_ids,
|
| 366 |
+
negative_prompt_attention_mask,
|
| 367 |
+
)
|
| 368 |
+
|
| 369 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
|
| 370 |
+
def encode_image(self, image, device, num_images_per_prompt):
|
| 371 |
+
dtype = next(self.image_encoder.parameters()).dtype
|
| 372 |
+
|
| 373 |
+
if not isinstance(image, torch.Tensor):
|
| 374 |
+
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
| 375 |
+
|
| 376 |
+
image = image.to(device=device, dtype=dtype)
|
| 377 |
+
image_embeds = self.image_encoder(image).image_embeds
|
| 378 |
+
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
| 379 |
+
return image_embeds
|
| 380 |
+
|
| 381 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_ip_adapter_image_embeds
|
| 382 |
+
def prepare_ip_adapter_image_embeds(
|
| 383 |
+
self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
|
| 384 |
+
):
|
| 385 |
+
image_embeds = []
|
| 386 |
+
if ip_adapter_image_embeds is None:
|
| 387 |
+
if not isinstance(ip_adapter_image, list):
|
| 388 |
+
ip_adapter_image = [ip_adapter_image]
|
| 389 |
+
|
| 390 |
+
if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 391 |
+
raise ValueError(
|
| 392 |
+
f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
for single_ip_adapter_image in ip_adapter_image:
|
| 396 |
+
single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
|
| 397 |
+
image_embeds.append(single_image_embeds[None, :])
|
| 398 |
+
else:
|
| 399 |
+
if not isinstance(ip_adapter_image_embeds, list):
|
| 400 |
+
ip_adapter_image_embeds = [ip_adapter_image_embeds]
|
| 401 |
+
|
| 402 |
+
if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 403 |
+
raise ValueError(
|
| 404 |
+
f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
for single_image_embeds in ip_adapter_image_embeds:
|
| 408 |
+
image_embeds.append(single_image_embeds)
|
| 409 |
+
|
| 410 |
+
ip_adapter_image_embeds = []
|
| 411 |
+
for single_image_embeds in image_embeds:
|
| 412 |
+
single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
|
| 413 |
+
single_image_embeds = single_image_embeds.to(device=device)
|
| 414 |
+
ip_adapter_image_embeds.append(single_image_embeds)
|
| 415 |
+
|
| 416 |
+
return ip_adapter_image_embeds
|
| 417 |
+
|
| 418 |
+
def check_inputs(
|
| 419 |
+
self,
|
| 420 |
+
prompt,
|
| 421 |
+
height,
|
| 422 |
+
width,
|
| 423 |
+
negative_prompt=None,
|
| 424 |
+
prompt_embeds=None,
|
| 425 |
+
prompt_attention_mask=None,
|
| 426 |
+
negative_prompt_embeds=None,
|
| 427 |
+
negative_prompt_attention_mask=None,
|
| 428 |
+
callback_on_step_end_tensor_inputs=None,
|
| 429 |
+
max_sequence_length=None,
|
| 430 |
+
):
|
| 431 |
+
if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
|
| 432 |
+
logger.warning(
|
| 433 |
+
f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
|
| 434 |
+
)
|
| 435 |
+
|
| 436 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 437 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 438 |
+
):
|
| 439 |
+
raise ValueError(
|
| 440 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
if prompt is not None and prompt_embeds is not None:
|
| 444 |
+
raise ValueError(
|
| 445 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 446 |
+
" only forward one of the two."
|
| 447 |
+
)
|
| 448 |
+
elif prompt is None and prompt_embeds is None:
|
| 449 |
+
raise ValueError(
|
| 450 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 451 |
+
)
|
| 452 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 453 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 454 |
+
|
| 455 |
+
if negative_prompt is not None and negative_prompt_embeds is not None:
|
| 456 |
+
raise ValueError(
|
| 457 |
+
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
|
| 458 |
+
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
| 459 |
+
)
|
| 460 |
+
|
| 461 |
+
if prompt_embeds is not None and prompt_attention_mask is None:
|
| 462 |
+
raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
|
| 463 |
+
|
| 464 |
+
if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
|
| 465 |
+
raise ValueError(
|
| 466 |
+
"Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
if max_sequence_length is not None and max_sequence_length > 512:
|
| 470 |
+
raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
|
| 471 |
+
|
| 472 |
+
@staticmethod
|
| 473 |
+
def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
|
| 474 |
+
latent_image_ids = torch.zeros(height, width, 3)
|
| 475 |
+
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
|
| 476 |
+
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
|
| 477 |
+
|
| 478 |
+
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
| 479 |
+
|
| 480 |
+
latent_image_ids = latent_image_ids.reshape(
|
| 481 |
+
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
| 482 |
+
)
|
| 483 |
+
|
| 484 |
+
return latent_image_ids.to(device=device, dtype=dtype)
|
| 485 |
+
|
| 486 |
+
@staticmethod
|
| 487 |
+
def _pack_latents(latents, batch_size, num_channels_latents, height, width):
|
| 488 |
+
latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
|
| 489 |
+
latents = latents.permute(0, 2, 4, 1, 3, 5)
|
| 490 |
+
latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
|
| 491 |
+
|
| 492 |
+
return latents
|
| 493 |
+
|
| 494 |
+
@staticmethod
|
| 495 |
+
def _unpack_latents(latents, height, width, vae_scale_factor):
|
| 496 |
+
batch_size, num_patches, channels = latents.shape
|
| 497 |
+
|
| 498 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 499 |
+
# latent height and width to be divisible by 2.
|
| 500 |
+
height = 2 * (int(height) // (vae_scale_factor * 2))
|
| 501 |
+
width = 2 * (int(width) // (vae_scale_factor * 2))
|
| 502 |
+
|
| 503 |
+
latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
|
| 504 |
+
latents = latents.permute(0, 3, 1, 4, 2, 5)
|
| 505 |
+
|
| 506 |
+
latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
|
| 507 |
+
|
| 508 |
+
return latents
|
| 509 |
+
|
| 510 |
+
def enable_vae_slicing(self):
|
| 511 |
+
r"""
|
| 512 |
+
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
| 513 |
+
compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
|
| 514 |
+
"""
|
| 515 |
+
depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
|
| 516 |
+
deprecate(
|
| 517 |
+
"enable_vae_slicing",
|
| 518 |
+
"0.40.0",
|
| 519 |
+
depr_message,
|
| 520 |
+
)
|
| 521 |
+
self.vae.enable_slicing()
|
| 522 |
+
|
| 523 |
+
def disable_vae_slicing(self):
|
| 524 |
+
r"""
|
| 525 |
+
Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
|
| 526 |
+
computing decoding in one step.
|
| 527 |
+
"""
|
| 528 |
+
depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
|
| 529 |
+
deprecate(
|
| 530 |
+
"disable_vae_slicing",
|
| 531 |
+
"0.40.0",
|
| 532 |
+
depr_message,
|
| 533 |
+
)
|
| 534 |
+
self.vae.disable_slicing()
|
| 535 |
+
|
| 536 |
+
def enable_vae_tiling(self):
|
| 537 |
+
r"""
|
| 538 |
+
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
| 539 |
+
compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
|
| 540 |
+
processing larger images.
|
| 541 |
+
"""
|
| 542 |
+
depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
|
| 543 |
+
deprecate(
|
| 544 |
+
"enable_vae_tiling",
|
| 545 |
+
"0.40.0",
|
| 546 |
+
depr_message,
|
| 547 |
+
)
|
| 548 |
+
self.vae.enable_tiling()
|
| 549 |
+
|
| 550 |
+
def disable_vae_tiling(self):
|
| 551 |
+
r"""
|
| 552 |
+
Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
|
| 553 |
+
computing decoding in one step.
|
| 554 |
+
"""
|
| 555 |
+
depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
|
| 556 |
+
deprecate(
|
| 557 |
+
"disable_vae_tiling",
|
| 558 |
+
"0.40.0",
|
| 559 |
+
depr_message,
|
| 560 |
+
)
|
| 561 |
+
self.vae.disable_tiling()
|
| 562 |
+
|
| 563 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_latents
|
| 564 |
+
def prepare_latents(
|
| 565 |
+
self,
|
| 566 |
+
batch_size,
|
| 567 |
+
num_channels_latents,
|
| 568 |
+
height,
|
| 569 |
+
width,
|
| 570 |
+
dtype,
|
| 571 |
+
device,
|
| 572 |
+
generator,
|
| 573 |
+
latents=None,
|
| 574 |
+
):
|
| 575 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 576 |
+
# latent height and width to be divisible by 2.
|
| 577 |
+
height = 2 * (int(height) // (self.vae_scale_factor * 2))
|
| 578 |
+
width = 2 * (int(width) // (self.vae_scale_factor * 2))
|
| 579 |
+
|
| 580 |
+
shape = (batch_size, num_channels_latents, height, width)
|
| 581 |
+
|
| 582 |
+
if latents is not None:
|
| 583 |
+
latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
|
| 584 |
+
return latents.to(device=device, dtype=dtype), latent_image_ids
|
| 585 |
+
|
| 586 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 587 |
+
raise ValueError(
|
| 588 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 589 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 593 |
+
latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
|
| 594 |
+
|
| 595 |
+
latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
|
| 596 |
+
|
| 597 |
+
return latents, latent_image_ids
|
| 598 |
+
|
| 599 |
+
def _prepare_attention_mask(
|
| 600 |
+
self,
|
| 601 |
+
batch_size,
|
| 602 |
+
sequence_length,
|
| 603 |
+
dtype,
|
| 604 |
+
attention_mask=None,
|
| 605 |
+
):
|
| 606 |
+
if attention_mask is None:
|
| 607 |
+
return attention_mask
|
| 608 |
+
|
| 609 |
+
# Extend the prompt attention mask to account for image tokens in the final sequence
|
| 610 |
+
attention_mask = torch.cat(
|
| 611 |
+
[attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device, dtype=torch.bool)],
|
| 612 |
+
dim=1,
|
| 613 |
+
)
|
| 614 |
+
|
| 615 |
+
return attention_mask
|
| 616 |
+
|
| 617 |
+
@property
|
| 618 |
+
def guidance_scale(self):
|
| 619 |
+
return self._guidance_scale
|
| 620 |
+
|
| 621 |
+
@property
|
| 622 |
+
def joint_attention_kwargs(self):
|
| 623 |
+
return self._joint_attention_kwargs
|
| 624 |
+
|
| 625 |
+
@property
|
| 626 |
+
def do_classifier_free_guidance(self):
|
| 627 |
+
return self._guidance_scale > 1
|
| 628 |
+
|
| 629 |
+
@property
|
| 630 |
+
def num_timesteps(self):
|
| 631 |
+
return self._num_timesteps
|
| 632 |
+
|
| 633 |
+
@property
|
| 634 |
+
def current_timestep(self):
|
| 635 |
+
return self._current_timestep
|
| 636 |
+
|
| 637 |
+
@property
|
| 638 |
+
def interrupt(self):
|
| 639 |
+
return self._interrupt
|
| 640 |
+
|
| 641 |
+
@torch.no_grad()
|
| 642 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 643 |
+
def __call__(
|
| 644 |
+
self,
|
| 645 |
+
prompt: str | list[str] = None,
|
| 646 |
+
negative_prompt: str | list[str] = None,
|
| 647 |
+
height: int | None = None,
|
| 648 |
+
width: int | None = None,
|
| 649 |
+
num_inference_steps: int = 35,
|
| 650 |
+
sigmas: list[float] | None = None,
|
| 651 |
+
guidance_scale: float = 5.0,
|
| 652 |
+
num_images_per_prompt: int | None = 1,
|
| 653 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 654 |
+
latents: torch.Tensor | None = None,
|
| 655 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 656 |
+
ip_adapter_image: PipelineImageInput | None = None,
|
| 657 |
+
ip_adapter_image_embeds: list[torch.Tensor] | None = None,
|
| 658 |
+
negative_ip_adapter_image: PipelineImageInput | None = None,
|
| 659 |
+
negative_ip_adapter_image_embeds: list[torch.Tensor] | None = None,
|
| 660 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 661 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 662 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 663 |
+
output_type: str | None = "pil",
|
| 664 |
+
return_dict: bool = True,
|
| 665 |
+
joint_attention_kwargs: dict[str, Any] | None = None,
|
| 666 |
+
callback_on_step_end: Callable[[int, int], None] | None = None,
|
| 667 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 668 |
+
max_sequence_length: int = 512,
|
| 669 |
+
):
|
| 670 |
+
r"""
|
| 671 |
+
Function invoked when calling the pipeline for generation.
|
| 672 |
+
|
| 673 |
+
Args:
|
| 674 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 675 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 676 |
+
instead.
|
| 677 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 678 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 679 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 680 |
+
not greater than `1`).
|
| 681 |
+
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 682 |
+
The height in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 683 |
+
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 684 |
+
The width in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 685 |
+
num_inference_steps (`int`, *optional*, defaults to 50):
|
| 686 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 687 |
+
expense of slower inference.
|
| 688 |
+
sigmas (`list[float]`, *optional*):
|
| 689 |
+
Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
|
| 690 |
+
their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
|
| 691 |
+
will be used.
|
| 692 |
+
guidance_scale (`float`, *optional*, defaults to 3.5):
|
| 693 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 694 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 695 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 696 |
+
`guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
|
| 697 |
+
the text `prompt`, usually at the expense of lower image quality.
|
| 698 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 699 |
+
The number of images to generate per prompt.
|
| 700 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 701 |
+
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
|
| 702 |
+
to make generation deterministic.
|
| 703 |
+
latents (`torch.Tensor`, *optional*):
|
| 704 |
+
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
|
| 705 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 706 |
+
tensor will be generated by sampling using the supplied random `generator`.
|
| 707 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 708 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 709 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 710 |
+
ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 711 |
+
ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
|
| 712 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 713 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 714 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 715 |
+
negative_ip_adapter_image:
|
| 716 |
+
(`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 717 |
+
negative_ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
|
| 718 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 719 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 720 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 721 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 722 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 723 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 724 |
+
argument.
|
| 725 |
+
prompt_attention_mask (torch.Tensor, *optional*):
|
| 726 |
+
Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
|
| 727 |
+
Chroma requires a single padding token remain unmasked. Please refer to
|
| 728 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 729 |
+
negative_prompt_attention_mask (torch.Tensor, *optional*):
|
| 730 |
+
Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
|
| 731 |
+
prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
|
| 732 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 733 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 734 |
+
The output format of the generate image. Choose between
|
| 735 |
+
[PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
|
| 736 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 737 |
+
Whether or not to return a [`~pipelines.flux.ChromaPipelineOutput`] instead of a plain tuple.
|
| 738 |
+
joint_attention_kwargs (`dict`, *optional*):
|
| 739 |
+
A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
|
| 740 |
+
`self.processor` in
|
| 741 |
+
[diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
| 742 |
+
callback_on_step_end (`Callable`, *optional*):
|
| 743 |
+
A function that calls at the end of each denoising steps during the inference. The function is called
|
| 744 |
+
with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
|
| 745 |
+
callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
|
| 746 |
+
`callback_on_step_end_tensor_inputs`.
|
| 747 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 748 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 749 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 750 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 751 |
+
max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
|
| 752 |
+
|
| 753 |
+
Examples:
|
| 754 |
+
|
| 755 |
+
Returns:
|
| 756 |
+
[`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
|
| 757 |
+
`return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
|
| 758 |
+
generated images.
|
| 759 |
+
"""
|
| 760 |
+
|
| 761 |
+
height = height or self.default_sample_size * self.vae_scale_factor
|
| 762 |
+
width = width or self.default_sample_size * self.vae_scale_factor
|
| 763 |
+
|
| 764 |
+
# 1. Check inputs. Raise error if not correct
|
| 765 |
+
self.check_inputs(
|
| 766 |
+
prompt,
|
| 767 |
+
height,
|
| 768 |
+
width,
|
| 769 |
+
negative_prompt=negative_prompt,
|
| 770 |
+
prompt_embeds=prompt_embeds,
|
| 771 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 772 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 773 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 774 |
+
callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
|
| 775 |
+
max_sequence_length=max_sequence_length,
|
| 776 |
+
)
|
| 777 |
+
|
| 778 |
+
self._guidance_scale = guidance_scale
|
| 779 |
+
self._joint_attention_kwargs = joint_attention_kwargs
|
| 780 |
+
self._current_timestep = None
|
| 781 |
+
self._interrupt = False
|
| 782 |
+
|
| 783 |
+
# 2. Define call parameters
|
| 784 |
+
if prompt is not None and isinstance(prompt, str):
|
| 785 |
+
batch_size = 1
|
| 786 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 787 |
+
batch_size = len(prompt)
|
| 788 |
+
else:
|
| 789 |
+
batch_size = prompt_embeds.shape[0]
|
| 790 |
+
|
| 791 |
+
device = self._execution_device
|
| 792 |
+
|
| 793 |
+
lora_scale = (
|
| 794 |
+
self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
|
| 795 |
+
)
|
| 796 |
+
(
|
| 797 |
+
prompt_embeds,
|
| 798 |
+
text_ids,
|
| 799 |
+
prompt_attention_mask,
|
| 800 |
+
negative_prompt_embeds,
|
| 801 |
+
negative_text_ids,
|
| 802 |
+
negative_prompt_attention_mask,
|
| 803 |
+
) = self.encode_prompt(
|
| 804 |
+
prompt=prompt,
|
| 805 |
+
negative_prompt=negative_prompt,
|
| 806 |
+
prompt_embeds=prompt_embeds,
|
| 807 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 808 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 809 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 810 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 811 |
+
device=device,
|
| 812 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 813 |
+
max_sequence_length=max_sequence_length,
|
| 814 |
+
lora_scale=lora_scale,
|
| 815 |
+
)
|
| 816 |
+
|
| 817 |
+
# 4. Prepare latent variables
|
| 818 |
+
num_channels_latents = self.transformer.config.in_channels // 4
|
| 819 |
+
latents, latent_image_ids = self.prepare_latents(
|
| 820 |
+
batch_size * num_images_per_prompt,
|
| 821 |
+
num_channels_latents,
|
| 822 |
+
height,
|
| 823 |
+
width,
|
| 824 |
+
prompt_embeds.dtype,
|
| 825 |
+
device,
|
| 826 |
+
generator,
|
| 827 |
+
latents,
|
| 828 |
+
)
|
| 829 |
+
|
| 830 |
+
# 5. Prepare timesteps
|
| 831 |
+
sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
|
| 832 |
+
image_seq_len = latents.shape[1]
|
| 833 |
+
mu = calculate_shift(
|
| 834 |
+
image_seq_len,
|
| 835 |
+
self.scheduler.config.get("base_image_seq_len", 256),
|
| 836 |
+
self.scheduler.config.get("max_image_seq_len", 4096),
|
| 837 |
+
self.scheduler.config.get("base_shift", 0.5),
|
| 838 |
+
self.scheduler.config.get("max_shift", 1.15),
|
| 839 |
+
)
|
| 840 |
+
|
| 841 |
+
attention_mask = self._prepare_attention_mask(
|
| 842 |
+
batch_size=latents.shape[0],
|
| 843 |
+
sequence_length=image_seq_len,
|
| 844 |
+
dtype=latents.dtype,
|
| 845 |
+
attention_mask=prompt_attention_mask,
|
| 846 |
+
)
|
| 847 |
+
negative_attention_mask = self._prepare_attention_mask(
|
| 848 |
+
batch_size=latents.shape[0],
|
| 849 |
+
sequence_length=image_seq_len,
|
| 850 |
+
dtype=latents.dtype,
|
| 851 |
+
attention_mask=negative_prompt_attention_mask,
|
| 852 |
+
)
|
| 853 |
+
|
| 854 |
+
timesteps, num_inference_steps = retrieve_timesteps(
|
| 855 |
+
self.scheduler,
|
| 856 |
+
num_inference_steps,
|
| 857 |
+
device,
|
| 858 |
+
sigmas=sigmas,
|
| 859 |
+
mu=mu,
|
| 860 |
+
)
|
| 861 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 862 |
+
self._num_timesteps = len(timesteps)
|
| 863 |
+
|
| 864 |
+
if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
|
| 865 |
+
negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
|
| 866 |
+
):
|
| 867 |
+
negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 868 |
+
negative_ip_adapter_image = [negative_ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
|
| 869 |
+
|
| 870 |
+
elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
|
| 871 |
+
negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
|
| 872 |
+
):
|
| 873 |
+
ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 874 |
+
ip_adapter_image = [ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
|
| 875 |
+
|
| 876 |
+
if self.joint_attention_kwargs is None:
|
| 877 |
+
self._joint_attention_kwargs = {}
|
| 878 |
+
|
| 879 |
+
image_embeds = None
|
| 880 |
+
negative_image_embeds = None
|
| 881 |
+
if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
|
| 882 |
+
image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 883 |
+
ip_adapter_image,
|
| 884 |
+
ip_adapter_image_embeds,
|
| 885 |
+
device,
|
| 886 |
+
batch_size * num_images_per_prompt,
|
| 887 |
+
)
|
| 888 |
+
if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
|
| 889 |
+
negative_image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 890 |
+
negative_ip_adapter_image,
|
| 891 |
+
negative_ip_adapter_image_embeds,
|
| 892 |
+
device,
|
| 893 |
+
batch_size * num_images_per_prompt,
|
| 894 |
+
)
|
| 895 |
+
|
| 896 |
+
# 6. Denoising loop
|
| 897 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 898 |
+
for i, t in enumerate(timesteps):
|
| 899 |
+
if self.interrupt:
|
| 900 |
+
continue
|
| 901 |
+
|
| 902 |
+
self._current_timestep = t
|
| 903 |
+
if image_embeds is not None:
|
| 904 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
|
| 905 |
+
|
| 906 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 907 |
+
timestep = t.expand(latents.shape[0]).to(latents.dtype)
|
| 908 |
+
|
| 909 |
+
noise_pred = self.transformer(
|
| 910 |
+
hidden_states=latents,
|
| 911 |
+
timestep=timestep / 1000,
|
| 912 |
+
encoder_hidden_states=prompt_embeds,
|
| 913 |
+
txt_ids=text_ids,
|
| 914 |
+
img_ids=latent_image_ids,
|
| 915 |
+
attention_mask=attention_mask,
|
| 916 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 917 |
+
return_dict=False,
|
| 918 |
+
)[0]
|
| 919 |
+
|
| 920 |
+
if self.do_classifier_free_guidance:
|
| 921 |
+
if negative_image_embeds is not None:
|
| 922 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
|
| 923 |
+
neg_noise_pred = self.transformer(
|
| 924 |
+
hidden_states=latents,
|
| 925 |
+
timestep=timestep / 1000,
|
| 926 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 927 |
+
txt_ids=negative_text_ids,
|
| 928 |
+
img_ids=latent_image_ids,
|
| 929 |
+
attention_mask=negative_attention_mask,
|
| 930 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 931 |
+
return_dict=False,
|
| 932 |
+
)[0]
|
| 933 |
+
noise_pred = neg_noise_pred + guidance_scale * (noise_pred - neg_noise_pred)
|
| 934 |
+
|
| 935 |
+
# compute the previous noisy sample x_t -> x_t-1
|
| 936 |
+
latents_dtype = latents.dtype
|
| 937 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 938 |
+
|
| 939 |
+
if latents.dtype != latents_dtype:
|
| 940 |
+
if torch.backends.mps.is_available():
|
| 941 |
+
# some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
|
| 942 |
+
latents = latents.to(latents_dtype)
|
| 943 |
+
|
| 944 |
+
if callback_on_step_end is not None:
|
| 945 |
+
callback_kwargs = {}
|
| 946 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 947 |
+
callback_kwargs[k] = locals()[k]
|
| 948 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 949 |
+
|
| 950 |
+
latents = callback_outputs.pop("latents", latents)
|
| 951 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 952 |
+
|
| 953 |
+
# call the callback, if provided
|
| 954 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 955 |
+
progress_bar.update()
|
| 956 |
+
|
| 957 |
+
if XLA_AVAILABLE:
|
| 958 |
+
xm.mark_step()
|
| 959 |
+
|
| 960 |
+
self._current_timestep = None
|
| 961 |
+
|
| 962 |
+
if output_type == "latent":
|
| 963 |
+
image = latents
|
| 964 |
+
else:
|
| 965 |
+
latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
|
| 966 |
+
latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
|
| 967 |
+
image = self.vae.decode(latents, return_dict=False)[0]
|
| 968 |
+
image = self.image_processor.postprocess(image, output_type=output_type)
|
| 969 |
+
|
| 970 |
+
# Offload all models
|
| 971 |
+
self.maybe_free_model_hooks()
|
| 972 |
+
|
| 973 |
+
if not return_dict:
|
| 974 |
+
return (image,)
|
| 975 |
+
|
| 976 |
+
return ChromaPipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_img2img.py
ADDED
|
@@ -0,0 +1,1060 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from typing import Any, Callable
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, T5EncoderModel, T5TokenizerFast
|
| 21 |
+
|
| 22 |
+
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
| 23 |
+
from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
|
| 24 |
+
from ...models import AutoencoderKL, ChromaTransformer2DModel
|
| 25 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 26 |
+
from ...utils import (
|
| 27 |
+
USE_PEFT_BACKEND,
|
| 28 |
+
deprecate,
|
| 29 |
+
is_torch_xla_available,
|
| 30 |
+
logging,
|
| 31 |
+
replace_example_docstring,
|
| 32 |
+
scale_lora_layers,
|
| 33 |
+
unscale_lora_layers,
|
| 34 |
+
)
|
| 35 |
+
from ...utils.torch_utils import randn_tensor
|
| 36 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 37 |
+
from .pipeline_output import ChromaPipelineOutput
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if is_torch_xla_available():
|
| 41 |
+
import torch_xla.core.xla_model as xm
|
| 42 |
+
|
| 43 |
+
XLA_AVAILABLE = True
|
| 44 |
+
else:
|
| 45 |
+
XLA_AVAILABLE = False
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 49 |
+
|
| 50 |
+
EXAMPLE_DOC_STRING = """
|
| 51 |
+
Examples:
|
| 52 |
+
```py
|
| 53 |
+
>>> import torch
|
| 54 |
+
>>> from diffusers import ChromaTransformer2DModel, ChromaImg2ImgPipeline
|
| 55 |
+
|
| 56 |
+
>>> model_id = "lodestones/Chroma1-HD"
|
| 57 |
+
>>> ckpt_path = "https://huggingface.co/lodestones/Chroma1-HD/blob/main/Chroma1-HD.safetensors"
|
| 58 |
+
>>> pipe = ChromaImg2ImgPipeline.from_pretrained(
|
| 59 |
+
... model_id,
|
| 60 |
+
... transformer=transformer,
|
| 61 |
+
... torch_dtype=torch.bfloat16,
|
| 62 |
+
... )
|
| 63 |
+
>>> pipe.enable_model_cpu_offload()
|
| 64 |
+
>>> init_image = load_image(
|
| 65 |
+
... "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
|
| 66 |
+
... )
|
| 67 |
+
>>> prompt = "a scenic fastasy landscape with a river and mountains in the background, vibrant colors, detailed, high resolution"
|
| 68 |
+
>>> negative_prompt = "low quality, ugly, unfinished, out of focus, deformed, disfigure, blurry, smudged, restricted palette, flat colors"
|
| 69 |
+
>>> image = pipe(prompt, image=init_image, negative_prompt=negative_prompt).images[0]
|
| 70 |
+
>>> image.save("chroma-img2img.png")
|
| 71 |
+
```
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
|
| 76 |
+
def calculate_shift(
|
| 77 |
+
image_seq_len,
|
| 78 |
+
base_seq_len: int = 256,
|
| 79 |
+
max_seq_len: int = 4096,
|
| 80 |
+
base_shift: float = 0.5,
|
| 81 |
+
max_shift: float = 1.15,
|
| 82 |
+
):
|
| 83 |
+
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
| 84 |
+
b = base_shift - m * base_seq_len
|
| 85 |
+
mu = image_seq_len * m + b
|
| 86 |
+
return mu
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
| 90 |
+
def retrieve_latents(
|
| 91 |
+
encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
|
| 92 |
+
):
|
| 93 |
+
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
| 94 |
+
return encoder_output.latent_dist.sample(generator)
|
| 95 |
+
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
| 96 |
+
return encoder_output.latent_dist.mode()
|
| 97 |
+
elif hasattr(encoder_output, "latents"):
|
| 98 |
+
return encoder_output.latents
|
| 99 |
+
else:
|
| 100 |
+
raise AttributeError("Could not access latents of provided encoder_output")
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 104 |
+
def retrieve_timesteps(
|
| 105 |
+
scheduler,
|
| 106 |
+
num_inference_steps: int | None = None,
|
| 107 |
+
device: str | torch.device | None = None,
|
| 108 |
+
timesteps: list[int] | None = None,
|
| 109 |
+
sigmas: list[float] | None = None,
|
| 110 |
+
**kwargs,
|
| 111 |
+
):
|
| 112 |
+
r"""
|
| 113 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 114 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 115 |
+
|
| 116 |
+
Args:
|
| 117 |
+
scheduler (`SchedulerMixin`):
|
| 118 |
+
The scheduler to get timesteps from.
|
| 119 |
+
num_inference_steps (`int`):
|
| 120 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 121 |
+
must be `None`.
|
| 122 |
+
device (`str` or `torch.device`, *optional*):
|
| 123 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 124 |
+
timesteps (`list[int]`, *optional*):
|
| 125 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 126 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 127 |
+
sigmas (`list[float]`, *optional*):
|
| 128 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 129 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 130 |
+
|
| 131 |
+
Returns:
|
| 132 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 133 |
+
second element is the number of inference steps.
|
| 134 |
+
"""
|
| 135 |
+
if timesteps is not None and sigmas is not None:
|
| 136 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 137 |
+
if timesteps is not None:
|
| 138 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 139 |
+
if not accepts_timesteps:
|
| 140 |
+
raise ValueError(
|
| 141 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 142 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 143 |
+
)
|
| 144 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 145 |
+
timesteps = scheduler.timesteps
|
| 146 |
+
num_inference_steps = len(timesteps)
|
| 147 |
+
elif sigmas is not None:
|
| 148 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 149 |
+
if not accept_sigmas:
|
| 150 |
+
raise ValueError(
|
| 151 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 152 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 153 |
+
)
|
| 154 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 155 |
+
timesteps = scheduler.timesteps
|
| 156 |
+
num_inference_steps = len(timesteps)
|
| 157 |
+
else:
|
| 158 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 159 |
+
timesteps = scheduler.timesteps
|
| 160 |
+
return timesteps, num_inference_steps
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
class ChromaImg2ImgPipeline(
|
| 164 |
+
DiffusionPipeline,
|
| 165 |
+
FluxLoraLoaderMixin,
|
| 166 |
+
FromSingleFileMixin,
|
| 167 |
+
TextualInversionLoaderMixin,
|
| 168 |
+
FluxIPAdapterMixin,
|
| 169 |
+
):
|
| 170 |
+
r"""
|
| 171 |
+
The Chroma pipeline for image-to-image generation.
|
| 172 |
+
|
| 173 |
+
Reference: https://huggingface.co/lodestones/Chroma1-HD/
|
| 174 |
+
|
| 175 |
+
Args:
|
| 176 |
+
transformer ([`ChromaTransformer2DModel`]):
|
| 177 |
+
Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
|
| 178 |
+
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
| 179 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 180 |
+
vae ([`AutoencoderKL`]):
|
| 181 |
+
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representation
|
| 182 |
+
text_encoder ([`T5EncoderModel`]):
|
| 183 |
+
[T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
|
| 184 |
+
the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
|
| 185 |
+
tokenizer (`T5TokenizerFast`):
|
| 186 |
+
Second Tokenizer of class
|
| 187 |
+
[T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
model_cpu_offload_seq = "text_encoder->image_encoder->transformer->vae"
|
| 191 |
+
_optional_components = ["image_encoder", "feature_extractor"]
|
| 192 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds"]
|
| 193 |
+
|
| 194 |
+
def __init__(
|
| 195 |
+
self,
|
| 196 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 197 |
+
vae: AutoencoderKL,
|
| 198 |
+
text_encoder: T5EncoderModel,
|
| 199 |
+
tokenizer: T5TokenizerFast,
|
| 200 |
+
transformer: ChromaTransformer2DModel,
|
| 201 |
+
image_encoder: CLIPVisionModelWithProjection = None,
|
| 202 |
+
feature_extractor: CLIPImageProcessor = None,
|
| 203 |
+
):
|
| 204 |
+
super().__init__()
|
| 205 |
+
|
| 206 |
+
self.register_modules(
|
| 207 |
+
vae=vae,
|
| 208 |
+
text_encoder=text_encoder,
|
| 209 |
+
tokenizer=tokenizer,
|
| 210 |
+
transformer=transformer,
|
| 211 |
+
scheduler=scheduler,
|
| 212 |
+
image_encoder=image_encoder,
|
| 213 |
+
feature_extractor=feature_extractor,
|
| 214 |
+
)
|
| 215 |
+
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
|
| 216 |
+
self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
|
| 217 |
+
|
| 218 |
+
# Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
|
| 219 |
+
# by the patch size. So the vae scale factor is multiplied by the patch size to account for this
|
| 220 |
+
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
|
| 221 |
+
self.default_sample_size = 128
|
| 222 |
+
|
| 223 |
+
def _get_t5_prompt_embeds(
|
| 224 |
+
self,
|
| 225 |
+
prompt: str | list[str] = None,
|
| 226 |
+
num_images_per_prompt: int = 1,
|
| 227 |
+
max_sequence_length: int = 512,
|
| 228 |
+
device: torch.device | None = None,
|
| 229 |
+
dtype: torch.dtype | None = None,
|
| 230 |
+
):
|
| 231 |
+
device = device or self._execution_device
|
| 232 |
+
dtype = dtype or self.text_encoder.dtype
|
| 233 |
+
|
| 234 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 235 |
+
batch_size = len(prompt)
|
| 236 |
+
|
| 237 |
+
if isinstance(self, TextualInversionLoaderMixin):
|
| 238 |
+
prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
|
| 239 |
+
|
| 240 |
+
text_inputs = self.tokenizer(
|
| 241 |
+
prompt,
|
| 242 |
+
padding="max_length",
|
| 243 |
+
max_length=max_sequence_length,
|
| 244 |
+
truncation=True,
|
| 245 |
+
return_length=False,
|
| 246 |
+
return_overflowing_tokens=False,
|
| 247 |
+
return_tensors="pt",
|
| 248 |
+
)
|
| 249 |
+
text_input_ids = text_inputs.input_ids
|
| 250 |
+
tokenizer_mask = text_inputs.attention_mask
|
| 251 |
+
|
| 252 |
+
tokenizer_mask_device = tokenizer_mask.to(device)
|
| 253 |
+
|
| 254 |
+
prompt_embeds = self.text_encoder(
|
| 255 |
+
text_input_ids.to(device),
|
| 256 |
+
output_hidden_states=False,
|
| 257 |
+
attention_mask=tokenizer_mask_device,
|
| 258 |
+
)[0]
|
| 259 |
+
|
| 260 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 261 |
+
|
| 262 |
+
seq_lengths = tokenizer_mask_device.sum(dim=1)
|
| 263 |
+
mask_indices = torch.arange(tokenizer_mask_device.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
|
| 264 |
+
attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
|
| 265 |
+
|
| 266 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 267 |
+
|
| 268 |
+
# duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
|
| 269 |
+
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
| 270 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 271 |
+
|
| 272 |
+
attention_mask = attention_mask.repeat(1, num_images_per_prompt)
|
| 273 |
+
attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
|
| 274 |
+
|
| 275 |
+
return prompt_embeds, attention_mask
|
| 276 |
+
|
| 277 |
+
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_inpaint.StableDiffusion3InpaintPipeline._encode_vae_image
|
| 278 |
+
def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
|
| 279 |
+
if isinstance(generator, list):
|
| 280 |
+
image_latents = [
|
| 281 |
+
retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
|
| 282 |
+
for i in range(image.shape[0])
|
| 283 |
+
]
|
| 284 |
+
image_latents = torch.cat(image_latents, dim=0)
|
| 285 |
+
else:
|
| 286 |
+
image_latents = retrieve_latents(self.vae.encode(image), generator=generator)
|
| 287 |
+
|
| 288 |
+
image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
|
| 289 |
+
|
| 290 |
+
return image_latents
|
| 291 |
+
|
| 292 |
+
def encode_prompt(
|
| 293 |
+
self,
|
| 294 |
+
prompt: str | list[str],
|
| 295 |
+
negative_prompt: str | list[str] = None,
|
| 296 |
+
device: torch.device | None = None,
|
| 297 |
+
num_images_per_prompt: int = 1,
|
| 298 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 299 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 300 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 301 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 302 |
+
do_classifier_free_guidance: bool = True,
|
| 303 |
+
max_sequence_length: int = 512,
|
| 304 |
+
lora_scale: float | None = None,
|
| 305 |
+
):
|
| 306 |
+
r"""
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 310 |
+
prompt to be encoded
|
| 311 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 312 |
+
The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
|
| 313 |
+
instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
|
| 314 |
+
device: (`torch.device`):
|
| 315 |
+
torch device
|
| 316 |
+
num_images_per_prompt (`int`):
|
| 317 |
+
number of images that should be generated per prompt
|
| 318 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 319 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 320 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 321 |
+
lora_scale (`float`, *optional*):
|
| 322 |
+
A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
|
| 323 |
+
"""
|
| 324 |
+
device = device or self._execution_device
|
| 325 |
+
|
| 326 |
+
# set lora scale so that monkey patched LoRA
|
| 327 |
+
# function of text encoder can correctly access it
|
| 328 |
+
if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
|
| 329 |
+
self._lora_scale = lora_scale
|
| 330 |
+
|
| 331 |
+
# dynamically adjust the LoRA scale
|
| 332 |
+
if self.text_encoder is not None and USE_PEFT_BACKEND:
|
| 333 |
+
scale_lora_layers(self.text_encoder, lora_scale)
|
| 334 |
+
|
| 335 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 336 |
+
|
| 337 |
+
if prompt is not None:
|
| 338 |
+
batch_size = len(prompt)
|
| 339 |
+
else:
|
| 340 |
+
batch_size = prompt_embeds.shape[0]
|
| 341 |
+
|
| 342 |
+
if prompt_embeds is None:
|
| 343 |
+
prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 344 |
+
prompt=prompt,
|
| 345 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 346 |
+
max_sequence_length=max_sequence_length,
|
| 347 |
+
device=device,
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
|
| 351 |
+
text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
|
| 352 |
+
negative_text_ids = None
|
| 353 |
+
|
| 354 |
+
if do_classifier_free_guidance:
|
| 355 |
+
if negative_prompt_embeds is None:
|
| 356 |
+
negative_prompt = negative_prompt or ""
|
| 357 |
+
negative_prompt = (
|
| 358 |
+
batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 362 |
+
raise TypeError(
|
| 363 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 364 |
+
f" {type(prompt)}."
|
| 365 |
+
)
|
| 366 |
+
elif batch_size != len(negative_prompt):
|
| 367 |
+
raise ValueError(
|
| 368 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 369 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 370 |
+
" the batch size of `prompt`."
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 374 |
+
prompt=negative_prompt,
|
| 375 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 376 |
+
max_sequence_length=max_sequence_length,
|
| 377 |
+
device=device,
|
| 378 |
+
)
|
| 379 |
+
|
| 380 |
+
negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
|
| 381 |
+
|
| 382 |
+
if self.text_encoder is not None:
|
| 383 |
+
if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
|
| 384 |
+
# Retrieve the original scale by scaling back the LoRA layers
|
| 385 |
+
unscale_lora_layers(self.text_encoder, lora_scale)
|
| 386 |
+
|
| 387 |
+
return (
|
| 388 |
+
prompt_embeds,
|
| 389 |
+
text_ids,
|
| 390 |
+
prompt_attention_mask,
|
| 391 |
+
negative_prompt_embeds,
|
| 392 |
+
negative_text_ids,
|
| 393 |
+
negative_prompt_attention_mask,
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
|
| 397 |
+
def encode_image(self, image, device, num_images_per_prompt):
|
| 398 |
+
dtype = next(self.image_encoder.parameters()).dtype
|
| 399 |
+
|
| 400 |
+
if not isinstance(image, torch.Tensor):
|
| 401 |
+
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
| 402 |
+
|
| 403 |
+
image = image.to(device=device, dtype=dtype)
|
| 404 |
+
image_embeds = self.image_encoder(image).image_embeds
|
| 405 |
+
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
| 406 |
+
return image_embeds
|
| 407 |
+
|
| 408 |
+
def prepare_ip_adapter_image_embeds(
|
| 409 |
+
self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
|
| 410 |
+
):
|
| 411 |
+
device = device or self._execution_device
|
| 412 |
+
|
| 413 |
+
image_embeds = []
|
| 414 |
+
if ip_adapter_image_embeds is None:
|
| 415 |
+
if not isinstance(ip_adapter_image, list):
|
| 416 |
+
ip_adapter_image = [ip_adapter_image]
|
| 417 |
+
|
| 418 |
+
if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 419 |
+
raise ValueError(
|
| 420 |
+
f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
for single_ip_adapter_image in ip_adapter_image:
|
| 424 |
+
single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
|
| 425 |
+
image_embeds.append(single_image_embeds[None, :])
|
| 426 |
+
else:
|
| 427 |
+
if not isinstance(ip_adapter_image_embeds, list):
|
| 428 |
+
ip_adapter_image_embeds = [ip_adapter_image_embeds]
|
| 429 |
+
|
| 430 |
+
if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 431 |
+
raise ValueError(
|
| 432 |
+
f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
for single_image_embeds in ip_adapter_image_embeds:
|
| 436 |
+
image_embeds.append(single_image_embeds)
|
| 437 |
+
|
| 438 |
+
ip_adapter_image_embeds = []
|
| 439 |
+
for single_image_embeds in image_embeds:
|
| 440 |
+
single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
|
| 441 |
+
single_image_embeds = single_image_embeds.to(device=device)
|
| 442 |
+
ip_adapter_image_embeds.append(single_image_embeds)
|
| 443 |
+
|
| 444 |
+
return ip_adapter_image_embeds
|
| 445 |
+
|
| 446 |
+
def check_inputs(
|
| 447 |
+
self,
|
| 448 |
+
prompt,
|
| 449 |
+
height,
|
| 450 |
+
width,
|
| 451 |
+
strength,
|
| 452 |
+
negative_prompt=None,
|
| 453 |
+
prompt_embeds=None,
|
| 454 |
+
negative_prompt_embeds=None,
|
| 455 |
+
prompt_attention_mask=None,
|
| 456 |
+
negative_prompt_attention_mask=None,
|
| 457 |
+
callback_on_step_end_tensor_inputs=None,
|
| 458 |
+
max_sequence_length=None,
|
| 459 |
+
):
|
| 460 |
+
if strength < 0 or strength > 1:
|
| 461 |
+
raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
|
| 462 |
+
|
| 463 |
+
if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
|
| 464 |
+
logger.warning(
|
| 465 |
+
f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
|
| 466 |
+
)
|
| 467 |
+
|
| 468 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 469 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 470 |
+
):
|
| 471 |
+
raise ValueError(
|
| 472 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 473 |
+
)
|
| 474 |
+
|
| 475 |
+
if prompt is not None and prompt_embeds is not None:
|
| 476 |
+
raise ValueError(
|
| 477 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 478 |
+
" only forward one of the two."
|
| 479 |
+
)
|
| 480 |
+
elif prompt is None and prompt_embeds is None:
|
| 481 |
+
raise ValueError(
|
| 482 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 483 |
+
)
|
| 484 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 485 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 486 |
+
|
| 487 |
+
if negative_prompt is not None and negative_prompt_embeds is not None:
|
| 488 |
+
raise ValueError(
|
| 489 |
+
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
|
| 490 |
+
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
if prompt_embeds is not None and prompt_attention_mask is None:
|
| 494 |
+
raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
|
| 495 |
+
|
| 496 |
+
if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
|
| 497 |
+
raise ValueError(
|
| 498 |
+
"Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
if max_sequence_length is not None and max_sequence_length > 512:
|
| 502 |
+
raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
|
| 503 |
+
|
| 504 |
+
@staticmethod
|
| 505 |
+
def _prepare_latent_image_ids(height, width, device, dtype):
|
| 506 |
+
latent_image_ids = torch.zeros(height, width, 3)
|
| 507 |
+
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
|
| 508 |
+
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
|
| 509 |
+
|
| 510 |
+
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
| 511 |
+
|
| 512 |
+
latent_image_ids = latent_image_ids.reshape(
|
| 513 |
+
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
return latent_image_ids.to(device=device, dtype=dtype)
|
| 517 |
+
|
| 518 |
+
@staticmethod
|
| 519 |
+
def _pack_latents(latents, batch_size, num_channels_latents, height, width):
|
| 520 |
+
latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
|
| 521 |
+
latents = latents.permute(0, 2, 4, 1, 3, 5)
|
| 522 |
+
latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
|
| 523 |
+
|
| 524 |
+
return latents
|
| 525 |
+
|
| 526 |
+
@staticmethod
|
| 527 |
+
def _unpack_latents(latents, height, width, vae_scale_factor):
|
| 528 |
+
batch_size, num_patches, channels = latents.shape
|
| 529 |
+
|
| 530 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 531 |
+
# latent height and width to be divisible by 2.
|
| 532 |
+
height = 2 * (int(height) // (vae_scale_factor * 2))
|
| 533 |
+
width = 2 * (int(width) // (vae_scale_factor * 2))
|
| 534 |
+
|
| 535 |
+
latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
|
| 536 |
+
latents = latents.permute(0, 3, 1, 4, 2, 5)
|
| 537 |
+
|
| 538 |
+
latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
|
| 539 |
+
|
| 540 |
+
return latents
|
| 541 |
+
|
| 542 |
+
def enable_vae_slicing(self):
|
| 543 |
+
r"""
|
| 544 |
+
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
| 545 |
+
compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
|
| 546 |
+
"""
|
| 547 |
+
depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
|
| 548 |
+
deprecate(
|
| 549 |
+
"enable_vae_slicing",
|
| 550 |
+
"0.40.0",
|
| 551 |
+
depr_message,
|
| 552 |
+
)
|
| 553 |
+
self.vae.enable_slicing()
|
| 554 |
+
|
| 555 |
+
def disable_vae_slicing(self):
|
| 556 |
+
r"""
|
| 557 |
+
Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
|
| 558 |
+
computing decoding in one step.
|
| 559 |
+
"""
|
| 560 |
+
depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
|
| 561 |
+
deprecate(
|
| 562 |
+
"disable_vae_slicing",
|
| 563 |
+
"0.40.0",
|
| 564 |
+
depr_message,
|
| 565 |
+
)
|
| 566 |
+
self.vae.disable_slicing()
|
| 567 |
+
|
| 568 |
+
def enable_vae_tiling(self):
|
| 569 |
+
r"""
|
| 570 |
+
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
| 571 |
+
compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
|
| 572 |
+
processing larger images.
|
| 573 |
+
"""
|
| 574 |
+
depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
|
| 575 |
+
deprecate(
|
| 576 |
+
"enable_vae_tiling",
|
| 577 |
+
"0.40.0",
|
| 578 |
+
depr_message,
|
| 579 |
+
)
|
| 580 |
+
self.vae.enable_tiling()
|
| 581 |
+
|
| 582 |
+
def disable_vae_tiling(self):
|
| 583 |
+
r"""
|
| 584 |
+
Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
|
| 585 |
+
computing decoding in one step.
|
| 586 |
+
"""
|
| 587 |
+
depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
|
| 588 |
+
deprecate(
|
| 589 |
+
"disable_vae_tiling",
|
| 590 |
+
"0.40.0",
|
| 591 |
+
depr_message,
|
| 592 |
+
)
|
| 593 |
+
self.vae.disable_tiling()
|
| 594 |
+
|
| 595 |
+
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_img2img.StableDiffusion3Img2ImgPipeline.get_timesteps
|
| 596 |
+
def get_timesteps(self, num_inference_steps, strength, device):
|
| 597 |
+
# get the original timestep using init_timestep
|
| 598 |
+
init_timestep = min(num_inference_steps * strength, num_inference_steps)
|
| 599 |
+
|
| 600 |
+
t_start = int(max(num_inference_steps - init_timestep, 0))
|
| 601 |
+
timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
|
| 602 |
+
if hasattr(self.scheduler, "set_begin_index"):
|
| 603 |
+
self.scheduler.set_begin_index(t_start * self.scheduler.order)
|
| 604 |
+
|
| 605 |
+
return timesteps, num_inference_steps - t_start
|
| 606 |
+
|
| 607 |
+
def prepare_latents(
|
| 608 |
+
self,
|
| 609 |
+
image,
|
| 610 |
+
timestep,
|
| 611 |
+
batch_size,
|
| 612 |
+
num_channels_latents,
|
| 613 |
+
height,
|
| 614 |
+
width,
|
| 615 |
+
dtype,
|
| 616 |
+
device,
|
| 617 |
+
generator,
|
| 618 |
+
latents=None,
|
| 619 |
+
):
|
| 620 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 621 |
+
raise ValueError(
|
| 622 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 623 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 624 |
+
)
|
| 625 |
+
|
| 626 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 627 |
+
# latent height and width to be divisible by 2.
|
| 628 |
+
height = 2 * (int(height) // (self.vae_scale_factor * 2))
|
| 629 |
+
width = 2 * (int(width) // (self.vae_scale_factor * 2))
|
| 630 |
+
shape = (batch_size, num_channels_latents, height, width)
|
| 631 |
+
latent_image_ids = self._prepare_latent_image_ids(height // 2, width // 2, device, dtype)
|
| 632 |
+
|
| 633 |
+
if latents is not None:
|
| 634 |
+
return latents.to(device=device, dtype=dtype), latent_image_ids
|
| 635 |
+
|
| 636 |
+
image = image.to(device=device, dtype=dtype)
|
| 637 |
+
if image.shape[1] != self.latent_channels:
|
| 638 |
+
image_latents = self._encode_vae_image(image=image, generator=generator)
|
| 639 |
+
else:
|
| 640 |
+
image_latents = image
|
| 641 |
+
if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
|
| 642 |
+
# expand init_latents for batch_size
|
| 643 |
+
additional_image_per_prompt = batch_size // image_latents.shape[0]
|
| 644 |
+
image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
|
| 645 |
+
elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
|
| 646 |
+
raise ValueError(
|
| 647 |
+
f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
|
| 648 |
+
)
|
| 649 |
+
else:
|
| 650 |
+
image_latents = torch.cat([image_latents], dim=0)
|
| 651 |
+
|
| 652 |
+
noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 653 |
+
latents = self.scheduler.scale_noise(image_latents, timestep, noise)
|
| 654 |
+
latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
|
| 655 |
+
return latents, latent_image_ids
|
| 656 |
+
|
| 657 |
+
def _prepare_attention_mask(
|
| 658 |
+
self,
|
| 659 |
+
batch_size,
|
| 660 |
+
sequence_length,
|
| 661 |
+
dtype,
|
| 662 |
+
attention_mask=None,
|
| 663 |
+
):
|
| 664 |
+
if attention_mask is None:
|
| 665 |
+
return attention_mask
|
| 666 |
+
|
| 667 |
+
# Extend the prompt attention mask to account for image tokens in the final sequence
|
| 668 |
+
attention_mask = torch.cat(
|
| 669 |
+
[attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device)],
|
| 670 |
+
dim=1,
|
| 671 |
+
)
|
| 672 |
+
attention_mask = attention_mask.to(dtype)
|
| 673 |
+
|
| 674 |
+
return attention_mask
|
| 675 |
+
|
| 676 |
+
@property
|
| 677 |
+
def guidance_scale(self):
|
| 678 |
+
return self._guidance_scale
|
| 679 |
+
|
| 680 |
+
@property
|
| 681 |
+
def joint_attention_kwargs(self):
|
| 682 |
+
return self._joint_attention_kwargs
|
| 683 |
+
|
| 684 |
+
@property
|
| 685 |
+
def do_classifier_free_guidance(self):
|
| 686 |
+
return self._guidance_scale > 1
|
| 687 |
+
|
| 688 |
+
@property
|
| 689 |
+
def num_timesteps(self):
|
| 690 |
+
return self._num_timesteps
|
| 691 |
+
|
| 692 |
+
@property
|
| 693 |
+
def current_timestep(self):
|
| 694 |
+
return self._current_timestep
|
| 695 |
+
|
| 696 |
+
@property
|
| 697 |
+
def interrupt(self):
|
| 698 |
+
return self._interrupt
|
| 699 |
+
|
| 700 |
+
@torch.no_grad()
|
| 701 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 702 |
+
def __call__(
|
| 703 |
+
self,
|
| 704 |
+
prompt: str | list[str] = None,
|
| 705 |
+
negative_prompt: str | list[str] = None,
|
| 706 |
+
image: PipelineImageInput = None,
|
| 707 |
+
height: int | None = None,
|
| 708 |
+
width: int | None = None,
|
| 709 |
+
num_inference_steps: int = 35,
|
| 710 |
+
sigmas: list[float] | None = None,
|
| 711 |
+
guidance_scale: float = 5.0,
|
| 712 |
+
strength: float = 0.9,
|
| 713 |
+
num_images_per_prompt: int | None = 1,
|
| 714 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 715 |
+
latents: torch.Tensor | None = None,
|
| 716 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 717 |
+
ip_adapter_image: PipelineImageInput | None = None,
|
| 718 |
+
ip_adapter_image_embeds: list[torch.Tensor] | None = None,
|
| 719 |
+
negative_ip_adapter_image: PipelineImageInput | None = None,
|
| 720 |
+
negative_ip_adapter_image_embeds: list[torch.Tensor] | None = None,
|
| 721 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 722 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 723 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 724 |
+
output_type: str | None = "pil",
|
| 725 |
+
return_dict: bool = True,
|
| 726 |
+
joint_attention_kwargs: dict[str, Any] | None = None,
|
| 727 |
+
callback_on_step_end: Callable[[int, int], None] | None = None,
|
| 728 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 729 |
+
max_sequence_length: int = 512,
|
| 730 |
+
):
|
| 731 |
+
r"""
|
| 732 |
+
Function invoked when calling the pipeline for generation.
|
| 733 |
+
|
| 734 |
+
Args:
|
| 735 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 736 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 737 |
+
instead.
|
| 738 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 739 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 740 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 741 |
+
not greater than `1`).
|
| 742 |
+
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 743 |
+
The height in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 744 |
+
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 745 |
+
The width in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 746 |
+
num_inference_steps (`int`, *optional*, defaults to 35):
|
| 747 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 748 |
+
expense of slower inference.
|
| 749 |
+
sigmas (`list[float]`, *optional*):
|
| 750 |
+
Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
|
| 751 |
+
their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
|
| 752 |
+
will be used.
|
| 753 |
+
guidance_scale (`float`, *optional*, defaults to 3.5):
|
| 754 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 755 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 756 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 757 |
+
`guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
|
| 758 |
+
the text `prompt`, usually at the expense of lower image quality.
|
| 759 |
+
strength (`float, *optional*, defaults to 0.9):
|
| 760 |
+
Conceptually, indicates how much to transform the reference image. Must be between 0 and 1. image will
|
| 761 |
+
be used as a starting point, adding more noise to it the larger the strength. The number of denoising
|
| 762 |
+
steps depends on the amount of noise initially added. When strength is 1, added noise will be maximum
|
| 763 |
+
and the denoising process will run for the full number of iterations specified in num_inference_steps.
|
| 764 |
+
A value of 1, therefore, essentially ignores image.
|
| 765 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 766 |
+
The number of images to generate per prompt.
|
| 767 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 768 |
+
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
|
| 769 |
+
to make generation deterministic.
|
| 770 |
+
latents (`torch.Tensor`, *optional*):
|
| 771 |
+
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
|
| 772 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 773 |
+
tensor will be generated by sampling using the supplied random `generator`.
|
| 774 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 775 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 776 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 777 |
+
ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 778 |
+
ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
|
| 779 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 780 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 781 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 782 |
+
negative_ip_adapter_image:
|
| 783 |
+
(`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 784 |
+
negative_ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
|
| 785 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 786 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 787 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 788 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 789 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 790 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 791 |
+
argument.
|
| 792 |
+
prompt_attention_mask (torch.Tensor, *optional*):
|
| 793 |
+
Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
|
| 794 |
+
Chroma requires a single padding token remain unmasked. Please refer to
|
| 795 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 796 |
+
negative_prompt_attention_mask (torch.Tensor, *optional*):
|
| 797 |
+
Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
|
| 798 |
+
prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
|
| 799 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 800 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 801 |
+
The output format of the generate image. Choose between
|
| 802 |
+
[PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
|
| 803 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 804 |
+
Whether or not to return a [`~pipelines.flux.ChromaPipelineOutput`] instead of a plain tuple.
|
| 805 |
+
joint_attention_kwargs (`dict`, *optional*):
|
| 806 |
+
A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
|
| 807 |
+
`self.processor` in
|
| 808 |
+
[diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
| 809 |
+
callback_on_step_end (`Callable`, *optional*):
|
| 810 |
+
A function that calls at the end of each denoising steps during the inference. The function is called
|
| 811 |
+
with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
|
| 812 |
+
callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
|
| 813 |
+
`callback_on_step_end_tensor_inputs`.
|
| 814 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 815 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 816 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 817 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 818 |
+
max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
|
| 819 |
+
|
| 820 |
+
Examples:
|
| 821 |
+
|
| 822 |
+
Returns:
|
| 823 |
+
[`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
|
| 824 |
+
`return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
|
| 825 |
+
generated images.
|
| 826 |
+
"""
|
| 827 |
+
|
| 828 |
+
height = height or self.default_sample_size * self.vae_scale_factor
|
| 829 |
+
width = width or self.default_sample_size * self.vae_scale_factor
|
| 830 |
+
|
| 831 |
+
# 1. Check inputs. Raise error if not correct
|
| 832 |
+
self.check_inputs(
|
| 833 |
+
prompt,
|
| 834 |
+
height,
|
| 835 |
+
width,
|
| 836 |
+
strength,
|
| 837 |
+
negative_prompt=negative_prompt,
|
| 838 |
+
prompt_embeds=prompt_embeds,
|
| 839 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 840 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 841 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 842 |
+
callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
|
| 843 |
+
max_sequence_length=max_sequence_length,
|
| 844 |
+
)
|
| 845 |
+
|
| 846 |
+
self._guidance_scale = guidance_scale
|
| 847 |
+
self._joint_attention_kwargs = joint_attention_kwargs
|
| 848 |
+
self._current_timestep = None
|
| 849 |
+
self._interrupt = False
|
| 850 |
+
|
| 851 |
+
# 2. Preprocess image
|
| 852 |
+
init_image = self.image_processor.preprocess(image, height=height, width=width)
|
| 853 |
+
init_image = init_image.to(dtype=torch.float32)
|
| 854 |
+
|
| 855 |
+
# 3. Define call parameters
|
| 856 |
+
if prompt is not None and isinstance(prompt, str):
|
| 857 |
+
batch_size = 1
|
| 858 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 859 |
+
batch_size = len(prompt)
|
| 860 |
+
else:
|
| 861 |
+
batch_size = prompt_embeds.shape[0]
|
| 862 |
+
|
| 863 |
+
device = self._execution_device
|
| 864 |
+
lora_scale = (
|
| 865 |
+
self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
|
| 866 |
+
)
|
| 867 |
+
|
| 868 |
+
(
|
| 869 |
+
prompt_embeds,
|
| 870 |
+
text_ids,
|
| 871 |
+
prompt_attention_mask,
|
| 872 |
+
negative_prompt_embeds,
|
| 873 |
+
negative_text_ids,
|
| 874 |
+
negative_prompt_attention_mask,
|
| 875 |
+
) = self.encode_prompt(
|
| 876 |
+
prompt=prompt,
|
| 877 |
+
negative_prompt=negative_prompt,
|
| 878 |
+
prompt_embeds=prompt_embeds,
|
| 879 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 880 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 881 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 882 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 883 |
+
device=device,
|
| 884 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 885 |
+
max_sequence_length=max_sequence_length,
|
| 886 |
+
lora_scale=lora_scale,
|
| 887 |
+
)
|
| 888 |
+
|
| 889 |
+
# 4. Prepare timesteps
|
| 890 |
+
sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
|
| 891 |
+
image_seq_len = (int(height) // self.vae_scale_factor // 2) * (int(width) // self.vae_scale_factor // 2)
|
| 892 |
+
mu = calculate_shift(
|
| 893 |
+
image_seq_len,
|
| 894 |
+
self.scheduler.config.get("base_image_seq_len", 256),
|
| 895 |
+
self.scheduler.config.get("max_image_seq_len", 4096),
|
| 896 |
+
self.scheduler.config.get("base_shift", 0.5),
|
| 897 |
+
self.scheduler.config.get("max_shift", 1.15),
|
| 898 |
+
)
|
| 899 |
+
timesteps, num_inference_steps = retrieve_timesteps(
|
| 900 |
+
self.scheduler,
|
| 901 |
+
num_inference_steps,
|
| 902 |
+
device,
|
| 903 |
+
sigmas=sigmas,
|
| 904 |
+
mu=mu,
|
| 905 |
+
)
|
| 906 |
+
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
|
| 907 |
+
|
| 908 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 909 |
+
self._num_timesteps = len(timesteps)
|
| 910 |
+
|
| 911 |
+
if num_inference_steps < 1:
|
| 912 |
+
raise ValueError(
|
| 913 |
+
f"After adjusting the num_inference_steps by strength parameter: {strength}, the number of pipeline"
|
| 914 |
+
f"steps is {num_inference_steps} which is < 1 and not appropriate for this pipeline."
|
| 915 |
+
)
|
| 916 |
+
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
|
| 917 |
+
|
| 918 |
+
# 5. Prepare latent variables
|
| 919 |
+
num_channels_latents = self.transformer.config.in_channels // 4
|
| 920 |
+
latents, latent_image_ids = self.prepare_latents(
|
| 921 |
+
init_image,
|
| 922 |
+
latent_timestep,
|
| 923 |
+
batch_size * num_images_per_prompt,
|
| 924 |
+
num_channels_latents,
|
| 925 |
+
height,
|
| 926 |
+
width,
|
| 927 |
+
prompt_embeds.dtype,
|
| 928 |
+
device,
|
| 929 |
+
generator,
|
| 930 |
+
latents,
|
| 931 |
+
)
|
| 932 |
+
|
| 933 |
+
attention_mask = self._prepare_attention_mask(
|
| 934 |
+
batch_size=latents.shape[0],
|
| 935 |
+
sequence_length=image_seq_len,
|
| 936 |
+
dtype=latents.dtype,
|
| 937 |
+
attention_mask=prompt_attention_mask,
|
| 938 |
+
)
|
| 939 |
+
negative_attention_mask = self._prepare_attention_mask(
|
| 940 |
+
batch_size=latents.shape[0],
|
| 941 |
+
sequence_length=image_seq_len,
|
| 942 |
+
dtype=latents.dtype,
|
| 943 |
+
attention_mask=negative_prompt_attention_mask,
|
| 944 |
+
)
|
| 945 |
+
|
| 946 |
+
# 6. Prepare image embeddings
|
| 947 |
+
if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
|
| 948 |
+
negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
|
| 949 |
+
):
|
| 950 |
+
negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 951 |
+
negative_ip_adapter_image = [negative_ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
|
| 952 |
+
|
| 953 |
+
elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
|
| 954 |
+
negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
|
| 955 |
+
):
|
| 956 |
+
ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 957 |
+
ip_adapter_image = [ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
|
| 958 |
+
|
| 959 |
+
if self.joint_attention_kwargs is None:
|
| 960 |
+
self._joint_attention_kwargs = {}
|
| 961 |
+
|
| 962 |
+
image_embeds = None
|
| 963 |
+
negative_image_embeds = None
|
| 964 |
+
if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
|
| 965 |
+
image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 966 |
+
ip_adapter_image,
|
| 967 |
+
ip_adapter_image_embeds,
|
| 968 |
+
device,
|
| 969 |
+
batch_size * num_images_per_prompt,
|
| 970 |
+
)
|
| 971 |
+
if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
|
| 972 |
+
negative_image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 973 |
+
negative_ip_adapter_image,
|
| 974 |
+
negative_ip_adapter_image_embeds,
|
| 975 |
+
device,
|
| 976 |
+
batch_size * num_images_per_prompt,
|
| 977 |
+
)
|
| 978 |
+
|
| 979 |
+
# 6. Denoising loop
|
| 980 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 981 |
+
for i, t in enumerate(timesteps):
|
| 982 |
+
if self.interrupt:
|
| 983 |
+
continue
|
| 984 |
+
|
| 985 |
+
self._current_timestep = t
|
| 986 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 987 |
+
timestep = t.expand(latents.shape[0])
|
| 988 |
+
|
| 989 |
+
if image_embeds is not None:
|
| 990 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
|
| 991 |
+
|
| 992 |
+
noise_pred = self.transformer(
|
| 993 |
+
hidden_states=latents,
|
| 994 |
+
timestep=timestep / 1000,
|
| 995 |
+
encoder_hidden_states=prompt_embeds,
|
| 996 |
+
txt_ids=text_ids,
|
| 997 |
+
img_ids=latent_image_ids,
|
| 998 |
+
attention_mask=attention_mask,
|
| 999 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 1000 |
+
return_dict=False,
|
| 1001 |
+
)[0]
|
| 1002 |
+
|
| 1003 |
+
if self.do_classifier_free_guidance:
|
| 1004 |
+
if negative_image_embeds is not None:
|
| 1005 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
|
| 1006 |
+
|
| 1007 |
+
noise_pred_uncond = self.transformer(
|
| 1008 |
+
hidden_states=latents,
|
| 1009 |
+
timestep=timestep / 1000,
|
| 1010 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 1011 |
+
txt_ids=negative_text_ids,
|
| 1012 |
+
img_ids=latent_image_ids,
|
| 1013 |
+
attention_mask=negative_attention_mask,
|
| 1014 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 1015 |
+
return_dict=False,
|
| 1016 |
+
)[0]
|
| 1017 |
+
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
|
| 1018 |
+
|
| 1019 |
+
# compute the previous noisy sample x_t -> x_t-1
|
| 1020 |
+
latents_dtype = latents.dtype
|
| 1021 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 1022 |
+
|
| 1023 |
+
if latents.dtype != latents_dtype:
|
| 1024 |
+
if torch.backends.mps.is_available():
|
| 1025 |
+
# some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
|
| 1026 |
+
latents = latents.to(latents_dtype)
|
| 1027 |
+
|
| 1028 |
+
if callback_on_step_end is not None:
|
| 1029 |
+
callback_kwargs = {}
|
| 1030 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 1031 |
+
callback_kwargs[k] = locals()[k]
|
| 1032 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 1033 |
+
|
| 1034 |
+
latents = callback_outputs.pop("latents", latents)
|
| 1035 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 1036 |
+
|
| 1037 |
+
# call the callback, if provided
|
| 1038 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 1039 |
+
progress_bar.update()
|
| 1040 |
+
|
| 1041 |
+
if XLA_AVAILABLE:
|
| 1042 |
+
xm.mark_step()
|
| 1043 |
+
|
| 1044 |
+
self._current_timestep = None
|
| 1045 |
+
|
| 1046 |
+
if output_type == "latent":
|
| 1047 |
+
image = latents
|
| 1048 |
+
else:
|
| 1049 |
+
latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
|
| 1050 |
+
latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
|
| 1051 |
+
image = self.vae.decode(latents, return_dict=False)[0]
|
| 1052 |
+
image = self.image_processor.postprocess(image, output_type=output_type)
|
| 1053 |
+
|
| 1054 |
+
# Offload all models
|
| 1055 |
+
self.maybe_free_model_hooks()
|
| 1056 |
+
|
| 1057 |
+
if not return_dict:
|
| 1058 |
+
return (image,)
|
| 1059 |
+
|
| 1060 |
+
return ChromaPipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_inpainting.py
ADDED
|
@@ -0,0 +1,1184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ChromaInpaintPipeline implements a text-guided image inpainting pipeline for the lodestones/Chroma1-HD model, based on
|
| 3 |
+
the ChromaPipeline from Hugging Face Diffusers:contentReference[oaicite:0]{index=0} and the Stable Diffusion inpainting
|
| 4 |
+
approach:contentReference[oaicite:1]{index=1}.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
# Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
|
| 8 |
+
#
|
| 9 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 10 |
+
# you may not use this file except in compliance with the License.
|
| 11 |
+
# You may obtain a copy of the License at
|
| 12 |
+
#
|
| 13 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 14 |
+
#
|
| 15 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 16 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 17 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 18 |
+
# See the License for the specific language governing permissions and
|
| 19 |
+
# limitations under the License.
|
| 20 |
+
import inspect
|
| 21 |
+
from typing import Any, Callable, Dict, List, Optional, Union
|
| 22 |
+
|
| 23 |
+
import numpy as np
|
| 24 |
+
import PIL.Image
|
| 25 |
+
import torch
|
| 26 |
+
from transformers import (
|
| 27 |
+
CLIPImageProcessor,
|
| 28 |
+
CLIPVisionModelWithProjection,
|
| 29 |
+
T5EncoderModel,
|
| 30 |
+
T5TokenizerFast,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
from ...image_processor import PipelineImageInput, VaeImageProcessor
|
| 34 |
+
from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
|
| 35 |
+
from ...models.autoencoders import AutoencoderKL
|
| 36 |
+
from ...models.transformers import ChromaTransformer2DModel
|
| 37 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 38 |
+
from ...utils import (
|
| 39 |
+
USE_PEFT_BACKEND,
|
| 40 |
+
is_torch_xla_available,
|
| 41 |
+
logging,
|
| 42 |
+
replace_example_docstring,
|
| 43 |
+
scale_lora_layers,
|
| 44 |
+
unscale_lora_layers,
|
| 45 |
+
)
|
| 46 |
+
from ...utils.torch_utils import randn_tensor
|
| 47 |
+
from ..chroma.pipeline_output import ChromaPipelineOutput
|
| 48 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if is_torch_xla_available():
|
| 52 |
+
import torch_xla.core.xla_model as xm
|
| 53 |
+
|
| 54 |
+
XLA_AVAILABLE = True
|
| 55 |
+
else:
|
| 56 |
+
XLA_AVAILABLE = False
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 60 |
+
|
| 61 |
+
EXAMPLE_DOC_STRING = """
|
| 62 |
+
Examples:
|
| 63 |
+
```py
|
| 64 |
+
>>> import torch
|
| 65 |
+
>>> from diffusers import ChromaInpaintPipeline
|
| 66 |
+
>>> from diffusers.utils import load_image
|
| 67 |
+
|
| 68 |
+
>>> pipe = ChromaInpaintPipeline.from_pretrained("lodestones/Chroma1-HD", torch_dtype=torch.bfloat16)
|
| 69 |
+
>>> pipe.to("cuda")
|
| 70 |
+
>>> prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
|
| 71 |
+
>>> img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
|
| 72 |
+
>>> mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
|
| 73 |
+
>>> source = load_image(img_url)
|
| 74 |
+
>>> mask = load_image(mask_url)
|
| 75 |
+
>>> image = pipe(prompt=prompt, image=source, mask_image=mask).images[0]
|
| 76 |
+
>>> image.save("chroma_inpainting.png")
|
| 77 |
+
```
|
| 78 |
+
"""
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
|
| 82 |
+
def calculate_shift(
|
| 83 |
+
image_seq_len,
|
| 84 |
+
base_seq_len: int = 256,
|
| 85 |
+
max_seq_len: int = 4096,
|
| 86 |
+
base_shift: float = 0.5,
|
| 87 |
+
max_shift: float = 1.15,
|
| 88 |
+
):
|
| 89 |
+
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
| 90 |
+
b = base_shift - m * base_seq_len
|
| 91 |
+
mu = image_seq_len * m + b
|
| 92 |
+
return mu
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
| 96 |
+
def retrieve_latents(
|
| 97 |
+
encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
|
| 98 |
+
):
|
| 99 |
+
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
| 100 |
+
return encoder_output.latent_dist.sample(generator)
|
| 101 |
+
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
| 102 |
+
return encoder_output.latent_dist.mode()
|
| 103 |
+
elif hasattr(encoder_output, "latents"):
|
| 104 |
+
return encoder_output.latents
|
| 105 |
+
else:
|
| 106 |
+
raise AttributeError("Could not access latents of provided encoder_output")
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 110 |
+
def retrieve_timesteps(
|
| 111 |
+
scheduler,
|
| 112 |
+
num_inference_steps: int | None = None,
|
| 113 |
+
device: str | torch.device | None = None,
|
| 114 |
+
timesteps: list[int] | None = None,
|
| 115 |
+
sigmas: list[float] | None = None,
|
| 116 |
+
**kwargs,
|
| 117 |
+
):
|
| 118 |
+
r"""
|
| 119 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 120 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 121 |
+
|
| 122 |
+
Args:
|
| 123 |
+
scheduler (`SchedulerMixin`):
|
| 124 |
+
The scheduler to get timesteps from.
|
| 125 |
+
num_inference_steps (`int`):
|
| 126 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 127 |
+
must be `None`.
|
| 128 |
+
device (`str` or `torch.device`, *optional*):
|
| 129 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 130 |
+
timesteps (`list[int]`, *optional*):
|
| 131 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 132 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 133 |
+
sigmas (`list[float]`, *optional*):
|
| 134 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 135 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 136 |
+
|
| 137 |
+
Returns:
|
| 138 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 139 |
+
second element is the number of inference steps.
|
| 140 |
+
"""
|
| 141 |
+
if timesteps is not None and sigmas is not None:
|
| 142 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 143 |
+
if timesteps is not None:
|
| 144 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 145 |
+
if not accepts_timesteps:
|
| 146 |
+
raise ValueError(
|
| 147 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 148 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 149 |
+
)
|
| 150 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 151 |
+
timesteps = scheduler.timesteps
|
| 152 |
+
num_inference_steps = len(timesteps)
|
| 153 |
+
elif sigmas is not None:
|
| 154 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 155 |
+
if not accept_sigmas:
|
| 156 |
+
raise ValueError(
|
| 157 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 158 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 159 |
+
)
|
| 160 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 161 |
+
timesteps = scheduler.timesteps
|
| 162 |
+
num_inference_steps = len(timesteps)
|
| 163 |
+
else:
|
| 164 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 165 |
+
timesteps = scheduler.timesteps
|
| 166 |
+
return timesteps, num_inference_steps
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
class ChromaInpaintPipeline(
|
| 170 |
+
DiffusionPipeline, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin, FluxIPAdapterMixin
|
| 171 |
+
):
|
| 172 |
+
r"""
|
| 173 |
+
The Flux pipeline for image inpainting.
|
| 174 |
+
|
| 175 |
+
Reference: https://blackforestlabs.ai/announcing-black-forest-labs/
|
| 176 |
+
|
| 177 |
+
Args:
|
| 178 |
+
transformer ([`ChromaTransformer2DModel`]):
|
| 179 |
+
Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
|
| 180 |
+
scheduler ([`DDIMScheduler`]):
|
| 181 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 182 |
+
vae ([`AutoencoderKL`]):
|
| 183 |
+
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
| 184 |
+
text_encoder ([`CLIPTextModel`]):
|
| 185 |
+
[CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
|
| 186 |
+
the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
|
| 187 |
+
text_encoder_2 ([`T5EncoderModel`]):
|
| 188 |
+
[T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
|
| 189 |
+
the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
|
| 190 |
+
tokenizer (`CLIPTokenizer`):
|
| 191 |
+
Tokenizer of class
|
| 192 |
+
[CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
|
| 193 |
+
tokenizer_2 (`T5TokenizerFast`):
|
| 194 |
+
Second Tokenizer of class
|
| 195 |
+
[T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
|
| 196 |
+
"""
|
| 197 |
+
|
| 198 |
+
model_cpu_offload_seq = "text_encoder->text_encoder_2->image_encoder->transformer->vae"
|
| 199 |
+
_optional_components = ["image_encoder", "feature_extractor"]
|
| 200 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds"]
|
| 201 |
+
|
| 202 |
+
def __init__(
|
| 203 |
+
self,
|
| 204 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 205 |
+
vae: AutoencoderKL,
|
| 206 |
+
text_encoder: T5EncoderModel,
|
| 207 |
+
tokenizer: T5TokenizerFast,
|
| 208 |
+
transformer: ChromaTransformer2DModel,
|
| 209 |
+
image_encoder: CLIPVisionModelWithProjection = None,
|
| 210 |
+
feature_extractor: CLIPImageProcessor = None,
|
| 211 |
+
):
|
| 212 |
+
super().__init__()
|
| 213 |
+
|
| 214 |
+
self.register_modules(
|
| 215 |
+
vae=vae,
|
| 216 |
+
text_encoder=text_encoder,
|
| 217 |
+
tokenizer=tokenizer,
|
| 218 |
+
transformer=transformer,
|
| 219 |
+
scheduler=scheduler,
|
| 220 |
+
image_encoder=image_encoder,
|
| 221 |
+
feature_extractor=feature_extractor,
|
| 222 |
+
)
|
| 223 |
+
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
|
| 224 |
+
self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
|
| 225 |
+
|
| 226 |
+
# Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
|
| 227 |
+
# by the patch size. So the vae scale factor is multiplied by the patch size to account for this
|
| 228 |
+
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
|
| 229 |
+
self.default_sample_size = 128
|
| 230 |
+
|
| 231 |
+
self.mask_processor = VaeImageProcessor(
|
| 232 |
+
vae_scale_factor=self.vae_scale_factor * 2,
|
| 233 |
+
vae_latent_channels=self.latent_channels,
|
| 234 |
+
do_normalize=False,
|
| 235 |
+
do_binarize=True,
|
| 236 |
+
do_convert_grayscale=True,
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
def _get_t5_prompt_embeds(
|
| 240 |
+
self,
|
| 241 |
+
prompt: Union[str, List[str], None] = None,
|
| 242 |
+
num_images_per_prompt: int = 1,
|
| 243 |
+
max_sequence_length: int = 512,
|
| 244 |
+
device: torch.device | None = None,
|
| 245 |
+
dtype: torch.dtype | None = None,
|
| 246 |
+
):
|
| 247 |
+
device = device or self._execution_device
|
| 248 |
+
dtype = dtype or self.text_encoder.dtype
|
| 249 |
+
|
| 250 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 251 |
+
batch_size = len(prompt)
|
| 252 |
+
|
| 253 |
+
if isinstance(self, TextualInversionLoaderMixin):
|
| 254 |
+
prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
|
| 255 |
+
|
| 256 |
+
text_inputs = self.tokenizer(
|
| 257 |
+
prompt,
|
| 258 |
+
padding="max_length",
|
| 259 |
+
max_length=max_sequence_length,
|
| 260 |
+
truncation=True,
|
| 261 |
+
return_length=False,
|
| 262 |
+
return_overflowing_tokens=False,
|
| 263 |
+
return_tensors="pt",
|
| 264 |
+
)
|
| 265 |
+
text_input_ids = text_inputs.input_ids
|
| 266 |
+
tokenizer_mask = text_inputs.attention_mask
|
| 267 |
+
|
| 268 |
+
tokenizer_mask = tokenizer_mask.to(device)
|
| 269 |
+
|
| 270 |
+
prompt_embeds = self.text_encoder(
|
| 271 |
+
text_input_ids.to(device),
|
| 272 |
+
output_hidden_states=False,
|
| 273 |
+
attention_mask=tokenizer_mask,
|
| 274 |
+
)[0]
|
| 275 |
+
|
| 276 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 277 |
+
|
| 278 |
+
seq_lengths = tokenizer_mask.sum(dim=1)
|
| 279 |
+
mask_indices = torch.arange(tokenizer_mask.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
|
| 280 |
+
attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
|
| 281 |
+
|
| 282 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 283 |
+
|
| 284 |
+
# duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
|
| 285 |
+
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
| 286 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 287 |
+
|
| 288 |
+
attention_mask = attention_mask.repeat(1, num_images_per_prompt)
|
| 289 |
+
attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
|
| 290 |
+
|
| 291 |
+
return prompt_embeds, attention_mask
|
| 292 |
+
|
| 293 |
+
def encode_prompt(
|
| 294 |
+
self,
|
| 295 |
+
prompt: Union[str, List[str]],
|
| 296 |
+
negative_prompt: Union[str, List[str], None] = None,
|
| 297 |
+
device: torch.device | None = None,
|
| 298 |
+
num_images_per_prompt: int = 1,
|
| 299 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 300 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 301 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 302 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 303 |
+
do_classifier_free_guidance: bool = True,
|
| 304 |
+
max_sequence_length: int = 256,
|
| 305 |
+
lora_scale: bool | None = None,
|
| 306 |
+
):
|
| 307 |
+
r"""
|
| 308 |
+
|
| 309 |
+
Args:
|
| 310 |
+
prompt (`str` or `List[str]`, *optional*):
|
| 311 |
+
prompt to be encoded
|
| 312 |
+
negative_prompt (`str` or `List[str]`, *optional*):
|
| 313 |
+
The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
|
| 314 |
+
instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
|
| 315 |
+
device: (`torch.device`):
|
| 316 |
+
torch device
|
| 317 |
+
num_images_per_prompt (`int`):
|
| 318 |
+
number of images that should be generated per prompt
|
| 319 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 320 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 321 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 322 |
+
lora_scale (`float`, *optional*):
|
| 323 |
+
A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
|
| 324 |
+
"""
|
| 325 |
+
device = device or self._execution_device
|
| 326 |
+
|
| 327 |
+
# set lora scale so that monkey patched LoRA
|
| 328 |
+
# function of text encoder can correctly access it
|
| 329 |
+
if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
|
| 330 |
+
self._lora_scale = lora_scale
|
| 331 |
+
|
| 332 |
+
# dynamically adjust the LoRA scale
|
| 333 |
+
if self.text_encoder is not None and USE_PEFT_BACKEND:
|
| 334 |
+
scale_lora_layers(self.text_encoder, lora_scale)
|
| 335 |
+
|
| 336 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 337 |
+
|
| 338 |
+
if prompt is not None:
|
| 339 |
+
batch_size = len(prompt)
|
| 340 |
+
else:
|
| 341 |
+
batch_size = prompt_embeds.shape[0]
|
| 342 |
+
|
| 343 |
+
if prompt_embeds is None:
|
| 344 |
+
prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 345 |
+
prompt=prompt,
|
| 346 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 347 |
+
max_sequence_length=max_sequence_length,
|
| 348 |
+
device=device,
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
|
| 352 |
+
text_ids = torch.zeros(prompt_embeds.shape[1], 3, device=device, dtype=dtype)
|
| 353 |
+
negative_text_ids = None
|
| 354 |
+
|
| 355 |
+
if do_classifier_free_guidance:
|
| 356 |
+
if negative_prompt_embeds is None:
|
| 357 |
+
negative_prompt = negative_prompt or ""
|
| 358 |
+
negative_prompt = (
|
| 359 |
+
batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 363 |
+
raise TypeError(
|
| 364 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 365 |
+
f" {type(prompt)}."
|
| 366 |
+
)
|
| 367 |
+
elif batch_size != len(negative_prompt):
|
| 368 |
+
raise ValueError(
|
| 369 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 370 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 371 |
+
" the batch size of `prompt`."
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
|
| 375 |
+
prompt=negative_prompt,
|
| 376 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 377 |
+
max_sequence_length=max_sequence_length,
|
| 378 |
+
device=device,
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3, device=device, dtype=dtype)
|
| 382 |
+
|
| 383 |
+
if self.text_encoder is not None:
|
| 384 |
+
if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
|
| 385 |
+
# Retrieve the original scale by scaling back the LoRA layers
|
| 386 |
+
unscale_lora_layers(self.text_encoder, lora_scale)
|
| 387 |
+
|
| 388 |
+
return (
|
| 389 |
+
prompt_embeds,
|
| 390 |
+
text_ids,
|
| 391 |
+
prompt_attention_mask,
|
| 392 |
+
negative_prompt_embeds,
|
| 393 |
+
negative_text_ids,
|
| 394 |
+
negative_prompt_attention_mask,
|
| 395 |
+
)
|
| 396 |
+
|
| 397 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
|
| 398 |
+
def encode_image(self, image, device, num_images_per_prompt):
|
| 399 |
+
dtype = next(self.image_encoder.parameters()).dtype
|
| 400 |
+
|
| 401 |
+
if not isinstance(image, torch.Tensor):
|
| 402 |
+
image = self.feature_extractor(image, return_tensors="pt").pixel_values
|
| 403 |
+
|
| 404 |
+
image = image.to(device=device, dtype=dtype)
|
| 405 |
+
image_embeds = self.image_encoder(image).image_embeds
|
| 406 |
+
image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
| 407 |
+
return image_embeds
|
| 408 |
+
|
| 409 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_ip_adapter_image_embeds
|
| 410 |
+
def prepare_ip_adapter_image_embeds(
|
| 411 |
+
self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
|
| 412 |
+
):
|
| 413 |
+
image_embeds = []
|
| 414 |
+
if ip_adapter_image_embeds is None:
|
| 415 |
+
if not isinstance(ip_adapter_image, list):
|
| 416 |
+
ip_adapter_image = [ip_adapter_image]
|
| 417 |
+
|
| 418 |
+
if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 419 |
+
raise ValueError(
|
| 420 |
+
f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 421 |
+
)
|
| 422 |
+
|
| 423 |
+
for single_ip_adapter_image in ip_adapter_image:
|
| 424 |
+
single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
|
| 425 |
+
image_embeds.append(single_image_embeds[None, :])
|
| 426 |
+
else:
|
| 427 |
+
if not isinstance(ip_adapter_image_embeds, list):
|
| 428 |
+
ip_adapter_image_embeds = [ip_adapter_image_embeds]
|
| 429 |
+
|
| 430 |
+
if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
|
| 431 |
+
raise ValueError(
|
| 432 |
+
f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
for single_image_embeds in ip_adapter_image_embeds:
|
| 436 |
+
image_embeds.append(single_image_embeds)
|
| 437 |
+
|
| 438 |
+
ip_adapter_image_embeds = []
|
| 439 |
+
for single_image_embeds in image_embeds:
|
| 440 |
+
single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
|
| 441 |
+
single_image_embeds = single_image_embeds.to(device=device)
|
| 442 |
+
ip_adapter_image_embeds.append(single_image_embeds)
|
| 443 |
+
|
| 444 |
+
return ip_adapter_image_embeds
|
| 445 |
+
|
| 446 |
+
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_inpaint.StableDiffusion3InpaintPipeline._encode_vae_image
|
| 447 |
+
def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
|
| 448 |
+
if isinstance(generator, list):
|
| 449 |
+
image_latents = [
|
| 450 |
+
retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
|
| 451 |
+
for i in range(image.shape[0])
|
| 452 |
+
]
|
| 453 |
+
image_latents = torch.cat(image_latents, dim=0)
|
| 454 |
+
else:
|
| 455 |
+
image_latents = retrieve_latents(self.vae.encode(image), generator=generator)
|
| 456 |
+
|
| 457 |
+
image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
|
| 458 |
+
|
| 459 |
+
return image_latents
|
| 460 |
+
|
| 461 |
+
# Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_img2img.StableDiffusion3Img2ImgPipeline.get_timesteps
|
| 462 |
+
def get_timesteps(self, num_inference_steps, strength, device):
|
| 463 |
+
# get the original timestep using init_timestep
|
| 464 |
+
init_timestep = min(num_inference_steps * strength, num_inference_steps)
|
| 465 |
+
|
| 466 |
+
t_start = int(max(num_inference_steps - init_timestep, 0))
|
| 467 |
+
timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
|
| 468 |
+
if hasattr(self.scheduler, "set_begin_index"):
|
| 469 |
+
self.scheduler.set_begin_index(t_start * self.scheduler.order)
|
| 470 |
+
|
| 471 |
+
return timesteps, num_inference_steps - t_start
|
| 472 |
+
|
| 473 |
+
def check_inputs(
|
| 474 |
+
self,
|
| 475 |
+
prompt,
|
| 476 |
+
image,
|
| 477 |
+
mask_image,
|
| 478 |
+
strength,
|
| 479 |
+
height,
|
| 480 |
+
width,
|
| 481 |
+
output_type,
|
| 482 |
+
negative_prompt=None,
|
| 483 |
+
prompt_embeds=None,
|
| 484 |
+
negative_prompt_embeds=None,
|
| 485 |
+
callback_on_step_end_tensor_inputs=None,
|
| 486 |
+
padding_mask_crop=None,
|
| 487 |
+
max_sequence_length=None,
|
| 488 |
+
prompt_attention_mask=None,
|
| 489 |
+
negative_prompt_attention_mask=None,
|
| 490 |
+
):
|
| 491 |
+
if strength < 0 or strength > 1:
|
| 492 |
+
raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
|
| 493 |
+
|
| 494 |
+
if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
|
| 495 |
+
logger.warning(
|
| 496 |
+
f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 500 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 501 |
+
):
|
| 502 |
+
raise ValueError(
|
| 503 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 504 |
+
)
|
| 505 |
+
|
| 506 |
+
if prompt is not None and prompt_embeds is not None:
|
| 507 |
+
raise ValueError(
|
| 508 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 509 |
+
" only forward one of the two."
|
| 510 |
+
)
|
| 511 |
+
elif prompt is None and prompt_embeds is None:
|
| 512 |
+
raise ValueError(
|
| 513 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 514 |
+
)
|
| 515 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 516 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 517 |
+
|
| 518 |
+
if negative_prompt is not None and negative_prompt_embeds is not None:
|
| 519 |
+
raise ValueError(
|
| 520 |
+
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
|
| 521 |
+
f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
if prompt_embeds is not None and negative_prompt_embeds is not None:
|
| 525 |
+
if prompt_embeds.shape != negative_prompt_embeds.shape:
|
| 526 |
+
raise ValueError(
|
| 527 |
+
"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
|
| 528 |
+
f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
|
| 529 |
+
f" {negative_prompt_embeds.shape}."
|
| 530 |
+
)
|
| 531 |
+
|
| 532 |
+
if prompt_embeds is not None and prompt_attention_mask is None:
|
| 533 |
+
raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
|
| 534 |
+
|
| 535 |
+
if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
|
| 536 |
+
raise ValueError(
|
| 537 |
+
"Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
|
| 538 |
+
)
|
| 539 |
+
|
| 540 |
+
if padding_mask_crop is not None:
|
| 541 |
+
if not isinstance(image, PIL.Image.Image):
|
| 542 |
+
raise ValueError(
|
| 543 |
+
f"The image should be a PIL image when inpainting mask crop, but is of type {type(image)}."
|
| 544 |
+
)
|
| 545 |
+
if not isinstance(mask_image, PIL.Image.Image):
|
| 546 |
+
raise ValueError(
|
| 547 |
+
f"The mask image should be a PIL image when inpainting mask crop, but is of type"
|
| 548 |
+
f" {type(mask_image)}."
|
| 549 |
+
)
|
| 550 |
+
if output_type != "pil":
|
| 551 |
+
raise ValueError(f"The output type should be PIL when inpainting mask crop, but is {output_type}.")
|
| 552 |
+
|
| 553 |
+
if max_sequence_length is not None and max_sequence_length > 512:
|
| 554 |
+
raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
|
| 555 |
+
|
| 556 |
+
@staticmethod
|
| 557 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._prepare_latent_image_ids
|
| 558 |
+
def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
|
| 559 |
+
latent_image_ids = torch.zeros(height, width, 3)
|
| 560 |
+
latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
|
| 561 |
+
latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
|
| 562 |
+
|
| 563 |
+
latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
|
| 564 |
+
|
| 565 |
+
latent_image_ids = latent_image_ids.reshape(
|
| 566 |
+
latent_image_id_height * latent_image_id_width, latent_image_id_channels
|
| 567 |
+
)
|
| 568 |
+
|
| 569 |
+
return latent_image_ids.to(device=device, dtype=dtype)
|
| 570 |
+
|
| 571 |
+
@staticmethod
|
| 572 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._pack_latents
|
| 573 |
+
def _pack_latents(latents, batch_size, num_channels_latents, height, width):
|
| 574 |
+
latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
|
| 575 |
+
latents = latents.permute(0, 2, 4, 1, 3, 5)
|
| 576 |
+
latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
|
| 577 |
+
|
| 578 |
+
return latents
|
| 579 |
+
|
| 580 |
+
@staticmethod
|
| 581 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._unpack_latents
|
| 582 |
+
def _unpack_latents(latents, height, width, vae_scale_factor):
|
| 583 |
+
batch_size, num_patches, channels = latents.shape
|
| 584 |
+
|
| 585 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 586 |
+
# latent height and width to be divisible by 2.
|
| 587 |
+
height = 2 * (int(height) // (vae_scale_factor * 2))
|
| 588 |
+
width = 2 * (int(width) // (vae_scale_factor * 2))
|
| 589 |
+
|
| 590 |
+
latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
|
| 591 |
+
latents = latents.permute(0, 3, 1, 4, 2, 5)
|
| 592 |
+
|
| 593 |
+
latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
|
| 594 |
+
|
| 595 |
+
return latents
|
| 596 |
+
|
| 597 |
+
def prepare_latents(
|
| 598 |
+
self,
|
| 599 |
+
image,
|
| 600 |
+
timestep,
|
| 601 |
+
batch_size,
|
| 602 |
+
num_channels_latents,
|
| 603 |
+
height,
|
| 604 |
+
width,
|
| 605 |
+
dtype,
|
| 606 |
+
device,
|
| 607 |
+
generator,
|
| 608 |
+
latents=None,
|
| 609 |
+
):
|
| 610 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 611 |
+
raise ValueError(
|
| 612 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 613 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 614 |
+
)
|
| 615 |
+
|
| 616 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 617 |
+
# latent height and width to be divisible by 2.
|
| 618 |
+
height = 2 * (int(height) // (self.vae_scale_factor * 2))
|
| 619 |
+
width = 2 * (int(width) // (self.vae_scale_factor * 2))
|
| 620 |
+
shape = (batch_size, num_channels_latents, height, width)
|
| 621 |
+
latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
|
| 622 |
+
|
| 623 |
+
image = image.to(device=device, dtype=dtype)
|
| 624 |
+
if image.shape[1] != self.latent_channels:
|
| 625 |
+
image_latents = self._encode_vae_image(image=image, generator=generator)
|
| 626 |
+
else:
|
| 627 |
+
image_latents = image
|
| 628 |
+
|
| 629 |
+
if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
|
| 630 |
+
# expand init_latents for batch_size
|
| 631 |
+
additional_image_per_prompt = batch_size // image_latents.shape[0]
|
| 632 |
+
image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
|
| 633 |
+
elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
|
| 634 |
+
raise ValueError(
|
| 635 |
+
f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
|
| 636 |
+
)
|
| 637 |
+
else:
|
| 638 |
+
image_latents = torch.cat([image_latents], dim=0)
|
| 639 |
+
|
| 640 |
+
if latents is None:
|
| 641 |
+
noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 642 |
+
latents = self.scheduler.scale_noise(image_latents, timestep, noise)
|
| 643 |
+
else:
|
| 644 |
+
noise = latents.to(device)
|
| 645 |
+
latents = noise
|
| 646 |
+
|
| 647 |
+
noise = self._pack_latents(noise, batch_size, num_channels_latents, height, width)
|
| 648 |
+
image_latents = self._pack_latents(image_latents, batch_size, num_channels_latents, height, width)
|
| 649 |
+
latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
|
| 650 |
+
return latents, noise, image_latents, latent_image_ids
|
| 651 |
+
|
| 652 |
+
def prepare_mask_latents(
|
| 653 |
+
self,
|
| 654 |
+
mask,
|
| 655 |
+
masked_image,
|
| 656 |
+
batch_size,
|
| 657 |
+
num_channels_latents,
|
| 658 |
+
num_images_per_prompt,
|
| 659 |
+
height,
|
| 660 |
+
width,
|
| 661 |
+
dtype,
|
| 662 |
+
device,
|
| 663 |
+
generator,
|
| 664 |
+
):
|
| 665 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 666 |
+
# latent height and width to be divisible by 2.
|
| 667 |
+
height = 2 * (int(height) // (self.vae_scale_factor * 2))
|
| 668 |
+
width = 2 * (int(width) // (self.vae_scale_factor * 2))
|
| 669 |
+
# resize the mask to latents shape as we concatenate the mask to the latents
|
| 670 |
+
# we do that before converting to dtype to avoid breaking in case we're using cpu_offload
|
| 671 |
+
# and half precision
|
| 672 |
+
mask = torch.nn.functional.interpolate(mask, size=(height, width))
|
| 673 |
+
mask = mask.to(device=device, dtype=dtype)
|
| 674 |
+
|
| 675 |
+
batch_size = batch_size * num_images_per_prompt
|
| 676 |
+
|
| 677 |
+
masked_image = masked_image.to(device=device, dtype=dtype)
|
| 678 |
+
|
| 679 |
+
if masked_image.shape[1] == 16:
|
| 680 |
+
masked_image_latents = masked_image
|
| 681 |
+
else:
|
| 682 |
+
masked_image_latents = retrieve_latents(self.vae.encode(masked_image), generator=generator)
|
| 683 |
+
|
| 684 |
+
masked_image_latents = (
|
| 685 |
+
masked_image_latents - self.vae.config.shift_factor
|
| 686 |
+
) * self.vae.config.scaling_factor
|
| 687 |
+
|
| 688 |
+
# duplicate mask and masked_image_latents for each generation per prompt, using mps friendly method
|
| 689 |
+
if mask.shape[0] < batch_size:
|
| 690 |
+
if not batch_size % mask.shape[0] == 0:
|
| 691 |
+
raise ValueError(
|
| 692 |
+
"The passed mask and the required batch size don't match. Masks are supposed to be duplicated to"
|
| 693 |
+
f" a total batch size of {batch_size}, but {mask.shape[0]} masks were passed. Make sure the number"
|
| 694 |
+
" of masks that you pass is divisible by the total requested batch size."
|
| 695 |
+
)
|
| 696 |
+
mask = mask.repeat(batch_size // mask.shape[0], 1, 1, 1)
|
| 697 |
+
if masked_image_latents.shape[0] < batch_size:
|
| 698 |
+
if not batch_size % masked_image_latents.shape[0] == 0:
|
| 699 |
+
raise ValueError(
|
| 700 |
+
"The passed images and the required batch size don't match. Images are supposed to be duplicated"
|
| 701 |
+
f" to a total batch size of {batch_size}, but {masked_image_latents.shape[0]} images were passed."
|
| 702 |
+
" Make sure the number of images that you pass is divisible by the total requested batch size."
|
| 703 |
+
)
|
| 704 |
+
masked_image_latents = masked_image_latents.repeat(batch_size // masked_image_latents.shape[0], 1, 1, 1)
|
| 705 |
+
|
| 706 |
+
# aligning device to prevent device errors when concating it with the latent model input
|
| 707 |
+
masked_image_latents = masked_image_latents.to(device=device, dtype=dtype)
|
| 708 |
+
masked_image_latents = self._pack_latents(
|
| 709 |
+
masked_image_latents,
|
| 710 |
+
batch_size,
|
| 711 |
+
num_channels_latents,
|
| 712 |
+
height,
|
| 713 |
+
width,
|
| 714 |
+
)
|
| 715 |
+
mask = self._pack_latents(
|
| 716 |
+
mask.repeat(1, num_channels_latents, 1, 1),
|
| 717 |
+
batch_size,
|
| 718 |
+
num_channels_latents,
|
| 719 |
+
height,
|
| 720 |
+
width,
|
| 721 |
+
)
|
| 722 |
+
|
| 723 |
+
return mask, masked_image_latents
|
| 724 |
+
|
| 725 |
+
@property
|
| 726 |
+
def guidance_scale(self):
|
| 727 |
+
return self._guidance_scale
|
| 728 |
+
|
| 729 |
+
@property
|
| 730 |
+
def joint_attention_kwargs(self):
|
| 731 |
+
return self._joint_attention_kwargs
|
| 732 |
+
|
| 733 |
+
@property
|
| 734 |
+
def num_timesteps(self):
|
| 735 |
+
return self._num_timesteps
|
| 736 |
+
|
| 737 |
+
@property
|
| 738 |
+
def interrupt(self):
|
| 739 |
+
return self._interrupt
|
| 740 |
+
|
| 741 |
+
def _prepare_attention_mask(
|
| 742 |
+
self,
|
| 743 |
+
batch_size,
|
| 744 |
+
sequence_length,
|
| 745 |
+
dtype,
|
| 746 |
+
attention_mask=None,
|
| 747 |
+
):
|
| 748 |
+
if attention_mask is None:
|
| 749 |
+
return attention_mask
|
| 750 |
+
|
| 751 |
+
# Extend the prompt attention mask to account for image tokens in the final sequence
|
| 752 |
+
attention_mask = torch.cat(
|
| 753 |
+
[attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device, dtype=torch.bool)],
|
| 754 |
+
dim=1,
|
| 755 |
+
)
|
| 756 |
+
attention_mask = attention_mask.to(dtype)
|
| 757 |
+
|
| 758 |
+
return attention_mask
|
| 759 |
+
|
| 760 |
+
@property
|
| 761 |
+
def do_classifier_free_guidance(self):
|
| 762 |
+
return self._guidance_scale > 1
|
| 763 |
+
|
| 764 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 765 |
+
@torch.no_grad()
|
| 766 |
+
def __call__(
|
| 767 |
+
self,
|
| 768 |
+
prompt: Union[str, List[str]] = None,
|
| 769 |
+
negative_prompt: Union[str, List[str]] = None,
|
| 770 |
+
true_cfg_scale: float = 1.0,
|
| 771 |
+
image: PipelineImageInput = None,
|
| 772 |
+
mask_image: PipelineImageInput = None,
|
| 773 |
+
masked_image_latents: PipelineImageInput = None,
|
| 774 |
+
height: int | None = None,
|
| 775 |
+
width: int | None = None,
|
| 776 |
+
padding_mask_crop: int | None = None,
|
| 777 |
+
strength: float = 0.6,
|
| 778 |
+
num_inference_steps: int = 28,
|
| 779 |
+
sigmas: list[float] | None = None,
|
| 780 |
+
guidance_scale: float = 7.0,
|
| 781 |
+
num_images_per_prompt: Optional[int] = 1,
|
| 782 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 783 |
+
latents: Optional[torch.FloatTensor] = None,
|
| 784 |
+
prompt_embeds: Optional[torch.FloatTensor] = None,
|
| 785 |
+
ip_adapter_image: Optional[PipelineImageInput] = None,
|
| 786 |
+
ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
|
| 787 |
+
negative_ip_adapter_image: Optional[PipelineImageInput] = None,
|
| 788 |
+
negative_ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
|
| 789 |
+
negative_prompt_embeds: Optional[torch.FloatTensor] = None,
|
| 790 |
+
output_type: str = "pil",
|
| 791 |
+
return_dict: bool = True,
|
| 792 |
+
joint_attention_kwargs: Optional[Dict[str, Any]] = None,
|
| 793 |
+
callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
|
| 794 |
+
callback_on_step_end_tensor_inputs: List[str] = ["latents"],
|
| 795 |
+
max_sequence_length: int = 256,
|
| 796 |
+
prompt_attention_mask: torch.Tensor | None = None,
|
| 797 |
+
negative_prompt_attention_mask: torch.Tensor | None = None,
|
| 798 |
+
):
|
| 799 |
+
r"""
|
| 800 |
+
Function invoked when calling the pipeline for generation.
|
| 801 |
+
|
| 802 |
+
Args:
|
| 803 |
+
prompt (`str` or `List[str]`, *optional*):
|
| 804 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 805 |
+
instead.
|
| 806 |
+
negative_prompt (`str` or `List[str]`, *optional*):
|
| 807 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 808 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 809 |
+
not greater than `1`).
|
| 810 |
+
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 811 |
+
The height in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 812 |
+
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 813 |
+
The width in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 814 |
+
num_inference_steps (`int`, *optional*, defaults to 35):
|
| 815 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 816 |
+
expense of slower inference.
|
| 817 |
+
sigmas (`List[float]`, *optional*):
|
| 818 |
+
Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
|
| 819 |
+
their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
|
| 820 |
+
will be used.
|
| 821 |
+
guidance_scale (`float`, *optional*, defaults to 3.5):
|
| 822 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 823 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 824 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 825 |
+
`guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
|
| 826 |
+
the text `prompt`, usually at the expense of lower image quality.
|
| 827 |
+
strength (`float, *optional*, defaults to 0.9):
|
| 828 |
+
Conceptually, indicates how much to transform the reference image. Must be between 0 and 1. image will
|
| 829 |
+
be used as a starting point, adding more noise to it the larger the strength. The number of denoising
|
| 830 |
+
steps depends on the amount of noise initially added. When strength is 1, added noise will be maximum
|
| 831 |
+
and the denoising process will run for the full number of iterations specified in num_inference_steps.
|
| 832 |
+
A value of 1, therefore, essentially ignores image.
|
| 833 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 834 |
+
The number of images to generate per prompt.
|
| 835 |
+
generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
|
| 836 |
+
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
|
| 837 |
+
to make generation deterministic.
|
| 838 |
+
latents (`torch.Tensor`, *optional*):
|
| 839 |
+
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
|
| 840 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 841 |
+
tensor will be generated by sampling using the supplied random `generator`.
|
| 842 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 843 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 844 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 845 |
+
ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 846 |
+
ip_adapter_image_embeds (`List[torch.Tensor]`, *optional*):
|
| 847 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 848 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 849 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 850 |
+
negative_ip_adapter_image:
|
| 851 |
+
(`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
|
| 852 |
+
negative_ip_adapter_image_embeds (`List[torch.Tensor]`, *optional*):
|
| 853 |
+
Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
|
| 854 |
+
IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
|
| 855 |
+
provided, embeddings are computed from the `ip_adapter_image` input argument.
|
| 856 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 857 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 858 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 859 |
+
argument.
|
| 860 |
+
prompt_attention_mask (torch.Tensor, *optional*):
|
| 861 |
+
Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
|
| 862 |
+
Chroma requires a single padding token remain unmasked. Please refer to
|
| 863 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 864 |
+
negative_prompt_attention_mask (torch.Tensor, *optional*):
|
| 865 |
+
Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
|
| 866 |
+
prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
|
| 867 |
+
https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
|
| 868 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 869 |
+
The output format of the generate image. Choose between
|
| 870 |
+
[PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
|
| 871 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 872 |
+
Whether or not to return a [`~pipelines.chroma.ChromaPipelineOutput`] instead of a plain tuple.
|
| 873 |
+
joint_attention_kwargs (`dict`, *optional*):
|
| 874 |
+
A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
|
| 875 |
+
`self.processor` in
|
| 876 |
+
[diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
| 877 |
+
callback_on_step_end (`Callable`, *optional*):
|
| 878 |
+
A function that calls at the end of each denoising steps during the inference. The function is called
|
| 879 |
+
with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
|
| 880 |
+
callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
|
| 881 |
+
`callback_on_step_end_tensor_inputs`.
|
| 882 |
+
callback_on_step_end_tensor_inputs (`List`, *optional*):
|
| 883 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 884 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 885 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 886 |
+
max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
|
| 887 |
+
|
| 888 |
+
Examples:
|
| 889 |
+
|
| 890 |
+
Returns:
|
| 891 |
+
[`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
|
| 892 |
+
`return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
|
| 893 |
+
generated images.
|
| 894 |
+
"""
|
| 895 |
+
|
| 896 |
+
height = height or self.default_sample_size * self.vae_scale_factor
|
| 897 |
+
width = width or self.default_sample_size * self.vae_scale_factor
|
| 898 |
+
|
| 899 |
+
# 1. Check inputs. Raise error if not correct
|
| 900 |
+
self.check_inputs(
|
| 901 |
+
prompt=prompt,
|
| 902 |
+
height=height,
|
| 903 |
+
width=width,
|
| 904 |
+
output_type=output_type,
|
| 905 |
+
strength=strength,
|
| 906 |
+
negative_prompt=negative_prompt,
|
| 907 |
+
prompt_embeds=prompt_embeds,
|
| 908 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 909 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 910 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 911 |
+
callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
|
| 912 |
+
max_sequence_length=max_sequence_length,
|
| 913 |
+
image=image,
|
| 914 |
+
mask_image=mask_image,
|
| 915 |
+
padding_mask_crop=padding_mask_crop,
|
| 916 |
+
)
|
| 917 |
+
|
| 918 |
+
self._guidance_scale = guidance_scale
|
| 919 |
+
self._joint_attention_kwargs = joint_attention_kwargs
|
| 920 |
+
self._current_timestep = None
|
| 921 |
+
self._interrupt = False
|
| 922 |
+
|
| 923 |
+
# 2. Preprocess mask and image
|
| 924 |
+
if padding_mask_crop is not None:
|
| 925 |
+
crops_coords = self.mask_processor.get_crop_region(mask_image, width, height, pad=padding_mask_crop)
|
| 926 |
+
resize_mode = "fill"
|
| 927 |
+
else:
|
| 928 |
+
crops_coords = None
|
| 929 |
+
resize_mode = "default"
|
| 930 |
+
|
| 931 |
+
original_image = image
|
| 932 |
+
init_image = self.image_processor.preprocess(
|
| 933 |
+
image, height=height, width=width, crops_coords=crops_coords, resize_mode=resize_mode
|
| 934 |
+
)
|
| 935 |
+
init_image = init_image.to(dtype=torch.float32)
|
| 936 |
+
|
| 937 |
+
# 3. Define call parameters
|
| 938 |
+
if prompt is not None and isinstance(prompt, str):
|
| 939 |
+
batch_size = 1
|
| 940 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 941 |
+
batch_size = len(prompt)
|
| 942 |
+
else:
|
| 943 |
+
batch_size = prompt_embeds.shape[0]
|
| 944 |
+
|
| 945 |
+
device = self._execution_device
|
| 946 |
+
lora_scale = (
|
| 947 |
+
self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
|
| 948 |
+
)
|
| 949 |
+
|
| 950 |
+
(
|
| 951 |
+
prompt_embeds,
|
| 952 |
+
text_ids,
|
| 953 |
+
prompt_attention_mask,
|
| 954 |
+
negative_prompt_embeds,
|
| 955 |
+
negative_text_ids,
|
| 956 |
+
negative_prompt_attention_mask,
|
| 957 |
+
) = self.encode_prompt(
|
| 958 |
+
prompt=prompt,
|
| 959 |
+
negative_prompt=negative_prompt,
|
| 960 |
+
prompt_embeds=prompt_embeds,
|
| 961 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 962 |
+
prompt_attention_mask=prompt_attention_mask,
|
| 963 |
+
negative_prompt_attention_mask=negative_prompt_attention_mask,
|
| 964 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 965 |
+
device=device,
|
| 966 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 967 |
+
max_sequence_length=max_sequence_length,
|
| 968 |
+
lora_scale=lora_scale,
|
| 969 |
+
)
|
| 970 |
+
|
| 971 |
+
# 4. Prepare timesteps
|
| 972 |
+
sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
|
| 973 |
+
image_seq_len = (int(height) // self.vae_scale_factor // 2) * (int(width) // self.vae_scale_factor // 2)
|
| 974 |
+
mu = calculate_shift(
|
| 975 |
+
image_seq_len,
|
| 976 |
+
self.scheduler.config.get("base_image_seq_len", 256),
|
| 977 |
+
self.scheduler.config.get("max_image_seq_len", 4096),
|
| 978 |
+
self.scheduler.config.get("base_shift", 0.5),
|
| 979 |
+
self.scheduler.config.get("max_shift", 1.15),
|
| 980 |
+
)
|
| 981 |
+
timesteps, num_inference_steps = retrieve_timesteps(
|
| 982 |
+
self.scheduler,
|
| 983 |
+
num_inference_steps,
|
| 984 |
+
device,
|
| 985 |
+
sigmas=sigmas,
|
| 986 |
+
mu=mu,
|
| 987 |
+
)
|
| 988 |
+
timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
|
| 989 |
+
|
| 990 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 991 |
+
self._num_timesteps = len(timesteps)
|
| 992 |
+
|
| 993 |
+
if num_inference_steps < 1:
|
| 994 |
+
raise ValueError(
|
| 995 |
+
f"After adjusting the num_inference_steps by strength parameter: {strength}, the number of pipeline"
|
| 996 |
+
f"steps is {num_inference_steps} which is < 1 and not appropriate for this pipeline."
|
| 997 |
+
)
|
| 998 |
+
latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
|
| 999 |
+
|
| 1000 |
+
# 5. Prepare latent variables
|
| 1001 |
+
num_channels_latents = self.transformer.config.in_channels // 4
|
| 1002 |
+
num_channels_transformer = self.transformer.config.in_channels
|
| 1003 |
+
|
| 1004 |
+
latents, noise, image_latents, latent_image_ids = self.prepare_latents(
|
| 1005 |
+
init_image,
|
| 1006 |
+
latent_timestep,
|
| 1007 |
+
batch_size * num_images_per_prompt,
|
| 1008 |
+
num_channels_latents,
|
| 1009 |
+
height,
|
| 1010 |
+
width,
|
| 1011 |
+
prompt_embeds.dtype,
|
| 1012 |
+
device,
|
| 1013 |
+
generator,
|
| 1014 |
+
latents,
|
| 1015 |
+
)
|
| 1016 |
+
|
| 1017 |
+
mask_condition = self.mask_processor.preprocess(
|
| 1018 |
+
mask_image, height=height, width=width, resize_mode=resize_mode, crops_coords=crops_coords
|
| 1019 |
+
)
|
| 1020 |
+
|
| 1021 |
+
if masked_image_latents is None:
|
| 1022 |
+
masked_image = init_image * (mask_condition < 0.5)
|
| 1023 |
+
else:
|
| 1024 |
+
masked_image = masked_image_latents
|
| 1025 |
+
|
| 1026 |
+
mask, masked_image_latents = self.prepare_mask_latents(
|
| 1027 |
+
mask_condition,
|
| 1028 |
+
masked_image,
|
| 1029 |
+
batch_size,
|
| 1030 |
+
num_channels_latents,
|
| 1031 |
+
num_images_per_prompt,
|
| 1032 |
+
height,
|
| 1033 |
+
width,
|
| 1034 |
+
prompt_embeds.dtype,
|
| 1035 |
+
device,
|
| 1036 |
+
generator,
|
| 1037 |
+
)
|
| 1038 |
+
|
| 1039 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 1040 |
+
self._num_timesteps = len(timesteps)
|
| 1041 |
+
|
| 1042 |
+
# handle guidance
|
| 1043 |
+
if self.transformer.config.guidance_embeds:
|
| 1044 |
+
guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
|
| 1045 |
+
guidance = guidance.expand(latents.shape[0])
|
| 1046 |
+
else:
|
| 1047 |
+
guidance = None
|
| 1048 |
+
|
| 1049 |
+
if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
|
| 1050 |
+
negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
|
| 1051 |
+
):
|
| 1052 |
+
negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 1053 |
+
elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
|
| 1054 |
+
negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
|
| 1055 |
+
):
|
| 1056 |
+
ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
|
| 1057 |
+
|
| 1058 |
+
if self.joint_attention_kwargs is None:
|
| 1059 |
+
self._joint_attention_kwargs = {}
|
| 1060 |
+
|
| 1061 |
+
image_embeds = None
|
| 1062 |
+
negative_image_embeds = None
|
| 1063 |
+
if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
|
| 1064 |
+
image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 1065 |
+
ip_adapter_image,
|
| 1066 |
+
ip_adapter_image_embeds,
|
| 1067 |
+
device,
|
| 1068 |
+
batch_size * num_images_per_prompt,
|
| 1069 |
+
)
|
| 1070 |
+
if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
|
| 1071 |
+
negative_image_embeds = self.prepare_ip_adapter_image_embeds(
|
| 1072 |
+
negative_ip_adapter_image,
|
| 1073 |
+
negative_ip_adapter_image_embeds,
|
| 1074 |
+
device,
|
| 1075 |
+
batch_size * num_images_per_prompt,
|
| 1076 |
+
)
|
| 1077 |
+
|
| 1078 |
+
attention_mask = self._prepare_attention_mask(
|
| 1079 |
+
batch_size=latents.shape[0],
|
| 1080 |
+
sequence_length=image_seq_len,
|
| 1081 |
+
dtype=latents.dtype,
|
| 1082 |
+
attention_mask=prompt_attention_mask,
|
| 1083 |
+
)
|
| 1084 |
+
negative_attention_mask = self._prepare_attention_mask(
|
| 1085 |
+
batch_size=latents.shape[0],
|
| 1086 |
+
sequence_length=image_seq_len,
|
| 1087 |
+
dtype=latents.dtype,
|
| 1088 |
+
attention_mask=negative_prompt_attention_mask,
|
| 1089 |
+
)
|
| 1090 |
+
|
| 1091 |
+
# 6. Denoising loop
|
| 1092 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 1093 |
+
for i, t in enumerate(timesteps):
|
| 1094 |
+
if self.interrupt:
|
| 1095 |
+
continue
|
| 1096 |
+
|
| 1097 |
+
self._current_timestep = t
|
| 1098 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 1099 |
+
timestep = t.expand(latents.shape[0]).to(latents.dtype)
|
| 1100 |
+
|
| 1101 |
+
if image_embeds is not None:
|
| 1102 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
|
| 1103 |
+
|
| 1104 |
+
noise_pred = self.transformer(
|
| 1105 |
+
hidden_states=latents,
|
| 1106 |
+
timestep=timestep / 1000,
|
| 1107 |
+
encoder_hidden_states=prompt_embeds,
|
| 1108 |
+
txt_ids=text_ids,
|
| 1109 |
+
img_ids=latent_image_ids,
|
| 1110 |
+
attention_mask=attention_mask,
|
| 1111 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 1112 |
+
return_dict=False,
|
| 1113 |
+
)[0]
|
| 1114 |
+
|
| 1115 |
+
if self.do_classifier_free_guidance:
|
| 1116 |
+
if negative_image_embeds is not None:
|
| 1117 |
+
self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
|
| 1118 |
+
|
| 1119 |
+
noise_pred_uncond = self.transformer(
|
| 1120 |
+
hidden_states=latents,
|
| 1121 |
+
timestep=timestep / 1000,
|
| 1122 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 1123 |
+
txt_ids=negative_text_ids,
|
| 1124 |
+
img_ids=latent_image_ids,
|
| 1125 |
+
attention_mask=negative_attention_mask,
|
| 1126 |
+
joint_attention_kwargs=self.joint_attention_kwargs,
|
| 1127 |
+
return_dict=False,
|
| 1128 |
+
)[0]
|
| 1129 |
+
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
|
| 1130 |
+
|
| 1131 |
+
# compute the previous noisy sample x_t -> x_t-1
|
| 1132 |
+
latents_dtype = latents.dtype
|
| 1133 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 1134 |
+
|
| 1135 |
+
# for 64 channel transformer only.
|
| 1136 |
+
init_latents_proper = image_latents
|
| 1137 |
+
init_mask = mask
|
| 1138 |
+
|
| 1139 |
+
if i < len(timesteps) - 1:
|
| 1140 |
+
noise_timestep = timesteps[i + 1]
|
| 1141 |
+
init_latents_proper = self.scheduler.scale_noise(
|
| 1142 |
+
init_latents_proper, torch.tensor([noise_timestep]), noise
|
| 1143 |
+
)
|
| 1144 |
+
|
| 1145 |
+
latents = (1 - init_mask) * init_latents_proper + init_mask * latents
|
| 1146 |
+
|
| 1147 |
+
if latents.dtype != latents_dtype:
|
| 1148 |
+
if torch.backends.mps.is_available():
|
| 1149 |
+
# some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
|
| 1150 |
+
latents = latents.to(latents_dtype)
|
| 1151 |
+
|
| 1152 |
+
if callback_on_step_end is not None:
|
| 1153 |
+
callback_kwargs = {}
|
| 1154 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 1155 |
+
callback_kwargs[k] = locals()[k]
|
| 1156 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 1157 |
+
|
| 1158 |
+
latents = callback_outputs.pop("latents", latents)
|
| 1159 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 1160 |
+
|
| 1161 |
+
# call the callback, if provided
|
| 1162 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 1163 |
+
progress_bar.update()
|
| 1164 |
+
|
| 1165 |
+
if XLA_AVAILABLE:
|
| 1166 |
+
xm.mark_step()
|
| 1167 |
+
|
| 1168 |
+
self._current_timestep = None
|
| 1169 |
+
|
| 1170 |
+
if output_type == "latent":
|
| 1171 |
+
image = latents
|
| 1172 |
+
else:
|
| 1173 |
+
latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
|
| 1174 |
+
latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
|
| 1175 |
+
image = self.vae.decode(latents, return_dict=False)[0]
|
| 1176 |
+
image = self.image_processor.postprocess(image, output_type=output_type)
|
| 1177 |
+
|
| 1178 |
+
# Offload all models
|
| 1179 |
+
self.maybe_free_model_hooks()
|
| 1180 |
+
|
| 1181 |
+
if not return_dict:
|
| 1182 |
+
return (image,)
|
| 1183 |
+
|
| 1184 |
+
return ChromaPipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_output.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import PIL.Image
|
| 5 |
+
|
| 6 |
+
from ...utils import BaseOutput
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@dataclass
|
| 10 |
+
class ChromaPipelineOutput(BaseOutput):
|
| 11 |
+
"""
|
| 12 |
+
Output class for Stable Diffusion pipelines.
|
| 13 |
+
|
| 14 |
+
Args:
|
| 15 |
+
images (`list[PIL.Image.Image]` or `np.ndarray`)
|
| 16 |
+
list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
|
| 17 |
+
num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
images: list[PIL.Image.Image] | np.ndarray
|
third_party/diffusers/src/diffusers/pipelines/cosmos/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ...utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_torch_available,
|
| 9 |
+
is_transformers_available,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_dummy_objects = {}
|
| 14 |
+
_import_structure = {}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 19 |
+
raise OptionalDependencyNotAvailable()
|
| 20 |
+
except OptionalDependencyNotAvailable:
|
| 21 |
+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
| 22 |
+
|
| 23 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 24 |
+
else:
|
| 25 |
+
_import_structure["pipeline_cosmos2_5_predict"] = [
|
| 26 |
+
"Cosmos2_5_PredictBasePipeline",
|
| 27 |
+
]
|
| 28 |
+
_import_structure["pipeline_cosmos2_5_transfer"] = [
|
| 29 |
+
"Cosmos2_5_TransferPipeline",
|
| 30 |
+
]
|
| 31 |
+
_import_structure["pipeline_cosmos2_text2image"] = ["Cosmos2TextToImagePipeline"]
|
| 32 |
+
_import_structure["pipeline_cosmos2_video2world"] = ["Cosmos2VideoToWorldPipeline"]
|
| 33 |
+
_import_structure["pipeline_cosmos_text2world"] = ["CosmosTextToWorldPipeline"]
|
| 34 |
+
_import_structure["pipeline_cosmos_video2world"] = ["CosmosVideoToWorldPipeline"]
|
| 35 |
+
|
| 36 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 37 |
+
try:
|
| 38 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 39 |
+
raise OptionalDependencyNotAvailable()
|
| 40 |
+
|
| 41 |
+
except OptionalDependencyNotAvailable:
|
| 42 |
+
from ...utils.dummy_torch_and_transformers_objects import *
|
| 43 |
+
else:
|
| 44 |
+
from .pipeline_cosmos2_5_predict import (
|
| 45 |
+
Cosmos2_5_PredictBasePipeline,
|
| 46 |
+
)
|
| 47 |
+
from .pipeline_cosmos2_5_transfer import Cosmos2_5_TransferPipeline
|
| 48 |
+
from .pipeline_cosmos2_text2image import Cosmos2TextToImagePipeline
|
| 49 |
+
from .pipeline_cosmos2_video2world import Cosmos2VideoToWorldPipeline
|
| 50 |
+
from .pipeline_cosmos_text2world import CosmosTextToWorldPipeline
|
| 51 |
+
from .pipeline_cosmos_video2world import CosmosVideoToWorldPipeline
|
| 52 |
+
|
| 53 |
+
else:
|
| 54 |
+
import sys
|
| 55 |
+
|
| 56 |
+
sys.modules[__name__] = _LazyModule(
|
| 57 |
+
__name__,
|
| 58 |
+
globals()["__file__"],
|
| 59 |
+
_import_structure,
|
| 60 |
+
module_spec=__spec__,
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
for name, value in _dummy_objects.items():
|
| 64 |
+
setattr(sys.modules[__name__], name, value)
|
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_5_predict.py
ADDED
|
@@ -0,0 +1,888 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
|
| 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 |
+
import numpy as np
|
| 18 |
+
import torch
|
| 19 |
+
from transformers import AutoTokenizer, Qwen2_5_VLForConditionalGeneration
|
| 20 |
+
|
| 21 |
+
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
|
| 22 |
+
from ...image_processor import PipelineImageInput
|
| 23 |
+
from ...models import AutoencoderKLWan, CosmosTransformer3DModel
|
| 24 |
+
from ...schedulers import UniPCMultistepScheduler
|
| 25 |
+
from ...utils import (
|
| 26 |
+
is_cosmos_guardrail_available,
|
| 27 |
+
is_torch_xla_available,
|
| 28 |
+
is_torchvision_available,
|
| 29 |
+
logging,
|
| 30 |
+
replace_example_docstring,
|
| 31 |
+
)
|
| 32 |
+
from ...utils.torch_utils import randn_tensor
|
| 33 |
+
from ...video_processor import VideoProcessor
|
| 34 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 35 |
+
from .pipeline_output import CosmosPipelineOutput
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
if is_torchvision_available():
|
| 39 |
+
import torchvision.transforms.functional
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
if is_cosmos_guardrail_available():
|
| 43 |
+
from cosmos_guardrail import CosmosSafetyChecker
|
| 44 |
+
else:
|
| 45 |
+
|
| 46 |
+
class CosmosSafetyChecker:
|
| 47 |
+
def __init__(self, *args, **kwargs):
|
| 48 |
+
raise ImportError(
|
| 49 |
+
"`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
if is_torch_xla_available():
|
| 54 |
+
import torch_xla.core.xla_model as xm
|
| 55 |
+
|
| 56 |
+
XLA_AVAILABLE = True
|
| 57 |
+
else:
|
| 58 |
+
XLA_AVAILABLE = False
|
| 59 |
+
|
| 60 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 61 |
+
|
| 62 |
+
DEFAULT_NEGATIVE_PROMPT = (
|
| 63 |
+
"The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
|
| 64 |
+
"over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
|
| 65 |
+
"underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
|
| 66 |
+
"jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
|
| 67 |
+
"fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
|
| 68 |
+
"Overall, the video is of poor quality."
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
| 73 |
+
def retrieve_latents(
|
| 74 |
+
encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
|
| 75 |
+
):
|
| 76 |
+
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
| 77 |
+
return encoder_output.latent_dist.sample(generator)
|
| 78 |
+
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
| 79 |
+
return encoder_output.latent_dist.mode()
|
| 80 |
+
elif hasattr(encoder_output, "latents"):
|
| 81 |
+
return encoder_output.latents
|
| 82 |
+
else:
|
| 83 |
+
raise AttributeError("Could not access latents of provided encoder_output")
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
EXAMPLE_DOC_STRING = """
|
| 87 |
+
Examples:
|
| 88 |
+
```python
|
| 89 |
+
>>> import torch
|
| 90 |
+
>>> from diffusers import Cosmos2_5_PredictBasePipeline
|
| 91 |
+
>>> from diffusers.utils import export_to_video, load_image, load_video
|
| 92 |
+
|
| 93 |
+
>>> model_id = "nvidia/Cosmos-Predict2.5-2B"
|
| 94 |
+
>>> pipe = Cosmos2_5_PredictBasePipeline.from_pretrained(
|
| 95 |
+
... model_id, revision="diffusers/base/post-trained", torch_dtype=torch.bfloat16
|
| 96 |
+
... )
|
| 97 |
+
>>> pipe = pipe.to("cuda")
|
| 98 |
+
|
| 99 |
+
>>> # Common negative prompt reused across modes.
|
| 100 |
+
>>> negative_prompt = (
|
| 101 |
+
... "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
|
| 102 |
+
... "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
|
| 103 |
+
... "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, jerky "
|
| 104 |
+
... "movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
|
| 105 |
+
... "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
|
| 106 |
+
... "Overall, the video is of poor quality."
|
| 107 |
+
... )
|
| 108 |
+
|
| 109 |
+
>>> # Text2World: generate a 93-frame world video from text only.
|
| 110 |
+
>>> prompt = (
|
| 111 |
+
... "As the red light shifts to green, the red bus at the intersection begins to move forward, its headlights "
|
| 112 |
+
... "cutting through the falling snow. The snowy tire tracks deepen as the vehicle inches ahead, casting fresh "
|
| 113 |
+
... "lines onto the slushy road. Around it, streetlights glow warmer, illuminating the drifting flakes and wet "
|
| 114 |
+
... "reflections on the asphalt. Other cars behind start to edge forward, their beams joining the scene. "
|
| 115 |
+
... "The stillness of the urban street transitions into motion as the quiet snowfall is punctuated by the slow "
|
| 116 |
+
... "advance of traffic through the frosty city corridor."
|
| 117 |
+
... )
|
| 118 |
+
>>> video = pipe(
|
| 119 |
+
... image=None,
|
| 120 |
+
... video=None,
|
| 121 |
+
... prompt=prompt,
|
| 122 |
+
... negative_prompt=negative_prompt,
|
| 123 |
+
... num_frames=93,
|
| 124 |
+
... generator=torch.Generator().manual_seed(1),
|
| 125 |
+
... ).frames[0]
|
| 126 |
+
>>> export_to_video(video, "text2world.mp4", fps=16)
|
| 127 |
+
|
| 128 |
+
>>> # Image2World: condition on a single image and generate a 93-frame world video.
|
| 129 |
+
>>> prompt = (
|
| 130 |
+
... "A high-definition video captures the precision of robotic welding in an industrial setting. "
|
| 131 |
+
... "The first frame showcases a robotic arm, equipped with a welding torch, positioned over a large metal structure. "
|
| 132 |
+
... "The welding process is in full swing, with bright sparks and intense light illuminating the scene, creating a vivid "
|
| 133 |
+
... "display of blue and white hues. A significant amount of smoke billows around the welding area, partially obscuring "
|
| 134 |
+
... "the view but emphasizing the heat and activity. The background reveals parts of the workshop environment, including a "
|
| 135 |
+
... "ventilation system and various pieces of machinery, indicating a busy and functional industrial workspace. As the video "
|
| 136 |
+
... "progresses, the robotic arm maintains its steady position, continuing the welding process and moving to its left. "
|
| 137 |
+
... "The welding torch consistently emits sparks and light, and the smoke continues to rise, diffusing slightly as it moves upward. "
|
| 138 |
+
... "The metal surface beneath the torch shows ongoing signs of heating and melting. The scene retains its industrial ambiance, with "
|
| 139 |
+
... "the welding sparks and smoke dominating the visual field, underscoring the ongoing nature of the welding operation."
|
| 140 |
+
... )
|
| 141 |
+
>>> image = load_image(
|
| 142 |
+
... "https://media.githubusercontent.com/media/nvidia-cosmos/cosmos-predict2.5/refs/heads/main/assets/base/robot_welding.jpg"
|
| 143 |
+
... )
|
| 144 |
+
>>> video = pipe(
|
| 145 |
+
... image=image,
|
| 146 |
+
... video=None,
|
| 147 |
+
... prompt=prompt,
|
| 148 |
+
... negative_prompt=negative_prompt,
|
| 149 |
+
... num_frames=93,
|
| 150 |
+
... generator=torch.Generator().manual_seed(1),
|
| 151 |
+
... ).frames[0]
|
| 152 |
+
>>> export_to_video(video, "image2world.mp4", fps=16)
|
| 153 |
+
|
| 154 |
+
>>> # Video2World: condition on an input clip and predict a 93-frame world video.
|
| 155 |
+
>>> prompt = (
|
| 156 |
+
... "The video opens with an aerial view of a large-scale sand mining construction operation, showcasing extensive piles "
|
| 157 |
+
... "of brown sand meticulously arranged in parallel rows. A central water channel, fed by a water pipe, flows through the "
|
| 158 |
+
... "middle of these sand heaps, creating ripples and movement as it cascades down. The surrounding area features dense green "
|
| 159 |
+
... "vegetation on the left, contrasting with the sandy terrain, while a body of water is visible in the background on the right. "
|
| 160 |
+
... "As the video progresses, a piece of heavy machinery, likely a bulldozer, enters the frame from the right, moving slowly along "
|
| 161 |
+
... "the edge of the sand piles. This machinery's presence indicates ongoing construction work in the operation. The final frame "
|
| 162 |
+
... "captures the same scene, with the water continuing its flow and the bulldozer still in motion, maintaining the dynamic yet "
|
| 163 |
+
... "steady pace of the construction activity."
|
| 164 |
+
... )
|
| 165 |
+
>>> input_video = load_video(
|
| 166 |
+
... "https://github.com/nvidia-cosmos/cosmos-predict2.5/raw/refs/heads/main/assets/base/sand_mining.mp4"
|
| 167 |
+
... )
|
| 168 |
+
>>> video = pipe(
|
| 169 |
+
... image=None,
|
| 170 |
+
... video=input_video,
|
| 171 |
+
... prompt=prompt,
|
| 172 |
+
... negative_prompt=negative_prompt,
|
| 173 |
+
... num_frames=93,
|
| 174 |
+
... generator=torch.Generator().manual_seed(1),
|
| 175 |
+
... ).frames[0]
|
| 176 |
+
>>> export_to_video(video, "video2world.mp4", fps=16)
|
| 177 |
+
|
| 178 |
+
>>> # To produce an image instead of a world (video) clip, set num_frames=1 and
|
| 179 |
+
>>> # save the first frame: pipe(..., num_frames=1).frames[0][0].
|
| 180 |
+
```
|
| 181 |
+
"""
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
class Cosmos2_5_PredictBasePipeline(DiffusionPipeline):
|
| 185 |
+
r"""
|
| 186 |
+
Pipeline for [Cosmos Predict2.5](https://github.com/nvidia-cosmos/cosmos-predict2.5) base model.
|
| 187 |
+
|
| 188 |
+
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
| 189 |
+
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
| 190 |
+
|
| 191 |
+
Args:
|
| 192 |
+
text_encoder ([`Qwen2_5_VLForConditionalGeneration`]):
|
| 193 |
+
Frozen text-encoder. Cosmos Predict2.5 uses the [Qwen2.5
|
| 194 |
+
VL](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct) encoder.
|
| 195 |
+
tokenizer (`AutoTokenizer`):
|
| 196 |
+
Tokenizer associated with the Qwen2.5 VL encoder.
|
| 197 |
+
transformer ([`CosmosTransformer3DModel`]):
|
| 198 |
+
Conditional Transformer to denoise the encoded image latents.
|
| 199 |
+
scheduler ([`UniPCMultistepScheduler`]):
|
| 200 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 201 |
+
vae ([`AutoencoderKLWan`]):
|
| 202 |
+
Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
|
| 203 |
+
"""
|
| 204 |
+
|
| 205 |
+
model_cpu_offload_seq = "text_encoder->transformer->vae"
|
| 206 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
| 207 |
+
# We mark safety_checker as optional here to get around some test failures, but it is not really optional
|
| 208 |
+
_optional_components = ["safety_checker"]
|
| 209 |
+
_exclude_from_cpu_offload = ["safety_checker"]
|
| 210 |
+
|
| 211 |
+
def __init__(
|
| 212 |
+
self,
|
| 213 |
+
text_encoder: Qwen2_5_VLForConditionalGeneration,
|
| 214 |
+
tokenizer: AutoTokenizer,
|
| 215 |
+
transformer: CosmosTransformer3DModel,
|
| 216 |
+
vae: AutoencoderKLWan,
|
| 217 |
+
scheduler: UniPCMultistepScheduler,
|
| 218 |
+
safety_checker: CosmosSafetyChecker = None,
|
| 219 |
+
):
|
| 220 |
+
super().__init__()
|
| 221 |
+
|
| 222 |
+
if safety_checker is None:
|
| 223 |
+
safety_checker = CosmosSafetyChecker()
|
| 224 |
+
|
| 225 |
+
self.register_modules(
|
| 226 |
+
vae=vae,
|
| 227 |
+
text_encoder=text_encoder,
|
| 228 |
+
tokenizer=tokenizer,
|
| 229 |
+
transformer=transformer,
|
| 230 |
+
scheduler=scheduler,
|
| 231 |
+
safety_checker=safety_checker,
|
| 232 |
+
)
|
| 233 |
+
|
| 234 |
+
self.vae_scale_factor_temporal = 2 ** sum(self.vae.temperal_downsample) if getattr(self, "vae", None) else 4
|
| 235 |
+
self.vae_scale_factor_spatial = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
|
| 236 |
+
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
|
| 237 |
+
|
| 238 |
+
latents_mean = (
|
| 239 |
+
torch.tensor(self.vae.config.latents_mean).view(1, self.vae.config.z_dim, 1, 1, 1).float()
|
| 240 |
+
if getattr(self.vae.config, "latents_mean", None) is not None
|
| 241 |
+
else None
|
| 242 |
+
)
|
| 243 |
+
latents_std = (
|
| 244 |
+
torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).float()
|
| 245 |
+
if getattr(self.vae.config, "latents_std", None) is not None
|
| 246 |
+
else None
|
| 247 |
+
)
|
| 248 |
+
self.latents_mean = latents_mean
|
| 249 |
+
self.latents_std = latents_std
|
| 250 |
+
|
| 251 |
+
if self.latents_mean is None or self.latents_std is None:
|
| 252 |
+
raise ValueError("VAE configuration must define both `latents_mean` and `latents_std`.")
|
| 253 |
+
|
| 254 |
+
def _get_prompt_embeds(
|
| 255 |
+
self,
|
| 256 |
+
prompt: str | list[str] = None,
|
| 257 |
+
max_sequence_length: int = 512,
|
| 258 |
+
device: torch.device | None = None,
|
| 259 |
+
dtype: torch.dtype | None = None,
|
| 260 |
+
):
|
| 261 |
+
device = device or self._execution_device
|
| 262 |
+
dtype = dtype or self.text_encoder.dtype
|
| 263 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 264 |
+
|
| 265 |
+
input_ids_batch = []
|
| 266 |
+
|
| 267 |
+
for sample_idx in range(len(prompt)):
|
| 268 |
+
conversations = [
|
| 269 |
+
{
|
| 270 |
+
"role": "system",
|
| 271 |
+
"content": [
|
| 272 |
+
{
|
| 273 |
+
"type": "text",
|
| 274 |
+
"text": "You are a helpful assistant who will provide prompts to an image generator.",
|
| 275 |
+
}
|
| 276 |
+
],
|
| 277 |
+
},
|
| 278 |
+
{
|
| 279 |
+
"role": "user",
|
| 280 |
+
"content": [
|
| 281 |
+
{
|
| 282 |
+
"type": "text",
|
| 283 |
+
"text": prompt[sample_idx],
|
| 284 |
+
}
|
| 285 |
+
],
|
| 286 |
+
},
|
| 287 |
+
]
|
| 288 |
+
input_ids = self.tokenizer.apply_chat_template(
|
| 289 |
+
conversations,
|
| 290 |
+
tokenize=True,
|
| 291 |
+
add_generation_prompt=False,
|
| 292 |
+
add_vision_id=False,
|
| 293 |
+
max_length=max_sequence_length,
|
| 294 |
+
truncation=True,
|
| 295 |
+
padding="max_length",
|
| 296 |
+
)
|
| 297 |
+
input_ids = (
|
| 298 |
+
input_ids["input_ids"] if not isinstance(input_ids, list) and "input_ids" in input_ids else input_ids
|
| 299 |
+
)
|
| 300 |
+
input_ids = torch.LongTensor(input_ids)
|
| 301 |
+
input_ids_batch.append(input_ids)
|
| 302 |
+
|
| 303 |
+
input_ids_batch = torch.stack(input_ids_batch, dim=0)
|
| 304 |
+
|
| 305 |
+
outputs = self.text_encoder(
|
| 306 |
+
input_ids_batch.to(device),
|
| 307 |
+
output_hidden_states=True,
|
| 308 |
+
)
|
| 309 |
+
hidden_states = outputs.hidden_states
|
| 310 |
+
|
| 311 |
+
normalized_hidden_states = []
|
| 312 |
+
for layer_idx in range(1, len(hidden_states)):
|
| 313 |
+
normalized_state = (hidden_states[layer_idx] - hidden_states[layer_idx].mean(dim=-1, keepdim=True)) / (
|
| 314 |
+
hidden_states[layer_idx].std(dim=-1, keepdim=True) + 1e-8
|
| 315 |
+
)
|
| 316 |
+
normalized_hidden_states.append(normalized_state)
|
| 317 |
+
|
| 318 |
+
prompt_embeds = torch.cat(normalized_hidden_states, dim=-1)
|
| 319 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 320 |
+
|
| 321 |
+
return prompt_embeds
|
| 322 |
+
|
| 323 |
+
# Modified from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt
|
| 324 |
+
def encode_prompt(
|
| 325 |
+
self,
|
| 326 |
+
prompt: str | list[str],
|
| 327 |
+
negative_prompt: str | list[str] | None = None,
|
| 328 |
+
do_classifier_free_guidance: bool = True,
|
| 329 |
+
num_videos_per_prompt: int = 1,
|
| 330 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 331 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 332 |
+
max_sequence_length: int = 512,
|
| 333 |
+
device: torch.device | None = None,
|
| 334 |
+
dtype: torch.dtype | None = None,
|
| 335 |
+
):
|
| 336 |
+
r"""
|
| 337 |
+
Encodes the prompt into text encoder hidden states.
|
| 338 |
+
|
| 339 |
+
Args:
|
| 340 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 341 |
+
prompt to be encoded
|
| 342 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 343 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 344 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 345 |
+
less than `1`).
|
| 346 |
+
do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
|
| 347 |
+
Whether to use classifier free guidance or not.
|
| 348 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 349 |
+
Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
|
| 350 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 351 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 352 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 353 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 354 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 355 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 356 |
+
argument.
|
| 357 |
+
device: (`torch.device`, *optional*):
|
| 358 |
+
torch device
|
| 359 |
+
dtype: (`torch.dtype`, *optional*):
|
| 360 |
+
torch dtype
|
| 361 |
+
"""
|
| 362 |
+
device = device or self._execution_device
|
| 363 |
+
|
| 364 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 365 |
+
if prompt is not None:
|
| 366 |
+
batch_size = len(prompt)
|
| 367 |
+
else:
|
| 368 |
+
batch_size = prompt_embeds.shape[0]
|
| 369 |
+
|
| 370 |
+
if prompt_embeds is None:
|
| 371 |
+
prompt_embeds = self._get_prompt_embeds(
|
| 372 |
+
prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 376 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 377 |
+
prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 378 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 379 |
+
|
| 380 |
+
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
| 381 |
+
negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
|
| 382 |
+
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 383 |
+
|
| 384 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 385 |
+
raise TypeError(
|
| 386 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 387 |
+
f" {type(prompt)}."
|
| 388 |
+
)
|
| 389 |
+
elif batch_size != len(negative_prompt):
|
| 390 |
+
raise ValueError(
|
| 391 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 392 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 393 |
+
" the batch size of `prompt`."
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
negative_prompt_embeds = self._get_prompt_embeds(
|
| 397 |
+
prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 401 |
+
_, seq_len, _ = negative_prompt_embeds.shape
|
| 402 |
+
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 403 |
+
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 404 |
+
|
| 405 |
+
return prompt_embeds, negative_prompt_embeds
|
| 406 |
+
|
| 407 |
+
# Modified from diffusers.pipelines.cosmos.pipeline_cosmos2_video2world.Cosmos2VideoToWorldPipeline.prepare_latents and
|
| 408 |
+
# diffusers.pipelines.cosmos.pipeline_cosmos2_video2world.Cosmos2TextToImagePipeline.prepare_latents
|
| 409 |
+
def prepare_latents(
|
| 410 |
+
self,
|
| 411 |
+
video: torch.Tensor | None,
|
| 412 |
+
batch_size: int,
|
| 413 |
+
num_channels_latents: int = 16,
|
| 414 |
+
height: int = 704,
|
| 415 |
+
width: int = 1280,
|
| 416 |
+
num_frames_in: int = 93,
|
| 417 |
+
num_frames_out: int = 93,
|
| 418 |
+
do_classifier_free_guidance: bool = True,
|
| 419 |
+
dtype: torch.dtype | None = None,
|
| 420 |
+
device: torch.device | None = None,
|
| 421 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 422 |
+
latents: torch.Tensor | None = None,
|
| 423 |
+
) -> torch.Tensor:
|
| 424 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 425 |
+
raise ValueError(
|
| 426 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 427 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 428 |
+
)
|
| 429 |
+
|
| 430 |
+
B = batch_size
|
| 431 |
+
C = num_channels_latents
|
| 432 |
+
T = (num_frames_out - 1) // self.vae_scale_factor_temporal + 1
|
| 433 |
+
H = height // self.vae_scale_factor_spatial
|
| 434 |
+
W = width // self.vae_scale_factor_spatial
|
| 435 |
+
shape = (B, C, T, H, W)
|
| 436 |
+
|
| 437 |
+
if num_frames_in == 0:
|
| 438 |
+
if latents is None:
|
| 439 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 440 |
+
|
| 441 |
+
cond_mask = torch.zeros((B, 1, T, H, W), dtype=latents.dtype, device=latents.device)
|
| 442 |
+
cond_indicator = torch.zeros((B, 1, T, 1, 1), dtype=latents.dtype, device=latents.device)
|
| 443 |
+
|
| 444 |
+
cond_latents = torch.zeros_like(latents)
|
| 445 |
+
|
| 446 |
+
return (
|
| 447 |
+
latents,
|
| 448 |
+
cond_latents,
|
| 449 |
+
cond_mask,
|
| 450 |
+
cond_indicator,
|
| 451 |
+
)
|
| 452 |
+
else:
|
| 453 |
+
if video is None:
|
| 454 |
+
raise ValueError("`video` must be provided when `num_frames_in` is greater than 0.")
|
| 455 |
+
needs_preprocessing = not (isinstance(video, torch.Tensor) and video.ndim == 5 and video.shape[1] == 3)
|
| 456 |
+
if needs_preprocessing:
|
| 457 |
+
video = self.video_processor.preprocess_video(video, height, width)
|
| 458 |
+
video = video.to(device=device, dtype=self.vae.dtype)
|
| 459 |
+
if isinstance(generator, list):
|
| 460 |
+
cond_latents = [
|
| 461 |
+
retrieve_latents(self.vae.encode(video[i].unsqueeze(0)), generator=generator[i])
|
| 462 |
+
for i in range(batch_size)
|
| 463 |
+
]
|
| 464 |
+
else:
|
| 465 |
+
cond_latents = [retrieve_latents(self.vae.encode(vid.unsqueeze(0)), generator) for vid in video]
|
| 466 |
+
|
| 467 |
+
cond_latents = torch.cat(cond_latents, dim=0).to(dtype)
|
| 468 |
+
|
| 469 |
+
latents_mean = self.latents_mean.to(device=device, dtype=dtype)
|
| 470 |
+
latents_std = self.latents_std.to(device=device, dtype=dtype)
|
| 471 |
+
cond_latents = (cond_latents - latents_mean) / latents_std
|
| 472 |
+
|
| 473 |
+
if latents is None:
|
| 474 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 475 |
+
else:
|
| 476 |
+
latents = latents.to(device=device, dtype=dtype)
|
| 477 |
+
|
| 478 |
+
padding_shape = (B, 1, T, H, W)
|
| 479 |
+
ones_padding = latents.new_ones(padding_shape)
|
| 480 |
+
zeros_padding = latents.new_zeros(padding_shape)
|
| 481 |
+
|
| 482 |
+
num_cond_latent_frames = (num_frames_in - 1) // self.vae_scale_factor_temporal + 1
|
| 483 |
+
cond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
|
| 484 |
+
cond_indicator[:, :, 0:num_cond_latent_frames] = 1.0
|
| 485 |
+
cond_mask = cond_indicator * ones_padding + (1 - cond_indicator) * zeros_padding
|
| 486 |
+
|
| 487 |
+
return (
|
| 488 |
+
latents,
|
| 489 |
+
cond_latents,
|
| 490 |
+
cond_mask,
|
| 491 |
+
cond_indicator,
|
| 492 |
+
)
|
| 493 |
+
|
| 494 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.check_inputs
|
| 495 |
+
def check_inputs(
|
| 496 |
+
self,
|
| 497 |
+
prompt,
|
| 498 |
+
height,
|
| 499 |
+
width,
|
| 500 |
+
prompt_embeds=None,
|
| 501 |
+
callback_on_step_end_tensor_inputs=None,
|
| 502 |
+
):
|
| 503 |
+
if height % 16 != 0 or width % 16 != 0:
|
| 504 |
+
raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
|
| 505 |
+
|
| 506 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 507 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 508 |
+
):
|
| 509 |
+
raise ValueError(
|
| 510 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 511 |
+
)
|
| 512 |
+
|
| 513 |
+
if prompt is not None and prompt_embeds is not None:
|
| 514 |
+
raise ValueError(
|
| 515 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 516 |
+
" only forward one of the two."
|
| 517 |
+
)
|
| 518 |
+
elif prompt is None and prompt_embeds is None:
|
| 519 |
+
raise ValueError(
|
| 520 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 521 |
+
)
|
| 522 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 523 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 524 |
+
|
| 525 |
+
@property
|
| 526 |
+
def guidance_scale(self):
|
| 527 |
+
return self._guidance_scale
|
| 528 |
+
|
| 529 |
+
@property
|
| 530 |
+
def do_classifier_free_guidance(self):
|
| 531 |
+
return self._guidance_scale > 1.0
|
| 532 |
+
|
| 533 |
+
@property
|
| 534 |
+
def num_timesteps(self):
|
| 535 |
+
return self._num_timesteps
|
| 536 |
+
|
| 537 |
+
@property
|
| 538 |
+
def current_timestep(self):
|
| 539 |
+
return self._current_timestep
|
| 540 |
+
|
| 541 |
+
@property
|
| 542 |
+
def interrupt(self):
|
| 543 |
+
return self._interrupt
|
| 544 |
+
|
| 545 |
+
@torch.no_grad()
|
| 546 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 547 |
+
def __call__(
|
| 548 |
+
self,
|
| 549 |
+
image: PipelineImageInput | None = None,
|
| 550 |
+
video: list[PipelineImageInput] | None = None,
|
| 551 |
+
prompt: str | list[str] | None = None,
|
| 552 |
+
negative_prompt: str | list[str] | None = None,
|
| 553 |
+
height: int = 704,
|
| 554 |
+
width: int = 1280,
|
| 555 |
+
num_frames: int = 93,
|
| 556 |
+
num_inference_steps: int = 36,
|
| 557 |
+
guidance_scale: float = 7.0,
|
| 558 |
+
num_videos_per_prompt: int | None = 1,
|
| 559 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 560 |
+
latents: torch.Tensor | None = None,
|
| 561 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 562 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 563 |
+
output_type: str | None = "pil",
|
| 564 |
+
return_dict: bool = True,
|
| 565 |
+
callback_on_step_end: Callable[[int, int, None], PipelineCallback | MultiPipelineCallbacks] | None = None,
|
| 566 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 567 |
+
max_sequence_length: int = 512,
|
| 568 |
+
conditional_frame_timestep: float = 0.1,
|
| 569 |
+
num_latent_conditional_frames: int = 2,
|
| 570 |
+
):
|
| 571 |
+
r"""
|
| 572 |
+
The call function to the pipeline for generation. Supports three modes:
|
| 573 |
+
|
| 574 |
+
- **Text2World**: `image=None`, `video=None`, `prompt` provided. Generates a world clip.
|
| 575 |
+
- **Image2World**: `image` provided, `video=None`, `prompt` provided. Conditions on a single frame.
|
| 576 |
+
- **Video2World**: `video` provided, `image=None`, `prompt` provided. Conditions on an input clip.
|
| 577 |
+
|
| 578 |
+
Set `num_frames=93` (default) to produce a world video, or `num_frames=1` to produce a single image frame (the
|
| 579 |
+
above in "*2Image mode").
|
| 580 |
+
|
| 581 |
+
Outputs follow `output_type` (e.g., `"pil"` returns a list of `num_frames` PIL images per prompt).
|
| 582 |
+
|
| 583 |
+
Args:
|
| 584 |
+
image (`PIL.Image.Image`, `np.ndarray`, `torch.Tensor`, *optional*):
|
| 585 |
+
Optional single image for Image2World conditioning. Must be `None` when `video` is provided.
|
| 586 |
+
video (`list[PIL.Image.Image]`, `np.ndarray`, `torch.Tensor`, *optional*):
|
| 587 |
+
Optional input video for Video2World conditioning. Must be `None` when `image` is provided.
|
| 588 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 589 |
+
The prompt or prompts to guide generation. Required unless `prompt_embeds` is supplied.
|
| 590 |
+
height (`int`, defaults to `704`):
|
| 591 |
+
The height in pixels of the generated image.
|
| 592 |
+
width (`int`, defaults to `1280`):
|
| 593 |
+
The width in pixels of the generated image.
|
| 594 |
+
num_frames (`int`, defaults to `93`):
|
| 595 |
+
Number of output frames. Use `93` for world (video) generation; set to `1` to return a single frame.
|
| 596 |
+
num_inference_steps (`int`, defaults to `35`):
|
| 597 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 598 |
+
expense of slower inference.
|
| 599 |
+
guidance_scale (`float`, defaults to `7.0`):
|
| 600 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 601 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 602 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 603 |
+
`guidance_scale > 1`.
|
| 604 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 605 |
+
The number of images to generate per prompt.
|
| 606 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 607 |
+
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
| 608 |
+
generation deterministic.
|
| 609 |
+
latents (`torch.Tensor`, *optional*):
|
| 610 |
+
Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
|
| 611 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 612 |
+
tensor is generated by sampling using the supplied random `generator`.
|
| 613 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 614 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 615 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 616 |
+
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 617 |
+
Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
|
| 618 |
+
provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
|
| 619 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 620 |
+
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
| 621 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 622 |
+
Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
|
| 623 |
+
callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
|
| 624 |
+
A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
|
| 625 |
+
each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
|
| 626 |
+
DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
|
| 627 |
+
list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
|
| 628 |
+
callback_on_step_end_tensor_inputs (`List`, *optional*):
|
| 629 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 630 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 631 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 632 |
+
max_sequence_length (`int`, defaults to `512`):
|
| 633 |
+
The maximum number of tokens in the prompt. If the prompt exceeds this length, it will be truncated. If
|
| 634 |
+
the prompt is shorter than this length, it will be padded.
|
| 635 |
+
num_latent_conditional_frames (`int`, defaults to `2`):
|
| 636 |
+
Number of latent conditional frames to use for Video2World conditioning. The number of pixel frames
|
| 637 |
+
extracted from the input video is calculated as `4 * (num_latent_conditional_frames - 1) + 1`. Set to 1
|
| 638 |
+
for Image2World-like behavior (single frame conditioning).
|
| 639 |
+
|
| 640 |
+
Examples:
|
| 641 |
+
|
| 642 |
+
Returns:
|
| 643 |
+
[`~CosmosPipelineOutput`] or `tuple`:
|
| 644 |
+
If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
|
| 645 |
+
the first element is a list with the generated images and the second element is a list of `bool`s
|
| 646 |
+
indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
|
| 647 |
+
"""
|
| 648 |
+
if self.safety_checker is None:
|
| 649 |
+
raise ValueError(
|
| 650 |
+
f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
|
| 651 |
+
"[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
|
| 652 |
+
f"Please ensure that you are compliant with the license agreement."
|
| 653 |
+
)
|
| 654 |
+
|
| 655 |
+
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
|
| 656 |
+
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
|
| 657 |
+
|
| 658 |
+
# Check inputs. Raise error if not correct
|
| 659 |
+
self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
|
| 660 |
+
|
| 661 |
+
self._guidance_scale = guidance_scale
|
| 662 |
+
self._current_timestep = None
|
| 663 |
+
self._interrupt = False
|
| 664 |
+
|
| 665 |
+
device = self._execution_device
|
| 666 |
+
|
| 667 |
+
if self.safety_checker is not None:
|
| 668 |
+
self.safety_checker.to(device)
|
| 669 |
+
if prompt is not None:
|
| 670 |
+
prompt_list = [prompt] if isinstance(prompt, str) else prompt
|
| 671 |
+
for p in prompt_list:
|
| 672 |
+
if not self.safety_checker.check_text_safety(p):
|
| 673 |
+
raise ValueError(
|
| 674 |
+
f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
|
| 675 |
+
f"prompt abides by the NVIDIA Open Model License Agreement."
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
# Define call parameters
|
| 679 |
+
if prompt is not None and isinstance(prompt, str):
|
| 680 |
+
batch_size = 1
|
| 681 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 682 |
+
batch_size = len(prompt)
|
| 683 |
+
else:
|
| 684 |
+
batch_size = prompt_embeds.shape[0]
|
| 685 |
+
|
| 686 |
+
# Encode input prompt
|
| 687 |
+
(
|
| 688 |
+
prompt_embeds,
|
| 689 |
+
negative_prompt_embeds,
|
| 690 |
+
) = self.encode_prompt(
|
| 691 |
+
prompt=prompt,
|
| 692 |
+
negative_prompt=negative_prompt,
|
| 693 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 694 |
+
num_videos_per_prompt=num_videos_per_prompt,
|
| 695 |
+
prompt_embeds=prompt_embeds,
|
| 696 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 697 |
+
device=device,
|
| 698 |
+
max_sequence_length=max_sequence_length,
|
| 699 |
+
)
|
| 700 |
+
|
| 701 |
+
vae_dtype = self.vae.dtype
|
| 702 |
+
transformer_dtype = self.transformer.dtype
|
| 703 |
+
|
| 704 |
+
num_frames_in = None
|
| 705 |
+
if image is not None:
|
| 706 |
+
if batch_size != 1:
|
| 707 |
+
raise ValueError(f"batch_size must be 1 for image input (given {batch_size})")
|
| 708 |
+
|
| 709 |
+
image = torchvision.transforms.functional.to_tensor(image).unsqueeze(0)
|
| 710 |
+
video = torch.cat([image, torch.zeros_like(image).repeat(num_frames - 1, 1, 1, 1)], dim=0)
|
| 711 |
+
video = video.unsqueeze(0)
|
| 712 |
+
num_frames_in = 1
|
| 713 |
+
elif video is None:
|
| 714 |
+
video = torch.zeros(batch_size, num_frames, 3, height, width, dtype=torch.uint8)
|
| 715 |
+
num_frames_in = 0
|
| 716 |
+
else:
|
| 717 |
+
if batch_size != 1:
|
| 718 |
+
raise ValueError(f"batch_size must be 1 for video input (given {batch_size})")
|
| 719 |
+
|
| 720 |
+
if num_latent_conditional_frames not in [1, 2]:
|
| 721 |
+
raise ValueError(
|
| 722 |
+
f"num_latent_conditional_frames must be 1 or 2, but got {num_latent_conditional_frames}"
|
| 723 |
+
)
|
| 724 |
+
|
| 725 |
+
frames_to_extract = 4 * (num_latent_conditional_frames - 1) + 1
|
| 726 |
+
|
| 727 |
+
total_input_frames = len(video)
|
| 728 |
+
|
| 729 |
+
if total_input_frames < frames_to_extract:
|
| 730 |
+
raise ValueError(
|
| 731 |
+
f"Input video has only {total_input_frames} frames but Video2World requires at least "
|
| 732 |
+
f"{frames_to_extract} frames for conditioning."
|
| 733 |
+
)
|
| 734 |
+
|
| 735 |
+
num_frames_in = frames_to_extract
|
| 736 |
+
|
| 737 |
+
assert video is not None
|
| 738 |
+
video = self.video_processor.preprocess_video(video, height, width)
|
| 739 |
+
|
| 740 |
+
# For Video2World: extract last frames_to_extract frames from input, then pad
|
| 741 |
+
if image is None and num_frames_in > 0 and num_frames_in < video.shape[2]:
|
| 742 |
+
video = video[:, :, -num_frames_in:, :, :]
|
| 743 |
+
|
| 744 |
+
num_frames_out = num_frames
|
| 745 |
+
|
| 746 |
+
if video.shape[2] < num_frames_out:
|
| 747 |
+
n_pad_frames = num_frames_out - video.shape[2]
|
| 748 |
+
last_frame = video[:, :, -1:, :, :] # [B, C, T==1, H, W]
|
| 749 |
+
pad_frames = last_frame.repeat(1, 1, n_pad_frames, 1, 1) # [B, C, T, H, W]
|
| 750 |
+
video = torch.cat((video, pad_frames), dim=2)
|
| 751 |
+
|
| 752 |
+
assert num_frames_in <= num_frames_out, f"expected ({num_frames_in=}) <= ({num_frames_out=})"
|
| 753 |
+
|
| 754 |
+
video = video.to(device=device, dtype=vae_dtype)
|
| 755 |
+
|
| 756 |
+
num_channels_latents = self.transformer.config.in_channels - 1
|
| 757 |
+
latents, cond_latent, cond_mask, cond_indicator = self.prepare_latents(
|
| 758 |
+
video=video,
|
| 759 |
+
batch_size=batch_size * num_videos_per_prompt,
|
| 760 |
+
num_channels_latents=num_channels_latents,
|
| 761 |
+
height=height,
|
| 762 |
+
width=width,
|
| 763 |
+
num_frames_in=num_frames_in,
|
| 764 |
+
num_frames_out=num_frames,
|
| 765 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 766 |
+
dtype=torch.float32,
|
| 767 |
+
device=device,
|
| 768 |
+
generator=generator,
|
| 769 |
+
latents=latents,
|
| 770 |
+
)
|
| 771 |
+
cond_timestep = torch.ones_like(cond_indicator) * conditional_frame_timestep
|
| 772 |
+
cond_mask = cond_mask.to(transformer_dtype)
|
| 773 |
+
|
| 774 |
+
padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
|
| 775 |
+
|
| 776 |
+
# Denoising loop
|
| 777 |
+
self.scheduler.set_timesteps(num_inference_steps, device=device)
|
| 778 |
+
timesteps = self.scheduler.timesteps
|
| 779 |
+
self._num_timesteps = len(timesteps)
|
| 780 |
+
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
| 781 |
+
|
| 782 |
+
gt_velocity = (latents - cond_latent) * cond_mask
|
| 783 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 784 |
+
for i, t in enumerate(timesteps):
|
| 785 |
+
if self.interrupt:
|
| 786 |
+
continue
|
| 787 |
+
|
| 788 |
+
self._current_timestep = t.cpu().item()
|
| 789 |
+
|
| 790 |
+
# NOTE: assumes sigma(t) \in [0, 1]
|
| 791 |
+
sigma_t = (
|
| 792 |
+
torch.tensor(self.scheduler.sigmas[i].item())
|
| 793 |
+
.unsqueeze(0)
|
| 794 |
+
.to(device=device, dtype=transformer_dtype)
|
| 795 |
+
)
|
| 796 |
+
|
| 797 |
+
in_latents = cond_mask * cond_latent + (1 - cond_mask) * latents
|
| 798 |
+
in_latents = in_latents.to(transformer_dtype)
|
| 799 |
+
in_timestep = cond_indicator * cond_timestep + (1 - cond_indicator) * sigma_t
|
| 800 |
+
noise_pred = self.transformer(
|
| 801 |
+
hidden_states=in_latents,
|
| 802 |
+
condition_mask=cond_mask,
|
| 803 |
+
timestep=in_timestep,
|
| 804 |
+
encoder_hidden_states=prompt_embeds,
|
| 805 |
+
padding_mask=padding_mask,
|
| 806 |
+
return_dict=False,
|
| 807 |
+
)[0]
|
| 808 |
+
# NOTE: replace velocity (noise_pred) with gt_velocity for conditioning inputs only
|
| 809 |
+
noise_pred = gt_velocity + noise_pred * (1 - cond_mask)
|
| 810 |
+
|
| 811 |
+
if self.do_classifier_free_guidance:
|
| 812 |
+
noise_pred_neg = self.transformer(
|
| 813 |
+
hidden_states=in_latents,
|
| 814 |
+
condition_mask=cond_mask,
|
| 815 |
+
timestep=in_timestep,
|
| 816 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 817 |
+
padding_mask=padding_mask,
|
| 818 |
+
return_dict=False,
|
| 819 |
+
)[0]
|
| 820 |
+
# NOTE: replace velocity (noise_pred_neg) with gt_velocity for conditioning inputs only
|
| 821 |
+
noise_pred_neg = gt_velocity + noise_pred_neg * (1 - cond_mask)
|
| 822 |
+
noise_pred = noise_pred + self.guidance_scale * (noise_pred - noise_pred_neg)
|
| 823 |
+
|
| 824 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 825 |
+
|
| 826 |
+
if callback_on_step_end is not None:
|
| 827 |
+
callback_kwargs = {}
|
| 828 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 829 |
+
callback_kwargs[k] = locals()[k]
|
| 830 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 831 |
+
|
| 832 |
+
latents = callback_outputs.pop("latents", latents)
|
| 833 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 834 |
+
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
|
| 835 |
+
|
| 836 |
+
# call the callback, if provided
|
| 837 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 838 |
+
progress_bar.update()
|
| 839 |
+
|
| 840 |
+
if XLA_AVAILABLE:
|
| 841 |
+
xm.mark_step()
|
| 842 |
+
|
| 843 |
+
self._current_timestep = None
|
| 844 |
+
|
| 845 |
+
if not output_type == "latent":
|
| 846 |
+
latents_mean = self.latents_mean.to(latents.device, latents.dtype)
|
| 847 |
+
latents_std = self.latents_std.to(latents.device, latents.dtype)
|
| 848 |
+
latents = latents * latents_std + latents_mean
|
| 849 |
+
video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
|
| 850 |
+
video = self._match_num_frames(video, num_frames)
|
| 851 |
+
|
| 852 |
+
assert self.safety_checker is not None
|
| 853 |
+
self.safety_checker.to(device)
|
| 854 |
+
video = self.video_processor.postprocess_video(video, output_type="np")
|
| 855 |
+
video = (video * 255).astype(np.uint8)
|
| 856 |
+
video_batch = []
|
| 857 |
+
for vid in video:
|
| 858 |
+
vid = self.safety_checker.check_video_safety(vid)
|
| 859 |
+
video_batch.append(vid)
|
| 860 |
+
video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
|
| 861 |
+
video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
|
| 862 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 863 |
+
else:
|
| 864 |
+
video = latents
|
| 865 |
+
|
| 866 |
+
# Offload all models
|
| 867 |
+
self.maybe_free_model_hooks()
|
| 868 |
+
|
| 869 |
+
if not return_dict:
|
| 870 |
+
return (video,)
|
| 871 |
+
|
| 872 |
+
return CosmosPipelineOutput(frames=video)
|
| 873 |
+
|
| 874 |
+
def _match_num_frames(self, video: torch.Tensor, target_num_frames: int) -> torch.Tensor:
|
| 875 |
+
if target_num_frames <= 0 or video.shape[2] == target_num_frames:
|
| 876 |
+
return video
|
| 877 |
+
|
| 878 |
+
frames_per_latent = max(self.vae_scale_factor_temporal, 1)
|
| 879 |
+
video = torch.repeat_interleave(video, repeats=frames_per_latent, dim=2)
|
| 880 |
+
|
| 881 |
+
current_frames = video.shape[2]
|
| 882 |
+
if current_frames < target_num_frames:
|
| 883 |
+
pad = video[:, :, -1:, :, :].repeat(1, 1, target_num_frames - current_frames, 1, 1)
|
| 884 |
+
video = torch.cat([video, pad], dim=2)
|
| 885 |
+
elif current_frames > target_num_frames:
|
| 886 |
+
video = video[:, :, :target_num_frames]
|
| 887 |
+
|
| 888 |
+
return video
|
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_text2image.py
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from typing import Callable
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import T5EncoderModel, T5TokenizerFast
|
| 21 |
+
|
| 22 |
+
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
|
| 23 |
+
from ...models import AutoencoderKLWan, CosmosTransformer3DModel
|
| 24 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 25 |
+
from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
|
| 26 |
+
from ...utils.torch_utils import randn_tensor
|
| 27 |
+
from ...video_processor import VideoProcessor
|
| 28 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 29 |
+
from .pipeline_output import CosmosImagePipelineOutput
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
if is_cosmos_guardrail_available():
|
| 33 |
+
from cosmos_guardrail import CosmosSafetyChecker
|
| 34 |
+
else:
|
| 35 |
+
|
| 36 |
+
class CosmosSafetyChecker:
|
| 37 |
+
def __init__(self, *args, **kwargs):
|
| 38 |
+
raise ImportError(
|
| 39 |
+
"`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if is_torch_xla_available():
|
| 44 |
+
import torch_xla.core.xla_model as xm
|
| 45 |
+
|
| 46 |
+
XLA_AVAILABLE = True
|
| 47 |
+
else:
|
| 48 |
+
XLA_AVAILABLE = False
|
| 49 |
+
|
| 50 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 51 |
+
|
| 52 |
+
DEFAULT_NEGATIVE_PROMPT = (
|
| 53 |
+
"The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
|
| 54 |
+
"over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
|
| 55 |
+
"underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
|
| 56 |
+
"jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
|
| 57 |
+
"fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
|
| 58 |
+
"Overall, the video is of poor quality."
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
EXAMPLE_DOC_STRING = """
|
| 62 |
+
Examples:
|
| 63 |
+
```python
|
| 64 |
+
>>> import torch
|
| 65 |
+
>>> from diffusers import Cosmos2TextToImagePipeline
|
| 66 |
+
|
| 67 |
+
>>> # Available checkpoints: nvidia/Cosmos-Predict2-2B-Text2Image, nvidia/Cosmos-Predict2-14B-Text2Image
|
| 68 |
+
>>> model_id = "nvidia/Cosmos-Predict2-2B-Text2Image"
|
| 69 |
+
>>> pipe = Cosmos2TextToImagePipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
|
| 70 |
+
>>> pipe.to("cuda")
|
| 71 |
+
|
| 72 |
+
>>> prompt = "A close-up shot captures a vibrant yellow scrubber vigorously working on a grimy plate, its bristles moving in circular motions to lift stubborn grease and food residue. The dish, once covered in remnants of a hearty meal, gradually reveals its original glossy surface. Suds form and bubble around the scrubber, creating a satisfying visual of cleanliness in progress. The sound of scrubbing fills the air, accompanied by the gentle clinking of the dish against the sink. As the scrubber continues its task, the dish transforms, gleaming under the bright kitchen lights, symbolizing the triumph of cleanliness over mess."
|
| 73 |
+
>>> negative_prompt = "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. Overall, the video is of poor quality."
|
| 74 |
+
|
| 75 |
+
>>> output = pipe(
|
| 76 |
+
... prompt=prompt, negative_prompt=negative_prompt, generator=torch.Generator().manual_seed(1)
|
| 77 |
+
... ).images[0]
|
| 78 |
+
>>> output.save("output.png")
|
| 79 |
+
```
|
| 80 |
+
"""
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 84 |
+
def retrieve_timesteps(
|
| 85 |
+
scheduler,
|
| 86 |
+
num_inference_steps: int | None = None,
|
| 87 |
+
device: str | torch.device | None = None,
|
| 88 |
+
timesteps: list[int] | None = None,
|
| 89 |
+
sigmas: list[float] | None = None,
|
| 90 |
+
**kwargs,
|
| 91 |
+
):
|
| 92 |
+
r"""
|
| 93 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 94 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 95 |
+
|
| 96 |
+
Args:
|
| 97 |
+
scheduler (`SchedulerMixin`):
|
| 98 |
+
The scheduler to get timesteps from.
|
| 99 |
+
num_inference_steps (`int`):
|
| 100 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 101 |
+
must be `None`.
|
| 102 |
+
device (`str` or `torch.device`, *optional*):
|
| 103 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 104 |
+
timesteps (`list[int]`, *optional*):
|
| 105 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 106 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 107 |
+
sigmas (`list[float]`, *optional*):
|
| 108 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 109 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 110 |
+
|
| 111 |
+
Returns:
|
| 112 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 113 |
+
second element is the number of inference steps.
|
| 114 |
+
"""
|
| 115 |
+
if timesteps is not None and sigmas is not None:
|
| 116 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 117 |
+
if timesteps is not None:
|
| 118 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 119 |
+
if not accepts_timesteps:
|
| 120 |
+
raise ValueError(
|
| 121 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 122 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 123 |
+
)
|
| 124 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 125 |
+
timesteps = scheduler.timesteps
|
| 126 |
+
num_inference_steps = len(timesteps)
|
| 127 |
+
elif sigmas is not None:
|
| 128 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 129 |
+
if not accept_sigmas:
|
| 130 |
+
raise ValueError(
|
| 131 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 132 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 133 |
+
)
|
| 134 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 135 |
+
timesteps = scheduler.timesteps
|
| 136 |
+
num_inference_steps = len(timesteps)
|
| 137 |
+
else:
|
| 138 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 139 |
+
timesteps = scheduler.timesteps
|
| 140 |
+
return timesteps, num_inference_steps
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
class Cosmos2TextToImagePipeline(DiffusionPipeline):
|
| 144 |
+
r"""
|
| 145 |
+
Pipeline for text-to-image generation using [Cosmos Predict2](https://github.com/nvidia-cosmos/cosmos-predict2).
|
| 146 |
+
|
| 147 |
+
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
| 148 |
+
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
| 149 |
+
|
| 150 |
+
Args:
|
| 151 |
+
text_encoder ([`T5EncoderModel`]):
|
| 152 |
+
Frozen text-encoder. Cosmos uses
|
| 153 |
+
[T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
|
| 154 |
+
[t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
|
| 155 |
+
tokenizer (`T5TokenizerFast`):
|
| 156 |
+
Tokenizer of class
|
| 157 |
+
[T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
|
| 158 |
+
transformer ([`CosmosTransformer3DModel`]):
|
| 159 |
+
Conditional Transformer to denoise the encoded image latents.
|
| 160 |
+
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
| 161 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 162 |
+
vae ([`AutoencoderKLWan`]):
|
| 163 |
+
Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
|
| 164 |
+
"""
|
| 165 |
+
|
| 166 |
+
model_cpu_offload_seq = "text_encoder->transformer->vae"
|
| 167 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
| 168 |
+
# We mark safety_checker as optional here to get around some test failures, but it is not really optional
|
| 169 |
+
_optional_components = ["safety_checker"]
|
| 170 |
+
|
| 171 |
+
def __init__(
|
| 172 |
+
self,
|
| 173 |
+
text_encoder: T5EncoderModel,
|
| 174 |
+
tokenizer: T5TokenizerFast,
|
| 175 |
+
transformer: CosmosTransformer3DModel,
|
| 176 |
+
vae: AutoencoderKLWan,
|
| 177 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 178 |
+
safety_checker: CosmosSafetyChecker = None,
|
| 179 |
+
):
|
| 180 |
+
super().__init__()
|
| 181 |
+
|
| 182 |
+
if safety_checker is None:
|
| 183 |
+
safety_checker = CosmosSafetyChecker()
|
| 184 |
+
|
| 185 |
+
self.register_modules(
|
| 186 |
+
vae=vae,
|
| 187 |
+
text_encoder=text_encoder,
|
| 188 |
+
tokenizer=tokenizer,
|
| 189 |
+
transformer=transformer,
|
| 190 |
+
scheduler=scheduler,
|
| 191 |
+
safety_checker=safety_checker,
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
self.vae_scale_factor_temporal = 2 ** sum(self.vae.temperal_downsample) if getattr(self, "vae", None) else 4
|
| 195 |
+
self.vae_scale_factor_spatial = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
|
| 196 |
+
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
|
| 197 |
+
|
| 198 |
+
self.sigma_max = 80.0
|
| 199 |
+
self.sigma_min = 0.002
|
| 200 |
+
self.sigma_data = 1.0
|
| 201 |
+
self.final_sigmas_type = "sigma_min"
|
| 202 |
+
if self.scheduler is not None:
|
| 203 |
+
self.scheduler.register_to_config(
|
| 204 |
+
sigma_max=self.sigma_max,
|
| 205 |
+
sigma_min=self.sigma_min,
|
| 206 |
+
sigma_data=self.sigma_data,
|
| 207 |
+
final_sigmas_type=self.final_sigmas_type,
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline._get_t5_prompt_embeds
|
| 211 |
+
def _get_t5_prompt_embeds(
|
| 212 |
+
self,
|
| 213 |
+
prompt: str | list[str] = None,
|
| 214 |
+
max_sequence_length: int = 512,
|
| 215 |
+
device: torch.device | None = None,
|
| 216 |
+
dtype: torch.dtype | None = None,
|
| 217 |
+
):
|
| 218 |
+
device = device or self._execution_device
|
| 219 |
+
dtype = dtype or self.text_encoder.dtype
|
| 220 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 221 |
+
|
| 222 |
+
text_inputs = self.tokenizer(
|
| 223 |
+
prompt,
|
| 224 |
+
padding="max_length",
|
| 225 |
+
max_length=max_sequence_length,
|
| 226 |
+
truncation=True,
|
| 227 |
+
return_tensors="pt",
|
| 228 |
+
return_length=True,
|
| 229 |
+
return_offsets_mapping=False,
|
| 230 |
+
)
|
| 231 |
+
text_input_ids = text_inputs.input_ids
|
| 232 |
+
prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
|
| 233 |
+
|
| 234 |
+
untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
|
| 235 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 236 |
+
removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
|
| 237 |
+
logger.warning(
|
| 238 |
+
"The following part of your input was truncated because `max_sequence_length` is set to "
|
| 239 |
+
f" {max_sequence_length} tokens: {removed_text}"
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
prompt_embeds = self.text_encoder(
|
| 243 |
+
text_input_ids.to(device), attention_mask=prompt_attention_mask
|
| 244 |
+
).last_hidden_state
|
| 245 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 246 |
+
|
| 247 |
+
lengths = prompt_attention_mask.sum(dim=1).cpu()
|
| 248 |
+
for i, length in enumerate(lengths):
|
| 249 |
+
prompt_embeds[i, length:] = 0
|
| 250 |
+
|
| 251 |
+
return prompt_embeds
|
| 252 |
+
|
| 253 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt with num_videos_per_prompt->num_images_per_prompt
|
| 254 |
+
def encode_prompt(
|
| 255 |
+
self,
|
| 256 |
+
prompt: str | list[str],
|
| 257 |
+
negative_prompt: str | list[str] | None = None,
|
| 258 |
+
do_classifier_free_guidance: bool = True,
|
| 259 |
+
num_images_per_prompt: int = 1,
|
| 260 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 261 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 262 |
+
max_sequence_length: int = 512,
|
| 263 |
+
device: torch.device | None = None,
|
| 264 |
+
dtype: torch.dtype | None = None,
|
| 265 |
+
):
|
| 266 |
+
r"""
|
| 267 |
+
Encodes the prompt into text encoder hidden states.
|
| 268 |
+
|
| 269 |
+
Args:
|
| 270 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 271 |
+
prompt to be encoded
|
| 272 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 273 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 274 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 275 |
+
less than `1`).
|
| 276 |
+
do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
|
| 277 |
+
Whether to use classifier free guidance or not.
|
| 278 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 279 |
+
Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
|
| 280 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 281 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 282 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 283 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 284 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 285 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 286 |
+
argument.
|
| 287 |
+
device: (`torch.device`, *optional*):
|
| 288 |
+
torch device
|
| 289 |
+
dtype: (`torch.dtype`, *optional*):
|
| 290 |
+
torch dtype
|
| 291 |
+
"""
|
| 292 |
+
device = device or self._execution_device
|
| 293 |
+
|
| 294 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 295 |
+
if prompt is not None:
|
| 296 |
+
batch_size = len(prompt)
|
| 297 |
+
else:
|
| 298 |
+
batch_size = prompt_embeds.shape[0]
|
| 299 |
+
|
| 300 |
+
if prompt_embeds is None:
|
| 301 |
+
prompt_embeds = self._get_t5_prompt_embeds(
|
| 302 |
+
prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 306 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 307 |
+
prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
| 308 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 309 |
+
|
| 310 |
+
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
| 311 |
+
negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
|
| 312 |
+
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 313 |
+
|
| 314 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 315 |
+
raise TypeError(
|
| 316 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 317 |
+
f" {type(prompt)}."
|
| 318 |
+
)
|
| 319 |
+
elif batch_size != len(negative_prompt):
|
| 320 |
+
raise ValueError(
|
| 321 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 322 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 323 |
+
" the batch size of `prompt`."
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
negative_prompt_embeds = self._get_t5_prompt_embeds(
|
| 327 |
+
prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 331 |
+
_, seq_len, _ = negative_prompt_embeds.shape
|
| 332 |
+
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
|
| 333 |
+
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 334 |
+
|
| 335 |
+
return prompt_embeds, negative_prompt_embeds
|
| 336 |
+
|
| 337 |
+
def prepare_latents(
|
| 338 |
+
self,
|
| 339 |
+
batch_size: int,
|
| 340 |
+
num_channels_latents: 16,
|
| 341 |
+
height: int = 768,
|
| 342 |
+
width: int = 1360,
|
| 343 |
+
num_frames: int = 1,
|
| 344 |
+
dtype: torch.dtype | None = None,
|
| 345 |
+
device: torch.device | None = None,
|
| 346 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 347 |
+
latents: torch.Tensor | None = None,
|
| 348 |
+
) -> torch.Tensor:
|
| 349 |
+
if latents is not None:
|
| 350 |
+
return latents.to(device=device, dtype=dtype) * self.scheduler.config.sigma_max
|
| 351 |
+
|
| 352 |
+
num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
|
| 353 |
+
latent_height = height // self.vae_scale_factor_spatial
|
| 354 |
+
latent_width = width // self.vae_scale_factor_spatial
|
| 355 |
+
shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
|
| 356 |
+
|
| 357 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 358 |
+
raise ValueError(
|
| 359 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 360 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 361 |
+
)
|
| 362 |
+
|
| 363 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 364 |
+
return latents * self.scheduler.config.sigma_max
|
| 365 |
+
|
| 366 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.check_inputs
|
| 367 |
+
def check_inputs(
|
| 368 |
+
self,
|
| 369 |
+
prompt,
|
| 370 |
+
height,
|
| 371 |
+
width,
|
| 372 |
+
prompt_embeds=None,
|
| 373 |
+
callback_on_step_end_tensor_inputs=None,
|
| 374 |
+
):
|
| 375 |
+
if height % 16 != 0 or width % 16 != 0:
|
| 376 |
+
raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
|
| 377 |
+
|
| 378 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 379 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 380 |
+
):
|
| 381 |
+
raise ValueError(
|
| 382 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
if prompt is not None and prompt_embeds is not None:
|
| 386 |
+
raise ValueError(
|
| 387 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 388 |
+
" only forward one of the two."
|
| 389 |
+
)
|
| 390 |
+
elif prompt is None and prompt_embeds is None:
|
| 391 |
+
raise ValueError(
|
| 392 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 393 |
+
)
|
| 394 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 395 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 396 |
+
|
| 397 |
+
@property
|
| 398 |
+
def guidance_scale(self):
|
| 399 |
+
return self._guidance_scale
|
| 400 |
+
|
| 401 |
+
@property
|
| 402 |
+
def do_classifier_free_guidance(self):
|
| 403 |
+
return self._guidance_scale > 1.0
|
| 404 |
+
|
| 405 |
+
@property
|
| 406 |
+
def num_timesteps(self):
|
| 407 |
+
return self._num_timesteps
|
| 408 |
+
|
| 409 |
+
@property
|
| 410 |
+
def current_timestep(self):
|
| 411 |
+
return self._current_timestep
|
| 412 |
+
|
| 413 |
+
@property
|
| 414 |
+
def interrupt(self):
|
| 415 |
+
return self._interrupt
|
| 416 |
+
|
| 417 |
+
@torch.no_grad()
|
| 418 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 419 |
+
def __call__(
|
| 420 |
+
self,
|
| 421 |
+
prompt: str | list[str] = None,
|
| 422 |
+
negative_prompt: str | list[str] | None = None,
|
| 423 |
+
height: int = 768,
|
| 424 |
+
width: int = 1360,
|
| 425 |
+
num_inference_steps: int = 35,
|
| 426 |
+
guidance_scale: float = 7.0,
|
| 427 |
+
num_images_per_prompt: int | None = 1,
|
| 428 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 429 |
+
latents: torch.Tensor | None = None,
|
| 430 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 431 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 432 |
+
output_type: str | None = "pil",
|
| 433 |
+
return_dict: bool = True,
|
| 434 |
+
callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
|
| 435 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 436 |
+
max_sequence_length: int = 512,
|
| 437 |
+
):
|
| 438 |
+
r"""
|
| 439 |
+
The call function to the pipeline for generation.
|
| 440 |
+
|
| 441 |
+
Args:
|
| 442 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 443 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 444 |
+
instead.
|
| 445 |
+
height (`int`, defaults to `768`):
|
| 446 |
+
The height in pixels of the generated image.
|
| 447 |
+
width (`int`, defaults to `1360`):
|
| 448 |
+
The width in pixels of the generated image.
|
| 449 |
+
num_inference_steps (`int`, defaults to `35`):
|
| 450 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 451 |
+
expense of slower inference.
|
| 452 |
+
guidance_scale (`float`, defaults to `7.0`):
|
| 453 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 454 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 455 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 456 |
+
`guidance_scale > 1`.
|
| 457 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 458 |
+
The number of images to generate per prompt.
|
| 459 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 460 |
+
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
| 461 |
+
generation deterministic.
|
| 462 |
+
latents (`torch.Tensor`, *optional*):
|
| 463 |
+
Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
|
| 464 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 465 |
+
tensor is generated by sampling using the supplied random `generator`.
|
| 466 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 467 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 468 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 469 |
+
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 470 |
+
Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
|
| 471 |
+
provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
|
| 472 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 473 |
+
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
| 474 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 475 |
+
Whether or not to return a [`CosmosImagePipelineOutput`] instead of a plain tuple.
|
| 476 |
+
callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
|
| 477 |
+
A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
|
| 478 |
+
each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
|
| 479 |
+
DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
|
| 480 |
+
list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
|
| 481 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 482 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 483 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 484 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 485 |
+
|
| 486 |
+
Examples:
|
| 487 |
+
|
| 488 |
+
Returns:
|
| 489 |
+
[`~CosmosImagePipelineOutput`] or `tuple`:
|
| 490 |
+
If `return_dict` is `True`, [`CosmosImagePipelineOutput`] is returned, otherwise a `tuple` is returned
|
| 491 |
+
where the first element is a list with the generated images and the second element is a list of `bool`s
|
| 492 |
+
indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
|
| 493 |
+
"""
|
| 494 |
+
|
| 495 |
+
if self.safety_checker is None:
|
| 496 |
+
raise ValueError(
|
| 497 |
+
f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
|
| 498 |
+
"[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
|
| 499 |
+
f"Please ensure that you are compliant with the license agreement."
|
| 500 |
+
)
|
| 501 |
+
|
| 502 |
+
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
|
| 503 |
+
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
|
| 504 |
+
|
| 505 |
+
num_frames = 1
|
| 506 |
+
|
| 507 |
+
# 1. Check inputs. Raise error if not correct
|
| 508 |
+
self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
|
| 509 |
+
|
| 510 |
+
self._guidance_scale = guidance_scale
|
| 511 |
+
self._current_timestep = None
|
| 512 |
+
self._interrupt = False
|
| 513 |
+
|
| 514 |
+
device = self._execution_device
|
| 515 |
+
|
| 516 |
+
if self.safety_checker is not None:
|
| 517 |
+
self.safety_checker.to(device)
|
| 518 |
+
if prompt is not None:
|
| 519 |
+
prompt_list = [prompt] if isinstance(prompt, str) else prompt
|
| 520 |
+
for p in prompt_list:
|
| 521 |
+
if not self.safety_checker.check_text_safety(p):
|
| 522 |
+
raise ValueError(
|
| 523 |
+
f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
|
| 524 |
+
f"prompt abides by the NVIDIA Open Model License Agreement."
|
| 525 |
+
)
|
| 526 |
+
self.safety_checker.to("cpu")
|
| 527 |
+
|
| 528 |
+
# 2. Define call parameters
|
| 529 |
+
if prompt is not None and isinstance(prompt, str):
|
| 530 |
+
batch_size = 1
|
| 531 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 532 |
+
batch_size = len(prompt)
|
| 533 |
+
else:
|
| 534 |
+
batch_size = prompt_embeds.shape[0]
|
| 535 |
+
|
| 536 |
+
# 3. Encode input prompt
|
| 537 |
+
(
|
| 538 |
+
prompt_embeds,
|
| 539 |
+
negative_prompt_embeds,
|
| 540 |
+
) = self.encode_prompt(
|
| 541 |
+
prompt=prompt,
|
| 542 |
+
negative_prompt=negative_prompt,
|
| 543 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 544 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 545 |
+
prompt_embeds=prompt_embeds,
|
| 546 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 547 |
+
device=device,
|
| 548 |
+
max_sequence_length=max_sequence_length,
|
| 549 |
+
)
|
| 550 |
+
|
| 551 |
+
# 4. Prepare timesteps
|
| 552 |
+
sigmas_dtype = torch.float32 if torch.backends.mps.is_available() else torch.float64
|
| 553 |
+
sigmas = torch.linspace(0, 1, num_inference_steps, dtype=sigmas_dtype)
|
| 554 |
+
timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, device=device, sigmas=sigmas)
|
| 555 |
+
if self.scheduler.config.get("final_sigmas_type", "zero") == "sigma_min":
|
| 556 |
+
# Replace the last sigma (which is zero) with the minimum sigma value
|
| 557 |
+
self.scheduler.sigmas[-1] = self.scheduler.sigmas[-2]
|
| 558 |
+
|
| 559 |
+
# 5. Prepare latent variables
|
| 560 |
+
transformer_dtype = self.transformer.dtype
|
| 561 |
+
num_channels_latents = self.transformer.config.in_channels
|
| 562 |
+
latents = self.prepare_latents(
|
| 563 |
+
batch_size * num_images_per_prompt,
|
| 564 |
+
num_channels_latents,
|
| 565 |
+
height,
|
| 566 |
+
width,
|
| 567 |
+
num_frames,
|
| 568 |
+
torch.float32,
|
| 569 |
+
device,
|
| 570 |
+
generator,
|
| 571 |
+
latents,
|
| 572 |
+
)
|
| 573 |
+
|
| 574 |
+
padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
|
| 575 |
+
|
| 576 |
+
# 6. Denoising loop
|
| 577 |
+
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
| 578 |
+
self._num_timesteps = len(timesteps)
|
| 579 |
+
|
| 580 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 581 |
+
for i, t in enumerate(timesteps):
|
| 582 |
+
if self.interrupt:
|
| 583 |
+
continue
|
| 584 |
+
|
| 585 |
+
self._current_timestep = t
|
| 586 |
+
current_sigma = self.scheduler.sigmas[i]
|
| 587 |
+
|
| 588 |
+
current_t = current_sigma / (current_sigma + 1)
|
| 589 |
+
c_in = 1 - current_t
|
| 590 |
+
c_skip = 1 - current_t
|
| 591 |
+
c_out = -current_t
|
| 592 |
+
timestep = current_t.expand(latents.shape[0]).to(transformer_dtype) # [B, 1, T, 1, 1]
|
| 593 |
+
|
| 594 |
+
latent_model_input = latents * c_in
|
| 595 |
+
latent_model_input = latent_model_input.to(transformer_dtype)
|
| 596 |
+
|
| 597 |
+
noise_pred = self.transformer(
|
| 598 |
+
hidden_states=latent_model_input,
|
| 599 |
+
timestep=timestep,
|
| 600 |
+
encoder_hidden_states=prompt_embeds,
|
| 601 |
+
padding_mask=padding_mask,
|
| 602 |
+
return_dict=False,
|
| 603 |
+
)[0]
|
| 604 |
+
noise_pred = (c_skip * latents + c_out * noise_pred.float()).to(transformer_dtype)
|
| 605 |
+
|
| 606 |
+
if self.do_classifier_free_guidance:
|
| 607 |
+
noise_pred_uncond = self.transformer(
|
| 608 |
+
hidden_states=latent_model_input,
|
| 609 |
+
timestep=timestep,
|
| 610 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 611 |
+
padding_mask=padding_mask,
|
| 612 |
+
return_dict=False,
|
| 613 |
+
)[0]
|
| 614 |
+
noise_pred_uncond = (c_skip * latents + c_out * noise_pred_uncond.float()).to(transformer_dtype)
|
| 615 |
+
noise_pred = noise_pred + self.guidance_scale * (noise_pred - noise_pred_uncond)
|
| 616 |
+
|
| 617 |
+
noise_pred = (latents - noise_pred) / current_sigma
|
| 618 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 619 |
+
|
| 620 |
+
if callback_on_step_end is not None:
|
| 621 |
+
callback_kwargs = {}
|
| 622 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 623 |
+
callback_kwargs[k] = locals()[k]
|
| 624 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 625 |
+
|
| 626 |
+
latents = callback_outputs.pop("latents", latents)
|
| 627 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 628 |
+
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
|
| 629 |
+
|
| 630 |
+
# call the callback, if provided
|
| 631 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 632 |
+
progress_bar.update()
|
| 633 |
+
|
| 634 |
+
if XLA_AVAILABLE:
|
| 635 |
+
xm.mark_step()
|
| 636 |
+
|
| 637 |
+
self._current_timestep = None
|
| 638 |
+
|
| 639 |
+
if not output_type == "latent":
|
| 640 |
+
latents_mean = (
|
| 641 |
+
torch.tensor(self.vae.config.latents_mean)
|
| 642 |
+
.view(1, self.vae.config.z_dim, 1, 1, 1)
|
| 643 |
+
.to(latents.device, latents.dtype)
|
| 644 |
+
)
|
| 645 |
+
latents_std = 1.0 / torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).to(
|
| 646 |
+
latents.device, latents.dtype
|
| 647 |
+
)
|
| 648 |
+
latents = latents / latents_std / self.scheduler.config.sigma_data + latents_mean
|
| 649 |
+
video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
|
| 650 |
+
|
| 651 |
+
if self.safety_checker is not None:
|
| 652 |
+
self.safety_checker.to(device)
|
| 653 |
+
video = self.video_processor.postprocess_video(video, output_type="np")
|
| 654 |
+
video = (video * 255).astype(np.uint8)
|
| 655 |
+
video_batch = []
|
| 656 |
+
for vid in video:
|
| 657 |
+
vid = self.safety_checker.check_video_safety(vid)
|
| 658 |
+
video_batch.append(vid)
|
| 659 |
+
video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
|
| 660 |
+
video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
|
| 661 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 662 |
+
self.safety_checker.to("cpu")
|
| 663 |
+
else:
|
| 664 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 665 |
+
image = [batch[0] for batch in video]
|
| 666 |
+
if isinstance(video, torch.Tensor):
|
| 667 |
+
image = torch.stack(image)
|
| 668 |
+
elif isinstance(video, np.ndarray):
|
| 669 |
+
image = np.stack(image)
|
| 670 |
+
else:
|
| 671 |
+
image = latents[:, :, 0]
|
| 672 |
+
|
| 673 |
+
# Offload all models
|
| 674 |
+
self.maybe_free_model_hooks()
|
| 675 |
+
|
| 676 |
+
if not return_dict:
|
| 677 |
+
return (image,)
|
| 678 |
+
|
| 679 |
+
return CosmosImagePipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_text2world.py
ADDED
|
@@ -0,0 +1,670 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from typing import Callable
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import T5EncoderModel, T5TokenizerFast
|
| 21 |
+
|
| 22 |
+
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
|
| 23 |
+
from ...models import AutoencoderKLCosmos, CosmosTransformer3DModel
|
| 24 |
+
from ...schedulers import EDMEulerScheduler
|
| 25 |
+
from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
|
| 26 |
+
from ...utils.torch_utils import randn_tensor
|
| 27 |
+
from ...video_processor import VideoProcessor
|
| 28 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 29 |
+
from .pipeline_output import CosmosPipelineOutput
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
if is_cosmos_guardrail_available():
|
| 33 |
+
from cosmos_guardrail import CosmosSafetyChecker
|
| 34 |
+
else:
|
| 35 |
+
|
| 36 |
+
class CosmosSafetyChecker:
|
| 37 |
+
def __init__(self, *args, **kwargs):
|
| 38 |
+
raise ImportError(
|
| 39 |
+
"`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if is_torch_xla_available():
|
| 44 |
+
import torch_xla.core.xla_model as xm
|
| 45 |
+
|
| 46 |
+
XLA_AVAILABLE = True
|
| 47 |
+
else:
|
| 48 |
+
XLA_AVAILABLE = False
|
| 49 |
+
|
| 50 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 51 |
+
|
| 52 |
+
DEFAULT_NEGATIVE_PROMPT = (
|
| 53 |
+
"The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
|
| 54 |
+
"over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
|
| 55 |
+
"underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
|
| 56 |
+
"jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
|
| 57 |
+
"fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
|
| 58 |
+
"Overall, the video is of poor quality."
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
EXAMPLE_DOC_STRING = """
|
| 62 |
+
Examples:
|
| 63 |
+
```python
|
| 64 |
+
>>> import torch
|
| 65 |
+
>>> from diffusers import CosmosTextToWorldPipeline
|
| 66 |
+
>>> from diffusers.utils import export_to_video
|
| 67 |
+
|
| 68 |
+
>>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Text2World"
|
| 69 |
+
>>> pipe = CosmosTextToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
|
| 70 |
+
>>> pipe.to("cuda")
|
| 71 |
+
|
| 72 |
+
>>> prompt = "A sleek, humanoid robot stands in a vast warehouse filled with neatly stacked cardboard boxes on industrial shelves. The robot's metallic body gleams under the bright, even lighting, highlighting its futuristic design and intricate joints. A glowing blue light emanates from its chest, adding a touch of advanced technology. The background is dominated by rows of boxes, suggesting a highly organized storage system. The floor is lined with wooden pallets, enhancing the industrial setting. The camera remains static, capturing the robot's poised stance amidst the orderly environment, with a shallow depth of field that keeps the focus on the robot while subtly blurring the background for a cinematic effect."
|
| 73 |
+
|
| 74 |
+
>>> output = pipe(prompt=prompt).frames[0]
|
| 75 |
+
>>> export_to_video(output, "output.mp4", fps=30)
|
| 76 |
+
```
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 81 |
+
def retrieve_timesteps(
|
| 82 |
+
scheduler,
|
| 83 |
+
num_inference_steps: int | None = None,
|
| 84 |
+
device: str | torch.device | None = None,
|
| 85 |
+
timesteps: list[int] | None = None,
|
| 86 |
+
sigmas: list[float] | None = None,
|
| 87 |
+
**kwargs,
|
| 88 |
+
):
|
| 89 |
+
r"""
|
| 90 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 91 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 92 |
+
|
| 93 |
+
Args:
|
| 94 |
+
scheduler (`SchedulerMixin`):
|
| 95 |
+
The scheduler to get timesteps from.
|
| 96 |
+
num_inference_steps (`int`):
|
| 97 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 98 |
+
must be `None`.
|
| 99 |
+
device (`str` or `torch.device`, *optional*):
|
| 100 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 101 |
+
timesteps (`list[int]`, *optional*):
|
| 102 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 103 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 104 |
+
sigmas (`list[float]`, *optional*):
|
| 105 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 106 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 110 |
+
second element is the number of inference steps.
|
| 111 |
+
"""
|
| 112 |
+
if timesteps is not None and sigmas is not None:
|
| 113 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 114 |
+
if timesteps is not None:
|
| 115 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 116 |
+
if not accepts_timesteps:
|
| 117 |
+
raise ValueError(
|
| 118 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 119 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 120 |
+
)
|
| 121 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 122 |
+
timesteps = scheduler.timesteps
|
| 123 |
+
num_inference_steps = len(timesteps)
|
| 124 |
+
elif sigmas is not None:
|
| 125 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 126 |
+
if not accept_sigmas:
|
| 127 |
+
raise ValueError(
|
| 128 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 129 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 130 |
+
)
|
| 131 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 132 |
+
timesteps = scheduler.timesteps
|
| 133 |
+
num_inference_steps = len(timesteps)
|
| 134 |
+
else:
|
| 135 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 136 |
+
timesteps = scheduler.timesteps
|
| 137 |
+
return timesteps, num_inference_steps
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
class CosmosTextToWorldPipeline(DiffusionPipeline):
|
| 141 |
+
r"""
|
| 142 |
+
Pipeline for text-to-world generation using [Cosmos Predict1](https://github.com/nvidia-cosmos/cosmos-predict1).
|
| 143 |
+
|
| 144 |
+
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
| 145 |
+
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
| 146 |
+
|
| 147 |
+
Args:
|
| 148 |
+
text_encoder ([`T5EncoderModel`]):
|
| 149 |
+
Frozen text-encoder. Cosmos uses
|
| 150 |
+
[T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
|
| 151 |
+
[t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
|
| 152 |
+
tokenizer (`T5TokenizerFast`):
|
| 153 |
+
Tokenizer of class
|
| 154 |
+
[T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
|
| 155 |
+
transformer ([`CosmosTransformer3DModel`]):
|
| 156 |
+
Conditional Transformer to denoise the encoded image latents.
|
| 157 |
+
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
| 158 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 159 |
+
vae ([`AutoencoderKLCosmos`]):
|
| 160 |
+
Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
|
| 161 |
+
"""
|
| 162 |
+
|
| 163 |
+
model_cpu_offload_seq = "text_encoder->transformer->vae"
|
| 164 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
| 165 |
+
# We mark safety_checker as optional here to get around some test failures, but it is not really optional
|
| 166 |
+
_optional_components = ["safety_checker"]
|
| 167 |
+
|
| 168 |
+
def __init__(
|
| 169 |
+
self,
|
| 170 |
+
text_encoder: T5EncoderModel,
|
| 171 |
+
tokenizer: T5TokenizerFast,
|
| 172 |
+
transformer: CosmosTransformer3DModel,
|
| 173 |
+
vae: AutoencoderKLCosmos,
|
| 174 |
+
scheduler: EDMEulerScheduler,
|
| 175 |
+
safety_checker: CosmosSafetyChecker = None,
|
| 176 |
+
):
|
| 177 |
+
super().__init__()
|
| 178 |
+
|
| 179 |
+
if safety_checker is None:
|
| 180 |
+
safety_checker = CosmosSafetyChecker()
|
| 181 |
+
|
| 182 |
+
self.register_modules(
|
| 183 |
+
vae=vae,
|
| 184 |
+
text_encoder=text_encoder,
|
| 185 |
+
tokenizer=tokenizer,
|
| 186 |
+
transformer=transformer,
|
| 187 |
+
scheduler=scheduler,
|
| 188 |
+
safety_checker=safety_checker,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
self.vae_scale_factor_temporal = (
|
| 192 |
+
self.vae.config.temporal_compression_ratio if getattr(self, "vae", None) else 8
|
| 193 |
+
)
|
| 194 |
+
self.vae_scale_factor_spatial = self.vae.config.spatial_compression_ratio if getattr(self, "vae", None) else 8
|
| 195 |
+
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
|
| 196 |
+
|
| 197 |
+
def _get_t5_prompt_embeds(
|
| 198 |
+
self,
|
| 199 |
+
prompt: str | list[str] = None,
|
| 200 |
+
max_sequence_length: int = 512,
|
| 201 |
+
device: torch.device | None = None,
|
| 202 |
+
dtype: torch.dtype | None = None,
|
| 203 |
+
):
|
| 204 |
+
device = device or self._execution_device
|
| 205 |
+
dtype = dtype or self.text_encoder.dtype
|
| 206 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 207 |
+
|
| 208 |
+
text_inputs = self.tokenizer(
|
| 209 |
+
prompt,
|
| 210 |
+
padding="max_length",
|
| 211 |
+
max_length=max_sequence_length,
|
| 212 |
+
truncation=True,
|
| 213 |
+
return_tensors="pt",
|
| 214 |
+
return_length=True,
|
| 215 |
+
return_offsets_mapping=False,
|
| 216 |
+
)
|
| 217 |
+
text_input_ids = text_inputs.input_ids
|
| 218 |
+
prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
|
| 219 |
+
|
| 220 |
+
untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
|
| 221 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 222 |
+
removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
|
| 223 |
+
logger.warning(
|
| 224 |
+
"The following part of your input was truncated because `max_sequence_length` is set to "
|
| 225 |
+
f" {max_sequence_length} tokens: {removed_text}"
|
| 226 |
+
)
|
| 227 |
+
|
| 228 |
+
prompt_embeds = self.text_encoder(
|
| 229 |
+
text_input_ids.to(device), attention_mask=prompt_attention_mask
|
| 230 |
+
).last_hidden_state
|
| 231 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 232 |
+
|
| 233 |
+
lengths = prompt_attention_mask.sum(dim=1).cpu()
|
| 234 |
+
for i, length in enumerate(lengths):
|
| 235 |
+
prompt_embeds[i, length:] = 0
|
| 236 |
+
|
| 237 |
+
return prompt_embeds
|
| 238 |
+
|
| 239 |
+
def encode_prompt(
|
| 240 |
+
self,
|
| 241 |
+
prompt: str | list[str],
|
| 242 |
+
negative_prompt: str | list[str] | None = None,
|
| 243 |
+
do_classifier_free_guidance: bool = True,
|
| 244 |
+
num_videos_per_prompt: int = 1,
|
| 245 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 246 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 247 |
+
max_sequence_length: int = 512,
|
| 248 |
+
device: torch.device | None = None,
|
| 249 |
+
dtype: torch.dtype | None = None,
|
| 250 |
+
):
|
| 251 |
+
r"""
|
| 252 |
+
Encodes the prompt into text encoder hidden states.
|
| 253 |
+
|
| 254 |
+
Args:
|
| 255 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 256 |
+
prompt to be encoded
|
| 257 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 258 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 259 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 260 |
+
less than `1`).
|
| 261 |
+
do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
|
| 262 |
+
Whether to use classifier free guidance or not.
|
| 263 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 264 |
+
Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
|
| 265 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 266 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 267 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 268 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 269 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 270 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 271 |
+
argument.
|
| 272 |
+
device: (`torch.device`, *optional*):
|
| 273 |
+
torch device
|
| 274 |
+
dtype: (`torch.dtype`, *optional*):
|
| 275 |
+
torch dtype
|
| 276 |
+
"""
|
| 277 |
+
device = device or self._execution_device
|
| 278 |
+
|
| 279 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 280 |
+
if prompt is not None:
|
| 281 |
+
batch_size = len(prompt)
|
| 282 |
+
else:
|
| 283 |
+
batch_size = prompt_embeds.shape[0]
|
| 284 |
+
|
| 285 |
+
if prompt_embeds is None:
|
| 286 |
+
prompt_embeds = self._get_t5_prompt_embeds(
|
| 287 |
+
prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 291 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 292 |
+
prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 293 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 294 |
+
|
| 295 |
+
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
| 296 |
+
negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
|
| 297 |
+
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 298 |
+
|
| 299 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 300 |
+
raise TypeError(
|
| 301 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 302 |
+
f" {type(prompt)}."
|
| 303 |
+
)
|
| 304 |
+
elif batch_size != len(negative_prompt):
|
| 305 |
+
raise ValueError(
|
| 306 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 307 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 308 |
+
" the batch size of `prompt`."
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
negative_prompt_embeds = self._get_t5_prompt_embeds(
|
| 312 |
+
prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 316 |
+
_, seq_len, _ = negative_prompt_embeds.shape
|
| 317 |
+
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 318 |
+
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 319 |
+
|
| 320 |
+
return prompt_embeds, negative_prompt_embeds
|
| 321 |
+
|
| 322 |
+
def prepare_latents(
|
| 323 |
+
self,
|
| 324 |
+
batch_size: int,
|
| 325 |
+
num_channels_latents: 16,
|
| 326 |
+
height: int = 704,
|
| 327 |
+
width: int = 1280,
|
| 328 |
+
num_frames: int = 121,
|
| 329 |
+
dtype: torch.dtype | None = None,
|
| 330 |
+
device: torch.device | None = None,
|
| 331 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 332 |
+
latents: torch.Tensor | None = None,
|
| 333 |
+
) -> torch.Tensor:
|
| 334 |
+
if latents is not None:
|
| 335 |
+
return latents.to(device=device, dtype=dtype) * self.scheduler.config.sigma_max
|
| 336 |
+
|
| 337 |
+
num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
|
| 338 |
+
latent_height = height // self.vae_scale_factor_spatial
|
| 339 |
+
latent_width = width // self.vae_scale_factor_spatial
|
| 340 |
+
shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
|
| 341 |
+
|
| 342 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 343 |
+
raise ValueError(
|
| 344 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 345 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 349 |
+
return latents * self.scheduler.config.sigma_max
|
| 350 |
+
|
| 351 |
+
def check_inputs(
|
| 352 |
+
self,
|
| 353 |
+
prompt,
|
| 354 |
+
height,
|
| 355 |
+
width,
|
| 356 |
+
prompt_embeds=None,
|
| 357 |
+
callback_on_step_end_tensor_inputs=None,
|
| 358 |
+
):
|
| 359 |
+
if height % 16 != 0 or width % 16 != 0:
|
| 360 |
+
raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
|
| 361 |
+
|
| 362 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 363 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 364 |
+
):
|
| 365 |
+
raise ValueError(
|
| 366 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 367 |
+
)
|
| 368 |
+
|
| 369 |
+
if prompt is not None and prompt_embeds is not None:
|
| 370 |
+
raise ValueError(
|
| 371 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 372 |
+
" only forward one of the two."
|
| 373 |
+
)
|
| 374 |
+
elif prompt is None and prompt_embeds is None:
|
| 375 |
+
raise ValueError(
|
| 376 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 377 |
+
)
|
| 378 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 379 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 380 |
+
|
| 381 |
+
@property
|
| 382 |
+
def guidance_scale(self):
|
| 383 |
+
return self._guidance_scale
|
| 384 |
+
|
| 385 |
+
@property
|
| 386 |
+
def do_classifier_free_guidance(self):
|
| 387 |
+
return self._guidance_scale > 1.0
|
| 388 |
+
|
| 389 |
+
@property
|
| 390 |
+
def num_timesteps(self):
|
| 391 |
+
return self._num_timesteps
|
| 392 |
+
|
| 393 |
+
@property
|
| 394 |
+
def current_timestep(self):
|
| 395 |
+
return self._current_timestep
|
| 396 |
+
|
| 397 |
+
@property
|
| 398 |
+
def interrupt(self):
|
| 399 |
+
return self._interrupt
|
| 400 |
+
|
| 401 |
+
@torch.no_grad()
|
| 402 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 403 |
+
def __call__(
|
| 404 |
+
self,
|
| 405 |
+
prompt: str | list[str] = None,
|
| 406 |
+
negative_prompt: str | list[str] | None = None,
|
| 407 |
+
height: int = 704,
|
| 408 |
+
width: int = 1280,
|
| 409 |
+
num_frames: int = 121,
|
| 410 |
+
num_inference_steps: int = 36,
|
| 411 |
+
guidance_scale: float = 7.0,
|
| 412 |
+
fps: int = 30,
|
| 413 |
+
num_videos_per_prompt: int | None = 1,
|
| 414 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 415 |
+
latents: torch.Tensor | None = None,
|
| 416 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 417 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 418 |
+
output_type: str | None = "pil",
|
| 419 |
+
return_dict: bool = True,
|
| 420 |
+
callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
|
| 421 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 422 |
+
max_sequence_length: int = 512,
|
| 423 |
+
):
|
| 424 |
+
r"""
|
| 425 |
+
The call function to the pipeline for generation.
|
| 426 |
+
|
| 427 |
+
Args:
|
| 428 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 429 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 430 |
+
instead.
|
| 431 |
+
height (`int`, defaults to `720`):
|
| 432 |
+
The height in pixels of the generated image.
|
| 433 |
+
width (`int`, defaults to `1280`):
|
| 434 |
+
The width in pixels of the generated image.
|
| 435 |
+
num_frames (`int`, defaults to `121`):
|
| 436 |
+
The number of frames in the generated video.
|
| 437 |
+
num_inference_steps (`int`, defaults to `36`):
|
| 438 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 439 |
+
expense of slower inference.
|
| 440 |
+
guidance_scale (`float`, defaults to `7.0`):
|
| 441 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 442 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 443 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 444 |
+
`guidance_scale > 1`.
|
| 445 |
+
fps (`int`, defaults to `30`):
|
| 446 |
+
The frames per second of the generated video.
|
| 447 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 448 |
+
The number of images to generate per prompt.
|
| 449 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 450 |
+
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
| 451 |
+
generation deterministic.
|
| 452 |
+
latents (`torch.Tensor`, *optional*):
|
| 453 |
+
Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
|
| 454 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 455 |
+
tensor is generated by sampling using the supplied random `generator`.
|
| 456 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 457 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 458 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 459 |
+
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 460 |
+
Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
|
| 461 |
+
provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
|
| 462 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 463 |
+
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
| 464 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 465 |
+
Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
|
| 466 |
+
callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
|
| 467 |
+
A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
|
| 468 |
+
each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
|
| 469 |
+
DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
|
| 470 |
+
list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
|
| 471 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 472 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 473 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 474 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 475 |
+
|
| 476 |
+
Examples:
|
| 477 |
+
|
| 478 |
+
Returns:
|
| 479 |
+
[`~CosmosPipelineOutput`] or `tuple`:
|
| 480 |
+
If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
|
| 481 |
+
the first element is a list with the generated images and the second element is a list of `bool`s
|
| 482 |
+
indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
|
| 483 |
+
"""
|
| 484 |
+
|
| 485 |
+
if self.safety_checker is None:
|
| 486 |
+
raise ValueError(
|
| 487 |
+
f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
|
| 488 |
+
"[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
|
| 489 |
+
f"Please ensure that you are compliant with the license agreement."
|
| 490 |
+
)
|
| 491 |
+
|
| 492 |
+
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
|
| 493 |
+
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
|
| 494 |
+
|
| 495 |
+
# 1. Check inputs. Raise error if not correct
|
| 496 |
+
self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
|
| 497 |
+
|
| 498 |
+
self._guidance_scale = guidance_scale
|
| 499 |
+
self._current_timestep = None
|
| 500 |
+
self._interrupt = False
|
| 501 |
+
|
| 502 |
+
device = self._execution_device
|
| 503 |
+
|
| 504 |
+
if self.safety_checker is not None:
|
| 505 |
+
self.safety_checker.to(device)
|
| 506 |
+
if prompt is not None:
|
| 507 |
+
prompt_list = [prompt] if isinstance(prompt, str) else prompt
|
| 508 |
+
for p in prompt_list:
|
| 509 |
+
if not self.safety_checker.check_text_safety(p):
|
| 510 |
+
raise ValueError(
|
| 511 |
+
f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
|
| 512 |
+
f"prompt abides by the NVIDIA Open Model License Agreement."
|
| 513 |
+
)
|
| 514 |
+
self.safety_checker.to("cpu")
|
| 515 |
+
|
| 516 |
+
# 2. Define call parameters
|
| 517 |
+
if prompt is not None and isinstance(prompt, str):
|
| 518 |
+
batch_size = 1
|
| 519 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 520 |
+
batch_size = len(prompt)
|
| 521 |
+
else:
|
| 522 |
+
batch_size = prompt_embeds.shape[0]
|
| 523 |
+
|
| 524 |
+
# 3. Encode input prompt
|
| 525 |
+
(
|
| 526 |
+
prompt_embeds,
|
| 527 |
+
negative_prompt_embeds,
|
| 528 |
+
) = self.encode_prompt(
|
| 529 |
+
prompt=prompt,
|
| 530 |
+
negative_prompt=negative_prompt,
|
| 531 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 532 |
+
num_videos_per_prompt=num_videos_per_prompt,
|
| 533 |
+
prompt_embeds=prompt_embeds,
|
| 534 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 535 |
+
device=device,
|
| 536 |
+
max_sequence_length=max_sequence_length,
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
# 4. Prepare timesteps
|
| 540 |
+
timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device)
|
| 541 |
+
|
| 542 |
+
# 5. Prepare latent variables
|
| 543 |
+
transformer_dtype = self.transformer.dtype
|
| 544 |
+
num_channels_latents = self.transformer.config.in_channels
|
| 545 |
+
latents = self.prepare_latents(
|
| 546 |
+
batch_size * num_videos_per_prompt,
|
| 547 |
+
num_channels_latents,
|
| 548 |
+
height,
|
| 549 |
+
width,
|
| 550 |
+
num_frames,
|
| 551 |
+
torch.float32,
|
| 552 |
+
device,
|
| 553 |
+
generator,
|
| 554 |
+
latents,
|
| 555 |
+
)
|
| 556 |
+
|
| 557 |
+
padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
|
| 558 |
+
|
| 559 |
+
# 6. Denoising loop
|
| 560 |
+
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
| 561 |
+
self._num_timesteps = len(timesteps)
|
| 562 |
+
|
| 563 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 564 |
+
for i, t in enumerate(timesteps):
|
| 565 |
+
if self.interrupt:
|
| 566 |
+
continue
|
| 567 |
+
|
| 568 |
+
self._current_timestep = t
|
| 569 |
+
timestep = t.expand(latents.shape[0]).to(transformer_dtype)
|
| 570 |
+
|
| 571 |
+
latent_model_input = latents
|
| 572 |
+
latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
|
| 573 |
+
latent_model_input = latent_model_input.to(transformer_dtype)
|
| 574 |
+
|
| 575 |
+
noise_pred = self.transformer(
|
| 576 |
+
hidden_states=latent_model_input,
|
| 577 |
+
timestep=timestep,
|
| 578 |
+
encoder_hidden_states=prompt_embeds,
|
| 579 |
+
fps=fps,
|
| 580 |
+
padding_mask=padding_mask,
|
| 581 |
+
return_dict=False,
|
| 582 |
+
)[0]
|
| 583 |
+
|
| 584 |
+
sample = latents
|
| 585 |
+
if self.do_classifier_free_guidance:
|
| 586 |
+
noise_pred_uncond = self.transformer(
|
| 587 |
+
hidden_states=latent_model_input,
|
| 588 |
+
timestep=timestep,
|
| 589 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 590 |
+
fps=fps,
|
| 591 |
+
padding_mask=padding_mask,
|
| 592 |
+
return_dict=False,
|
| 593 |
+
)[0]
|
| 594 |
+
noise_pred = torch.cat([noise_pred_uncond, noise_pred])
|
| 595 |
+
sample = torch.cat([sample, sample])
|
| 596 |
+
|
| 597 |
+
# pred_original_sample (x0)
|
| 598 |
+
noise_pred = self.scheduler.step(noise_pred, t, sample, return_dict=False)[1]
|
| 599 |
+
self.scheduler._step_index -= 1
|
| 600 |
+
|
| 601 |
+
if self.do_classifier_free_guidance:
|
| 602 |
+
noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2)
|
| 603 |
+
noise_pred = noise_pred_cond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
|
| 604 |
+
|
| 605 |
+
# pred_sample (eps)
|
| 606 |
+
latents = self.scheduler.step(
|
| 607 |
+
noise_pred, t, latents, return_dict=False, pred_original_sample=noise_pred
|
| 608 |
+
)[0]
|
| 609 |
+
|
| 610 |
+
if callback_on_step_end is not None:
|
| 611 |
+
callback_kwargs = {}
|
| 612 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 613 |
+
callback_kwargs[k] = locals()[k]
|
| 614 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 615 |
+
|
| 616 |
+
latents = callback_outputs.pop("latents", latents)
|
| 617 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 618 |
+
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
|
| 619 |
+
|
| 620 |
+
# call the callback, if provided
|
| 621 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 622 |
+
progress_bar.update()
|
| 623 |
+
|
| 624 |
+
if XLA_AVAILABLE:
|
| 625 |
+
xm.mark_step()
|
| 626 |
+
|
| 627 |
+
self._current_timestep = None
|
| 628 |
+
|
| 629 |
+
if not output_type == "latent":
|
| 630 |
+
if self.vae.config.latents_mean is not None:
|
| 631 |
+
latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
|
| 632 |
+
latents_mean = (
|
| 633 |
+
torch.tensor(latents_mean)
|
| 634 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
|
| 635 |
+
.to(latents)
|
| 636 |
+
)
|
| 637 |
+
latents_std = (
|
| 638 |
+
torch.tensor(latents_std)
|
| 639 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
|
| 640 |
+
.to(latents)
|
| 641 |
+
)
|
| 642 |
+
latents = latents * latents_std / self.scheduler.config.sigma_data + latents_mean
|
| 643 |
+
else:
|
| 644 |
+
latents = latents / self.scheduler.config.sigma_data
|
| 645 |
+
video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
|
| 646 |
+
|
| 647 |
+
if self.safety_checker is not None:
|
| 648 |
+
self.safety_checker.to(device)
|
| 649 |
+
video = self.video_processor.postprocess_video(video, output_type="np")
|
| 650 |
+
video = (video * 255).astype(np.uint8)
|
| 651 |
+
video_batch = []
|
| 652 |
+
for vid in video:
|
| 653 |
+
vid = self.safety_checker.check_video_safety(vid)
|
| 654 |
+
video_batch.append(vid)
|
| 655 |
+
video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
|
| 656 |
+
video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
|
| 657 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 658 |
+
self.safety_checker.to("cpu")
|
| 659 |
+
else:
|
| 660 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 661 |
+
else:
|
| 662 |
+
video = latents
|
| 663 |
+
|
| 664 |
+
# Offload all models
|
| 665 |
+
self.maybe_free_model_hooks()
|
| 666 |
+
|
| 667 |
+
if not return_dict:
|
| 668 |
+
return (video,)
|
| 669 |
+
|
| 670 |
+
return CosmosPipelineOutput(frames=video)
|
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_video2world.py
ADDED
|
@@ -0,0 +1,832 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
from typing import Callable
|
| 17 |
+
|
| 18 |
+
import numpy as np
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import T5EncoderModel, T5TokenizerFast
|
| 21 |
+
|
| 22 |
+
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
|
| 23 |
+
from ...image_processor import PipelineImageInput
|
| 24 |
+
from ...models import AutoencoderKLCosmos, CosmosTransformer3DModel
|
| 25 |
+
from ...schedulers import EDMEulerScheduler
|
| 26 |
+
from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
|
| 27 |
+
from ...utils.torch_utils import randn_tensor
|
| 28 |
+
from ...video_processor import VideoProcessor
|
| 29 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 30 |
+
from .pipeline_output import CosmosPipelineOutput
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
if is_cosmos_guardrail_available():
|
| 34 |
+
from cosmos_guardrail import CosmosSafetyChecker
|
| 35 |
+
else:
|
| 36 |
+
|
| 37 |
+
class CosmosSafetyChecker:
|
| 38 |
+
def __init__(self, *args, **kwargs):
|
| 39 |
+
raise ImportError(
|
| 40 |
+
"`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
if is_torch_xla_available():
|
| 45 |
+
import torch_xla.core.xla_model as xm
|
| 46 |
+
|
| 47 |
+
XLA_AVAILABLE = True
|
| 48 |
+
else:
|
| 49 |
+
XLA_AVAILABLE = False
|
| 50 |
+
|
| 51 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 52 |
+
|
| 53 |
+
DEFAULT_NEGATIVE_PROMPT = (
|
| 54 |
+
"The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
|
| 55 |
+
"over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
|
| 56 |
+
"underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
|
| 57 |
+
"jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
|
| 58 |
+
"fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
|
| 59 |
+
"Overall, the video is of poor quality."
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
EXAMPLE_DOC_STRING = """
|
| 63 |
+
Examples:
|
| 64 |
+
Image conditioning:
|
| 65 |
+
|
| 66 |
+
```python
|
| 67 |
+
>>> import torch
|
| 68 |
+
>>> from diffusers import CosmosVideoToWorldPipeline
|
| 69 |
+
>>> from diffusers.utils import export_to_video, load_image
|
| 70 |
+
|
| 71 |
+
>>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Video2World"
|
| 72 |
+
>>> pipe = CosmosVideoToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
|
| 73 |
+
>>> pipe.to("cuda")
|
| 74 |
+
|
| 75 |
+
>>> prompt = "The video depicts a long, straight highway stretching into the distance, flanked by metal guardrails. The road is divided into multiple lanes, with a few vehicles visible in the far distance. The surrounding landscape features dry, grassy fields on one side and rolling hills on the other. The sky is mostly clear with a few scattered clouds, suggesting a bright, sunny day."
|
| 76 |
+
>>> image = load_image(
|
| 77 |
+
... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cosmos/cosmos-video2world-input.jpg"
|
| 78 |
+
... )
|
| 79 |
+
|
| 80 |
+
>>> video = pipe(image=image, prompt=prompt).frames[0]
|
| 81 |
+
>>> export_to_video(video, "output.mp4", fps=30)
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
Video conditioning:
|
| 85 |
+
|
| 86 |
+
```python
|
| 87 |
+
>>> import torch
|
| 88 |
+
>>> from diffusers import CosmosVideoToWorldPipeline
|
| 89 |
+
>>> from diffusers.utils import export_to_video, load_video
|
| 90 |
+
|
| 91 |
+
>>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Video2World"
|
| 92 |
+
>>> pipe = CosmosVideoToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
|
| 93 |
+
>>> pipe.transformer = torch.compile(pipe.transformer)
|
| 94 |
+
>>> pipe.to("cuda")
|
| 95 |
+
|
| 96 |
+
>>> prompt = "The video depicts a winding mountain road covered in snow, with a single vehicle traveling along it. The road is flanked by steep, rocky cliffs and sparse vegetation. The landscape is characterized by rugged terrain and a river visible in the distance. The scene captures the solitude and beauty of a winter drive through a mountainous region."
|
| 97 |
+
>>> video = load_video(
|
| 98 |
+
... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cosmos/cosmos-video2world-input-vid.mp4"
|
| 99 |
+
... )[
|
| 100 |
+
... :21
|
| 101 |
+
... ] # This example uses only the first 21 frames
|
| 102 |
+
|
| 103 |
+
>>> video = pipe(video=video, prompt=prompt).frames[0]
|
| 104 |
+
>>> export_to_video(video, "output.mp4", fps=30)
|
| 105 |
+
```
|
| 106 |
+
"""
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 110 |
+
def retrieve_timesteps(
|
| 111 |
+
scheduler,
|
| 112 |
+
num_inference_steps: int | None = None,
|
| 113 |
+
device: str | torch.device | None = None,
|
| 114 |
+
timesteps: list[int] | None = None,
|
| 115 |
+
sigmas: list[float] | None = None,
|
| 116 |
+
**kwargs,
|
| 117 |
+
):
|
| 118 |
+
r"""
|
| 119 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 120 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 121 |
+
|
| 122 |
+
Args:
|
| 123 |
+
scheduler (`SchedulerMixin`):
|
| 124 |
+
The scheduler to get timesteps from.
|
| 125 |
+
num_inference_steps (`int`):
|
| 126 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 127 |
+
must be `None`.
|
| 128 |
+
device (`str` or `torch.device`, *optional*):
|
| 129 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 130 |
+
timesteps (`list[int]`, *optional*):
|
| 131 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 132 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 133 |
+
sigmas (`list[float]`, *optional*):
|
| 134 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 135 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 136 |
+
|
| 137 |
+
Returns:
|
| 138 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 139 |
+
second element is the number of inference steps.
|
| 140 |
+
"""
|
| 141 |
+
if timesteps is not None and sigmas is not None:
|
| 142 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 143 |
+
if timesteps is not None:
|
| 144 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 145 |
+
if not accepts_timesteps:
|
| 146 |
+
raise ValueError(
|
| 147 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 148 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 149 |
+
)
|
| 150 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 151 |
+
timesteps = scheduler.timesteps
|
| 152 |
+
num_inference_steps = len(timesteps)
|
| 153 |
+
elif sigmas is not None:
|
| 154 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 155 |
+
if not accept_sigmas:
|
| 156 |
+
raise ValueError(
|
| 157 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 158 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 159 |
+
)
|
| 160 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 161 |
+
timesteps = scheduler.timesteps
|
| 162 |
+
num_inference_steps = len(timesteps)
|
| 163 |
+
else:
|
| 164 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 165 |
+
timesteps = scheduler.timesteps
|
| 166 |
+
return timesteps, num_inference_steps
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
| 170 |
+
def retrieve_latents(
|
| 171 |
+
encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
|
| 172 |
+
):
|
| 173 |
+
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
| 174 |
+
return encoder_output.latent_dist.sample(generator)
|
| 175 |
+
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
| 176 |
+
return encoder_output.latent_dist.mode()
|
| 177 |
+
elif hasattr(encoder_output, "latents"):
|
| 178 |
+
return encoder_output.latents
|
| 179 |
+
else:
|
| 180 |
+
raise AttributeError("Could not access latents of provided encoder_output")
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
class CosmosVideoToWorldPipeline(DiffusionPipeline):
|
| 184 |
+
r"""
|
| 185 |
+
Pipeline for image-to-world and video-to-world generation using [Cosmos
|
| 186 |
+
Predict-1](https://github.com/nvidia-cosmos/cosmos-predict1).
|
| 187 |
+
|
| 188 |
+
This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
|
| 189 |
+
implemented for all pipelines (downloading, saving, running on a particular device, etc.).
|
| 190 |
+
|
| 191 |
+
Args:
|
| 192 |
+
text_encoder ([`T5EncoderModel`]):
|
| 193 |
+
Frozen text-encoder. Cosmos uses
|
| 194 |
+
[T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
|
| 195 |
+
[t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
|
| 196 |
+
tokenizer (`T5TokenizerFast`):
|
| 197 |
+
Tokenizer of class
|
| 198 |
+
[T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
|
| 199 |
+
transformer ([`CosmosTransformer3DModel`]):
|
| 200 |
+
Conditional Transformer to denoise the encoded image latents.
|
| 201 |
+
scheduler ([`FlowMatchEulerDiscreteScheduler`]):
|
| 202 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 203 |
+
vae ([`AutoencoderKLCosmos`]):
|
| 204 |
+
Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
|
| 205 |
+
"""
|
| 206 |
+
|
| 207 |
+
model_cpu_offload_seq = "text_encoder->transformer->vae"
|
| 208 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
|
| 209 |
+
# We mark safety_checker as optional here to get around some test failures, but it is not really optional
|
| 210 |
+
_optional_components = ["safety_checker"]
|
| 211 |
+
|
| 212 |
+
def __init__(
|
| 213 |
+
self,
|
| 214 |
+
text_encoder: T5EncoderModel,
|
| 215 |
+
tokenizer: T5TokenizerFast,
|
| 216 |
+
transformer: CosmosTransformer3DModel,
|
| 217 |
+
vae: AutoencoderKLCosmos,
|
| 218 |
+
scheduler: EDMEulerScheduler,
|
| 219 |
+
safety_checker: CosmosSafetyChecker = None,
|
| 220 |
+
):
|
| 221 |
+
super().__init__()
|
| 222 |
+
|
| 223 |
+
if safety_checker is None:
|
| 224 |
+
safety_checker = CosmosSafetyChecker()
|
| 225 |
+
|
| 226 |
+
self.register_modules(
|
| 227 |
+
vae=vae,
|
| 228 |
+
text_encoder=text_encoder,
|
| 229 |
+
tokenizer=tokenizer,
|
| 230 |
+
transformer=transformer,
|
| 231 |
+
scheduler=scheduler,
|
| 232 |
+
safety_checker=safety_checker,
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
self.vae_scale_factor_temporal = (
|
| 236 |
+
self.vae.config.temporal_compression_ratio if getattr(self, "vae", None) else 8
|
| 237 |
+
)
|
| 238 |
+
self.vae_scale_factor_spatial = self.vae.config.spatial_compression_ratio if getattr(self, "vae", None) else 8
|
| 239 |
+
self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
|
| 240 |
+
|
| 241 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline._get_t5_prompt_embeds
|
| 242 |
+
def _get_t5_prompt_embeds(
|
| 243 |
+
self,
|
| 244 |
+
prompt: str | list[str] = None,
|
| 245 |
+
max_sequence_length: int = 512,
|
| 246 |
+
device: torch.device | None = None,
|
| 247 |
+
dtype: torch.dtype | None = None,
|
| 248 |
+
):
|
| 249 |
+
device = device or self._execution_device
|
| 250 |
+
dtype = dtype or self.text_encoder.dtype
|
| 251 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 252 |
+
|
| 253 |
+
text_inputs = self.tokenizer(
|
| 254 |
+
prompt,
|
| 255 |
+
padding="max_length",
|
| 256 |
+
max_length=max_sequence_length,
|
| 257 |
+
truncation=True,
|
| 258 |
+
return_tensors="pt",
|
| 259 |
+
return_length=True,
|
| 260 |
+
return_offsets_mapping=False,
|
| 261 |
+
)
|
| 262 |
+
text_input_ids = text_inputs.input_ids
|
| 263 |
+
prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
|
| 264 |
+
|
| 265 |
+
untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
|
| 266 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 267 |
+
removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
|
| 268 |
+
logger.warning(
|
| 269 |
+
"The following part of your input was truncated because `max_sequence_length` is set to "
|
| 270 |
+
f" {max_sequence_length} tokens: {removed_text}"
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
prompt_embeds = self.text_encoder(
|
| 274 |
+
text_input_ids.to(device), attention_mask=prompt_attention_mask
|
| 275 |
+
).last_hidden_state
|
| 276 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 277 |
+
|
| 278 |
+
lengths = prompt_attention_mask.sum(dim=1).cpu()
|
| 279 |
+
for i, length in enumerate(lengths):
|
| 280 |
+
prompt_embeds[i, length:] = 0
|
| 281 |
+
|
| 282 |
+
return prompt_embeds
|
| 283 |
+
|
| 284 |
+
# Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt
|
| 285 |
+
def encode_prompt(
|
| 286 |
+
self,
|
| 287 |
+
prompt: str | list[str],
|
| 288 |
+
negative_prompt: str | list[str] | None = None,
|
| 289 |
+
do_classifier_free_guidance: bool = True,
|
| 290 |
+
num_videos_per_prompt: int = 1,
|
| 291 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 292 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 293 |
+
max_sequence_length: int = 512,
|
| 294 |
+
device: torch.device | None = None,
|
| 295 |
+
dtype: torch.dtype | None = None,
|
| 296 |
+
):
|
| 297 |
+
r"""
|
| 298 |
+
Encodes the prompt into text encoder hidden states.
|
| 299 |
+
|
| 300 |
+
Args:
|
| 301 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 302 |
+
prompt to be encoded
|
| 303 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 304 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 305 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
|
| 306 |
+
less than `1`).
|
| 307 |
+
do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
|
| 308 |
+
Whether to use classifier free guidance or not.
|
| 309 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 310 |
+
Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
|
| 311 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 312 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 313 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 314 |
+
negative_prompt_embeds (`torch.Tensor`, *optional*):
|
| 315 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 316 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 317 |
+
argument.
|
| 318 |
+
device: (`torch.device`, *optional*):
|
| 319 |
+
torch device
|
| 320 |
+
dtype: (`torch.dtype`, *optional*):
|
| 321 |
+
torch dtype
|
| 322 |
+
"""
|
| 323 |
+
device = device or self._execution_device
|
| 324 |
+
|
| 325 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 326 |
+
if prompt is not None:
|
| 327 |
+
batch_size = len(prompt)
|
| 328 |
+
else:
|
| 329 |
+
batch_size = prompt_embeds.shape[0]
|
| 330 |
+
|
| 331 |
+
if prompt_embeds is None:
|
| 332 |
+
prompt_embeds = self._get_t5_prompt_embeds(
|
| 333 |
+
prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 337 |
+
_, seq_len, _ = prompt_embeds.shape
|
| 338 |
+
prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 339 |
+
prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 340 |
+
|
| 341 |
+
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
| 342 |
+
negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
|
| 343 |
+
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 344 |
+
|
| 345 |
+
if prompt is not None and type(prompt) is not type(negative_prompt):
|
| 346 |
+
raise TypeError(
|
| 347 |
+
f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
|
| 348 |
+
f" {type(prompt)}."
|
| 349 |
+
)
|
| 350 |
+
elif batch_size != len(negative_prompt):
|
| 351 |
+
raise ValueError(
|
| 352 |
+
f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
|
| 353 |
+
f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
|
| 354 |
+
" the batch size of `prompt`."
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
negative_prompt_embeds = self._get_t5_prompt_embeds(
|
| 358 |
+
prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
# duplicate text embeddings for each generation per prompt, using mps friendly method
|
| 362 |
+
_, seq_len, _ = negative_prompt_embeds.shape
|
| 363 |
+
negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
|
| 364 |
+
negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
|
| 365 |
+
|
| 366 |
+
return prompt_embeds, negative_prompt_embeds
|
| 367 |
+
|
| 368 |
+
def prepare_latents(
|
| 369 |
+
self,
|
| 370 |
+
video: torch.Tensor,
|
| 371 |
+
batch_size: int,
|
| 372 |
+
num_channels_latents: 16,
|
| 373 |
+
height: int = 704,
|
| 374 |
+
width: int = 1280,
|
| 375 |
+
num_frames: int = 121,
|
| 376 |
+
do_classifier_free_guidance: bool = True,
|
| 377 |
+
input_frames_guidance: bool = False,
|
| 378 |
+
dtype: torch.dtype | None = None,
|
| 379 |
+
device: torch.device | None = None,
|
| 380 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 381 |
+
latents: torch.Tensor | None = None,
|
| 382 |
+
) -> torch.Tensor:
|
| 383 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 384 |
+
raise ValueError(
|
| 385 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 386 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 387 |
+
)
|
| 388 |
+
|
| 389 |
+
num_cond_frames = video.size(2)
|
| 390 |
+
if num_cond_frames >= num_frames:
|
| 391 |
+
# Take the last `num_frames` frames for conditioning
|
| 392 |
+
num_cond_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
|
| 393 |
+
video = video[:, :, -num_frames:]
|
| 394 |
+
else:
|
| 395 |
+
num_cond_latent_frames = (num_cond_frames - 1) // self.vae_scale_factor_temporal + 1
|
| 396 |
+
num_padding_frames = num_frames - num_cond_frames
|
| 397 |
+
padding = video.new_zeros(video.size(0), video.size(1), num_padding_frames, video.size(3), video.size(4))
|
| 398 |
+
video = torch.cat([video, padding], dim=2)
|
| 399 |
+
|
| 400 |
+
if isinstance(generator, list):
|
| 401 |
+
init_latents = [
|
| 402 |
+
retrieve_latents(self.vae.encode(video[i].unsqueeze(0)), generator=generator[i])
|
| 403 |
+
for i in range(batch_size)
|
| 404 |
+
]
|
| 405 |
+
else:
|
| 406 |
+
init_latents = [retrieve_latents(self.vae.encode(vid.unsqueeze(0)), generator) for vid in video]
|
| 407 |
+
|
| 408 |
+
init_latents = torch.cat(init_latents, dim=0).to(dtype)
|
| 409 |
+
|
| 410 |
+
if self.vae.config.latents_mean is not None:
|
| 411 |
+
latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
|
| 412 |
+
latents_mean = (
|
| 413 |
+
torch.tensor(latents_mean)
|
| 414 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : init_latents.size(2)]
|
| 415 |
+
.to(init_latents)
|
| 416 |
+
)
|
| 417 |
+
latents_std = (
|
| 418 |
+
torch.tensor(latents_std)
|
| 419 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : init_latents.size(2)]
|
| 420 |
+
.to(init_latents)
|
| 421 |
+
)
|
| 422 |
+
init_latents = (init_latents - latents_mean) * self.scheduler.config.sigma_data / latents_std
|
| 423 |
+
else:
|
| 424 |
+
init_latents = init_latents * self.scheduler.config.sigma_data
|
| 425 |
+
|
| 426 |
+
num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
|
| 427 |
+
latent_height = height // self.vae_scale_factor_spatial
|
| 428 |
+
latent_width = width // self.vae_scale_factor_spatial
|
| 429 |
+
shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
|
| 430 |
+
|
| 431 |
+
if latents is None:
|
| 432 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 433 |
+
else:
|
| 434 |
+
latents = latents.to(device=device, dtype=dtype)
|
| 435 |
+
|
| 436 |
+
latents = latents * self.scheduler.config.sigma_max
|
| 437 |
+
|
| 438 |
+
padding_shape = (batch_size, 1, num_latent_frames, latent_height, latent_width)
|
| 439 |
+
ones_padding = latents.new_ones(padding_shape)
|
| 440 |
+
zeros_padding = latents.new_zeros(padding_shape)
|
| 441 |
+
|
| 442 |
+
cond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
|
| 443 |
+
cond_indicator[:, :, :num_cond_latent_frames] = 1.0
|
| 444 |
+
cond_mask = cond_indicator * ones_padding + (1 - cond_indicator) * zeros_padding
|
| 445 |
+
|
| 446 |
+
uncond_indicator = uncond_mask = None
|
| 447 |
+
if do_classifier_free_guidance:
|
| 448 |
+
uncond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
|
| 449 |
+
uncond_indicator[:, :, :num_cond_latent_frames] = 1.0
|
| 450 |
+
uncond_mask = zeros_padding
|
| 451 |
+
if not input_frames_guidance:
|
| 452 |
+
uncond_mask = uncond_indicator * ones_padding + (1 - uncond_indicator) * zeros_padding
|
| 453 |
+
|
| 454 |
+
return latents, init_latents, cond_indicator, uncond_indicator, cond_mask, uncond_mask
|
| 455 |
+
|
| 456 |
+
def check_inputs(
|
| 457 |
+
self,
|
| 458 |
+
prompt,
|
| 459 |
+
height,
|
| 460 |
+
width,
|
| 461 |
+
prompt_embeds=None,
|
| 462 |
+
callback_on_step_end_tensor_inputs=None,
|
| 463 |
+
image=None,
|
| 464 |
+
video=None,
|
| 465 |
+
):
|
| 466 |
+
if height % 16 != 0 or width % 16 != 0:
|
| 467 |
+
raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
|
| 468 |
+
|
| 469 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 470 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 471 |
+
):
|
| 472 |
+
raise ValueError(
|
| 473 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
if prompt is not None and prompt_embeds is not None:
|
| 477 |
+
raise ValueError(
|
| 478 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 479 |
+
" only forward one of the two."
|
| 480 |
+
)
|
| 481 |
+
elif prompt is None and prompt_embeds is None:
|
| 482 |
+
raise ValueError(
|
| 483 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 484 |
+
)
|
| 485 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 486 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 487 |
+
|
| 488 |
+
if image is None and video is None:
|
| 489 |
+
raise ValueError("Either `image` or `video` has to be provided.")
|
| 490 |
+
if image is not None and video is not None:
|
| 491 |
+
raise ValueError("Only one of `image` or `video` has to be provided.")
|
| 492 |
+
|
| 493 |
+
@property
|
| 494 |
+
def guidance_scale(self):
|
| 495 |
+
return self._guidance_scale
|
| 496 |
+
|
| 497 |
+
@property
|
| 498 |
+
def do_classifier_free_guidance(self):
|
| 499 |
+
return self._guidance_scale > 1.0
|
| 500 |
+
|
| 501 |
+
@property
|
| 502 |
+
def num_timesteps(self):
|
| 503 |
+
return self._num_timesteps
|
| 504 |
+
|
| 505 |
+
@property
|
| 506 |
+
def current_timestep(self):
|
| 507 |
+
return self._current_timestep
|
| 508 |
+
|
| 509 |
+
@property
|
| 510 |
+
def interrupt(self):
|
| 511 |
+
return self._interrupt
|
| 512 |
+
|
| 513 |
+
@torch.no_grad()
|
| 514 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 515 |
+
def __call__(
|
| 516 |
+
self,
|
| 517 |
+
image: PipelineImageInput = None,
|
| 518 |
+
video: list[PipelineImageInput] = None,
|
| 519 |
+
prompt: str | list[str] = None,
|
| 520 |
+
negative_prompt: str | list[str] | None = None,
|
| 521 |
+
height: int = 704,
|
| 522 |
+
width: int = 1280,
|
| 523 |
+
num_frames: int = 121,
|
| 524 |
+
num_inference_steps: int = 36,
|
| 525 |
+
guidance_scale: float = 7.0,
|
| 526 |
+
input_frames_guidance: bool = False,
|
| 527 |
+
augment_sigma: float = 0.001,
|
| 528 |
+
fps: int = 30,
|
| 529 |
+
num_videos_per_prompt: int | None = 1,
|
| 530 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 531 |
+
latents: torch.Tensor | None = None,
|
| 532 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 533 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 534 |
+
output_type: str | None = "pil",
|
| 535 |
+
return_dict: bool = True,
|
| 536 |
+
callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
|
| 537 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 538 |
+
max_sequence_length: int = 512,
|
| 539 |
+
):
|
| 540 |
+
r"""
|
| 541 |
+
The call function to the pipeline for generation.
|
| 542 |
+
|
| 543 |
+
Args:
|
| 544 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 545 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 546 |
+
instead.
|
| 547 |
+
height (`int`, defaults to `720`):
|
| 548 |
+
The height in pixels of the generated image.
|
| 549 |
+
width (`int`, defaults to `1280`):
|
| 550 |
+
The width in pixels of the generated image.
|
| 551 |
+
num_frames (`int`, defaults to `121`):
|
| 552 |
+
The number of frames in the generated video.
|
| 553 |
+
num_inference_steps (`int`, defaults to `36`):
|
| 554 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 555 |
+
expense of slower inference.
|
| 556 |
+
guidance_scale (`float`, defaults to `7.0`):
|
| 557 |
+
Guidance scale as defined in [Classifier-Free Diffusion
|
| 558 |
+
Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
|
| 559 |
+
of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
|
| 560 |
+
`guidance_scale > 1`.
|
| 561 |
+
fps (`int`, defaults to `30`):
|
| 562 |
+
The frames per second of the generated video.
|
| 563 |
+
num_videos_per_prompt (`int`, *optional*, defaults to 1):
|
| 564 |
+
The number of images to generate per prompt.
|
| 565 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 566 |
+
A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
|
| 567 |
+
generation deterministic.
|
| 568 |
+
latents (`torch.Tensor`, *optional*):
|
| 569 |
+
Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
|
| 570 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 571 |
+
tensor is generated by sampling using the supplied random `generator`.
|
| 572 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 573 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 574 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 575 |
+
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 576 |
+
Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
|
| 577 |
+
provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
|
| 578 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 579 |
+
The output format of the generated image. Choose between `PIL.Image` or `np.array`.
|
| 580 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 581 |
+
Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
|
| 582 |
+
callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
|
| 583 |
+
A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
|
| 584 |
+
each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
|
| 585 |
+
DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
|
| 586 |
+
list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
|
| 587 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 588 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 589 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 590 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 591 |
+
|
| 592 |
+
Examples:
|
| 593 |
+
|
| 594 |
+
Returns:
|
| 595 |
+
[`~CosmosPipelineOutput`] or `tuple`:
|
| 596 |
+
If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
|
| 597 |
+
the first element is a list with the generated images and the second element is a list of `bool`s
|
| 598 |
+
indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
|
| 599 |
+
"""
|
| 600 |
+
|
| 601 |
+
if self.safety_checker is None:
|
| 602 |
+
raise ValueError(
|
| 603 |
+
f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
|
| 604 |
+
"[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
|
| 605 |
+
f"Please ensure that you are compliant with the license agreement."
|
| 606 |
+
)
|
| 607 |
+
|
| 608 |
+
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
|
| 609 |
+
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
|
| 610 |
+
|
| 611 |
+
# 1. Check inputs. Raise error if not correct
|
| 612 |
+
self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs, image, video)
|
| 613 |
+
|
| 614 |
+
self._guidance_scale = guidance_scale
|
| 615 |
+
self._current_timestep = None
|
| 616 |
+
self._interrupt = False
|
| 617 |
+
|
| 618 |
+
device = self._execution_device
|
| 619 |
+
|
| 620 |
+
if self.safety_checker is not None:
|
| 621 |
+
self.safety_checker.to(device)
|
| 622 |
+
if prompt is not None:
|
| 623 |
+
prompt_list = [prompt] if isinstance(prompt, str) else prompt
|
| 624 |
+
for p in prompt_list:
|
| 625 |
+
if not self.safety_checker.check_text_safety(p):
|
| 626 |
+
raise ValueError(
|
| 627 |
+
f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
|
| 628 |
+
f"prompt abides by the NVIDIA Open Model License Agreement."
|
| 629 |
+
)
|
| 630 |
+
self.safety_checker.to("cpu")
|
| 631 |
+
|
| 632 |
+
# 2. Define call parameters
|
| 633 |
+
if prompt is not None and isinstance(prompt, str):
|
| 634 |
+
batch_size = 1
|
| 635 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 636 |
+
batch_size = len(prompt)
|
| 637 |
+
else:
|
| 638 |
+
batch_size = prompt_embeds.shape[0]
|
| 639 |
+
|
| 640 |
+
# 3. Encode input prompt
|
| 641 |
+
(
|
| 642 |
+
prompt_embeds,
|
| 643 |
+
negative_prompt_embeds,
|
| 644 |
+
) = self.encode_prompt(
|
| 645 |
+
prompt=prompt,
|
| 646 |
+
negative_prompt=negative_prompt,
|
| 647 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 648 |
+
num_videos_per_prompt=num_videos_per_prompt,
|
| 649 |
+
prompt_embeds=prompt_embeds,
|
| 650 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 651 |
+
device=device,
|
| 652 |
+
max_sequence_length=max_sequence_length,
|
| 653 |
+
)
|
| 654 |
+
|
| 655 |
+
# 4. Prepare timesteps
|
| 656 |
+
timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device)
|
| 657 |
+
|
| 658 |
+
# 5. Prepare latent variables
|
| 659 |
+
vae_dtype = self.vae.dtype
|
| 660 |
+
transformer_dtype = self.transformer.dtype
|
| 661 |
+
|
| 662 |
+
if image is not None:
|
| 663 |
+
video = self.video_processor.preprocess(image, height, width).unsqueeze(2)
|
| 664 |
+
else:
|
| 665 |
+
video = self.video_processor.preprocess_video(video, height, width)
|
| 666 |
+
video = video.to(device=device, dtype=vae_dtype)
|
| 667 |
+
|
| 668 |
+
num_channels_latents = self.transformer.config.in_channels - 1
|
| 669 |
+
latents, conditioning_latents, cond_indicator, uncond_indicator, cond_mask, uncond_mask = self.prepare_latents(
|
| 670 |
+
video,
|
| 671 |
+
batch_size * num_videos_per_prompt,
|
| 672 |
+
num_channels_latents,
|
| 673 |
+
height,
|
| 674 |
+
width,
|
| 675 |
+
num_frames,
|
| 676 |
+
self.do_classifier_free_guidance,
|
| 677 |
+
input_frames_guidance,
|
| 678 |
+
torch.float32,
|
| 679 |
+
device,
|
| 680 |
+
generator,
|
| 681 |
+
latents,
|
| 682 |
+
)
|
| 683 |
+
cond_mask = cond_mask.to(transformer_dtype)
|
| 684 |
+
if self.do_classifier_free_guidance:
|
| 685 |
+
uncond_mask = uncond_mask.to(transformer_dtype)
|
| 686 |
+
|
| 687 |
+
augment_sigma = torch.tensor([augment_sigma], device=device, dtype=torch.float32)
|
| 688 |
+
padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
|
| 689 |
+
|
| 690 |
+
# 6. Denoising loop
|
| 691 |
+
num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
|
| 692 |
+
self._num_timesteps = len(timesteps)
|
| 693 |
+
|
| 694 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 695 |
+
for i, t in enumerate(timesteps):
|
| 696 |
+
if self.interrupt:
|
| 697 |
+
continue
|
| 698 |
+
|
| 699 |
+
self._current_timestep = t
|
| 700 |
+
timestep = t.expand(latents.shape[0]).to(transformer_dtype)
|
| 701 |
+
|
| 702 |
+
current_sigma = self.scheduler.sigmas[i]
|
| 703 |
+
is_augment_sigma_greater = augment_sigma >= current_sigma
|
| 704 |
+
|
| 705 |
+
c_in_augment = self.scheduler._get_conditioning_c_in(augment_sigma)
|
| 706 |
+
c_in_original = self.scheduler._get_conditioning_c_in(current_sigma)
|
| 707 |
+
|
| 708 |
+
current_cond_indicator = cond_indicator * 0 if is_augment_sigma_greater else cond_indicator
|
| 709 |
+
cond_noise = randn_tensor(latents.shape, generator=generator, device=device, dtype=torch.float32)
|
| 710 |
+
cond_latent = conditioning_latents + cond_noise * augment_sigma[:, None, None, None, None]
|
| 711 |
+
cond_latent = cond_latent * c_in_augment / c_in_original
|
| 712 |
+
cond_latent = current_cond_indicator * cond_latent + (1 - current_cond_indicator) * latents
|
| 713 |
+
cond_latent = self.scheduler.scale_model_input(cond_latent, t)
|
| 714 |
+
cond_latent = cond_latent.to(transformer_dtype)
|
| 715 |
+
|
| 716 |
+
noise_pred = self.transformer(
|
| 717 |
+
hidden_states=cond_latent,
|
| 718 |
+
timestep=timestep,
|
| 719 |
+
encoder_hidden_states=prompt_embeds,
|
| 720 |
+
fps=fps,
|
| 721 |
+
condition_mask=cond_mask,
|
| 722 |
+
padding_mask=padding_mask,
|
| 723 |
+
return_dict=False,
|
| 724 |
+
)[0]
|
| 725 |
+
|
| 726 |
+
sample = latents
|
| 727 |
+
if self.do_classifier_free_guidance:
|
| 728 |
+
current_uncond_indicator = uncond_indicator * 0 if is_augment_sigma_greater else uncond_indicator
|
| 729 |
+
uncond_noise = randn_tensor(latents.shape, generator=generator, device=device, dtype=torch.float32)
|
| 730 |
+
uncond_latent = conditioning_latents + uncond_noise * augment_sigma[:, None, None, None, None]
|
| 731 |
+
uncond_latent = uncond_latent * c_in_augment / c_in_original
|
| 732 |
+
uncond_latent = current_uncond_indicator * uncond_latent + (1 - current_uncond_indicator) * latents
|
| 733 |
+
uncond_latent = self.scheduler.scale_model_input(uncond_latent, t)
|
| 734 |
+
uncond_latent = uncond_latent.to(transformer_dtype)
|
| 735 |
+
|
| 736 |
+
noise_pred_uncond = self.transformer(
|
| 737 |
+
hidden_states=uncond_latent,
|
| 738 |
+
timestep=timestep,
|
| 739 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 740 |
+
fps=fps,
|
| 741 |
+
condition_mask=uncond_mask,
|
| 742 |
+
padding_mask=padding_mask,
|
| 743 |
+
return_dict=False,
|
| 744 |
+
)[0]
|
| 745 |
+
noise_pred = torch.cat([noise_pred_uncond, noise_pred])
|
| 746 |
+
sample = torch.cat([sample, sample])
|
| 747 |
+
|
| 748 |
+
# pred_original_sample (x0)
|
| 749 |
+
noise_pred = self.scheduler.step(noise_pred, t, sample, return_dict=False)[1]
|
| 750 |
+
self.scheduler._step_index -= 1
|
| 751 |
+
|
| 752 |
+
if self.do_classifier_free_guidance:
|
| 753 |
+
noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2, dim=0)
|
| 754 |
+
noise_pred_uncond = (
|
| 755 |
+
current_uncond_indicator * conditioning_latents
|
| 756 |
+
+ (1 - current_uncond_indicator) * noise_pred_uncond
|
| 757 |
+
)
|
| 758 |
+
noise_pred_cond = (
|
| 759 |
+
current_cond_indicator * conditioning_latents + (1 - current_cond_indicator) * noise_pred_cond
|
| 760 |
+
)
|
| 761 |
+
noise_pred = noise_pred_cond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
|
| 762 |
+
else:
|
| 763 |
+
noise_pred = (
|
| 764 |
+
current_cond_indicator * conditioning_latents + (1 - current_cond_indicator) * noise_pred
|
| 765 |
+
)
|
| 766 |
+
|
| 767 |
+
# pred_sample (eps)
|
| 768 |
+
latents = self.scheduler.step(
|
| 769 |
+
noise_pred, t, latents, return_dict=False, pred_original_sample=noise_pred
|
| 770 |
+
)[0]
|
| 771 |
+
|
| 772 |
+
if callback_on_step_end is not None:
|
| 773 |
+
callback_kwargs = {}
|
| 774 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 775 |
+
callback_kwargs[k] = locals()[k]
|
| 776 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 777 |
+
|
| 778 |
+
latents = callback_outputs.pop("latents", latents)
|
| 779 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 780 |
+
negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
|
| 781 |
+
|
| 782 |
+
# call the callback, if provided
|
| 783 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 784 |
+
progress_bar.update()
|
| 785 |
+
|
| 786 |
+
if XLA_AVAILABLE:
|
| 787 |
+
xm.mark_step()
|
| 788 |
+
|
| 789 |
+
self._current_timestep = None
|
| 790 |
+
|
| 791 |
+
if not output_type == "latent":
|
| 792 |
+
if self.vae.config.latents_mean is not None:
|
| 793 |
+
latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
|
| 794 |
+
latents_mean = (
|
| 795 |
+
torch.tensor(latents_mean)
|
| 796 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
|
| 797 |
+
.to(latents)
|
| 798 |
+
)
|
| 799 |
+
latents_std = (
|
| 800 |
+
torch.tensor(latents_std)
|
| 801 |
+
.view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
|
| 802 |
+
.to(latents)
|
| 803 |
+
)
|
| 804 |
+
latents = latents * latents_std / self.scheduler.config.sigma_data + latents_mean
|
| 805 |
+
else:
|
| 806 |
+
latents = latents / self.scheduler.config.sigma_data
|
| 807 |
+
video = self.vae.decode(latents.to(vae_dtype), return_dict=False)[0]
|
| 808 |
+
|
| 809 |
+
if self.safety_checker is not None:
|
| 810 |
+
self.safety_checker.to(device)
|
| 811 |
+
video = self.video_processor.postprocess_video(video, output_type="np")
|
| 812 |
+
video = (video * 255).astype(np.uint8)
|
| 813 |
+
video_batch = []
|
| 814 |
+
for vid in video:
|
| 815 |
+
vid = self.safety_checker.check_video_safety(vid)
|
| 816 |
+
video_batch.append(vid)
|
| 817 |
+
video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
|
| 818 |
+
video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
|
| 819 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 820 |
+
self.safety_checker.to("cpu")
|
| 821 |
+
else:
|
| 822 |
+
video = self.video_processor.postprocess_video(video, output_type=output_type)
|
| 823 |
+
else:
|
| 824 |
+
video = latents
|
| 825 |
+
|
| 826 |
+
# Offload all models
|
| 827 |
+
self.maybe_free_model_hooks()
|
| 828 |
+
|
| 829 |
+
if not return_dict:
|
| 830 |
+
return (video,)
|
| 831 |
+
|
| 832 |
+
return CosmosPipelineOutput(frames=video)
|
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_output.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import PIL.Image
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
from diffusers.utils import BaseOutput, get_logger
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
logger = get_logger(__name__)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@dataclass
|
| 14 |
+
class CosmosPipelineOutput(BaseOutput):
|
| 15 |
+
r"""
|
| 16 |
+
Output class for Cosmos any-to-world/video pipelines.
|
| 17 |
+
|
| 18 |
+
Args:
|
| 19 |
+
frames (`torch.Tensor`, `np.ndarray`, or list[list[PIL.Image.Image]]):
|
| 20 |
+
list of video outputs - It can be a nested list of length `batch_size,` with each sub-list containing
|
| 21 |
+
denoised PIL image sequences of length `num_frames.` It can also be a NumPy array or Torch tensor of shape
|
| 22 |
+
`(batch_size, num_frames, channels, height, width)`.
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
frames: torch.Tensor
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@dataclass
|
| 29 |
+
class CosmosImagePipelineOutput(BaseOutput):
|
| 30 |
+
"""
|
| 31 |
+
Output class for Cosmos any-to-image pipelines.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
images (`list[PIL.Image.Image]` or `np.ndarray`)
|
| 35 |
+
list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
|
| 36 |
+
num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
images: list[PIL.Image.Image] | np.ndarray
|
third_party/diffusers/src/diffusers/pipelines/free_init_utils.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 math
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.fft as fft
|
| 19 |
+
|
| 20 |
+
from ..utils.torch_utils import randn_tensor
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class FreeInitMixin:
|
| 24 |
+
r"""Mixin class for FreeInit."""
|
| 25 |
+
|
| 26 |
+
def enable_free_init(
|
| 27 |
+
self,
|
| 28 |
+
num_iters: int = 3,
|
| 29 |
+
use_fast_sampling: bool = False,
|
| 30 |
+
method: str = "butterworth",
|
| 31 |
+
order: int = 4,
|
| 32 |
+
spatial_stop_frequency: float = 0.25,
|
| 33 |
+
temporal_stop_frequency: float = 0.25,
|
| 34 |
+
):
|
| 35 |
+
"""Enables the FreeInit mechanism as in https://huggingface.co/papers/2312.07537.
|
| 36 |
+
|
| 37 |
+
This implementation has been adapted from the [official repository](https://github.com/TianxingWu/FreeInit).
|
| 38 |
+
|
| 39 |
+
Args:
|
| 40 |
+
num_iters (`int`, *optional*, defaults to `3`):
|
| 41 |
+
Number of FreeInit noise re-initialization iterations.
|
| 42 |
+
use_fast_sampling (`bool`, *optional*, defaults to `False`):
|
| 43 |
+
Whether or not to speedup sampling procedure at the cost of probably lower quality results. Enables the
|
| 44 |
+
"Coarse-to-Fine Sampling" strategy, as mentioned in the paper, if set to `True`.
|
| 45 |
+
method (`str`, *optional*, defaults to `butterworth`):
|
| 46 |
+
Must be one of `butterworth`, `ideal` or `gaussian` to use as the filtering method for the FreeInit low
|
| 47 |
+
pass filter.
|
| 48 |
+
order (`int`, *optional*, defaults to `4`):
|
| 49 |
+
Order of the filter used in `butterworth` method. Larger values lead to `ideal` method behaviour
|
| 50 |
+
whereas lower values lead to `gaussian` method behaviour.
|
| 51 |
+
spatial_stop_frequency (`float`, *optional*, defaults to `0.25`):
|
| 52 |
+
Normalized stop frequency for spatial dimensions. Must be between 0 to 1. Referred to as `d_s` in the
|
| 53 |
+
original implementation.
|
| 54 |
+
temporal_stop_frequency (`float`, *optional*, defaults to `0.25`):
|
| 55 |
+
Normalized stop frequency for temporal dimensions. Must be between 0 to 1. Referred to as `d_t` in the
|
| 56 |
+
original implementation.
|
| 57 |
+
"""
|
| 58 |
+
self._free_init_num_iters = num_iters
|
| 59 |
+
self._free_init_use_fast_sampling = use_fast_sampling
|
| 60 |
+
self._free_init_method = method
|
| 61 |
+
self._free_init_order = order
|
| 62 |
+
self._free_init_spatial_stop_frequency = spatial_stop_frequency
|
| 63 |
+
self._free_init_temporal_stop_frequency = temporal_stop_frequency
|
| 64 |
+
|
| 65 |
+
def disable_free_init(self):
|
| 66 |
+
"""Disables the FreeInit mechanism if enabled."""
|
| 67 |
+
self._free_init_num_iters = None
|
| 68 |
+
|
| 69 |
+
@property
|
| 70 |
+
def free_init_enabled(self):
|
| 71 |
+
return hasattr(self, "_free_init_num_iters") and self._free_init_num_iters is not None
|
| 72 |
+
|
| 73 |
+
def _get_free_init_freq_filter(
|
| 74 |
+
self,
|
| 75 |
+
shape: tuple[int, ...],
|
| 76 |
+
device: str | torch.dtype,
|
| 77 |
+
filter_type: str,
|
| 78 |
+
order: float,
|
| 79 |
+
spatial_stop_frequency: float,
|
| 80 |
+
temporal_stop_frequency: float,
|
| 81 |
+
) -> torch.Tensor:
|
| 82 |
+
r"""Returns the FreeInit filter based on filter type and other input conditions."""
|
| 83 |
+
|
| 84 |
+
time, height, width = shape[-3], shape[-2], shape[-1]
|
| 85 |
+
mask = torch.zeros(shape)
|
| 86 |
+
|
| 87 |
+
if spatial_stop_frequency == 0 or temporal_stop_frequency == 0:
|
| 88 |
+
return mask
|
| 89 |
+
|
| 90 |
+
if filter_type == "butterworth":
|
| 91 |
+
|
| 92 |
+
def retrieve_mask(x):
|
| 93 |
+
return 1 / (1 + (x / spatial_stop_frequency**2) ** order)
|
| 94 |
+
elif filter_type == "gaussian":
|
| 95 |
+
|
| 96 |
+
def retrieve_mask(x):
|
| 97 |
+
return math.exp(-1 / (2 * spatial_stop_frequency**2) * x)
|
| 98 |
+
elif filter_type == "ideal":
|
| 99 |
+
|
| 100 |
+
def retrieve_mask(x):
|
| 101 |
+
return 1 if x <= spatial_stop_frequency * 2 else 0
|
| 102 |
+
else:
|
| 103 |
+
raise NotImplementedError("`filter_type` must be one of gaussian, butterworth or ideal")
|
| 104 |
+
|
| 105 |
+
for t in range(time):
|
| 106 |
+
for h in range(height):
|
| 107 |
+
for w in range(width):
|
| 108 |
+
d_square = (
|
| 109 |
+
((spatial_stop_frequency / temporal_stop_frequency) * (2 * t / time - 1)) ** 2
|
| 110 |
+
+ (2 * h / height - 1) ** 2
|
| 111 |
+
+ (2 * w / width - 1) ** 2
|
| 112 |
+
)
|
| 113 |
+
mask[..., t, h, w] = retrieve_mask(d_square)
|
| 114 |
+
|
| 115 |
+
return mask.to(device)
|
| 116 |
+
|
| 117 |
+
def _apply_freq_filter(self, x: torch.Tensor, noise: torch.Tensor, low_pass_filter: torch.Tensor) -> torch.Tensor:
|
| 118 |
+
r"""Noise reinitialization."""
|
| 119 |
+
# FFT
|
| 120 |
+
x_freq = fft.fftn(x, dim=(-3, -2, -1))
|
| 121 |
+
x_freq = fft.fftshift(x_freq, dim=(-3, -2, -1))
|
| 122 |
+
noise_freq = fft.fftn(noise, dim=(-3, -2, -1))
|
| 123 |
+
noise_freq = fft.fftshift(noise_freq, dim=(-3, -2, -1))
|
| 124 |
+
|
| 125 |
+
# frequency mix
|
| 126 |
+
high_pass_filter = 1 - low_pass_filter
|
| 127 |
+
x_freq_low = x_freq * low_pass_filter
|
| 128 |
+
noise_freq_high = noise_freq * high_pass_filter
|
| 129 |
+
x_freq_mixed = x_freq_low + noise_freq_high # mix in freq domain
|
| 130 |
+
|
| 131 |
+
# IFFT
|
| 132 |
+
x_freq_mixed = fft.ifftshift(x_freq_mixed, dim=(-3, -2, -1))
|
| 133 |
+
x_mixed = fft.ifftn(x_freq_mixed, dim=(-3, -2, -1)).real
|
| 134 |
+
|
| 135 |
+
return x_mixed
|
| 136 |
+
|
| 137 |
+
def _apply_free_init(
|
| 138 |
+
self,
|
| 139 |
+
latents: torch.Tensor,
|
| 140 |
+
free_init_iteration: int,
|
| 141 |
+
num_inference_steps: int,
|
| 142 |
+
device: torch.device,
|
| 143 |
+
dtype: torch.dtype,
|
| 144 |
+
generator: torch.Generator,
|
| 145 |
+
):
|
| 146 |
+
if free_init_iteration == 0:
|
| 147 |
+
self._free_init_initial_noise = latents.detach().clone()
|
| 148 |
+
else:
|
| 149 |
+
latent_shape = latents.shape
|
| 150 |
+
|
| 151 |
+
free_init_filter_shape = (1, *latent_shape[1:])
|
| 152 |
+
free_init_freq_filter = self._get_free_init_freq_filter(
|
| 153 |
+
shape=free_init_filter_shape,
|
| 154 |
+
device=device,
|
| 155 |
+
filter_type=self._free_init_method,
|
| 156 |
+
order=self._free_init_order,
|
| 157 |
+
spatial_stop_frequency=self._free_init_spatial_stop_frequency,
|
| 158 |
+
temporal_stop_frequency=self._free_init_temporal_stop_frequency,
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
current_diffuse_timestep = self.scheduler.config.num_train_timesteps - 1
|
| 162 |
+
diffuse_timesteps = torch.full((latent_shape[0],), current_diffuse_timestep).long()
|
| 163 |
+
|
| 164 |
+
z_t = self.scheduler.add_noise(
|
| 165 |
+
original_samples=latents, noise=self._free_init_initial_noise, timesteps=diffuse_timesteps.to(device)
|
| 166 |
+
).to(dtype=torch.float32)
|
| 167 |
+
|
| 168 |
+
z_rand = randn_tensor(
|
| 169 |
+
shape=latent_shape,
|
| 170 |
+
generator=generator,
|
| 171 |
+
device=device,
|
| 172 |
+
dtype=torch.float32,
|
| 173 |
+
)
|
| 174 |
+
latents = self._apply_freq_filter(z_t, z_rand, low_pass_filter=free_init_freq_filter)
|
| 175 |
+
latents = latents.to(dtype)
|
| 176 |
+
|
| 177 |
+
# Coarse-to-Fine Sampling for faster inference (can lead to lower quality)
|
| 178 |
+
if self._free_init_use_fast_sampling:
|
| 179 |
+
num_inference_steps = max(
|
| 180 |
+
1, int(num_inference_steps / self._free_init_num_iters * (free_init_iteration + 1))
|
| 181 |
+
)
|
| 182 |
+
|
| 183 |
+
if num_inference_steps > 0:
|
| 184 |
+
self.scheduler.set_timesteps(num_inference_steps, device=device)
|
| 185 |
+
|
| 186 |
+
return latents, self.scheduler.timesteps
|
third_party/diffusers/src/diffusers/pipelines/free_noise_utils.py
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The HuggingFace Team. All rights reserved.
|
| 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 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
|
| 20 |
+
from ..models.attention import BasicTransformerBlock, FreeNoiseTransformerBlock
|
| 21 |
+
from ..models.resnet import Downsample2D, ResnetBlock2D, Upsample2D
|
| 22 |
+
from ..models.transformers.transformer_2d import Transformer2DModel
|
| 23 |
+
from ..models.unets.unet_motion_model import (
|
| 24 |
+
AnimateDiffTransformer3D,
|
| 25 |
+
CrossAttnDownBlockMotion,
|
| 26 |
+
DownBlockMotion,
|
| 27 |
+
UpBlockMotion,
|
| 28 |
+
)
|
| 29 |
+
from ..pipelines.pipeline_utils import DiffusionPipeline
|
| 30 |
+
from ..utils import logging
|
| 31 |
+
from ..utils.torch_utils import randn_tensor
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class SplitInferenceModule(nn.Module):
|
| 38 |
+
r"""
|
| 39 |
+
A wrapper module class that splits inputs along a specified dimension before performing a forward pass.
|
| 40 |
+
|
| 41 |
+
This module is useful when you need to perform inference on large tensors in a memory-efficient way by breaking
|
| 42 |
+
them into smaller chunks, processing each chunk separately, and then reassembling the results.
|
| 43 |
+
|
| 44 |
+
Args:
|
| 45 |
+
module (`nn.Module`):
|
| 46 |
+
The underlying PyTorch module that will be applied to each chunk of split inputs.
|
| 47 |
+
split_size (`int`, defaults to `1`):
|
| 48 |
+
The size of each chunk after splitting the input tensor.
|
| 49 |
+
split_dim (`int`, defaults to `0`):
|
| 50 |
+
The dimension along which the input tensors are split.
|
| 51 |
+
input_kwargs_to_split (`list[str]`, defaults to `["hidden_states"]`):
|
| 52 |
+
A list of keyword arguments (strings) that represent the input tensors to be split.
|
| 53 |
+
|
| 54 |
+
Workflow:
|
| 55 |
+
1. The keyword arguments specified in `input_kwargs_to_split` are split into smaller chunks using
|
| 56 |
+
`torch.split()` along the dimension `split_dim` and with a chunk size of `split_size`.
|
| 57 |
+
2. The `module` is invoked once for each split with both the split inputs and any unchanged arguments
|
| 58 |
+
that were passed.
|
| 59 |
+
3. The output tensors from each split are concatenated back together along `split_dim` before returning.
|
| 60 |
+
|
| 61 |
+
Example:
|
| 62 |
+
```python
|
| 63 |
+
>>> import torch
|
| 64 |
+
>>> import torch.nn as nn
|
| 65 |
+
|
| 66 |
+
>>> model = nn.Linear(1000, 1000)
|
| 67 |
+
>>> split_module = SplitInferenceModule(model, split_size=2, split_dim=0, input_kwargs_to_split=["input"])
|
| 68 |
+
|
| 69 |
+
>>> input_tensor = torch.randn(42, 1000)
|
| 70 |
+
>>> # Will split the tensor into 21 slices of shape [2, 1000].
|
| 71 |
+
>>> output = split_module(input=input_tensor)
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
It is also possible to nest `SplitInferenceModule` across different split dimensions for more complex
|
| 75 |
+
multi-dimensional splitting.
|
| 76 |
+
"""
|
| 77 |
+
|
| 78 |
+
def __init__(
|
| 79 |
+
self,
|
| 80 |
+
module: nn.Module,
|
| 81 |
+
split_size: int = 1,
|
| 82 |
+
split_dim: int = 0,
|
| 83 |
+
input_kwargs_to_split: list[str] = ["hidden_states"],
|
| 84 |
+
) -> None:
|
| 85 |
+
super().__init__()
|
| 86 |
+
|
| 87 |
+
self.module = module
|
| 88 |
+
self.split_size = split_size
|
| 89 |
+
self.split_dim = split_dim
|
| 90 |
+
self.input_kwargs_to_split = set(input_kwargs_to_split)
|
| 91 |
+
|
| 92 |
+
def forward(self, *args, **kwargs) -> torch.Tensor | tuple[torch.Tensor]:
|
| 93 |
+
r"""Forward method for the `SplitInferenceModule`.
|
| 94 |
+
|
| 95 |
+
This method processes the input by splitting specified keyword arguments along a given dimension, running the
|
| 96 |
+
underlying module on each split, and then concatenating the results. The splitting is controlled by the
|
| 97 |
+
`split_size` and `split_dim` parameters specified during initialization.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
*args (`Any`):
|
| 101 |
+
Positional arguments that are passed directly to the `module` without modification.
|
| 102 |
+
**kwargs (`dict[str, torch.Tensor]`):
|
| 103 |
+
Keyword arguments passed to the underlying `module`. Only keyword arguments whose names match the
|
| 104 |
+
entries in `input_kwargs_to_split` and are of type `torch.Tensor` will be split. The remaining keyword
|
| 105 |
+
arguments are passed unchanged.
|
| 106 |
+
|
| 107 |
+
Returns:
|
| 108 |
+
`torch.Tensor | tuple[torch.Tensor]`:
|
| 109 |
+
The outputs obtained from `SplitInferenceModule` are the same as if the underlying module was inferred
|
| 110 |
+
without it.
|
| 111 |
+
- If the underlying module returns a single tensor, the result will be a single concatenated tensor
|
| 112 |
+
along the same `split_dim` after processing all splits.
|
| 113 |
+
- If the underlying module returns a tuple of tensors, each element of the tuple will be concatenated
|
| 114 |
+
along the `split_dim` across all splits, and the final result will be a tuple of concatenated tensors.
|
| 115 |
+
"""
|
| 116 |
+
split_inputs = {}
|
| 117 |
+
|
| 118 |
+
# 1. Split inputs that were specified during initialization and also present in passed kwargs
|
| 119 |
+
for key in list(kwargs.keys()):
|
| 120 |
+
if key not in self.input_kwargs_to_split or not torch.is_tensor(kwargs[key]):
|
| 121 |
+
continue
|
| 122 |
+
split_inputs[key] = torch.split(kwargs[key], self.split_size, self.split_dim)
|
| 123 |
+
kwargs.pop(key)
|
| 124 |
+
|
| 125 |
+
# 2. Invoke forward pass across each split
|
| 126 |
+
results = []
|
| 127 |
+
for split_input in zip(*split_inputs.values()):
|
| 128 |
+
inputs = dict(zip(split_inputs.keys(), split_input))
|
| 129 |
+
inputs.update(kwargs)
|
| 130 |
+
|
| 131 |
+
intermediate_tensor_or_tensor_tuple = self.module(*args, **inputs)
|
| 132 |
+
results.append(intermediate_tensor_or_tensor_tuple)
|
| 133 |
+
|
| 134 |
+
# 3. Concatenate split restuls to obtain final outputs
|
| 135 |
+
if isinstance(results[0], torch.Tensor):
|
| 136 |
+
return torch.cat(results, dim=self.split_dim)
|
| 137 |
+
elif isinstance(results[0], tuple):
|
| 138 |
+
return tuple([torch.cat(x, dim=self.split_dim) for x in zip(*results)])
|
| 139 |
+
else:
|
| 140 |
+
raise ValueError(
|
| 141 |
+
"In order to use the SplitInferenceModule, it is necessary for the underlying `module` to either return a torch.Tensor or a tuple of torch.Tensor's."
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
class AnimateDiffFreeNoiseMixin:
|
| 146 |
+
r"""Mixin class for [FreeNoise](https://huggingface.co/papers/2310.15169)."""
|
| 147 |
+
|
| 148 |
+
def _enable_free_noise_in_block(self, block: CrossAttnDownBlockMotion | DownBlockMotion | UpBlockMotion):
|
| 149 |
+
r"""Helper function to enable FreeNoise in transformer blocks."""
|
| 150 |
+
|
| 151 |
+
for motion_module in block.motion_modules:
|
| 152 |
+
num_transformer_blocks = len(motion_module.transformer_blocks)
|
| 153 |
+
|
| 154 |
+
for i in range(num_transformer_blocks):
|
| 155 |
+
if isinstance(motion_module.transformer_blocks[i], FreeNoiseTransformerBlock):
|
| 156 |
+
motion_module.transformer_blocks[i].set_free_noise_properties(
|
| 157 |
+
self._free_noise_context_length,
|
| 158 |
+
self._free_noise_context_stride,
|
| 159 |
+
self._free_noise_weighting_scheme,
|
| 160 |
+
)
|
| 161 |
+
else:
|
| 162 |
+
assert isinstance(motion_module.transformer_blocks[i], BasicTransformerBlock)
|
| 163 |
+
basic_transfomer_block = motion_module.transformer_blocks[i]
|
| 164 |
+
|
| 165 |
+
motion_module.transformer_blocks[i] = FreeNoiseTransformerBlock(
|
| 166 |
+
dim=basic_transfomer_block.dim,
|
| 167 |
+
num_attention_heads=basic_transfomer_block.num_attention_heads,
|
| 168 |
+
attention_head_dim=basic_transfomer_block.attention_head_dim,
|
| 169 |
+
dropout=basic_transfomer_block.dropout,
|
| 170 |
+
cross_attention_dim=basic_transfomer_block.cross_attention_dim,
|
| 171 |
+
activation_fn=basic_transfomer_block.activation_fn,
|
| 172 |
+
attention_bias=basic_transfomer_block.attention_bias,
|
| 173 |
+
only_cross_attention=basic_transfomer_block.only_cross_attention,
|
| 174 |
+
double_self_attention=basic_transfomer_block.double_self_attention,
|
| 175 |
+
positional_embeddings=basic_transfomer_block.positional_embeddings,
|
| 176 |
+
num_positional_embeddings=basic_transfomer_block.num_positional_embeddings,
|
| 177 |
+
context_length=self._free_noise_context_length,
|
| 178 |
+
context_stride=self._free_noise_context_stride,
|
| 179 |
+
weighting_scheme=self._free_noise_weighting_scheme,
|
| 180 |
+
).to(device=self.device, dtype=self.dtype)
|
| 181 |
+
|
| 182 |
+
motion_module.transformer_blocks[i].load_state_dict(
|
| 183 |
+
basic_transfomer_block.state_dict(), strict=True
|
| 184 |
+
)
|
| 185 |
+
motion_module.transformer_blocks[i].set_chunk_feed_forward(
|
| 186 |
+
basic_transfomer_block._chunk_size, basic_transfomer_block._chunk_dim
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
def _disable_free_noise_in_block(self, block: CrossAttnDownBlockMotion | DownBlockMotion | UpBlockMotion):
|
| 190 |
+
r"""Helper function to disable FreeNoise in transformer blocks."""
|
| 191 |
+
|
| 192 |
+
for motion_module in block.motion_modules:
|
| 193 |
+
num_transformer_blocks = len(motion_module.transformer_blocks)
|
| 194 |
+
|
| 195 |
+
for i in range(num_transformer_blocks):
|
| 196 |
+
if isinstance(motion_module.transformer_blocks[i], FreeNoiseTransformerBlock):
|
| 197 |
+
free_noise_transfomer_block = motion_module.transformer_blocks[i]
|
| 198 |
+
|
| 199 |
+
motion_module.transformer_blocks[i] = BasicTransformerBlock(
|
| 200 |
+
dim=free_noise_transfomer_block.dim,
|
| 201 |
+
num_attention_heads=free_noise_transfomer_block.num_attention_heads,
|
| 202 |
+
attention_head_dim=free_noise_transfomer_block.attention_head_dim,
|
| 203 |
+
dropout=free_noise_transfomer_block.dropout,
|
| 204 |
+
cross_attention_dim=free_noise_transfomer_block.cross_attention_dim,
|
| 205 |
+
activation_fn=free_noise_transfomer_block.activation_fn,
|
| 206 |
+
attention_bias=free_noise_transfomer_block.attention_bias,
|
| 207 |
+
only_cross_attention=free_noise_transfomer_block.only_cross_attention,
|
| 208 |
+
double_self_attention=free_noise_transfomer_block.double_self_attention,
|
| 209 |
+
positional_embeddings=free_noise_transfomer_block.positional_embeddings,
|
| 210 |
+
num_positional_embeddings=free_noise_transfomer_block.num_positional_embeddings,
|
| 211 |
+
).to(device=self.device, dtype=self.dtype)
|
| 212 |
+
|
| 213 |
+
motion_module.transformer_blocks[i].load_state_dict(
|
| 214 |
+
free_noise_transfomer_block.state_dict(), strict=True
|
| 215 |
+
)
|
| 216 |
+
motion_module.transformer_blocks[i].set_chunk_feed_forward(
|
| 217 |
+
free_noise_transfomer_block._chunk_size, free_noise_transfomer_block._chunk_dim
|
| 218 |
+
)
|
| 219 |
+
|
| 220 |
+
def _check_inputs_free_noise(
|
| 221 |
+
self,
|
| 222 |
+
prompt,
|
| 223 |
+
negative_prompt,
|
| 224 |
+
prompt_embeds,
|
| 225 |
+
negative_prompt_embeds,
|
| 226 |
+
num_frames,
|
| 227 |
+
) -> None:
|
| 228 |
+
if not isinstance(prompt, (str, dict)):
|
| 229 |
+
raise ValueError(f"Expected `prompt` to have type `str` or `dict` but found {type(prompt)=}")
|
| 230 |
+
|
| 231 |
+
if negative_prompt is not None:
|
| 232 |
+
if not isinstance(negative_prompt, (str, dict)):
|
| 233 |
+
raise ValueError(
|
| 234 |
+
f"Expected `negative_prompt` to have type `str` or `dict` but found {type(negative_prompt)=}"
|
| 235 |
+
)
|
| 236 |
+
|
| 237 |
+
if prompt_embeds is not None or negative_prompt_embeds is not None:
|
| 238 |
+
raise ValueError("`prompt_embeds` and `negative_prompt_embeds` is not supported in FreeNoise yet.")
|
| 239 |
+
|
| 240 |
+
frame_indices = [isinstance(x, int) for x in prompt.keys()]
|
| 241 |
+
frame_prompts = [isinstance(x, str) for x in prompt.values()]
|
| 242 |
+
min_frame = min(list(prompt.keys()))
|
| 243 |
+
max_frame = max(list(prompt.keys()))
|
| 244 |
+
|
| 245 |
+
if not all(frame_indices):
|
| 246 |
+
raise ValueError("Expected integer keys in `prompt` dict for FreeNoise.")
|
| 247 |
+
if not all(frame_prompts):
|
| 248 |
+
raise ValueError("Expected str values in `prompt` dict for FreeNoise.")
|
| 249 |
+
if min_frame != 0:
|
| 250 |
+
raise ValueError("The minimum frame index in `prompt` dict must be 0 as a starting prompt is necessary.")
|
| 251 |
+
if max_frame >= num_frames:
|
| 252 |
+
raise ValueError(
|
| 253 |
+
f"The maximum frame index in `prompt` dict must be lesser than {num_frames=} and follow 0-based indexing."
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
def _encode_prompt_free_noise(
|
| 257 |
+
self,
|
| 258 |
+
prompt: str | dict[int, str],
|
| 259 |
+
num_frames: int,
|
| 260 |
+
device: torch.device,
|
| 261 |
+
num_videos_per_prompt: int,
|
| 262 |
+
do_classifier_free_guidance: bool,
|
| 263 |
+
negative_prompt: str | dict[int, str] | None = None,
|
| 264 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 265 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 266 |
+
lora_scale: float | None = None,
|
| 267 |
+
clip_skip: int | None = None,
|
| 268 |
+
) -> torch.Tensor:
|
| 269 |
+
if negative_prompt is None:
|
| 270 |
+
negative_prompt = ""
|
| 271 |
+
|
| 272 |
+
# Ensure that we have a dictionary of prompts
|
| 273 |
+
if isinstance(prompt, str):
|
| 274 |
+
prompt = {0: prompt}
|
| 275 |
+
if isinstance(negative_prompt, str):
|
| 276 |
+
negative_prompt = {0: negative_prompt}
|
| 277 |
+
|
| 278 |
+
self._check_inputs_free_noise(prompt, negative_prompt, prompt_embeds, negative_prompt_embeds, num_frames)
|
| 279 |
+
|
| 280 |
+
# Sort the prompts based on frame indices
|
| 281 |
+
prompt = dict(sorted(prompt.items()))
|
| 282 |
+
negative_prompt = dict(sorted(negative_prompt.items()))
|
| 283 |
+
|
| 284 |
+
# Ensure that we have a prompt for the last frame index
|
| 285 |
+
prompt[num_frames - 1] = prompt[list(prompt.keys())[-1]]
|
| 286 |
+
negative_prompt[num_frames - 1] = negative_prompt[list(negative_prompt.keys())[-1]]
|
| 287 |
+
|
| 288 |
+
frame_indices = list(prompt.keys())
|
| 289 |
+
frame_prompts = list(prompt.values())
|
| 290 |
+
frame_negative_indices = list(negative_prompt.keys())
|
| 291 |
+
frame_negative_prompts = list(negative_prompt.values())
|
| 292 |
+
|
| 293 |
+
# Generate and interpolate positive prompts
|
| 294 |
+
prompt_embeds, _ = self.encode_prompt(
|
| 295 |
+
prompt=frame_prompts,
|
| 296 |
+
device=device,
|
| 297 |
+
num_images_per_prompt=num_videos_per_prompt,
|
| 298 |
+
do_classifier_free_guidance=False,
|
| 299 |
+
negative_prompt=None,
|
| 300 |
+
prompt_embeds=None,
|
| 301 |
+
negative_prompt_embeds=None,
|
| 302 |
+
lora_scale=lora_scale,
|
| 303 |
+
clip_skip=clip_skip,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
shape = (num_frames, *prompt_embeds.shape[1:])
|
| 307 |
+
prompt_interpolation_embeds = prompt_embeds.new_zeros(shape)
|
| 308 |
+
|
| 309 |
+
for i in range(len(frame_indices) - 1):
|
| 310 |
+
start_frame = frame_indices[i]
|
| 311 |
+
end_frame = frame_indices[i + 1]
|
| 312 |
+
start_tensor = prompt_embeds[i].unsqueeze(0)
|
| 313 |
+
end_tensor = prompt_embeds[i + 1].unsqueeze(0)
|
| 314 |
+
|
| 315 |
+
prompt_interpolation_embeds[start_frame : end_frame + 1] = self._free_noise_prompt_interpolation_callback(
|
| 316 |
+
start_frame, end_frame, start_tensor, end_tensor
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
# Generate and interpolate negative prompts
|
| 320 |
+
negative_prompt_embeds = None
|
| 321 |
+
negative_prompt_interpolation_embeds = None
|
| 322 |
+
|
| 323 |
+
if do_classifier_free_guidance:
|
| 324 |
+
_, negative_prompt_embeds = self.encode_prompt(
|
| 325 |
+
prompt=[""] * len(frame_negative_prompts),
|
| 326 |
+
device=device,
|
| 327 |
+
num_images_per_prompt=num_videos_per_prompt,
|
| 328 |
+
do_classifier_free_guidance=True,
|
| 329 |
+
negative_prompt=frame_negative_prompts,
|
| 330 |
+
prompt_embeds=None,
|
| 331 |
+
negative_prompt_embeds=None,
|
| 332 |
+
lora_scale=lora_scale,
|
| 333 |
+
clip_skip=clip_skip,
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
negative_prompt_interpolation_embeds = negative_prompt_embeds.new_zeros(shape)
|
| 337 |
+
|
| 338 |
+
for i in range(len(frame_negative_indices) - 1):
|
| 339 |
+
start_frame = frame_negative_indices[i]
|
| 340 |
+
end_frame = frame_negative_indices[i + 1]
|
| 341 |
+
start_tensor = negative_prompt_embeds[i].unsqueeze(0)
|
| 342 |
+
end_tensor = negative_prompt_embeds[i + 1].unsqueeze(0)
|
| 343 |
+
|
| 344 |
+
negative_prompt_interpolation_embeds[start_frame : end_frame + 1] = (
|
| 345 |
+
self._free_noise_prompt_interpolation_callback(start_frame, end_frame, start_tensor, end_tensor)
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
prompt_embeds = prompt_interpolation_embeds
|
| 349 |
+
negative_prompt_embeds = negative_prompt_interpolation_embeds
|
| 350 |
+
|
| 351 |
+
if do_classifier_free_guidance:
|
| 352 |
+
prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
|
| 353 |
+
|
| 354 |
+
return prompt_embeds, negative_prompt_embeds
|
| 355 |
+
|
| 356 |
+
def _prepare_latents_free_noise(
|
| 357 |
+
self,
|
| 358 |
+
batch_size: int,
|
| 359 |
+
num_channels_latents: int,
|
| 360 |
+
num_frames: int,
|
| 361 |
+
height: int,
|
| 362 |
+
width: int,
|
| 363 |
+
dtype: torch.dtype,
|
| 364 |
+
device: torch.device,
|
| 365 |
+
generator: torch.Generator | None = None,
|
| 366 |
+
latents: torch.Tensor | None = None,
|
| 367 |
+
):
|
| 368 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 369 |
+
raise ValueError(
|
| 370 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 371 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 372 |
+
)
|
| 373 |
+
|
| 374 |
+
context_num_frames = (
|
| 375 |
+
self._free_noise_context_length if self._free_noise_context_length == "repeat_context" else num_frames
|
| 376 |
+
)
|
| 377 |
+
|
| 378 |
+
shape = (
|
| 379 |
+
batch_size,
|
| 380 |
+
num_channels_latents,
|
| 381 |
+
context_num_frames,
|
| 382 |
+
height // self.vae_scale_factor,
|
| 383 |
+
width // self.vae_scale_factor,
|
| 384 |
+
)
|
| 385 |
+
|
| 386 |
+
if latents is None:
|
| 387 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 388 |
+
if self._free_noise_noise_type == "random":
|
| 389 |
+
return latents
|
| 390 |
+
else:
|
| 391 |
+
if latents.size(2) == num_frames:
|
| 392 |
+
return latents
|
| 393 |
+
elif latents.size(2) != self._free_noise_context_length:
|
| 394 |
+
raise ValueError(
|
| 395 |
+
f"You have passed `latents` as a parameter to FreeNoise. The expected number of frames is either {num_frames} or {self._free_noise_context_length}, but found {latents.size(2)}"
|
| 396 |
+
)
|
| 397 |
+
latents = latents.to(device)
|
| 398 |
+
|
| 399 |
+
if self._free_noise_noise_type == "shuffle_context":
|
| 400 |
+
for i in range(self._free_noise_context_length, num_frames, self._free_noise_context_stride):
|
| 401 |
+
# ensure window is within bounds
|
| 402 |
+
window_start = max(0, i - self._free_noise_context_length)
|
| 403 |
+
window_end = min(num_frames, window_start + self._free_noise_context_stride)
|
| 404 |
+
window_length = window_end - window_start
|
| 405 |
+
|
| 406 |
+
if window_length == 0:
|
| 407 |
+
break
|
| 408 |
+
|
| 409 |
+
indices = torch.LongTensor(list(range(window_start, window_end)))
|
| 410 |
+
shuffled_indices = indices[torch.randperm(window_length, generator=generator)]
|
| 411 |
+
|
| 412 |
+
current_start = i
|
| 413 |
+
current_end = min(num_frames, current_start + window_length)
|
| 414 |
+
if current_end == current_start + window_length:
|
| 415 |
+
# batch of frames perfectly fits the window
|
| 416 |
+
latents[:, :, current_start:current_end] = latents[:, :, shuffled_indices]
|
| 417 |
+
else:
|
| 418 |
+
# handle the case where the last batch of frames does not fit perfectly with the window
|
| 419 |
+
prefix_length = current_end - current_start
|
| 420 |
+
shuffled_indices = shuffled_indices[:prefix_length]
|
| 421 |
+
latents[:, :, current_start:current_end] = latents[:, :, shuffled_indices]
|
| 422 |
+
|
| 423 |
+
elif self._free_noise_noise_type == "repeat_context":
|
| 424 |
+
num_repeats = (num_frames + self._free_noise_context_length - 1) // self._free_noise_context_length
|
| 425 |
+
latents = torch.cat([latents] * num_repeats, dim=2)
|
| 426 |
+
|
| 427 |
+
latents = latents[:, :, :num_frames]
|
| 428 |
+
return latents
|
| 429 |
+
|
| 430 |
+
def _lerp(
|
| 431 |
+
self, start_index: int, end_index: int, start_tensor: torch.Tensor, end_tensor: torch.Tensor
|
| 432 |
+
) -> torch.Tensor:
|
| 433 |
+
num_indices = end_index - start_index + 1
|
| 434 |
+
interpolated_tensors = []
|
| 435 |
+
|
| 436 |
+
for i in range(num_indices):
|
| 437 |
+
alpha = i / (num_indices - 1)
|
| 438 |
+
interpolated_tensor = (1 - alpha) * start_tensor + alpha * end_tensor
|
| 439 |
+
interpolated_tensors.append(interpolated_tensor)
|
| 440 |
+
|
| 441 |
+
interpolated_tensors = torch.cat(interpolated_tensors)
|
| 442 |
+
return interpolated_tensors
|
| 443 |
+
|
| 444 |
+
def enable_free_noise(
|
| 445 |
+
self,
|
| 446 |
+
context_length: int | None = 16,
|
| 447 |
+
context_stride: int = 4,
|
| 448 |
+
weighting_scheme: str = "pyramid",
|
| 449 |
+
noise_type: str = "shuffle_context",
|
| 450 |
+
prompt_interpolation_callback: Callable[
|
| 451 |
+
[DiffusionPipeline, int, int, torch.Tensor, torch.Tensor], torch.Tensor
|
| 452 |
+
]
|
| 453 |
+
| None = None,
|
| 454 |
+
) -> None:
|
| 455 |
+
r"""
|
| 456 |
+
Enable long video generation using FreeNoise.
|
| 457 |
+
|
| 458 |
+
Args:
|
| 459 |
+
context_length (`int`, defaults to `16`, *optional*):
|
| 460 |
+
The number of video frames to process at once. It's recommended to set this to the maximum frames the
|
| 461 |
+
Motion Adapter was trained with (usually 16/24/32). If `None`, the default value from the motion
|
| 462 |
+
adapter config is used.
|
| 463 |
+
context_stride (`int`, *optional*):
|
| 464 |
+
Long videos are generated by processing many frames. FreeNoise processes these frames in sliding
|
| 465 |
+
windows of size `context_length`. Context stride allows you to specify how many frames to skip between
|
| 466 |
+
each window. For example, a context length of 16 and context stride of 4 would process 24 frames as:
|
| 467 |
+
[0, 15], [4, 19], [8, 23] (0-based indexing)
|
| 468 |
+
weighting_scheme (`str`, defaults to `pyramid`):
|
| 469 |
+
Weighting scheme for averaging latents after accumulation in FreeNoise blocks. The following weighting
|
| 470 |
+
schemes are supported currently:
|
| 471 |
+
- "flat"
|
| 472 |
+
Performs weighting averaging with a flat weight pattern: [1, 1, 1, 1, 1].
|
| 473 |
+
- "pyramid"
|
| 474 |
+
Performs weighted averaging with a pyramid like weight pattern: [1, 2, 3, 2, 1].
|
| 475 |
+
- "delayed_reverse_sawtooth"
|
| 476 |
+
Performs weighted averaging with low weights for earlier frames and high-to-low weights for
|
| 477 |
+
later frames: [0.01, 0.01, 3, 2, 1].
|
| 478 |
+
noise_type (`str`, defaults to "shuffle_context"):
|
| 479 |
+
Must be one of ["shuffle_context", "repeat_context", "random"].
|
| 480 |
+
- "shuffle_context"
|
| 481 |
+
Shuffles a fixed batch of `context_length` latents to create a final latent of size
|
| 482 |
+
`num_frames`. This is usually the best setting for most generation scenarios. However, there
|
| 483 |
+
might be visible repetition noticeable in the kinds of motion/animation generated.
|
| 484 |
+
- "repeated_context"
|
| 485 |
+
Repeats a fixed batch of `context_length` latents to create a final latent of size
|
| 486 |
+
`num_frames`.
|
| 487 |
+
- "random"
|
| 488 |
+
The final latents are random without any repetition.
|
| 489 |
+
"""
|
| 490 |
+
|
| 491 |
+
allowed_weighting_scheme = ["flat", "pyramid", "delayed_reverse_sawtooth"]
|
| 492 |
+
allowed_noise_type = ["shuffle_context", "repeat_context", "random"]
|
| 493 |
+
|
| 494 |
+
if context_length > self.motion_adapter.config.motion_max_seq_length:
|
| 495 |
+
logger.warning(
|
| 496 |
+
f"You have set {context_length=} which is greater than {self.motion_adapter.config.motion_max_seq_length=}. This can lead to bad generation results."
|
| 497 |
+
)
|
| 498 |
+
if weighting_scheme not in allowed_weighting_scheme:
|
| 499 |
+
raise ValueError(
|
| 500 |
+
f"The parameter `weighting_scheme` must be one of {allowed_weighting_scheme}, but got {weighting_scheme=}"
|
| 501 |
+
)
|
| 502 |
+
if noise_type not in allowed_noise_type:
|
| 503 |
+
raise ValueError(f"The parameter `noise_type` must be one of {allowed_noise_type}, but got {noise_type=}")
|
| 504 |
+
|
| 505 |
+
self._free_noise_context_length = context_length or self.motion_adapter.config.motion_max_seq_length
|
| 506 |
+
self._free_noise_context_stride = context_stride
|
| 507 |
+
self._free_noise_weighting_scheme = weighting_scheme
|
| 508 |
+
self._free_noise_noise_type = noise_type
|
| 509 |
+
self._free_noise_prompt_interpolation_callback = prompt_interpolation_callback or self._lerp
|
| 510 |
+
|
| 511 |
+
if hasattr(self.unet.mid_block, "motion_modules"):
|
| 512 |
+
blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
|
| 513 |
+
else:
|
| 514 |
+
blocks = [*self.unet.down_blocks, *self.unet.up_blocks]
|
| 515 |
+
|
| 516 |
+
for block in blocks:
|
| 517 |
+
self._enable_free_noise_in_block(block)
|
| 518 |
+
|
| 519 |
+
def disable_free_noise(self) -> None:
|
| 520 |
+
r"""Disable the FreeNoise sampling mechanism."""
|
| 521 |
+
self._free_noise_context_length = None
|
| 522 |
+
|
| 523 |
+
if hasattr(self.unet.mid_block, "motion_modules"):
|
| 524 |
+
blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
|
| 525 |
+
else:
|
| 526 |
+
blocks = [*self.unet.down_blocks, *self.unet.up_blocks]
|
| 527 |
+
|
| 528 |
+
blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
|
| 529 |
+
for block in blocks:
|
| 530 |
+
self._disable_free_noise_in_block(block)
|
| 531 |
+
|
| 532 |
+
def _enable_split_inference_motion_modules_(
|
| 533 |
+
self, motion_modules: list[AnimateDiffTransformer3D], spatial_split_size: int
|
| 534 |
+
) -> None:
|
| 535 |
+
for motion_module in motion_modules:
|
| 536 |
+
motion_module.proj_in = SplitInferenceModule(motion_module.proj_in, spatial_split_size, 0, ["input"])
|
| 537 |
+
|
| 538 |
+
for i in range(len(motion_module.transformer_blocks)):
|
| 539 |
+
motion_module.transformer_blocks[i] = SplitInferenceModule(
|
| 540 |
+
motion_module.transformer_blocks[i],
|
| 541 |
+
spatial_split_size,
|
| 542 |
+
0,
|
| 543 |
+
["hidden_states", "encoder_hidden_states"],
|
| 544 |
+
)
|
| 545 |
+
|
| 546 |
+
motion_module.proj_out = SplitInferenceModule(motion_module.proj_out, spatial_split_size, 0, ["input"])
|
| 547 |
+
|
| 548 |
+
def _enable_split_inference_attentions_(
|
| 549 |
+
self, attentions: list[Transformer2DModel], temporal_split_size: int
|
| 550 |
+
) -> None:
|
| 551 |
+
for i in range(len(attentions)):
|
| 552 |
+
attentions[i] = SplitInferenceModule(
|
| 553 |
+
attentions[i], temporal_split_size, 0, ["hidden_states", "encoder_hidden_states"]
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
def _enable_split_inference_resnets_(self, resnets: list[ResnetBlock2D], temporal_split_size: int) -> None:
|
| 557 |
+
for i in range(len(resnets)):
|
| 558 |
+
resnets[i] = SplitInferenceModule(resnets[i], temporal_split_size, 0, ["input_tensor", "temb"])
|
| 559 |
+
|
| 560 |
+
def _enable_split_inference_samplers_(
|
| 561 |
+
self, samplers: list[Downsample2D] | list[Upsample2D], temporal_split_size: int
|
| 562 |
+
) -> None:
|
| 563 |
+
for i in range(len(samplers)):
|
| 564 |
+
samplers[i] = SplitInferenceModule(samplers[i], temporal_split_size, 0, ["hidden_states"])
|
| 565 |
+
|
| 566 |
+
def enable_free_noise_split_inference(self, spatial_split_size: int = 256, temporal_split_size: int = 16) -> None:
|
| 567 |
+
r"""
|
| 568 |
+
Enable FreeNoise memory optimizations by utilizing
|
| 569 |
+
[`~diffusers.pipelines.free_noise_utils.SplitInferenceModule`] across different intermediate modeling blocks.
|
| 570 |
+
|
| 571 |
+
Args:
|
| 572 |
+
spatial_split_size (`int`, defaults to `256`):
|
| 573 |
+
The split size across spatial dimensions for internal blocks. This is used in facilitating split
|
| 574 |
+
inference across the effective batch dimension (`[B x H x W, F, C]`) of intermediate tensors in motion
|
| 575 |
+
modeling blocks.
|
| 576 |
+
temporal_split_size (`int`, defaults to `16`):
|
| 577 |
+
The split size across temporal dimensions for internal blocks. This is used in facilitating split
|
| 578 |
+
inference across the effective batch dimension (`[B x F, H x W, C]`) of intermediate tensors in spatial
|
| 579 |
+
attention, resnets, downsampling and upsampling blocks.
|
| 580 |
+
"""
|
| 581 |
+
# TODO(aryan): Discuss on what's the best way to provide more control to users
|
| 582 |
+
blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
|
| 583 |
+
for block in blocks:
|
| 584 |
+
if getattr(block, "motion_modules", None) is not None:
|
| 585 |
+
self._enable_split_inference_motion_modules_(block.motion_modules, spatial_split_size)
|
| 586 |
+
if getattr(block, "attentions", None) is not None:
|
| 587 |
+
self._enable_split_inference_attentions_(block.attentions, temporal_split_size)
|
| 588 |
+
if getattr(block, "resnets", None) is not None:
|
| 589 |
+
self._enable_split_inference_resnets_(block.resnets, temporal_split_size)
|
| 590 |
+
if getattr(block, "downsamplers", None) is not None:
|
| 591 |
+
self._enable_split_inference_samplers_(block.downsamplers, temporal_split_size)
|
| 592 |
+
if getattr(block, "upsamplers", None) is not None:
|
| 593 |
+
self._enable_split_inference_samplers_(block.upsamplers, temporal_split_size)
|
| 594 |
+
|
| 595 |
+
@property
|
| 596 |
+
def free_noise_enabled(self):
|
| 597 |
+
return hasattr(self, "_free_noise_context_length") and self._free_noise_context_length is not None
|
third_party/diffusers/src/diffusers/pipelines/glm_image/__init__.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ...utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_torch_available,
|
| 9 |
+
is_transformers_available,
|
| 10 |
+
is_transformers_version,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
_dummy_objects = {}
|
| 15 |
+
_additional_imports = {}
|
| 16 |
+
_import_structure = {"pipeline_output": ["GlmImagePipelineOutput"]}
|
| 17 |
+
|
| 18 |
+
# Import transformers components so they can be resolved during pipeline loading
|
| 19 |
+
|
| 20 |
+
if is_transformers_available() and is_transformers_version(">=", "4.57.4"):
|
| 21 |
+
try:
|
| 22 |
+
from transformers import GlmImageForConditionalGeneration, GlmImageProcessor
|
| 23 |
+
|
| 24 |
+
_additional_imports["GlmImageForConditionalGeneration"] = GlmImageForConditionalGeneration
|
| 25 |
+
_additional_imports["GlmImageProcessor"] = GlmImageProcessor
|
| 26 |
+
except ImportError:
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
try:
|
| 30 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 31 |
+
raise OptionalDependencyNotAvailable()
|
| 32 |
+
except OptionalDependencyNotAvailable:
|
| 33 |
+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
| 34 |
+
|
| 35 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 36 |
+
else:
|
| 37 |
+
_import_structure["pipeline_glm_image"] = ["GlmImagePipeline"]
|
| 38 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 39 |
+
try:
|
| 40 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 41 |
+
raise OptionalDependencyNotAvailable()
|
| 42 |
+
except OptionalDependencyNotAvailable:
|
| 43 |
+
from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
|
| 44 |
+
else:
|
| 45 |
+
from .pipeline_glm_image import GlmImagePipeline
|
| 46 |
+
else:
|
| 47 |
+
import sys
|
| 48 |
+
|
| 49 |
+
sys.modules[__name__] = _LazyModule(
|
| 50 |
+
__name__,
|
| 51 |
+
globals()["__file__"],
|
| 52 |
+
_import_structure,
|
| 53 |
+
module_spec=__spec__,
|
| 54 |
+
)
|
| 55 |
+
|
| 56 |
+
for name, value in _dummy_objects.items():
|
| 57 |
+
setattr(sys.modules[__name__], name, value)
|
| 58 |
+
for name, value in _additional_imports.items():
|
| 59 |
+
setattr(sys.modules[__name__], name, value)
|
third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_glm_image.py
ADDED
|
@@ -0,0 +1,1049 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 The CogVideoX team, Tsinghua University & ZhipuAI and The HuggingFace Team.
|
| 2 |
+
# All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import inspect
|
| 17 |
+
import re
|
| 18 |
+
from typing import Any, Callable
|
| 19 |
+
|
| 20 |
+
import numpy as np
|
| 21 |
+
import PIL
|
| 22 |
+
import torch
|
| 23 |
+
from transformers import ByT5Tokenizer, PreTrainedModel, ProcessorMixin, T5EncoderModel
|
| 24 |
+
|
| 25 |
+
from ...callbacks import MultiPipelineCallbacks, PipelineCallback
|
| 26 |
+
from ...image_processor import VaeImageProcessor
|
| 27 |
+
from ...models import AutoencoderKL, GlmImageTransformer2DModel
|
| 28 |
+
from ...models.transformers.transformer_glm_image import GlmImageKVCache
|
| 29 |
+
from ...pipelines.pipeline_utils import DiffusionPipeline
|
| 30 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler
|
| 31 |
+
from ...utils import is_torch_xla_available, is_transformers_version, logging, replace_example_docstring
|
| 32 |
+
from ...utils.torch_utils import randn_tensor
|
| 33 |
+
from .pipeline_output import GlmImagePipelineOutput
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# Because it's not released in stable as of 13/01/2026. So this is just a proxy.
|
| 37 |
+
GlmImageProcessor = ProcessorMixin
|
| 38 |
+
GlmImageForConditionalGeneration = PreTrainedModel
|
| 39 |
+
if is_transformers_version(">=", "5.0.0.dev0"):
|
| 40 |
+
from transformers import GlmImageForConditionalGeneration, GlmImageProcessor
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
if is_torch_xla_available():
|
| 44 |
+
import torch_xla.core.xla_model as xm
|
| 45 |
+
|
| 46 |
+
XLA_AVAILABLE = True
|
| 47 |
+
else:
|
| 48 |
+
XLA_AVAILABLE = False
|
| 49 |
+
|
| 50 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 51 |
+
|
| 52 |
+
EXAMPLE_DOC_STRING = """
|
| 53 |
+
Examples:
|
| 54 |
+
```python
|
| 55 |
+
>>> import torch
|
| 56 |
+
>>> from diffusers import GlmImagePipeline
|
| 57 |
+
|
| 58 |
+
>>> pipe = GlmImagePipeline.from_pretrained("zai-org/GLM-Image", torch_dtype=torch.bfloat16)
|
| 59 |
+
>>> pipe.to("cuda")
|
| 60 |
+
|
| 61 |
+
>>> prompt = "A photo of an astronaut riding a horse on mars"
|
| 62 |
+
>>> image = pipe(prompt).images[0]
|
| 63 |
+
>>> image.save("output.png")
|
| 64 |
+
```
|
| 65 |
+
"""
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def calculate_shift(
|
| 69 |
+
image_seq_len,
|
| 70 |
+
base_seq_len: int = 256,
|
| 71 |
+
base_shift: float = 0.25,
|
| 72 |
+
max_shift: float = 0.75,
|
| 73 |
+
) -> float:
|
| 74 |
+
m = (image_seq_len / base_seq_len) ** 0.5
|
| 75 |
+
mu = m * max_shift + base_shift
|
| 76 |
+
return mu
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
# Copied from diffusers.pipelines.cogview4.pipeline_cogview4.retrieve_timesteps
|
| 80 |
+
def retrieve_timesteps(
|
| 81 |
+
scheduler,
|
| 82 |
+
num_inference_steps: int | None = None,
|
| 83 |
+
device: str | torch.device | None = None,
|
| 84 |
+
timesteps: list[int] | None = None,
|
| 85 |
+
sigmas: list[float] | None = None,
|
| 86 |
+
**kwargs,
|
| 87 |
+
):
|
| 88 |
+
r"""
|
| 89 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 90 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
scheduler (`SchedulerMixin`):
|
| 94 |
+
The scheduler to get timesteps from.
|
| 95 |
+
num_inference_steps (`int`):
|
| 96 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 97 |
+
must be `None`.
|
| 98 |
+
device (`str` or `torch.device`, *optional*):
|
| 99 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 100 |
+
timesteps (`list[int]`, *optional*):
|
| 101 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 102 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 103 |
+
sigmas (`list[float]`, *optional*):
|
| 104 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 105 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 106 |
+
|
| 107 |
+
Returns:
|
| 108 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 109 |
+
second element is the number of inference steps.
|
| 110 |
+
"""
|
| 111 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 112 |
+
accepts_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 113 |
+
|
| 114 |
+
if timesteps is not None and sigmas is not None:
|
| 115 |
+
if not accepts_timesteps and not accepts_sigmas:
|
| 116 |
+
raise ValueError(
|
| 117 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 118 |
+
f" timestep or sigma schedules. Please check whether you are using the correct scheduler."
|
| 119 |
+
)
|
| 120 |
+
scheduler.set_timesteps(timesteps=timesteps, sigmas=sigmas, device=device, **kwargs)
|
| 121 |
+
timesteps = scheduler.timesteps
|
| 122 |
+
num_inference_steps = len(timesteps)
|
| 123 |
+
elif timesteps is not None and sigmas is None:
|
| 124 |
+
if not accepts_timesteps:
|
| 125 |
+
raise ValueError(
|
| 126 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 127 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 128 |
+
)
|
| 129 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 130 |
+
timesteps = scheduler.timesteps
|
| 131 |
+
num_inference_steps = len(timesteps)
|
| 132 |
+
elif timesteps is None and sigmas is not None:
|
| 133 |
+
if not accepts_sigmas:
|
| 134 |
+
raise ValueError(
|
| 135 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 136 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 137 |
+
)
|
| 138 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 139 |
+
timesteps = scheduler.timesteps
|
| 140 |
+
num_inference_steps = len(timesteps)
|
| 141 |
+
else:
|
| 142 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 143 |
+
timesteps = scheduler.timesteps
|
| 144 |
+
return timesteps, num_inference_steps
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
|
| 148 |
+
def retrieve_latents(
|
| 149 |
+
encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
|
| 150 |
+
):
|
| 151 |
+
if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
|
| 152 |
+
return encoder_output.latent_dist.sample(generator)
|
| 153 |
+
elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
|
| 154 |
+
return encoder_output.latent_dist.mode()
|
| 155 |
+
elif hasattr(encoder_output, "latents"):
|
| 156 |
+
return encoder_output.latents
|
| 157 |
+
else:
|
| 158 |
+
raise AttributeError("Could not access latents of provided encoder_output")
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
class GlmImagePipeline(DiffusionPipeline):
|
| 162 |
+
r"""
|
| 163 |
+
Pipeline for text-to-image generation using GLM-Image.
|
| 164 |
+
|
| 165 |
+
This pipeline integrates both the AR (autoregressive) model for token generation and the DiT (diffusion
|
| 166 |
+
transformer) model for image decoding.
|
| 167 |
+
|
| 168 |
+
Args:
|
| 169 |
+
tokenizer (`PreTrainedTokenizer`):
|
| 170 |
+
Tokenizer for the text encoder.
|
| 171 |
+
processor (`AutoProcessor`):
|
| 172 |
+
Processor for the AR model to handle chat templates and tokenization.
|
| 173 |
+
text_encoder ([`T5EncoderModel`]):
|
| 174 |
+
Frozen text-encoder for glyph embeddings.
|
| 175 |
+
vision_language_encoder ([`GlmImageForConditionalGeneration`]):
|
| 176 |
+
The AR model that generates image tokens from text prompts.
|
| 177 |
+
vae ([`AutoencoderKL`]):
|
| 178 |
+
Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
|
| 179 |
+
transformer ([`GlmImageTransformer2DModel`]):
|
| 180 |
+
A text conditioned transformer to denoise the encoded image latents (DiT).
|
| 181 |
+
scheduler ([`SchedulerMixin`]):
|
| 182 |
+
A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
|
| 183 |
+
"""
|
| 184 |
+
|
| 185 |
+
_optional_components = []
|
| 186 |
+
model_cpu_offload_seq = "vision_language_encoder->text_encoder->transformer->vae"
|
| 187 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds"]
|
| 188 |
+
|
| 189 |
+
def __init__(
|
| 190 |
+
self,
|
| 191 |
+
tokenizer: ByT5Tokenizer,
|
| 192 |
+
processor: GlmImageProcessor,
|
| 193 |
+
text_encoder: T5EncoderModel,
|
| 194 |
+
vision_language_encoder: GlmImageForConditionalGeneration,
|
| 195 |
+
vae: AutoencoderKL,
|
| 196 |
+
transformer: GlmImageTransformer2DModel,
|
| 197 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 198 |
+
):
|
| 199 |
+
super().__init__()
|
| 200 |
+
|
| 201 |
+
self.register_modules(
|
| 202 |
+
tokenizer=tokenizer,
|
| 203 |
+
processor=processor,
|
| 204 |
+
text_encoder=text_encoder,
|
| 205 |
+
vision_language_encoder=vision_language_encoder,
|
| 206 |
+
vae=vae,
|
| 207 |
+
transformer=transformer,
|
| 208 |
+
scheduler=scheduler,
|
| 209 |
+
)
|
| 210 |
+
self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
|
| 211 |
+
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
|
| 212 |
+
|
| 213 |
+
self.default_sample_size = (
|
| 214 |
+
self.transformer.config.sample_size
|
| 215 |
+
if hasattr(self, "transformer")
|
| 216 |
+
and self.transformer is not None
|
| 217 |
+
and hasattr(self.transformer.config, "sample_size")
|
| 218 |
+
else 128
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
@staticmethod
|
| 222 |
+
def _compute_generation_params(
|
| 223 |
+
image_grid_thw,
|
| 224 |
+
is_text_to_image: bool,
|
| 225 |
+
):
|
| 226 |
+
grid_sizes = []
|
| 227 |
+
grid_hw = []
|
| 228 |
+
|
| 229 |
+
for i in range(image_grid_thw.shape[0]):
|
| 230 |
+
t, h, w = image_grid_thw[i].tolist()
|
| 231 |
+
grid_sizes.append(int(h * w))
|
| 232 |
+
grid_hw.append((int(h), int(w)))
|
| 233 |
+
|
| 234 |
+
if not is_text_to_image:
|
| 235 |
+
max_new_tokens = grid_sizes[-1] + 1
|
| 236 |
+
large_image_start_offset = 0
|
| 237 |
+
target_grid_h, target_grid_w = grid_hw[-1]
|
| 238 |
+
else:
|
| 239 |
+
total_tokens = sum(grid_sizes)
|
| 240 |
+
max_new_tokens = total_tokens + 1
|
| 241 |
+
large_image_start_offset = sum(grid_sizes[1:])
|
| 242 |
+
target_grid_h, target_grid_w = grid_hw[0]
|
| 243 |
+
return max_new_tokens, large_image_start_offset, target_grid_h, target_grid_w
|
| 244 |
+
|
| 245 |
+
@staticmethod
|
| 246 |
+
def _extract_large_image_tokens(
|
| 247 |
+
outputs: torch.Tensor, input_length: int, large_image_start_offset: int, large_image_tokens: int
|
| 248 |
+
) -> torch.Tensor:
|
| 249 |
+
generated_tokens = outputs[0][input_length:]
|
| 250 |
+
large_image_start = large_image_start_offset
|
| 251 |
+
large_image_end = large_image_start + large_image_tokens
|
| 252 |
+
return generated_tokens[large_image_start:large_image_end]
|
| 253 |
+
|
| 254 |
+
@staticmethod
|
| 255 |
+
def _upsample_token_ids(token_ids: torch.Tensor, token_h: int, token_w: int) -> torch.Tensor:
|
| 256 |
+
token_ids = token_ids.view(1, 1, token_h, token_w)
|
| 257 |
+
token_ids = torch.nn.functional.interpolate(token_ids.float(), scale_factor=2, mode="nearest").to(
|
| 258 |
+
dtype=torch.long
|
| 259 |
+
)
|
| 260 |
+
token_ids = token_ids.view(1, -1)
|
| 261 |
+
return token_ids
|
| 262 |
+
|
| 263 |
+
@staticmethod
|
| 264 |
+
def _validate_and_normalize_images(
|
| 265 |
+
image: list[PIL.Image.Image] | list[list[PIL.Image.Image]],
|
| 266 |
+
batch_size: int,
|
| 267 |
+
) -> list[list[PIL.Image.Image]]:
|
| 268 |
+
"""
|
| 269 |
+
Validate and normalize image inputs to List[List[PIL.Image]].
|
| 270 |
+
|
| 271 |
+
Rules:
|
| 272 |
+
- batch_size > 1: Only accepts List[List[PIL.Image]], each sublist must have equal length
|
| 273 |
+
- batch_size == 1: Accepts List[PIL.Image] for legacy compatibility (converted to [[img1, img2, ...]])
|
| 274 |
+
- Other formats raise ValueError
|
| 275 |
+
|
| 276 |
+
Args:
|
| 277 |
+
image: Input images in various formats
|
| 278 |
+
batch_size: Number of prompts in the batch
|
| 279 |
+
|
| 280 |
+
Returns:
|
| 281 |
+
Normalized images as List[List[PIL.Image]], or None if no images provided
|
| 282 |
+
"""
|
| 283 |
+
if image is None or len(image) == 0:
|
| 284 |
+
return None
|
| 285 |
+
|
| 286 |
+
first_element = image[0]
|
| 287 |
+
|
| 288 |
+
if batch_size == 1:
|
| 289 |
+
# Legacy format: List[PIL.Image] -> [[img1, img2, ...]]
|
| 290 |
+
if not isinstance(first_element, (list, tuple)):
|
| 291 |
+
return [list(image)]
|
| 292 |
+
# Already in List[List[PIL.Image]] format
|
| 293 |
+
if len(image) != 1:
|
| 294 |
+
raise ValueError(
|
| 295 |
+
f"For batch_size=1 with List[List[PIL.Image]] format, expected 1 image list, got {len(image)}."
|
| 296 |
+
)
|
| 297 |
+
return [list(image[0])]
|
| 298 |
+
|
| 299 |
+
# batch_size > 1: must be List[List[PIL.Image]]
|
| 300 |
+
if not isinstance(first_element, (list, tuple)):
|
| 301 |
+
raise ValueError(
|
| 302 |
+
f"For batch_size > 1, images must be List[List[PIL.Image]] format. "
|
| 303 |
+
f"Got List[{type(first_element).__name__}] instead. "
|
| 304 |
+
f"Each prompt requires its own list of condition images."
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
if len(image) != batch_size:
|
| 308 |
+
raise ValueError(f"Number of image lists ({len(image)}) must match batch size ({batch_size}).")
|
| 309 |
+
|
| 310 |
+
# Validate homogeneous: all sublists must have same length
|
| 311 |
+
num_input_images_per_prompt = len(image[0])
|
| 312 |
+
for idx, imgs in enumerate(image):
|
| 313 |
+
if len(imgs) != num_input_images_per_prompt:
|
| 314 |
+
raise ValueError(
|
| 315 |
+
f"All prompts must have the same number of condition images. "
|
| 316 |
+
f"Prompt 0 has {num_input_images_per_prompt} images, but prompt {idx} has {len(imgs)} images."
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
return [list(imgs) for imgs in image]
|
| 320 |
+
|
| 321 |
+
def generate_prior_tokens(
|
| 322 |
+
self,
|
| 323 |
+
prompt: str | list[str],
|
| 324 |
+
height: int,
|
| 325 |
+
width: int,
|
| 326 |
+
image: list[list[PIL.Image.Image]] | None = None,
|
| 327 |
+
device: torch.device | None = None,
|
| 328 |
+
generator: torch.Generator | None = None,
|
| 329 |
+
):
|
| 330 |
+
"""
|
| 331 |
+
Generate prior tokens for the DiT model using the AR model.
|
| 332 |
+
|
| 333 |
+
Args:
|
| 334 |
+
prompt: Single prompt or list of prompts
|
| 335 |
+
height: Target image height
|
| 336 |
+
width: Target image width
|
| 337 |
+
image: Normalized image input as List[List[PIL.Image]]. Should be pre-validated
|
| 338 |
+
using _validate_and_normalize_images() before calling this method.
|
| 339 |
+
device: Target device
|
| 340 |
+
generator: Random generator for reproducibility
|
| 341 |
+
|
| 342 |
+
Returns:
|
| 343 |
+
Tuple of:
|
| 344 |
+
- prior_token_ids: Tensor of shape (batch_size, num_tokens) with upsampled prior tokens
|
| 345 |
+
- prior_token_image_ids_per_sample: List of tensors, one per sample. Each tensor contains
|
| 346 |
+
the upsampled prior token ids for all condition images in that sample. None for t2i.
|
| 347 |
+
- source_image_grid_thw_per_sample: List of tensors, one per sample. Each tensor has shape
|
| 348 |
+
(num_condition_images, 3) with upsampled grid info. None for t2i.
|
| 349 |
+
"""
|
| 350 |
+
device = device or self._execution_device
|
| 351 |
+
|
| 352 |
+
# Normalize prompt to list format
|
| 353 |
+
prompt_list = [prompt] if isinstance(prompt, str) else prompt
|
| 354 |
+
batch_size = len(prompt_list)
|
| 355 |
+
|
| 356 |
+
# Image is already normalized by _validate_and_normalize_images(): None or List[List[PIL.Image]]
|
| 357 |
+
is_text_to_image = image is None
|
| 358 |
+
# Build messages for each sample in the batch
|
| 359 |
+
all_messages = []
|
| 360 |
+
for idx, p in enumerate(prompt_list):
|
| 361 |
+
content = []
|
| 362 |
+
if not is_text_to_image:
|
| 363 |
+
for img in image[idx]:
|
| 364 |
+
content.append({"type": "image", "image": img})
|
| 365 |
+
content.append({"type": "text", "text": p})
|
| 366 |
+
all_messages.append([{"role": "user", "content": content}])
|
| 367 |
+
# Process with the processor (supports batch with left padding)
|
| 368 |
+
inputs = self.processor.apply_chat_template(
|
| 369 |
+
all_messages,
|
| 370 |
+
tokenize=True,
|
| 371 |
+
padding=True if batch_size > 1 else False,
|
| 372 |
+
target_h=height,
|
| 373 |
+
target_w=width,
|
| 374 |
+
return_dict=True,
|
| 375 |
+
return_tensors="pt",
|
| 376 |
+
).to(device)
|
| 377 |
+
|
| 378 |
+
image_grid_thw = inputs.get("image_grid_thw")
|
| 379 |
+
images_per_sample = inputs.get("images_per_sample")
|
| 380 |
+
|
| 381 |
+
# Determine number of condition images and grids per sample
|
| 382 |
+
num_condition_images = 0 if is_text_to_image else len(image[0])
|
| 383 |
+
if images_per_sample is not None:
|
| 384 |
+
num_grids_per_sample = images_per_sample[0].item()
|
| 385 |
+
else:
|
| 386 |
+
# Fallback for batch_size=1: total grids is for single sample
|
| 387 |
+
num_grids_per_sample = image_grid_thw.shape[0]
|
| 388 |
+
|
| 389 |
+
# Compute generation params (same for all samples in homogeneous batch)
|
| 390 |
+
first_sample_grids = image_grid_thw[:num_grids_per_sample]
|
| 391 |
+
max_new_tokens, large_image_offset, token_h, token_w = self._compute_generation_params(
|
| 392 |
+
image_grid_thw=first_sample_grids, is_text_to_image=is_text_to_image
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
# Generate source image tokens (prior_token_image_ids) for i2i mode
|
| 396 |
+
prior_token_image_ids = None
|
| 397 |
+
source_image_grid_thw = None
|
| 398 |
+
if not is_text_to_image:
|
| 399 |
+
# Extract source grids by selecting condition image indices (skip target grids)
|
| 400 |
+
# Grid order from processor: [s0_cond1, s0_cond2, ..., s0_target, s1_cond1, s1_cond2, ..., s1_target, ...]
|
| 401 |
+
# We need indices: [0, 1, ..., num_condition_images-1, num_grids_per_sample, num_grids_per_sample+1, ...]
|
| 402 |
+
source_indices = []
|
| 403 |
+
for sample_idx in range(batch_size):
|
| 404 |
+
base = sample_idx * num_grids_per_sample
|
| 405 |
+
source_indices.extend(range(base, base + num_condition_images))
|
| 406 |
+
source_grids = image_grid_thw[source_indices]
|
| 407 |
+
|
| 408 |
+
if len(source_grids) > 0:
|
| 409 |
+
prior_token_image_embed = self.vision_language_encoder.get_image_features(
|
| 410 |
+
inputs["pixel_values"], source_grids
|
| 411 |
+
).pooler_output
|
| 412 |
+
prior_token_image_embed = torch.cat(prior_token_image_embed, dim=0)
|
| 413 |
+
prior_token_image_ids_d32 = self.vision_language_encoder.get_image_tokens(
|
| 414 |
+
prior_token_image_embed, source_grids
|
| 415 |
+
)
|
| 416 |
+
# Upsample each source image's prior tokens to match VAE/DiT resolution
|
| 417 |
+
split_sizes = source_grids.prod(dim=-1).tolist()
|
| 418 |
+
prior_ids_per_source = torch.split(prior_token_image_ids_d32, split_sizes)
|
| 419 |
+
upsampled_prior_ids = []
|
| 420 |
+
for i, prior_ids in enumerate(prior_ids_per_source):
|
| 421 |
+
t, h, w = source_grids[i].tolist()
|
| 422 |
+
upsampled = self._upsample_token_ids(prior_ids, int(h), int(w))
|
| 423 |
+
upsampled_prior_ids.append(upsampled.squeeze(0))
|
| 424 |
+
prior_token_image_ids = torch.cat(upsampled_prior_ids, dim=0)
|
| 425 |
+
# Upsample grid dimensions for later splitting
|
| 426 |
+
upsampled_grids = source_grids.clone()
|
| 427 |
+
upsampled_grids[:, 1] = upsampled_grids[:, 1] * 2
|
| 428 |
+
upsampled_grids[:, 2] = upsampled_grids[:, 2] * 2
|
| 429 |
+
source_image_grid_thw = upsampled_grids
|
| 430 |
+
|
| 431 |
+
# Generate with AR model
|
| 432 |
+
# Set torch random seed from generator for reproducibility
|
| 433 |
+
# (transformers generate() doesn't accept generator parameter)
|
| 434 |
+
if generator is not None:
|
| 435 |
+
seed = generator.initial_seed()
|
| 436 |
+
torch.manual_seed(seed)
|
| 437 |
+
if device is not None and device.type == "cuda":
|
| 438 |
+
torch.cuda.manual_seed(seed)
|
| 439 |
+
outputs = self.vision_language_encoder.generate(
|
| 440 |
+
**inputs,
|
| 441 |
+
max_new_tokens=max_new_tokens,
|
| 442 |
+
do_sample=True,
|
| 443 |
+
)
|
| 444 |
+
|
| 445 |
+
# Extract and upsample prior tokens for each sample
|
| 446 |
+
# For left-padded inputs, generated tokens start after the padded input sequence
|
| 447 |
+
all_prior_token_ids = []
|
| 448 |
+
max_input_length = inputs["input_ids"].shape[-1]
|
| 449 |
+
for idx in range(batch_size):
|
| 450 |
+
# For left-padded sequences, generated tokens start at max_input_length
|
| 451 |
+
# (padding is on the left, so all sequences end at the same position)
|
| 452 |
+
prior_token_ids_d32 = self._extract_large_image_tokens(
|
| 453 |
+
outputs[idx : idx + 1], max_input_length, large_image_offset, token_h * token_w
|
| 454 |
+
)
|
| 455 |
+
prior_token_ids = self._upsample_token_ids(prior_token_ids_d32, token_h, token_w)
|
| 456 |
+
all_prior_token_ids.append(prior_token_ids)
|
| 457 |
+
prior_token_ids = torch.cat(all_prior_token_ids, dim=0)
|
| 458 |
+
|
| 459 |
+
# Split prior_token_image_ids and source_image_grid_thw into per-sample lists for easier consumption
|
| 460 |
+
prior_token_image_ids_per_sample = None
|
| 461 |
+
source_image_grid_thw_per_sample = None
|
| 462 |
+
if prior_token_image_ids is not None and source_image_grid_thw is not None:
|
| 463 |
+
# Split grids: each sample has num_condition_images grids
|
| 464 |
+
source_image_grid_thw_per_sample = list(torch.split(source_image_grid_thw, num_condition_images))
|
| 465 |
+
# Split prior_token_image_ids: tokens per sample may vary due to different image sizes
|
| 466 |
+
tokens_per_image = source_image_grid_thw.prod(dim=-1).tolist()
|
| 467 |
+
tokens_per_sample = []
|
| 468 |
+
for i in range(batch_size):
|
| 469 |
+
start_idx = i * num_condition_images
|
| 470 |
+
end_idx = start_idx + num_condition_images
|
| 471 |
+
tokens_per_sample.append(sum(tokens_per_image[start_idx:end_idx]))
|
| 472 |
+
prior_token_image_ids_per_sample = list(torch.split(prior_token_image_ids, tokens_per_sample))
|
| 473 |
+
|
| 474 |
+
return prior_token_ids, prior_token_image_ids_per_sample, source_image_grid_thw_per_sample
|
| 475 |
+
|
| 476 |
+
def get_glyph_texts(self, prompt):
|
| 477 |
+
"""Extract glyph texts from prompt(s). Returns a list of lists for batch processing."""
|
| 478 |
+
if isinstance(prompt, str):
|
| 479 |
+
prompt = [prompt]
|
| 480 |
+
all_ocr_texts = []
|
| 481 |
+
for p in prompt:
|
| 482 |
+
ocr_texts = (
|
| 483 |
+
re.findall(r"'([^']*)'", p)
|
| 484 |
+
+ re.findall(r"\u201c([^\u201c\u201d]*)\u201d", p)
|
| 485 |
+
+ re.findall(r'"([^"]*)"', p)
|
| 486 |
+
+ re.findall(r"「([^「」]*)」", p)
|
| 487 |
+
)
|
| 488 |
+
all_ocr_texts.append(ocr_texts)
|
| 489 |
+
return all_ocr_texts
|
| 490 |
+
|
| 491 |
+
def _get_glyph_embeds(
|
| 492 |
+
self,
|
| 493 |
+
prompt: str | list[str] = None,
|
| 494 |
+
max_sequence_length: int = 2048,
|
| 495 |
+
device: torch.device | None = None,
|
| 496 |
+
dtype: torch.dtype | None = None,
|
| 497 |
+
):
|
| 498 |
+
"""Get glyph embeddings for each prompt in the batch."""
|
| 499 |
+
device = device or self._execution_device
|
| 500 |
+
dtype = dtype or self.text_encoder.dtype
|
| 501 |
+
|
| 502 |
+
# get_glyph_texts now returns a list of lists (one per prompt)
|
| 503 |
+
all_glyph_texts = self.get_glyph_texts(prompt)
|
| 504 |
+
|
| 505 |
+
all_glyph_embeds = []
|
| 506 |
+
for glyph_texts in all_glyph_texts:
|
| 507 |
+
if len(glyph_texts) == 0:
|
| 508 |
+
glyph_texts = [""]
|
| 509 |
+
input_ids = self.tokenizer(
|
| 510 |
+
glyph_texts,
|
| 511 |
+
max_length=max_sequence_length,
|
| 512 |
+
truncation=True,
|
| 513 |
+
).input_ids
|
| 514 |
+
input_ids = [
|
| 515 |
+
[self.tokenizer.pad_token_id] * ((len(input_ids) + 1) % 2) + input_ids_ for input_ids_ in input_ids
|
| 516 |
+
]
|
| 517 |
+
max_length = max(len(input_ids_) for input_ids_ in input_ids)
|
| 518 |
+
attention_mask = torch.tensor(
|
| 519 |
+
[[1] * len(input_ids_) + [0] * (max_length - len(input_ids_)) for input_ids_ in input_ids],
|
| 520 |
+
device=device,
|
| 521 |
+
)
|
| 522 |
+
input_ids = torch.tensor(
|
| 523 |
+
[
|
| 524 |
+
input_ids_ + [self.tokenizer.pad_token_id] * (max_length - len(input_ids_))
|
| 525 |
+
for input_ids_ in input_ids
|
| 526 |
+
],
|
| 527 |
+
device=device,
|
| 528 |
+
)
|
| 529 |
+
outputs = self.text_encoder(input_ids, attention_mask=attention_mask)
|
| 530 |
+
glyph_embeds = outputs.last_hidden_state[attention_mask.bool()].unsqueeze(0)
|
| 531 |
+
all_glyph_embeds.append(glyph_embeds)
|
| 532 |
+
|
| 533 |
+
# Pad to same sequence length and stack (use left padding to match transformers)
|
| 534 |
+
max_seq_len = max(emb.size(1) for emb in all_glyph_embeds)
|
| 535 |
+
padded_embeds = []
|
| 536 |
+
for emb in all_glyph_embeds:
|
| 537 |
+
if emb.size(1) < max_seq_len:
|
| 538 |
+
pad = torch.zeros(emb.size(0), max_seq_len - emb.size(1), emb.size(2), device=device, dtype=emb.dtype)
|
| 539 |
+
emb = torch.cat([pad, emb], dim=1) # left padding
|
| 540 |
+
padded_embeds.append(emb)
|
| 541 |
+
|
| 542 |
+
glyph_embeds = torch.cat(padded_embeds, dim=0)
|
| 543 |
+
return glyph_embeds.to(device=device, dtype=dtype)
|
| 544 |
+
|
| 545 |
+
def encode_prompt(
|
| 546 |
+
self,
|
| 547 |
+
prompt: str | list[str],
|
| 548 |
+
do_classifier_free_guidance: bool = True,
|
| 549 |
+
num_images_per_prompt: int = 1,
|
| 550 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 551 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 552 |
+
device: torch.device | None = None,
|
| 553 |
+
dtype: torch.dtype | None = None,
|
| 554 |
+
max_sequence_length: int = 2048,
|
| 555 |
+
):
|
| 556 |
+
r"""
|
| 557 |
+
Encodes the prompt into text encoder hidden states.
|
| 558 |
+
|
| 559 |
+
Args:
|
| 560 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 561 |
+
prompt to be encoded
|
| 562 |
+
do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
|
| 563 |
+
Whether to use classifier free guidance or not.
|
| 564 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 565 |
+
Number of images that should be generated per prompt. torch device to place the resulting embeddings on
|
| 566 |
+
prompt_embeds (`torch.Tensor`, *optional*):
|
| 567 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 568 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 569 |
+
device: (`torch.device`, *optional*):
|
| 570 |
+
torch device
|
| 571 |
+
dtype: (`torch.dtype`, *optional*):
|
| 572 |
+
torch dtype
|
| 573 |
+
max_sequence_length (`int`, defaults to `2048`):
|
| 574 |
+
Maximum sequence length in encoded prompt. Can be set to other values but may lead to poorer results.
|
| 575 |
+
"""
|
| 576 |
+
device = device or self._execution_device
|
| 577 |
+
|
| 578 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 579 |
+
if prompt is not None:
|
| 580 |
+
batch_size = len(prompt)
|
| 581 |
+
else:
|
| 582 |
+
batch_size = prompt_embeds.shape[0]
|
| 583 |
+
|
| 584 |
+
if prompt_embeds is None:
|
| 585 |
+
prompt_embeds = self._get_glyph_embeds(prompt, max_sequence_length, device, dtype)
|
| 586 |
+
|
| 587 |
+
# Repeat embeddings for num_images_per_prompt
|
| 588 |
+
if num_images_per_prompt > 1:
|
| 589 |
+
prompt_embeds = prompt_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
| 590 |
+
|
| 591 |
+
# For GLM-Image, negative_prompt must be "" instead of None
|
| 592 |
+
if do_classifier_free_guidance and negative_prompt_embeds is None:
|
| 593 |
+
negative_prompt = ""
|
| 594 |
+
negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 595 |
+
negative_prompt_embeds = self._get_glyph_embeds(negative_prompt, max_sequence_length, device, dtype)
|
| 596 |
+
|
| 597 |
+
if num_images_per_prompt > 1:
|
| 598 |
+
negative_prompt_embeds = negative_prompt_embeds.repeat_interleave(num_images_per_prompt, dim=0)
|
| 599 |
+
|
| 600 |
+
return prompt_embeds, negative_prompt_embeds
|
| 601 |
+
|
| 602 |
+
def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
|
| 603 |
+
if latents is not None:
|
| 604 |
+
return latents.to(device)
|
| 605 |
+
|
| 606 |
+
shape = (
|
| 607 |
+
batch_size,
|
| 608 |
+
num_channels_latents,
|
| 609 |
+
int(height) // self.vae_scale_factor,
|
| 610 |
+
int(width) // self.vae_scale_factor,
|
| 611 |
+
)
|
| 612 |
+
if isinstance(generator, list) and len(generator) != batch_size:
|
| 613 |
+
raise ValueError(
|
| 614 |
+
f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
|
| 615 |
+
f" size of {batch_size}. Make sure the batch size matches the length of the generators."
|
| 616 |
+
)
|
| 617 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 618 |
+
return latents
|
| 619 |
+
|
| 620 |
+
def check_inputs(
|
| 621 |
+
self,
|
| 622 |
+
prompt,
|
| 623 |
+
height,
|
| 624 |
+
width,
|
| 625 |
+
callback_on_step_end_tensor_inputs,
|
| 626 |
+
prompt_embeds=None,
|
| 627 |
+
negative_prompt_embeds=None,
|
| 628 |
+
prior_token_ids=None,
|
| 629 |
+
prior_token_image_ids=None,
|
| 630 |
+
source_image_grid_thw=None,
|
| 631 |
+
image=None,
|
| 632 |
+
):
|
| 633 |
+
if (
|
| 634 |
+
height is not None
|
| 635 |
+
and height % (self.vae_scale_factor * self.transformer.config.patch_size * 2) != 0
|
| 636 |
+
or width is not None
|
| 637 |
+
and width % (self.transformer.config.patch_size * 2) != 0
|
| 638 |
+
):
|
| 639 |
+
# GLM-Image uses 32× downsampling, so the image dimensions must be multiples of 32.
|
| 640 |
+
raise ValueError(
|
| 641 |
+
f"`height` and `width` have to be divisible by {self.vae_scale_factor * 4} but are {height} and {width}."
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 645 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 646 |
+
):
|
| 647 |
+
raise ValueError(
|
| 648 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 649 |
+
)
|
| 650 |
+
if prompt is not None and prompt_embeds is not None:
|
| 651 |
+
raise ValueError(
|
| 652 |
+
f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
|
| 653 |
+
" only forward one of the two."
|
| 654 |
+
)
|
| 655 |
+
elif prompt is None and prompt_embeds is None:
|
| 656 |
+
raise ValueError(
|
| 657 |
+
"Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
|
| 658 |
+
)
|
| 659 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 660 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 661 |
+
if prompt is None and prior_token_ids is None:
|
| 662 |
+
raise ValueError(
|
| 663 |
+
"Provide either `prompt` or `prior_token_ids`. Cannot leave both `prompt` and `prior_token_ids` undefined."
|
| 664 |
+
)
|
| 665 |
+
if prompt_embeds is not None and negative_prompt_embeds is not None:
|
| 666 |
+
if prompt_embeds.shape != negative_prompt_embeds.shape:
|
| 667 |
+
raise ValueError(
|
| 668 |
+
"`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
|
| 669 |
+
f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
|
| 670 |
+
f" {negative_prompt_embeds.shape}."
|
| 671 |
+
)
|
| 672 |
+
# Validate prior token inputs: for i2i mode, all three must be provided together
|
| 673 |
+
# For t2i mode, only prior_token_ids is needed (prior_token_image_ids and source_image_grid_thw should be None)
|
| 674 |
+
prior_image_inputs = [prior_token_image_ids, source_image_grid_thw]
|
| 675 |
+
num_prior_image_inputs = sum(x is not None for x in prior_image_inputs)
|
| 676 |
+
if num_prior_image_inputs > 0 and num_prior_image_inputs < len(prior_image_inputs):
|
| 677 |
+
raise ValueError(
|
| 678 |
+
"`prior_token_image_ids` and `source_image_grid_thw` must be provided together for i2i mode. "
|
| 679 |
+
f"Got prior_token_image_ids={prior_token_image_ids is not None}, "
|
| 680 |
+
f"source_image_grid_thw={source_image_grid_thw is not None}."
|
| 681 |
+
)
|
| 682 |
+
if num_prior_image_inputs > 0 and prior_token_ids is None:
|
| 683 |
+
raise ValueError(
|
| 684 |
+
"`prior_token_ids` must be provided when `prior_token_image_ids` and `source_image_grid_thw` are provided."
|
| 685 |
+
)
|
| 686 |
+
if num_prior_image_inputs > 0 and image is None:
|
| 687 |
+
raise ValueError(
|
| 688 |
+
"`image` must be provided when `prior_token_image_ids` and `source_image_grid_thw` are provided "
|
| 689 |
+
"for i2i mode, as the images are needed for VAE encoding to build the KV cache."
|
| 690 |
+
)
|
| 691 |
+
|
| 692 |
+
if prior_token_ids is not None and prompt_embeds is None and prompt is None:
|
| 693 |
+
raise ValueError("`prompt_embeds` or `prompt` must also be provided with `prior_token_ids`.")
|
| 694 |
+
|
| 695 |
+
@property
|
| 696 |
+
def guidance_scale(self):
|
| 697 |
+
return self._guidance_scale
|
| 698 |
+
|
| 699 |
+
@property
|
| 700 |
+
def do_classifier_free_guidance(self):
|
| 701 |
+
return self._guidance_scale > 1
|
| 702 |
+
|
| 703 |
+
@property
|
| 704 |
+
def num_timesteps(self):
|
| 705 |
+
return self._num_timesteps
|
| 706 |
+
|
| 707 |
+
@property
|
| 708 |
+
def attention_kwargs(self):
|
| 709 |
+
return self._attention_kwargs
|
| 710 |
+
|
| 711 |
+
@property
|
| 712 |
+
def current_timestep(self):
|
| 713 |
+
return self._current_timestep
|
| 714 |
+
|
| 715 |
+
@property
|
| 716 |
+
def interrupt(self):
|
| 717 |
+
return self._interrupt
|
| 718 |
+
|
| 719 |
+
@torch.no_grad()
|
| 720 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 721 |
+
def __call__(
|
| 722 |
+
self,
|
| 723 |
+
prompt: str | list[str] | None = None,
|
| 724 |
+
image: torch.Tensor
|
| 725 |
+
| PIL.Image.Image
|
| 726 |
+
| np.ndarray
|
| 727 |
+
| list[torch.Tensor]
|
| 728 |
+
| list[PIL.Image.Image]
|
| 729 |
+
| list[np.ndarray]
|
| 730 |
+
| None = None,
|
| 731 |
+
height: int | None = None,
|
| 732 |
+
width: int | None = None,
|
| 733 |
+
num_inference_steps: int = 50,
|
| 734 |
+
timesteps: list[int] | None = None,
|
| 735 |
+
sigmas: list[float] | None = None,
|
| 736 |
+
guidance_scale: float = 1.5,
|
| 737 |
+
num_images_per_prompt: int = 1,
|
| 738 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 739 |
+
latents: torch.Tensor | None = None,
|
| 740 |
+
prompt_embeds: torch.Tensor | None = None,
|
| 741 |
+
negative_prompt_embeds: torch.Tensor | None = None,
|
| 742 |
+
prior_token_ids: torch.Tensor | None = None,
|
| 743 |
+
prior_token_image_ids: list[torch.Tensor] | None = None,
|
| 744 |
+
source_image_grid_thw: list[torch.Tensor] | None = None,
|
| 745 |
+
crops_coords_top_left: tuple[int, int] = (0, 0),
|
| 746 |
+
output_type: str = "pil",
|
| 747 |
+
return_dict: bool = True,
|
| 748 |
+
attention_kwargs: dict[str, Any] | None = None,
|
| 749 |
+
callback_on_step_end: Callable[[int, int, dict], None]
|
| 750 |
+
| PipelineCallback
|
| 751 |
+
| MultiPipelineCallbacks
|
| 752 |
+
| None = None,
|
| 753 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 754 |
+
max_sequence_length: int = 2048,
|
| 755 |
+
) -> GlmImagePipelineOutput | tuple:
|
| 756 |
+
"""
|
| 757 |
+
Function invoked when calling the pipeline for generation.
|
| 758 |
+
|
| 759 |
+
Args:
|
| 760 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 761 |
+
The prompt or prompts to guide the image generation. Must contain shape info in the format '<sop>H
|
| 762 |
+
W<eop>' where H and W are token dimensions (d32). Example: "A beautiful sunset<sop>36 24<eop>"
|
| 763 |
+
generates a 1152x768 image.
|
| 764 |
+
image: Optional condition images for image-to-image generation.
|
| 765 |
+
height (`int`, *optional*):
|
| 766 |
+
The height in pixels. If not provided, derived from prompt shape info.
|
| 767 |
+
width (`int`, *optional*):
|
| 768 |
+
The width in pixels. If not provided, derived from prompt shape info.
|
| 769 |
+
num_inference_steps (`int`, *optional*, defaults to `50`):
|
| 770 |
+
The number of denoising steps for DiT.
|
| 771 |
+
guidance_scale (`float`, *optional*, defaults to `1.5`):
|
| 772 |
+
Guidance scale for classifier-free guidance.
|
| 773 |
+
num_images_per_prompt (`int`, *optional*, defaults to `1`):
|
| 774 |
+
The number of images to generate per prompt.
|
| 775 |
+
generator (`torch.Generator`, *optional*):
|
| 776 |
+
Random generator for reproducibility.
|
| 777 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 778 |
+
Output format: "pil", "np", or "latent".
|
| 779 |
+
|
| 780 |
+
Examples:
|
| 781 |
+
|
| 782 |
+
Returns:
|
| 783 |
+
[`GlmImagePipelineOutput`] or `tuple`: Generated images.
|
| 784 |
+
"""
|
| 785 |
+
|
| 786 |
+
if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
|
| 787 |
+
callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
|
| 788 |
+
|
| 789 |
+
# 1. Check inputs
|
| 790 |
+
self.check_inputs(
|
| 791 |
+
prompt,
|
| 792 |
+
height,
|
| 793 |
+
width,
|
| 794 |
+
callback_on_step_end_tensor_inputs,
|
| 795 |
+
prompt_embeds,
|
| 796 |
+
negative_prompt_embeds,
|
| 797 |
+
prior_token_ids,
|
| 798 |
+
prior_token_image_ids,
|
| 799 |
+
source_image_grid_thw,
|
| 800 |
+
image,
|
| 801 |
+
)
|
| 802 |
+
self._guidance_scale = guidance_scale
|
| 803 |
+
self._attention_kwargs = attention_kwargs
|
| 804 |
+
self._current_timestep = None
|
| 805 |
+
self._interrupt = False
|
| 806 |
+
|
| 807 |
+
if prompt is not None and isinstance(prompt, str):
|
| 808 |
+
batch_size = 1
|
| 809 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 810 |
+
batch_size = len(prompt)
|
| 811 |
+
else:
|
| 812 |
+
batch_size = prompt_embeds.shape[0]
|
| 813 |
+
|
| 814 |
+
device = self._execution_device
|
| 815 |
+
|
| 816 |
+
# 2. Validate and normalize image format
|
| 817 |
+
normalized_image = self._validate_and_normalize_images(image, batch_size)
|
| 818 |
+
|
| 819 |
+
# 3. Generate prior tokens (batch mode)
|
| 820 |
+
# Get a single generator for AR model (use first if list provided)
|
| 821 |
+
ar_generator = generator[0] if isinstance(generator, list) else generator
|
| 822 |
+
if prior_token_ids is None:
|
| 823 |
+
prior_token_ids, prior_token_image_ids_per_sample, source_image_grid_thw_per_sample = (
|
| 824 |
+
self.generate_prior_tokens(
|
| 825 |
+
prompt=prompt,
|
| 826 |
+
image=normalized_image,
|
| 827 |
+
height=height,
|
| 828 |
+
width=width,
|
| 829 |
+
device=device,
|
| 830 |
+
generator=ar_generator,
|
| 831 |
+
)
|
| 832 |
+
)
|
| 833 |
+
else:
|
| 834 |
+
# User provided prior_token_ids directly (from generate_prior_tokens)
|
| 835 |
+
prior_token_image_ids_per_sample = prior_token_image_ids
|
| 836 |
+
source_image_grid_thw_per_sample = source_image_grid_thw
|
| 837 |
+
|
| 838 |
+
# 4. Preprocess images for VAE encoding
|
| 839 |
+
preprocessed_images = None
|
| 840 |
+
if normalized_image is not None:
|
| 841 |
+
preprocessed_images = []
|
| 842 |
+
for prompt_images in normalized_image:
|
| 843 |
+
prompt_preprocessed = []
|
| 844 |
+
for img in prompt_images:
|
| 845 |
+
image_height, image_width = img.size[::-1] if isinstance(img, PIL.Image.Image) else img.shape[:2]
|
| 846 |
+
multiple_of = self.vae_scale_factor * self.transformer.config.patch_size
|
| 847 |
+
image_height = (image_height // multiple_of) * multiple_of
|
| 848 |
+
image_width = (image_width // multiple_of) * multiple_of
|
| 849 |
+
img = self.image_processor.preprocess(img, height=image_height, width=image_width)
|
| 850 |
+
prompt_preprocessed.append(img)
|
| 851 |
+
height = height or image_height
|
| 852 |
+
width = width or image_width
|
| 853 |
+
preprocessed_images.append(prompt_preprocessed)
|
| 854 |
+
|
| 855 |
+
# 5. Encode input prompt
|
| 856 |
+
prompt_embeds, negative_prompt_embeds = self.encode_prompt(
|
| 857 |
+
prompt,
|
| 858 |
+
self.do_classifier_free_guidance,
|
| 859 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 860 |
+
prompt_embeds=prompt_embeds,
|
| 861 |
+
negative_prompt_embeds=negative_prompt_embeds,
|
| 862 |
+
max_sequence_length=max_sequence_length,
|
| 863 |
+
device=device,
|
| 864 |
+
dtype=self.dtype,
|
| 865 |
+
)
|
| 866 |
+
|
| 867 |
+
# 6. Prepare latents and (optional) image kv cache
|
| 868 |
+
latent_channels = self.transformer.config.in_channels
|
| 869 |
+
latents = self.prepare_latents(
|
| 870 |
+
batch_size=batch_size * num_images_per_prompt,
|
| 871 |
+
num_channels_latents=latent_channels,
|
| 872 |
+
height=height,
|
| 873 |
+
width=width,
|
| 874 |
+
dtype=prompt_embeds.dtype,
|
| 875 |
+
device=device,
|
| 876 |
+
generator=generator,
|
| 877 |
+
latents=latents,
|
| 878 |
+
)
|
| 879 |
+
kv_caches = GlmImageKVCache(num_layers=self.transformer.config.num_layers)
|
| 880 |
+
|
| 881 |
+
if normalized_image is not None:
|
| 882 |
+
kv_caches.set_mode("write")
|
| 883 |
+
latents_mean = torch.tensor(self.vae.config.latents_mean).view(1, self.vae.config.latent_channels, 1, 1)
|
| 884 |
+
latents_std = torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.latent_channels, 1, 1)
|
| 885 |
+
|
| 886 |
+
latents_mean = latents_mean.to(device=device, dtype=prompt_embeds.dtype)
|
| 887 |
+
latents_std = latents_std.to(device=device, dtype=prompt_embeds.dtype)
|
| 888 |
+
|
| 889 |
+
# Process each sample's condition images
|
| 890 |
+
for prompt_idx in range(batch_size):
|
| 891 |
+
prompt_images = preprocessed_images[prompt_idx]
|
| 892 |
+
prompt_prior_ids = prior_token_image_ids_per_sample[prompt_idx]
|
| 893 |
+
prompt_grid_thw = source_image_grid_thw_per_sample[prompt_idx]
|
| 894 |
+
|
| 895 |
+
# Split this sample's prior_token_image_ids by each image's token count
|
| 896 |
+
split_sizes = prompt_grid_thw.prod(dim=-1).tolist()
|
| 897 |
+
prior_ids_per_image = torch.split(prompt_prior_ids, split_sizes)
|
| 898 |
+
# Process each condition image for this sample
|
| 899 |
+
for condition_image, condition_image_prior_token_id in zip(prompt_images, prior_ids_per_image):
|
| 900 |
+
condition_image = condition_image.to(device=device, dtype=prompt_embeds.dtype)
|
| 901 |
+
condition_latent = retrieve_latents(
|
| 902 |
+
self.vae.encode(condition_image), generator=generator, sample_mode="argmax"
|
| 903 |
+
)
|
| 904 |
+
condition_latent = (condition_latent - latents_mean) / latents_std
|
| 905 |
+
|
| 906 |
+
_ = self.transformer(
|
| 907 |
+
hidden_states=condition_latent,
|
| 908 |
+
encoder_hidden_states=torch.zeros_like(prompt_embeds)[:1, :0, ...],
|
| 909 |
+
prior_token_id=condition_image_prior_token_id,
|
| 910 |
+
prior_token_drop=torch.full_like(condition_image_prior_token_id, False, dtype=torch.bool),
|
| 911 |
+
timestep=torch.zeros((1,), device=device),
|
| 912 |
+
target_size=torch.tensor([condition_image.shape[-2:]], device=device),
|
| 913 |
+
crop_coords=torch.zeros((1, 2), device=device),
|
| 914 |
+
attention_kwargs=attention_kwargs,
|
| 915 |
+
kv_caches=kv_caches,
|
| 916 |
+
)
|
| 917 |
+
# Move to next sample's cache slot
|
| 918 |
+
kv_caches.next_sample()
|
| 919 |
+
|
| 920 |
+
# 7. Prepare additional timestep conditions
|
| 921 |
+
target_size = (height, width)
|
| 922 |
+
target_size = torch.tensor([target_size], dtype=prompt_embeds.dtype, device=device)
|
| 923 |
+
crops_coords_top_left = torch.tensor([crops_coords_top_left], dtype=prompt_embeds.dtype, device=device)
|
| 924 |
+
|
| 925 |
+
target_size = target_size.repeat(batch_size * num_images_per_prompt, 1)
|
| 926 |
+
crops_coords_top_left = crops_coords_top_left.repeat(batch_size * num_images_per_prompt, 1)
|
| 927 |
+
|
| 928 |
+
# Prepare timesteps
|
| 929 |
+
image_seq_len = ((height // self.vae_scale_factor) * (width // self.vae_scale_factor)) // (
|
| 930 |
+
self.transformer.config.patch_size**2
|
| 931 |
+
)
|
| 932 |
+
timesteps = (
|
| 933 |
+
np.linspace(self.scheduler.config.num_train_timesteps, 1.0, num_inference_steps + 1)[:-1]
|
| 934 |
+
if timesteps is None
|
| 935 |
+
else np.array(timesteps)
|
| 936 |
+
)
|
| 937 |
+
timesteps = timesteps.astype(np.int64).astype(np.float32)
|
| 938 |
+
sigmas = timesteps / self.scheduler.config.num_train_timesteps if sigmas is None else sigmas
|
| 939 |
+
mu = calculate_shift(
|
| 940 |
+
image_seq_len,
|
| 941 |
+
self.scheduler.config.get("base_image_seq_len", 256),
|
| 942 |
+
self.scheduler.config.get("base_shift", 0.25),
|
| 943 |
+
self.scheduler.config.get("max_shift", 0.75),
|
| 944 |
+
)
|
| 945 |
+
timesteps, num_inference_steps = retrieve_timesteps(
|
| 946 |
+
self.scheduler, num_inference_steps, device, timesteps, sigmas, mu=mu
|
| 947 |
+
)
|
| 948 |
+
self._num_timesteps = len(timesteps)
|
| 949 |
+
|
| 950 |
+
# 8. Denoising loop
|
| 951 |
+
transformer_dtype = self.transformer.dtype
|
| 952 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 953 |
+
|
| 954 |
+
# Repeat prior_token_ids for num_images_per_prompt
|
| 955 |
+
if num_images_per_prompt > 1:
|
| 956 |
+
prior_token_ids = prior_token_ids.repeat_interleave(num_images_per_prompt, dim=0)
|
| 957 |
+
prior_token_drop_cond = torch.full_like(prior_token_ids, False, dtype=torch.bool)
|
| 958 |
+
prior_token_drop_uncond = torch.full_like(prior_token_ids, True, dtype=torch.bool)
|
| 959 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 960 |
+
for i, t in enumerate(timesteps):
|
| 961 |
+
if self.interrupt:
|
| 962 |
+
continue
|
| 963 |
+
|
| 964 |
+
self._current_timestep = t
|
| 965 |
+
latent_model_input = latents.to(transformer_dtype)
|
| 966 |
+
|
| 967 |
+
timestep = t.expand(latents.shape[0]) - 1
|
| 968 |
+
|
| 969 |
+
if prior_token_image_ids_per_sample is not None:
|
| 970 |
+
kv_caches.set_mode("read")
|
| 971 |
+
|
| 972 |
+
noise_pred_cond = self.transformer(
|
| 973 |
+
hidden_states=latent_model_input,
|
| 974 |
+
encoder_hidden_states=prompt_embeds,
|
| 975 |
+
prior_token_id=prior_token_ids,
|
| 976 |
+
prior_token_drop=prior_token_drop_cond,
|
| 977 |
+
timestep=timestep,
|
| 978 |
+
target_size=target_size,
|
| 979 |
+
crop_coords=crops_coords_top_left,
|
| 980 |
+
attention_kwargs=attention_kwargs,
|
| 981 |
+
return_dict=False,
|
| 982 |
+
kv_caches=kv_caches,
|
| 983 |
+
)[0].float()
|
| 984 |
+
|
| 985 |
+
# perform guidance
|
| 986 |
+
if self.do_classifier_free_guidance:
|
| 987 |
+
if prior_token_image_ids_per_sample is not None:
|
| 988 |
+
kv_caches.set_mode("skip")
|
| 989 |
+
noise_pred_uncond = self.transformer(
|
| 990 |
+
hidden_states=latent_model_input,
|
| 991 |
+
encoder_hidden_states=negative_prompt_embeds,
|
| 992 |
+
prior_token_id=prior_token_ids,
|
| 993 |
+
prior_token_drop=prior_token_drop_uncond,
|
| 994 |
+
timestep=timestep,
|
| 995 |
+
target_size=target_size,
|
| 996 |
+
crop_coords=crops_coords_top_left,
|
| 997 |
+
attention_kwargs=attention_kwargs,
|
| 998 |
+
return_dict=False,
|
| 999 |
+
kv_caches=kv_caches,
|
| 1000 |
+
)[0].float()
|
| 1001 |
+
|
| 1002 |
+
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
|
| 1003 |
+
else:
|
| 1004 |
+
noise_pred = noise_pred_cond
|
| 1005 |
+
|
| 1006 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 1007 |
+
|
| 1008 |
+
if callback_on_step_end is not None:
|
| 1009 |
+
callback_kwargs = {}
|
| 1010 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 1011 |
+
callback_kwargs[k] = locals()[k]
|
| 1012 |
+
callback_outputs = callback_on_step_end(self, i, self.scheduler.sigmas[i], callback_kwargs)
|
| 1013 |
+
latents = callback_outputs.pop("latents", latents)
|
| 1014 |
+
prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
|
| 1015 |
+
|
| 1016 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 1017 |
+
progress_bar.update()
|
| 1018 |
+
|
| 1019 |
+
if XLA_AVAILABLE:
|
| 1020 |
+
xm.mark_step()
|
| 1021 |
+
|
| 1022 |
+
self._current_timestep = None
|
| 1023 |
+
kv_caches.clear()
|
| 1024 |
+
|
| 1025 |
+
if not output_type == "latent":
|
| 1026 |
+
latents = latents.to(self.vae.dtype)
|
| 1027 |
+
latents_mean = (
|
| 1028 |
+
torch.tensor(self.vae.config.latents_mean)
|
| 1029 |
+
.view(1, self.vae.config.latent_channels, 1, 1)
|
| 1030 |
+
.to(latents.device, latents.dtype)
|
| 1031 |
+
)
|
| 1032 |
+
latents_std = (
|
| 1033 |
+
torch.tensor(self.vae.config.latents_std)
|
| 1034 |
+
.view(1, self.vae.config.latent_channels, 1, 1)
|
| 1035 |
+
.to(latents.device, latents.dtype)
|
| 1036 |
+
)
|
| 1037 |
+
latents = latents * latents_std + latents_mean
|
| 1038 |
+
image = self.vae.decode(latents, return_dict=False, generator=generator)[0]
|
| 1039 |
+
image = self.image_processor.postprocess(image, output_type=output_type)
|
| 1040 |
+
else:
|
| 1041 |
+
image = latents
|
| 1042 |
+
|
| 1043 |
+
# Offload all models
|
| 1044 |
+
self.maybe_free_model_hooks()
|
| 1045 |
+
|
| 1046 |
+
if not return_dict:
|
| 1047 |
+
return (image,)
|
| 1048 |
+
|
| 1049 |
+
return GlmImagePipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_output.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
|
| 3 |
+
import numpy as np
|
| 4 |
+
import PIL.Image
|
| 5 |
+
|
| 6 |
+
from ...utils import BaseOutput
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@dataclass
|
| 10 |
+
class GlmImagePipelineOutput(BaseOutput):
|
| 11 |
+
"""
|
| 12 |
+
Output class for CogView3 pipelines.
|
| 13 |
+
|
| 14 |
+
Args:
|
| 15 |
+
images (`List[PIL.Image.Image]` or `np.ndarray`)
|
| 16 |
+
List of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
|
| 17 |
+
num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
images: list[PIL.Image.Image] | np.ndarray
|
third_party/diffusers/src/diffusers/pipelines/hidream_image/__init__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ...utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_torch_available,
|
| 9 |
+
is_transformers_available,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_dummy_objects = {}
|
| 14 |
+
_additional_imports = {}
|
| 15 |
+
_import_structure = {"pipeline_output": ["HiDreamImagePipelineOutput"]}
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 19 |
+
raise OptionalDependencyNotAvailable()
|
| 20 |
+
except OptionalDependencyNotAvailable:
|
| 21 |
+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
| 22 |
+
|
| 23 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 24 |
+
else:
|
| 25 |
+
_import_structure["pipeline_hidream_image"] = ["HiDreamImagePipeline"]
|
| 26 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 27 |
+
try:
|
| 28 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 29 |
+
raise OptionalDependencyNotAvailable()
|
| 30 |
+
except OptionalDependencyNotAvailable:
|
| 31 |
+
from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
|
| 32 |
+
else:
|
| 33 |
+
from .pipeline_hidream_image import HiDreamImagePipeline
|
| 34 |
+
else:
|
| 35 |
+
import sys
|
| 36 |
+
|
| 37 |
+
sys.modules[__name__] = _LazyModule(
|
| 38 |
+
__name__,
|
| 39 |
+
globals()["__file__"],
|
| 40 |
+
_import_structure,
|
| 41 |
+
module_spec=__spec__,
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
for name, value in _dummy_objects.items():
|
| 45 |
+
setattr(sys.modules[__name__], name, value)
|
| 46 |
+
for name, value in _additional_imports.items():
|
| 47 |
+
setattr(sys.modules[__name__], name, value)
|
third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_hidream_image.py
ADDED
|
@@ -0,0 +1,1053 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 HiDream-ai Team and The HuggingFace Team. All rights reserved.
|
| 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 inspect
|
| 16 |
+
import math
|
| 17 |
+
from typing import Any, Callable
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
from transformers import (
|
| 21 |
+
CLIPTextModelWithProjection,
|
| 22 |
+
CLIPTokenizer,
|
| 23 |
+
LlamaForCausalLM,
|
| 24 |
+
PreTrainedTokenizerFast,
|
| 25 |
+
T5EncoderModel,
|
| 26 |
+
T5Tokenizer,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
from ...image_processor import VaeImageProcessor
|
| 30 |
+
from ...loaders import HiDreamImageLoraLoaderMixin
|
| 31 |
+
from ...models import AutoencoderKL, HiDreamImageTransformer2DModel
|
| 32 |
+
from ...schedulers import FlowMatchEulerDiscreteScheduler, UniPCMultistepScheduler
|
| 33 |
+
from ...utils import deprecate, is_torch_xla_available, logging, replace_example_docstring
|
| 34 |
+
from ...utils.torch_utils import randn_tensor
|
| 35 |
+
from ..pipeline_utils import DiffusionPipeline
|
| 36 |
+
from .pipeline_output import HiDreamImagePipelineOutput
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
if is_torch_xla_available():
|
| 40 |
+
import torch_xla.core.xla_model as xm
|
| 41 |
+
|
| 42 |
+
XLA_AVAILABLE = True
|
| 43 |
+
else:
|
| 44 |
+
XLA_AVAILABLE = False
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
logger = logging.get_logger(__name__) # pylint: disable=invalid-name
|
| 48 |
+
|
| 49 |
+
EXAMPLE_DOC_STRING = """
|
| 50 |
+
Examples:
|
| 51 |
+
```py
|
| 52 |
+
>>> import torch
|
| 53 |
+
>>> from transformers import AutoTokenizer, LlamaForCausalLM
|
| 54 |
+
>>> from diffusers import HiDreamImagePipeline
|
| 55 |
+
|
| 56 |
+
>>> tokenizer_4 = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3.1-8B-Instruct")
|
| 57 |
+
>>> text_encoder_4 = LlamaForCausalLM.from_pretrained(
|
| 58 |
+
... "meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 59 |
+
... output_hidden_states=True,
|
| 60 |
+
... output_attentions=True,
|
| 61 |
+
... torch_dtype=torch.bfloat16,
|
| 62 |
+
... )
|
| 63 |
+
|
| 64 |
+
>>> pipe = HiDreamImagePipeline.from_pretrained(
|
| 65 |
+
... "HiDream-ai/HiDream-I1-Full",
|
| 66 |
+
... tokenizer_4=tokenizer_4,
|
| 67 |
+
... text_encoder_4=text_encoder_4,
|
| 68 |
+
... torch_dtype=torch.bfloat16,
|
| 69 |
+
... )
|
| 70 |
+
>>> pipe.enable_model_cpu_offload()
|
| 71 |
+
|
| 72 |
+
>>> image = pipe(
|
| 73 |
+
... 'A cat holding a sign that says "Hi-Dreams.ai".',
|
| 74 |
+
... height=1024,
|
| 75 |
+
... width=1024,
|
| 76 |
+
... guidance_scale=5.0,
|
| 77 |
+
... num_inference_steps=50,
|
| 78 |
+
... generator=torch.Generator("cuda").manual_seed(0),
|
| 79 |
+
... ).images[0]
|
| 80 |
+
>>> image.save("output.png")
|
| 81 |
+
```
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
# Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
|
| 86 |
+
def calculate_shift(
|
| 87 |
+
image_seq_len,
|
| 88 |
+
base_seq_len: int = 256,
|
| 89 |
+
max_seq_len: int = 4096,
|
| 90 |
+
base_shift: float = 0.5,
|
| 91 |
+
max_shift: float = 1.15,
|
| 92 |
+
):
|
| 93 |
+
m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
|
| 94 |
+
b = base_shift - m * base_seq_len
|
| 95 |
+
mu = image_seq_len * m + b
|
| 96 |
+
return mu
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
|
| 100 |
+
def retrieve_timesteps(
|
| 101 |
+
scheduler,
|
| 102 |
+
num_inference_steps: int | None = None,
|
| 103 |
+
device: str | torch.device | None = None,
|
| 104 |
+
timesteps: list[int] | None = None,
|
| 105 |
+
sigmas: list[float] | None = None,
|
| 106 |
+
**kwargs,
|
| 107 |
+
):
|
| 108 |
+
r"""
|
| 109 |
+
Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
|
| 110 |
+
custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
scheduler (`SchedulerMixin`):
|
| 114 |
+
The scheduler to get timesteps from.
|
| 115 |
+
num_inference_steps (`int`):
|
| 116 |
+
The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
|
| 117 |
+
must be `None`.
|
| 118 |
+
device (`str` or `torch.device`, *optional*):
|
| 119 |
+
The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
|
| 120 |
+
timesteps (`list[int]`, *optional*):
|
| 121 |
+
Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
|
| 122 |
+
`num_inference_steps` and `sigmas` must be `None`.
|
| 123 |
+
sigmas (`list[float]`, *optional*):
|
| 124 |
+
Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
|
| 125 |
+
`num_inference_steps` and `timesteps` must be `None`.
|
| 126 |
+
|
| 127 |
+
Returns:
|
| 128 |
+
`tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
|
| 129 |
+
second element is the number of inference steps.
|
| 130 |
+
"""
|
| 131 |
+
if timesteps is not None and sigmas is not None:
|
| 132 |
+
raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
|
| 133 |
+
if timesteps is not None:
|
| 134 |
+
accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 135 |
+
if not accepts_timesteps:
|
| 136 |
+
raise ValueError(
|
| 137 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 138 |
+
f" timestep schedules. Please check whether you are using the correct scheduler."
|
| 139 |
+
)
|
| 140 |
+
scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
|
| 141 |
+
timesteps = scheduler.timesteps
|
| 142 |
+
num_inference_steps = len(timesteps)
|
| 143 |
+
elif sigmas is not None:
|
| 144 |
+
accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
|
| 145 |
+
if not accept_sigmas:
|
| 146 |
+
raise ValueError(
|
| 147 |
+
f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
|
| 148 |
+
f" sigmas schedules. Please check whether you are using the correct scheduler."
|
| 149 |
+
)
|
| 150 |
+
scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
|
| 151 |
+
timesteps = scheduler.timesteps
|
| 152 |
+
num_inference_steps = len(timesteps)
|
| 153 |
+
else:
|
| 154 |
+
scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
|
| 155 |
+
timesteps = scheduler.timesteps
|
| 156 |
+
return timesteps, num_inference_steps
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
class HiDreamImagePipeline(DiffusionPipeline, HiDreamImageLoraLoaderMixin):
|
| 160 |
+
model_cpu_offload_seq = "text_encoder->text_encoder_2->text_encoder_3->text_encoder_4->transformer->vae"
|
| 161 |
+
_callback_tensor_inputs = ["latents", "prompt_embeds_t5", "prompt_embeds_llama3", "pooled_prompt_embeds"]
|
| 162 |
+
|
| 163 |
+
def __init__(
|
| 164 |
+
self,
|
| 165 |
+
scheduler: FlowMatchEulerDiscreteScheduler,
|
| 166 |
+
vae: AutoencoderKL,
|
| 167 |
+
text_encoder: CLIPTextModelWithProjection,
|
| 168 |
+
tokenizer: CLIPTokenizer,
|
| 169 |
+
text_encoder_2: CLIPTextModelWithProjection,
|
| 170 |
+
tokenizer_2: CLIPTokenizer,
|
| 171 |
+
text_encoder_3: T5EncoderModel,
|
| 172 |
+
tokenizer_3: T5Tokenizer,
|
| 173 |
+
text_encoder_4: LlamaForCausalLM,
|
| 174 |
+
tokenizer_4: PreTrainedTokenizerFast,
|
| 175 |
+
transformer: HiDreamImageTransformer2DModel,
|
| 176 |
+
):
|
| 177 |
+
super().__init__()
|
| 178 |
+
|
| 179 |
+
self.register_modules(
|
| 180 |
+
vae=vae,
|
| 181 |
+
text_encoder=text_encoder,
|
| 182 |
+
text_encoder_2=text_encoder_2,
|
| 183 |
+
text_encoder_3=text_encoder_3,
|
| 184 |
+
text_encoder_4=text_encoder_4,
|
| 185 |
+
tokenizer=tokenizer,
|
| 186 |
+
tokenizer_2=tokenizer_2,
|
| 187 |
+
tokenizer_3=tokenizer_3,
|
| 188 |
+
tokenizer_4=tokenizer_4,
|
| 189 |
+
scheduler=scheduler,
|
| 190 |
+
transformer=transformer,
|
| 191 |
+
)
|
| 192 |
+
self.vae_scale_factor = (
|
| 193 |
+
2 ** (len(self.vae.config.block_out_channels) - 1) if hasattr(self, "vae") and self.vae is not None else 8
|
| 194 |
+
)
|
| 195 |
+
# HiDreamImage latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
|
| 196 |
+
# by the patch size. So the vae scale factor is multiplied by the patch size to account for this
|
| 197 |
+
self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
|
| 198 |
+
self.default_sample_size = 128
|
| 199 |
+
if getattr(self, "tokenizer_4", None) is not None:
|
| 200 |
+
self.tokenizer_4.pad_token = self.tokenizer_4.eos_token
|
| 201 |
+
|
| 202 |
+
def _get_t5_prompt_embeds(
|
| 203 |
+
self,
|
| 204 |
+
prompt: str | list[str] = None,
|
| 205 |
+
max_sequence_length: int = 128,
|
| 206 |
+
device: torch.device | None = None,
|
| 207 |
+
dtype: torch.dtype | None = None,
|
| 208 |
+
):
|
| 209 |
+
device = device or self._execution_device
|
| 210 |
+
dtype = dtype or self.text_encoder_3.dtype
|
| 211 |
+
|
| 212 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 213 |
+
|
| 214 |
+
text_inputs = self.tokenizer_3(
|
| 215 |
+
prompt,
|
| 216 |
+
padding="max_length",
|
| 217 |
+
max_length=min(max_sequence_length, self.tokenizer_3.model_max_length),
|
| 218 |
+
truncation=True,
|
| 219 |
+
add_special_tokens=True,
|
| 220 |
+
return_tensors="pt",
|
| 221 |
+
)
|
| 222 |
+
text_input_ids = text_inputs.input_ids
|
| 223 |
+
attention_mask = text_inputs.attention_mask
|
| 224 |
+
untruncated_ids = self.tokenizer_3(prompt, padding="longest", return_tensors="pt").input_ids
|
| 225 |
+
|
| 226 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 227 |
+
removed_text = self.tokenizer_3.batch_decode(
|
| 228 |
+
untruncated_ids[:, min(max_sequence_length, self.tokenizer_3.model_max_length) - 1 : -1]
|
| 229 |
+
)
|
| 230 |
+
logger.warning(
|
| 231 |
+
"The following part of your input was truncated because `max_sequence_length` is set to "
|
| 232 |
+
f" {min(max_sequence_length, self.tokenizer_3.model_max_length)} tokens: {removed_text}"
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
prompt_embeds = self.text_encoder_3(text_input_ids.to(device), attention_mask=attention_mask.to(device))[0]
|
| 236 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 237 |
+
return prompt_embeds
|
| 238 |
+
|
| 239 |
+
def _get_clip_prompt_embeds(
|
| 240 |
+
self,
|
| 241 |
+
tokenizer,
|
| 242 |
+
text_encoder,
|
| 243 |
+
prompt: str | list[str],
|
| 244 |
+
max_sequence_length: int = 128,
|
| 245 |
+
device: torch.device | None = None,
|
| 246 |
+
dtype: torch.dtype | None = None,
|
| 247 |
+
):
|
| 248 |
+
device = device or self._execution_device
|
| 249 |
+
dtype = dtype or text_encoder.dtype
|
| 250 |
+
|
| 251 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 252 |
+
|
| 253 |
+
text_inputs = tokenizer(
|
| 254 |
+
prompt,
|
| 255 |
+
padding="max_length",
|
| 256 |
+
max_length=min(max_sequence_length, 218),
|
| 257 |
+
truncation=True,
|
| 258 |
+
return_tensors="pt",
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
text_input_ids = text_inputs.input_ids
|
| 262 |
+
untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
|
| 263 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 264 |
+
removed_text = tokenizer.batch_decode(untruncated_ids[:, 218 - 1 : -1])
|
| 265 |
+
logger.warning(
|
| 266 |
+
"The following part of your input was truncated because CLIP can only handle sequences up to"
|
| 267 |
+
f" {218} tokens: {removed_text}"
|
| 268 |
+
)
|
| 269 |
+
prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
|
| 270 |
+
|
| 271 |
+
# Use pooled output of CLIPTextModel
|
| 272 |
+
prompt_embeds = prompt_embeds[0]
|
| 273 |
+
prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
|
| 274 |
+
return prompt_embeds
|
| 275 |
+
|
| 276 |
+
def _get_llama3_prompt_embeds(
|
| 277 |
+
self,
|
| 278 |
+
prompt: str | list[str] = None,
|
| 279 |
+
max_sequence_length: int = 128,
|
| 280 |
+
device: torch.device | None = None,
|
| 281 |
+
dtype: torch.dtype | None = None,
|
| 282 |
+
):
|
| 283 |
+
device = device or self._execution_device
|
| 284 |
+
dtype = dtype or self.text_encoder_4.dtype
|
| 285 |
+
|
| 286 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 287 |
+
|
| 288 |
+
text_inputs = self.tokenizer_4(
|
| 289 |
+
prompt,
|
| 290 |
+
padding="max_length",
|
| 291 |
+
max_length=min(max_sequence_length, self.tokenizer_4.model_max_length),
|
| 292 |
+
truncation=True,
|
| 293 |
+
add_special_tokens=True,
|
| 294 |
+
return_tensors="pt",
|
| 295 |
+
)
|
| 296 |
+
text_input_ids = text_inputs.input_ids
|
| 297 |
+
attention_mask = text_inputs.attention_mask
|
| 298 |
+
untruncated_ids = self.tokenizer_4(prompt, padding="longest", return_tensors="pt").input_ids
|
| 299 |
+
|
| 300 |
+
if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
|
| 301 |
+
removed_text = self.tokenizer_4.batch_decode(
|
| 302 |
+
untruncated_ids[:, min(max_sequence_length, self.tokenizer_4.model_max_length) - 1 : -1]
|
| 303 |
+
)
|
| 304 |
+
logger.warning(
|
| 305 |
+
"The following part of your input was truncated because `max_sequence_length` is set to "
|
| 306 |
+
f" {min(max_sequence_length, self.tokenizer_4.model_max_length)} tokens: {removed_text}"
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
outputs = self.text_encoder_4(
|
| 310 |
+
text_input_ids.to(device),
|
| 311 |
+
attention_mask=attention_mask.to(device),
|
| 312 |
+
output_hidden_states=True,
|
| 313 |
+
output_attentions=True,
|
| 314 |
+
)
|
| 315 |
+
|
| 316 |
+
prompt_embeds = outputs.hidden_states[1:]
|
| 317 |
+
prompt_embeds = torch.stack(prompt_embeds, dim=0)
|
| 318 |
+
return prompt_embeds
|
| 319 |
+
|
| 320 |
+
def encode_prompt(
|
| 321 |
+
self,
|
| 322 |
+
prompt: str | list[str] | None = None,
|
| 323 |
+
prompt_2: str | list[str] | None = None,
|
| 324 |
+
prompt_3: str | list[str] | None = None,
|
| 325 |
+
prompt_4: str | list[str] | None = None,
|
| 326 |
+
device: torch.device | None = None,
|
| 327 |
+
dtype: torch.dtype | None = None,
|
| 328 |
+
num_images_per_prompt: int = 1,
|
| 329 |
+
do_classifier_free_guidance: bool = True,
|
| 330 |
+
negative_prompt: str | list[str] | None = None,
|
| 331 |
+
negative_prompt_2: str | list[str] | None = None,
|
| 332 |
+
negative_prompt_3: str | list[str] | None = None,
|
| 333 |
+
negative_prompt_4: str | list[str] | None = None,
|
| 334 |
+
prompt_embeds_t5: list[torch.FloatTensor] | None = None,
|
| 335 |
+
prompt_embeds_llama3: list[torch.FloatTensor] | None = None,
|
| 336 |
+
negative_prompt_embeds_t5: list[torch.FloatTensor] | None = None,
|
| 337 |
+
negative_prompt_embeds_llama3: list[torch.FloatTensor] | None = None,
|
| 338 |
+
pooled_prompt_embeds: torch.FloatTensor | None = None,
|
| 339 |
+
negative_pooled_prompt_embeds: torch.FloatTensor | None = None,
|
| 340 |
+
max_sequence_length: int = 128,
|
| 341 |
+
lora_scale: float | None = None,
|
| 342 |
+
):
|
| 343 |
+
prompt = [prompt] if isinstance(prompt, str) else prompt
|
| 344 |
+
if prompt is not None:
|
| 345 |
+
batch_size = len(prompt)
|
| 346 |
+
else:
|
| 347 |
+
batch_size = pooled_prompt_embeds.shape[0]
|
| 348 |
+
|
| 349 |
+
device = device or self._execution_device
|
| 350 |
+
|
| 351 |
+
if pooled_prompt_embeds is None:
|
| 352 |
+
pooled_prompt_embeds_1 = self._get_clip_prompt_embeds(
|
| 353 |
+
self.tokenizer, self.text_encoder, prompt, max_sequence_length, device, dtype
|
| 354 |
+
)
|
| 355 |
+
|
| 356 |
+
if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
|
| 357 |
+
negative_prompt = negative_prompt or ""
|
| 358 |
+
negative_prompt = [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
|
| 359 |
+
|
| 360 |
+
if len(negative_prompt) > 1 and len(negative_prompt) != batch_size:
|
| 361 |
+
raise ValueError(f"negative_prompt must be of length 1 or {batch_size}")
|
| 362 |
+
|
| 363 |
+
negative_pooled_prompt_embeds_1 = self._get_clip_prompt_embeds(
|
| 364 |
+
self.tokenizer, self.text_encoder, negative_prompt, max_sequence_length, device, dtype
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
if negative_pooled_prompt_embeds_1.shape[0] == 1 and batch_size > 1:
|
| 368 |
+
negative_pooled_prompt_embeds_1 = negative_pooled_prompt_embeds_1.repeat(batch_size, 1)
|
| 369 |
+
|
| 370 |
+
if pooled_prompt_embeds is None:
|
| 371 |
+
prompt_2 = prompt_2 or prompt
|
| 372 |
+
prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
|
| 373 |
+
|
| 374 |
+
if len(prompt_2) > 1 and len(prompt_2) != batch_size:
|
| 375 |
+
raise ValueError(f"prompt_2 must be of length 1 or {batch_size}")
|
| 376 |
+
|
| 377 |
+
pooled_prompt_embeds_2 = self._get_clip_prompt_embeds(
|
| 378 |
+
self.tokenizer_2, self.text_encoder_2, prompt_2, max_sequence_length, device, dtype
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
if pooled_prompt_embeds_2.shape[0] == 1 and batch_size > 1:
|
| 382 |
+
pooled_prompt_embeds_2 = pooled_prompt_embeds_2.repeat(batch_size, 1)
|
| 383 |
+
|
| 384 |
+
if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
|
| 385 |
+
negative_prompt_2 = negative_prompt_2 or negative_prompt
|
| 386 |
+
negative_prompt_2 = [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
|
| 387 |
+
|
| 388 |
+
if len(negative_prompt_2) > 1 and len(negative_prompt_2) != batch_size:
|
| 389 |
+
raise ValueError(f"negative_prompt_2 must be of length 1 or {batch_size}")
|
| 390 |
+
|
| 391 |
+
negative_pooled_prompt_embeds_2 = self._get_clip_prompt_embeds(
|
| 392 |
+
self.tokenizer_2, self.text_encoder_2, negative_prompt_2, max_sequence_length, device, dtype
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
if negative_pooled_prompt_embeds_2.shape[0] == 1 and batch_size > 1:
|
| 396 |
+
negative_pooled_prompt_embeds_2 = negative_pooled_prompt_embeds_2.repeat(batch_size, 1)
|
| 397 |
+
|
| 398 |
+
if pooled_prompt_embeds is None:
|
| 399 |
+
pooled_prompt_embeds = torch.cat([pooled_prompt_embeds_1, pooled_prompt_embeds_2], dim=-1)
|
| 400 |
+
|
| 401 |
+
if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
|
| 402 |
+
negative_pooled_prompt_embeds = torch.cat(
|
| 403 |
+
[negative_pooled_prompt_embeds_1, negative_pooled_prompt_embeds_2], dim=-1
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
if prompt_embeds_t5 is None:
|
| 407 |
+
prompt_3 = prompt_3 or prompt
|
| 408 |
+
prompt_3 = [prompt_3] if isinstance(prompt_3, str) else prompt_3
|
| 409 |
+
|
| 410 |
+
if len(prompt_3) > 1 and len(prompt_3) != batch_size:
|
| 411 |
+
raise ValueError(f"prompt_3 must be of length 1 or {batch_size}")
|
| 412 |
+
|
| 413 |
+
prompt_embeds_t5 = self._get_t5_prompt_embeds(prompt_3, max_sequence_length, device, dtype)
|
| 414 |
+
|
| 415 |
+
if prompt_embeds_t5.shape[0] == 1 and batch_size > 1:
|
| 416 |
+
prompt_embeds_t5 = prompt_embeds_t5.repeat(batch_size, 1, 1)
|
| 417 |
+
|
| 418 |
+
if do_classifier_free_guidance and negative_prompt_embeds_t5 is None:
|
| 419 |
+
negative_prompt_3 = negative_prompt_3 or negative_prompt
|
| 420 |
+
negative_prompt_3 = [negative_prompt_3] if isinstance(negative_prompt_3, str) else negative_prompt_3
|
| 421 |
+
|
| 422 |
+
if len(negative_prompt_3) > 1 and len(negative_prompt_3) != batch_size:
|
| 423 |
+
raise ValueError(f"negative_prompt_3 must be of length 1 or {batch_size}")
|
| 424 |
+
|
| 425 |
+
negative_prompt_embeds_t5 = self._get_t5_prompt_embeds(
|
| 426 |
+
negative_prompt_3, max_sequence_length, device, dtype
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
if negative_prompt_embeds_t5.shape[0] == 1 and batch_size > 1:
|
| 430 |
+
negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(batch_size, 1, 1)
|
| 431 |
+
|
| 432 |
+
if prompt_embeds_llama3 is None:
|
| 433 |
+
prompt_4 = prompt_4 or prompt
|
| 434 |
+
prompt_4 = [prompt_4] if isinstance(prompt_4, str) else prompt_4
|
| 435 |
+
|
| 436 |
+
if len(prompt_4) > 1 and len(prompt_4) != batch_size:
|
| 437 |
+
raise ValueError(f"prompt_4 must be of length 1 or {batch_size}")
|
| 438 |
+
|
| 439 |
+
prompt_embeds_llama3 = self._get_llama3_prompt_embeds(prompt_4, max_sequence_length, device, dtype)
|
| 440 |
+
|
| 441 |
+
if prompt_embeds_llama3.shape[0] == 1 and batch_size > 1:
|
| 442 |
+
prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
|
| 443 |
+
|
| 444 |
+
if do_classifier_free_guidance and negative_prompt_embeds_llama3 is None:
|
| 445 |
+
negative_prompt_4 = negative_prompt_4 or negative_prompt
|
| 446 |
+
negative_prompt_4 = [negative_prompt_4] if isinstance(negative_prompt_4, str) else negative_prompt_4
|
| 447 |
+
|
| 448 |
+
if len(negative_prompt_4) > 1 and len(negative_prompt_4) != batch_size:
|
| 449 |
+
raise ValueError(f"negative_prompt_4 must be of length 1 or {batch_size}")
|
| 450 |
+
|
| 451 |
+
negative_prompt_embeds_llama3 = self._get_llama3_prompt_embeds(
|
| 452 |
+
negative_prompt_4, max_sequence_length, device, dtype
|
| 453 |
+
)
|
| 454 |
+
|
| 455 |
+
if negative_prompt_embeds_llama3.shape[0] == 1 and batch_size > 1:
|
| 456 |
+
negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
|
| 457 |
+
|
| 458 |
+
# duplicate pooled_prompt_embeds for each generation per prompt
|
| 459 |
+
pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt)
|
| 460 |
+
pooled_prompt_embeds = pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
|
| 461 |
+
|
| 462 |
+
# duplicate t5_prompt_embeds for batch_size and num_images_per_prompt
|
| 463 |
+
bs_embed, seq_len, _ = prompt_embeds_t5.shape
|
| 464 |
+
if bs_embed == 1 and batch_size > 1:
|
| 465 |
+
prompt_embeds_t5 = prompt_embeds_t5.repeat(batch_size, 1, 1)
|
| 466 |
+
elif bs_embed > 1 and bs_embed != batch_size:
|
| 467 |
+
raise ValueError(f"cannot duplicate prompt_embeds_t5 of batch size {bs_embed}")
|
| 468 |
+
prompt_embeds_t5 = prompt_embeds_t5.repeat(1, num_images_per_prompt, 1)
|
| 469 |
+
prompt_embeds_t5 = prompt_embeds_t5.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 470 |
+
|
| 471 |
+
# duplicate llama3_prompt_embeds for batch_size and num_images_per_prompt
|
| 472 |
+
_, bs_embed, seq_len, dim = prompt_embeds_llama3.shape
|
| 473 |
+
if bs_embed == 1 and batch_size > 1:
|
| 474 |
+
prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
|
| 475 |
+
elif bs_embed > 1 and bs_embed != batch_size:
|
| 476 |
+
raise ValueError(f"cannot duplicate prompt_embeds_llama3 of batch size {bs_embed}")
|
| 477 |
+
prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, 1, num_images_per_prompt, 1)
|
| 478 |
+
prompt_embeds_llama3 = prompt_embeds_llama3.view(-1, batch_size * num_images_per_prompt, seq_len, dim)
|
| 479 |
+
|
| 480 |
+
if do_classifier_free_guidance:
|
| 481 |
+
# duplicate negative_pooled_prompt_embeds for batch_size and num_images_per_prompt
|
| 482 |
+
bs_embed, seq_len = negative_pooled_prompt_embeds.shape
|
| 483 |
+
if bs_embed == 1 and batch_size > 1:
|
| 484 |
+
negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(batch_size, 1)
|
| 485 |
+
elif bs_embed > 1 and bs_embed != batch_size:
|
| 486 |
+
raise ValueError(f"cannot duplicate negative_pooled_prompt_embeds of batch size {bs_embed}")
|
| 487 |
+
negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(1, num_images_per_prompt)
|
| 488 |
+
negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
|
| 489 |
+
|
| 490 |
+
# duplicate negative_t5_prompt_embeds for batch_size and num_images_per_prompt
|
| 491 |
+
bs_embed, seq_len, _ = negative_prompt_embeds_t5.shape
|
| 492 |
+
if bs_embed == 1 and batch_size > 1:
|
| 493 |
+
negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(batch_size, 1, 1)
|
| 494 |
+
elif bs_embed > 1 and bs_embed != batch_size:
|
| 495 |
+
raise ValueError(f"cannot duplicate negative_prompt_embeds_t5 of batch size {bs_embed}")
|
| 496 |
+
negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(1, num_images_per_prompt, 1)
|
| 497 |
+
negative_prompt_embeds_t5 = negative_prompt_embeds_t5.view(batch_size * num_images_per_prompt, seq_len, -1)
|
| 498 |
+
|
| 499 |
+
# duplicate negative_prompt_embeds_llama3 for batch_size and num_images_per_prompt
|
| 500 |
+
_, bs_embed, seq_len, dim = negative_prompt_embeds_llama3.shape
|
| 501 |
+
if bs_embed == 1 and batch_size > 1:
|
| 502 |
+
negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
|
| 503 |
+
elif bs_embed > 1 and bs_embed != batch_size:
|
| 504 |
+
raise ValueError(f"cannot duplicate negative_prompt_embeds_llama3 of batch size {bs_embed}")
|
| 505 |
+
negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, 1, num_images_per_prompt, 1)
|
| 506 |
+
negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.view(
|
| 507 |
+
-1, batch_size * num_images_per_prompt, seq_len, dim
|
| 508 |
+
)
|
| 509 |
+
|
| 510 |
+
return (
|
| 511 |
+
prompt_embeds_t5,
|
| 512 |
+
negative_prompt_embeds_t5,
|
| 513 |
+
prompt_embeds_llama3,
|
| 514 |
+
negative_prompt_embeds_llama3,
|
| 515 |
+
pooled_prompt_embeds,
|
| 516 |
+
negative_pooled_prompt_embeds,
|
| 517 |
+
)
|
| 518 |
+
|
| 519 |
+
def enable_vae_slicing(self):
|
| 520 |
+
r"""
|
| 521 |
+
Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
|
| 522 |
+
compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
|
| 523 |
+
"""
|
| 524 |
+
depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
|
| 525 |
+
deprecate(
|
| 526 |
+
"enable_vae_slicing",
|
| 527 |
+
"0.40.0",
|
| 528 |
+
depr_message,
|
| 529 |
+
)
|
| 530 |
+
self.vae.enable_slicing()
|
| 531 |
+
|
| 532 |
+
def disable_vae_slicing(self):
|
| 533 |
+
r"""
|
| 534 |
+
Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
|
| 535 |
+
computing decoding in one step.
|
| 536 |
+
"""
|
| 537 |
+
depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
|
| 538 |
+
deprecate(
|
| 539 |
+
"disable_vae_slicing",
|
| 540 |
+
"0.40.0",
|
| 541 |
+
depr_message,
|
| 542 |
+
)
|
| 543 |
+
self.vae.disable_slicing()
|
| 544 |
+
|
| 545 |
+
def enable_vae_tiling(self):
|
| 546 |
+
r"""
|
| 547 |
+
Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
|
| 548 |
+
compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
|
| 549 |
+
processing larger images.
|
| 550 |
+
"""
|
| 551 |
+
depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
|
| 552 |
+
deprecate(
|
| 553 |
+
"enable_vae_tiling",
|
| 554 |
+
"0.40.0",
|
| 555 |
+
depr_message,
|
| 556 |
+
)
|
| 557 |
+
self.vae.enable_tiling()
|
| 558 |
+
|
| 559 |
+
def disable_vae_tiling(self):
|
| 560 |
+
r"""
|
| 561 |
+
Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
|
| 562 |
+
computing decoding in one step.
|
| 563 |
+
"""
|
| 564 |
+
depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
|
| 565 |
+
deprecate(
|
| 566 |
+
"disable_vae_tiling",
|
| 567 |
+
"0.40.0",
|
| 568 |
+
depr_message,
|
| 569 |
+
)
|
| 570 |
+
self.vae.disable_tiling()
|
| 571 |
+
|
| 572 |
+
def check_inputs(
|
| 573 |
+
self,
|
| 574 |
+
prompt,
|
| 575 |
+
prompt_2,
|
| 576 |
+
prompt_3,
|
| 577 |
+
prompt_4,
|
| 578 |
+
negative_prompt=None,
|
| 579 |
+
negative_prompt_2=None,
|
| 580 |
+
negative_prompt_3=None,
|
| 581 |
+
negative_prompt_4=None,
|
| 582 |
+
prompt_embeds_t5=None,
|
| 583 |
+
prompt_embeds_llama3=None,
|
| 584 |
+
negative_prompt_embeds_t5=None,
|
| 585 |
+
negative_prompt_embeds_llama3=None,
|
| 586 |
+
pooled_prompt_embeds=None,
|
| 587 |
+
negative_pooled_prompt_embeds=None,
|
| 588 |
+
callback_on_step_end_tensor_inputs=None,
|
| 589 |
+
):
|
| 590 |
+
if callback_on_step_end_tensor_inputs is not None and not all(
|
| 591 |
+
k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
|
| 592 |
+
):
|
| 593 |
+
raise ValueError(
|
| 594 |
+
f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
|
| 595 |
+
)
|
| 596 |
+
|
| 597 |
+
if prompt is not None and pooled_prompt_embeds is not None:
|
| 598 |
+
raise ValueError(
|
| 599 |
+
f"Cannot forward both `prompt`: {prompt} and `pooled_prompt_embeds`: {pooled_prompt_embeds}. Please make sure to"
|
| 600 |
+
" only forward one of the two."
|
| 601 |
+
)
|
| 602 |
+
elif prompt_2 is not None and pooled_prompt_embeds is not None:
|
| 603 |
+
raise ValueError(
|
| 604 |
+
f"Cannot forward both `prompt_2`: {prompt_2} and `pooled_prompt_embeds`: {pooled_prompt_embeds}. Please make sure to"
|
| 605 |
+
" only forward one of the two."
|
| 606 |
+
)
|
| 607 |
+
elif prompt_3 is not None and prompt_embeds_t5 is not None:
|
| 608 |
+
raise ValueError(
|
| 609 |
+
f"Cannot forward both `prompt_3`: {prompt_3} and `prompt_embeds_t5`: {prompt_embeds_t5}. Please make sure to"
|
| 610 |
+
" only forward one of the two."
|
| 611 |
+
)
|
| 612 |
+
elif prompt_4 is not None and prompt_embeds_llama3 is not None:
|
| 613 |
+
raise ValueError(
|
| 614 |
+
f"Cannot forward both `prompt_4`: {prompt_4} and `prompt_embeds_llama3`: {prompt_embeds_llama3}. Please make sure to"
|
| 615 |
+
" only forward one of the two."
|
| 616 |
+
)
|
| 617 |
+
elif prompt is None and pooled_prompt_embeds is None:
|
| 618 |
+
raise ValueError(
|
| 619 |
+
"Provide either `prompt` or `pooled_prompt_embeds`. Cannot leave both `prompt` and `pooled_prompt_embeds` undefined."
|
| 620 |
+
)
|
| 621 |
+
elif prompt is None and prompt_embeds_t5 is None:
|
| 622 |
+
raise ValueError(
|
| 623 |
+
"Provide either `prompt` or `prompt_embeds_t5`. Cannot leave both `prompt` and `prompt_embeds_t5` undefined."
|
| 624 |
+
)
|
| 625 |
+
elif prompt is None and prompt_embeds_llama3 is None:
|
| 626 |
+
raise ValueError(
|
| 627 |
+
"Provide either `prompt` or `prompt_embeds_llama3`. Cannot leave both `prompt` and `prompt_embeds_llama3` undefined."
|
| 628 |
+
)
|
| 629 |
+
elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
|
| 630 |
+
raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
|
| 631 |
+
elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
|
| 632 |
+
raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
|
| 633 |
+
elif prompt_3 is not None and (not isinstance(prompt_3, str) and not isinstance(prompt_3, list)):
|
| 634 |
+
raise ValueError(f"`prompt_3` has to be of type `str` or `list` but is {type(prompt_3)}")
|
| 635 |
+
elif prompt_4 is not None and (not isinstance(prompt_4, str) and not isinstance(prompt_4, list)):
|
| 636 |
+
raise ValueError(f"`prompt_4` has to be of type `str` or `list` but is {type(prompt_4)}")
|
| 637 |
+
|
| 638 |
+
if negative_prompt is not None and negative_pooled_prompt_embeds is not None:
|
| 639 |
+
raise ValueError(
|
| 640 |
+
f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_pooled_prompt_embeds`:"
|
| 641 |
+
f" {negative_pooled_prompt_embeds}. Please make sure to only forward one of the two."
|
| 642 |
+
)
|
| 643 |
+
elif negative_prompt_2 is not None and negative_pooled_prompt_embeds is not None:
|
| 644 |
+
raise ValueError(
|
| 645 |
+
f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_pooled_prompt_embeds`:"
|
| 646 |
+
f" {negative_pooled_prompt_embeds}. Please make sure to only forward one of the two."
|
| 647 |
+
)
|
| 648 |
+
elif negative_prompt_3 is not None and negative_prompt_embeds_t5 is not None:
|
| 649 |
+
raise ValueError(
|
| 650 |
+
f"Cannot forward both `negative_prompt_3`: {negative_prompt_3} and `negative_prompt_embeds_t5`:"
|
| 651 |
+
f" {negative_prompt_embeds_t5}. Please make sure to only forward one of the two."
|
| 652 |
+
)
|
| 653 |
+
elif negative_prompt_4 is not None and negative_prompt_embeds_llama3 is not None:
|
| 654 |
+
raise ValueError(
|
| 655 |
+
f"Cannot forward both `negative_prompt_4`: {negative_prompt_4} and `negative_prompt_embeds_llama3`:"
|
| 656 |
+
f" {negative_prompt_embeds_llama3}. Please make sure to only forward one of the two."
|
| 657 |
+
)
|
| 658 |
+
|
| 659 |
+
if pooled_prompt_embeds is not None and negative_pooled_prompt_embeds is not None:
|
| 660 |
+
if pooled_prompt_embeds.shape != negative_pooled_prompt_embeds.shape:
|
| 661 |
+
raise ValueError(
|
| 662 |
+
"`pooled_prompt_embeds` and `negative_pooled_prompt_embeds` must have the same shape when passed directly, but"
|
| 663 |
+
f" got: `pooled_prompt_embeds` {pooled_prompt_embeds.shape} != `negative_pooled_prompt_embeds`"
|
| 664 |
+
f" {negative_pooled_prompt_embeds.shape}."
|
| 665 |
+
)
|
| 666 |
+
if prompt_embeds_t5 is not None and negative_prompt_embeds_t5 is not None:
|
| 667 |
+
if prompt_embeds_t5.shape != negative_prompt_embeds_t5.shape:
|
| 668 |
+
raise ValueError(
|
| 669 |
+
"`prompt_embeds_t5` and `negative_prompt_embeds_t5` must have the same shape when passed directly, but"
|
| 670 |
+
f" got: `prompt_embeds_t5` {prompt_embeds_t5.shape} != `negative_prompt_embeds_t5`"
|
| 671 |
+
f" {negative_prompt_embeds_t5.shape}."
|
| 672 |
+
)
|
| 673 |
+
if prompt_embeds_llama3 is not None and negative_prompt_embeds_llama3 is not None:
|
| 674 |
+
if prompt_embeds_llama3.shape != negative_prompt_embeds_llama3.shape:
|
| 675 |
+
raise ValueError(
|
| 676 |
+
"`prompt_embeds_llama3` and `negative_prompt_embeds_llama3` must have the same shape when passed directly, but"
|
| 677 |
+
f" got: `prompt_embeds_llama3` {prompt_embeds_llama3.shape} != `negative_prompt_embeds_llama3`"
|
| 678 |
+
f" {negative_prompt_embeds_llama3.shape}."
|
| 679 |
+
)
|
| 680 |
+
|
| 681 |
+
def prepare_latents(
|
| 682 |
+
self,
|
| 683 |
+
batch_size,
|
| 684 |
+
num_channels_latents,
|
| 685 |
+
height,
|
| 686 |
+
width,
|
| 687 |
+
dtype,
|
| 688 |
+
device,
|
| 689 |
+
generator,
|
| 690 |
+
latents=None,
|
| 691 |
+
):
|
| 692 |
+
# VAE applies 8x compression on images but we must also account for packing which requires
|
| 693 |
+
# latent height and width to be divisible by 2.
|
| 694 |
+
height = 2 * (int(height) // (self.vae_scale_factor * 2))
|
| 695 |
+
width = 2 * (int(width) // (self.vae_scale_factor * 2))
|
| 696 |
+
|
| 697 |
+
shape = (batch_size, num_channels_latents, height, width)
|
| 698 |
+
|
| 699 |
+
if latents is None:
|
| 700 |
+
latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
|
| 701 |
+
else:
|
| 702 |
+
if latents.shape != shape:
|
| 703 |
+
raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}")
|
| 704 |
+
latents = latents.to(device)
|
| 705 |
+
return latents
|
| 706 |
+
|
| 707 |
+
@property
|
| 708 |
+
def guidance_scale(self):
|
| 709 |
+
return self._guidance_scale
|
| 710 |
+
|
| 711 |
+
@property
|
| 712 |
+
def do_classifier_free_guidance(self):
|
| 713 |
+
return self._guidance_scale > 1
|
| 714 |
+
|
| 715 |
+
@property
|
| 716 |
+
def attention_kwargs(self):
|
| 717 |
+
return self._attention_kwargs
|
| 718 |
+
|
| 719 |
+
@property
|
| 720 |
+
def num_timesteps(self):
|
| 721 |
+
return self._num_timesteps
|
| 722 |
+
|
| 723 |
+
@property
|
| 724 |
+
def interrupt(self):
|
| 725 |
+
return self._interrupt
|
| 726 |
+
|
| 727 |
+
@torch.no_grad()
|
| 728 |
+
@replace_example_docstring(EXAMPLE_DOC_STRING)
|
| 729 |
+
def __call__(
|
| 730 |
+
self,
|
| 731 |
+
prompt: str | list[str] = None,
|
| 732 |
+
prompt_2: str | list[str] | None = None,
|
| 733 |
+
prompt_3: str | list[str] | None = None,
|
| 734 |
+
prompt_4: str | list[str] | None = None,
|
| 735 |
+
height: int | None = None,
|
| 736 |
+
width: int | None = None,
|
| 737 |
+
num_inference_steps: int = 50,
|
| 738 |
+
sigmas: list[float] | None = None,
|
| 739 |
+
guidance_scale: float = 5.0,
|
| 740 |
+
negative_prompt: str | list[str] | None = None,
|
| 741 |
+
negative_prompt_2: str | list[str] | None = None,
|
| 742 |
+
negative_prompt_3: str | list[str] | None = None,
|
| 743 |
+
negative_prompt_4: str | list[str] | None = None,
|
| 744 |
+
num_images_per_prompt: int | None = 1,
|
| 745 |
+
generator: torch.Generator | list[torch.Generator] | None = None,
|
| 746 |
+
latents: torch.FloatTensor | None = None,
|
| 747 |
+
prompt_embeds_t5: torch.FloatTensor | None = None,
|
| 748 |
+
prompt_embeds_llama3: torch.FloatTensor | None = None,
|
| 749 |
+
negative_prompt_embeds_t5: torch.FloatTensor | None = None,
|
| 750 |
+
negative_prompt_embeds_llama3: torch.FloatTensor | None = None,
|
| 751 |
+
pooled_prompt_embeds: torch.FloatTensor | None = None,
|
| 752 |
+
negative_pooled_prompt_embeds: torch.FloatTensor | None = None,
|
| 753 |
+
output_type: str | None = "pil",
|
| 754 |
+
return_dict: bool = True,
|
| 755 |
+
attention_kwargs: dict[str, Any] | None = None,
|
| 756 |
+
callback_on_step_end: Callable[[int, int], None] | None = None,
|
| 757 |
+
callback_on_step_end_tensor_inputs: list[str] = ["latents"],
|
| 758 |
+
max_sequence_length: int = 128,
|
| 759 |
+
**kwargs,
|
| 760 |
+
):
|
| 761 |
+
r"""
|
| 762 |
+
Function invoked when calling the pipeline for generation.
|
| 763 |
+
|
| 764 |
+
Args:
|
| 765 |
+
prompt (`str` or `list[str]`, *optional*):
|
| 766 |
+
The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
|
| 767 |
+
instead.
|
| 768 |
+
prompt_2 (`str` or `list[str]`, *optional*):
|
| 769 |
+
The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
|
| 770 |
+
will be used instead.
|
| 771 |
+
prompt_3 (`str` or `list[str]`, *optional*):
|
| 772 |
+
The prompt or prompts to be sent to `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
|
| 773 |
+
will be used instead.
|
| 774 |
+
prompt_4 (`str` or `list[str]`, *optional*):
|
| 775 |
+
The prompt or prompts to be sent to `tokenizer_4` and `text_encoder_4`. If not defined, `prompt` is
|
| 776 |
+
will be used instead.
|
| 777 |
+
height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 778 |
+
The height in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 779 |
+
width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
|
| 780 |
+
The width in pixels of the generated image. This is set to 1024 by default for the best results.
|
| 781 |
+
num_inference_steps (`int`, *optional*, defaults to 50):
|
| 782 |
+
The number of denoising steps. More denoising steps usually lead to a higher quality image at the
|
| 783 |
+
expense of slower inference.
|
| 784 |
+
sigmas (`list[float]`, *optional*):
|
| 785 |
+
Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
|
| 786 |
+
their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
|
| 787 |
+
will be used.
|
| 788 |
+
guidance_scale (`float`, *optional*, defaults to 3.5):
|
| 789 |
+
Embedded guiddance scale is enabled by setting `guidance_scale` > 1. Higher `guidance_scale` encourages
|
| 790 |
+
a model to generate images more aligned with `prompt` at the expense of lower image quality.
|
| 791 |
+
|
| 792 |
+
Guidance-distilled models approximates true classifer-free guidance for `guidance_scale` > 1. Refer to
|
| 793 |
+
the [paper](https://huggingface.co/papers/2210.03142) to learn more.
|
| 794 |
+
negative_prompt (`str` or `list[str]`, *optional*):
|
| 795 |
+
The prompt or prompts not to guide the image generation. If not defined, one has to pass
|
| 796 |
+
`negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `true_cfg_scale` is
|
| 797 |
+
not greater than `1`).
|
| 798 |
+
negative_prompt_2 (`str` or `list[str]`, *optional*):
|
| 799 |
+
The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
|
| 800 |
+
`text_encoder_2`. If not defined, `negative_prompt` is used in all the text-encoders.
|
| 801 |
+
negative_prompt_3 (`str` or `list[str]`, *optional*):
|
| 802 |
+
The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
|
| 803 |
+
`text_encoder_3`. If not defined, `negative_prompt` is used in all the text-encoders.
|
| 804 |
+
negative_prompt_4 (`str` or `list[str]`, *optional*):
|
| 805 |
+
The prompt or prompts not to guide the image generation to be sent to `tokenizer_4` and
|
| 806 |
+
`text_encoder_4`. If not defined, `negative_prompt` is used in all the text-encoders.
|
| 807 |
+
num_images_per_prompt (`int`, *optional*, defaults to 1):
|
| 808 |
+
The number of images to generate per prompt.
|
| 809 |
+
generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
|
| 810 |
+
One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
|
| 811 |
+
to make generation deterministic.
|
| 812 |
+
latents (`torch.FloatTensor`, *optional*):
|
| 813 |
+
Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
|
| 814 |
+
generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
|
| 815 |
+
tensor will be generated by sampling using the supplied random `generator`.
|
| 816 |
+
prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 817 |
+
Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
|
| 818 |
+
provided, text embeddings will be generated from `prompt` input argument.
|
| 819 |
+
negative_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 820 |
+
Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 821 |
+
weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
|
| 822 |
+
argument.
|
| 823 |
+
pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 824 |
+
Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
|
| 825 |
+
If not provided, pooled text embeddings will be generated from `prompt` input argument.
|
| 826 |
+
negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
|
| 827 |
+
Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
|
| 828 |
+
weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
|
| 829 |
+
input argument.
|
| 830 |
+
output_type (`str`, *optional*, defaults to `"pil"`):
|
| 831 |
+
The output format of the generate image. Choose between
|
| 832 |
+
[PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
|
| 833 |
+
return_dict (`bool`, *optional*, defaults to `True`):
|
| 834 |
+
Whether or not to return a [`~pipelines.flux.FluxPipelineOutput`] instead of a plain tuple.
|
| 835 |
+
attention_kwargs (`dict`, *optional*):
|
| 836 |
+
A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
|
| 837 |
+
`self.processor` in
|
| 838 |
+
[diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
|
| 839 |
+
callback_on_step_end (`Callable`, *optional*):
|
| 840 |
+
A function that calls at the end of each denoising steps during the inference. The function is called
|
| 841 |
+
with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
|
| 842 |
+
callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
|
| 843 |
+
`callback_on_step_end_tensor_inputs`.
|
| 844 |
+
callback_on_step_end_tensor_inputs (`list`, *optional*):
|
| 845 |
+
The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
|
| 846 |
+
will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
|
| 847 |
+
`._callback_tensor_inputs` attribute of your pipeline class.
|
| 848 |
+
max_sequence_length (`int` defaults to 128): Maximum sequence length to use with the `prompt`.
|
| 849 |
+
|
| 850 |
+
Examples:
|
| 851 |
+
|
| 852 |
+
Returns:
|
| 853 |
+
[`~pipelines.hidream_image.HiDreamImagePipelineOutput`] or `tuple`:
|
| 854 |
+
[`~pipelines.hidream_image.HiDreamImagePipelineOutput`] if `return_dict` is True, otherwise a `tuple`. When
|
| 855 |
+
returning a tuple, the first element is a list with the generated. images.
|
| 856 |
+
"""
|
| 857 |
+
|
| 858 |
+
prompt_embeds = kwargs.get("prompt_embeds", None)
|
| 859 |
+
negative_prompt_embeds = kwargs.get("negative_prompt_embeds", None)
|
| 860 |
+
|
| 861 |
+
if prompt_embeds is not None:
|
| 862 |
+
deprecation_message = "The `prompt_embeds` argument is deprecated. Please use `prompt_embeds_t5` and `prompt_embeds_llama3` instead."
|
| 863 |
+
deprecate("prompt_embeds", "0.35.0", deprecation_message)
|
| 864 |
+
prompt_embeds_t5 = prompt_embeds[0]
|
| 865 |
+
prompt_embeds_llama3 = prompt_embeds[1]
|
| 866 |
+
|
| 867 |
+
if negative_prompt_embeds is not None:
|
| 868 |
+
deprecation_message = "The `negative_prompt_embeds` argument is deprecated. Please use `negative_prompt_embeds_t5` and `negative_prompt_embeds_llama3` instead."
|
| 869 |
+
deprecate("negative_prompt_embeds", "0.35.0", deprecation_message)
|
| 870 |
+
negative_prompt_embeds_t5 = negative_prompt_embeds[0]
|
| 871 |
+
negative_prompt_embeds_llama3 = negative_prompt_embeds[1]
|
| 872 |
+
|
| 873 |
+
height = height or self.default_sample_size * self.vae_scale_factor
|
| 874 |
+
width = width or self.default_sample_size * self.vae_scale_factor
|
| 875 |
+
|
| 876 |
+
division = self.vae_scale_factor * 2
|
| 877 |
+
S_max = (self.default_sample_size * self.vae_scale_factor) ** 2
|
| 878 |
+
scale = S_max / (width * height)
|
| 879 |
+
scale = math.sqrt(scale)
|
| 880 |
+
width, height = int(width * scale // division * division), int(height * scale // division * division)
|
| 881 |
+
|
| 882 |
+
# 1. Check inputs. Raise error if not correct
|
| 883 |
+
self.check_inputs(
|
| 884 |
+
prompt,
|
| 885 |
+
prompt_2,
|
| 886 |
+
prompt_3,
|
| 887 |
+
prompt_4,
|
| 888 |
+
negative_prompt=negative_prompt,
|
| 889 |
+
negative_prompt_2=negative_prompt_2,
|
| 890 |
+
negative_prompt_3=negative_prompt_3,
|
| 891 |
+
negative_prompt_4=negative_prompt_4,
|
| 892 |
+
prompt_embeds_t5=prompt_embeds_t5,
|
| 893 |
+
prompt_embeds_llama3=prompt_embeds_llama3,
|
| 894 |
+
negative_prompt_embeds_t5=negative_prompt_embeds_t5,
|
| 895 |
+
negative_prompt_embeds_llama3=negative_prompt_embeds_llama3,
|
| 896 |
+
pooled_prompt_embeds=pooled_prompt_embeds,
|
| 897 |
+
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
| 898 |
+
callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
|
| 899 |
+
)
|
| 900 |
+
|
| 901 |
+
self._guidance_scale = guidance_scale
|
| 902 |
+
self._attention_kwargs = attention_kwargs
|
| 903 |
+
self._interrupt = False
|
| 904 |
+
|
| 905 |
+
# 2. Define call parameters
|
| 906 |
+
if prompt is not None and isinstance(prompt, str):
|
| 907 |
+
batch_size = 1
|
| 908 |
+
elif prompt is not None and isinstance(prompt, list):
|
| 909 |
+
batch_size = len(prompt)
|
| 910 |
+
elif pooled_prompt_embeds is not None:
|
| 911 |
+
batch_size = pooled_prompt_embeds.shape[0]
|
| 912 |
+
|
| 913 |
+
device = self._execution_device
|
| 914 |
+
|
| 915 |
+
# 3. Encode prompt
|
| 916 |
+
lora_scale = self.attention_kwargs.get("scale", None) if self.attention_kwargs is not None else None
|
| 917 |
+
(
|
| 918 |
+
prompt_embeds_t5,
|
| 919 |
+
negative_prompt_embeds_t5,
|
| 920 |
+
prompt_embeds_llama3,
|
| 921 |
+
negative_prompt_embeds_llama3,
|
| 922 |
+
pooled_prompt_embeds,
|
| 923 |
+
negative_pooled_prompt_embeds,
|
| 924 |
+
) = self.encode_prompt(
|
| 925 |
+
prompt=prompt,
|
| 926 |
+
prompt_2=prompt_2,
|
| 927 |
+
prompt_3=prompt_3,
|
| 928 |
+
prompt_4=prompt_4,
|
| 929 |
+
negative_prompt=negative_prompt,
|
| 930 |
+
negative_prompt_2=negative_prompt_2,
|
| 931 |
+
negative_prompt_3=negative_prompt_3,
|
| 932 |
+
negative_prompt_4=negative_prompt_4,
|
| 933 |
+
do_classifier_free_guidance=self.do_classifier_free_guidance,
|
| 934 |
+
prompt_embeds_t5=prompt_embeds_t5,
|
| 935 |
+
prompt_embeds_llama3=prompt_embeds_llama3,
|
| 936 |
+
negative_prompt_embeds_t5=negative_prompt_embeds_t5,
|
| 937 |
+
negative_prompt_embeds_llama3=negative_prompt_embeds_llama3,
|
| 938 |
+
pooled_prompt_embeds=pooled_prompt_embeds,
|
| 939 |
+
negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
|
| 940 |
+
device=device,
|
| 941 |
+
num_images_per_prompt=num_images_per_prompt,
|
| 942 |
+
max_sequence_length=max_sequence_length,
|
| 943 |
+
lora_scale=lora_scale,
|
| 944 |
+
)
|
| 945 |
+
|
| 946 |
+
if self.do_classifier_free_guidance:
|
| 947 |
+
prompt_embeds_t5 = torch.cat([negative_prompt_embeds_t5, prompt_embeds_t5], dim=0)
|
| 948 |
+
prompt_embeds_llama3 = torch.cat([negative_prompt_embeds_llama3, prompt_embeds_llama3], dim=1)
|
| 949 |
+
pooled_prompt_embeds = torch.cat([negative_pooled_prompt_embeds, pooled_prompt_embeds], dim=0)
|
| 950 |
+
|
| 951 |
+
# 4. Prepare latent variables
|
| 952 |
+
num_channels_latents = self.transformer.config.in_channels
|
| 953 |
+
latents = self.prepare_latents(
|
| 954 |
+
batch_size * num_images_per_prompt,
|
| 955 |
+
num_channels_latents,
|
| 956 |
+
height,
|
| 957 |
+
width,
|
| 958 |
+
pooled_prompt_embeds.dtype,
|
| 959 |
+
device,
|
| 960 |
+
generator,
|
| 961 |
+
latents,
|
| 962 |
+
)
|
| 963 |
+
|
| 964 |
+
# 5. Prepare timesteps
|
| 965 |
+
mu = calculate_shift(self.transformer.max_seq)
|
| 966 |
+
scheduler_kwargs = {"mu": mu}
|
| 967 |
+
if XLA_AVAILABLE:
|
| 968 |
+
timestep_device = "cpu"
|
| 969 |
+
else:
|
| 970 |
+
timestep_device = device
|
| 971 |
+
if isinstance(self.scheduler, UniPCMultistepScheduler):
|
| 972 |
+
self.scheduler.set_timesteps(num_inference_steps, device=timestep_device) # , shift=math.exp(mu))
|
| 973 |
+
timesteps = self.scheduler.timesteps
|
| 974 |
+
else:
|
| 975 |
+
timesteps, num_inference_steps = retrieve_timesteps(
|
| 976 |
+
self.scheduler,
|
| 977 |
+
num_inference_steps,
|
| 978 |
+
timestep_device,
|
| 979 |
+
sigmas=sigmas,
|
| 980 |
+
**scheduler_kwargs,
|
| 981 |
+
)
|
| 982 |
+
num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
|
| 983 |
+
self._num_timesteps = len(timesteps)
|
| 984 |
+
|
| 985 |
+
# 6. Denoising loop
|
| 986 |
+
with self.progress_bar(total=num_inference_steps) as progress_bar:
|
| 987 |
+
for i, t in enumerate(timesteps):
|
| 988 |
+
if self.interrupt:
|
| 989 |
+
continue
|
| 990 |
+
|
| 991 |
+
# expand the latents if we are doing classifier free guidance
|
| 992 |
+
latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
|
| 993 |
+
# broadcast to batch dimension in a way that's compatible with ONNX/Core ML
|
| 994 |
+
timestep = t.expand(latent_model_input.shape[0])
|
| 995 |
+
|
| 996 |
+
noise_pred = self.transformer(
|
| 997 |
+
hidden_states=latent_model_input,
|
| 998 |
+
timesteps=timestep,
|
| 999 |
+
encoder_hidden_states_t5=prompt_embeds_t5,
|
| 1000 |
+
encoder_hidden_states_llama3=prompt_embeds_llama3,
|
| 1001 |
+
pooled_embeds=pooled_prompt_embeds,
|
| 1002 |
+
return_dict=False,
|
| 1003 |
+
)[0]
|
| 1004 |
+
noise_pred = -noise_pred
|
| 1005 |
+
|
| 1006 |
+
# perform guidance
|
| 1007 |
+
if self.do_classifier_free_guidance:
|
| 1008 |
+
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
|
| 1009 |
+
noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
|
| 1010 |
+
|
| 1011 |
+
# compute the previous noisy sample x_t -> x_t-1
|
| 1012 |
+
latents_dtype = latents.dtype
|
| 1013 |
+
latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
|
| 1014 |
+
|
| 1015 |
+
if latents.dtype != latents_dtype:
|
| 1016 |
+
if torch.backends.mps.is_available():
|
| 1017 |
+
# some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
|
| 1018 |
+
latents = latents.to(latents_dtype)
|
| 1019 |
+
|
| 1020 |
+
if callback_on_step_end is not None:
|
| 1021 |
+
callback_kwargs = {}
|
| 1022 |
+
for k in callback_on_step_end_tensor_inputs:
|
| 1023 |
+
callback_kwargs[k] = locals()[k]
|
| 1024 |
+
callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
|
| 1025 |
+
|
| 1026 |
+
latents = callback_outputs.pop("latents", latents)
|
| 1027 |
+
prompt_embeds_t5 = callback_outputs.pop("prompt_embeds_t5", prompt_embeds_t5)
|
| 1028 |
+
prompt_embeds_llama3 = callback_outputs.pop("prompt_embeds_llama3", prompt_embeds_llama3)
|
| 1029 |
+
pooled_prompt_embeds = callback_outputs.pop("pooled_prompt_embeds", pooled_prompt_embeds)
|
| 1030 |
+
|
| 1031 |
+
# call the callback, if provided
|
| 1032 |
+
if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
|
| 1033 |
+
progress_bar.update()
|
| 1034 |
+
|
| 1035 |
+
if XLA_AVAILABLE:
|
| 1036 |
+
xm.mark_step()
|
| 1037 |
+
|
| 1038 |
+
if output_type == "latent":
|
| 1039 |
+
image = latents
|
| 1040 |
+
|
| 1041 |
+
else:
|
| 1042 |
+
latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
|
| 1043 |
+
|
| 1044 |
+
image = self.vae.decode(latents, return_dict=False)[0]
|
| 1045 |
+
image = self.image_processor.postprocess(image, output_type=output_type)
|
| 1046 |
+
|
| 1047 |
+
# Offload all models
|
| 1048 |
+
self.maybe_free_model_hooks()
|
| 1049 |
+
|
| 1050 |
+
if not return_dict:
|
| 1051 |
+
return (image,)
|
| 1052 |
+
|
| 1053 |
+
return HiDreamImagePipelineOutput(images=image)
|
third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_output.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 HiDream-ai Team and The HuggingFace Team. All rights reserved.
|
| 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 dataclasses import dataclass
|
| 16 |
+
|
| 17 |
+
import numpy as np
|
| 18 |
+
import PIL.Image
|
| 19 |
+
|
| 20 |
+
from ...utils import BaseOutput
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class HiDreamImagePipelineOutput(BaseOutput):
|
| 25 |
+
"""
|
| 26 |
+
Output class for HiDreamImage pipelines.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
images (`list[PIL.Image.Image]` or `np.ndarray`)
|
| 30 |
+
list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
|
| 31 |
+
num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
images: list[PIL.Image.Image] | np.ndarray
|
third_party/diffusers/src/diffusers/pipelines/latte/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import TYPE_CHECKING
|
| 2 |
+
|
| 3 |
+
from ...utils import (
|
| 4 |
+
DIFFUSERS_SLOW_IMPORT,
|
| 5 |
+
OptionalDependencyNotAvailable,
|
| 6 |
+
_LazyModule,
|
| 7 |
+
get_objects_from_module,
|
| 8 |
+
is_torch_available,
|
| 9 |
+
is_transformers_available,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
_dummy_objects = {}
|
| 14 |
+
_import_structure = {}
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 19 |
+
raise OptionalDependencyNotAvailable()
|
| 20 |
+
except OptionalDependencyNotAvailable:
|
| 21 |
+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
|
| 22 |
+
|
| 23 |
+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
|
| 24 |
+
else:
|
| 25 |
+
_import_structure["pipeline_latte"] = ["LattePipeline"]
|
| 26 |
+
|
| 27 |
+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
|
| 28 |
+
try:
|
| 29 |
+
if not (is_transformers_available() and is_torch_available()):
|
| 30 |
+
raise OptionalDependencyNotAvailable()
|
| 31 |
+
|
| 32 |
+
except OptionalDependencyNotAvailable:
|
| 33 |
+
from ...utils.dummy_torch_and_transformers_objects import *
|
| 34 |
+
else:
|
| 35 |
+
from .pipeline_latte import LattePipeline
|
| 36 |
+
|
| 37 |
+
else:
|
| 38 |
+
import sys
|
| 39 |
+
|
| 40 |
+
sys.modules[__name__] = _LazyModule(
|
| 41 |
+
__name__,
|
| 42 |
+
globals()["__file__"],
|
| 43 |
+
_import_structure,
|
| 44 |
+
module_spec=__spec__,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
for name, value in _dummy_objects.items():
|
| 48 |
+
setattr(sys.modules[__name__], name, value)
|