Compare commits
45
Commits
code-refactor
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08aad38986 | ||
|
|
e132c43556 | ||
|
|
e6909ae6b2 | ||
|
|
4dc7fa8f39 | ||
|
|
77dd7dbb91 | ||
|
|
86401148f7 | ||
|
|
bce8450e07 | ||
|
|
aed11f1196 | ||
|
|
97404944f5 | ||
|
|
3619aee188 | ||
|
|
f1e326ad4a | ||
|
|
a62ca4222e | ||
|
|
45858eebc4 | ||
|
|
32aad09a9a | ||
|
|
f40207480f | ||
|
|
6416ffafa2 | ||
|
|
49916eae86 | ||
|
|
dccfb2326e | ||
|
|
62569f0184 | ||
|
|
dc6268aea5 | ||
|
|
9d32153349 | ||
|
|
63c068c59f | ||
|
|
a0bdb7e06c | ||
|
|
5d1d909b53 | ||
|
|
52609c02a9 | ||
|
|
1aa6948063 | ||
|
|
71ac33b8f3 | ||
|
|
8bd67a69cf | ||
|
|
9f9513c236 | ||
|
|
149a6bb3de | ||
|
|
3b8c80ba6e | ||
|
|
c41af1c324 | ||
|
|
746330b5bf | ||
|
|
91a286cdf6 | ||
|
|
07f8b8d2a9 | ||
|
|
d849f6c7d6 | ||
|
|
12ea0093e3 | ||
|
|
4e881671aa | ||
|
|
414c5d3bb8 | ||
|
|
78e04fcdc6 | ||
|
|
60d14a9840 | ||
|
|
427cf04893 | ||
|
|
87815b7aae | ||
|
|
9ae375fbd8 | ||
|
|
d4f5328a47 |
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -5,73 +5,233 @@
|
|||||||
## How to Use
|
## How to Use
|
||||||
|
|
||||||
1. Clone this repo into `custom_nodes` folder.
|
1. Clone this repo into `custom_nodes` folder.
|
||||||
2. Download motion modules from [Google Drive](https://drive.google.com/drive/folders/1EqLC65eR1-W-sGD0Im7fkED6c8GkiNFI) | [HuggingFace](https://huggingface.co/guoyww/animatediff) | [CivitAI](https://civitai.com/models/108836) | [Baidu NetDisk](https://pan.baidu.com/s/18ZpcSM6poBqxWNHtnyMcxg?pwd=et8y). You only need to download one of `mm_sd_v14.ckpt` | `mm_sd_v15.ckpt`. Put the model weights under `comfyui-animatediff/models/`. DO NOT change model filename.
|
2. Download motion modules and put them under `comfyui-animatediff/models/`.
|
||||||
|
|
||||||
#### Update 2023/09/15
|
- Original modules: [Google Drive](https://drive.google.com/drive/folders/1EqLC65eR1-W-sGD0Im7fkED6c8GkiNFI) | [HuggingFace](https://huggingface.co/guoyww/animatediff) | [CivitAI](https://civitai.com/models/108836) | [Baidu NetDisk](https://pan.baidu.com/s/18ZpcSM6poBqxWNHtnyMcxg?pwd=et8y)
|
||||||
|
- Community modules: [manshoety/AD_Stabilized_Motion](https://huggingface.co/manshoety/AD_Stabilized_Motion) | [CiaraRowles/TemporalDiff](https://huggingface.co/CiaraRowles/TemporalDiff)
|
||||||
|
- AnimateDiff v2 [mm_sd_v15_v2.ckpt](https://huggingface.co/guoyww/animatediff/blob/main/mm_sd_v15_v2.ckpt)
|
||||||
|
|
||||||
- You can now use community models from [manshoety/AD_Stabilized_Motion](https://huggingface.co/manshoety/AD_Stabilized_Motion) or [CiaraRowles/TemporalDiff](https://huggingface.co/CiaraRowles/TemporalDiff)
|
## Update 2023/09/25
|
||||||
- Supports AnimateDiff v2 [mm_sd_v15_v2.ckpt](https://huggingface.co/guoyww/animatediff/blob/main/mm_sd_v15_v2.ckpt) model
|
|
||||||
- Fix image is grayed out.
|
|
||||||
- New node: **AnimateDiffSampler** and **AnimateDiffLoader**
|
|
||||||
- Mostly the same with `KSampler`
|
|
||||||
- Use `AnimateDiffLoader` to load the motion module
|
|
||||||
- `inject_method`: should left default. See [this issue](https://github.com/ArtVentureX/comfyui-animatediff#gif-has-wartermark-after-update-to-the-latest-version) for more details.
|
|
||||||
- `frame_number`: animation length
|
|
||||||
|
|
||||||
<img width="506" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/f22d6b36-ce36-44cc-80e8-dffe6f77b296">
|
#### **Motion LoRA** is now supported!
|
||||||
|
|
||||||
#### Example Workflow
|
Download [motion LoRAs](https://huggingface.co/guoyww/animatediff/tree/main) and put them under `comfyui-animatediff/loras/` folder.
|
||||||
|
|
||||||
<img width="1311" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/b7164539-bc58-4ef9-b178-d914e833805e">
|
Note: LoRAs only work with **AnimateDiff v2** [mm_sd_v15_v2.ckpt](https://huggingface.co/guoyww/animatediff/blob/main/mm_sd_v15_v2.ckpt) module.
|
||||||
|
|
||||||
|
#### New node: `AnimateDiffLoraLoader`
|
||||||
|
|
||||||
Workflow file: https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflow.json
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/7a9f62f7-702e-48a4-934c-bbfe1e23aff2">
|
||||||
|
|
||||||
## Samples
|
Example workflow:
|
||||||
|
<img width="1280" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/93e7550f-4648-4482-9961-6cece5132dc9">
|
||||||
|
|
||||||

|
Workflow: [lora.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/lora.json)
|
||||||
|
|
||||||

|
Samples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="512" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/2c5aa25e-0682-481f-8842-066c5b988864">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="512" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/adfbad45-3ba5-42e3-9bee-d2b83f43989c">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="512" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/8e484c74-c691-4d1c-9514-719dbfe3a0b5">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="512" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/4921a335-9207-4a7b-9d66-61a5d76e3179">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Update 2023/09/21
|
||||||
|
|
||||||
|
#### **Sliding Window** is now available!
|
||||||
|
|
||||||
|
The sliding window feature enables you to generate GIFs without a frame length limit. It divides frames into smaller batches with a slight overlap. This feature is activated automatically when generating more than 16 frames. To modify the trigger number and other settings, utilize the `SlidingWindowOptions` node. See the [sample workflow](#long-duration-with-sliding-window) bellow.
|
||||||
|
|
||||||
|
## Nodes
|
||||||
|
|
||||||
|
#### AnimateDiffLoader
|
||||||
|
|
||||||
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/9d756d01-ea45-4d1c-8e48-56f2725c7ca1">
|
||||||
|
|
||||||
|
#### AnimateDiffSampler
|
||||||
|
|
||||||
|
- Mostly the same with `KSampler`
|
||||||
|
- `motion_module`: use `AnimateDiffLoader` to load the motion module
|
||||||
|
- `inject_method`: should left default
|
||||||
|
- `frame_number`: animation length
|
||||||
|
- `latent_image`: You can pass an `EmptyLatentImage`
|
||||||
|
- `sliding_window_opts`: custom sliding window options
|
||||||
|
|
||||||
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/a352195d-f40c-494d-bd3d-30ee88174b88">
|
||||||
|
|
||||||
|
#### AnimateDiffCombine
|
||||||
|
|
||||||
|
- Combine GIF frames and produce the GIF image
|
||||||
|
- `frame_rate`: number of frame per second
|
||||||
|
- `loop_count`: use 0 for infinite loop
|
||||||
|
- `save_image`: should GIF be saved to disk
|
||||||
|
- `format`: supports `image/gif`, `image/webp` (better compression), `video/webm`, `video/h264-mp4`, `video/h265-mp4`. To use video formats, you'll need [ffmpeg](https://ffmpeg.org/download.html) installed and available in **`PATH`**
|
||||||
|
|
||||||
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/381c5acc-06ef-43da-ada0-3dc76f37a3e4">
|
||||||
|
|
||||||
|
#### SlidingWindowOptions
|
||||||
|
|
||||||
|
Custom sliding window options
|
||||||
|
|
||||||
|
- `context_length`: number of frame per _window_. Use **16** to get the best results. Reduce it if you have low VRAM.
|
||||||
|
- `context_stride`:
|
||||||
|
- 1: sampling every frame
|
||||||
|
- 2: sampling every frame then every second frame
|
||||||
|
- 3: sampling every frame then every second frame then every third frames
|
||||||
|
- ...
|
||||||
|
- `context_overlap`: overlap frames between each window slice
|
||||||
|
- `closed_loop`: make the GIF a closed loop, will add more sampling step
|
||||||
|
|
||||||
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/6679a8dd-bf96-419f-8934-ea2b046dd23c">
|
||||||
|
|
||||||
|
#### LoadVideo
|
||||||
|
|
||||||
|
Load GIF or video as images. Usefull to load a GIF as ControlNet input.
|
||||||
|
|
||||||
|
- `frame_start`: Skip some begining frames and start at `frame_start`
|
||||||
|
- `frame_limit`: Only take `frame_limit` frames
|
||||||
|
|
||||||
|
<img width="370" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/684176d5-6369-4a27-9f33-e721e0fe1876">
|
||||||
|
|
||||||
|
## Workflows
|
||||||
|
|
||||||
|
### Simple txt2gif
|
||||||
|
|
||||||
|
<img width="1280" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/b7164539-bc58-4ef9-b178-d914e833805e">
|
||||||
|
|
||||||
|
Workflow: [simple.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/simple.json)
|
||||||
|
|
||||||
|
Samples:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Long duration with sliding window
|
||||||
|
|
||||||
|
<img width="1280" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/0f8bfb87-83cb-4119-9777-e3948ec0cb5c">
|
||||||
|
|
||||||
|
Workflow: [sliding-window.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/sliding-window.json)
|
||||||
|
|
||||||
|
Samples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="512" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/e1da7a66-e615-475d-9400-41eff484ad49">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="768" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/4faa7e5e-cdaa-49da-8759-46d779c0e0b6">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Latent upscale
|
||||||
|
|
||||||
|
Upscale latent output using `LatentUpscale` then do a 2nd pass with `AnimateDiffSampler`.
|
||||||
|
|
||||||
|
<img width="1280" alt="image" src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/987a1c5a-c1f8-4b24-8c62-f14496261d6c">
|
||||||
|
|
||||||
|
Workflow: [latent-upscale.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/latent-upscale.json)
|
||||||
|
|
||||||
|
Samples:
|
||||||
|

|
||||||
|
|
||||||
|
### Using with ControlNet
|
||||||
|
|
||||||
|
You will need following additional nodes:
|
||||||
|
|
||||||
|
- [Kosinkadink/ComfyUI-Advanced-ControlNet](https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet): Apply different weight for each latent in batch
|
||||||
|
- [Fannovel16/comfyui_controlnet_aux](https://github.com/Fannovel16/comfyui_controlnet_aux): ControlNet preprocessors
|
||||||
|
|
||||||
|
#### Animate with starting and ending images
|
||||||
|
|
||||||
|
- Use `LatentKeyframe` and `TimestampKeyframe` from [ComfyUI-Advanced-ControlNet](https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet) to apply diffrent weights for each latent index.
|
||||||
|
- Use 2 controlnet modules for two images with weights reverted.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Workflow: [cn-2images.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/cn-2images.json)
|
||||||
|
|
||||||
|
Samples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/e73fc3cd-a590-40a9-8b33-11358b54f0cd">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/96c2ee92-d457-4862-94d3-d675b7fa2d1f">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/46338853-1ae0-433e-925c-2a41e0382e68">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/707e4ce3-3594-4ff5-9a5f-f9596eb2bcf4">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
#### Using GIF as ControlNet input
|
||||||
|
|
||||||
|
Using a GIF (or video, or a list of images) as ControlNet input.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Workflow: [cn-vid2vid.json](https://github.com/ArtVentureX/comfyui-animatediff/blob/main/workflows/cn-vid2vid.json)
|
||||||
|
|
||||||
|
Samples:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/bf926f52-da97-4fb4-b86a-8b26ef5fab04">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/f6472c8c-9b92-47c2-8f28-638726f21be7">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
|
### CUDA error: invalid configuration argument
|
||||||
|
|
||||||
|
It's an `xformers` bug accidentally triggered by the way the original AnimateDiff CrossAttention is passed in. The current workaround is to disable xformers with `--disable-xformers` when booting ComfyUI.
|
||||||
|
|
||||||
### GIF split into multiple scenes
|
### GIF split into multiple scenes
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
See: https://github.com/continue-revolution/sd-webui-animatediff/issues/38
|
|
||||||
|
|
||||||
Main reasons:
|
|
||||||
|
|
||||||
- Promt are too long (more than 75 tokens)
|
|
||||||
- Resolution are too high
|
|
||||||
- Number of frame too high
|
|
||||||
|
|
||||||
Work around:
|
Work around:
|
||||||
|
|
||||||
- Shorter your prompt and negative prompt
|
- Shorter your prompt and negative prompt
|
||||||
- Reduce resolution. AnimateDiff is trained on 512x512 images so it works best with 512x512 output.
|
- Reduce resolution. AnimateDiff is trained on 512x512 images so it works best with 512x512 output.
|
||||||
- Shouldn't generate longer than 16 frames. AnimateDiff is trained to output the best results with 16 frames.
|
- Disable xformers with `--disable-xformers`
|
||||||
|
|
||||||
### GIF has Wartermark after update to the latest version
|
### GIF has Wartermark (especially when using mm_sd_v15)
|
||||||
|
|
||||||
See: https://github.com/continue-revolution/sd-webui-animatediff/issues/31
|
See: https://github.com/continue-revolution/sd-webui-animatediff/issues/31
|
||||||
|
|
||||||
As mentioned in the issue thread, it seems to be due to the training dataset. The new version is the correct implementation and produces smoother GIFs compared to the older version.
|
Training data used by the authors of the AnimateDiff paper contained Shutterstock watermarks. Since mm_sd_v15 was finetuned on finer, less drastic movement, the motion module attempts to replicate the transparency of that watermark and does not get blurred away like mm_sd_v14. Try other community finetuned modules.
|
||||||
|
|
||||||
<table class="center">
|
|
||||||
<tr>
|
|
||||||
<td>Old revision</td>
|
|
||||||
<td>New revision</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/8f1a6233-875f-4f0c-aa60-ba93e73b7d64" /></td>
|
|
||||||
<td><img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/a2029eba-f519-437c-a0b5-1f881e099a20" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/41ec449f-1955-466c-bd38-6f2a55d654f8" /></td>
|
|
||||||
<td><img src="https://github.com/ArtVentureX/comfyui-animatediff/assets/133728487/766c2891-5d27-4052-99f9-be9862620919" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
I played around with both version and found that the watermark only present in some models, not always. To use the **old (legacy)** method, change `injection_method` to `legacy` in the `AnimateDiffSampler` node.
|
|
||||||
|
|||||||
+3
-1
@@ -5,4 +5,6 @@ from .animatediff.model_utils import get_available_models
|
|||||||
if len(get_available_models()) == 0:
|
if len(get_available_models()) == 0:
|
||||||
logger.error("No models available. Please download one and put it in models folder")
|
logger.error("No models available. Please download one and put it in models folder")
|
||||||
|
|
||||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
WEB_DIRECTORY = "./web"
|
||||||
|
|
||||||
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,18 @@
|
|||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import torch
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
import comfy.model_management as model_management
|
||||||
|
from comfy.utils import load_torch_file, calculate_parameters
|
||||||
|
|
||||||
|
from .logger import logger
|
||||||
|
from .motion_module import MotionWrapper
|
||||||
|
|
||||||
|
|
||||||
|
motion_modules: Dict[str, MotionWrapper] = {}
|
||||||
|
motion_loras: Dict[str, Dict[str, torch.Tensor]] = {}
|
||||||
|
|
||||||
|
|
||||||
folder_paths.folder_names_and_paths["AnimateDiff"] = (
|
folder_paths.folder_names_and_paths["AnimateDiff"] = (
|
||||||
@@ -11,17 +22,79 @@ folder_paths.folder_names_and_paths["AnimateDiff"] = (
|
|||||||
],
|
],
|
||||||
folder_paths.supported_pt_extensions,
|
folder_paths.supported_pt_extensions,
|
||||||
)
|
)
|
||||||
|
folder_paths.folder_names_and_paths["AnimateDiffLora"] = (
|
||||||
|
[
|
||||||
|
os.path.join(folder_paths.models_dir, "AnimateDiffLora"),
|
||||||
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "loras"),
|
||||||
|
],
|
||||||
|
folder_paths.supported_pt_extensions,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_available_models():
|
def get_available_models():
|
||||||
return folder_paths.get_filename_list("AnimateDiff")
|
return folder_paths.get_filename_list("AnimateDiff")
|
||||||
|
|
||||||
|
|
||||||
|
def get_available_loras():
|
||||||
|
return folder_paths.get_filename_list("AnimateDiffLora")
|
||||||
|
|
||||||
|
|
||||||
def get_model_path(model_name):
|
def get_model_path(model_name):
|
||||||
return folder_paths.get_full_path("AnimateDiff", model_name)
|
return folder_paths.get_full_path("AnimateDiff", model_name)
|
||||||
|
|
||||||
|
|
||||||
|
def get_lora_path(lora_name):
|
||||||
|
return folder_paths.get_full_path("AnimateDiffLora", lora_name)
|
||||||
|
|
||||||
|
|
||||||
def get_model_hash(file_path):
|
def get_model_hash(file_path):
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
bytes = f.read() # read entire file as bytes
|
bytes = f.read(1024 * 1024) # read entire file as bytes
|
||||||
return hashlib.sha256(bytes).hexdigest()
|
return hashlib.sha256(bytes).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def load_motion_module(model_name: str):
|
||||||
|
model_path = get_model_path(model_name)
|
||||||
|
model_hash = get_model_hash(model_path)
|
||||||
|
if model_hash not in motion_modules:
|
||||||
|
logger.info(f"Loading motion module {model_name}")
|
||||||
|
mm_state_dict = load_torch_file(model_path)
|
||||||
|
motion_module = MotionWrapper.from_state_dict(mm_state_dict, model_name)
|
||||||
|
|
||||||
|
params = calculate_parameters(mm_state_dict, "")
|
||||||
|
if model_management.should_use_fp16(model_params=params):
|
||||||
|
logger.info(f"Converting motion module to fp16.")
|
||||||
|
motion_module.half()
|
||||||
|
offload_device = model_management.unet_offload_device()
|
||||||
|
motion_module = motion_module.to(offload_device)
|
||||||
|
|
||||||
|
motion_modules[model_hash] = motion_module
|
||||||
|
|
||||||
|
return motion_modules[model_hash]
|
||||||
|
|
||||||
|
|
||||||
|
def load_lora(lora_name: str):
|
||||||
|
lora_path = get_lora_path(lora_name)
|
||||||
|
lora_hash = get_model_hash(lora_path)
|
||||||
|
if lora_hash not in motion_modules:
|
||||||
|
logger.info(f"Loading lora {lora_name}")
|
||||||
|
state_dict = load_torch_file(lora_path)
|
||||||
|
updated_state_dict: Dict[str, torch.Tensor] = {}
|
||||||
|
|
||||||
|
for key in state_dict:
|
||||||
|
# only process lora down key
|
||||||
|
if "up." in key:
|
||||||
|
continue
|
||||||
|
|
||||||
|
up_key = key.replace(".down.", ".up.")
|
||||||
|
model_key = key.replace("processor.", "").replace("_lora", "").replace("down.", "").replace("up.", "")
|
||||||
|
model_key = model_key.replace("to_out.", "to_out.0.")
|
||||||
|
combined_key = ".".join(model_key.split(".")[:-1])
|
||||||
|
|
||||||
|
weight_down = state_dict[key]
|
||||||
|
weight_up = state_dict[up_key]
|
||||||
|
updated_state_dict[combined_key] = torch.mm(weight_up, weight_down).to("cpu")
|
||||||
|
|
||||||
|
motion_loras[lora_hash] = updated_state_dict
|
||||||
|
|
||||||
|
return motion_loras[lora_hash]
|
||||||
|
|||||||
@@ -1,12 +1,35 @@
|
|||||||
import os
|
|
||||||
import torch
|
import torch
|
||||||
from torch import Tensor, nn
|
from torch import Tensor, nn
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from einops import rearrange, repeat
|
from einops import rearrange, repeat
|
||||||
|
|
||||||
from comfy.utils import load_torch_file
|
import comfy.model_management as model_management
|
||||||
from comfy.ldm.modules.attention import FeedForward, CrossAttention
|
from comfy.ldm.modules.attention import (
|
||||||
|
default,
|
||||||
|
FeedForward,
|
||||||
|
CrossAttention as ComfyCrossAttention,
|
||||||
|
attention_basic,
|
||||||
|
attention_pytorch,
|
||||||
|
attention_split,
|
||||||
|
attention_sub_quad,
|
||||||
|
)
|
||||||
|
from comfy.cli_args import args
|
||||||
|
|
||||||
|
from .logger import logger
|
||||||
|
|
||||||
|
attention = attention_basic
|
||||||
|
|
||||||
|
if model_management.xformers_enabled():
|
||||||
|
logger.warn("xformers is enabled but it has a bug that can cause issue while using with AnimateDiff.")
|
||||||
|
|
||||||
|
if model_management.pytorch_attention_enabled():
|
||||||
|
attention = attention_pytorch
|
||||||
|
else:
|
||||||
|
if args.use_split_cross_attention:
|
||||||
|
attention = attention_split
|
||||||
|
else:
|
||||||
|
attention = attention_sub_quad
|
||||||
|
|
||||||
|
|
||||||
def zero_module(module):
|
def zero_module(module):
|
||||||
@@ -33,6 +56,24 @@ def has_mid_block(mm_state_dict: dict[str, Tensor]):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class CrossAttention(ComfyCrossAttention):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def forward(self, x, context=None, value=None, mask=None):
|
||||||
|
q = self.to_q(x)
|
||||||
|
context = default(context, x)
|
||||||
|
k = self.to_k(context)
|
||||||
|
if value is not None:
|
||||||
|
v = self.to_v(value)
|
||||||
|
del value
|
||||||
|
else:
|
||||||
|
v = self.to_v(context)
|
||||||
|
|
||||||
|
out = attention(q, k, v, self.heads, mask)
|
||||||
|
return self.to_out(out)
|
||||||
|
|
||||||
|
|
||||||
class MotionWrapper(nn.Module):
|
class MotionWrapper(nn.Module):
|
||||||
def __init__(self, mm_type: str, encoding_max_len: int = 24, is_v2=False):
|
def __init__(self, mm_type: str, encoding_max_len: int = 24, is_v2=False):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -42,29 +83,22 @@ class MotionWrapper(nn.Module):
|
|||||||
self.down_blocks = nn.ModuleList([])
|
self.down_blocks = nn.ModuleList([])
|
||||||
self.up_blocks = nn.ModuleList([])
|
self.up_blocks = nn.ModuleList([])
|
||||||
self.mid_block = None
|
self.mid_block = None
|
||||||
|
self.encoding_max_len = encoding_max_len
|
||||||
|
|
||||||
for c in (320, 640, 1280, 1280):
|
for c in (320, 640, 1280, 1280):
|
||||||
self.down_blocks.append(
|
self.down_blocks.append(MotionModule(c, BlockType.DOWN, encoding_max_len=encoding_max_len))
|
||||||
MotionModule(c, BlockType.DOWN, encoding_max_len=encoding_max_len)
|
|
||||||
)
|
|
||||||
for c in (1280, 1280, 640, 320):
|
for c in (1280, 1280, 640, 320):
|
||||||
self.up_blocks.append(
|
self.up_blocks.append(MotionModule(c, BlockType.UP, encoding_max_len=encoding_max_len))
|
||||||
MotionModule(c, BlockType.UP, encoding_max_len=encoding_max_len)
|
|
||||||
)
|
|
||||||
if is_v2:
|
if is_v2:
|
||||||
self.mid_block = MotionModule(
|
self.mid_block = MotionModule(1280, BlockType.MID, encoding_max_len=encoding_max_len)
|
||||||
1280, BlockType.MID, encoding_max_len=encoding_max_len
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_pretrained(cls, checkpoint_path: str):
|
def from_state_dict(cls, mm_state_dict: dict[str, Tensor], mm_type: str):
|
||||||
mm_state_dict = load_torch_file(checkpoint_path)
|
|
||||||
mm_type = os.path.basename(checkpoint_path)
|
|
||||||
encoding_max_len = get_encoding_max_len(mm_state_dict)
|
encoding_max_len = get_encoding_max_len(mm_state_dict)
|
||||||
is_v2 = has_mid_block(mm_state_dict)
|
is_v2 = has_mid_block(mm_state_dict)
|
||||||
|
|
||||||
mm = cls(mm_type, encoding_max_len=encoding_max_len, is_v2=is_v2)
|
mm = cls(mm_type, encoding_max_len=encoding_max_len, is_v2=is_v2)
|
||||||
mm.load_state_dict(mm_state_dict)
|
mm.load_state_dict(mm_state_dict, strict=False)
|
||||||
return mm
|
return mm
|
||||||
|
|
||||||
def set_video_length(self, video_length: int):
|
def set_video_length(self, video_length: int):
|
||||||
@@ -93,9 +127,7 @@ class MotionModule(nn.Module):
|
|||||||
self.block_type = block_type
|
self.block_type = block_type
|
||||||
|
|
||||||
if block_type == BlockType.MID:
|
if block_type == BlockType.MID:
|
||||||
self.motion_modules = nn.ModuleList(
|
self.motion_modules = nn.ModuleList([get_motion_module(in_channels, encoding_max_len)])
|
||||||
[get_motion_module(in_channels, encoding_max_len)]
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.motion_modules = nn.ModuleList(
|
self.motion_modules = nn.ModuleList(
|
||||||
[
|
[
|
||||||
@@ -104,9 +136,7 @@ class MotionModule(nn.Module):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
if block_type == BlockType.UP:
|
if block_type == BlockType.UP:
|
||||||
self.motion_modules.append(
|
self.motion_modules.append(get_motion_module(in_channels, encoding_max_len))
|
||||||
get_motion_module(in_channels, encoding_max_len)
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_video_length(self, video_length: int):
|
def set_video_length(self, video_length: int):
|
||||||
for motion_module in self.motion_modules:
|
for motion_module in self.motion_modules:
|
||||||
@@ -114,9 +144,7 @@ class MotionModule(nn.Module):
|
|||||||
|
|
||||||
|
|
||||||
def get_motion_module(in_channels, max_len):
|
def get_motion_module(in_channels, max_len):
|
||||||
return VanillaTemporalModule(
|
return VanillaTemporalModule(in_channels=in_channels, temporal_position_encoding_max_len=max_len)
|
||||||
in_channels=in_channels, temporal_position_encoding_max_len=max_len
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class VanillaTemporalModule(nn.Module):
|
class VanillaTemporalModule(nn.Module):
|
||||||
@@ -137,9 +165,7 @@ class VanillaTemporalModule(nn.Module):
|
|||||||
self.temporal_transformer = TemporalTransformer3DModel(
|
self.temporal_transformer = TemporalTransformer3DModel(
|
||||||
in_channels=in_channels,
|
in_channels=in_channels,
|
||||||
num_attention_heads=num_attention_heads,
|
num_attention_heads=num_attention_heads,
|
||||||
attention_head_dim=in_channels
|
attention_head_dim=in_channels // num_attention_heads // temporal_attention_dim_div,
|
||||||
// num_attention_heads
|
|
||||||
// temporal_attention_dim_div,
|
|
||||||
num_layers=num_transformer_block,
|
num_layers=num_transformer_block,
|
||||||
attention_block_types=attention_block_types,
|
attention_block_types=attention_block_types,
|
||||||
cross_frame_attention_mode=cross_frame_attention_mode,
|
cross_frame_attention_mode=cross_frame_attention_mode,
|
||||||
@@ -148,17 +174,13 @@ class VanillaTemporalModule(nn.Module):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if zero_initialize:
|
if zero_initialize:
|
||||||
self.temporal_transformer.proj_out = zero_module(
|
self.temporal_transformer.proj_out = zero_module(self.temporal_transformer.proj_out)
|
||||||
self.temporal_transformer.proj_out
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_video_length(self, video_length: int):
|
def set_video_length(self, video_length: int):
|
||||||
self.temporal_transformer.set_video_length(video_length)
|
self.temporal_transformer.set_video_length(video_length)
|
||||||
|
|
||||||
def forward(self, input_tensor, encoder_hidden_states, attention_mask=None):
|
def forward(self, input_tensor, encoder_hidden_states=None, attention_mask=None):
|
||||||
return self.temporal_transformer(
|
return self.temporal_transformer(input_tensor, encoder_hidden_states, attention_mask)
|
||||||
input_tensor, encoder_hidden_states, attention_mask
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TemporalTransformer3DModel(nn.Module):
|
class TemporalTransformer3DModel(nn.Module):
|
||||||
@@ -186,9 +208,7 @@ class TemporalTransformer3DModel(nn.Module):
|
|||||||
|
|
||||||
inner_dim = num_attention_heads * attention_head_dim
|
inner_dim = num_attention_heads * attention_head_dim
|
||||||
|
|
||||||
self.norm = torch.nn.GroupNorm(
|
self.norm = torch.nn.GroupNorm(num_groups=norm_num_groups, num_channels=in_channels, eps=1e-6, affine=True)
|
||||||
num_groups=norm_num_groups, num_channels=in_channels, eps=1e-6, affine=True
|
|
||||||
)
|
|
||||||
self.proj_in = nn.Linear(in_channels, inner_dim)
|
self.proj_in = nn.Linear(in_channels, inner_dim)
|
||||||
|
|
||||||
self.transformer_blocks = nn.ModuleList(
|
self.transformer_blocks = nn.ModuleList(
|
||||||
@@ -223,9 +243,7 @@ class TemporalTransformer3DModel(nn.Module):
|
|||||||
|
|
||||||
hidden_states = self.norm(hidden_states)
|
hidden_states = self.norm(hidden_states)
|
||||||
inner_dim = hidden_states.shape[1]
|
inner_dim = hidden_states.shape[1]
|
||||||
hidden_states = hidden_states.permute(0, 2, 3, 1).reshape(
|
hidden_states = hidden_states.permute(0, 2, 3, 1).reshape(batch, height * weight, inner_dim)
|
||||||
batch, height * weight, inner_dim
|
|
||||||
)
|
|
||||||
hidden_states = self.proj_in(hidden_states)
|
hidden_states = self.proj_in(hidden_states)
|
||||||
|
|
||||||
# Transformer Blocks
|
# Transformer Blocks
|
||||||
@@ -238,11 +256,7 @@ class TemporalTransformer3DModel(nn.Module):
|
|||||||
|
|
||||||
# output
|
# output
|
||||||
hidden_states = self.proj_out(hidden_states)
|
hidden_states = self.proj_out(hidden_states)
|
||||||
hidden_states = (
|
hidden_states = hidden_states.reshape(batch, height, weight, inner_dim).permute(0, 3, 1, 2).contiguous()
|
||||||
hidden_states.reshape(batch, height, weight, inner_dim)
|
|
||||||
.permute(0, 3, 1, 2)
|
|
||||||
.contiguous()
|
|
||||||
)
|
|
||||||
|
|
||||||
output = hidden_states + residual
|
output = hidden_states + residual
|
||||||
|
|
||||||
@@ -278,9 +292,7 @@ class TemporalTransformerBlock(nn.Module):
|
|||||||
attention_blocks.append(
|
attention_blocks.append(
|
||||||
VersatileAttention(
|
VersatileAttention(
|
||||||
attention_mode=block_name.split("_")[0],
|
attention_mode=block_name.split("_")[0],
|
||||||
context_dim=cross_attention_dim
|
context_dim=cross_attention_dim if block_name.endswith("_Cross") else None,
|
||||||
if block_name.endswith("_Cross")
|
|
||||||
else None,
|
|
||||||
query_dim=dim,
|
query_dim=dim,
|
||||||
heads=num_attention_heads,
|
heads=num_attention_heads,
|
||||||
dim_head=attention_head_dim,
|
dim_head=attention_head_dim,
|
||||||
@@ -312,9 +324,7 @@ class TemporalTransformerBlock(nn.Module):
|
|||||||
hidden_states = (
|
hidden_states = (
|
||||||
attention_block(
|
attention_block(
|
||||||
norm_hidden_states,
|
norm_hidden_states,
|
||||||
encoder_hidden_states=encoder_hidden_states
|
encoder_hidden_states=encoder_hidden_states if attention_block.is_cross_attention else None,
|
||||||
if attention_block.is_cross_attention
|
|
||||||
else None,
|
|
||||||
video_length=video_length,
|
video_length=video_length,
|
||||||
)
|
)
|
||||||
+ hidden_states
|
+ hidden_states
|
||||||
@@ -331,9 +341,7 @@ class PositionalEncoding(nn.Module):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.dropout = nn.Dropout(p=dropout)
|
self.dropout = nn.Dropout(p=dropout)
|
||||||
position = torch.arange(max_len).unsqueeze(1)
|
position = torch.arange(max_len).unsqueeze(1)
|
||||||
div_term = torch.exp(
|
div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
|
||||||
torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model)
|
|
||||||
)
|
|
||||||
pe = torch.zeros(1, max_len, d_model)
|
pe = torch.zeros(1, max_len, d_model)
|
||||||
pe[0, :, 0::2] = torch.sin(position * div_term)
|
pe[0, :, 0::2] = torch.sin(position * div_term)
|
||||||
pe[0, :, 1::2] = torch.cos(position * div_term)
|
pe[0, :, 1::2] = torch.cos(position * div_term)
|
||||||
@@ -385,9 +393,7 @@ class VersatileAttention(CrossAttention):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
d = hidden_states.shape[1]
|
d = hidden_states.shape[1]
|
||||||
hidden_states = rearrange(
|
hidden_states = rearrange(hidden_states, "(b f) d c -> (b d) f c", f=video_length)
|
||||||
hidden_states, "(b f) d c -> (b d) f c", f=video_length
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.pos_encoder is not None:
|
if self.pos_encoder is not None:
|
||||||
hidden_states = self.pos_encoder(hidden_states)
|
hidden_states = self.pos_encoder(hidden_states)
|
||||||
|
|||||||
+291
-292
@@ -2,168 +2,25 @@ import os
|
|||||||
import json
|
import json
|
||||||
import torch
|
import torch
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from typing import Dict, List
|
import hashlib
|
||||||
|
from typing import List, Dict, Tuple
|
||||||
from torch import Tensor
|
from torch import Tensor
|
||||||
from torch.nn.functional import group_norm
|
from PIL import Image, ImageSequence
|
||||||
from PIL import Image
|
|
||||||
from PIL.PngImagePlugin import PngInfo
|
from PIL.PngImagePlugin import PngInfo
|
||||||
from einops import rearrange
|
|
||||||
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
import comfy.ldm.modules.diffusionmodules.openaimodel as openaimodel
|
|
||||||
import comfy.model_management as model_management
|
|
||||||
from comfy.model_base import BaseModel
|
|
||||||
from comfy.ldm.modules.attention import SpatialTransformer
|
|
||||||
from comfy.cli_args import args as cli_args
|
|
||||||
from nodes import KSampler
|
|
||||||
|
|
||||||
|
from .motion_module import MotionWrapper
|
||||||
|
from .model_utils import get_available_models, load_motion_module, get_available_loras, load_lora
|
||||||
|
from .utils import pil2tensor, ensure_opencv
|
||||||
|
from .sampler import AnimateDiffSampler, AnimateDiffSlidingWindowOptions
|
||||||
from .logger import logger
|
from .logger import logger
|
||||||
from .motion_module import MotionWrapper, VanillaTemporalModule
|
|
||||||
from .model_utils import get_available_models, get_model_path, get_model_hash
|
|
||||||
|
|
||||||
|
|
||||||
def forward_timestep_embed(
|
SLIDING_CONTEXT_LENGTH = 16
|
||||||
ts, x, emb, context=None, transformer_options={}, output_shape=None
|
|
||||||
):
|
|
||||||
for layer in ts:
|
|
||||||
if isinstance(layer, openaimodel.TimestepBlock):
|
|
||||||
x = layer(x, emb)
|
|
||||||
elif isinstance(layer, VanillaTemporalModule):
|
|
||||||
x = layer(x, context)
|
|
||||||
elif isinstance(layer, SpatialTransformer):
|
|
||||||
x = layer(x, context, transformer_options)
|
|
||||||
transformer_options["current_index"] += 1
|
|
||||||
elif isinstance(layer, openaimodel.Upsample):
|
|
||||||
x = layer(x, output_shape=output_shape)
|
|
||||||
else:
|
|
||||||
x = layer(x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
|
video_formats_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "video_formats")
|
||||||
def groupnorm_mm_factory(video_length: int):
|
video_formats = ["video/" + x[:-5] for x in os.listdir(video_formats_dir)]
|
||||||
def groupnorm_mm_forward(self, input: Tensor) -> Tensor:
|
|
||||||
# axes_factor normalizes batch based on total conds and unconds passed in batch;
|
|
||||||
# the conds and unconds per batch can change based on VRAM optimizations that may kick in
|
|
||||||
axes_factor = input.size(0) // video_length
|
|
||||||
|
|
||||||
input = rearrange(input, "(b f) c h w -> b c f h w", b=axes_factor)
|
|
||||||
input = group_norm(input, self.num_groups, self.weight, self.bias, self.eps)
|
|
||||||
input = rearrange(input, "b c f h w -> (b f) c h w", b=axes_factor)
|
|
||||||
return input
|
|
||||||
|
|
||||||
return groupnorm_mm_forward
|
|
||||||
|
|
||||||
|
|
||||||
orig_forward_timestep_embed = openaimodel.forward_timestep_embed
|
|
||||||
orig_maximum_batch_area = model_management.maximum_batch_area
|
|
||||||
orig_groupnorm_forward = torch.nn.GroupNorm.forward
|
|
||||||
openaimodel.forward_timestep_embed = forward_timestep_embed
|
|
||||||
|
|
||||||
motion_modules: Dict[str, MotionWrapper] = {}
|
|
||||||
|
|
||||||
|
|
||||||
def load_motion_module(model_name: str):
|
|
||||||
model_path = get_model_path(model_name)
|
|
||||||
model_hash = get_model_hash(model_path)
|
|
||||||
if model_hash not in motion_modules:
|
|
||||||
logger.info(f"Loading motion module {model_name}")
|
|
||||||
motion_module = MotionWrapper.from_pretrained(model_path)
|
|
||||||
if not cli_args.force_fp32:
|
|
||||||
logger.info(f"Converting motion module to fp16.")
|
|
||||||
motion_module.half()
|
|
||||||
|
|
||||||
motion_modules[model_hash] = motion_module
|
|
||||||
|
|
||||||
return motion_modules[model_hash]
|
|
||||||
|
|
||||||
|
|
||||||
def inject_motion_module_to_unet_legacy(unet, motion_module: MotionWrapper):
|
|
||||||
for mm_idx, unet_idx in enumerate([1, 2, 4, 5, 7, 8, 10, 11]):
|
|
||||||
mm_idx0, mm_idx1 = mm_idx // 2, mm_idx % 2
|
|
||||||
unet.input_blocks[unet_idx].append(
|
|
||||||
motion_module.down_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
|
|
||||||
for unet_idx in range(12):
|
|
||||||
mm_idx0, mm_idx1 = unet_idx // 3, unet_idx % 3
|
|
||||||
if unet_idx % 2 == 2:
|
|
||||||
unet.output_blocks[unet_idx].insert(
|
|
||||||
-1, motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
unet.output_blocks[unet_idx].append(
|
|
||||||
motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
if motion_module.is_v2:
|
|
||||||
unet.middle_block.insert(-1, motion_module.mid_block.motion_modules[0])
|
|
||||||
|
|
||||||
unet.motion_module = motion_module
|
|
||||||
|
|
||||||
|
|
||||||
def eject_motion_module_from_unet_legacy(unet):
|
|
||||||
for unet_idx in [1, 2, 4, 5, 7, 8, 10, 11]:
|
|
||||||
unet.input_blocks[unet_idx].pop(-1)
|
|
||||||
|
|
||||||
for unet_idx in range(12):
|
|
||||||
if unet_idx % 2 == 2:
|
|
||||||
unet.output_blocks[unet_idx].pop(-2)
|
|
||||||
else:
|
|
||||||
unet.output_blocks[unet_idx].pop(-1)
|
|
||||||
|
|
||||||
if unet.motion_module.is_v2:
|
|
||||||
unet.middle_block.pop(-2)
|
|
||||||
|
|
||||||
del unet.motion_module
|
|
||||||
|
|
||||||
|
|
||||||
def inject_motion_module_to_unet(unet, motion_module: MotionWrapper):
|
|
||||||
for mm_idx, unet_idx in enumerate([1, 2, 4, 5, 7, 8, 10, 11]):
|
|
||||||
mm_idx0, mm_idx1 = mm_idx // 2, mm_idx % 2
|
|
||||||
unet.input_blocks[unet_idx].append(
|
|
||||||
motion_module.down_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
|
|
||||||
for unet_idx in range(12):
|
|
||||||
mm_idx0, mm_idx1 = unet_idx // 3, unet_idx % 3
|
|
||||||
if unet_idx % 3 == 2 and unet_idx != 11:
|
|
||||||
unet.output_blocks[unet_idx].insert(
|
|
||||||
-1, motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
unet.output_blocks[unet_idx].append(
|
|
||||||
motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1]
|
|
||||||
)
|
|
||||||
if motion_module.is_v2:
|
|
||||||
unet.middle_block.insert(-1, motion_module.mid_block.motion_modules[0])
|
|
||||||
|
|
||||||
unet.motion_module = motion_module
|
|
||||||
|
|
||||||
|
|
||||||
def eject_motion_module_from_unet(unet):
|
|
||||||
for unet_idx in [1, 2, 4, 5, 7, 8, 10, 11]:
|
|
||||||
unet.input_blocks[unet_idx].pop(-1)
|
|
||||||
|
|
||||||
for unet_idx in range(12):
|
|
||||||
if unet_idx % 3 == 2 and unet_idx != 11:
|
|
||||||
unet.output_blocks[unet_idx].pop(-2)
|
|
||||||
else:
|
|
||||||
unet.output_blocks[unet_idx].pop(-1)
|
|
||||||
|
|
||||||
if unet.motion_module.is_v2:
|
|
||||||
unet.middle_block.pop(-2)
|
|
||||||
|
|
||||||
del unet.motion_module
|
|
||||||
|
|
||||||
|
|
||||||
injectors = {
|
|
||||||
"legacy": inject_motion_module_to_unet_legacy,
|
|
||||||
"default": inject_motion_module_to_unet,
|
|
||||||
}
|
|
||||||
|
|
||||||
ejectors = {
|
|
||||||
"legacy": eject_motion_module_from_unet_legacy,
|
|
||||||
"default": eject_motion_module_from_unet,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class AnimateDiffModuleLoader:
|
class AnimateDiffModuleLoader:
|
||||||
@@ -173,146 +30,97 @@ class AnimateDiffModuleLoader:
|
|||||||
"required": {
|
"required": {
|
||||||
"model_name": (get_available_models(),),
|
"model_name": (get_available_models(),),
|
||||||
},
|
},
|
||||||
|
"optional": {
|
||||||
|
"lora_stack": ("MOTION_LORA_STACK",),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("MOTION_MODULE",)
|
RETURN_TYPES = ("MOTION_MODULE",)
|
||||||
CATEGORY = "Animate Diff"
|
CATEGORY = "Animate Diff"
|
||||||
FUNCTION = "load_motion_module"
|
FUNCTION = "load_motion_module"
|
||||||
|
|
||||||
|
def inject_loras(self, motion_module: MotionWrapper, lora_stack: List[Tuple[Dict[str, Tensor], float]]):
|
||||||
|
for lora in lora_stack:
|
||||||
|
(state_dict, alpha) = lora
|
||||||
|
|
||||||
|
for key in state_dict:
|
||||||
|
layer_infos = key.split(".")
|
||||||
|
|
||||||
|
curr_layer = motion_module
|
||||||
|
while len(layer_infos) > 0:
|
||||||
|
temp_name = layer_infos.pop(0)
|
||||||
|
curr_layer = curr_layer.__getattr__(temp_name)
|
||||||
|
|
||||||
|
curr_layer.weight.data += alpha * state_dict[key].to(curr_layer.weight.data.device)
|
||||||
|
|
||||||
|
def eject_loras(self, motion_module: MotionWrapper, lora_stack: List[Tuple[float, Dict[str, Tensor]]]):
|
||||||
|
lora_stack.reverse() # should not matter but just in case
|
||||||
|
for lora in lora_stack:
|
||||||
|
(state_dict, alpha) = lora
|
||||||
|
|
||||||
|
for key in state_dict:
|
||||||
|
layer_infos = key.split(".")
|
||||||
|
|
||||||
|
curr_layer = motion_module
|
||||||
|
while len(layer_infos) > 0:
|
||||||
|
temp_name = layer_infos.pop(0)
|
||||||
|
curr_layer = curr_layer.__getattr__(temp_name)
|
||||||
|
|
||||||
|
curr_layer.weight.data -= alpha * state_dict[key].to(curr_layer.weight.data.device)
|
||||||
|
|
||||||
def load_motion_module(
|
def load_motion_module(
|
||||||
self,
|
self,
|
||||||
model_name: str,
|
model_name: str,
|
||||||
|
lora_stack: List = None,
|
||||||
):
|
):
|
||||||
motion_module = load_motion_module(model_name)
|
motion_module = load_motion_module(model_name)
|
||||||
|
|
||||||
|
# inject loras
|
||||||
|
if motion_module.is_v2:
|
||||||
|
if hasattr(motion_module, "lora_stack") and isinstance(motion_module.lora_stack, list):
|
||||||
|
self.eject_loras(motion_module, motion_module.lora_stack)
|
||||||
|
delattr(motion_module, "lora_stack")
|
||||||
|
|
||||||
|
if isinstance(lora_stack, list):
|
||||||
|
self.inject_loras(motion_module, lora_stack)
|
||||||
|
setattr(motion_module, "lora_stack", lora_stack)
|
||||||
|
|
||||||
|
elif isinstance(lora_stack, list):
|
||||||
|
logger.warning("LoRA is provided but only motion module v2 is supported.")
|
||||||
|
|
||||||
return (motion_module,)
|
return (motion_module,)
|
||||||
|
|
||||||
|
|
||||||
class AnimateDiffSampler(KSampler):
|
class AnimateDiffLoraLoader:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
inputs = {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"motion_module": ("MOTION_MODULE",),
|
"lora_name": (get_available_loras(),),
|
||||||
"inject_method": (["default", "legacy"],),
|
"alpha": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}),
|
||||||
"frame_number": (
|
},
|
||||||
"INT",
|
"optional": {
|
||||||
{"default": 16, "min": 2, "max": 32, "step": 1},
|
"lora_stack": ("MOTION_LORA_STACK",),
|
||||||
),
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
inputs["required"].update(KSampler.INPUT_TYPES()["required"])
|
|
||||||
return inputs
|
|
||||||
|
|
||||||
FUNCTION = "animatediff_sample"
|
RETURN_TYPES = ("MOTION_LORA_STACK",)
|
||||||
CATEGORY = "Animate Diff"
|
CATEGORY = "Animate Diff"
|
||||||
|
FUNCTION = "load_lora"
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def load_lora(
|
||||||
super().__init__()
|
|
||||||
self.prev_beta = None
|
|
||||||
self.prev_linear_start = None
|
|
||||||
self.prev_linear_end = None
|
|
||||||
|
|
||||||
def override_beta_schedule(self, model: BaseModel):
|
|
||||||
logger.info(f"Override beta schedule.")
|
|
||||||
self.prev_beta = model.get_buffer("betas")
|
|
||||||
self.prev_linear_start = model.linear_start
|
|
||||||
self.prev_linear_end = model.linear_end
|
|
||||||
model.register_schedule(
|
|
||||||
given_betas=None,
|
|
||||||
beta_schedule="sqrt_linear",
|
|
||||||
timesteps=1000,
|
|
||||||
linear_start=0.00085,
|
|
||||||
linear_end=0.012,
|
|
||||||
cosine_s=8e-3,
|
|
||||||
)
|
|
||||||
|
|
||||||
def restore_beta_schedule(self, model: BaseModel):
|
|
||||||
logger.info(f"Restoring beta schedule.")
|
|
||||||
model.register_schedule(
|
|
||||||
given_betas=self.prev_beta,
|
|
||||||
linear_start=self.prev_linear_start,
|
|
||||||
linear_end=self.prev_linear_end,
|
|
||||||
)
|
|
||||||
self.prev_beta = None
|
|
||||||
self.prev_linear_start = None
|
|
||||||
self.prev_linear_end = None
|
|
||||||
|
|
||||||
def inject_motion_module(
|
|
||||||
self, model, motion_module: MotionWrapper, inject_method: str, frame_number: int
|
|
||||||
):
|
|
||||||
model = model.clone()
|
|
||||||
unet = model.model.diffusion_model
|
|
||||||
|
|
||||||
logger.info(f"Injecting motion module with method {inject_method}.")
|
|
||||||
injectors[inject_method](unet, motion_module)
|
|
||||||
self.override_beta_schedule(model.model)
|
|
||||||
if not motion_module.is_v2:
|
|
||||||
logger.info(f"Hacking GroupNorm.forward function.")
|
|
||||||
torch.nn.GroupNorm.forward = groupnorm_mm_factory(frame_number)
|
|
||||||
|
|
||||||
return model
|
|
||||||
|
|
||||||
def eject_motion_module(self, model, inject_method):
|
|
||||||
unet = model.model.diffusion_model
|
|
||||||
|
|
||||||
self.restore_beta_schedule(model.model)
|
|
||||||
if not unet.motion_module.is_v2:
|
|
||||||
logger.info(f"Restore GroupNorm32 forward function.")
|
|
||||||
torch.nn.GroupNorm.forward = orig_groupnorm_forward
|
|
||||||
|
|
||||||
logger.info(f"Ejecting motion module with method {inject_method}.")
|
|
||||||
ejectors[inject_method](unet)
|
|
||||||
|
|
||||||
def animatediff_sample(
|
|
||||||
self,
|
self,
|
||||||
motion_module,
|
lora_name: str,
|
||||||
inject_method,
|
alpha: float,
|
||||||
frame_number,
|
lora_stack: List = None,
|
||||||
model,
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
cfg,
|
|
||||||
sampler_name,
|
|
||||||
scheduler,
|
|
||||||
positive,
|
|
||||||
negative,
|
|
||||||
latent_image,
|
|
||||||
denoise=1.0,
|
|
||||||
):
|
):
|
||||||
model = self.inject_motion_module(
|
if not lora_stack:
|
||||||
model, motion_module, inject_method, frame_number
|
lora_stack = []
|
||||||
)
|
|
||||||
|
|
||||||
init_frames = len(latent_image["samples"])
|
lora = load_lora(lora_name)
|
||||||
samples = latent_image["samples"][:init_frames, :, :, :].clone().cpu()
|
lora_stack.append((lora, alpha))
|
||||||
|
|
||||||
if init_frames < frame_number:
|
return (lora_stack,)
|
||||||
last_frame = samples[-1].unsqueeze(0)
|
|
||||||
repeated_last_frames = last_frame.repeat(
|
|
||||||
frame_number - init_frames, 1, 1, 1
|
|
||||||
)
|
|
||||||
samples = torch.cat((samples, repeated_last_frames), dim=0)
|
|
||||||
|
|
||||||
latent_image = {"samples": samples}
|
|
||||||
|
|
||||||
try:
|
|
||||||
return super().sample(
|
|
||||||
model,
|
|
||||||
seed,
|
|
||||||
steps,
|
|
||||||
cfg,
|
|
||||||
sampler_name,
|
|
||||||
scheduler,
|
|
||||||
positive,
|
|
||||||
negative,
|
|
||||||
latent_image,
|
|
||||||
denoise=denoise,
|
|
||||||
)
|
|
||||||
except:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
self.eject_motion_module(model, inject_method)
|
|
||||||
|
|
||||||
|
|
||||||
class AnimateDiffCombine:
|
class AnimateDiffCombine:
|
||||||
@@ -326,8 +134,10 @@ class AnimateDiffCombine:
|
|||||||
{"default": 8, "min": 1, "max": 24, "step": 1},
|
{"default": 8, "min": 1, "max": 24, "step": 1},
|
||||||
),
|
),
|
||||||
"loop_count": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}),
|
"loop_count": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}),
|
||||||
"save_image": (["Enabled", "Disabled"],),
|
"save_image": ("BOOLEAN", {"default": True}),
|
||||||
"filename_prefix": ("STRING", {"default": "AnimateDiff"}),
|
"filename_prefix": ("STRING", {"default": "animate_diff"}),
|
||||||
|
"format": (["image/gif", "image/webp"] + video_formats,),
|
||||||
|
"pingpong": ("BOOLEAN", {"default": False}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"hidden": {
|
||||||
"prompt": "PROMPT",
|
"prompt": "PROMPT",
|
||||||
@@ -345,24 +155,22 @@ class AnimateDiffCombine:
|
|||||||
images,
|
images,
|
||||||
frame_rate: int,
|
frame_rate: int,
|
||||||
loop_count: int,
|
loop_count: int,
|
||||||
save_image="Enabled",
|
save_image=True,
|
||||||
filename_prefix="AnimateDiff",
|
filename_prefix="AnimateDiff",
|
||||||
|
format="image/gif",
|
||||||
|
pingpong=False,
|
||||||
prompt=None,
|
prompt=None,
|
||||||
extra_pnginfo=None,
|
extra_pnginfo=None,
|
||||||
):
|
):
|
||||||
# convert images to numpy
|
# convert images to numpy
|
||||||
pil_images: List[Image.Image] = []
|
frames: List[Image.Image] = []
|
||||||
for image in images:
|
for image in images:
|
||||||
img = 255.0 * image.cpu().numpy()
|
img = 255.0 * image.cpu().numpy()
|
||||||
img = Image.fromarray(np.clip(img, 0, 255).astype(np.uint8))
|
img = Image.fromarray(np.clip(img, 0, 255).astype(np.uint8))
|
||||||
pil_images.append(img)
|
frames.append(img)
|
||||||
|
|
||||||
# save image
|
# save image
|
||||||
output_dir = (
|
output_dir = folder_paths.get_output_directory() if save_image else folder_paths.get_temp_directory()
|
||||||
folder_paths.get_output_directory()
|
|
||||||
if save_image == "Enabled"
|
|
||||||
else folder_paths.get_temp_directory()
|
|
||||||
)
|
|
||||||
(
|
(
|
||||||
full_output_folder,
|
full_output_folder,
|
||||||
filename,
|
filename,
|
||||||
@@ -381,43 +189,234 @@ class AnimateDiffCombine:
|
|||||||
# save first frame as png to keep metadata
|
# save first frame as png to keep metadata
|
||||||
file = f"{filename}_{counter:05}_.png"
|
file = f"{filename}_{counter:05}_.png"
|
||||||
file_path = os.path.join(full_output_folder, file)
|
file_path = os.path.join(full_output_folder, file)
|
||||||
pil_images[0].save(
|
frames[0].save(
|
||||||
file_path,
|
file_path,
|
||||||
pnginfo=metadata,
|
pnginfo=metadata,
|
||||||
compress_level=4,
|
compress_level=4,
|
||||||
)
|
)
|
||||||
|
if pingpong:
|
||||||
|
frames = frames + frames[-2:0:-1]
|
||||||
|
|
||||||
# save gif
|
format_type, format_ext = format.split("/")
|
||||||
file = f"{filename}_{counter:05}_.gif"
|
|
||||||
file_path = os.path.join(full_output_folder, file)
|
|
||||||
pil_images[0].save(
|
|
||||||
file_path,
|
|
||||||
save_all=True,
|
|
||||||
append_images=pil_images[1:],
|
|
||||||
duration=round(1000 / frame_rate),
|
|
||||||
loop=loop_count,
|
|
||||||
compress_level=4,
|
|
||||||
)
|
|
||||||
|
|
||||||
print("Saved gif to", file_path, os.path.exists(file_path))
|
if format_type == "image":
|
||||||
|
file = f"{filename}_{counter:05}_.{format_ext}"
|
||||||
|
file_path = os.path.join(full_output_folder, file)
|
||||||
|
frames[0].save(
|
||||||
|
file_path,
|
||||||
|
format=format_ext.upper(),
|
||||||
|
save_all=True,
|
||||||
|
append_images=frames[1:],
|
||||||
|
duration=round(1000 / frame_rate),
|
||||||
|
loop=loop_count,
|
||||||
|
compress_level=4,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# save webm
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
ffmpeg_path = shutil.which("ffmpeg")
|
||||||
|
if ffmpeg_path is None:
|
||||||
|
raise ProcessLookupError("Could not find ffmpeg")
|
||||||
|
video_format_path = os.path.join(video_formats_dir, format_ext + ".json")
|
||||||
|
with open(video_format_path, "r") as stream:
|
||||||
|
video_format = json.load(stream)
|
||||||
|
file = f"{filename}_{counter:05}_.{video_format['extension']}"
|
||||||
|
file_path = os.path.join(full_output_folder, file)
|
||||||
|
dimensions = f"{frames[0].width}x{frames[0].height}"
|
||||||
|
args = (
|
||||||
|
[
|
||||||
|
ffmpeg_path,
|
||||||
|
"-v",
|
||||||
|
"error",
|
||||||
|
"-f",
|
||||||
|
"rawvideo",
|
||||||
|
"-pix_fmt",
|
||||||
|
"rgb24",
|
||||||
|
"-s",
|
||||||
|
dimensions,
|
||||||
|
"-r",
|
||||||
|
str(frame_rate),
|
||||||
|
"-i",
|
||||||
|
"-",
|
||||||
|
]
|
||||||
|
+ video_format["main_pass"]
|
||||||
|
+ [file_path]
|
||||||
|
)
|
||||||
|
|
||||||
|
env = os.environ
|
||||||
|
if "environment" in video_format:
|
||||||
|
env.update(video_format["environment"])
|
||||||
|
with subprocess.Popen(args, stdin=subprocess.PIPE, env=env) as proc:
|
||||||
|
for frame in frames:
|
||||||
|
proc.stdin.write(frame.tobytes())
|
||||||
|
|
||||||
previews = [
|
previews = [
|
||||||
{
|
{
|
||||||
"filename": file,
|
"filename": file,
|
||||||
"subfolder": subfolder,
|
"subfolder": subfolder,
|
||||||
"type": "output" if save_image == "Enabled" else "temp",
|
"type": "output" if save_image else "temp",
|
||||||
|
"format": format,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
return {"ui": {"images": previews}}
|
return {"ui": {"videos": previews}}
|
||||||
|
|
||||||
|
|
||||||
|
class LoadVideo:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
input_dir = os.path.join(folder_paths.get_input_directory(), "video")
|
||||||
|
if not os.path.exists(input_dir):
|
||||||
|
os.makedirs(input_dir, exist_ok=True)
|
||||||
|
|
||||||
|
files = [f"video/{f}" for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"video": (sorted(files), {"video_upload": True}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"frame_start": ("INT", {"default": 0, "min": 0, "max": 0xFFFFFFFF, "step": 1}),
|
||||||
|
"frame_limit": ("INT", {"default": 16, "min": 1, "max": 10240, "step": 1}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY = "Animate Diff/Utils"
|
||||||
|
RETURN_TYPES = ("IMAGE", "INT")
|
||||||
|
RETURN_NAMES = ("frames", "frame_count")
|
||||||
|
FUNCTION = "load"
|
||||||
|
|
||||||
|
def load_gif(self, gif_path: str, frame_start: int, frame_limit: int):
|
||||||
|
image = Image.open(gif_path)
|
||||||
|
frames = []
|
||||||
|
|
||||||
|
for i, frame in enumerate(ImageSequence.Iterator(image)):
|
||||||
|
if i < frame_start:
|
||||||
|
continue
|
||||||
|
elif i >= frame_start + frame_limit:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
frames.append(pil2tensor(frame.copy().convert("RGB")))
|
||||||
|
|
||||||
|
return frames
|
||||||
|
|
||||||
|
def load_video(self, video_path, frame_start: int, frame_limit: int):
|
||||||
|
ensure_opencv()
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
video = cv2.VideoCapture(video_path)
|
||||||
|
video.set(cv2.CAP_PROP_POS_FRAMES, frame_start)
|
||||||
|
|
||||||
|
frames = []
|
||||||
|
for i in range(frame_limit):
|
||||||
|
# Read the next frame
|
||||||
|
ret, frame = video.read()
|
||||||
|
if ret:
|
||||||
|
# Convert the frame to RGB (OpenCV uses BGR)
|
||||||
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||||
|
# Convert the NumPy array to a PIL image and append to list
|
||||||
|
frames.append(pil2tensor(Image.fromarray(frame)))
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
video.release()
|
||||||
|
|
||||||
|
return frames
|
||||||
|
|
||||||
|
def load(self, video: str, frame_start=0, frame_limit=16):
|
||||||
|
video_path = folder_paths.get_annotated_filepath(video)
|
||||||
|
(_, ext) = os.path.splitext(video_path)
|
||||||
|
|
||||||
|
if ext.lower() in {".gif", ".webp"}:
|
||||||
|
frames = self.load_gif(video_path, frame_start, frame_limit)
|
||||||
|
elif ext.lower() in {".webp", ".mp4", ".mov", ".avi", ".webm"}:
|
||||||
|
frames = self.load_video(video_path, frame_start, frame_limit)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported video format: {ext}")
|
||||||
|
|
||||||
|
return (torch.cat(frames, dim=0), len(frames))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def IS_CHANGED(s, image, *args, **kwargs):
|
||||||
|
image_path = folder_paths.get_annotated_filepath(image)
|
||||||
|
m = hashlib.sha256()
|
||||||
|
with open(image_path, "rb") as f:
|
||||||
|
m.update(f.read())
|
||||||
|
return m.digest().hex()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def VALIDATE_INPUTS(s, video, *args, **kwargs):
|
||||||
|
if not folder_paths.exists_annotated_filepath(video):
|
||||||
|
return "Invalid video file: {}".format(video)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSizeAndBatchSize:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY = "Animate Diff/Utils"
|
||||||
|
RETURN_TYPES = ("INT", "INT", "INT")
|
||||||
|
RETURN_NAMES = ("width", "height", "batch_size")
|
||||||
|
FUNCTION = "batch_size"
|
||||||
|
|
||||||
|
def batch_size(self, image: Tensor):
|
||||||
|
(batch_size, height, width) = image.shape[0:3]
|
||||||
|
return (width, height, batch_size)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageChunking:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"images": ("IMAGE",),
|
||||||
|
"chunk_size": ("INT", {"default": 16, "min": 1, "max": 1024, "step": 1}),
|
||||||
|
"allow_remainder": ("BOOLEAN", {"default": True}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CATEGORY = "Animate Diff/Utils"
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
FUNCTION = "chunk"
|
||||||
|
|
||||||
|
def chunk(self, images: Tensor, chunk_size: int, allow_remainder: bool):
|
||||||
|
# Check if tensor is divisible into chunks of chunk_size
|
||||||
|
if images.shape[0] % chunk_size != 0 and not allow_remainder:
|
||||||
|
raise ValueError("Tensor's first dimension is not divisible by chunk size")
|
||||||
|
|
||||||
|
# Use torch.chunk to divide the tensor
|
||||||
|
chunk_count = images.shape[0] // chunk_size + images.shape[0] % chunk_size
|
||||||
|
|
||||||
|
print("chunk_count", chunk_count)
|
||||||
|
chunks = torch.chunk(images, chunk_count, dim=0)
|
||||||
|
|
||||||
|
return (list(chunks),)
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"AnimateDiffModuleLoader": AnimateDiffModuleLoader,
|
"AnimateDiffModuleLoader": AnimateDiffModuleLoader,
|
||||||
|
"AnimateDiffLoraLoader": AnimateDiffLoraLoader,
|
||||||
"AnimateDiffCombine": AnimateDiffCombine,
|
"AnimateDiffCombine": AnimateDiffCombine,
|
||||||
"AnimateDiffSampler": AnimateDiffSampler,
|
"AnimateDiffSampler": AnimateDiffSampler,
|
||||||
|
"AnimateDiffSlidingWindowOptions": AnimateDiffSlidingWindowOptions,
|
||||||
|
"LoadVideo": LoadVideo,
|
||||||
|
"ImageSizeAndBatchSize": ImageSizeAndBatchSize,
|
||||||
}
|
}
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
"AnimateDiffModuleLoader": "Animate Diff Module Loader",
|
"AnimateDiffModuleLoader": "Animate Diff Module Loader",
|
||||||
|
"AnimateDiffLoraLoader": "Animate Diff Lora Loader",
|
||||||
"AnimateDiffSampler": "Animate Diff Sampler",
|
"AnimateDiffSampler": "Animate Diff Sampler",
|
||||||
|
"AnimateDiffSlidingWindowOptions": "Sliding Window Options",
|
||||||
"AnimateDiffCombine": "Animate Diff Combine",
|
"AnimateDiffCombine": "Animate Diff Combine",
|
||||||
|
"LoadVideo": "Load Video",
|
||||||
|
"ImageSizeAndBatchSize": "Get Image Size + Batch Size",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,298 @@
|
|||||||
|
import torch
|
||||||
|
from torch import Tensor
|
||||||
|
from torch.nn.functional import group_norm
|
||||||
|
from einops import rearrange
|
||||||
|
|
||||||
|
import comfy.ldm.modules.diffusionmodules.openaimodel as openaimodel
|
||||||
|
from comfy.model_base import BaseModel, model_sampling
|
||||||
|
from nodes import KSampler
|
||||||
|
|
||||||
|
from .logger import logger
|
||||||
|
from .motion_module import MotionWrapper, VanillaTemporalModule
|
||||||
|
from .sliding_schedule import ContextSchedules
|
||||||
|
from .sliding_context_sampling import SlidingContext, inject_sampling_function, eject_sampling_function
|
||||||
|
|
||||||
|
|
||||||
|
SLIDING_CONTEXT_LENGTH = 16
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSamplingConfig:
|
||||||
|
def __init__(self, beta_schedule: str):
|
||||||
|
self.sampling_settings = {}
|
||||||
|
self.sampling_settings["beta_schedule"] = beta_schedule
|
||||||
|
|
||||||
|
|
||||||
|
def forward_timestep_embed(ts, x, emb, context=None, *args, **kwargs):
|
||||||
|
for layer in ts:
|
||||||
|
if isinstance(layer, VanillaTemporalModule):
|
||||||
|
x = layer(x, context)
|
||||||
|
else:
|
||||||
|
x = orig_forward_timestep_embed([layer], x, emb, context, *args, **kwargs)
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
def groupnorm_mm_factory(video_length: int):
|
||||||
|
def groupnorm_mm_forward(self, input: Tensor) -> Tensor:
|
||||||
|
# axes_factor normalizes batch based on total conds and unconds passed in batch;
|
||||||
|
# the conds and unconds per batch can change based on VRAM optimizations that may kick in
|
||||||
|
axes_factor = input.size(0) // video_length
|
||||||
|
|
||||||
|
input = rearrange(input, "(b f) c h w -> b c f h w", b=axes_factor)
|
||||||
|
input = group_norm(input, self.num_groups, self.weight, self.bias, self.eps)
|
||||||
|
input = rearrange(input, "b c f h w -> (b f) c h w", b=axes_factor)
|
||||||
|
return input
|
||||||
|
|
||||||
|
return groupnorm_mm_forward
|
||||||
|
|
||||||
|
|
||||||
|
orig_forward_timestep_embed = openaimodel.forward_timestep_embed
|
||||||
|
orig_groupnorm_forward = torch.nn.GroupNorm.forward
|
||||||
|
|
||||||
|
|
||||||
|
def inject_motion_module_to_unet_legacy(unet, motion_module: MotionWrapper):
|
||||||
|
for mm_idx, unet_idx in enumerate([1, 2, 4, 5, 7, 8, 10, 11]):
|
||||||
|
mm_idx0, mm_idx1 = mm_idx // 2, mm_idx % 2
|
||||||
|
unet.input_blocks[unet_idx].append(motion_module.down_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
|
||||||
|
for unet_idx in range(12):
|
||||||
|
mm_idx0, mm_idx1 = unet_idx // 3, unet_idx % 3
|
||||||
|
if unet_idx % 2 == 2:
|
||||||
|
unet.output_blocks[unet_idx].insert(-1, motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
else:
|
||||||
|
unet.output_blocks[unet_idx].append(motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
if motion_module.is_v2:
|
||||||
|
unet.middle_block.insert(-1, motion_module.mid_block.motion_modules[0])
|
||||||
|
|
||||||
|
unet.motion_module = motion_module
|
||||||
|
|
||||||
|
|
||||||
|
def eject_motion_module_from_unet_legacy(unet):
|
||||||
|
for unet_idx in [1, 2, 4, 5, 7, 8, 10, 11]:
|
||||||
|
unet.input_blocks[unet_idx].pop(-1)
|
||||||
|
|
||||||
|
for unet_idx in range(12):
|
||||||
|
if unet_idx % 2 == 2:
|
||||||
|
unet.output_blocks[unet_idx].pop(-2)
|
||||||
|
else:
|
||||||
|
unet.output_blocks[unet_idx].pop(-1)
|
||||||
|
|
||||||
|
if unet.motion_module.is_v2:
|
||||||
|
unet.middle_block.pop(-2)
|
||||||
|
|
||||||
|
del unet.motion_module
|
||||||
|
|
||||||
|
|
||||||
|
def inject_motion_module_to_unet(unet, motion_module: MotionWrapper):
|
||||||
|
for mm_idx, unet_idx in enumerate([1, 2, 4, 5, 7, 8, 10, 11]):
|
||||||
|
mm_idx0, mm_idx1 = mm_idx // 2, mm_idx % 2
|
||||||
|
unet.input_blocks[unet_idx].append(motion_module.down_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
|
||||||
|
for unet_idx in range(12):
|
||||||
|
mm_idx0, mm_idx1 = unet_idx // 3, unet_idx % 3
|
||||||
|
if unet_idx % 3 == 2 and unet_idx != 11:
|
||||||
|
unet.output_blocks[unet_idx].insert(-1, motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
else:
|
||||||
|
unet.output_blocks[unet_idx].append(motion_module.up_blocks[mm_idx0].motion_modules[mm_idx1])
|
||||||
|
if motion_module.is_v2:
|
||||||
|
unet.middle_block.insert(-1, motion_module.mid_block.motion_modules[0])
|
||||||
|
|
||||||
|
unet.motion_module = motion_module
|
||||||
|
|
||||||
|
|
||||||
|
def eject_motion_module_from_unet(unet):
|
||||||
|
for unet_idx in [1, 2, 4, 5, 7, 8, 10, 11]:
|
||||||
|
unet.input_blocks[unet_idx].pop(-1)
|
||||||
|
|
||||||
|
for unet_idx in range(12):
|
||||||
|
if unet_idx % 3 == 2 and unet_idx != 11:
|
||||||
|
unet.output_blocks[unet_idx].pop(-2)
|
||||||
|
else:
|
||||||
|
unet.output_blocks[unet_idx].pop(-1)
|
||||||
|
|
||||||
|
if unet.motion_module.is_v2:
|
||||||
|
unet.middle_block.pop(-2)
|
||||||
|
|
||||||
|
del unet.motion_module
|
||||||
|
|
||||||
|
|
||||||
|
injectors = {
|
||||||
|
"legacy": inject_motion_module_to_unet_legacy,
|
||||||
|
"default": inject_motion_module_to_unet,
|
||||||
|
}
|
||||||
|
|
||||||
|
ejectors = {
|
||||||
|
"legacy": eject_motion_module_from_unet_legacy,
|
||||||
|
"default": eject_motion_module_from_unet,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AnimateDiffSlidingWindowOptions:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"context_length": ("INT", {"default": SLIDING_CONTEXT_LENGTH, "min": 2, "max": 32}),
|
||||||
|
"context_stride": ("INT", {"default": 1, "min": 1, "max": 32}),
|
||||||
|
"context_overlap": ("INT", {"default": 4, "min": 0, "max": 32}),
|
||||||
|
"context_schedule": (ContextSchedules.CONTEXT_SCHEDULE_LIST, {"default": ContextSchedules.UNIFORM}),
|
||||||
|
"closed_loop": ("BOOLEAN", {"default": False}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("SLIDING_WINDOW_OPTS",)
|
||||||
|
FUNCTION = "init_options"
|
||||||
|
CATEGORY = "Animate Diff"
|
||||||
|
|
||||||
|
def init_options(self, context_length, context_stride, context_overlap, context_schedule, closed_loop):
|
||||||
|
ctx = SlidingContext(
|
||||||
|
context_length=context_length,
|
||||||
|
context_stride=context_stride,
|
||||||
|
context_overlap=context_overlap,
|
||||||
|
context_schedule=context_schedule,
|
||||||
|
closed_loop=closed_loop,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (ctx,)
|
||||||
|
|
||||||
|
|
||||||
|
class AnimateDiffSampler(KSampler):
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
inputs = {
|
||||||
|
"required": {
|
||||||
|
"motion_module": ("MOTION_MODULE",),
|
||||||
|
"inject_method": (["default", "legacy"],),
|
||||||
|
"frame_number": (
|
||||||
|
"INT",
|
||||||
|
{"default": 16, "min": 2, "max": 10000, "step": 1},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputs["required"].update(KSampler.INPUT_TYPES()["required"])
|
||||||
|
inputs["optional"] = {"sliding_window_opts": ("SLIDING_WINDOW_OPTS",)}
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
FUNCTION = "animatediff_sample"
|
||||||
|
CATEGORY = "Animate Diff"
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.model_sampling = None
|
||||||
|
|
||||||
|
def override_beta_schedule(self, model: BaseModel):
|
||||||
|
self.model_sampling = model.model_sampling
|
||||||
|
model.model_sampling = model_sampling(
|
||||||
|
ModelSamplingConfig(beta_schedule="sqrt_linear"), model_type=model.model_type
|
||||||
|
)
|
||||||
|
|
||||||
|
def restore_beta_schedule(self, model: BaseModel):
|
||||||
|
model.model_sampling = self.model_sampling
|
||||||
|
self.model_sampling = None
|
||||||
|
|
||||||
|
def inject_motion_module(self, model, motion_module: MotionWrapper, inject_method: str, frame_number: int):
|
||||||
|
model = model.clone()
|
||||||
|
unet = model.model.diffusion_model
|
||||||
|
|
||||||
|
logger.info(f"Injecting motion module with method {inject_method}.")
|
||||||
|
motion_module.set_video_length(frame_number)
|
||||||
|
injectors[inject_method](unet, motion_module)
|
||||||
|
self.override_beta_schedule(model.model)
|
||||||
|
openaimodel.forward_timestep_embed = forward_timestep_embed
|
||||||
|
if not motion_module.is_v2:
|
||||||
|
logger.info(f"Hacking GroupNorm.forward function.")
|
||||||
|
torch.nn.GroupNorm.forward = groupnorm_mm_factory(frame_number)
|
||||||
|
|
||||||
|
return model
|
||||||
|
|
||||||
|
def inject_sliding_sampler(self, video_length, sliding_window_opts: SlidingContext = None):
|
||||||
|
ctx = sliding_window_opts.copy() if sliding_window_opts else SlidingContext()
|
||||||
|
ctx.video_length = video_length
|
||||||
|
|
||||||
|
inject_sampling_function(ctx)
|
||||||
|
|
||||||
|
def eject_motion_module(self, model, inject_method):
|
||||||
|
unet = model.model.diffusion_model
|
||||||
|
|
||||||
|
self.restore_beta_schedule(model.model)
|
||||||
|
openaimodel.forward_timestep_embed = orig_forward_timestep_embed
|
||||||
|
if not unet.motion_module.is_v2:
|
||||||
|
logger.info(f"Restore GroupNorm.forward function.")
|
||||||
|
torch.nn.GroupNorm.forward = orig_groupnorm_forward
|
||||||
|
|
||||||
|
logger.info(f"Ejecting motion module with method {inject_method}.")
|
||||||
|
ejectors[inject_method](unet)
|
||||||
|
|
||||||
|
def eject_sliding_sampler(self):
|
||||||
|
eject_sampling_function()
|
||||||
|
|
||||||
|
def animatediff_sample(
|
||||||
|
self,
|
||||||
|
motion_module,
|
||||||
|
inject_method,
|
||||||
|
frame_number,
|
||||||
|
model,
|
||||||
|
seed,
|
||||||
|
steps,
|
||||||
|
cfg,
|
||||||
|
sampler_name,
|
||||||
|
scheduler,
|
||||||
|
positive,
|
||||||
|
negative,
|
||||||
|
latent_image,
|
||||||
|
denoise=1.0,
|
||||||
|
sliding_window_opts: SlidingContext = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
# init latents
|
||||||
|
samples = latent_image["samples"]
|
||||||
|
init_frames = len(samples)
|
||||||
|
if init_frames < frame_number:
|
||||||
|
# TODO: apply different noise to each frame
|
||||||
|
last_frame = samples[-1].clone().cpu().unsqueeze(0)
|
||||||
|
repeated_last_frames = last_frame.repeat(frame_number - init_frames, 1, 1, 1)
|
||||||
|
samples = torch.cat((samples, repeated_last_frames), dim=0)
|
||||||
|
|
||||||
|
latent_image = {"samples": samples}
|
||||||
|
|
||||||
|
# validate context_length
|
||||||
|
context_length = sliding_window_opts.context_length if sliding_window_opts else SLIDING_CONTEXT_LENGTH
|
||||||
|
is_sliding = frame_number > context_length
|
||||||
|
video_length = context_length if is_sliding else frame_number
|
||||||
|
|
||||||
|
if video_length > motion_module.encoding_max_len:
|
||||||
|
error = f'{"context_length" if is_sliding else "frame_number"} = {video_length}'
|
||||||
|
raise ValueError(
|
||||||
|
f"AnimateDiff model {motion_module.mm_type} has upper limit of {motion_module.encoding_max_len} frames, but received {error}."
|
||||||
|
)
|
||||||
|
|
||||||
|
# inject motion module
|
||||||
|
model = self.inject_motion_module(model, motion_module, inject_method, video_length)
|
||||||
|
|
||||||
|
# inject sliding sampler
|
||||||
|
if is_sliding:
|
||||||
|
self.inject_sliding_sampler(frame_number, sliding_window_opts=sliding_window_opts)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return super().sample(
|
||||||
|
model,
|
||||||
|
seed,
|
||||||
|
steps,
|
||||||
|
cfg,
|
||||||
|
sampler_name,
|
||||||
|
scheduler,
|
||||||
|
positive,
|
||||||
|
negative,
|
||||||
|
latent_image,
|
||||||
|
denoise=denoise,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
# eject motion module
|
||||||
|
self.eject_motion_module(model, inject_method)
|
||||||
|
|
||||||
|
# eject sliding sampler
|
||||||
|
if is_sliding:
|
||||||
|
self.eject_sliding_sampler()
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
import math
|
||||||
|
import torch
|
||||||
|
from torch import Tensor
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
import comfy.utils
|
||||||
|
import comfy.sample
|
||||||
|
import comfy.samplers as comfy_samplers
|
||||||
|
import comfy.model_management as model_management
|
||||||
|
from comfy.controlnet import ControlBase
|
||||||
|
from comfy.model_patcher import ModelPatcher
|
||||||
|
|
||||||
|
from .logger import logger
|
||||||
|
from .sliding_schedule import get_context_scheduler, ContextSchedules
|
||||||
|
|
||||||
|
|
||||||
|
orig_comfy_sample = comfy.sample.sample
|
||||||
|
orig_sampling_function = comfy_samplers.sampling_function
|
||||||
|
|
||||||
|
|
||||||
|
def lcm(a, b):
|
||||||
|
return abs(a * b) // math.gcd(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
class SlidingContext:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
context_length=16,
|
||||||
|
context_stride=1,
|
||||||
|
context_overlap=4,
|
||||||
|
context_schedule=ContextSchedules.UNIFORM,
|
||||||
|
closed_loop=False,
|
||||||
|
video_length=0,
|
||||||
|
current_step=0,
|
||||||
|
total_steps=0,
|
||||||
|
):
|
||||||
|
self.context_length = context_length
|
||||||
|
self.context_stride = context_stride
|
||||||
|
self.context_overlap = context_overlap
|
||||||
|
self.context_schedule = context_schedule
|
||||||
|
self.closed_loop = closed_loop
|
||||||
|
self.video_length = video_length
|
||||||
|
self.current_step = current_step
|
||||||
|
self.total_steps = total_steps
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return SlidingContext(
|
||||||
|
context_length=self.context_length,
|
||||||
|
context_stride=self.context_stride,
|
||||||
|
context_overlap=self.context_overlap,
|
||||||
|
context_schedule=self.context_schedule,
|
||||||
|
closed_loop=self.closed_loop,
|
||||||
|
video_length=self.video_length,
|
||||||
|
current_step=self.current_step,
|
||||||
|
total_steps=self.total_steps,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __sliding_sample_factory(ctx: SlidingContext):
|
||||||
|
logger.info(f"Injecting sliding context sampling function.")
|
||||||
|
logger.info(f"Video length: {ctx.video_length}")
|
||||||
|
logger.info(f"Context length: {ctx.context_length}")
|
||||||
|
logger.info(f"Context schedule: {ctx.context_schedule}")
|
||||||
|
|
||||||
|
context_scheduler = get_context_scheduler(ctx.context_schedule)
|
||||||
|
|
||||||
|
def sample(model: ModelPatcher, *args, **kwargs):
|
||||||
|
orig_callback = kwargs.pop("callback", None)
|
||||||
|
start_step = kwargs.get("start_step") or 0
|
||||||
|
|
||||||
|
# adjust progressbar to account for context frames
|
||||||
|
def callback(step, x0, x, total_steps):
|
||||||
|
if orig_callback:
|
||||||
|
orig_callback(step, x0, x, total_steps)
|
||||||
|
|
||||||
|
ctx.current_step = start_step + step + 1
|
||||||
|
|
||||||
|
return orig_comfy_sample(model, *args, **kwargs, callback=callback)
|
||||||
|
|
||||||
|
def sampling_function(model, x, timestep, uncond, cond, cond_scale, model_options={}, seed=None):
|
||||||
|
def get_area_and_mult(conds, x_in, timestep_in):
|
||||||
|
area = (x_in.shape[2], x_in.shape[3], 0, 0)
|
||||||
|
strength = 1.0
|
||||||
|
|
||||||
|
if "timestep_start" in conds:
|
||||||
|
timestep_start = conds["timestep_start"]
|
||||||
|
if timestep_in[0] > timestep_start:
|
||||||
|
return None
|
||||||
|
if "timestep_end" in conds:
|
||||||
|
timestep_end = conds["timestep_end"]
|
||||||
|
if timestep_in[0] < timestep_end:
|
||||||
|
return None
|
||||||
|
if "area" in conds:
|
||||||
|
area = conds["area"]
|
||||||
|
if "strength" in conds:
|
||||||
|
strength = conds["strength"]
|
||||||
|
|
||||||
|
input_x = x_in[:, :, area[2] : area[0] + area[2], area[3] : area[1] + area[3]]
|
||||||
|
if "mask" in conds:
|
||||||
|
# Scale the mask to the size of the input
|
||||||
|
# The mask should have been resized as we began the sampling process
|
||||||
|
mask_strength = 1.0
|
||||||
|
if "mask_strength" in conds:
|
||||||
|
mask_strength = conds["mask_strength"]
|
||||||
|
mask = conds["mask"]
|
||||||
|
assert mask.shape[1] == x_in.shape[2]
|
||||||
|
assert mask.shape[2] == x_in.shape[3]
|
||||||
|
mask = mask[:, area[2] : area[0] + area[2], area[3] : area[1] + area[3]] * mask_strength
|
||||||
|
mask = mask.unsqueeze(1).repeat(input_x.shape[0] // mask.shape[0], input_x.shape[1], 1, 1)
|
||||||
|
else:
|
||||||
|
mask = torch.ones_like(input_x)
|
||||||
|
mult = mask * strength
|
||||||
|
|
||||||
|
if "mask" not in conds:
|
||||||
|
rr = 8
|
||||||
|
if area[2] != 0:
|
||||||
|
for t in range(rr):
|
||||||
|
mult[:, :, t : 1 + t, :] *= (1.0 / rr) * (t + 1)
|
||||||
|
if (area[0] + area[2]) < x_in.shape[2]:
|
||||||
|
for t in range(rr):
|
||||||
|
mult[:, :, area[0] - 1 - t : area[0] - t, :] *= (1.0 / rr) * (t + 1)
|
||||||
|
if area[3] != 0:
|
||||||
|
for t in range(rr):
|
||||||
|
mult[:, :, :, t : 1 + t] *= (1.0 / rr) * (t + 1)
|
||||||
|
if (area[1] + area[3]) < x_in.shape[3]:
|
||||||
|
for t in range(rr):
|
||||||
|
mult[:, :, :, area[1] - 1 - t : area[1] - t] *= (1.0 / rr) * (t + 1)
|
||||||
|
|
||||||
|
conditionning = {}
|
||||||
|
model_conds = conds["model_conds"]
|
||||||
|
for c in model_conds:
|
||||||
|
conditionning[c] = model_conds[c].process_cond(batch_size=x_in.shape[0], device=x_in.device, area=area)
|
||||||
|
|
||||||
|
control = None
|
||||||
|
if "control" in conds:
|
||||||
|
control = conds["control"]
|
||||||
|
|
||||||
|
patches = None
|
||||||
|
if "gligen" in conds:
|
||||||
|
gligen = conds["gligen"]
|
||||||
|
patches = {}
|
||||||
|
gligen_type = gligen[0]
|
||||||
|
gligen_model = gligen[1]
|
||||||
|
if gligen_type == "position":
|
||||||
|
gligen_patch = gligen_model.model.set_position(input_x.shape, gligen[2], input_x.device)
|
||||||
|
else:
|
||||||
|
gligen_patch = gligen_model.model.set_empty(input_x.shape, input_x.device)
|
||||||
|
|
||||||
|
patches["middle_patch"] = [gligen_patch]
|
||||||
|
|
||||||
|
return (input_x, mult, conditionning, area, control, patches)
|
||||||
|
|
||||||
|
def cond_equal_size(c1, c2):
|
||||||
|
if c1 is c2:
|
||||||
|
return True
|
||||||
|
if c1.keys() != c2.keys():
|
||||||
|
return False
|
||||||
|
for k in c1:
|
||||||
|
if not c1[k].can_concat(c2[k]):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def can_concat_cond(c1, c2):
|
||||||
|
if c1[0].shape != c2[0].shape:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# control
|
||||||
|
if (c1[4] is None) != (c2[4] is None):
|
||||||
|
return False
|
||||||
|
if c1[4] is not None:
|
||||||
|
if c1[4] is not c2[4]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# patches
|
||||||
|
if (c1[5] is None) != (c2[5] is None):
|
||||||
|
return False
|
||||||
|
if c1[5] is not None:
|
||||||
|
if c1[5] is not c2[5]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return cond_equal_size(c1[2], c2[2])
|
||||||
|
|
||||||
|
def cond_cat(c_list):
|
||||||
|
c_crossattn = []
|
||||||
|
c_concat = []
|
||||||
|
c_adm = []
|
||||||
|
crossattn_max_len = 0
|
||||||
|
|
||||||
|
temp = {}
|
||||||
|
for x in c_list:
|
||||||
|
for k in x:
|
||||||
|
cur = temp.get(k, [])
|
||||||
|
cur.append(x[k])
|
||||||
|
temp[k] = cur
|
||||||
|
|
||||||
|
out = {}
|
||||||
|
for k in temp:
|
||||||
|
conds = temp[k]
|
||||||
|
out[k] = conds[0].concat(conds[1:])
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
def calc_cond_uncond_batch(model, cond, uncond, x_in, timestep, model_options):
|
||||||
|
out_cond = torch.zeros_like(x_in)
|
||||||
|
out_count = torch.ones_like(x_in) * 1e-37
|
||||||
|
|
||||||
|
out_uncond = torch.zeros_like(x_in)
|
||||||
|
out_uncond_count = torch.ones_like(x_in) * 1e-37
|
||||||
|
|
||||||
|
COND = 0
|
||||||
|
UNCOND = 1
|
||||||
|
|
||||||
|
to_run = []
|
||||||
|
for x in cond:
|
||||||
|
p = get_area_and_mult(x, x_in, timestep)
|
||||||
|
if p is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_run += [(p, COND)]
|
||||||
|
if uncond is not None:
|
||||||
|
for x in uncond:
|
||||||
|
p = get_area_and_mult(x, x_in, timestep)
|
||||||
|
if p is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_run += [(p, UNCOND)]
|
||||||
|
|
||||||
|
while len(to_run) > 0:
|
||||||
|
first = to_run[0]
|
||||||
|
first_shape = first[0][0].shape
|
||||||
|
to_batch_temp = []
|
||||||
|
for x in range(len(to_run)):
|
||||||
|
if can_concat_cond(to_run[x][0], first[0]):
|
||||||
|
to_batch_temp += [x]
|
||||||
|
|
||||||
|
to_batch_temp.reverse()
|
||||||
|
to_batch = to_batch_temp[:1]
|
||||||
|
|
||||||
|
free_memory = model_management.get_free_memory(x_in.device)
|
||||||
|
for i in range(1, len(to_batch_temp) + 1):
|
||||||
|
batch_amount = to_batch_temp[: len(to_batch_temp) // i]
|
||||||
|
input_shape = [len(batch_amount) * first_shape[0]] + list(first_shape)[1:]
|
||||||
|
if model.memory_required(input_shape) < free_memory:
|
||||||
|
to_batch = batch_amount
|
||||||
|
break
|
||||||
|
|
||||||
|
input_x = []
|
||||||
|
mult = []
|
||||||
|
c = []
|
||||||
|
cond_or_uncond = []
|
||||||
|
area = []
|
||||||
|
control = None
|
||||||
|
patches = None
|
||||||
|
for x in to_batch:
|
||||||
|
o = to_run.pop(x)
|
||||||
|
p = o[0]
|
||||||
|
input_x += [p[0]]
|
||||||
|
mult += [p[1]]
|
||||||
|
c += [p[2]]
|
||||||
|
area += [p[3]]
|
||||||
|
cond_or_uncond += [o[1]]
|
||||||
|
control = p[4]
|
||||||
|
patches = p[5]
|
||||||
|
|
||||||
|
batch_chunks = len(cond_or_uncond)
|
||||||
|
input_x = torch.cat(input_x)
|
||||||
|
c = cond_cat(c)
|
||||||
|
timestep_ = torch.cat([timestep] * batch_chunks)
|
||||||
|
|
||||||
|
if control is not None:
|
||||||
|
c["control"] = control.get_control(input_x, timestep_, c, len(cond_or_uncond))
|
||||||
|
|
||||||
|
transformer_options = {}
|
||||||
|
if "transformer_options" in model_options:
|
||||||
|
transformer_options = model_options["transformer_options"].copy()
|
||||||
|
|
||||||
|
if patches is not None:
|
||||||
|
if "patches" in transformer_options:
|
||||||
|
cur_patches = transformer_options["patches"].copy()
|
||||||
|
for p in patches:
|
||||||
|
if p in cur_patches:
|
||||||
|
cur_patches[p] = cur_patches[p] + patches[p]
|
||||||
|
else:
|
||||||
|
cur_patches[p] = patches[p]
|
||||||
|
else:
|
||||||
|
transformer_options["patches"] = patches
|
||||||
|
|
||||||
|
transformer_options["cond_or_uncond"] = cond_or_uncond[:]
|
||||||
|
c["transformer_options"] = transformer_options
|
||||||
|
|
||||||
|
if "model_function_wrapper" in model_options:
|
||||||
|
output = model_options["model_function_wrapper"](
|
||||||
|
model.apply_model,
|
||||||
|
{"input": input_x, "timestep": timestep_, "c": c, "cond_or_uncond": cond_or_uncond},
|
||||||
|
).chunk(batch_chunks)
|
||||||
|
else:
|
||||||
|
output = model.apply_model(input_x, timestep_, **c).chunk(batch_chunks)
|
||||||
|
del input_x
|
||||||
|
|
||||||
|
for o in range(batch_chunks):
|
||||||
|
if cond_or_uncond[o] == COND:
|
||||||
|
out_cond[:, :, area[o][2] : area[o][0] + area[o][2], area[o][3] : area[o][1] + area[o][3]] += (
|
||||||
|
output[o] * mult[o]
|
||||||
|
)
|
||||||
|
out_count[
|
||||||
|
:, :, area[o][2] : area[o][0] + area[o][2], area[o][3] : area[o][1] + area[o][3]
|
||||||
|
] += mult[o]
|
||||||
|
else:
|
||||||
|
out_uncond[
|
||||||
|
:, :, area[o][2] : area[o][0] + area[o][2], area[o][3] : area[o][1] + area[o][3]
|
||||||
|
] += (output[o] * mult[o])
|
||||||
|
out_uncond_count[
|
||||||
|
:, :, area[o][2] : area[o][0] + area[o][2], area[o][3] : area[o][1] + area[o][3]
|
||||||
|
] += mult[o]
|
||||||
|
del mult
|
||||||
|
|
||||||
|
out_cond /= out_count
|
||||||
|
del out_count
|
||||||
|
out_uncond /= out_uncond_count
|
||||||
|
del out_uncond_count
|
||||||
|
return out_cond, out_uncond
|
||||||
|
|
||||||
|
# sliding_calc_cond_uncond_batch inspired by ashen's initial hack for 16-frame sliding context:
|
||||||
|
# https://github.com/comfyanonymous/ComfyUI/compare/master...ashen-sensored:ComfyUI:master
|
||||||
|
def sliding_calc_cond_uncond_batch(model, cond, uncond, x_in, timestep, model_options):
|
||||||
|
# figure out how input is split
|
||||||
|
axes_factor = x.size(0) // ctx.video_length
|
||||||
|
|
||||||
|
# prepare final cond, uncond, and out_count
|
||||||
|
cond_final = torch.zeros_like(x)
|
||||||
|
uncond_final = torch.zeros_like(x)
|
||||||
|
out_count_final = torch.zeros((x.shape[0], 1, 1, 1), device=x.device)
|
||||||
|
|
||||||
|
def prepare_control_objects(control: ControlBase, full_idxs: list[int]):
|
||||||
|
if control.previous_controlnet is not None:
|
||||||
|
prepare_control_objects(control.previous_controlnet, full_idxs)
|
||||||
|
control.sub_idxs = full_idxs
|
||||||
|
control.full_latent_length = ctx.video_length
|
||||||
|
control.context_length = ctx.context_length
|
||||||
|
|
||||||
|
def get_resized_cond(cond_in: List[Dict], full_idxs) -> list:
|
||||||
|
# reuse or resize cond items to match context requirements
|
||||||
|
resized_cond = []
|
||||||
|
# cond object is a list containing a list - outer list is irrelevant, so just loop through it
|
||||||
|
for actual_cond in cond_in:
|
||||||
|
new_cond_item = actual_cond.copy()
|
||||||
|
for key, cond_item in new_cond_item.items():
|
||||||
|
if isinstance(cond_item, Tensor):
|
||||||
|
# check that tensor is the expected length - x.size(0)
|
||||||
|
if cond_item.size(0) == x.size(0):
|
||||||
|
# if so, it's subsetting time - tell controls the expected indeces so they can handle them
|
||||||
|
actual_cond_item = cond_item[full_idxs]
|
||||||
|
new_cond_item[key] = actual_cond_item
|
||||||
|
elif key == "control":
|
||||||
|
control_item = cond_item
|
||||||
|
if hasattr(control_item, "sub_idxs"):
|
||||||
|
prepare_control_objects(control_item, full_idxs)
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Control type {type(control_item).__name__} may not support required features for sliding context window; use Control objects from Kosinkadink/Advanced-ControlNet nodes."
|
||||||
|
)
|
||||||
|
new_cond_item[key] = cond_item
|
||||||
|
resized_cond.append(new_cond_item)
|
||||||
|
return resized_cond
|
||||||
|
|
||||||
|
# perform calc_cond_uncond_batch per context window
|
||||||
|
for ctx_idxs in context_scheduler(
|
||||||
|
ctx.current_step,
|
||||||
|
ctx.total_steps,
|
||||||
|
ctx.video_length,
|
||||||
|
ctx.context_length,
|
||||||
|
ctx.context_stride,
|
||||||
|
ctx.context_overlap,
|
||||||
|
ctx.closed_loop,
|
||||||
|
):
|
||||||
|
# account for all portions of input frames
|
||||||
|
full_idxs = []
|
||||||
|
for n in range(axes_factor):
|
||||||
|
for ind in ctx_idxs:
|
||||||
|
full_idxs.append((ctx.video_length * n) + ind)
|
||||||
|
# get subsections of x, timestep, cond, uncond, cond_concat
|
||||||
|
sub_x = x[full_idxs]
|
||||||
|
sub_timestep = timestep[full_idxs]
|
||||||
|
sub_cond = get_resized_cond(cond, full_idxs) if cond is not None else None
|
||||||
|
sub_uncond = get_resized_cond(uncond, full_idxs) if uncond is not None else None
|
||||||
|
|
||||||
|
sub_cond_out, sub_uncond_out = calc_cond_uncond_batch(
|
||||||
|
model,
|
||||||
|
sub_cond,
|
||||||
|
sub_uncond,
|
||||||
|
sub_x,
|
||||||
|
sub_timestep,
|
||||||
|
model_options,
|
||||||
|
)
|
||||||
|
|
||||||
|
cond_final[full_idxs] += sub_cond_out
|
||||||
|
uncond_final[full_idxs] += sub_uncond_out
|
||||||
|
out_count_final[full_idxs] += 1 # increment which indeces were used
|
||||||
|
|
||||||
|
# normalize cond and uncond via division by context usage counts
|
||||||
|
cond_final /= out_count_final
|
||||||
|
uncond_final /= out_count_final
|
||||||
|
return cond_final, uncond_final
|
||||||
|
|
||||||
|
if math.isclose(cond_scale, 1.0):
|
||||||
|
uncond = None
|
||||||
|
|
||||||
|
cond, uncond = sliding_calc_cond_uncond_batch(model, cond, uncond, x, timestep, model_options)
|
||||||
|
|
||||||
|
if "sampler_cfg_function" in model_options:
|
||||||
|
args = {
|
||||||
|
"cond": x - cond,
|
||||||
|
"uncond": x - uncond,
|
||||||
|
"cond_scale": cond_scale,
|
||||||
|
"timestep": timestep,
|
||||||
|
"input": x,
|
||||||
|
"sigma": timestep,
|
||||||
|
}
|
||||||
|
return x - model_options["sampler_cfg_function"](args)
|
||||||
|
else:
|
||||||
|
return uncond + (cond - uncond) * cond_scale
|
||||||
|
|
||||||
|
return (sample, sampling_function)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_sampling_function(ctx: SlidingContext):
|
||||||
|
global orig_comfy_sample, orig_sampling_function
|
||||||
|
orig_comfy_sample = comfy.sample.sample
|
||||||
|
orig_sampling_function = comfy_samplers.sampling_function
|
||||||
|
|
||||||
|
(sample, sampling_function) = __sliding_sample_factory(ctx)
|
||||||
|
comfy.sample.sample = sample
|
||||||
|
comfy_samplers.sampling_function = sampling_function
|
||||||
|
|
||||||
|
|
||||||
|
def eject_sampling_function():
|
||||||
|
comfy.sample.sample = orig_comfy_sample
|
||||||
|
comfy_samplers.sampling_function = orig_sampling_function
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
# from https://github.com/neggles/animatediff-cli/blob/main/src/animatediff/pipelines/context.py
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class ContextSchedules:
|
||||||
|
UNIFORM = "uniform"
|
||||||
|
UNIFORM_CONSTANT = "uniform_constant"
|
||||||
|
UNIFORM_V2 = "uniform v2"
|
||||||
|
|
||||||
|
CONTEXT_SCHEDULE_LIST = [UNIFORM]
|
||||||
|
|
||||||
|
|
||||||
|
# Returns fraction that has denominator that is a power of 2
|
||||||
|
def ordered_halving(val, print_final=False):
|
||||||
|
# get binary value, padded with 0s for 64 bits
|
||||||
|
bin_str = f"{val:064b}"
|
||||||
|
# flip binary value, padding included
|
||||||
|
bin_flip = bin_str[::-1]
|
||||||
|
# convert binary to int
|
||||||
|
as_int = int(bin_flip, 2)
|
||||||
|
# divide by 1 << 64, equivalent to 2**64, or 18446744073709551616,
|
||||||
|
# or b10000000000000000000000000000000000000000000000000000000000000000 (1 with 64 zero's)
|
||||||
|
final = as_int / (1 << 64)
|
||||||
|
if print_final:
|
||||||
|
print(f"$$$$ final: {final}")
|
||||||
|
return final
|
||||||
|
|
||||||
|
|
||||||
|
# Generator that returns lists of latent indeces to diffuse on
|
||||||
|
def uniform(
|
||||||
|
step: int = ...,
|
||||||
|
num_steps: Optional[int] = None,
|
||||||
|
num_frames: int = ...,
|
||||||
|
context_size: Optional[int] = None,
|
||||||
|
context_stride: int = 3,
|
||||||
|
context_overlap: int = 4,
|
||||||
|
closed_loop: bool = True,
|
||||||
|
print_final: bool = False,
|
||||||
|
):
|
||||||
|
if num_frames <= context_size:
|
||||||
|
yield list(range(num_frames))
|
||||||
|
return
|
||||||
|
|
||||||
|
context_stride = min(context_stride, int(np.ceil(np.log2(num_frames / context_size))) + 1)
|
||||||
|
|
||||||
|
for context_step in 1 << np.arange(context_stride):
|
||||||
|
pad = int(round(num_frames * ordered_halving(step, print_final)))
|
||||||
|
for j in range(
|
||||||
|
int(ordered_halving(step) * context_step) + pad,
|
||||||
|
num_frames + pad + (0 if closed_loop else -context_overlap),
|
||||||
|
(context_size * context_step - context_overlap),
|
||||||
|
):
|
||||||
|
yield [e % num_frames for e in range(j, j + context_size * context_step, context_step)]
|
||||||
|
|
||||||
|
|
||||||
|
def uniform_v2(
|
||||||
|
step: int = ...,
|
||||||
|
num_steps: Optional[int] = None,
|
||||||
|
num_frames: int = ...,
|
||||||
|
context_size: Optional[int] = None,
|
||||||
|
context_stride: int = 3,
|
||||||
|
context_overlap: int = 4,
|
||||||
|
closed_loop: bool = True,
|
||||||
|
print_final: bool = False,
|
||||||
|
):
|
||||||
|
if num_frames <= context_size:
|
||||||
|
yield list(range(num_frames))
|
||||||
|
return
|
||||||
|
|
||||||
|
context_stride = min(context_stride, int(np.ceil(np.log2(num_frames / context_size))) + 1)
|
||||||
|
|
||||||
|
pad = int(round(num_frames * ordered_halving(step, print_final)))
|
||||||
|
for context_step in 1 << np.arange(context_stride):
|
||||||
|
j_initial = int(ordered_halving(step) * context_step) + pad
|
||||||
|
for j in range(
|
||||||
|
j_initial,
|
||||||
|
num_frames + pad - context_overlap,
|
||||||
|
(context_size * context_step - context_overlap),
|
||||||
|
):
|
||||||
|
if context_size * context_step > num_frames:
|
||||||
|
# On the final context_step,
|
||||||
|
# ensure no frame appears in the window twice
|
||||||
|
yield [e % num_frames for e in range(j, j + num_frames, context_step)]
|
||||||
|
continue
|
||||||
|
j = j % num_frames
|
||||||
|
if j > (j + context_size * context_step) % num_frames and not closed_loop:
|
||||||
|
yield [e for e in range(j, num_frames, context_step)]
|
||||||
|
j_stop = (j + context_size * context_step) % num_frames
|
||||||
|
# When ((num_frames % (context_size - context_overlap)+context_overlap) % context_size != 0,
|
||||||
|
# This can cause 'superflous' runs where all frames in
|
||||||
|
# a context window have already been processed during
|
||||||
|
# the first context window of this stride and step.
|
||||||
|
# While the following commented if should prevent this,
|
||||||
|
# I believe leaving it in is more correct as it maintains
|
||||||
|
# the total conditional passes per frame over a large total steps
|
||||||
|
# if j_stop > context_overlap:
|
||||||
|
yield [e for e in range(0, j_stop, context_step)]
|
||||||
|
continue
|
||||||
|
yield [e % num_frames for e in range(j, j + context_size * context_step, context_step)]
|
||||||
|
|
||||||
|
|
||||||
|
def uniform_constant(
|
||||||
|
step: int = ...,
|
||||||
|
num_steps: Optional[int] = None,
|
||||||
|
num_frames: int = ...,
|
||||||
|
context_size: Optional[int] = None,
|
||||||
|
context_stride: int = 3,
|
||||||
|
context_overlap: int = 4,
|
||||||
|
closed_loop: bool = True,
|
||||||
|
print_final: bool = False,
|
||||||
|
):
|
||||||
|
if num_frames <= context_size:
|
||||||
|
yield list(range(num_frames))
|
||||||
|
return
|
||||||
|
|
||||||
|
context_stride = min(context_stride, int(np.ceil(np.log2(num_frames / context_size))) + 1)
|
||||||
|
|
||||||
|
# want to avoid loops that connect end to beginning
|
||||||
|
|
||||||
|
for context_step in 1 << np.arange(context_stride):
|
||||||
|
pad = int(round(num_frames * ordered_halving(step, print_final)))
|
||||||
|
for j in range(
|
||||||
|
int(ordered_halving(step) * context_step) + pad,
|
||||||
|
num_frames + pad + (0 if closed_loop else -context_overlap),
|
||||||
|
(context_size * context_step - context_overlap),
|
||||||
|
):
|
||||||
|
skip_this_window = False
|
||||||
|
prev_val = -1
|
||||||
|
to_yield = []
|
||||||
|
for e in range(j, j + context_size * context_step, context_step):
|
||||||
|
e = e % num_frames
|
||||||
|
# if not a closed loop and loops back on itself, should be skipped
|
||||||
|
if not closed_loop and e < prev_val:
|
||||||
|
skip_this_window = True
|
||||||
|
break
|
||||||
|
to_yield.append(e)
|
||||||
|
prev_val = e
|
||||||
|
if skip_this_window:
|
||||||
|
continue
|
||||||
|
# yield if not skipped
|
||||||
|
yield to_yield
|
||||||
|
|
||||||
|
def get_context_scheduler(name: str) -> Callable:
|
||||||
|
if name == ContextSchedules.UNIFORM:
|
||||||
|
return uniform
|
||||||
|
elif name == ContextSchedules.UNIFORM_CONSTANT:
|
||||||
|
return uniform_constant
|
||||||
|
elif name == ContextSchedules.UNIFORM_V2:
|
||||||
|
return uniform_v2
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown context_overlap policy {name}")
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import sys
|
||||||
|
import torch
|
||||||
|
import numpy as np
|
||||||
|
import subprocess
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
from .logger import logger
|
||||||
|
|
||||||
|
# Tensor to PIL
|
||||||
|
def tensor2pil(image):
|
||||||
|
return Image.fromarray(np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||||
|
|
||||||
|
|
||||||
|
# Convert PIL to Tensor
|
||||||
|
def pil2tensor(image):
|
||||||
|
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_opencv():
|
||||||
|
if "python_embeded" in sys.executable or "python_embedded" in sys.executable:
|
||||||
|
pip_install = [sys.executable, "-s", "-m", "pip", "install"]
|
||||||
|
else:
|
||||||
|
pip_install = [sys.executable, "-m", "pip", "install"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cv2
|
||||||
|
except Exception as e:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(pip_install + ['opencv-python'])
|
||||||
|
except:
|
||||||
|
logger.error(f"Failed to install 'opencv-python'. Please, install manually.")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
opencv-python
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"main_pass":
|
||||||
|
[
|
||||||
|
"-n", "-c:v", "libsvtav1",
|
||||||
|
"-pix_fmt", "yuv420p10le",
|
||||||
|
"-crf", "23"
|
||||||
|
],
|
||||||
|
"extension": "webm",
|
||||||
|
"environment": {"SVT_LOG": "1"}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"main_pass":
|
||||||
|
[
|
||||||
|
"-n", "-c:v", "libx264",
|
||||||
|
"-pix_fmt", "yuv420p",
|
||||||
|
"-crf", "19"
|
||||||
|
],
|
||||||
|
"extension": "mp4"
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"main_pass":
|
||||||
|
[
|
||||||
|
"-n", "-c:v", "libx265",
|
||||||
|
"-pix_fmt", "yuv420p10le",
|
||||||
|
"-preset", "medium",
|
||||||
|
"-crf", "22",
|
||||||
|
"-x265-params", "log-level=quiet"
|
||||||
|
],
|
||||||
|
"extension": "mp4"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"main_pass":
|
||||||
|
[
|
||||||
|
"-n",
|
||||||
|
"-pix_fmt", "yuv420p",
|
||||||
|
"-crf", "23"
|
||||||
|
],
|
||||||
|
"extension": "webm"
|
||||||
|
}
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import { app, ANIM_PREVIEW_WIDGET } from '../../../scripts/app.js';
|
||||||
|
import { api } from "../../../scripts/api.js";
|
||||||
|
import { $el } from '../../../scripts/ui.js';
|
||||||
|
import { createImageHost } from "../../../scripts/ui/imagePreview.js"
|
||||||
|
|
||||||
|
const URL_REGEX = /^(https?:\/\/|\/view\?|data:image\/)/;
|
||||||
|
|
||||||
|
const style = `
|
||||||
|
.comfy-img-preview video {
|
||||||
|
object-fit: contain;
|
||||||
|
width: var(--comfy-img-preview-width);
|
||||||
|
height: var(--comfy-img-preview-height);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function chainCallback(object, property, callback) {
|
||||||
|
if (object == undefined) {
|
||||||
|
//This should not happen.
|
||||||
|
console.error("Tried to add callback to non-existant object");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (property in object) {
|
||||||
|
const callback_orig = object[property];
|
||||||
|
object[property] = function () {
|
||||||
|
const r = callback_orig.apply(this, arguments);
|
||||||
|
callback.apply(this, arguments);
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
object[property] = callback;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function formatUploadedUrl(params) {
|
||||||
|
if (params.url) {
|
||||||
|
return params.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = { ...params };
|
||||||
|
|
||||||
|
if (!params.filename && params.name) {
|
||||||
|
params.filename = params.name;
|
||||||
|
delete params.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.apiURL("/view?" + new URLSearchParams(params));
|
||||||
|
};
|
||||||
|
|
||||||
|
export function addVideoPreview(nodeType, options = {}) {
|
||||||
|
const createVideoNode = (url) => {
|
||||||
|
return new Promise((cb) => {
|
||||||
|
const videoEl = document.createElement('video');
|
||||||
|
Object.defineProperty(videoEl, 'naturalWidth', {
|
||||||
|
get: () => {
|
||||||
|
return videoEl.videoWidth;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Object.defineProperty(videoEl, 'naturalHeight', {
|
||||||
|
get: () => {
|
||||||
|
return videoEl.videoHeight;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
videoEl.addEventListener('loadedmetadata', () => {
|
||||||
|
videoEl.controls = false;
|
||||||
|
videoEl.loop = true;
|
||||||
|
videoEl.muted = true;
|
||||||
|
cb(videoEl);
|
||||||
|
});
|
||||||
|
videoEl.addEventListener('error', () => {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
videoEl.src = url;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createImageNode = (url) => {
|
||||||
|
return new Promise((cb) => {
|
||||||
|
const imgEl = document.createElement('img');
|
||||||
|
imgEl.onload = () => {
|
||||||
|
cb(imgEl);
|
||||||
|
};
|
||||||
|
imgEl.addEventListener('error', () => {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
imgEl.src = url;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
nodeType.prototype.onDrawBackground = function (ctx) {
|
||||||
|
if (this.flags.collapsed) return;
|
||||||
|
|
||||||
|
let imageURLs = (this.images ?? []).map((i) =>
|
||||||
|
typeof i === 'string' ? i : formatUploadedUrl(i),
|
||||||
|
);
|
||||||
|
let imagesChanged = false;
|
||||||
|
|
||||||
|
if (JSON.stringify(this.displayingImages) !== JSON.stringify(imageURLs)) {
|
||||||
|
this.displayingImages = imageURLs;
|
||||||
|
imagesChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imagesChanged) return;
|
||||||
|
if (!imageURLs.length) {
|
||||||
|
this.imgs = null;
|
||||||
|
this.animatedImages = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises = imageURLs.map((url) => {
|
||||||
|
if (url.startsWith('/view')) {
|
||||||
|
url = window.location.origin + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u = new URL(url);
|
||||||
|
const filename =
|
||||||
|
u.searchParams.get('filename') || u.searchParams.get('name') || u.pathname.split('/').pop();
|
||||||
|
const ext = filename.split('.').pop();
|
||||||
|
const format = ['gif', 'webp', 'avif'].includes(ext) ? 'image' : 'video';
|
||||||
|
if (format === 'video') {
|
||||||
|
return createVideoNode(url);
|
||||||
|
} else {
|
||||||
|
return createImageNode(url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(promises)
|
||||||
|
.then((imgs) => {
|
||||||
|
this.imgs = imgs.filter(Boolean);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
if (!this.imgs.length) return;
|
||||||
|
|
||||||
|
this.animatedImages = true;
|
||||||
|
const widgetIdx = this.widgets?.findIndex((w) => w.name === ANIM_PREVIEW_WIDGET);
|
||||||
|
|
||||||
|
// Instead of using the canvas we'll use a IMG
|
||||||
|
if (widgetIdx > -1) {
|
||||||
|
// Replace content
|
||||||
|
const widget = this.widgets[widgetIdx];
|
||||||
|
widget.options.host.updateImages(this.imgs);
|
||||||
|
} else {
|
||||||
|
const host = createImageHost(this);
|
||||||
|
this.setSizeForImage(true);
|
||||||
|
const widget = this.addDOMWidget(ANIM_PREVIEW_WIDGET, 'img', host.el, {
|
||||||
|
host,
|
||||||
|
getHeight: host.getHeight,
|
||||||
|
onDraw: host.onDraw,
|
||||||
|
hideOnZoom: false,
|
||||||
|
});
|
||||||
|
widget.serializeValue = () => ({
|
||||||
|
height: host.el.clientHeight,
|
||||||
|
});
|
||||||
|
// widget.computeSize = (w) => ([w, 220]);
|
||||||
|
|
||||||
|
widget.options.host.updateImages(this.imgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.imgs.forEach((img) => {
|
||||||
|
if (img instanceof HTMLVideoElement) {
|
||||||
|
img.muted = true;
|
||||||
|
img.autoplay = true;
|
||||||
|
img.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { textWidget, comboWidget } = options;
|
||||||
|
|
||||||
|
if (textWidget) {
|
||||||
|
chainCallback(nodeType.prototype, 'onNodeCreated', function () {
|
||||||
|
const pathWidget = this.widgets.find((w) => w.name === textWidget);
|
||||||
|
pathWidget._value = pathWidget.value;
|
||||||
|
Object.defineProperty(pathWidget, 'value', {
|
||||||
|
set: (value) => {
|
||||||
|
pathWidget._value = value;
|
||||||
|
pathWidget.inputEl.value = value;
|
||||||
|
this.images = (value ?? '').split('\n').filter((url) => URL_REGEX.test(url));
|
||||||
|
},
|
||||||
|
get: () => {
|
||||||
|
return pathWidget._value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
pathWidget.inputEl.addEventListener('change', (e) => {
|
||||||
|
const value = e.target.value;
|
||||||
|
pathWidget._value = value;
|
||||||
|
this.images = (value ?? '').split('\n').filter((url) => URL_REGEX.test(url));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set value to ensure preview displays on initial add.
|
||||||
|
pathWidget.value = pathWidget._value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comboWidget) {
|
||||||
|
chainCallback(nodeType.prototype, 'onNodeCreated', function () {
|
||||||
|
const pathWidget = this.widgets.find((w) => w.name === comboWidget);
|
||||||
|
pathWidget._value = pathWidget.value;
|
||||||
|
Object.defineProperty(pathWidget, 'value', {
|
||||||
|
set: (value) => {
|
||||||
|
pathWidget._value = value;
|
||||||
|
if (!value) {
|
||||||
|
return this.images = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = value.split("/")
|
||||||
|
const filename = parts.pop()
|
||||||
|
const subfolder = parts.join("/")
|
||||||
|
const extension = filename.split(".").pop();
|
||||||
|
const format = (["gif", "webp", "avif"].includes(extension)) ? 'image' : 'video'
|
||||||
|
this.images = [formatUploadedUrl({ filename, subfolder, type: "input", format: format })]
|
||||||
|
},
|
||||||
|
get: () => {
|
||||||
|
return pathWidget._value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set value to ensure preview displays on initial add.
|
||||||
|
pathWidget.value = pathWidget._value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chainCallback(nodeType.prototype, "onExecuted", function (message) {
|
||||||
|
if (message?.videos) {
|
||||||
|
this.images = message?.videos.map(formatUploadedUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: "AnimateDiff.VideoPreview",
|
||||||
|
init() {
|
||||||
|
$el('style', {
|
||||||
|
textContent: style,
|
||||||
|
parent: document.head,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async beforeRegisterNodeDef(nodeType, nodeData) {
|
||||||
|
if (nodeData.name !== "AnimateDiffCombine") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addVideoPreview(nodeType);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import { app } from "../../../scripts/app.js";
|
||||||
|
import { api } from "../../../scripts/api.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
chainCallback,
|
||||||
|
addVideoPreview,
|
||||||
|
} from "./vid_preview.js";
|
||||||
|
|
||||||
|
async function uploadFile(file) {
|
||||||
|
try {
|
||||||
|
// Wrap file in formdata so it includes filename
|
||||||
|
const body = new FormData();
|
||||||
|
const new_file = new File([file], file.name, {
|
||||||
|
type: file.type,
|
||||||
|
lastModified: file.lastModified,
|
||||||
|
});
|
||||||
|
body.append("image", new_file);
|
||||||
|
body.append("subfolder", "video");
|
||||||
|
const resp = await api.fetchApi("/upload/image", {
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resp.status === 200 || resp.status === 201) {
|
||||||
|
return resp.json();
|
||||||
|
} else {
|
||||||
|
alert(`Upload failed: ${resp.statusText}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
alert(`Upload failed: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUploadWidget(nodeType, widgetName) {
|
||||||
|
chainCallback(nodeType.prototype, "onNodeCreated", function () {
|
||||||
|
const pathWidget = this.widgets.find((w) => w.name === widgetName);
|
||||||
|
if (pathWidget.element) {
|
||||||
|
pathWidget.options.getMinHeight = () => 50;
|
||||||
|
pathWidget.options.getMaxHeight = () => 150;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileInput = document.createElement("input");
|
||||||
|
chainCallback(this, "onRemoved", () => {
|
||||||
|
fileInput?.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(fileInput, {
|
||||||
|
type: "file",
|
||||||
|
accept: "video/webm,video/mp4,video/mkv,image/gif,image/webp",
|
||||||
|
style: "display: none",
|
||||||
|
onchange: async () => {
|
||||||
|
if (fileInput.files.length) {
|
||||||
|
const params = await uploadFile(fileInput.files[0]);
|
||||||
|
if (!params) {
|
||||||
|
// upload failed and file can not be added to options
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInput.value = "";
|
||||||
|
const filename = [params.subfolder, params.name || params.filename].filter(Boolean).join('/')
|
||||||
|
pathWidget.value = filename;
|
||||||
|
pathWidget.options.values.push(filename);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.append(fileInput);
|
||||||
|
let uploadWidget = this.addWidget(
|
||||||
|
"button",
|
||||||
|
"choose video to upload",
|
||||||
|
"image",
|
||||||
|
() => {
|
||||||
|
app.canvas.node_widget = null;
|
||||||
|
fileInput.click();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
uploadWidget.options.serialize = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds an upload button to the nodes
|
||||||
|
app.registerExtension({
|
||||||
|
name: "AnimateDiff.UploadVideo",
|
||||||
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
|
if (nodeData?.input?.required?.video?.[1]?.video_upload === true) {
|
||||||
|
addUploadWidget(nodeType, 'video');
|
||||||
|
addVideoPreview(nodeType, { comboWidget: 'video' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,877 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 107,
|
||||||
|
"last_link_id": 199,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"type": "AnimateDiffModuleLoader",
|
||||||
|
"pos": [
|
||||||
|
-280,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 60
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MOTION_MODULE",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"links": [
|
||||||
|
193
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffModuleLoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"mm-Stabilized_mid.pth"
|
||||||
|
],
|
||||||
|
"color": "#571a1a",
|
||||||
|
"bgcolor": "#6b2e2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"type": "VAELoader",
|
||||||
|
"pos": [
|
||||||
|
-280,
|
||||||
|
400
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 60
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [
|
||||||
|
82
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAELoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"vae-ft-mse-840000-ema-pruned.safetensors"
|
||||||
|
],
|
||||||
|
"color": "#571a1a",
|
||||||
|
"bgcolor": "#6b2e2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 45,
|
||||||
|
"type": "AnimateDiffCombine",
|
||||||
|
"pos": [
|
||||||
|
1240,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 360,
|
||||||
|
"1": 732
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 13,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 172
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "GIF",
|
||||||
|
"type": "GIF",
|
||||||
|
"links": null,
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffCombine"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
"AnimateDiff",
|
||||||
|
"image/gif",
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"pos": [
|
||||||
|
-280,
|
||||||
|
250
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 100
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MODEL",
|
||||||
|
"type": "MODEL",
|
||||||
|
"links": [
|
||||||
|
194
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIP",
|
||||||
|
"type": "CLIP",
|
||||||
|
"links": [
|
||||||
|
3,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [],
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"SDHK_v4.safetensors"
|
||||||
|
],
|
||||||
|
"color": "#571a1a",
|
||||||
|
"bgcolor": "#6b2e2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
60,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 100
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 6,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
70
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"embedding:easynegative, embedding:badhandv4, nsfw"
|
||||||
|
],
|
||||||
|
"color": "#572e1a",
|
||||||
|
"bgcolor": "#6b422e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
60,
|
||||||
|
140
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 110
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 5,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
69
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"(best quality, masterpiece), 1girl, short hair, blue eyes, dancing, city, cloudy"
|
||||||
|
],
|
||||||
|
"color": "#572e1a",
|
||||||
|
"bgcolor": "#6b422e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 39,
|
||||||
|
"type": "ControlNetApplyAdvanced",
|
||||||
|
"pos": [
|
||||||
|
471,
|
||||||
|
275
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 300,
|
||||||
|
"1": 170
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 9,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 69
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "control_net",
|
||||||
|
"type": "CONTROL_NET",
|
||||||
|
"link": 68
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 181
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
195
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
196
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "ControlNetApplyAdvanced"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"color": "#43571a",
|
||||||
|
"bgcolor": "#576b2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 103,
|
||||||
|
"type": "LoadVideo",
|
||||||
|
"pos": [
|
||||||
|
-280,
|
||||||
|
650
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
310,
|
||||||
|
629
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 3,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "frames",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
181,
|
||||||
|
182,
|
||||||
|
186
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "frame_count",
|
||||||
|
"type": "INT",
|
||||||
|
"links": null,
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "LoadVideo"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"video/265043418-23291941-864d-495a-8ba8-d02e05756396.gif",
|
||||||
|
"image",
|
||||||
|
0,
|
||||||
|
16,
|
||||||
|
"/view?filename=265043418-23291941-864d-495a-8ba8-d02e05756396.gif&type=input&subfolder=video&format=image%2Fgif"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"type": "EmptyLatentImage",
|
||||||
|
"pos": [
|
||||||
|
520,
|
||||||
|
630
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 80
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 10,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "INT",
|
||||||
|
"link": 190,
|
||||||
|
"widget": {
|
||||||
|
"name": "width",
|
||||||
|
"config": [
|
||||||
|
"INT",
|
||||||
|
{
|
||||||
|
"default": 512,
|
||||||
|
"min": 64,
|
||||||
|
"max": 8192,
|
||||||
|
"step": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "height",
|
||||||
|
"type": "INT",
|
||||||
|
"link": 191,
|
||||||
|
"widget": {
|
||||||
|
"name": "height",
|
||||||
|
"config": [
|
||||||
|
"INT",
|
||||||
|
{
|
||||||
|
"default": 512,
|
||||||
|
"min": 64,
|
||||||
|
"max": 8192,
|
||||||
|
"step": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
197
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "EmptyLatentImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
512,
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"color": "#1a572e",
|
||||||
|
"bgcolor": "#2e6b42"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 36,
|
||||||
|
"type": "ControlNetLoaderAdvanced",
|
||||||
|
"pos": [
|
||||||
|
-280,
|
||||||
|
540
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 310,
|
||||||
|
"1": 60
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "timestep_keyframe",
|
||||||
|
"type": "TIMESTEP_KEYFRAME",
|
||||||
|
"link": null,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONTROL_NET",
|
||||||
|
"type": "CONTROL_NET",
|
||||||
|
"links": [
|
||||||
|
68
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "ControlNetLoaderAdvanced"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"control_v11p_sd15_openpose.pth"
|
||||||
|
],
|
||||||
|
"color": "#571a1a",
|
||||||
|
"bgcolor": "#6b2e2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 105,
|
||||||
|
"type": "PreviewImage",
|
||||||
|
"pos": [
|
||||||
|
70,
|
||||||
|
830
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 530,
|
||||||
|
"1": 420
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 8,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 186
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "PreviewImage"
|
||||||
|
},
|
||||||
|
"color": "#1a5757",
|
||||||
|
"bgcolor": "#2e6b6b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 106,
|
||||||
|
"type": "PreviewImage",
|
||||||
|
"pos": [
|
||||||
|
670,
|
||||||
|
830
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 530,
|
||||||
|
"1": 420
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 14,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 187
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "PreviewImage"
|
||||||
|
},
|
||||||
|
"color": "#1a5757",
|
||||||
|
"bgcolor": "#2e6b6b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 104,
|
||||||
|
"type": "ImageSizeAndBatchSize",
|
||||||
|
"pos": [
|
||||||
|
258,
|
||||||
|
631
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 226.8000030517578,
|
||||||
|
"1": 80
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 7,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 182
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "width",
|
||||||
|
"type": "INT",
|
||||||
|
"links": [
|
||||||
|
190
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "height",
|
||||||
|
"type": "INT",
|
||||||
|
"links": [
|
||||||
|
191
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "batch_size",
|
||||||
|
"type": "INT",
|
||||||
|
"links": [
|
||||||
|
198
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "ImageSizeAndBatchSize"
|
||||||
|
},
|
||||||
|
"color": "#1a5757",
|
||||||
|
"bgcolor": "#2e6b6b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 44,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1000,
|
||||||
|
548
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 46
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 12,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 199
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": 82
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
172,
|
||||||
|
187
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
},
|
||||||
|
"color": "#2e571a",
|
||||||
|
"bgcolor": "#426b2e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 107,
|
||||||
|
"type": "AnimateDiffSampler",
|
||||||
|
"pos": [
|
||||||
|
881,
|
||||||
|
141
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
330,
|
||||||
|
350
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 11,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "motion_module",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"link": 193
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 194
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 195
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 197
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sliding_window_opts",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"link": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "frame_number",
|
||||||
|
"type": "INT",
|
||||||
|
"link": 198,
|
||||||
|
"widget": {
|
||||||
|
"name": "frame_number",
|
||||||
|
"config": [
|
||||||
|
"INT",
|
||||||
|
{
|
||||||
|
"default": 16,
|
||||||
|
"min": 2,
|
||||||
|
"max": 10000,
|
||||||
|
"step": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
199
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"default",
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
"randomize",
|
||||||
|
20,
|
||||||
|
8,
|
||||||
|
"euler",
|
||||||
|
"normal",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
68,
|
||||||
|
36,
|
||||||
|
0,
|
||||||
|
39,
|
||||||
|
2,
|
||||||
|
"CONTROL_NET"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
69,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
39,
|
||||||
|
0,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
70,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
39,
|
||||||
|
1,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82,
|
||||||
|
13,
|
||||||
|
0,
|
||||||
|
44,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
172,
|
||||||
|
44,
|
||||||
|
0,
|
||||||
|
45,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
181,
|
||||||
|
103,
|
||||||
|
0,
|
||||||
|
39,
|
||||||
|
3,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
182,
|
||||||
|
103,
|
||||||
|
0,
|
||||||
|
104,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
186,
|
||||||
|
103,
|
||||||
|
0,
|
||||||
|
105,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
187,
|
||||||
|
44,
|
||||||
|
0,
|
||||||
|
106,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
190,
|
||||||
|
104,
|
||||||
|
0,
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
"INT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
191,
|
||||||
|
104,
|
||||||
|
1,
|
||||||
|
20,
|
||||||
|
1,
|
||||||
|
"INT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
193,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
107,
|
||||||
|
0,
|
||||||
|
"MOTION_MODULE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
194,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
107,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
195,
|
||||||
|
39,
|
||||||
|
0,
|
||||||
|
107,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
196,
|
||||||
|
39,
|
||||||
|
1,
|
||||||
|
107,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
197,
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
107,
|
||||||
|
4,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
198,
|
||||||
|
104,
|
||||||
|
2,
|
||||||
|
107,
|
||||||
|
6,
|
||||||
|
"INT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
199,
|
||||||
|
107,
|
||||||
|
0,
|
||||||
|
44,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
@@ -0,0 +1,828 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 28,
|
||||||
|
"last_link_id": 56,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"type": "EmptyLatentImage",
|
||||||
|
"pos": [
|
||||||
|
520,
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 106
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
35
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "EmptyLatentImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
512,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"type": "Reroute",
|
||||||
|
"pos": [
|
||||||
|
440,
|
||||||
|
611
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
75,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 9,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "*",
|
||||||
|
"link": 45
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
46
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"showOutputText": false,
|
||||||
|
"horizontal": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"type": "Reroute",
|
||||||
|
"pos": [
|
||||||
|
1224,
|
||||||
|
604
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
75,
|
||||||
|
26
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 7,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "*",
|
||||||
|
"link": 44
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
45
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"showOutputText": false,
|
||||||
|
"horizontal": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"type": "AnimateDiffModuleLoader",
|
||||||
|
"pos": [
|
||||||
|
27,
|
||||||
|
345
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MOTION_MODULE",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"links": [
|
||||||
|
24,
|
||||||
|
48
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffModuleLoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"mm-Stabilized_mid.pth"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"pos": [
|
||||||
|
26,
|
||||||
|
474
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 98
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MODEL",
|
||||||
|
"type": "MODEL",
|
||||||
|
"links": [
|
||||||
|
25,
|
||||||
|
49
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIP",
|
||||||
|
"type": "CLIP",
|
||||||
|
"links": [
|
||||||
|
3,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [],
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"AnimeLike25D_v11.safetensors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"type": "LatentUpscaleBy",
|
||||||
|
"pos": [
|
||||||
|
571,
|
||||||
|
712
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 275.35137939453125,
|
||||||
|
"1": 82
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 11,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 46
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
47
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "LatentUpscaleBy"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"nearest-exact",
|
||||||
|
1.5
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
415,
|
||||||
|
186
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 422.84503173828125,
|
||||||
|
"1": 164.31304931640625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
29,
|
||||||
|
50
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"masterpiece, best quality, 1girl, solo, cherry blossoms, hanami, pink flower, white flower, spring season, wisteria, petals, flower, plum blossoms, outdoors, falling petals, white hair, black eyes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
413,
|
||||||
|
389
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 425.27801513671875,
|
||||||
|
"1": 180.6060791015625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 5,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
30,
|
||||||
|
51
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"embedding:easynegative, embedding:badhandv4, "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"type": "AnimateDiffSampler",
|
||||||
|
"pos": [
|
||||||
|
893,
|
||||||
|
712
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 350
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 12,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "motion_module",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"link": 48,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 49,
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 51
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 47
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sliding_window_opts",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
52
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"default",
|
||||||
|
16,
|
||||||
|
345029849956687,
|
||||||
|
"increment",
|
||||||
|
20,
|
||||||
|
8,
|
||||||
|
"euler",
|
||||||
|
"normal",
|
||||||
|
0.4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1239,
|
||||||
|
712
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 46
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 13,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": 20
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "AnimateDiffSampler",
|
||||||
|
"pos": [
|
||||||
|
882,
|
||||||
|
192
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 350
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 6,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "motion_module",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"link": 24,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 25,
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 35
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sliding_window_opts",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
44,
|
||||||
|
53
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"default",
|
||||||
|
16,
|
||||||
|
345029849956687,
|
||||||
|
"increment",
|
||||||
|
20,
|
||||||
|
8,
|
||||||
|
"euler",
|
||||||
|
"normal",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"type": "VAELoader",
|
||||||
|
"pos": [
|
||||||
|
27,
|
||||||
|
631
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 3,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [
|
||||||
|
20,
|
||||||
|
54
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAELoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"klF8Anime2.ckpt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1258,
|
||||||
|
187
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 46
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 8,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 53
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": 54
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
56
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"type": "AnimateDiffCombine",
|
||||||
|
"pos": [
|
||||||
|
1505,
|
||||||
|
-72
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 321.19171142578125,
|
||||||
|
"1": 513.0408935546875
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 10,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 56
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "GIF",
|
||||||
|
"type": "GIF",
|
||||||
|
"links": null,
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffCombine"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
"AnimateDiff",
|
||||||
|
"image/gif",
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"type": "AnimateDiffCombine",
|
||||||
|
"pos": [
|
||||||
|
1504,
|
||||||
|
481
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 325.7265625,
|
||||||
|
"1": 517.7265625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 14,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 19
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "GIF",
|
||||||
|
"type": "GIF",
|
||||||
|
"links": null,
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffCombine"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
"AnimateDiff",
|
||||||
|
"image/gif",
|
||||||
|
false
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
19,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
13,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
24,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
"MOTION_MODULE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
25,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
35,
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
4,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
44,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
24,
|
||||||
|
0,
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
45,
|
||||||
|
24,
|
||||||
|
0,
|
||||||
|
25,
|
||||||
|
0,
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
46,
|
||||||
|
25,
|
||||||
|
0,
|
||||||
|
22,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
47,
|
||||||
|
22,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
4,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
"MOTION_MODULE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
49,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
50,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
51,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
52,
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
53,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
27,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
54,
|
||||||
|
13,
|
||||||
|
0,
|
||||||
|
27,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
56,
|
||||||
|
27,
|
||||||
|
0,
|
||||||
|
28,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
@@ -0,0 +1,515 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 21,
|
||||||
|
"last_link_id": 38,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
415,
|
||||||
|
186
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 422.84503173828125,
|
||||||
|
"1": 164.31304931640625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
29
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"photo of coastline, rocks, storm weather, wind, waves, lightning, 8k uhd, dslr, soft lighting, high quality, film grain, Fujifilm XT3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1253,
|
||||||
|
191
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 46
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 8,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": 20
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"type": "AnimateDiffCombine",
|
||||||
|
"pos": [
|
||||||
|
1254,
|
||||||
|
290
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
315,
|
||||||
|
507
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 9,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 19
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffCombine"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
"AnimateDiff",
|
||||||
|
"image/gif",
|
||||||
|
false,
|
||||||
|
"/view?filename=AnimateDiff_00003_.gif&subfolder=&type=temp&format=image%2Fgif"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
413,
|
||||||
|
389
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 425.27801513671875,
|
||||||
|
"1": 180.6060791015625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 5,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
30
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"blur, haze, deformed iris, deformed pupils, semi-realistic, cgi, 3d, render, sketch, cartoon, drawing, anime, mutated hands and fingers, deformed, distorted, disfigured, poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, disconnected limbs, mutation, mutated, ugly, disgusting, amputation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"type": "EmptyLatentImage",
|
||||||
|
"pos": [
|
||||||
|
522,
|
||||||
|
621
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 106
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
35
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "EmptyLatentImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
512,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"type": "VAELoader",
|
||||||
|
"pos": [
|
||||||
|
28,
|
||||||
|
223
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAELoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"vae-ft-mse-840000-ema-pruned.safetensors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "AnimateDiffSampler",
|
||||||
|
"pos": [
|
||||||
|
882,
|
||||||
|
192
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 350
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 7,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "motion_module",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"link": 24,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 25,
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 35
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sliding_window_opts",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
28
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"default",
|
||||||
|
14,
|
||||||
|
45987230,
|
||||||
|
"fixed",
|
||||||
|
25,
|
||||||
|
7.5,
|
||||||
|
"ddim",
|
||||||
|
"ddim_uniform",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"type": "AnimateDiffModuleLoader",
|
||||||
|
"pos": [
|
||||||
|
27,
|
||||||
|
345
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 6,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "lora_stack",
|
||||||
|
"type": "MOTION_LORA_STACK",
|
||||||
|
"link": 38,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MOTION_MODULE",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"links": [
|
||||||
|
24
|
||||||
|
],
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffModuleLoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"mm_sd_v15_v2.ckpt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"type": "AnimateDiffLoraLoader",
|
||||||
|
"pos": [
|
||||||
|
-317,
|
||||||
|
350
|
||||||
|
],
|
||||||
|
"size": [
|
||||||
|
310,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"flags": {},
|
||||||
|
"order": 3,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "lora_stack",
|
||||||
|
"type": "MOTION_LORA_STACK",
|
||||||
|
"link": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MOTION_LORA_STACK",
|
||||||
|
"type": "MOTION_LORA_STACK",
|
||||||
|
"links": [
|
||||||
|
38
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffLoraLoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"v2_lora_ZoomIn.ckpt",
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"pos": [
|
||||||
|
28,
|
||||||
|
457
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 98
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MODEL",
|
||||||
|
"type": "MODEL",
|
||||||
|
"links": [
|
||||||
|
25
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIP",
|
||||||
|
"type": "CLIP",
|
||||||
|
"links": [
|
||||||
|
3,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [],
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"RealisticVision_v20.safetensors"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
19,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
13,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
24,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
"MOTION_MODULE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
25,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
28,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
35,
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
4,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
38,
|
||||||
|
21,
|
||||||
|
0,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
"MOTION_LORA_STACK"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
@@ -0,0 +1,502 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 21,
|
||||||
|
"last_link_id": 36,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
415,
|
||||||
|
186
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 422.84503173828125,
|
||||||
|
"1": 164.31304931640625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 5,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
29
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"masterpiece, best quality, 1girl, solo, cherry blossoms, hanami, pink flower, white flower, spring season, wisteria, petals, flower, plum blossoms, outdoors, falling petals, white hair, black eyes"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"type": "VAEDecode",
|
||||||
|
"pos": [
|
||||||
|
1253,
|
||||||
|
191
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 46
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 8,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "samples",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vae",
|
||||||
|
"type": "VAE",
|
||||||
|
"link": 20
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
19
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAEDecode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"type": "AnimateDiffCombine",
|
||||||
|
"pos": [
|
||||||
|
1254,
|
||||||
|
290
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 342
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 9,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 19
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffCombine"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
"AnimateDiff",
|
||||||
|
"image/gif",
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"type": "AnimateDiffModuleLoader",
|
||||||
|
"pos": [
|
||||||
|
27,
|
||||||
|
345
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MOTION_MODULE",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"links": [
|
||||||
|
24
|
||||||
|
],
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffModuleLoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"mm-Stabilized_mid.pth"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"pos": [
|
||||||
|
26,
|
||||||
|
474
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 98
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MODEL",
|
||||||
|
"type": "MODEL",
|
||||||
|
"links": [
|
||||||
|
25
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIP",
|
||||||
|
"type": "CLIP",
|
||||||
|
"links": [
|
||||||
|
3,
|
||||||
|
5
|
||||||
|
],
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [],
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"AnimeLike25D_v11.safetensors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"type": "VAELoader",
|
||||||
|
"pos": [
|
||||||
|
28,
|
||||||
|
223
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [
|
||||||
|
20
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "VAELoader"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"klF8Anime2.ckpt"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
413,
|
||||||
|
389
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 425.27801513671875,
|
||||||
|
"1": 180.6060791015625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 6,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
30
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"embedding:easynegative, embedding:badhandv4, "
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"type": "EmptyLatentImage",
|
||||||
|
"pos": [
|
||||||
|
522,
|
||||||
|
621
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 106
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 3,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
35
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "EmptyLatentImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
512,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"type": "AnimateDiffSlidingWindowOptions",
|
||||||
|
"pos": [
|
||||||
|
517,
|
||||||
|
-34
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 154
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "SLIDING_WINDOW_OPTS",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"links": [
|
||||||
|
36
|
||||||
|
],
|
||||||
|
"shape": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSlidingWindowOptions"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
16,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
"uniform",
|
||||||
|
true
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "AnimateDiffSampler",
|
||||||
|
"pos": [
|
||||||
|
882,
|
||||||
|
192
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 350
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 7,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "motion_module",
|
||||||
|
"type": "MOTION_MODULE",
|
||||||
|
"link": 24,
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 25,
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 35
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sliding_window_opts",
|
||||||
|
"type": "SLIDING_WINDOW_OPTS",
|
||||||
|
"link": 36,
|
||||||
|
"slot_index": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
28
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "AnimateDiffSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"default",
|
||||||
|
40,
|
||||||
|
345029849956677,
|
||||||
|
"fixed",
|
||||||
|
20,
|
||||||
|
8,
|
||||||
|
"euler",
|
||||||
|
"normal",
|
||||||
|
0.8
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
19,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
12,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
20,
|
||||||
|
13,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
24,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
"MOTION_MODULE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
25,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
1,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
28,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30,
|
||||||
|
7,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
3,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
35,
|
||||||
|
20,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
4,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
36,
|
||||||
|
21,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
5,
|
||||||
|
"SLIDING_WINDOW_OPTS"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user