Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76954ab102 | ||
|
|
71a6fb94e8 | ||
|
|
a84cd191bf | ||
|
|
80d1046623 | ||
|
|
61489e1a17 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -21,7 +21,6 @@
|
||||
*.html
|
||||
*.pdf
|
||||
*.whl
|
||||
*.exe
|
||||
cache
|
||||
__pycache__/
|
||||
storage/
|
||||
@@ -35,8 +34,3 @@ Wan2.1-T2V-14B/
|
||||
Wan2.1-T2V-1.3B/
|
||||
Wan2.1-I2V-14B-480P/
|
||||
Wan2.1-I2V-14B-720P/
|
||||
outputs/
|
||||
gradio_outputs/
|
||||
ckpts/
|
||||
loras/
|
||||
loras_i2v/
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
You can override the choice of Resolutions offered by WanGP, if you create a file "resolutions.json" in the main WanGP folder.
|
||||
This file is composed of a list of 2 elements sublists. Each 2 elements sublist should have the format ["Label", "WxH"] where W, H are respectively the Width and Height of the resolution. Please make sure that W and H are multiples of 16. The letter "x" should be placed inbetween these two dimensions.
|
||||
|
||||
Here is below a sample "resolutions.json" file :
|
||||
|
||||
[
|
||||
["1280x720 (16:9, 720p)", "1280x720"],
|
||||
["720x1280 (9:16, 720p)", "720x1280"],
|
||||
["1024x1024 (1:1, 720p)", "1024x1024"],
|
||||
["1280x544 (21:9, 720p)", "1280x544"],
|
||||
["544x1280 (9:21, 720p)", "544x1280"],
|
||||
["1104x832 (4:3, 720p)", "1104x832"],
|
||||
["832x1104 (3:4, 720p)", "832x1104"],
|
||||
["960x960 (1:1, 720p)", "960x960"],
|
||||
["832x480 (16:9, 480p)", "832x480"]
|
||||
]
|
||||
477
README.md
477
README.md
@@ -1,212 +1,361 @@
|
||||
# WanGP
|
||||
# Wan2.1 GP
|
||||
|
||||
|
||||
<p align="center">
|
||||
💜 <a href=""><b>Wan</b></a>    |    🖥️ <a href="https://github.com/Wan-Video/Wan2.1">GitHub</a>    |   🤗 <a href="https://huggingface.co/Wan-AI/">Hugging Face</a>   |   🤖 <a href="https://modelscope.cn/organization/Wan-AI">ModelScope</a>   |    📑 <a href="">Paper (Coming soon)</a>    |    📑 <a href="https://wanxai.com">Blog</a>    |   💬 <a href="https://gw.alicdn.com/imgextra/i2/O1CN01tqjWFi1ByuyehkTSB_!!6000000000015-0-tps-611-1279.jpg">WeChat Group</a>   |    📖 <a href="https://discord.gg/p5XbdQV7">Discord</a>  
|
||||
<br>
|
||||
|
||||
-----
|
||||
<p align="center">
|
||||
<b>WanGP by DeepBeepMeep : The best Open Source Video Generative Models Accessible to the GPU Poor</b>
|
||||
<b>Wan2.1 GP by DeepBeepMeep based on Wan2.1's Alibaba: Open and Advanced Large-Scale Video Generative Models for the GPU Poor</b>
|
||||
</p>
|
||||
|
||||
WanGP supports the Wan (and derived models), Hunyuan Video and LTV Video models with:
|
||||
- Low VRAM requirements (as low as 6 GB of VRAM is sufficient for certain models)
|
||||
- Support for old GPUs (RTX 10XX, 20xx, ...)
|
||||
- Very Fast on the latest GPUs
|
||||
- Easy to use Full Web based interface
|
||||
- Auto download of the required model adapted to your specific architecture
|
||||
- Tools integrated to facilitate Video Generation : Mask Editor, Prompt Enhancer, Temporal and Spatial Generation, MMAudio, Video Browser, Pose / Depth / Flow extractor
|
||||
- Loras Support to customize each model
|
||||
- Queuing system : make your shopping list of videos to generate and come back later
|
||||
|
||||
**Discord Server to get Help from Other Users and show your Best Videos:** https://discord.gg/g7efUW9jGV
|
||||
|
||||
**Follow DeepBeepMeep on Twitter/X to get the Latest News**: https://x.com/deepbeepmeep
|
||||
|
||||
## 🔥 Latest Updates
|
||||
Wan 2.2 is here. The good news is that WanGP wont require a single byte of extra VRAM to run it and it will be as fast as Wan 2.1. The bad news is that you will need much more RAM if you want to leverage entirely this new model since it has twice has many parameters.
|
||||
|
||||
So here is a preview version of Wan 2.2 that is without the 5B model and Wan 2.2 image to video for the moment.
|
||||
|
||||
However as I felt bad to deliver only half of the wares, I gave you instead .....** Wan 2.2 Vace Cocktail** !
|
||||
|
||||
Very good surprise indeed, the loras and Vace mostly work with Wan 2.2 !!! I have made also a light version of the cocktail that uses only half of the parameters of Wan 2.2, this version has exactly the same RAM requirements as Wan 2.1. but Videos baked with half of the model are not so good. Maybe they are better than Wan 2.1 . So you tell me if we should keep the light version.
|
||||
|
||||
Probably Multitalk should work too, but I have a life to attend to so I will let you test.
|
||||
|
||||
Bonus zone: Flux multi images conditions has been added, or maybe not if I broke everything as I have been distracted by Wan...
|
||||
|
||||
### July 27 2025: WanGP v7.3 : Interlude
|
||||
While waiting for Wan 2.2, you will appreciate the model selection hierarchy which is very useful to collect even more models. You will also appreciate that WanGP remembers which model you used last in each model family.
|
||||
|
||||
### July 26 2025: WanGP v7.2 : Ode to Vace
|
||||
I am really convinced that Vace can do everything the other models can do and in a better way especially as Vace can be combined with Multitalk.
|
||||
|
||||
Here are some new Vace improvements:
|
||||
- I have provided a default finetune named *Vace Cocktail* which is a model created on the fly using the Wan text 2 video model and the Loras used to build FusioniX. The weight of the *Detail Enhancer* Lora has been reduced to improve identity preservation. Copy the model definition in *defaults/vace_14B_cocktail.json* in the *finetunes/* folder to change the Cocktail composition. Cocktail contains already some Loras acccelerators so no need to add on top a Lora Accvid, Causvid or Fusionix, ... . The whole point of Cocktail is to be able to build you own FusioniX (which originally is a combination of 4 loras) but without the inconvenient of FusioniX.
|
||||
- Talking about identity preservation, it tends to go away when one generates a single Frame instead of a Video which is shame for our Vace photoshop. But there is a solution : I have added an Advanced Quality option, that tells WanGP to generate a little more than a frame (it will still keep only the first frame). It will be a little slower but you will be amazed how Vace Cocktail combined with this option will preserve identities (bye bye *Phantom*).
|
||||
- As in practise I have observed one switches frequently between *Vace text2video* and *Vace text2image* I have put them in the same place they are now just one tab away, no need to reload the model. Likewise *Wan text2video* and *Wan tex2image* have been merged.
|
||||
- Color fixing when using Sliding Windows. A new postprocessing *Color Correction* applied automatically by default (you can disable it in the *Advanced tab Sliding Window*) will try to match the colors of the new window with that of the previous window. It doesnt fix all the unwanted artifacts of the new window but at least this makes the transition smoother. Thanks to the multitalk team for the original code.
|
||||
|
||||
Also you will enjoy our new real time statistics (CPU / GPU usage, RAM / VRAM used, ... ). Many thanks to **Redtash1** for providing the framework for this new feature ! You need to go in the Config tab to enable real time stats.
|
||||
|
||||
|
||||
### July 21 2025: WanGP v7.12
|
||||
- Flux Family Reunion : *Flux Dev* and *Flux Schnell* have been invited aboard WanGP. To celebrate that, Loras support for the Flux *diffusers* format has also been added.
|
||||
|
||||
- LTX Video upgraded to version 0.9.8: you can now generate 1800 frames (1 min of video !) in one go without a sliding window. With the distilled model it will take only 5 minutes with a RTX 4090 (you will need 22 GB of VRAM though). I have added options to select higher humber frames if you want to experiment (go to Configuration Tab / General / Increase the Max Number of Frames, change the value and restart the App)
|
||||
## 🔥 Latest News!!
|
||||
* April 4 2025: 👋 Wan 2.1GP v4.0: lots of goodies for you !
|
||||
- A Simplified interface that lets you switch quickly between the numerous Wan derived models
|
||||
- A new queuing system that lets you stack in a queue as many text2video and imag2video tasks as you want. Each task can rely on complete different generation parameters (different model, different number of frames, steps, loras, ...). Many thanks to **Tophness** for his huge contribution to this new useful feature.
|
||||
- Temporal upsampling (Rife) and spatial upsampling (Lanczos) for a smoother video (32 fps or 64 fps) and to enlarge you video by x2 or x4. Check these new advanced options.
|
||||
- Wan Vace Control Net support : with Vace you can inject in the scene people or objects, animate a person, perform inpainting or outpainting, continue a video, ... I have provided an introduction guide below.
|
||||
* Mar 27 2025: 👋 Added support for the new Wan Fun InP models (image2video). The 14B Fun InP has probably better end image support but unfortunately existing loras do not work so well with it. The great novelty is the Fun InP image2 1.3B model : Image 2 Video is now accessible to even lower hardware configuration. It is not as good as the 14B models but very impressive for its size. You can choose any of those models in the Configuration tab. Many thanks to the VideoX-Fun team (https://github.com/aigc-apps/VideoX-Fun)
|
||||
* Mar 26 2025: 👋 Good news ! Official support for RTX 50xx please check the installation instructions below.
|
||||
* Mar 24 2025: 👋 Wan2.1GP v3.2:
|
||||
- Added Classifier-Free Guidance Zero Star. The video should match better the text prompt (especially with text2video) at no performance cost: many thanks to the **CFG Zero * Team:**\
|
||||
Dont hesitate to give them a star if you appreciate the results: https://github.com/WeichenFan/CFG-Zero-star
|
||||
- Added back support for Pytorch compilation with Loras. It seems it had been broken for some time
|
||||
- Added possibility to keep a number of pregenerated videos in the Video Gallery (useful to compare outputs of different settings)
|
||||
You will need one more *pip install -r requirements.txt*
|
||||
* Mar 19 2025: 👋 Wan2.1GP v3.1: Faster launch and RAM optimizations (should require less RAM to run)\
|
||||
You will need one more *pip install -r requirements.txt*
|
||||
* Mar 18 2025: 👋 Wan2.1GP v3.0:
|
||||
- New Tab based interface, yon can switch from i2v to t2v conversely without restarting the app
|
||||
- Experimental Dual Frames mode for i2v, you can also specify an End frame. It doesn't always work, so you will need a few attempts.
|
||||
- You can save default settings in the files *i2v_settings.json* and *t2v_settings.json* that will be used when launching the app (you can also specify the path to different settings files)
|
||||
- Slight acceleration with loras\
|
||||
You will need one more *pip install -r requirements.txt*
|
||||
Many thanks to *Tophness* who created the framework (and did a big part of the work) of the multitabs and saved settings features
|
||||
* Mar 18 2025: 👋 Wan2.1GP v2.11: Added more command line parameters to prefill the generation settings + customizable output directory and choice of type of metadata for generated videos. Many thanks to *Tophness* for his contributions. You will need one more *pip install -r requirements.txt* to reflect new dependencies\
|
||||
* Mar 18 2025: 👋 Wan2.1GP v2.1: More Loras !: added support for 'Safetensors' and 'Replicate' Lora formats.\
|
||||
You will need to refresh the requirements with a *pip install -r requirements.txt*
|
||||
* Mar 17 2025: 👋 Wan2.1GP v2.0: The Lora festival continues:
|
||||
- Clearer user interface
|
||||
- Download 30 Loras in one click to try them all (expand the info section)
|
||||
- Very to use Loras as now Lora presets can input the subject (or other need terms) of the Lora so that you dont have to modify manually a prompt
|
||||
- Added basic macro prompt language to prefill prompts with differnent values. With one prompt template, you can generate multiple prompts.
|
||||
- New Multiple images prompts: you can now combine any number of images with any number of text promtps (need to launch the app with --multiple-images)
|
||||
- New command lines options to launch directly the 1.3B t2v model or the 14B t2v model
|
||||
* Mar 14, 2025: 👋 Wan2.1GP v1.7:
|
||||
- Lora Fest special edition: very fast loading / unload of loras for those Loras collectors around. You can also now add / remove loras in the Lora folder without restarting the app. You will need to refresh the requirements *pip install -r requirements.txt*
|
||||
- Added experimental Skip Layer Guidance (advanced settings), that should improve the image quality at no extra cost. Many thanks to the *AmericanPresidentJimmyCarter* for the original implementation
|
||||
* Mar 13, 2025: 👋 Wan2.1GP v1.6: Better Loras support, accelerated loading Loras. You will need to refresh the requirements *pip install -r requirements.txt*
|
||||
* Mar 10, 2025: 👋 Wan2.1GP v1.5: Official Teacache support + Smart Teacache (find automatically best parameters for a requested speed multiplier), 10% speed boost with no quality loss, improved lora presets (they can now include prompts and comments to guide the user)
|
||||
* Mar 07, 2025: 👋 Wan2.1GP v1.4: Fix Pytorch compilation, now it is really 20% faster when activated
|
||||
* Mar 04, 2025: 👋 Wan2.1GP v1.3: Support for Image to Video with multiples images for different images / prompts combinations (requires *--multiple-images* switch), and added command line *--preload x* to preload in VRAM x MB of the main diffusion model if you find there is too much unused VRAM and you want to (slightly) accelerate the generation process.
|
||||
If you upgrade you will need to do a 'pip install -r requirements.txt' again.
|
||||
* Mar 04, 2025: 👋 Wan2.1GP v1.2: Implemented tiling on VAE encoding and decoding. No more VRAM peaks at the beginning and at the end
|
||||
* Mar 03, 2025: 👋 Wan2.1GP v1.1: added Tea Cache support for faster generations: optimization of kijai's implementation (https://github.com/kijai/ComfyUI-WanVideoWrapper/) of teacache (https://github.com/ali-vilab/TeaCache)
|
||||
* Mar 02, 2025: 👋 Wan2.1GP by DeepBeepMeep v1 brings:
|
||||
- Support for all Wan including the Image to Video model
|
||||
- Reduced memory consumption by 2, with possiblity to generate more than 10s of video at 720p with a RTX 4090 and 10s of video at 480p with less than 12GB of VRAM. Many thanks to REFLEx (https://github.com/thu-ml/RIFLEx) for their algorithm that allows generating nice looking video longer than 5s.
|
||||
- The usual perks: web interface, multiple generations, loras support, sage attebtion, auto download of models, ...
|
||||
|
||||
- LTX Video ControlNet : it is a Control Net that allows you for instance to transfer a Human motion or Depth from a control video. It is not as powerful as Vace but can produce interesting things especially as now you can generate quickly a 1 min video. Under the scene IC-Loras (see below) for Pose, Depth and Canny are automatically loaded for you, no need to add them.
|
||||
|
||||
- LTX IC-Lora support: these are special Loras that consumes a conditional image or video
|
||||
Beside the pose, depth and canny IC-Loras transparently loaded there is the *detailer* (https://huggingface.co/Lightricks/LTX-Video-ICLoRA-detailer-13b-0.9.8) which is basically an upsampler. Add the *detailer* as a Lora and use LTX Raw Format as control net choice to use it.
|
||||
|
||||
- Matanyone is now also for the GPU Poor as its VRAM requirements have been divided by 2! (7.12 shadow update)
|
||||
|
||||
- Easier way to select video resolution
|
||||
* Feb 25, 2025: 👋 We've released the inference code and weights of Wan2.1.
|
||||
* Feb 27, 2025: 👋 Wan2.1 has been integrated into [ComfyUI](https://comfyanonymous.github.io/ComfyUI_examples/wan/). Enjoy!
|
||||
|
||||
|
||||
### July 15 2025: WanGP v7.0 is an AI Powered Photoshop
|
||||
This release turns the Wan models into Image Generators. This goes way more than allowing to generate a video made of single frame :
|
||||
- Multiple Images generated at the same time so that you can choose the one you like best.It is Highly VRAM optimized so that you can generate for instance 4 720p Images at the same time with less than 10 GB
|
||||
- With the *image2image* the original text2video WanGP becomes an image upsampler / restorer
|
||||
- *Vace image2image* comes out of the box with image outpainting, person / object replacement, ...
|
||||
- You can use in one click a newly Image generated as Start Image or Reference Image for a Video generation
|
||||
## Features
|
||||
*GPU Poor version by **DeepBeepMeep**. This great video generator can now run smoothly on any GPU.*
|
||||
|
||||
And to complete the full suite of AI Image Generators, Ladies and Gentlemen please welcome for the first time in WanGP : **Flux Kontext**.\
|
||||
As a reminder Flux Kontext is an image editor : give it an image and a prompt and it will do the change for you.\
|
||||
This highly optimized version of Flux Kontext will make you feel that you have been cheated all this time as WanGP Flux Kontext requires only 8 GB of VRAM to generate 4 images at the same time with no need for quantization.
|
||||
|
||||
WanGP v7 comes with *Image2image* vanilla and *Vace FusinoniX*. However you can build your own finetune where you will combine a text2video or Vace model with any combination of Loras.
|
||||
|
||||
Also in the news:
|
||||
- You can now enter the *Bbox* for each speaker in *Multitalk* to precisely locate who is speaking. And to save some headaches the *Image Mask generator* will give you the *Bbox* coordinates of an area you have selected.
|
||||
- *Film Grain* post processing to add a vintage look at your video
|
||||
- *First Last Frame to Video* model should work much better now as I have discovered rencently its implementation was not complete
|
||||
- More power for the finetuners, you can now embed Loras directly in the finetune definition. You can also override the default models (titles, visibility, ...) with your own finetunes. Check the doc that has been updated.
|
||||
This version has the following improvements over the original Alibaba model:
|
||||
- Reduce greatly the RAM requirements and VRAM requirements
|
||||
- Much faster thanks to compilation and fast loading / unloading
|
||||
- Multiple profiles in order to able to run the model at a decent speed on a low end consumer config (32 GB of RAM and 12 VRAM) and to run it at a very good speed on a high end consumer config (48 GB of RAM and 24 GB of VRAM)
|
||||
- Autodownloading of the needed model files
|
||||
- Improved gradio interface with progression bar and more options
|
||||
- Multiples prompts / multiple generations per prompt
|
||||
- Support multiple pretrained Loras with 32 GB of RAM or less
|
||||
- Much simpler installation
|
||||
|
||||
|
||||
### July 10 2025: WanGP v6.7, is NAG a game changer ? you tell me
|
||||
Maybe you knew that already but most *Loras accelerators* we use today (Causvid, FusioniX) don't use *Guidance* at all (that it is *CFG* is set to 1). This helps to get much faster generations but the downside is that *Negative Prompts* are completely ignored (including the default ones set by the models). **NAG** (https://github.com/ChenDarYen/Normalized-Attention-Guidance) aims to solve that by injecting the *Negative Prompt* during the *attention* processing phase.
|
||||
This fork by DeepBeepMeep is an integration of the mmpg module on the original model
|
||||
|
||||
So WanGP 6.7 gives you NAG, but not any NAG, a *Low VRAM* implementation, the default one ends being VRAM greedy. You will find NAG in the *General* advanced tab for most Wan models.
|
||||
It is an illustration on how one can set up on an existing model some fast and properly working CPU offloading with changing only a few lines of code in the core model.
|
||||
|
||||
Use NAG especially when Guidance is set to 1. To turn it on set the **NAG scale** to something around 10. There are other NAG parameters **NAG tau** and **NAG alpha** which I recommend to change only if you don't get good results by just playing with the NAG scale. Don't hesitate to share on this discord server the best combinations for these 3 parameters.
|
||||
For more information on how to use the mmpg module, please go to: https://github.com/deepbeepmeep/mmgp
|
||||
|
||||
The authors of NAG claim that NAG can also be used when using a Guidance (CFG > 1) and to improve the prompt adherence.
|
||||
You will find the original Wan2.1 Video repository here: https://github.com/Wan-Video/Wan2.1
|
||||
|
||||
### July 8 2025: WanGP v6.6, WanGP offers you **Vace Multitalk Dual Voices Fusionix Infinite** :
|
||||
**Vace** our beloved super Control Net has been combined with **Multitalk** the new king in town that can animate up to two people speaking (**Dual Voices**). It is accelerated by the **Fusionix** model and thanks to *Sliding Windows* support and *Adaptive Projected Guidance* (much slower but should reduce the reddish effect with long videos) your two people will be able to talk for very a long time (which is an **Infinite** amount of time in the field of video generation).
|
||||
|
||||
|
||||
Of course you will get as well *Multitalk* vanilla and also *Multitalk 720p* as a bonus.
|
||||
|
||||
And since I am mister nice guy I have enclosed as an exclusivity an *Audio Separator* that will save you time to isolate each voice when using Multitalk with two people.
|
||||
## Installation Guide for Linux and Windows for GPUs up to RTX40xx
|
||||
|
||||
As I feel like resting a bit I haven't produced yet a nice sample Video to illustrate all these new capabilities. But here is the thing, I ams sure you will publish in the *Share Your Best Video* channel your *Master Pieces*. The best ones will be added to the *Announcements Channel* and will bring eternal fame to its authors.
|
||||
**If you are looking for a one click installation, just go to the Pinokio App store : https://pinokio.computer/**
|
||||
|
||||
But wait, there is more:
|
||||
- Sliding Windows support has been added anywhere with Wan models, so imagine with text2video recently upgraded in 6.5 into a video2video, you can now upsample very long videos regardless of your VRAM. The good old image2video model can now reuse the last image to produce new videos (as requested by many of you)
|
||||
- I have added also the capability to transfer the audio of the original control video (Misc. advanced tab) and an option to preserve the fps into the generated video, so from now on you will be to upsample / restore your old families video and keep the audio at their original pace. Be aware that the duration will be limited to 1000 frames as I still need to add streaming support for unlimited video sizes.
|
||||
Otherwise you will find the instructions below:
|
||||
|
||||
Also, of interest too:
|
||||
- Extract video info from Videos that have not been generated by WanGP, even better you can also apply post processing (Upsampling / MMAudio) on non WanGP videos
|
||||
- Force the generated video fps to your liking, works wery well with Vace when using a Control Video
|
||||
- Ability to chain URLs of Finetune models (for instance put the URLs of a model in your main finetune and reference this finetune in other finetune models to save time)
|
||||
This app has been tested on Python 3.10 / 2.6.0 / Cuda 12.4.
|
||||
|
||||
### July 2 2025: WanGP v6.5.1, WanGP takes care of you: lots of quality of life features:
|
||||
- View directly inside WanGP the properties (seed, resolutions, length, most settings...) of the past generations
|
||||
- In one click use the newly generated video as a Control Video or Source Video to be continued
|
||||
- Manage multiple settings for the same model and switch between them using a dropdown box
|
||||
- WanGP will keep the last generated videos in the Gallery and will remember the last model you used if you restart the app but kept the Web page open
|
||||
- Custom resolutions : add a file in the WanGP folder with the list of resolutions you want to see in WanGP (look at the instruction readme in this folder)
|
||||
|
||||
Taking care of your life is not enough, you want new stuff to play with ?
|
||||
- MMAudio directly inside WanGP : add an audio soundtrack that matches the content of your video. By the way it is a low VRAM MMAudio and 6 GB of VRAM should be sufficient. You will need to go in the *Extensions* tab of the WanGP *Configuration* to enable MMAudio
|
||||
- Forgot to upsample your video during the generation ? want to try another MMAudio variation ? Fear not you can also apply upsampling or add an MMAudio track once the video generation is done. Even better you can ask WangGP for multiple variations of MMAudio to pick the one you like best
|
||||
- MagCache support: a new step skipping approach, supposed to be better than TeaCache. Makes a difference if you usually generate with a high number of steps
|
||||
- SageAttention2++ support : not just the compatibility but also a slightly reduced VRAM usage
|
||||
- Video2Video in Wan Text2Video : this is the paradox, a text2video can become a video2video if you start the denoising process later on an existing video
|
||||
- FusioniX upsampler: this is an illustration of Video2Video in Text2Video. Use the FusioniX text2video model with an output resolution of 1080p and a denoising strength of 0.25 and you will get one of the best upsamplers (in only 2/3 steps, you will need lots of VRAM though). Increase the denoising strength and you will get one of the best Video Restorer
|
||||
- Choice of Wan Samplers / Schedulers
|
||||
- More Lora formats support
|
||||
|
||||
**If you had upgraded to v6.5 please upgrade again to 6.5.1 as this will fix a bug that ignored Loras beyond the first one**
|
||||
|
||||
See full changelog: **[Changelog](docs/CHANGELOG.md)**
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
- [🚀 Quick Start](#-quick-start)
|
||||
- [📦 Installation](#-installation)
|
||||
- [🎯 Usage](#-usage)
|
||||
- [📚 Documentation](#-documentation)
|
||||
- [🔗 Related Projects](#-related-projects)
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
**One-click installation:** Get started instantly with [Pinokio App](https://pinokio.computer/)
|
||||
|
||||
**Manual installation:**
|
||||
```bash
|
||||
```shell
|
||||
# 0 Download the source and create a Python 3.10.9 environment using conda or create a venv using python
|
||||
git clone https://github.com/deepbeepmeep/Wan2GP.git
|
||||
cd Wan2GP
|
||||
conda create -n wan2gp python=3.10.9
|
||||
conda activate wan2gp
|
||||
pip install torch==2.7.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu124
|
||||
|
||||
# 1 Install pytorch 2.6.0
|
||||
pip install torch==2.6.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu124
|
||||
|
||||
# 2. Install pip dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 3.1 optional Sage attention support (30% faster)
|
||||
# Windows only: extra step only needed for windows as triton is included in pytorch with the Linux version of pytorch
|
||||
pip install triton-windows
|
||||
# For both Windows and Linux
|
||||
pip install sageattention==1.0.6
|
||||
|
||||
|
||||
# 3.2 optional Sage 2 attention support (40% faster)
|
||||
# Windows only
|
||||
pip install triton-windows
|
||||
pip install https://github.com/woct0rdho/SageAttention/releases/download/v2.1.1-windows/sageattention-2.1.1+cu126torch2.6.0-cp310-cp310-win_amd64.whl
|
||||
# Linux only (sorry only manual compilation for the moment, but is straight forward with Linux)
|
||||
git clone https://github.com/thu-ml/SageAttention
|
||||
cd SageAttention
|
||||
pip install -e .
|
||||
|
||||
# 3.3 optional Flash attention support (easy to install on Linux but may be complex on Windows as it will try to compile the cuda kernels)
|
||||
pip install flash-attn==2.7.2.post1
|
||||
|
||||
```
|
||||
|
||||
**Run the application:**
|
||||
```bash
|
||||
python wgp.py # Text-to-video (default)
|
||||
python wgp.py --i2v # Image-to-video
|
||||
```
|
||||
Note pytorch *sdpa attention* is available by default. It is worth installing *Sage attention* (albout not as simple as it sounds) because it offers a 30% speed boost over *sdpa attention* at a small quality cost.
|
||||
In order to install Sage, you will need to install also Triton. If Triton is installed you can turn on *Pytorch Compilation* which will give you an additional 20% speed boost and reduced VRAM consumption.
|
||||
|
||||
**Update the application:**
|
||||
If using Pinokio use Pinokio to update otherwise:
|
||||
Get in the directory where WanGP is installed and:
|
||||
```bash
|
||||
git pull
|
||||
## Installation Guide for Linux and Windows for GPUs up to RTX50xx
|
||||
RTX50XX are only supported by pytorch starting from pytorch 2.7.0 which is still in beta. Therefore this version may be less stable.\
|
||||
It is important to use Python 3.10 otherwise the pip wheels may not be compatible.
|
||||
```
|
||||
# 0 Download the source and create a Python 3.10.9 environment using conda or create a venv using python
|
||||
git clone https://github.com/deepbeepmeep/Wan2GP.git
|
||||
cd Wan2GP
|
||||
conda create -n wan2gp python=3.10.9
|
||||
conda activate wan2gp
|
||||
|
||||
# 1 Install pytorch 2.7.0:
|
||||
pip install torch==2.7.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu128
|
||||
|
||||
# 2. Install pip dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 3.1 optional Sage attention support (30% faster)
|
||||
# Windows only: extra step only needed for windows as triton is included in pytorch with the Linux version of pytorch
|
||||
pip install triton-windows
|
||||
# For both Windows and Linux
|
||||
pip install sageattention==1.0.6
|
||||
|
||||
|
||||
# 3.2 optional Sage 2 attention support (40% faster)
|
||||
# Windows only
|
||||
pip install triton-windows
|
||||
pip install https://github.com/woct0rdho/SageAttention/releases/download/v2.1.1-windows/sageattention-2.1.1+cu128torch2.7.0-cp310-cp310-win_amd64.whl
|
||||
|
||||
# Linux only (sorry only manual compilation for the moment, but is straight forward with Linux)
|
||||
git clone https://github.com/thu-ml/SageAttention
|
||||
cd SageAttention
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Run the application
|
||||
|
||||
## 📦 Installation
|
||||
### Run a Gradio Server on port 7860 (recommended)
|
||||
|
||||
For detailed installation instructions for different GPU generations:
|
||||
- **[Installation Guide](docs/INSTALLATION.md)** - Complete setup instructions for RTX 10XX to RTX 50XX
|
||||
To run the text to video generator (in Low VRAM mode):
|
||||
```bash
|
||||
python wgp.py.py
|
||||
#or
|
||||
python wgp.py.py --t2v #launch the default text 2 video model
|
||||
#or
|
||||
python wgp.py.py --t2v-14B #for the 14B model
|
||||
#or
|
||||
python wgp.py.py --t2v-1-3B #for the 1.3B model
|
||||
|
||||
## 🎯 Usage
|
||||
```
|
||||
|
||||
### Basic Usage
|
||||
- **[Getting Started Guide](docs/GETTING_STARTED.md)** - First steps and basic usage
|
||||
- **[Models Overview](docs/MODELS.md)** - Available models and their capabilities
|
||||
To run the image to video generator (in Low VRAM mode):
|
||||
```bash
|
||||
python wgp.py.py --i2v
|
||||
```
|
||||
To run the 1.3B Fun InP image to video generator (in Low VRAM mode):
|
||||
```bash
|
||||
python wgp.py.py --i2v-1-3B
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
- **[Loras Guide](docs/LORAS.md)** - Using and managing Loras for customization
|
||||
- **[Finetunes](docs/FINETUNES.md)** - Add manually new models to WanGP
|
||||
- **[VACE ControlNet](docs/VACE.md)** - Advanced video control and manipulation
|
||||
- **[Command Line Reference](docs/CLI.md)** - All available command line options
|
||||
To be able to input multiple images with the image to video generator:
|
||||
```bash
|
||||
python wgp.py.py --i2v --multiple-images
|
||||
```
|
||||
|
||||
## 📚 Documentation
|
||||
Within the application you can configure which video generator will be launched without specifying a command line switch.
|
||||
|
||||
- **[Changelog](docs/CHANGELOG.md)** - Latest updates and version history
|
||||
- **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Common issues and solutions
|
||||
To run the application while loading entirely the diffusion model in VRAM (slightly faster but requires 24 GB of VRAM for a 8 bits quantized 14B model )
|
||||
```bash
|
||||
python wgp.py.py --profile 3
|
||||
```
|
||||
|
||||
## 📚 Video Guides
|
||||
- Nice Video that explain how to use Vace:\
|
||||
https://www.youtube.com/watch?v=FMo9oN2EAvE
|
||||
- Another Vace guide:\
|
||||
https://www.youtube.com/watch?v=T5jNiEhf9xk
|
||||
**Trouble shooting**:\
|
||||
If you have installed Sage attention, it may seem that it works because *pip install sageattention* didn't produce and error or because sage is offered as on option but in fact it doesnt work : in order to be fully operatioal Sage needs to compile its triton kernels the first time it is run (that is the first time you try to generate a video).
|
||||
|
||||
## 🔗 Related Projects
|
||||
Sometime fixing Sage compilation is easy (clear the triton cache, check triton is properly installed) sometime it is simply not possible because Sage is not supported on some older GPUs
|
||||
|
||||
Therefore you may have no choice but to fallback to sdpa attention, to do so:
|
||||
- In the configuration menu inside the application, switch "Attention mode" to "sdpa"
|
||||
or
|
||||
- Launch the application this way:
|
||||
```bash
|
||||
python wgp.py.py --attention sdpa
|
||||
```
|
||||
|
||||
### Loras support
|
||||
|
||||
|
||||
Every lora stored in the subfoler 'loras' for t2v and 'loras_i2v' will be automatically loaded. You will be then able to activate / desactive any of them when running the application by selecting them in the area below "Activated Loras" .
|
||||
|
||||
If you want to manage in different areas Loras for the 1.3B model and the 14B as they are not compatible, just create the following subfolders:
|
||||
- loras/1.3B
|
||||
- loras/14B
|
||||
|
||||
|
||||
For each activated Lora, you may specify a *multiplier* that is one float number that corresponds to its weight (default is 1.0) .The multipliers for each Lora should be separated by a space character or a carriage return. For instance:\
|
||||
*1.2 0.8* means that the first lora will have a 1.2 multiplier and the second one will have 0.8.
|
||||
|
||||
Alternatively for each Lora's multiplier you may specify a list of float numbers multipliers separated by a "," (no space) that gives the evolution of this Lora's multiplier over the steps. For instance let's assume there are 30 denoising steps and the multiplier is *0.9,0.8,0.7* then for the steps ranges 0-9, 10-19 and 20-29 the Lora multiplier will be respectively 0.9, 0.8 and 0.7.
|
||||
|
||||
If multiple Loras are defined, remember that each multiplier associated to different Loras should be separated by a space or a carriage return, so we can specify the evolution of multipliers for multiple Loras. For instance for two Loras (press Shift Return to force a carriage return):
|
||||
|
||||
```
|
||||
0.9,0.8,0.7
|
||||
1.2,1.1,1.0
|
||||
```
|
||||
You can edit, save or delete Loras presets (combinations of loras with their corresponding multipliers) directly from the gradio Web interface. These presets will save the *comment* part of the prompt that should contain some instructions how to use the corresponding the loras (for instance by specifying a trigger word or providing an example).A comment in the prompt is a line that starts that a #. It will be ignored by the video generator. For instance:
|
||||
|
||||
```
|
||||
# use they keyword ohnvx to trigger the Lora*
|
||||
A ohnvx is driving a car
|
||||
```
|
||||
Each preset, is a file with ".lset" extension stored in the loras directory and can be shared with other users
|
||||
|
||||
Last but not least you can pre activate Loras corresponding and prefill a prompt (comments only or full prompt) by specifying a preset when launching the gradio server:
|
||||
```bash
|
||||
python wgp.py.py --lora-preset mylorapreset.lset # where 'mylorapreset.lset' is a preset stored in the 'loras' folder
|
||||
```
|
||||
|
||||
You will find prebuilt Loras on https://civitai.com/ or you will be able to build them with tools such as kohya or onetrainer.
|
||||
|
||||
### Macros (basic)
|
||||
In *Advanced Mode*, you can starts prompt lines with a "!" , for instance:\
|
||||
```
|
||||
! {Subject}="cat","woman","man", {Location}="forest","lake","city", {Possessive}="its", "her", "his"
|
||||
In the video, a {Subject} is presented. The {Subject} is in a {Location} and looks at {Possessive} watch.
|
||||
```
|
||||
|
||||
This will create automatically 3 prompts that will cause the generation of 3 videos:
|
||||
```
|
||||
In the video, a cat is presented. The cat is in a forest and looks at its watch.
|
||||
In the video, a man is presented. The man is in a lake and looks at his watch.
|
||||
In the video, a woman is presented. The woman is in a city and looks at her watch.
|
||||
```
|
||||
|
||||
You can define multiple lines of macros. If there is only one macro line, the app will generate a simple user interface to enter the macro variables when getting back to *Normal Mode* (advanced mode turned off)
|
||||
|
||||
### VACE ControlNet introduction
|
||||
|
||||
Vace is a ControlNet 1.3B text2video model that allows you on top of a text prompt to provide visual hints to guide the generation. It can do more things than image2video although it is not as good for just starting a video with an image because it only a 1.3B model (in fact 3B) versus 14B and (it is not specialized for start frames). However, with Vace you can inject in the scene people or objects, animate a person, perform inpainting or outpainting, continue a video, ...
|
||||
|
||||
First you need to select the Vace 1.3B model in the Drop Down box at the top. Please note that Vace works well for the moment only with videos up to 5s (81 frames).
|
||||
|
||||
Beside the usual Text Prompt, three new types of visual hints can be provided (and combined !):
|
||||
- reference Images: use this to inject people or objects in the video. You can select multiple reference Images. The integration of the image is more efficient if the background is replaced by the full white color. You can do that with your preferred background remover or use the built in background remover by checking the box *Remove background*
|
||||
|
||||
- a Video: this can be a video that contains a body pose (an animated wireframe that indicates the positions of limbs of a person), a greyed depth map video, a normal video combined with a masked video (see below),... The Vace model will detect automatically what to do depending on the video content. You can tell WanGP to use only the n first frames of this Video. All the frames beyond and up the number of requested frames will be generated by following the Text prompt and the other visual hints (for instance reference images). If the video contains area of grey color 127, they will be considered as masks and will be filled based on the Text prompt of the reference Images. There
|
||||
|
||||
- a Video Mask
|
||||
This offers a stronger mechanism to tell Vace which parts should be kept (black) or replaced (white). You can do as well inpainting / outpainting, fill the missing part of a video more efficientlty with just the video hint.
|
||||
|
||||
|
||||
Examples:
|
||||
- Inject people and / objects into a scene describe by a text promtp: Ref. Images + text Prompt
|
||||
- Animate a character described in a text prompt: Body Pose Video + text Prompt
|
||||
- Animate a character of your choice : Ref Images + Body Pose Video + text Prompt
|
||||
|
||||
|
||||
There are lots of possible combinations. Some of them require to prepare some materials (masks on top of video, full masks, etc...).
|
||||
|
||||
Vace provides on its github (https://github.com/ali-vilab/VACE/tree/main/vace/gradios) annotators / preprocessors Gradio tool that can help you build some of these materials depending on the task you want to achieve.
|
||||
|
||||
There is also a guide that describes the various combination of hints (https://github.com/ali-vilab/VACE/blob/main/UserGuide.md).Good luck !
|
||||
|
||||
It seems you will get better results if you turn on "Skip Layer Guidance" with its default configuration
|
||||
### Command line parameters for Gradio Server
|
||||
--i2v : launch the image to video generator\
|
||||
--t2v : launch the text to video generator (default defined in the configuration)\
|
||||
--t2v-14B : launch the 14B model text to video generator\
|
||||
--t2v-1-3B : launch the 1.3B model text to video generator\
|
||||
--i2v-14B : launch the 14B model image to video generator\
|
||||
--i2v-1-3B : launch the Fun InP 1.3B model image to video generator\
|
||||
--vace : launch the Vace ControlNet 1.3B model image to video generator\
|
||||
--quantize-transformer bool: (default True) : enable / disable on the fly transformer quantization\
|
||||
--lora-dir path : Path of directory that contains Loras in diffusers / safetensor format\
|
||||
--lora-preset preset : name of preset gile (without the extension) to preload
|
||||
--verbose level : default (1) : level of information between 0 and 2\
|
||||
--server-port portno : default (7860) : Gradio port no\
|
||||
--server-name name : default (localhost) : Gradio server name\
|
||||
--open-browser : open automatically Browser when launching Gradio Server\
|
||||
--lock-config : prevent modifying the video engine configuration from the interface\
|
||||
--lock-model : prevent selecting a different model than the default model\
|
||||
--share : create a shareable URL on huggingface so that your server can be accessed remotely\
|
||||
--multiple-images : allow the users to choose multiple images as different starting points for new videos\
|
||||
--compile : turn on pytorch compilation\
|
||||
--attention mode: force attention mode among, sdpa, flash, sage, sage2\
|
||||
--profile no : default (4) : no of profile between 1 and 5\
|
||||
--preload no : number in Megabytes to preload partially the diffusion model in VRAM , may offer slight speed gains especially on older hardware. Works only with profile 2 and 4.\
|
||||
--seed no : set default seed value\
|
||||
--frames no : set the default number of frames to generate\
|
||||
--steps no : set the default number of denoising steps\
|
||||
--teacache speed multiplier: Tea cache speed multiplier, choices=["0", "1.5", "1.75", "2.0", "2.25", "2.5"]\
|
||||
--slg : turn on skip layer guidance for improved quality\
|
||||
--check-loras : filter loras that are incompatible (will take a few seconds while refreshing the lora list or while starting the app)\
|
||||
--advanced : turn on the advanced mode while launching the app\
|
||||
--listen : make server accessible on network\
|
||||
--gpu device : run Wan on device for instance "cuda:1"
|
||||
|
||||
### Profiles (for power users only)
|
||||
You can choose between 5 profiles, but two are really relevant here :
|
||||
- LowRAM_HighVRAM (3): loads entirely the model in VRAM if possible, slightly faster, but less VRAM available for the video data after that
|
||||
- LowRAM_LowVRAM (4): loads only the part of the model that is needed, low VRAM and low RAM requirement but slightly slower
|
||||
|
||||
You can adjust the number of megabytes to preload a model, with --preload nnn (nnn is the number of megabytes to preload)
|
||||
### Other Models for the GPU Poor
|
||||
- **[HuanyuanVideoGP](https://github.com/deepbeepmeep/HunyuanVideoGP)** - One of the best open source Text to Video generators
|
||||
- **[Hunyuan3D-2GP](https://github.com/deepbeepmeep/Hunyuan3D-2GP)** - Image to 3D and text to 3D tool
|
||||
- **[FluxFillGP](https://github.com/deepbeepmeep/FluxFillGP)** - Inpainting/outpainting tools based on Flux
|
||||
- **[Cosmos1GP](https://github.com/deepbeepmeep/Cosmos1GP)** - Text to world generator and image/video to world
|
||||
- **[OminiControlGP](https://github.com/deepbeepmeep/OminiControlGP)** - Flux-derived application for object transfer
|
||||
- **[YuE GP](https://github.com/deepbeepmeep/YuEGP)** - Song generator with instruments and singer's voice
|
||||
|
||||
---
|
||||
- HuanyuanVideoGP: https://github.com/deepbeepmeep/HunyuanVideoGP :\
|
||||
One of the best open source Text to Video generator
|
||||
|
||||
- Hunyuan3D-2GP: https://github.com/deepbeepmeep/Hunyuan3D-2GP :\
|
||||
A great image to 3D and text to 3D tool by the Tencent team. Thanks to mmgp it can run with less than 6 GB of VRAM
|
||||
|
||||
- FluxFillGP: https://github.com/deepbeepmeep/FluxFillGP :\
|
||||
One of the best inpainting / outpainting tools based on Flux that can run with less than 12 GB of VRAM.
|
||||
|
||||
- Cosmos1GP: https://github.com/deepbeepmeep/Cosmos1GP :\
|
||||
This application include two models: a text to world generator and a image / video to world (probably the best open source image to video generator).
|
||||
|
||||
- OminiControlGP: https://github.com/deepbeepmeep/OminiControlGP :\
|
||||
A Flux derived application very powerful that can be used to transfer an object of your choice in a prompted scene. With mmgp you can run it with only 6 GB of VRAM.
|
||||
|
||||
- YuE GP: https://github.com/deepbeepmeep/YuEGP :\
|
||||
A great song generator (instruments + singer's voice) based on prompted Lyrics and a genre description. Thanks to mmgp you can run it with less than 10 GB of VRAM without waiting forever.
|
||||
|
||||
|
||||
<p align="center">
|
||||
Made with ❤️ by DeepBeepMeep
|
||||
</p>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 36,
|
||||
"model_type": "i2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"fantasytalking_dim": 2048
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 36,
|
||||
"model_type": "i2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"flf": true
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 36,
|
||||
"model_type": "i2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 36,
|
||||
"model_type": "i2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 36,
|
||||
"model_type": "i2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"multitalk_output_dim": 768
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 1536,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 8960,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 12,
|
||||
"num_layers": 30,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 1536,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 8960,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 12,
|
||||
"num_layers": 30,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"_class_name": "WanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 1536,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 8960,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 12,
|
||||
"num_layers": 30,
|
||||
"out_dim": 16,
|
||||
"text_len": 512
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"_class_name": "VaceWanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 1536,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 8960,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 12,
|
||||
"num_layers": 30,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"vace_layers": [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28],
|
||||
"vace_in_dim": 96
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"_class_name": "VaceWanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"vace_layers": [0, 5, 10, 15, 20, 25, 30, 35],
|
||||
"vace_in_dim": 96
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"_class_name": "VaceWanModel",
|
||||
"_diffusers_version": "0.30.0",
|
||||
"dim": 5120,
|
||||
"eps": 1e-06,
|
||||
"ffn_dim": 13824,
|
||||
"freq_dim": 256,
|
||||
"in_dim": 16,
|
||||
"model_type": "t2v",
|
||||
"num_heads": 40,
|
||||
"num_layers": 40,
|
||||
"out_dim": 16,
|
||||
"text_len": 512,
|
||||
"vace_layers": [0, 5, 10, 15, 20, 25, 30, 35],
|
||||
"vace_in_dim": 96,
|
||||
"multitalk_output_dim": 768
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
Please dot not modify any file in this Folder.
|
||||
|
||||
If you want to change a property of a default model, copy the corrresponding model file in the ./finetunes folder and modify the properties you want to change in the new file.
|
||||
If a property is not in the new file, it will be inherited automatically from the default file that matches the same name file.
|
||||
|
||||
For instance to hide a model:
|
||||
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Fantasy Talking 720p",
|
||||
"architecture" : "fantasy",
|
||||
"modules": ["fantasy"],
|
||||
"description": "The Fantasy Talking model corresponds to the original Wan image 2 video model combined with the Fantasy Speaking module to process an audio Input.",
|
||||
"URLs": "i2v_720p",
|
||||
"teacache_coefficients" : [-114.36346466, 65.26524496, -18.82220707, 4.91518089, -0.23412683]
|
||||
},
|
||||
"resolution": "1280x720"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "First Last Frame to Video 720p (FLF2V) 14B",
|
||||
"architecture" : "flf2v_720p",
|
||||
"visible" : true,
|
||||
"description": "The First Last Frame 2 Video model is the official model Image 2 Video model that supports Start and End frames.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_FLF2V_720p_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_FLF2V_720p_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_FLF2V_720p_14B_quanto_mfp16_int8.safetensors"
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"resolution": "1280x720"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Flux 1 Dev 12B",
|
||||
"architecture": "flux",
|
||||
"description": "FLUX.1 Dev is a 12 billion parameter rectified flow transformer capable of generating images from text descriptions.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1-dev_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1-dev_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"image_outputs": true,
|
||||
"flux-model": "flux-dev"
|
||||
},
|
||||
"prompt": "draw a hat",
|
||||
"resolution": "1280x720",
|
||||
"batch_size": 1
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Flux 1 Dev Kontext 12B",
|
||||
"architecture": "flux",
|
||||
"description": "FLUX.1 Kontext is a 12 billion parameter rectified flow transformer capable of editing images based on instructions stored in the Prompt. Please be aware that Flux Kontext is picky on the resolution of the input image and the output dimensions may not match the dimensions of the input image.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1_kontext_dev_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1_kontext_dev_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"image_outputs": true,
|
||||
"reference_image": true,
|
||||
"flux-model": "flux-dev-kontext"
|
||||
},
|
||||
"prompt": "add a hat",
|
||||
"resolution": "1280x720",
|
||||
"batch_size": 1
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Flux 1 Schnell 12B",
|
||||
"architecture": "flux",
|
||||
"description": "FLUX.1 Schnell is a 12 billion parameter rectified flow transformer capable of generating images from text descriptions. As a distilled model it requires fewer denoising steps.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1-schnell_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Flux/resolve/main/flux1-schnell_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"image_outputs": true,
|
||||
"flux-model": "flux-schnell"
|
||||
},
|
||||
"prompt": "draw a hat",
|
||||
"resolution": "1280x720",
|
||||
"num_inference_steps": 10,
|
||||
"batch_size": 1
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Fun InP image2video 14B",
|
||||
"architecture" : "fun_inp",
|
||||
"description": "The Fun model is an alternative image 2 video that supports out the box End Image fixing (contrary to the original Wan image 2 video model).",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_Fun_InP_14B_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_Fun_InP_14B_quanto_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_Fun_InP_14B_quanto_fp16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Fun InP image2video 1.3B",
|
||||
"architecture" : "fun_inp_1.3B",
|
||||
"description": "The Fun model is an alternative image 2 video that supports out the box End Image fixing (contrary to the original Wan image 2 video model). The 1.3B adds also image 2 to video capability to the 1.3B model.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_Fun_InP_1.3B_bf16.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Text2video 720p 13B",
|
||||
"architecture" : "hunyuan",
|
||||
"description": "Probably the best text 2 video model available.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_720_bf16.safetensors.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_720_quanto_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Avatar 720p 13B",
|
||||
"architecture" : "hunyuan_avatar",
|
||||
"description": "With the Hunyuan Video Avatar model you can animate a person based on the content of an audio input. Please note that the video generator works by processing 128 frames segment at a time (even if you ask less). The good news is that it will concatenate multiple segments for long video generation (max 3 segments recommended as the quality will get worse).",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_avatar_720_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_avatar_720_quanto_bf16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Custom 720p 13B",
|
||||
"architecture" : "hunyuan_custom",
|
||||
"description": "The Hunyuan Video Custom model is probably the best model to transfer people (only people for the moment) as it is quite good to keep their identity. However it is slow as to get good results, you need to generate 720p videos with 30 steps.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_720_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_720_quanto_bf16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Custom Audio 720p 13B",
|
||||
"architecture" : "hunyuan_custom_audio",
|
||||
"description": "The Hunyuan Video Custom Audio model can be used to generate scenes of a person speaking given a Reference Image and a Recorded Voice or Song. The reference image is not a start image and therefore one can represent the person in a different context.The video length can be anything up to 10s. It is also quite good to generate no sound Video based on a person.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_audio_720_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_audio_720_quanto_bf16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Custom Edit 720p 13B",
|
||||
"architecture" : "hunyuan_custom_edit",
|
||||
"description": "The Hunyuan Video Custom Edit model can be used to do Video inpainting on a person (add accessories or completely replace the person). You will need in any case to define a Video Mask which will indicate which area of the Video should be edited.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_edit_720_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_custom_edit_720_quanto_bf16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Hunyuan Video Image2video 720p 13B",
|
||||
"architecture" : "hunyuan_i2v",
|
||||
"description": "A good looking image 2 video model, but not so good in prompt adherence.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_i2v_720_bf16v2.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/hunyuan_video_i2v_720_quanto_int8v2.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Hunyuan Video AccVideo 720p 13B",
|
||||
"architecture": "hunyuan",
|
||||
"description": " AccVideo is a novel efficient distillation method to accelerate video diffusion models with synthetic datset. Our method is 8.5x faster than HunyuanVideo.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/accvideo_hunyuan_video_720_quanto_int8.safetensors"
|
||||
],
|
||||
"preload_URLs": [
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": 42,
|
||||
"num_inference_steps": 5,
|
||||
"flow_shift": 7,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": [
|
||||
]
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Hunyuan Video FastHunyuan 720p 13B",
|
||||
"architecture": "hunyuan",
|
||||
"description": "Fast Hunyuan is an accelerated HunyuanVideo model. It can sample high quality videos with 6 diffusion steps.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/fast_hunyuan_video_720_quanto_int8.safetensors"
|
||||
],
|
||||
"preload_URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/HunyuanVideo/resolve/main/fast_hunyuan_video_720_quanto_int8_map.json"
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": 42,
|
||||
"num_inference_steps": 6,
|
||||
"flow_shift": 17,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": [
|
||||
]
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Image2video 480p 14B",
|
||||
"architecture" : "i2v",
|
||||
"description": "The standard Wan Image 2 Video specialized to generate 480p images. It also offers Start and End Image support (End Image is not supported in the original model but seems to work well)",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_480p_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_480p_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_480p_14B_quanto_mfp16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Image2video 720p 14B",
|
||||
"architecture" : "i2v",
|
||||
"description": "The standard Wan Image 2 Video specialized to generate 720p images. It also offers Start and End Image support (End Image is not supported in the original model but seems to work well).",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_720p_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_720p_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_image2video_720p_14B_quanto_mfp16_int8.safetensors"
|
||||
]
|
||||
},
|
||||
"resolution": "1280x720"
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Image2video 480p FusioniX 14B",
|
||||
"architecture" : "i2v",
|
||||
"description": "A powerful merged image-to-video model based on the original WAN 2.1 I2V model, enhanced using multiple open-source components and LoRAs to boost motion realism, temporal consistency, and expressive detail.",
|
||||
"URLs": "i2v",
|
||||
"loras": ["https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan2.1_I2V_14B_FusionX_LoRA.safetensors"]
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "LTX Video 0.9.8 13B",
|
||||
"architecture" : "ltxv_13B",
|
||||
"description": "LTX Video is a fast model that can be used to generate very very long videos (up to 1800 frames !).It is recommended to keep the number of steps to 30 or you will need to update the file 'ltxv_video/configs/ltxv-13b-0.9.8-dev.yaml'.The LTX Video model expects very long prompts, so don't hesitate to use the Prompt Enhancer.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv_0.9.8_13B_dev_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv_0.9.8_13B_dev_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"preload_URLs" : [
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv-097-ic-lora-pose-control-diffusers.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv-097-ic-lora-depth-control-diffusers.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv-097-ic-lora-canny-control-diffusers.safetensors"
|
||||
],
|
||||
"LTXV_config": "ltx_video/configs/ltxv-13b-0.9.8-dev.yaml"
|
||||
},
|
||||
"num_inference_steps": 30
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "LTX Video 0.9.8 Distilled 13B",
|
||||
"architecture" : "ltxv_13B",
|
||||
"description": "LTX Video is a fast model that can be used to generate very long videos (up to 1800 frames !).This distilled version is a very fast version and retains a high level of quality. The LTX Video model expects very long prompts, so don't hesitate to use the Prompt Enhancer.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv_0.9.8_13B_distilled_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/LTX_Video/resolve/main/ltxv_0.9.8_13B_distilled_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"preload_URLs" : "ltxv_13B",
|
||||
"LTXV_config": "ltx_video/configs/ltxv-13b-0.9.8-distilled.yaml"
|
||||
},
|
||||
"num_inference_steps": 6
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "MoviiGen 1080p 14B",
|
||||
"architecture" : "t2v",
|
||||
"description": "MoviiGen 1.1, a cutting-edge video generation model that excels in cinematic aesthetics and visual quality. Use it to generate videos in 720p or 1080p in the 21:9 ratio.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_moviigen1.1_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_moviigen1.1_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_moviigen1.1_14B_quanto_mfp16_int8.safetensors"
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"resolution": "1280x720",
|
||||
"video_length": 81
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Multitalk 480p",
|
||||
"architecture" : "multitalk",
|
||||
"modules": ["multitalk"],
|
||||
"description": "The Multitalk model corresponds to the original Wan image 2 video model combined with the Multitalk module. It lets you have up to two people have a conversation.",
|
||||
"URLs": "i2v",
|
||||
"teacache_coefficients" : [-3.02331670e+02, 2.23948934e+02, -5.25463970e+01, 5.87348440e+00, -2.01973289e-01]
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Multitalk 720p",
|
||||
"architecture" : "multitalk",
|
||||
"modules": ["multitalk"],
|
||||
"description": "The Multitalk model corresponds to the original Wan image 2 video 720p model combined with the Multitalk module. It lets you have up to two people have a conversation.",
|
||||
"URLs": "i2v_720p",
|
||||
"teacache_coefficients" : [-114.36346466, 65.26524496, -18.82220707, 4.91518089, -0.23412683],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"resolution": "1280x720"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Phantom 1.3B",
|
||||
"architecture" : "phantom_1.3B",
|
||||
"description": "The Phantom model is specialized in transferring people or objects of your choice into a generated Video. It produces very nice results when used at 720p.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2_1_phantom_1.3B_mbf16.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Phantom 14B",
|
||||
"architecture" : "phantom_14B",
|
||||
"description": "The Phantom model is specialized in transferring people or objects of your choice into a generated Video. It produces very nice results when used at 720p.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_phantom_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_phantom_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_phantom_14B_quanto_mfp16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "ReCamMaster 1.3B",
|
||||
"architecture" : "recam_1.3B",
|
||||
"description": "The Recam Master in theory should allow you to replay a video by applying a different camera movement. The model supports only video that are at least 81 frames long (any frame beyond will be ignored)",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_recammaster_1.3B_bf16.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "SkyReels2 Diffusion Forcing 1.3B",
|
||||
"architecture" : "sky_df_1.3B",
|
||||
"description": "The SkyReels 2 Diffusion Forcing model has been designed to generate very long videos that exceeds the usual 5s limit. You can also use this model to extend any existing video.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_1.3B_mbf16.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "SkyReels2 Diffusion Forcing 540p 14B",
|
||||
"architecture" : "sky_df_14B",
|
||||
"description": "The SkyReels 2 Diffusion Forcing model has been designed to generate very long videos that exceeds the usual 5s limit. You can also use this model to extend any existing video.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_14B_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_14B_quanto_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_14B_quanto_fp16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "SkyReels2 Diffusion Forcing 720p 14B",
|
||||
"architecture" : "sky_df_14B",
|
||||
"description": "The SkyReels 2 Diffusion Forcing model has been designed to generate very long videos that exceeds the usual 5s limit. You can also use this model to extend any existing video.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_720p_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_720p_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/sky_reels2_diffusion_forcing_720p_14B_quanto_mfp16_int8.safetensors"
|
||||
]
|
||||
},
|
||||
"resolution": "1280x720"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Text2video 14B",
|
||||
"architecture" : "t2v",
|
||||
"description": "The original Wan Text 2 Video model. Most other models have been built on top of it",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_text2video_14B_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_text2video_14B_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_text2video_14B_quanto_mfp16_int8.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Text2video 1.3B",
|
||||
"architecture" : "t2v_1.3B",
|
||||
"description": "The light version of the original Wan Text 2 Video model. Most other models have been built on top of it",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_text2video_1.3B_mbf16.safetensors"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.2 Text2video 14B",
|
||||
"architecture" : "t2v",
|
||||
"description": "Wan 2.2 Text 2 Video model",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_high_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_high_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_high_quanto_mfp16_int8.safetensors"
|
||||
],
|
||||
"URLs2": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_low_mbf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_low_quanto_mbf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.2/resolve/main/wan2.2_text2video_14B_low_quanto_mfp16_int8.safetensors"
|
||||
],
|
||||
"group": "wan2_2"
|
||||
},
|
||||
"switch_threshold" : 875,
|
||||
"guidance_scale" : 4,
|
||||
"guidance2_scale" : 3,
|
||||
"flow_shift" : 12
|
||||
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Wan2.1 Text2video FusioniX 14B",
|
||||
"architecture" : "t2v",
|
||||
"description": "A powerful merged text-to-video model based on the original WAN 2.1 T2V model, enhanced using multiple open-source components and LoRAs to boost motion realism, temporal consistency, and expressive detail.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_fp16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_fp16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": -1,
|
||||
"num_inference_steps": 8,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 5,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"multi_images_gen_type": 0,
|
||||
"tea_cache_setting": 0,
|
||||
"tea_cache_start_step_perc": 0,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_switch": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"cfg_star_switch": 0,
|
||||
"cfg_zero_step": -1,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": []
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Wan2.1 Text2video Self-Forcing 14B",
|
||||
"architecture": "t2v",
|
||||
"description": "This model is an advanced text-to-video generation model. This approach allows the model to generate videos with significantly fewer inference steps (4 or 8 steps) and without classifier-free guidance, substantially reducing video generation time while maintaining high quality outputs.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_quanto_bf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_quanto_fp16_int8.safetensors"
|
||||
],
|
||||
"author": "https://huggingface.co/lightx2v/Wan2.1-T2V-14B-StepDistill-CfgDistill",
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": -1,
|
||||
"num_inference_steps": 4,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 3,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"multi_images_gen_type": 0,
|
||||
"tea_cache_setting": 0,
|
||||
"tea_cache_start_step_perc": 0,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_switch": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"cfg_star_switch": 0,
|
||||
"cfg_zero_step": -1,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": []
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"model":
|
||||
{
|
||||
"name": "Vace ControlNet 1.3B",
|
||||
"architecture" : "vace_1.3B",
|
||||
"modules": ["vace_1.3B"],
|
||||
"description": "The Vace ControlNet model is a powerful model that allows you to control the content of the generated video based of additional custom data : pose or depth video, images or objects you want to see in the video.",
|
||||
"URLs": "t2v_1.3B"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Vace ControlNet 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "The Vace ControlNet model is a powerful model that allows you to control the content of the generated video based of additional custom data : pose or depth video, images or objects you want to see in the video.",
|
||||
"URLs": "t2v"
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Vace Cocktail 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "This model has been created on the fly using the Wan text 2 video model and the Loras of FusioniX. The weight of the Detail Enhancer Lora has been reduced to improve identity preservation. Copy the model def in the finetune folder to change the Cocktail composition.",
|
||||
"URLs": "t2v",
|
||||
"loras": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_CausVid_14B_T2V_lora_rank32_v2.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/DetailEnhancerV1.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_AccVid_T2V_14B_lora_rank32_fp16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_T2V_14B_MoviiGen_lora_rank32_fp16.safetensors"
|
||||
],
|
||||
"loras_multipliers": [1, 0.5, 0.5, 0.5]
|
||||
},
|
||||
"num_inference_steps": 10,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 2
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Wan2.2 Vace Cocktail 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "This model has been created on the fly using the Wan text 2.2 video model and the Loras of FusioniX. The weight of the Detail Enhancer Lora has been reduced to improve identity preservation. Copy the model def in the finetune folder to change the Cocktail composition.",
|
||||
"URLs": "t2v_2_2",
|
||||
"URLs2": "t2v_2_2",
|
||||
"loras": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_CausVid_14B_T2V_lora_rank32_v2.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/DetailEnhancerV1.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_AccVid_T2V_14B_lora_rank32_fp16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/loras_accelerators/Wan21_T2V_14B_MoviiGen_lora_rank32_fp16.safetensors"
|
||||
],
|
||||
"loras_multipliers": [1, 0.2, 0.5, 0.5],
|
||||
"group": "wan2_2"
|
||||
},
|
||||
"num_inference_steps": 10,
|
||||
"guidance_scale": 1,
|
||||
"guidance_scale": 2,
|
||||
"flow_shift": 2,
|
||||
"switch_threshold" : 875
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Wan2.2 Vace Cocktail Light 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "This model has been created on the fly using the Wan text 2.2 video model and the Loras of FusioniX. The weight of the Detail Enhancer Lora has been reduced to improve identity preservation. Only the high noise part of the v2.2 model is used to reduce RAM usage.",
|
||||
"URLs": "t2v_2_2",
|
||||
"URLs2": "t2v_2_2",
|
||||
"loras": "vace_14B_cocktail_2_2",
|
||||
"loras_multipliers": "vace_14B_cocktail_2_2",
|
||||
"group": "wan2_2"
|
||||
},
|
||||
"num_inference_steps": 10,
|
||||
"guidance_scale": 1,
|
||||
"guidance_scale": 2,
|
||||
"flow_shift": 2
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Vace FusioniX 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "Vace control model enhanced using multiple open-source components and LoRAs to boost motion realism, temporal consistency, and expressive detail.",
|
||||
"URLs": "t2v_fusionix"
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": -1,
|
||||
"num_inference_steps": 10,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 2,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"multi_images_gen_type": 0,
|
||||
"tea_cache_setting": 0,
|
||||
"tea_cache_start_step_perc": 0,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_switch": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"cfg_star_switch": 0,
|
||||
"cfg_zero_step": -1,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": []
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Vace Self-Forcing 14B",
|
||||
"architecture": "vace_14B",
|
||||
"modules": [
|
||||
"vace_14B"
|
||||
],
|
||||
"description": "This model is a combination of Vace and an advanced text-to-video generation model. This approach allows the model to generate videos with significantly fewer inference steps (4 or 8 steps) and without classifier-free guidance, substantially reducing video generation time while maintaining high quality outputs.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_bf16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_quanto_bf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/wan2.1_StepDistill-CfgDistill_14B_quanto_fp16_int8.safetensors"
|
||||
],
|
||||
"author": "https://huggingface.co/lightx2v/Wan2.1-T2V-14B-StepDistill-CfgDistill",
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": -1,
|
||||
"num_inference_steps": 4,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 3,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"multi_images_gen_type": 0,
|
||||
"tea_cache_setting": 0,
|
||||
"tea_cache_start_step_perc": 0,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_switch": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"cfg_star_switch": 0,
|
||||
"cfg_zero_step": -1,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": []
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"model": {
|
||||
"name": "Vace Multitalk FusioniX 14B",
|
||||
"architecture": "vace_multitalk_14B",
|
||||
"modules": [
|
||||
"vace_14B",
|
||||
"multitalk"
|
||||
],
|
||||
"description": "Vace control model enhanced using multiple open-source components and LoRAs to boost motion realism, temporal consistency, and expressive detail. And it that's not sufficient Vace is combined with Multitalk.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_fp16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_bf16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_fp16_int8.safetensors"
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
"negative_prompt": "",
|
||||
"prompt": "",
|
||||
"resolution": "832x480",
|
||||
"video_length": 81,
|
||||
"seed": -1,
|
||||
"num_inference_steps": 10,
|
||||
"guidance_scale": 1,
|
||||
"flow_shift": 5,
|
||||
"embedded_guidance_scale": 6,
|
||||
"repeat_generation": 1,
|
||||
"multi_images_gen_type": 0,
|
||||
"tea_cache_setting": 0,
|
||||
"tea_cache_start_step_perc": 0,
|
||||
"loras_multipliers": "",
|
||||
"temporal_upsampling": "",
|
||||
"spatial_upsampling": "",
|
||||
"RIFLEx_setting": 0,
|
||||
"slg_switch": 0,
|
||||
"slg_start_perc": 10,
|
||||
"slg_end_perc": 90,
|
||||
"cfg_star_switch": 0,
|
||||
"cfg_zero_step": -1,
|
||||
"prompt_enhancer": "",
|
||||
"activated_loras": []
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## 🔥 Latest News
|
||||
### July 21 2025: WanGP v7.1
|
||||
- Flux Family Reunion : *Flux Dev* and *Flux Schnell* have been invited aboard WanGP. To celebrate that, Loras support for the Flux *diffusers* format has also been added.
|
||||
|
||||
- LTX Video upgraded to version 0.9.8: you can now generate 1800 frames (1 min of video !) in one go without a sliding window. With the distilled model it will take only 5 minutes with a RTX 4090 (you will need 22 GB of VRAM though). I have added options to select higher humber frames if you want to experiment
|
||||
|
||||
- LTX Video ControlNet : it is a Control Net that allows you for instance to transfer a Human motion or Depth from a control video. It is not as powerful as Vace but can produce interesting things especially as now you can generate quickly a 1 min video. Under the scene IC-Loras (see below) for Pose, Depth and Canny are automatically loaded for you, no need to add them.
|
||||
|
||||
- LTX IC-Lora support: these are special Loras that consumes a conditional image or video
|
||||
Beside the pose, depth and canny IC-Loras transparently loaded there is the *detailer* (https://huggingface.co/Lightricks/LTX-Video-ICLoRA-detailer-13b-0.9.8) which is basically an upsampler. Add the *detailer* as a Lora and use LTX Raw Format as control net choice to use it.
|
||||
|
||||
And Also:
|
||||
- easier way to select video resolution
|
||||
- started to optimize Matanyone to reduce VRAM requirements
|
||||
|
||||
|
||||
### July 15 2025: WanGP v7.0 is an AI Powered Photoshop
|
||||
This release turns the Wan models into Image Generators. This goes way more than allowing to generate a video made of single frame :
|
||||
- Multiple Images generated at the same time so that you can choose the one you like best.It is Highly VRAM optimized so that you can generate for instance 4 720p Images at the same time with less than 10 GB
|
||||
- With the *image2image* the original text2video WanGP becomes an image upsampler / restorer
|
||||
- *Vace image2image* comes out of the box with image outpainting, person / object replacement, ...
|
||||
- You can use in one click a newly Image generated as Start Image or Reference Image for a Video generation
|
||||
|
||||
And to complete the full suite of AI Image Generators, Ladies and Gentlemen please welcome for the first time in WanGP : **Flux Kontext**.\
|
||||
As a reminder Flux Kontext is an image editor : give it an image and a prompt and it will do the change for you.\
|
||||
This highly optimized version of Flux Kontext will make you feel that you have been cheated all this time as WanGP Flux Kontext requires only 8 GB of VRAM to generate 4 images at the same time with no need for quantization.
|
||||
|
||||
WanGP v7 comes with *Image2image* vanilla and *Vace FusinoniX*. However you can build your own finetune where you will combine a text2video or Vace model with any combination of Loras.
|
||||
|
||||
Also in the news:
|
||||
- You can now enter the *Bbox* for each speaker in *Multitalk* to precisely locate who is speaking. And to save some headaches the *Image Mask generator* will give you the *Bbox* coordinates of an area you have selected.
|
||||
- *Film Grain* post processing to add a vintage look at your video
|
||||
- *First Last Frame to Video* model should work much better now as I have discovered rencently its implementation was not complete
|
||||
- More power for the finetuners, you can now embed Loras directly in the finetune definition. You can also override the default models (titles, visibility, ...) with your own finetunes. Check the doc that has been updated.
|
||||
|
||||
|
||||
### July 10 2025: WanGP v6.7, is NAG a game changer ? you tell me
|
||||
Maybe you knew that already but most *Loras accelerators* we use today (Causvid, FusioniX) don't use *Guidance* at all (that it is *CFG* is set to 1). This helps to get much faster generations but the downside is that *Negative Prompts* are completely ignored (including the default ones set by the models). **NAG** (https://github.com/ChenDarYen/Normalized-Attention-Guidance) aims to solve that by injecting the *Negative Prompt* during the *attention* processing phase.
|
||||
|
||||
So WanGP 6.7 gives you NAG, but not any NAG, a *Low VRAM* implementation, the default one ends being VRAM greedy. You will find NAG in the *General* advanced tab for most Wan models.
|
||||
|
||||
Use NAG especially when Guidance is set to 1. To turn it on set the **NAG scale** to something around 10. There are other NAG parameters **NAG tau** and **NAG alpha** which I recommend to change only if you don't get good results by just playing with the NAG scale. Don't hesitate to share on this discord server the best combinations for these 3 parameters.
|
||||
|
||||
The authors of NAG claim that NAG can also be used when using a Guidance (CFG > 1) and to improve the prompt adherence.
|
||||
|
||||
### July 8 2025: WanGP v6.6, WanGP offers you **Vace Multitalk Dual Voices Fusionix Infinite** :
|
||||
**Vace** our beloved super Control Net has been combined with **Multitalk** the new king in town that can animate up to two people speaking (**Dual Voices**). It is accelerated by the **Fusionix** model and thanks to *Sliding Windows* support and *Adaptive Projected Guidance* (much slower but should reduce the reddish effect with long videos) your two people will be able to talk for very a long time (which is an **Infinite** amount of time in the field of video generation).
|
||||
|
||||
Of course you will get as well *Multitalk* vanilla and also *Multitalk 720p* as a bonus.
|
||||
|
||||
And since I am mister nice guy I have enclosed as an exclusivity an *Audio Separator* that will save you time to isolate each voice when using Multitalk with two people.
|
||||
|
||||
As I feel like resting a bit I haven't produced yet a nice sample Video to illustrate all these new capabilities. But here is the thing, I ams sure you will publish in the *Share Your Best Video* channel your *Master Pieces*. The best ones will be added to the *Announcements Channel* and will bring eternal fame to its authors.
|
||||
|
||||
But wait, there is more:
|
||||
- Sliding Windows support has been added anywhere with Wan models, so imagine with text2video recently upgraded in 6.5 into a video2video, you can now upsample very long videos regardless of your VRAM. The good old image2video model can now reuse the last image to produce new videos (as requested by many of you)
|
||||
- I have added also the capability to transfer the audio of the original control video (Misc. advanced tab) and an option to preserve the fps into the generated video, so from now on you will be to upsample / restore your old families video and keep the audio at their original pace. Be aware that the duration will be limited to 1000 frames as I still need to add streaming support for unlimited video sizes.
|
||||
|
||||
Also, of interest too:
|
||||
- Extract video info from Videos that have not been generated by WanGP, even better you can also apply post processing (Upsampling / MMAudio) on non WanGP videos
|
||||
- Force the generated video fps to your liking, works wery well with Vace when using a Control Video
|
||||
- Ability to chain URLs of Finetune models (for instance put the URLs of a model in your main finetune and reference this finetune in other finetune models to save time)
|
||||
|
||||
### July 2 2025: WanGP v6.5.1, WanGP takes care of you: lots of quality of life features:
|
||||
- View directly inside WanGP the properties (seed, resolutions, length, most settings...) of the past generations
|
||||
- In one click use the newly generated video as a Control Video or Source Video to be continued
|
||||
- Manage multiple settings for the same model and switch between them using a dropdown box
|
||||
- WanGP will keep the last generated videos in the Gallery and will remember the last model you used if you restart the app but kept the Web page open
|
||||
- Custom resolutions : add a file in the WanGP folder with the list of resolutions you want to see in WanGP (look at the instruction readme in this folder)
|
||||
|
||||
Taking care of your life is not enough, you want new stuff to play with ?
|
||||
- MMAudio directly inside WanGP : add an audio soundtrack that matches the content of your video. By the way it is a low VRAM MMAudio and 6 GB of VRAM should be sufficient. You will need to go in the *Extensions* tab of the WanGP *Configuration* to enable MMAudio
|
||||
- Forgot to upsample your video during the generation ? want to try another MMAudio variation ? Fear not you can also apply upsampling or add an MMAudio track once the video generation is done. Even better you can ask WangGP for multiple variations of MMAudio to pick the one you like best
|
||||
- MagCache support: a new step skipping approach, supposed to be better than TeaCache. Makes a difference if you usually generate with a high number of steps
|
||||
- SageAttention2++ support : not just the compatibility but also a slightly reduced VRAM usage
|
||||
- Video2Video in Wan Text2Video : this is the paradox, a text2video can become a video2video if you start the denoising process later on an existing video
|
||||
- FusioniX upsampler: this is an illustration of Video2Video in Text2Video. Use the FusioniX text2video model with an output resolution of 1080p and a denoising strength of 0.25 and you will get one of the best upsamplers (in only 2/3 steps, you will need lots of VRAM though). Increase the denoising strength and you will get one of the best Video Restorer
|
||||
- Choice of Wan Samplers / Schedulers
|
||||
- More Lora formats support
|
||||
|
||||
**If you had upgraded to v6.5 please upgrade again to 6.5.1 as this will fix a bug that ignored Loras beyond the first one**
|
||||
|
||||
### June 23 2025: WanGP v6.3, Vace Unleashed. Thought we couldnt squeeze Vace even more ?
|
||||
- Multithreaded preprocessing when possible for faster generations
|
||||
- Multithreaded frames Lanczos Upsampling as a bonus
|
||||
- A new Vace preprocessor : *Flow* to extract fluid motion
|
||||
- Multi Vace Controlnets: you can now transfer several properties at the same time. This opens new possibilities to explore, for instance if you transfer *Human Movement* and *Shapes* at the same time for some reasons the lighting of your character will take into account much more the environment of your character.
|
||||
- Injected Frames Outpainting, in case you missed it in WanGP 6.21
|
||||
|
||||
Don't know how to use all of the Vace features ? Check the Vace Guide embedded in WanGP as it has also been updated.
|
||||
|
||||
|
||||
### June 19 2025: WanGP v6.2, Vace even more Powercharged
|
||||
👋 Have I told you that I am a big fan of Vace ? Here are more goodies to unleash its power:
|
||||
- If you ever wanted to watch Star Wars in 4:3, just use the new *Outpainting* feature and it will add the missing bits of image at the top and the bottom of the screen. The best thing is *Outpainting* can be combined with all the other Vace modifications, for instance you can change the main character of your favorite movie at the same time
|
||||
- More processing can combined at the same time (for instance the depth process can be applied outside the mask)
|
||||
- Upgraded the depth extractor to Depth Anything 2 which is much more detailed
|
||||
|
||||
As a bonus, I have added two finetunes based on the Safe-Forcing technology (which requires only 4 steps to generate a video): Wan 2.1 text2video Self-Forcing and Vace Self-Forcing. I know there is Lora around but the quality of the Lora is worse (at least with Vace) compared to the full model. Don't hesitate to share your opinion about this on the discord server.
|
||||
### June 17 2025: WanGP v6.1, Vace Powercharged
|
||||
👋 Lots of improvements for Vace the Mother of all Models:
|
||||
- masks can now be combined with on the fly processing of a control video, for instance you can extract the motion of a specific person defined by a mask
|
||||
- on the fly modification of masks : reversed masks (with the same mask you can modify the background instead of the people covered by the masks), enlarged masks (you can cover more area if for instance the person you are trying to inject is larger than the one in the mask), ...
|
||||
- view these modified masks directly inside WanGP during the video generation to check they are really as expected
|
||||
- multiple frames injections: multiples frames can be injected at any location of the video
|
||||
- expand past videos in on click: just select one generated video to expand it
|
||||
|
||||
Of course all these new stuff work on all Vace finetunes (including Vace Fusionix).
|
||||
|
||||
Thanks also to Reevoy24 for adding a Notfication sound at the end of a generation and for fixing the background color of the current generation summary.
|
||||
|
||||
### June 12 2025: WanGP v6.0
|
||||
👋 *Finetune models*: You find the 20 models supported by WanGP not sufficient ? Too impatient to wait for the next release to get the support for a newly released model ? Your prayers have been answered: if a new model is compatible with a model architecture supported by WanGP, you can add by yourself the support for this model in WanGP by just creating a finetune model definition. You can then store this model in the cloud (for instance in Huggingface) and the very light finetune definition file can be easily shared with other users. WanGP will download automatically the finetuned model for them.
|
||||
|
||||
To celebrate the new finetunes support, here are a few finetune gifts (directly accessible from the model selection menu):
|
||||
- *Fast Hunyuan Video* : generate model t2v in only 6 steps
|
||||
- *Hunyuan Vido AccVideo* : generate model t2v in only 5 steps
|
||||
- *Wan FusioniX*: it is a combo of AccVideo / CausVid ans other models and can generate high quality Wan videos in only 8 steps
|
||||
|
||||
One more thing...
|
||||
|
||||
The new finetune system can be used to combine complementaty models : what happens when you combine Fusionix Text2Video and Vace Control Net ?
|
||||
|
||||
You get **Vace FusioniX**: the Ultimate Vace Model, Fast (10 steps, no need for guidance) and with a much better quality Video than the original slower model (despite being the best Control Net out there). Here goes one more finetune...
|
||||
|
||||
Check the *Finetune Guide* to create finetune models definitions and share them on the WanGP discord server.
|
||||
|
||||
### June 11 2025: WanGP v5.5
|
||||
👋 *Hunyuan Video Custom Audio*: it is similar to Hunyuan Video Avatar excpet there isn't any lower limit on the number of frames and you can use your reference images in a different context than the image itself\
|
||||
*Hunyuan Video Custom Edit*: Hunyuan Video Controlnet, use it to do inpainting and replace a person in a video while still keeping his poses. Similar to Vace but less restricted than the Wan models in terms of content...
|
||||
|
||||
### June 6 2025: WanGP v5.41
|
||||
👋 Bonus release: Support for **AccVideo** Lora to speed up x2 Video generations in Wan models. Check the Loras documentation to get the usage instructions of AccVideo.
|
||||
|
||||
### June 6 2025: WanGP v5.4
|
||||
👋 World Exclusive : Hunyuan Video Avatar Support ! You won't need 80 GB of VRAM nor 32 GB oF VRAM, just 10 GB of VRAM will be sufficient to generate up to 15s of high quality speech / song driven Video at a high speed with no quality degradation. Support for TeaCache included.
|
||||
|
||||
### May 26, 2025: WanGP v5.3
|
||||
👋 Happy with a Video generation and want to do more generations using the same settings but you can't remember what you did or you find it too hard to copy/paste one per one each setting from the file metadata? Rejoice! There are now multiple ways to turn this tedious process into a one click task:
|
||||
- Select one Video recently generated in the Video Gallery and click *Use Selected Video Settings*
|
||||
- Click *Drop File Here* and select a Video you saved somewhere, if the settings metadata have been saved with the Video you will be able to extract them automatically
|
||||
- Click *Export Settings to File* to save on your harddrive the current settings. You will be able to use them later again by clicking *Drop File Here* and select this time a Settings json file
|
||||
|
||||
### May 23, 2025: WanGP v5.21
|
||||
👋 Improvements for Vace: better transitions between Sliding Windows, Support for Image masks in Matanyone, new Extend Video for Vace, different types of automated background removal
|
||||
|
||||
### May 20, 2025: WanGP v5.2
|
||||
👋 Added support for Wan CausVid which is a distilled Wan model that can generate nice looking videos in only 4 to 12 steps. The great thing is that Kijai (Kudos to him!) has created a CausVid Lora that can be combined with any existing Wan t2v model 14B like Wan Vace 14B. See [LORAS.md](LORAS.md) for instructions on how to use CausVid.
|
||||
|
||||
Also as an experiment I have added support for the MoviiGen, the first model that claims to be capable of generating 1080p videos (if you have enough VRAM (20GB...) and be ready to wait for a long time...). Don't hesitate to share your impressions on the Discord server.
|
||||
|
||||
### May 18, 2025: WanGP v5.1
|
||||
👋 Bonus Day, added LTX Video 13B Distilled: generate in less than one minute, very high quality Videos!
|
||||
|
||||
### May 17, 2025: WanGP v5.0
|
||||
👋 One App to Rule Them All! Added support for the other great open source architectures:
|
||||
- **Hunyuan Video**: text 2 video (one of the best, if not the best t2v), image 2 video and the recently released Hunyuan Custom (very good identity preservation when injecting a person into a video)
|
||||
- **LTX Video 13B** (released last week): very long video support and fast 720p generation. Wan GP version has been greatly optimized and reduced LTX Video VRAM requirements by 4!
|
||||
|
||||
Also:
|
||||
- Added support for the best Control Video Model, released 2 days ago: Vace 14B
|
||||
- New Integrated prompt enhancer to increase the quality of the generated videos
|
||||
|
||||
*You will need one more `pip install -r requirements.txt`*
|
||||
|
||||
### May 5, 2025: WanGP v4.5
|
||||
👋 FantasySpeaking model, you can animate a talking head using a voice track. This works not only on people but also on objects. Also better seamless transitions between Vace sliding windows for very long videos. New high quality processing features (mixed 16/32 bits calculation and 32 bits VAE)
|
||||
|
||||
### April 27, 2025: WanGP v4.4
|
||||
👋 Phantom model support, very good model to transfer people or objects into video, works quite well at 720p and with the number of steps > 30
|
||||
|
||||
### April 25, 2025: WanGP v4.3
|
||||
👋 Added preview mode and support for Sky Reels v2 Diffusion Forcing for high quality "infinite length videos". Note that Skyreel uses causal attention that is only supported by Sdpa attention so even if you choose another type of attention, some of the processes will use Sdpa attention.
|
||||
|
||||
### April 18, 2025: WanGP v4.2
|
||||
👋 FLF2V model support, official support from Wan for image2video start and end frames specialized for 720p.
|
||||
|
||||
### April 17, 2025: WanGP v4.1
|
||||
👋 Recam Master model support, view a video from a different angle. The video to process must be at least 81 frames long and you should set at least 15 steps denoising to get good results.
|
||||
|
||||
### April 13, 2025: WanGP v4.0
|
||||
👋 Lots of goodies for you!
|
||||
- A new UI, tabs were replaced by a Dropdown box to easily switch models
|
||||
- A new queuing system that lets you stack in a queue as many text2video, image2video tasks, ... as you want. Each task can rely on complete different generation parameters (different number of frames, steps, loras, ...). Many thanks to **Tophness** for being a big contributor on this new feature
|
||||
- Temporal upsampling (Rife) and spatial upsampling (Lanczos) for a smoother video (32 fps or 64 fps) and to enlarge your video by x2 or x4. Check these new advanced options.
|
||||
- Wan Vace Control Net support: with Vace you can inject in the scene people or objects, animate a person, perform inpainting or outpainting, continue a video, ... See [VACE.md](VACE.md) for introduction guide.
|
||||
- Integrated *Matanyone* tool directly inside WanGP so that you can create easily inpainting masks used in Vace
|
||||
- Sliding Window generation for Vace, create windows that can last dozens of seconds
|
||||
- New optimizations for old generation GPUs: Generate 5s (81 frames, 15 steps) of Vace 1.3B with only 5GB and in only 6 minutes on a RTX 2080Ti and 5s of t2v 14B in less than 10 minutes.
|
||||
|
||||
### March 27, 2025
|
||||
👋 Added support for the new Wan Fun InP models (image2video). The 14B Fun InP has probably better end image support but unfortunately existing loras do not work so well with it. The great novelty is the Fun InP image2 1.3B model: Image 2 Video is now accessible to even lower hardware configuration. It is not as good as the 14B models but very impressive for its size. Many thanks to the VideoX-Fun team (https://github.com/aigc-apps/VideoX-Fun)
|
||||
|
||||
### March 26, 2025
|
||||
👋 Good news! Official support for RTX 50xx please check the [installation instructions](INSTALLATION.md).
|
||||
|
||||
### March 24, 2025: Wan2.1GP v3.2
|
||||
👋
|
||||
- Added Classifier-Free Guidance Zero Star. The video should match better the text prompt (especially with text2video) at no performance cost: many thanks to the **CFG Zero * Team**. Don't hesitate to give them a star if you appreciate the results: https://github.com/WeichenFan/CFG-Zero-star
|
||||
- Added back support for PyTorch compilation with Loras. It seems it had been broken for some time
|
||||
- Added possibility to keep a number of pregenerated videos in the Video Gallery (useful to compare outputs of different settings)
|
||||
|
||||
*You will need one more `pip install -r requirements.txt`*
|
||||
|
||||
### March 19, 2025: Wan2.1GP v3.1
|
||||
👋 Faster launch and RAM optimizations (should require less RAM to run)
|
||||
|
||||
*You will need one more `pip install -r requirements.txt`*
|
||||
|
||||
### March 18, 2025: Wan2.1GP v3.0
|
||||
👋
|
||||
- New Tab based interface, you can switch from i2v to t2v conversely without restarting the app
|
||||
- Experimental Dual Frames mode for i2v, you can also specify an End frame. It doesn't always work, so you will need a few attempts.
|
||||
- You can save default settings in the files *i2v_settings.json* and *t2v_settings.json* that will be used when launching the app (you can also specify the path to different settings files)
|
||||
- Slight acceleration with loras
|
||||
|
||||
*You will need one more `pip install -r requirements.txt`*
|
||||
|
||||
Many thanks to *Tophness* who created the framework (and did a big part of the work) of the multitabs and saved settings features
|
||||
|
||||
### March 18, 2025: Wan2.1GP v2.11
|
||||
👋 Added more command line parameters to prefill the generation settings + customizable output directory and choice of type of metadata for generated videos. Many thanks to *Tophness* for his contributions.
|
||||
|
||||
*You will need one more `pip install -r requirements.txt` to reflect new dependencies*
|
||||
|
||||
### March 18, 2025: Wan2.1GP v2.1
|
||||
👋 More Loras!: added support for 'Safetensors' and 'Replicate' Lora formats.
|
||||
|
||||
*You will need to refresh the requirements with a `pip install -r requirements.txt`*
|
||||
|
||||
### March 17, 2025: Wan2.1GP v2.0
|
||||
👋 The Lora festival continues:
|
||||
- Clearer user interface
|
||||
- Download 30 Loras in one click to try them all (expand the info section)
|
||||
- Very easy to use Loras as now Lora presets can input the subject (or other needed terms) of the Lora so that you don't have to modify manually a prompt
|
||||
- Added basic macro prompt language to prefill prompts with different values. With one prompt template, you can generate multiple prompts.
|
||||
- New Multiple images prompts: you can now combine any number of images with any number of text prompts (need to launch the app with --multiple-images)
|
||||
- New command lines options to launch directly the 1.3B t2v model or the 14B t2v model
|
||||
|
||||
### March 14, 2025: Wan2.1GP v1.7
|
||||
👋
|
||||
- Lora Fest special edition: very fast loading/unload of loras for those Loras collectors around. You can also now add/remove loras in the Lora folder without restarting the app.
|
||||
- Added experimental Skip Layer Guidance (advanced settings), that should improve the image quality at no extra cost. Many thanks to the *AmericanPresidentJimmyCarter* for the original implementation
|
||||
|
||||
*You will need to refresh the requirements `pip install -r requirements.txt`*
|
||||
|
||||
### March 13, 2025: Wan2.1GP v1.6
|
||||
👋 Better Loras support, accelerated loading Loras.
|
||||
|
||||
*You will need to refresh the requirements `pip install -r requirements.txt`*
|
||||
|
||||
### March 10, 2025: Wan2.1GP v1.5
|
||||
👋 Official Teacache support + Smart Teacache (find automatically best parameters for a requested speed multiplier), 10% speed boost with no quality loss, improved lora presets (they can now include prompts and comments to guide the user)
|
||||
|
||||
### March 7, 2025: Wan2.1GP v1.4
|
||||
👋 Fix PyTorch compilation, now it is really 20% faster when activated
|
||||
|
||||
### March 4, 2025: Wan2.1GP v1.3
|
||||
👋 Support for Image to Video with multiples images for different images/prompts combinations (requires *--multiple-images* switch), and added command line *--preload x* to preload in VRAM x MB of the main diffusion model if you find there is too much unused VRAM and you want to (slightly) accelerate the generation process.
|
||||
|
||||
*If you upgrade you will need to do a `pip install -r requirements.txt` again.*
|
||||
|
||||
### March 4, 2025: Wan2.1GP v1.2
|
||||
👋 Implemented tiling on VAE encoding and decoding. No more VRAM peaks at the beginning and at the end
|
||||
|
||||
### March 3, 2025: Wan2.1GP v1.1
|
||||
👋 Added Tea Cache support for faster generations: optimization of kijai's implementation (https://github.com/kijai/ComfyUI-WanVideoWrapper/) of teacache (https://github.com/ali-vilab/TeaCache)
|
||||
|
||||
### March 2, 2025: Wan2.1GP by DeepBeepMeep v1
|
||||
👋 Brings:
|
||||
- Support for all Wan including the Image to Video model
|
||||
- Reduced memory consumption by 2, with possibility to generate more than 10s of video at 720p with a RTX 4090 and 10s of video at 480p with less than 12GB of VRAM. Many thanks to REFLEx (https://github.com/thu-ml/RIFLEx) for their algorithm that allows generating nice looking video longer than 5s.
|
||||
- The usual perks: web interface, multiple generations, loras support, sage attention, auto download of models, ...
|
||||
|
||||
## Original Wan Releases
|
||||
|
||||
### February 25, 2025
|
||||
👋 We've released the inference code and weights of Wan2.1.
|
||||
|
||||
### February 27, 2025
|
||||
👋 Wan2.1 has been integrated into [ComfyUI](https://comfyanonymous.github.io/ComfyUI_examples/wan/). Enjoy!
|
||||
226
docs/CLI.md
226
docs/CLI.md
@@ -1,226 +0,0 @@
|
||||
--vace-1-3B--vace-1-3B# Command Line Reference
|
||||
|
||||
This document covers all available command line options for WanGP.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
# Default launch
|
||||
python wgp.py
|
||||
|
||||
# Specific model modes
|
||||
python wgp.py --i2v # Image-to-video
|
||||
python wgp.py --t2v # Text-to-video (default)
|
||||
python wgp.py --t2v-14B # 14B text-to-video model
|
||||
python wgp.py --t2v-1-3B # 1.3B text-to-video model
|
||||
python wgp.py --i2v-14B # 14B image-to-video model
|
||||
python wgp.py --i2v-1-3B # Fun InP 1.3B image-to-video model
|
||||
python wgp.py --vace-1-3B # VACE ControlNet 1.3B model
|
||||
```
|
||||
|
||||
## Model and Performance Options
|
||||
|
||||
### Model Configuration
|
||||
```bash
|
||||
--quantize-transformer BOOL # Enable/disable transformer quantization (default: True)
|
||||
--compile # Enable PyTorch compilation (requires Triton)
|
||||
--attention MODE # Force attention mode: sdpa, flash, sage, sage2
|
||||
--profile NUMBER # Performance profile 1-5 (default: 4)
|
||||
--preload NUMBER # Preload N MB of diffusion model in VRAM
|
||||
--fp16 # Force fp16 instead of bf16 models
|
||||
--gpu DEVICE # Run on specific GPU device (e.g., "cuda:1")
|
||||
```
|
||||
|
||||
### Performance Profiles
|
||||
- **Profile 1**: Load entire current model in VRAM and keep all unused models in reserved RAM for fast VRAM tranfers
|
||||
- **Profile 2**: Load model parts as needed, keep all unused models in reserved RAM for fast VRAM tranfers
|
||||
- **Profile 3**: Load entire current model in VRAM (requires 24GB for 14B model)
|
||||
- **Profile 4**: Default and recommended, load model parts as needed, most flexible option
|
||||
- **Profile 5**: Minimum RAM usage
|
||||
|
||||
### Memory Management
|
||||
```bash
|
||||
--perc-reserved-mem-max FLOAT # Max percentage of RAM for reserved memory (< 0.5)
|
||||
```
|
||||
|
||||
## Lora Configuration
|
||||
|
||||
```bash
|
||||
--lora-dir PATH # Path to Wan t2v loras directory
|
||||
--lora-dir-i2v PATH # Path to Wan i2v loras directory
|
||||
--lora-dir-hunyuan PATH # Path to Hunyuan t2v loras directory
|
||||
--lora-dir-hunyuan-i2v PATH # Path to Hunyuan i2v loras directory
|
||||
--lora-dir-ltxv PATH # Path to LTX Video loras directory
|
||||
--lora-preset PRESET # Load lora preset file (.lset) on startup
|
||||
--check-loras # Filter incompatible loras (slower startup)
|
||||
```
|
||||
|
||||
## Generation Settings
|
||||
|
||||
### Basic Generation
|
||||
```bash
|
||||
--seed NUMBER # Set default seed value
|
||||
--frames NUMBER # Set default number of frames to generate
|
||||
--steps NUMBER # Set default number of denoising steps
|
||||
--advanced # Launch with advanced mode enabled
|
||||
```
|
||||
|
||||
### Advanced Generation
|
||||
```bash
|
||||
--teacache MULTIPLIER # TeaCache speed multiplier: 0, 1.5, 1.75, 2.0, 2.25, 2.5
|
||||
```
|
||||
|
||||
## Interface and Server Options
|
||||
|
||||
### Server Configuration
|
||||
```bash
|
||||
--server-port PORT # Gradio server port (default: 7860)
|
||||
--server-name NAME # Gradio server name (default: localhost)
|
||||
--listen # Make server accessible on network
|
||||
--share # Create shareable HuggingFace URL for remote access
|
||||
--open-browser # Open browser automatically when launching
|
||||
```
|
||||
|
||||
### Interface Options
|
||||
```bash
|
||||
--lock-config # Prevent modifying video engine configuration from interface
|
||||
--theme THEME_NAME # UI theme: "default" or "gradio"
|
||||
```
|
||||
|
||||
## File and Directory Options
|
||||
|
||||
```bash
|
||||
--settings PATH # Path to folder containing default settings for all models
|
||||
--verbose LEVEL # Information level 0-2 (default: 1)
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic Usage Examples
|
||||
```bash
|
||||
# Launch with specific model and loras
|
||||
python wgp.py --t2v-14B --lora-preset mystyle.lset
|
||||
|
||||
# High-performance setup with compilation
|
||||
python wgp.py --compile --attention sage2 --profile 3
|
||||
|
||||
# Low VRAM setup
|
||||
python wgp.py --t2v-1-3B --profile 4 --attention sdpa
|
||||
|
||||
# Multiple images with custom lora directory
|
||||
python wgp.py --i2v --multiple-images --lora-dir /path/to/shared/loras
|
||||
```
|
||||
|
||||
### Server Configuration Examples
|
||||
```bash
|
||||
# Network accessible server
|
||||
python wgp.py --listen --server-port 8080
|
||||
|
||||
# Shareable server with custom theme
|
||||
python wgp.py --share --theme gradio --open-browser
|
||||
|
||||
# Locked configuration for public use
|
||||
python wgp.py --lock-config --share
|
||||
```
|
||||
|
||||
### Advanced Performance Examples
|
||||
```bash
|
||||
# Maximum performance (requires high-end GPU)
|
||||
python wgp.py --compile --attention sage2 --profile 3 --preload 2000
|
||||
|
||||
# Optimized for RTX 2080Ti
|
||||
python wgp.py --profile 4 --attention sdpa --teacache 2.0
|
||||
|
||||
# Memory-efficient setup
|
||||
python wgp.py --fp16 --profile 4 --perc-reserved-mem-max 0.3
|
||||
```
|
||||
|
||||
### TeaCache Configuration
|
||||
```bash
|
||||
# Different speed multipliers
|
||||
python wgp.py --teacache 1.5 # 1.5x speed, minimal quality loss
|
||||
python wgp.py --teacache 2.0 # 2x speed, some quality loss
|
||||
python wgp.py --teacache 2.5 # 2.5x speed, noticeable quality loss
|
||||
python wgp.py --teacache 0 # Disable TeaCache
|
||||
```
|
||||
|
||||
## Attention Modes
|
||||
|
||||
### SDPA (Default)
|
||||
```bash
|
||||
python wgp.py --attention sdpa
|
||||
```
|
||||
- Available by default with PyTorch
|
||||
- Good compatibility with all GPUs
|
||||
- Moderate performance
|
||||
|
||||
### Sage Attention
|
||||
```bash
|
||||
python wgp.py --attention sage
|
||||
```
|
||||
- Requires Triton installation
|
||||
- 30% faster than SDPA
|
||||
- Small quality cost
|
||||
|
||||
### Sage2 Attention
|
||||
```bash
|
||||
python wgp.py --attention sage2
|
||||
```
|
||||
- Requires Triton and SageAttention 2.x
|
||||
- 40% faster than SDPA
|
||||
- Best performance option
|
||||
|
||||
### Flash Attention
|
||||
```bash
|
||||
python wgp.py --attention flash
|
||||
```
|
||||
- May require CUDA kernel compilation
|
||||
- Good performance
|
||||
- Can be complex to install on Windows
|
||||
|
||||
## Troubleshooting Command Lines
|
||||
|
||||
### Fallback to Basic Setup
|
||||
```bash
|
||||
# If advanced features don't work
|
||||
python wgp.py --attention sdpa --profile 4 --fp16
|
||||
```
|
||||
|
||||
### Debug Mode
|
||||
```bash
|
||||
# Maximum verbosity for troubleshooting
|
||||
python wgp.py --verbose 2 --check-loras
|
||||
```
|
||||
|
||||
### Memory Issue Debugging
|
||||
```bash
|
||||
# Minimal memory usage
|
||||
python wgp.py --profile 4 --attention sdpa --perc-reserved-mem-max 0.2
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Settings Files
|
||||
Load custom settings:
|
||||
```bash
|
||||
python wgp.py --settings /path/to/settings/folder
|
||||
```
|
||||
|
||||
### Lora Presets
|
||||
Create and share lora configurations:
|
||||
```bash
|
||||
# Load specific preset
|
||||
python wgp.py --lora-preset anime_style.lset
|
||||
|
||||
# With custom lora directory
|
||||
python wgp.py --lora-preset mystyle.lset --lora-dir /shared/loras
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
While not command line options, these environment variables can affect behavior:
|
||||
- `CUDA_VISIBLE_DEVICES` - Limit visible GPUs
|
||||
- `PYTORCH_CUDA_ALLOC_CONF` - CUDA memory allocation settings
|
||||
- `TRITON_CACHE_DIR` - Triton cache directory (for Sage attention)
|
||||
@@ -1,108 +0,0 @@
|
||||
# FINETUNES
|
||||
|
||||
A Finetuned model is model that shares the same architecture of one specific model but has derived weights from this model. Some finetuned models have been created by combining multiple finetuned models.
|
||||
|
||||
As there are potentially an infinite number of finetunes, specific finetuned models are not known by default by WanGP. However you can create a finetuned model definition that will tell WanGP about the existence of this finetuned model and WanGP will do as usual all the work for you: autodownload the model and build the user interface.
|
||||
|
||||
WanGP finetune system can be also used to tweak default models : for instance you can add on top of an existing model some loras that will be always applied transparently.
|
||||
|
||||
Finetune models definitions are light json files that can be easily shared. You can find some of them on the WanGP *discord* server https://discord.gg/g7efUW9jGV
|
||||
|
||||
All the finetunes definitions files should be stored in the *finetunes/* subfolder.
|
||||
|
||||
Finetuned models have been tested so far with Wan2.1 text2video, Wan2.1 image2video, Hunyuan Video text2video. There isn't currently any support for LTX Video finetunes.
|
||||
|
||||
|
||||
|
||||
## Create a new Finetune Model Definition
|
||||
All the finetune models definitions are json files stored in the **finetunes/** sub folder. All the corresponding finetune model weights when they are downloaded will be stored in the *ckpts/* subfolder and will sit next to the base models.
|
||||
|
||||
All the models used by WanGP are also described using the finetunes json format and can be found in the **defaults/** subfolder. Please don’t modify any file in the **defaults/** folder.
|
||||
|
||||
However you can use these files as starting points for new definition files and to get an idea of the structure of a definition file. If you want to change how a base model is handled (title, default settings, path to model weights, …) you may override any property of the default finetunes definition file by creating a new file in the finetunes folder with the same name. Everything will happen as if the two models will be merged property by property with a higher priority given to the finetunes model definition.
|
||||
|
||||
A definition is built from a *settings file* that can contains all the default parameters for a video generation. On top of this file a subtree named **model** contains all the information regarding the finetune (URLs to download model, corresponding base model id, ...).
|
||||
|
||||
You can obtain a settings file in several ways:
|
||||
- In the subfolder **settings**, get the json file that corresponds to the base model of your finetune (see the next section for the list of ids of base models)
|
||||
- From the user interface, select the base model for which you want to create a finetune and click **export settings**
|
||||
|
||||
Here are steps:
|
||||
1) Create a *settings file*
|
||||
2) Add a **model** subtree with the finetune description
|
||||
3) Save this file in the subfolder **finetunes**. The name used for the file will be used as its id. It is a good practise to prefix the name of this file with the base model. For instance for a finetune named **Fast*** based on Hunyuan Text 2 Video model *hunyuan_t2v_fast.json*. In this example the Id is *hunyuan_t2v_fast*.
|
||||
4) Restart WanGP
|
||||
|
||||
## Architecture Models Ids
|
||||
A finetune is derived from a base model and will inherit all the user interface and corresponding model capabilities, here are some Architecture Ids:
|
||||
- *t2v*: Wan 2.1 Video text 2 video
|
||||
- *i2v*: Wan 2.1 Video image 2 video 480p and 720p
|
||||
- *vace_14B*: Wan 2.1 Vace 14B
|
||||
- *hunyuan*: Hunyuan Video text 2 video
|
||||
- *hunyuan_i2v*: Hunyuan Video image 2 video
|
||||
|
||||
Any file name in the defaults subfolder (without the json extension) corresponds to an architecture id.
|
||||
|
||||
Please note that weights of some architectures correspond to a combination of weight of a one architecture which are completed by the weights of one more or modules.
|
||||
|
||||
A module is a set a weights that are insufficient to be model by itself but that can be added to an existing model to extend its capabilities.
|
||||
|
||||
For instance if one adds a module *vace_14B* on top of a model with architecture *t2v* one gets get a model with the *vace_14B* architecture. Here *vace_14B* stands for both an architecture name and a module name. The module system allows you to reuse shared weights between models.
|
||||
|
||||
|
||||
## The Model Subtree
|
||||
- *name* : name of the finetune used to select
|
||||
- *architecture* : architecture Id of the base model of the finetune (see previous section)
|
||||
- *description*: description of the finetune that will appear at the top
|
||||
- *URLs*: URLs of all the finetune versions (quantized / non quantized). WanGP will pick the version that is the closest to the user preferences. You will need to follow a naming convention to help WanGP identify the content of each version (see next section). Right now WanGP supports only 8 bits quantized model that have been quantized using **quanto**. WanGP offers a command switch to build easily such a quantized model (see below). *URLs* can contain also paths to local file to allow testing.
|
||||
- *modules*: this a list of modules to be combined with the models referenced by the URLs. A module is a model extension that is merged with a model to expand its capabilities. Supported models so far are : *vace_14B* and *multitalk*. For instance the full Vace model is the fusion of a Wan text 2 video and the Vace module.
|
||||
- *preload_URLs* : URLs of files to download no matter what (used to load quantization maps for instance)
|
||||
-*loras* : URLs of Loras that will applied before any other Lora specified by the user. These loras will be quite often Loras accelerator. For instance if you specified here the FusioniX Lora you will be able to reduce the number of generation steps to -*loras_multipliers* : a list of float numbers that defines the weight of each Lora mentioned above.
|
||||
- *auto_quantize*: if set to True and no quantized model URL is provided, WanGP will perform on the fly quantization if the user expects a quantized model
|
||||
-*visible* : by default assumed to be true. If set to false the model will no longer be visible. This can be useful if you create a finetune to override a default model and hide it.
|
||||
-*image_outputs* : turn any model that generates a video into a model that generates images. In fact it will adapt the user interface for image generation and ask the model to generate a video with a single frame.
|
||||
|
||||
In order to favor reusability the properties of *URLs*, *modules*, *loras* and *preload_URLs* can contain instead of a list of URLs a single text which corresponds to the id of a finetune or default model to reuse.
|
||||
|
||||
For example let’s say you have defined a *t2v_fusionix.json* file which contains the URLs to download the finetune. In the *vace_fusionix.json* you can write « URLs » : « fusionix » to reuse automatically the URLS already defined in the correspond file.
|
||||
|
||||
Example of **model** subtree
|
||||
```
|
||||
"model":
|
||||
{
|
||||
"name": "Wan text2video FusioniX 14B",
|
||||
"architecture" : "t2v",
|
||||
"description": "A powerful merged text-to-video model based on the original WAN 2.1 T2V model, enhanced using multiple open-source components and LoRAs to boost motion realism, temporal consistency, and expressive detail. multiple open-source models and LoRAs to boost temporal quality, expressiveness, and motion realism.",
|
||||
"URLs": [
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_fp16.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_fp16_int8.safetensors",
|
||||
"https://huggingface.co/DeepBeepMeep/Wan2.1/resolve/main/Wan14BT2VFusioniX_quanto_bf16_int8.safetensors"
|
||||
],
|
||||
"preload_URLs": [
|
||||
],
|
||||
"auto_quantize": true
|
||||
},
|
||||
```
|
||||
|
||||
## Finetune Model Naming Convention
|
||||
If a model is not quantized, it is assumed to be mostly 16 bits (with maybe a few 32 bits weights), so *bf16* or *fp16* should appear somewhere in the name. If you need examples just look at the **ckpts** subfolder, the naming convention for the base models is the same.
|
||||
|
||||
If a model is quantized the term *quanto* should also be included since WanGP supports for the moment only *quanto* quantized model, most specically you should replace *fp16* by *quanto_fp16_int8* or *bf6* by *quanto_bf16_int8*.
|
||||
|
||||
Please note it is important than *bf16", "fp16* and *quanto* are all in lower cases letters.
|
||||
|
||||
## Creating a Quanto Quantized file
|
||||
If you launch the app with the *--save-quantized* switch, WanGP will create a quantized file in the **ckpts** subfolder just after the model has been loaded. Please note that the model will *bf16* or *fp16* quantized depending on what you chose in the configuration menu.
|
||||
|
||||
1) Make sure that in the finetune definition json file there is only a URL or filepath that points to the non quantized model
|
||||
2) Launch WanGP *python wgp.py --save-quantized*
|
||||
3) In the configuration menu *Transformer Data Type* property choose either *BF16* of *FP16*
|
||||
4) Launch a video generation (settings used do not matter). As soon as the model is loaded, a new quantized model will be created in the **ckpts** subfolder if it doesn't already exist.
|
||||
5) WanGP will update automatically the finetune definition file with the local path of the newly created quantized file (the list "URLs" will have an extra value such as *"ckpts/finetune_quanto_fp16_int8.safetensors"*
|
||||
6) Remove *--save-quantized*, restart WanGP and select *Scaled Int8 Quantization* in the *Transformer Model Quantization* property
|
||||
7) Launch a new generation and verify in the terminal window that the right quantized model is loaded
|
||||
8) In order to share the finetune definition file you will need to store the fine model weights in the cloud. You can upload them for instance on *Huggingface*. You can now replace in the finetune definition file the local path by a URL (on Huggingface to get the URL of the model file click *Copy download link* when accessing the model properties)
|
||||
|
||||
You need to create a quantized model specifically for *bf16* or *fp16* as they can not converted on the fly. However there is no need for a non quantized model as they can be converted on the fly while being loaded.
|
||||
|
||||
Wan models supports both *fp16* and *bf16* data types albeit *fp16* delivers in theory better quality. On the contrary Hunyuan and LTXV supports only *bf16*.
|
||||
@@ -1,194 +0,0 @@
|
||||
# Getting Started with WanGP
|
||||
|
||||
This guide will help you get started with WanGP video generation quickly and easily.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
- A compatible GPU (RTX 10XX or newer recommended)
|
||||
- Python 3.10.9 installed
|
||||
- At least 6GB of VRAM for basic models
|
||||
- Internet connection for model downloads
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### Option 1: One-Click Installation (Recommended)
|
||||
Use [Pinokio App](https://pinokio.computer/) for the easiest installation experience.
|
||||
|
||||
### Option 2: Manual Installation
|
||||
```bash
|
||||
git clone https://github.com/deepbeepmeep/Wan2GP.git
|
||||
cd Wan2GP
|
||||
conda create -n wan2gp python=3.10.9
|
||||
conda activate wan2gp
|
||||
pip install torch==2.6.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu124
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
For detailed installation instructions, see [INSTALLATION.md](INSTALLATION.md).
|
||||
|
||||
## First Launch
|
||||
|
||||
### Basic Launch
|
||||
```bash
|
||||
python wgp.py
|
||||
```
|
||||
This launches the WanGP generator with default settings. You will be able to pick from a Drop Down menu which model you want to use.
|
||||
|
||||
### Alternative Modes
|
||||
```bash
|
||||
python wgp.py --i2v # Wan Image-to-video mode
|
||||
python wgp.py --t2v-1-3B # Wan Smaller, faster model
|
||||
```
|
||||
|
||||
## Understanding the Interface
|
||||
|
||||
When you launch WanGP, you'll see a web interface with several sections:
|
||||
|
||||
### Main Generation Panel
|
||||
- **Model Selection**: Dropdown to choose between different models
|
||||
- **Prompt**: Text description of what you want to generate
|
||||
- **Generate Button**: Start the video generation process
|
||||
|
||||
### Advanced Settings (click checkbox to enable)
|
||||
- **Generation Settings**: Steps, guidance, seeds
|
||||
- **Loras**: Additional style customizations
|
||||
- **Sliding Window**: For longer videos
|
||||
|
||||
## Your First Video
|
||||
|
||||
Let's generate a simple text-to-video:
|
||||
|
||||
1. **Launch WanGP**: `python wgp.py`
|
||||
2. **Open Browser**: Navigate to `http://localhost:7860`
|
||||
3. **Enter Prompt**: "A cat walking in a garden"
|
||||
4. **Click Generate**: Wait for the video to be created
|
||||
5. **View Result**: The video will appear in the output section
|
||||
|
||||
### Recommended First Settings
|
||||
- **Model**: Wan 2.1 text2video 1.3B (faster, lower VRAM)
|
||||
- **Frames**: 49 (about 2 seconds)
|
||||
- **Steps**: 20 (good balance of speed/quality)
|
||||
|
||||
## Model Selection
|
||||
|
||||
### Text-to-Video Models
|
||||
- **Wan 2.1 T2V 1.3B**: Fastest, lowest VRAM (6GB), good quality
|
||||
- **Wan 2.1 T2V 14B**: Best quality, requires more VRAM (12GB+)
|
||||
- **Hunyuan Video**: Excellent quality, slower generation
|
||||
- **LTX Video**: Good for longer videos
|
||||
|
||||
### Image-to-Video Models
|
||||
- **Wan Fun InP 1.3B**: Fast image animation
|
||||
- **Wan Fun InP 14B**: Higher quality image animation
|
||||
- **VACE**: Advanced control over video generation
|
||||
|
||||
### Choosing the Right Model
|
||||
- **Low VRAM (6-8GB)**: Use 1.3B models
|
||||
- **Medium VRAM (10-12GB)**: Use 14B models or Hunyuan
|
||||
- **High VRAM (16GB+)**: Any model, longer videos
|
||||
|
||||
## Basic Settings Explained
|
||||
|
||||
### Generation Settings
|
||||
- **Frames**: Number of frames (more = longer video)
|
||||
- 25 frames ≈ 1 second
|
||||
- 49 frames ≈ 2 seconds
|
||||
- 73 frames ≈ 3 seconds
|
||||
|
||||
- **Steps**: Quality vs Speed tradeoff
|
||||
- 15 steps: Fast, lower quality
|
||||
- 20 steps: Good balance
|
||||
- 30+ steps: High quality, slower
|
||||
|
||||
- **Guidance Scale**: How closely to follow the prompt
|
||||
- 3-5: More creative interpretation
|
||||
- 7-10: Closer to prompt description
|
||||
- 12+: Very literal interpretation
|
||||
|
||||
### Seeds
|
||||
- **Random Seed**: Different result each time
|
||||
- **Fixed Seed**: Reproducible results
|
||||
- **Use same seed + prompt**: Generate variations
|
||||
|
||||
## Common Beginner Issues
|
||||
|
||||
### "Out of Memory" Errors
|
||||
1. Use smaller models (1.3B instead of 14B)
|
||||
2. Reduce frame count
|
||||
3. Lower resolution in advanced settings
|
||||
4. Enable quantization (usually on by default)
|
||||
|
||||
### Slow Generation
|
||||
1. Use 1.3B models for speed
|
||||
2. Reduce number of steps
|
||||
3. Install Sage attention (see [INSTALLATION.md](INSTALLATION.md))
|
||||
4. Enable TeaCache: `python wgp.py --teacache 2.0`
|
||||
|
||||
### Poor Quality Results
|
||||
1. Increase number of steps (25-30)
|
||||
2. Improve prompt description
|
||||
3. Use 14B models if you have enough VRAM
|
||||
4. Enable Skip Layer Guidance in advanced settings
|
||||
|
||||
## Writing Good Prompts
|
||||
|
||||
### Basic Structure
|
||||
```
|
||||
[Subject] [Action] [Setting] [Style/Quality modifiers]
|
||||
```
|
||||
|
||||
### Examples
|
||||
```
|
||||
A red sports car driving through a mountain road at sunset, cinematic, high quality
|
||||
|
||||
A woman with long hair walking on a beach, waves in the background, realistic, detailed
|
||||
|
||||
A cat sitting on a windowsill watching rain, cozy atmosphere, soft lighting
|
||||
```
|
||||
|
||||
### Tips
|
||||
- Be specific about what you want
|
||||
- Include style descriptions (cinematic, realistic, etc.)
|
||||
- Mention lighting and atmosphere
|
||||
- Describe the setting in detail
|
||||
- Use quality modifiers (high quality, detailed, etc.)
|
||||
|
||||
## Next Steps
|
||||
|
||||
Once you're comfortable with basic generation:
|
||||
|
||||
1. **Explore Advanced Features**:
|
||||
- [Loras Guide](LORAS.md) - Customize styles and characters
|
||||
- [VACE ControlNet](VACE.md) - Advanced video control
|
||||
- [Command Line Options](CLI.md) - Optimize performance
|
||||
|
||||
2. **Improve Performance**:
|
||||
- Install better attention mechanisms
|
||||
- Optimize memory settings
|
||||
- Use compilation for speed
|
||||
|
||||
3. **Join the Community**:
|
||||
- [Discord Server](https://discord.gg/g7efUW9jGV) - Get help and share videos
|
||||
- Share your best results
|
||||
- Learn from other users
|
||||
|
||||
## Troubleshooting First Steps
|
||||
|
||||
### Installation Issues
|
||||
- Ensure Python 3.10.9 is used
|
||||
- Check CUDA version compatibility
|
||||
- See [INSTALLATION.md](INSTALLATION.md) for detailed steps
|
||||
|
||||
### Generation Issues
|
||||
- Check GPU compatibility
|
||||
- Verify sufficient VRAM
|
||||
- Try basic settings first
|
||||
- See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for specific issues
|
||||
|
||||
### Performance Issues
|
||||
- Use appropriate model for your hardware
|
||||
- Enable performance optimizations
|
||||
- Check [CLI.md](CLI.md) for optimization flags
|
||||
|
||||
Remember: Start simple and gradually explore more advanced features as you become comfortable with the basics!
|
||||
@@ -1,170 +0,0 @@
|
||||
# Installation Guide
|
||||
|
||||
This guide covers installation for different GPU generations and operating systems.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.10.9
|
||||
- Conda or Python venv
|
||||
- Compatible GPU (RTX 10XX or newer recommended)
|
||||
|
||||
## Installation for RTX 10XX to RTX 40XX (Stable)
|
||||
|
||||
This installation uses PyTorch 2.6.0 which is well-tested and stable.
|
||||
|
||||
### Step 1: Download and Setup Environment
|
||||
|
||||
```shell
|
||||
# Clone the repository
|
||||
git clone https://github.com/deepbeepmeep/Wan2GP.git
|
||||
cd Wan2GP
|
||||
|
||||
# Create Python 3.10.9 environment using conda
|
||||
conda create -n wan2gp python=3.10.9
|
||||
conda activate wan2gp
|
||||
```
|
||||
|
||||
### Step 2: Install PyTorch
|
||||
|
||||
```shell
|
||||
# Install PyTorch 2.6.0 with CUDA 12.4
|
||||
pip install torch==2.6.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu124
|
||||
```
|
||||
|
||||
### Step 3: Install Dependencies
|
||||
|
||||
```shell
|
||||
# Install core dependencies
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Step 4: Optional Performance Optimizations
|
||||
|
||||
#### Sage Attention (30% faster)
|
||||
|
||||
```shell
|
||||
# Windows only: Install Triton
|
||||
pip install triton-windows
|
||||
|
||||
# For both Windows and Linux
|
||||
pip install sageattention==1.0.6
|
||||
```
|
||||
|
||||
#### Sage 2 Attention (40% faster)
|
||||
|
||||
```shell
|
||||
# Windows
|
||||
pip install triton-windows
|
||||
pip install https://github.com/woct0rdho/SageAttention/releases/download/v2.1.1-windows/sageattention-2.1.1+cu126torch2.6.0-cp310-cp310-win_amd64.whl
|
||||
|
||||
# Linux (manual compilation required)
|
||||
git clone https://github.com/thu-ml/SageAttention
|
||||
cd SageAttention
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
#### Flash Attention
|
||||
|
||||
```shell
|
||||
# May require CUDA kernel compilation on Windows
|
||||
pip install flash-attn==2.7.2.post1
|
||||
```
|
||||
|
||||
## Installation for RTX 50XX (Beta)
|
||||
|
||||
RTX 50XX GPUs require PyTorch 2.7.0 (beta). This version may be less stable.
|
||||
|
||||
⚠️ **Important:** Use Python 3.10 for compatibility with pip wheels.
|
||||
|
||||
### Step 1: Setup Environment
|
||||
|
||||
```shell
|
||||
# Clone and setup (same as above)
|
||||
git clone https://github.com/deepbeepmeep/Wan2GP.git
|
||||
cd Wan2GP
|
||||
conda create -n wan2gp python=3.10.9
|
||||
conda activate wan2gp
|
||||
```
|
||||
|
||||
### Step 2: Install PyTorch Beta
|
||||
|
||||
```shell
|
||||
# Install PyTorch 2.7.0 with CUDA 12.8
|
||||
pip install torch==2.7.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu128
|
||||
```
|
||||
|
||||
### Step 3: Install Dependencies
|
||||
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Step 4: Optional Optimizations for RTX 50XX
|
||||
|
||||
#### Sage Attention
|
||||
|
||||
```shell
|
||||
# Windows
|
||||
pip install triton-windows
|
||||
pip install sageattention==1.0.6
|
||||
|
||||
# Linux
|
||||
pip install sageattention==1.0.6
|
||||
```
|
||||
|
||||
#### Sage 2 Attention
|
||||
|
||||
```shell
|
||||
# Windows
|
||||
pip install triton-windows
|
||||
pip install https://github.com/woct0rdho/SageAttention/releases/download/v2.1.1-windows/sageattention-2.1.1+cu128torch2.7.0-cp310-cp310-win_amd64.whl
|
||||
|
||||
# Linux (manual compilation)
|
||||
git clone https://github.com/thu-ml/SageAttention
|
||||
cd SageAttention
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Attention Modes
|
||||
|
||||
WanGP supports several attention implementations:
|
||||
|
||||
- **SDPA** (default): Available by default with PyTorch
|
||||
- **Sage**: 30% speed boost with small quality cost
|
||||
- **Sage2**: 40% speed boost
|
||||
- **Flash**: Good performance, may be complex to install on Windows
|
||||
|
||||
## Performance Profiles
|
||||
|
||||
Choose a profile based on your hardware:
|
||||
|
||||
- **Profile 3 (LowRAM_HighVRAM)**: Loads entire model in VRAM, requires 24GB VRAM for 8-bit quantized 14B model
|
||||
- **Profile 4 (LowRAM_LowVRAM)**: Default, loads model parts as needed, slower but lower VRAM requirement
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Sage Attention Issues
|
||||
|
||||
If Sage attention doesn't work:
|
||||
|
||||
1. Check if Triton is properly installed
|
||||
2. Clear Triton cache
|
||||
3. Fallback to SDPA attention:
|
||||
```bash
|
||||
python wgp.py --attention sdpa
|
||||
```
|
||||
|
||||
### Memory Issues
|
||||
|
||||
- Use lower resolution or shorter videos
|
||||
- Enable quantization (default)
|
||||
- Use Profile 4 for lower VRAM usage
|
||||
- Consider using 1.3B models instead of 14B models
|
||||
|
||||
### GPU Compatibility
|
||||
|
||||
- RTX 10XX, 20XX: Supported with SDPA attention
|
||||
- RTX 30XX, 40XX: Full feature support
|
||||
- RTX 50XX: Beta support with PyTorch 2.7.0
|
||||
|
||||
For more troubleshooting, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||
273
docs/LORAS.md
273
docs/LORAS.md
@@ -1,273 +0,0 @@
|
||||
# Loras Guide
|
||||
|
||||
Loras (Low-Rank Adaptations) allow you to customize video generation models by adding specific styles, characters, or effects to your videos.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
Loras are organized in different folders based on the model they're designed for:
|
||||
|
||||
### Wan Text-to-Video Models
|
||||
- `loras/` - General t2v loras
|
||||
- `loras/1.3B/` - Loras specifically for 1.3B models
|
||||
- `loras/14B/` - Loras specifically for 14B models
|
||||
|
||||
### Wan Image-to-Video Models
|
||||
- `loras_i2v/` - Image-to-video loras
|
||||
|
||||
### Other Models
|
||||
- `loras_hunyuan/` - Hunyuan Video t2v loras
|
||||
- `loras_hunyuan_i2v/` - Hunyuan Video i2v loras
|
||||
- `loras_ltxv/` - LTX Video loras
|
||||
- `loras_flux/` - Flux loras
|
||||
|
||||
## Custom Lora Directory
|
||||
|
||||
You can specify custom lora directories when launching the app:
|
||||
|
||||
```bash
|
||||
# Use shared lora directory for both t2v and i2v
|
||||
python wgp.py --lora-dir /path/to/shared/loras --lora-dir-i2v /path/to/shared/loras
|
||||
|
||||
# Specify different directories for different models
|
||||
python wgp.py --lora-dir-hunyuan /path/to/hunyuan/loras --lora-dir-ltxv /path/to/ltx/loras
|
||||
```
|
||||
|
||||
## Using Loras
|
||||
|
||||
### Basic Usage
|
||||
|
||||
1. Place your lora files in the appropriate directory
|
||||
2. Launch WanGP
|
||||
3. In the Advanced Tab, select the "Loras" section
|
||||
4. Check the loras you want to activate
|
||||
5. Set multipliers for each lora (default is 1.0)
|
||||
|
||||
### Lora Multipliers
|
||||
|
||||
Multipliers control the strength of each lora's effect:
|
||||
|
||||
#### Simple Multipliers
|
||||
```
|
||||
1.2 0.8
|
||||
```
|
||||
- First lora: 1.2 strength
|
||||
- Second lora: 0.8 strength
|
||||
|
||||
#### Time-based Multipliers
|
||||
For dynamic effects over generation steps, use comma-separated values:
|
||||
```
|
||||
0.9,0.8,0.7
|
||||
1.2,1.1,1.0
|
||||
```
|
||||
- For 30 steps: steps 0-9 use first value, 10-19 use second, 20-29 use third
|
||||
- First lora: 0.9 → 0.8 → 0.7
|
||||
- Second lora: 1.2 → 1.1 → 1.0
|
||||
|
||||
## Lora Presets
|
||||
|
||||
Lora Presets are combinations of loras with predefined multipliers and prompts.
|
||||
|
||||
### Creating Presets
|
||||
1. Configure your loras and multipliers
|
||||
2. Write a prompt with comments (lines starting with #)
|
||||
3. Save as a preset with `.lset` extension
|
||||
|
||||
### Example Preset
|
||||
```
|
||||
# Use the keyword "ohnvx" to trigger the lora
|
||||
A ohnvx character is driving a car through the city
|
||||
```
|
||||
|
||||
### Using Presets
|
||||
```bash
|
||||
# Load preset on startup
|
||||
python wgp.py --lora-preset mypreset.lset
|
||||
```
|
||||
|
||||
### Managing Presets
|
||||
- Edit, save, or delete presets directly from the web interface
|
||||
- Presets include comments with usage instructions
|
||||
- Share `.lset` files with other users
|
||||
|
||||
## Supported Formats
|
||||
|
||||
WanGP supports multiple lora formats:
|
||||
- **Safetensors** (.safetensors)
|
||||
- **Replicate** format
|
||||
- **Standard PyTorch** (.pt, .pth)
|
||||
|
||||
|
||||
## Loras Accelerators
|
||||
Most Loras are used to apply a specific style or to alter the content of the output of the generated video.
|
||||
However some Loras have been designed to tranform a model into a distilled model which requires fewer steps to generate a video.
|
||||
|
||||
You will find most *Loras Accelerators* here:
|
||||
https://huggingface.co/DeepBeepMeep/Wan2.1/tree/main/loras_accelerators
|
||||
|
||||
### Setup Instructions
|
||||
1. Download the Lora
|
||||
2. Place it in your `loras/` directory if it is a t2v lora or in the `loras_i2v/` directory if it isa i2v lora
|
||||
|
||||
## FusioniX (or FusionX) Lora
|
||||
If you need just one Lora accelerator use this one. It is a combination of multiple Loras acelerators (including Causvid below) and style loras. It will not only accelerate the video generation but it will also improve the quality. There are two versions of this lora whether you use it for t2v or i2v
|
||||
|
||||
### Usage
|
||||
1. Select a Wan t2v model (e.g., Wan 2.1 text2video 13B or Vace 13B)
|
||||
2. Enable Advanced Mode
|
||||
3. In Advanced Generation Tab:
|
||||
- Set Guidance Scale = 1
|
||||
- Set Shift Scale = 2
|
||||
4. In Advanced Lora Tab:
|
||||
- Select CausVid Lora
|
||||
- Set multiplier to 1
|
||||
5. Set generation steps from 8-10
|
||||
6. Generate!
|
||||
|
||||
## Safe-Forcing lightx2v Lora (Video Generation Accelerator)
|
||||
Safeforcing Lora has been created by Kijai from the Safe-Forcing lightx2v distilled Wan model and can generate videos with only 2 steps and offers also a 2x speed improvement since it doesnt require classifier free guidance. It works on both t2v and i2v models
|
||||
You will find it under the name of *Wan21_T2V_14B_lightx2v_cfg_step_distill_lora_rank32.safetensors*
|
||||
|
||||
### Usage
|
||||
1. Select a Wan t2v or i2v model (e.g., Wan 2.1 text2video 13B or Vace 13B)
|
||||
2. Enable Advanced Mode
|
||||
3. In Advanced Generation Tab:
|
||||
- Set Guidance Scale = 1
|
||||
- Set Shift Scale = 5
|
||||
4. In Advanced Lora Tab:
|
||||
- Select the Lora above
|
||||
- Set multiplier to 1
|
||||
5. Set generation steps to 2-8
|
||||
6. Generate!
|
||||
|
||||
|
||||
## CausVid Lora (Video Generation Accelerator)
|
||||
CausVid is a distilled Wan model that generates videos in 4-12 steps with 2x speed improvement.
|
||||
|
||||
### Usage
|
||||
1. Select a Wan t2v model (e.g., Wan 2.1 text2video 13B or Vace 13B)
|
||||
2. Enable Advanced Mode
|
||||
3. In Advanced Generation Tab:
|
||||
- Set Guidance Scale = 1
|
||||
- Set Shift Scale = 7
|
||||
4. In Advanced Lora Tab:
|
||||
- Select CausVid Lora
|
||||
- Set multiplier to 0.3
|
||||
5. Set generation steps to 12
|
||||
6. Generate!
|
||||
|
||||
### CausVid Step/Multiplier Relationship
|
||||
- **12 steps**: 0.3 multiplier (recommended)
|
||||
- **8 steps**: 0.5-0.7 multiplier
|
||||
- **4 steps**: 0.8-1.0 multiplier
|
||||
|
||||
*Note: Lower steps = lower quality (especially motion)*
|
||||
|
||||
|
||||
## AccVid Lora (Video Generation Accelerator)
|
||||
|
||||
AccVid is a distilled Wan model that generates videos with a 2x speed improvement since classifier free guidance is no longer needed (that is cfg = 1).
|
||||
|
||||
|
||||
### Usage
|
||||
1. Select a Wan t2v model (e.g., Wan 2.1 text2video 13B or Vace 13B) or Wan i2v model
|
||||
2. Enable Advanced Mode
|
||||
3. In Advanced Generation Tab:
|
||||
- Set Guidance Scale = 1
|
||||
- Set Shift Scale = 5
|
||||
4. The number steps remain unchanged compared to what you would use with the original model but it will be two times faster since classifier free guidance is not needed
|
||||
|
||||
|
||||
https://huggingface.co/Kijai/WanVideo_comfy/blob/main/Wan21_T2V_14B_lightx2v_cfg_step_distill_lora_rank32.safetensors
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Fast Loading/Unloading
|
||||
- Loras can be added/removed without restarting the app
|
||||
- Use the "Refresh" button to detect new loras
|
||||
- Enable `--check-loras` to filter incompatible loras (slower startup)
|
||||
|
||||
### Memory Management
|
||||
- Loras are loaded on-demand to save VRAM
|
||||
- Multiple loras can be used simultaneously
|
||||
- Time-based multipliers don't use extra memory
|
||||
|
||||
## Finding Loras
|
||||
|
||||
### Sources
|
||||
- **[Civitai](https://civitai.com/)** - Large community collection
|
||||
- **HuggingFace** - Official and community loras
|
||||
- **Discord Server** - Community recommendations
|
||||
|
||||
### Creating Loras
|
||||
- **Kohya** - Popular training tool
|
||||
- **OneTrainer** - Alternative training solution
|
||||
- **Custom datasets** - Train on your own content
|
||||
|
||||
## Macro System (Advanced)
|
||||
|
||||
Create multiple prompts from templates using macros. This allows you to generate variations of a sentence by defining lists of values for different variables.
|
||||
|
||||
**Syntax Rule:**
|
||||
|
||||
Define your variables on a single line starting with `!`. Each complete variable definition, including its name and values, **must be separated by a colon (`:`)**.
|
||||
|
||||
**Format:**
|
||||
|
||||
```
|
||||
! {Variable1}="valueA","valueB" : {Variable2}="valueC","valueD"
|
||||
This is a template using {Variable1} and {Variable2}.
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
The following macro will generate three distinct prompts by cycling through the values for each variable.
|
||||
|
||||
**Macro Definition:**
|
||||
|
||||
```
|
||||
! {Subject}="cat","woman","man" : {Location}="forest","lake","city" : {Possessive}="its","her","his"
|
||||
In the video, a {Subject} is presented. The {Subject} is in a {Location} and looks at {Possessive} watch.
|
||||
```
|
||||
|
||||
**Generated Output:**
|
||||
|
||||
```
|
||||
In the video, a cat is presented. The cat is in a forest and looks at its watch.
|
||||
In the video, a woman is presented. The woman is in a lake and looks at her watch.
|
||||
In the video, a man is presented. The man is in a city and looks at his watch.
|
||||
```
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Lora Not Working
|
||||
1. Check if lora is compatible with your model size (1.3B vs 14B)
|
||||
2. Verify lora format is supported
|
||||
3. Try different multiplier values
|
||||
4. Check the lora was trained for your model type (t2v vs i2v)
|
||||
|
||||
### Performance Issues
|
||||
1. Reduce number of active loras
|
||||
2. Lower multiplier values
|
||||
3. Use `--check-loras` to filter incompatible files
|
||||
4. Clear lora cache if issues persist
|
||||
|
||||
### Memory Errors
|
||||
1. Use fewer loras simultaneously
|
||||
2. Reduce model size (use 1.3B instead of 14B)
|
||||
3. Lower video resolution or frame count
|
||||
4. Enable quantization if not already active
|
||||
|
||||
## Command Line Options
|
||||
|
||||
```bash
|
||||
# Lora-related command line options
|
||||
--lora-dir path # Path to t2v loras directory
|
||||
--lora-dir-i2v path # Path to i2v loras directory
|
||||
--lora-dir-hunyuan path # Path to Hunyuan t2v loras
|
||||
--lora-dir-hunyuan-i2v path # Path to Hunyuan i2v loras
|
||||
--lora-dir-ltxv path # Path to LTX Video loras
|
||||
--lora-dir-flux path # Path to Flux loras
|
||||
--lora-preset preset # Load preset on startup
|
||||
--check-loras # Filter incompatible loras
|
||||
```
|
||||
267
docs/MODELS.md
267
docs/MODELS.md
@@ -1,267 +0,0 @@
|
||||
# Models Overview
|
||||
|
||||
WanGP supports multiple video generation models, each optimized for different use cases and hardware configurations.
|
||||
|
||||
Most models can combined with Loras Accelerators (check the Lora guide) to accelerate the generation of a video x2 or x3 with little quality loss
|
||||
|
||||
|
||||
## Wan 2.1 Text2Video Models
|
||||
Please note that that the term *Text2Video* refers to the underlying Wan architecture but as it has been greatly improved overtime many derived Text2Video models can now generate videos using images.
|
||||
|
||||
#### Wan 2.1 Text2Video 1.3B
|
||||
- **Size**: 1.3 billion parameters
|
||||
- **VRAM**: 6GB minimum
|
||||
- **Speed**: Fast generation
|
||||
- **Quality**: Good quality for the size
|
||||
- **Best for**: Quick iterations, lower-end hardware
|
||||
- **Command**: `python wgp.py --t2v-1-3B`
|
||||
|
||||
#### Wan 2.1 Text2Video 14B
|
||||
- **Size**: 14 billion parameters
|
||||
- **VRAM**: 12GB+ recommended
|
||||
- **Speed**: Slower but higher quality
|
||||
- **Quality**: Excellent detail and coherence
|
||||
- **Best for**: Final production videos
|
||||
- **Command**: `python wgp.py --t2v-14B`
|
||||
|
||||
#### Wan Vace 1.3B
|
||||
- **Type**: ControlNet for advanced video control
|
||||
- **VRAM**: 6GB minimum
|
||||
- **Features**: Motion transfer, object injection, inpainting
|
||||
- **Best for**: Advanced video manipulation
|
||||
- **Command**: `python wgp.py --vace-1.3B`
|
||||
|
||||
#### Wan Vace 14B
|
||||
- **Type**: Large ControlNet model
|
||||
- **VRAM**: 12GB+ recommended
|
||||
- **Features**: All Vace features with higher quality
|
||||
- **Best for**: Professional video editing workflows
|
||||
|
||||
#### MoviiGen (Experimental)
|
||||
- **Resolution**: Claims 1080p capability
|
||||
- **VRAM**: 20GB+ required
|
||||
- **Speed**: Very slow generation
|
||||
- **Features**: Should generate cinema like video, specialized for 2.1 / 1 ratios
|
||||
- **Status**: Experimental, feedback welcome
|
||||
|
||||
<BR>
|
||||
|
||||
## Wan 2.1 Image-to-Video Models
|
||||
|
||||
#### Wan 2.1 Image2Video 14B
|
||||
- **Size**: 14 billion parameters
|
||||
- **VRAM**: 12GB+ recommended
|
||||
- **Speed**: Slower but higher quality
|
||||
- **Quality**: Excellent detail and coherence
|
||||
- **Best for**: Most Loras available work with this model
|
||||
- **Command**: `python wgp.py --i2v-14B`
|
||||
|
||||
#### FLF2V
|
||||
- **Type**: Start/end frame specialist
|
||||
- **Resolution**: Optimized for 720p
|
||||
- **Official**: Wan team supported
|
||||
- **Use case**: Image-to-video with specific endpoints
|
||||
|
||||
|
||||
<BR>
|
||||
|
||||
## Wan 2.1 Specialized Models
|
||||
|
||||
#### Multitalk
|
||||
- **Type**: Multi Talking head animation
|
||||
- **Input**: Voice track + image
|
||||
- **Works on**: People
|
||||
- **Use case**: Lip-sync and voice-driven animation for up to two people
|
||||
|
||||
#### FantasySpeaking
|
||||
- **Type**: Talking head animation
|
||||
- **Input**: Voice track + image
|
||||
- **Works on**: People and objects
|
||||
- **Use case**: Lip-sync and voice-driven animation
|
||||
|
||||
#### Phantom
|
||||
- **Type**: Person/object transfer
|
||||
- **Resolution**: Works well at 720p
|
||||
- **Requirements**: 30+ steps for good results
|
||||
- **Best for**: Transferring subjects between videos
|
||||
|
||||
#### Recam Master
|
||||
- **Type**: Viewpoint change
|
||||
- **Requirements**: 81+ frame input videos, 15+ denoising steps
|
||||
- **Use case**: View same scene from different angles
|
||||
|
||||
#### Sky Reels v2 Diffusion
|
||||
- **Type**: Diffusion Forcing model
|
||||
- **Specialty**: "Infinite length" videos
|
||||
- **Features**: High quality continuous generation
|
||||
|
||||
|
||||
<BR>
|
||||
|
||||
## Wan Fun InP Models
|
||||
|
||||
#### Wan Fun InP 1.3B
|
||||
- **Size**: 1.3 billion parameters
|
||||
- **VRAM**: 6GB minimum
|
||||
- **Quality**: Good for the size, accessible to lower hardware
|
||||
- **Best for**: Entry-level image animation
|
||||
- **Command**: `python wgp.py --i2v-1-3B`
|
||||
|
||||
#### Wan Fun InP 14B
|
||||
- **Size**: 14 billion parameters
|
||||
- **VRAM**: 12GB+ recommended
|
||||
- **Quality**: Better end image support
|
||||
- **Limitation**: Existing loras don't work as well
|
||||
|
||||
<BR>
|
||||
|
||||
|
||||
## Hunyuan Video Models
|
||||
|
||||
#### Hunyuan Video Text2Video
|
||||
- **Quality**: Among the best open source t2v models
|
||||
- **VRAM**: 12GB+ recommended
|
||||
- **Speed**: Slower generation but excellent results
|
||||
- **Features**: Superior text adherence and video quality, up to 10s of video
|
||||
- **Best for**: High-quality text-to-video generation
|
||||
|
||||
#### Hunyuan Video Custom
|
||||
- **Specialty**: Identity preservation
|
||||
- **Use case**: Injecting specific people into videos
|
||||
- **Quality**: Excellent for character consistency
|
||||
- **Best for**: Character-focused video generation
|
||||
|
||||
#### Hunyuan Video Avater
|
||||
- **Specialty**: Generate up to 15s of high quality speech / song driven Video .
|
||||
- **Use case**: Injecting specific people into videos
|
||||
- **Quality**: Excellent for character consistency
|
||||
- **Best for**: Character-focused video generation, Video synchronized with voice
|
||||
|
||||
|
||||
<BR>
|
||||
|
||||
## LTX Video Models
|
||||
|
||||
#### LTX Video 13B
|
||||
- **Specialty**: Long video generation
|
||||
- **Resolution**: Fast 720p generation
|
||||
- **VRAM**: Optimized by WanGP (4x reduction in requirements)
|
||||
- **Best for**: Longer duration videos
|
||||
|
||||
#### LTX Video 13B Distilled
|
||||
- **Speed**: Generate in less than one minute
|
||||
- **Quality**: Very high quality despite speed
|
||||
- **Best for**: Rapid prototyping and quick results
|
||||
|
||||
<BR>
|
||||
|
||||
## Model Selection Guide
|
||||
|
||||
### By Hardware (VRAM)
|
||||
|
||||
#### 6-8GB VRAM
|
||||
- Wan 2.1 T2V 1.3B
|
||||
- Wan Fun InP 1.3B
|
||||
- Wan Vace 1.3B
|
||||
|
||||
#### 10-12GB VRAM
|
||||
- Wan 2.1 T2V 14B
|
||||
- Wan Fun InP 14B
|
||||
- Hunyuan Video (with optimizations)
|
||||
- LTX Video 13B
|
||||
|
||||
#### 16GB+ VRAM
|
||||
- All models supported
|
||||
- Longer videos possible
|
||||
- Higher resolutions
|
||||
- Multiple simultaneous Loras
|
||||
|
||||
#### 20GB+ VRAM
|
||||
- MoviiGen (experimental 1080p)
|
||||
- Very long videos
|
||||
- Maximum quality settings
|
||||
|
||||
### By Use Case
|
||||
|
||||
#### Quick Prototyping
|
||||
1. **LTX Video 13B Distilled** - Fastest, high quality
|
||||
2. **Wan 2.1 T2V 1.3B** - Fast, good quality
|
||||
3. **CausVid Lora** - 4-12 steps, very fast
|
||||
|
||||
#### Best Quality
|
||||
1. **Hunyuan Video** - Overall best t2v quality
|
||||
2. **Wan 2.1 T2V 14B** - Excellent Wan quality
|
||||
3. **Wan Vace 14B** - Best for controlled generation
|
||||
|
||||
#### Advanced Control
|
||||
1. **Wan Vace 14B/1.3B** - Motion transfer, object injection
|
||||
2. **Phantom** - Person/object transfer
|
||||
3. **FantasySpeaking** - Voice-driven animation
|
||||
|
||||
#### Long Videos
|
||||
1. **LTX Video 13B** - Specialized for length
|
||||
2. **Sky Reels v2** - Infinite length videos
|
||||
3. **Wan Vace + Sliding Windows** - Up to 1 minute
|
||||
|
||||
#### Lower Hardware
|
||||
1. **Wan Fun InP 1.3B** - Image-to-video
|
||||
2. **Wan 2.1 T2V 1.3B** - Text-to-video
|
||||
3. **Wan Vace 1.3B** - Advanced control
|
||||
|
||||
<BR>
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
### Speed (Relative)
|
||||
1. **CausVid Lora** (4-12 steps) - Fastest
|
||||
2. **LTX Video Distilled** - Very fast
|
||||
3. **Wan 1.3B models** - Fast
|
||||
4. **Wan 14B models** - Medium
|
||||
5. **Hunyuan Video** - Slower
|
||||
6. **MoviiGen** - Slowest
|
||||
|
||||
### Quality (Subjective)
|
||||
1. **Hunyuan Video** - Highest overall
|
||||
2. **Wan 14B models** - Excellent
|
||||
3. **LTX Video models** - Very good
|
||||
4. **Wan 1.3B models** - Good
|
||||
5. **CausVid** - Good (varies with steps)
|
||||
|
||||
### VRAM Efficiency
|
||||
1. **Wan 1.3B models** - Most efficient
|
||||
2. **LTX Video** (with WanGP optimizations)
|
||||
3. **Wan 14B models**
|
||||
4. **Hunyuan Video**
|
||||
5. **MoviiGen** - Least efficient
|
||||
|
||||
<BR>
|
||||
|
||||
## Model Switching
|
||||
|
||||
WanGP allows switching between models without restarting:
|
||||
|
||||
1. Use the dropdown menu in the web interface
|
||||
2. Models are loaded on-demand
|
||||
3. Previous model is unloaded to save VRAM
|
||||
4. Settings are preserved when possible
|
||||
|
||||
<BR>
|
||||
|
||||
## Tips for Model Selection
|
||||
|
||||
### First Time Users
|
||||
Start with **Wan 2.1 T2V 1.3B** to learn the interface and test your hardware.
|
||||
|
||||
### Production Work
|
||||
Use **Hunyuan Video** or **Wan 14B** models for final output quality.
|
||||
|
||||
### Experimentation
|
||||
**CausVid Lora** or **LTX Distilled** for rapid iteration and testing.
|
||||
|
||||
### Specialized Tasks
|
||||
- **VACE** for advanced control
|
||||
- **FantasySpeaking** for talking heads
|
||||
- **LTX Video** for long sequences
|
||||
|
||||
### Hardware Optimization
|
||||
Always start with the largest model your VRAM can handle, then optimize settings for speed vs quality based on your needs.
|
||||
@@ -1,338 +0,0 @@
|
||||
# Troubleshooting Guide
|
||||
|
||||
This guide covers common issues and their solutions when using WanGP.
|
||||
|
||||
## Installation Issues
|
||||
|
||||
### PyTorch Installation Problems
|
||||
|
||||
#### CUDA Version Mismatch
|
||||
**Problem**: PyTorch can't detect GPU or CUDA errors
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check your CUDA version
|
||||
nvidia-smi
|
||||
|
||||
# Install matching PyTorch version
|
||||
# For CUDA 12.4 (RTX 10XX-40XX)
|
||||
pip install torch==2.6.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu124
|
||||
|
||||
# For CUDA 12.8 (RTX 50XX)
|
||||
pip install torch==2.7.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/test/cu128
|
||||
```
|
||||
|
||||
#### Python Version Issues
|
||||
**Problem**: Package compatibility errors
|
||||
**Solution**: Ensure you're using Python 3.10.9
|
||||
```bash
|
||||
python --version # Should show 3.10.9
|
||||
conda create -n wan2gp python=3.10.9
|
||||
```
|
||||
|
||||
### Dependency Installation Failures
|
||||
|
||||
#### Triton Installation (Windows)
|
||||
**Problem**: `pip install triton-windows` fails
|
||||
**Solution**:
|
||||
1. Update pip: `pip install --upgrade pip`
|
||||
2. Try pre-compiled wheel
|
||||
3. Fallback to SDPA attention: `python wgp.py --attention sdpa`
|
||||
|
||||
#### SageAttention Compilation Issues
|
||||
**Problem**: SageAttention installation fails
|
||||
**Solution**:
|
||||
1. Install Visual Studio Build Tools (Windows)
|
||||
2. Use pre-compiled wheels when available
|
||||
3. Fallback to basic attention modes
|
||||
|
||||
## Memory Issues
|
||||
|
||||
### CUDA Out of Memory
|
||||
|
||||
#### During Model Loading
|
||||
**Problem**: "CUDA out of memory" when loading model
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Use smaller model
|
||||
python wgp.py --t2v-1-3B
|
||||
|
||||
# Enable quantization (usually default)
|
||||
python wgp.py --quantize-transformer True
|
||||
|
||||
# Use memory-efficient profile
|
||||
python wgp.py --profile 4
|
||||
|
||||
# Reduce preloaded model size
|
||||
python wgp.py --preload 0
|
||||
```
|
||||
|
||||
#### During Video Generation
|
||||
**Problem**: Memory error during generation
|
||||
**Solutions**:
|
||||
1. Reduce frame count (shorter videos)
|
||||
2. Lower resolution in advanced settings
|
||||
3. Use lower batch size
|
||||
4. Clear GPU cache between generations
|
||||
|
||||
### System RAM Issues
|
||||
|
||||
#### High RAM Usage
|
||||
**Problem**: System runs out of RAM
|
||||
**Solutions**:
|
||||
```bash
|
||||
# Limit reserved memory
|
||||
python wgp.py --perc-reserved-mem-max 0.3
|
||||
|
||||
# Use minimal RAM profile
|
||||
python wgp.py --profile 5
|
||||
|
||||
# Enable swap file (OS level)
|
||||
```
|
||||
|
||||
## Performance Issues
|
||||
|
||||
### Slow Generation Speed
|
||||
|
||||
#### General Optimization
|
||||
```bash
|
||||
# Enable compilation (requires Triton)
|
||||
python wgp.py --compile
|
||||
|
||||
# Use faster attention
|
||||
python wgp.py --attention sage2
|
||||
|
||||
# Enable TeaCache
|
||||
python wgp.py --teacache 2.0
|
||||
|
||||
# Use high-performance profile
|
||||
python wgp.py --profile 3
|
||||
```
|
||||
|
||||
#### GPU-Specific Optimizations
|
||||
|
||||
**RTX 10XX/20XX Series**:
|
||||
```bash
|
||||
python wgp.py --attention sdpa --profile 4 --teacache 1.5
|
||||
```
|
||||
|
||||
**RTX 30XX/40XX Series**:
|
||||
```bash
|
||||
python wgp.py --compile --attention sage --profile 3 --teacache 2.0
|
||||
```
|
||||
|
||||
**RTX 50XX Series**:
|
||||
```bash
|
||||
python wgp.py --attention sage --profile 4 --fp16
|
||||
```
|
||||
|
||||
### Attention Mechanism Issues
|
||||
|
||||
#### Sage Attention Not Working
|
||||
**Problem**: Sage attention fails to compile or work
|
||||
**Diagnostic Steps**:
|
||||
1. Check Triton installation:
|
||||
```python
|
||||
import triton
|
||||
print(triton.__version__)
|
||||
```
|
||||
2. Clear Triton cache:
|
||||
```bash
|
||||
# Windows
|
||||
rmdir /s %USERPROFILE%\.triton
|
||||
# Linux
|
||||
rm -rf ~/.triton
|
||||
```
|
||||
3. Fallback solution:
|
||||
```bash
|
||||
python wgp.py --attention sdpa
|
||||
```
|
||||
|
||||
#### Flash Attention Issues
|
||||
**Problem**: Flash attention compilation fails
|
||||
**Solution**:
|
||||
- Windows: Often requires manual CUDA kernel compilation
|
||||
- Linux: Usually works with `pip install flash-attn`
|
||||
- Fallback: Use Sage or SDPA attention
|
||||
|
||||
## Model-Specific Issues
|
||||
|
||||
### Lora Problems
|
||||
|
||||
#### Loras Not Loading
|
||||
**Problem**: Loras don't appear in the interface
|
||||
**Solutions**:
|
||||
1. Check file format (should be .safetensors, .pt, or .pth)
|
||||
2. Verify correct directory:
|
||||
```
|
||||
loras/ # For t2v models
|
||||
loras_i2v/ # For i2v models
|
||||
loras_hunyuan/ # For Hunyuan models
|
||||
```
|
||||
3. Click "Refresh" button in interface
|
||||
4. Use `--check-loras` to filter incompatible files
|
||||
|
||||
#### Lora Compatibility Issues
|
||||
**Problem**: Lora causes errors or poor results
|
||||
**Solutions**:
|
||||
1. Check model size compatibility (1.3B vs 14B)
|
||||
2. Verify lora was trained for your model type
|
||||
3. Try different multiplier values
|
||||
4. Use `--check-loras` flag to auto-filter
|
||||
|
||||
### VACE-Specific Issues
|
||||
|
||||
#### Poor VACE Results
|
||||
**Problem**: VACE generates poor quality or unexpected results
|
||||
**Solutions**:
|
||||
1. Enable Skip Layer Guidance
|
||||
2. Use detailed prompts describing all elements
|
||||
3. Ensure proper mask creation with Matanyone
|
||||
4. Check reference image quality
|
||||
5. Use at least 15 steps, preferably 30+
|
||||
|
||||
#### Matanyone Tool Issues
|
||||
**Problem**: Mask creation difficulties
|
||||
**Solutions**:
|
||||
1. Use negative point prompts to refine selection
|
||||
2. Create multiple sub-masks and combine them
|
||||
3. Try different background removal options
|
||||
4. Ensure sufficient contrast in source video
|
||||
|
||||
## Network and Server Issues
|
||||
|
||||
### Gradio Interface Problems
|
||||
|
||||
#### Port Already in Use
|
||||
**Problem**: "Port 7860 is already in use"
|
||||
**Solution**:
|
||||
```bash
|
||||
# Use different port
|
||||
python wgp.py --server-port 7861
|
||||
|
||||
# Or kill existing process
|
||||
# Windows
|
||||
netstat -ano | findstr :7860
|
||||
taskkill /PID <PID> /F
|
||||
|
||||
# Linux
|
||||
lsof -i :7860
|
||||
kill <PID>
|
||||
```
|
||||
|
||||
#### Interface Not Loading
|
||||
**Problem**: Browser shows "connection refused"
|
||||
**Solutions**:
|
||||
1. Check if server started successfully
|
||||
2. Try `http://127.0.0.1:7860` instead of `localhost:7860`
|
||||
3. Disable firewall temporarily
|
||||
4. Use `--listen` flag for network access
|
||||
|
||||
### Remote Access Issues
|
||||
|
||||
#### Sharing Not Working
|
||||
**Problem**: `--share` flag doesn't create public URL
|
||||
**Solutions**:
|
||||
1. Check internet connection
|
||||
2. Try different network
|
||||
3. Use `--listen` with port forwarding
|
||||
4. Check firewall settings
|
||||
|
||||
## Quality Issues
|
||||
|
||||
### Poor Video Quality
|
||||
|
||||
#### General Quality Improvements
|
||||
1. Increase number of steps (25-30+)
|
||||
2. Use larger models (14B instead of 1.3B)
|
||||
3. Enable Skip Layer Guidance
|
||||
4. Improve prompt descriptions
|
||||
5. Use higher resolution settings
|
||||
|
||||
#### Specific Quality Issues
|
||||
|
||||
**Blurry Videos**:
|
||||
- Increase steps
|
||||
- Check source image quality (i2v)
|
||||
- Reduce TeaCache multiplier
|
||||
- Use higher guidance scale
|
||||
|
||||
**Inconsistent Motion**:
|
||||
- Use longer overlap in sliding windows
|
||||
- Reduce window size
|
||||
- Improve prompt consistency
|
||||
- Check control video quality (VACE)
|
||||
|
||||
**Color Issues**:
|
||||
- Check model compatibility
|
||||
- Adjust guidance scale
|
||||
- Verify input image color space
|
||||
- Try different VAE settings
|
||||
|
||||
## Advanced Debugging
|
||||
|
||||
### Enable Verbose Output
|
||||
```bash
|
||||
# Maximum verbosity
|
||||
python wgp.py --verbose 2
|
||||
|
||||
# Check lora compatibility
|
||||
python wgp.py --check-loras --verbose 2
|
||||
```
|
||||
|
||||
### Memory Debugging
|
||||
```bash
|
||||
# Monitor GPU memory
|
||||
nvidia-smi -l 1
|
||||
|
||||
# Reduce memory usage
|
||||
python wgp.py --profile 4 --perc-reserved-mem-max 0.2
|
||||
```
|
||||
|
||||
### Performance Profiling
|
||||
```bash
|
||||
# Test different configurations
|
||||
python wgp.py --attention sdpa --profile 4 # Baseline
|
||||
python wgp.py --attention sage --profile 3 # Performance
|
||||
python wgp.py --compile --teacache 2.0 # Maximum speed
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Before Asking for Help
|
||||
1. Check this troubleshooting guide
|
||||
2. Read the relevant documentation:
|
||||
- [Installation Guide](INSTALLATION.md)
|
||||
- [Getting Started](GETTING_STARTED.md)
|
||||
- [Command Line Reference](CLI.md)
|
||||
3. Try basic fallback configuration:
|
||||
```bash
|
||||
python wgp.py --attention sdpa --profile 4
|
||||
```
|
||||
|
||||
### Community Support
|
||||
- **Discord Server**: https://discord.gg/g7efUW9jGV
|
||||
- Provide relevant information:
|
||||
- GPU model and VRAM amount
|
||||
- Python and PyTorch versions
|
||||
- Complete error messages
|
||||
- Command used to launch WanGP
|
||||
- Operating system
|
||||
|
||||
### Reporting Bugs
|
||||
When reporting issues:
|
||||
1. Include system specifications
|
||||
2. Provide complete error logs
|
||||
3. List the exact steps to reproduce
|
||||
4. Mention any modifications to default settings
|
||||
5. Include command line arguments used
|
||||
|
||||
## Emergency Fallback
|
||||
|
||||
If nothing works, try this minimal configuration:
|
||||
```bash
|
||||
# Absolute minimum setup
|
||||
python wgp.py --t2v-1-3B --attention sdpa --profile 4 --teacache 0 --fp16
|
||||
|
||||
# If that fails, check basic PyTorch installation
|
||||
python -c "import torch; print(torch.cuda.is_available())"
|
||||
```
|
||||
214
docs/VACE.md
214
docs/VACE.md
@@ -1,214 +0,0 @@
|
||||
# VACE ControlNet Guide
|
||||
|
||||
VACE is a powerful ControlNet that enables Video-to-Video and Reference-to-Video generation. It allows you to inject your own images into output videos, animate characters, perform inpainting/outpainting, and continue existing videos.
|
||||
|
||||
## Overview
|
||||
|
||||
VACE is probably one of the most powerful Wan models available. With it, you can:
|
||||
- Inject people or objects into scenes
|
||||
- Animate characters
|
||||
- Perform video inpainting and outpainting
|
||||
- Continue existing videos
|
||||
- Transfer motion from one video to another
|
||||
- Change the style of scenes while preserving the structure of the scenes
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Model Selection
|
||||
1. Select either "Vace 1.3B" or "Vace 13B" from the dropdown menu
|
||||
2. Note: VACE works best with videos up to 7 seconds with the Riflex option enabled
|
||||
|
||||
You can also use any derived Vace models such as Vace Fusionix or combine Vace with Loras accelerator such as Causvid.
|
||||
|
||||
### Input Types
|
||||
|
||||
#### 1. Control Video
|
||||
The Control Video is the source material that contains the instructions about what you want. So Vace expects in the Control Video some visual hints about the type of processing expected: for instance replacing an area by something else, converting an Open Pose wireframe into a human motion, colorizing an Area, transferring the depth of an image area, ...
|
||||
|
||||
For example, anywhere your control video contains the color 127 (grey), it will be considered as an area to be inpainting and replaced by the content of your text prompt and / or a reference image (see below). Likewise if the frames of a Control Video contains an Open Pose wireframe (basically some straight lines tied together that describes the pose of a person), Vace will automatically turn this Open Pose into a real human based on the text prompt and any reference Images (see below).
|
||||
|
||||
You can either build yourself the Control Video with the annotators tools provided by the Vace team (see the Vace ressources at the bottom) or you can let WanGP (recommended option) generates on the fly a Vace formatted Control Video based on information you provide.
|
||||
|
||||
WanGP wil need the following information to generate a Vace Control Video:
|
||||
- A *Control Video* : this video shouldn't have been altered by an annotator tool and can be taken straight from youtube or your camera
|
||||
- *Control Video Process* : This is the type of process you want to apply on the control video. For instance *Transfer Human Motion* will generate the Open Pose information from your video so that you can transfer this same motion to a generated character. If you want to do only *Spatial Outpainting* or *Temporal Inpainting / Outpainting* you may want to choose the *Keep Unchanged* process.
|
||||
- *Area Processed* : you can target the processing to a specific area. For instance even if there are multiple people in the Control Video you may want to replace only one them. If you decide to target an area you will need to provide a *Video Mask* as well. These types of videos can be easily created using the Matanyone tool embedded with WanGP (see the doc of Matanyone below). WanGP can apply different types of process, one the mask and another one on the outside the mask.
|
||||
|
||||
Another nice thing is that you can combine all effects above with Outpainting since WanGP will create automatically an outpainting area in the Control Video if you ask for this.
|
||||
|
||||
By default WanGP will ask Vace to generate new frames in the "same spirit" of the control video if the latter is shorter than the number frames that you have requested.
|
||||
|
||||
Be aware that the Control Video and Video Mask will be before anything happens resampled to the number of frames per second of Vace (usually 16) and resized to the output size you have requested.
|
||||
#### 2. Reference Images
|
||||
With Reference Images you can inject people or objects of your choice in the Video.
|
||||
You can also force Images to appear at a specific frame nos in the Video.
|
||||
|
||||
If the Reference Image is a person or an object, it is recommended to turn on the background remover that will replace the background by the white color.
|
||||
This is not needed for a background image or an injected frame at a specific position.
|
||||
|
||||
It is recommended to describe injected objects/people explicitly in your text prompt so that Vace can connect the Reference Images to the new generated video and this will increase the chance that you will find your injected people or objects.
|
||||
|
||||
|
||||
### Understanding Vace Control Video and Mask format
|
||||
As stated above WanGP will adapt the Control Video and the Video Mask to meet your instructions. You can preview the first frames of the new Control Video and of the Video Mask in the Generation Preview box (just click a thumbnail) to check that your request has been properly interpreted. You can as well ask WanGP to save in the main folder of WanGP the full generated Control Video and Video Mask by launching the app with the *--save-masks* command.
|
||||
|
||||
Look at the background colors of both the Control Video and the Video Mask:
|
||||
The Mask Video is the most important because depending on the color of its pixels, the Control Video will be interpreted differently. If an area in the Mask is black, the corresponding Control Video area will be kept as is. On the contrary if an area of the Mask is plain white, a Vace process will be applied on this area. If there isn't any Mask Video the Vace process will apply on the whole video frames. The nature of the process itself will depend on what there is in the Control Video for this area.
|
||||
- if the area is grey (127) in the Control Video, this area will be replaced by new content based on the text prompt or image references
|
||||
- if an area represents a person in the wireframe Open Pose format, it will be replaced by a person animated with motion described by the Open Pose.The appearance of the person will depend on the text prompt or image references
|
||||
- if an area contains multiples shades of grey, these will be assumed to represent different levels of image depth and Vace will try to generate new content located at the same depth
|
||||
|
||||
There are more Vace representations. For all the different mapping please refer the official Vace documentation.
|
||||
|
||||
### Other Processing
|
||||
Most of the processing below and the ones related to Control Video can be combined together.
|
||||
- **Temporal Outpainting**\
|
||||
Temporal Outpainting requires an existing *Source Video* or *Control Video* and it amounts to adding missing frames. It is implicit if you use a Source Video that you want to continue (new frames will be added at the end of this Video) or if you provide a Control Video that contains fewer frames than the number that you have requested to generate.
|
||||
|
||||
- **Temporal Inpainting**\
|
||||
With temporal inpainting you are asking Vace to generate missing frames that should exist between existing frames. There are two ways to do that:
|
||||
- *Injected Reference Images* : Each Image is injected a position of your choice and Vace will fill the gaps between these frames
|
||||
- *Frames to keep in Control Video* : If using a Control Video, you can ask WanGP to hide some of these frames to let Vace generate "alternate frames" for these parts of the Control Video.
|
||||
|
||||
- **Spatial Outpainting**\
|
||||
This feature creates new content to the top, bottom, left or right of existing frames of a Control Video. You can set the amount of content for each direction by specifying a percentage of extra content in relation to the existing frame. Please note that the resulting video will target the resolution you specified. So if this Resolution corresponds to that of your Control Video you may lose details. Therefore it may be relevant to pick a higher resolution with Spatial Outpainting.\
|
||||
There are two ways to do Spatial Outpainting:
|
||||
- *Injected Reference Frames* : new content will be added around Injected Frames
|
||||
- *Control Video* : new content will be added on all the frames of the whole Control Video
|
||||
|
||||
|
||||
### Example 1 : Replace a Person in one video by another one by keeping the Background
|
||||
1) In Vace, select *Control Video Process*=**Transfer human pose**, *Area processed*=**Masked area**
|
||||
2) In *Matanyone Video Mask Creator*, load your source video and create a mask where you targetted a specific person
|
||||
3) Click *Export to Control Video Input and Video Mask Input* to transfer both the original video that now becomes the *Control Video* and the black & white mask that now defines the *Video Mask Area*
|
||||
4) Back in Vace, in *Reference Image* select **Inject Landscapes / People / Objects** and upload one or several pictures of the new person
|
||||
5) Generate
|
||||
|
||||
This works also with several people at the same time (you just need to mask several people in *Matanyone*), you can also play with the slider *Expand / Shrink Mask* if the new person is larger than the original one and of course, you can also use the text *Prompt* if you dont want to use an image for the swap.
|
||||
|
||||
|
||||
### Example 2 : Change the Background behind some characters
|
||||
1) In Vace, select *Control Video Process*=**Inpainting**, *Area processed*=**Non Masked area**
|
||||
2) In *Matanyone Video Mask Creator*, load your source video and create a mask where you targetted the people you want to keep
|
||||
3) Click *Export to Control Video Input and Video Mask Input* to transfer both the original video that now becomes the *Control Video* and the black & white mask that now defines the *Video Mask Area*
|
||||
4) Generate
|
||||
|
||||
If instead *Control Video Process*=**Depth**, then the background although it will be still different it will have a similar geometry than in the control video
|
||||
|
||||
### Example 3 : Outpaint a Video to the Left and Inject a Character in this new area
|
||||
1) In Vace, select *Control Video Process*=**Keep Unchanged**
|
||||
2) *Control Video Outpainting in Percentage* enter the value 40 to the *Left* entry
|
||||
3) In *Reference Image* select **Inject Landscapes / People / Objects** and upload one or several pictures of a person
|
||||
4) Enter the *Prompt* such as "a person is coming from the left" (you will need of course a more accurate description)
|
||||
5) Generate
|
||||
|
||||
|
||||
|
||||
### Creating Face / Object Replacement Masks
|
||||
Matanyone is a tool that will generate the Video Mask that needs to be combined with the Control Video. It is very useful as you just need to indicate in the first frame the area you want to mask and it will compute masked areas for the following frames by taking into account the motion.
|
||||
1. Load your video in Matanyone
|
||||
2. Click on the face or object in the first frame
|
||||
3. Validate the mask by clicking **Set Mask**
|
||||
4. Generate a copy of the control video (for easy transfers) and a new mask video by clicking "Generate Video Matting"
|
||||
5. Export to VACE with *Export to Control Video Input and Video Mask Input*
|
||||
|
||||
### Advanced Matanyone Tips
|
||||
- **Negative Point Prompts**: Remove parts from current selection if the mask goes beyond the desired area
|
||||
- **Sub Masks**: Create multiple independent masks, then combine them. This may be useful if you are struggling to select exactly what you want.
|
||||
|
||||
|
||||
|
||||
## Window Sliding for Long Videos
|
||||
Generate videos up to 1 minute by merging multiple windows:
|
||||
The longer the video the greater the quality degradation. However the effect will be less visible if your generated video reuses mostly non altered control video.
|
||||
|
||||
When this feature is enabled it is important to keep in mind that every positional argument of Vace (frames positions of *Injected Reference Frames*, *Frames to keep in Control Video*) are related to the first frame of the first Window. This is convenient as changing the size of a sliding window won't have any impact and this allows you define in advance the inject frames for all the windows.
|
||||
|
||||
Likewise, if you use *Continue Video File* by providing a *Source Video*, this Source Video will be considered as the first window and the positional arguments will be calculated in relation to the first frame of this Source Video. Also the *overlap window size* parameter will correspond to the number of frames used of the Source Video that is temporally outpainted to produce new content.
|
||||
|
||||
### How It Works
|
||||
- Each window uses the corresponding time segment of the Control Video
|
||||
- Example: 0-4s control video → first window, 4-8s → second window, etc.
|
||||
- Automatic overlap management ensures smooth transitions
|
||||
|
||||
|
||||
### Formula
|
||||
This formula gives the number of Generated Frames for a specific number of Sliding Windows :
|
||||
```
|
||||
Generated Frames = [Nb Windows - 1] × [Window Size - Overlap - Discard] + Window Size
|
||||
```
|
||||
|
||||
### Multi-Line Prompts (Experimental)
|
||||
If you enable *Text Prompts separated by a Carriage Return will be used for a new Sliding Window*, you can define in advance a different prompt for each window.:
|
||||
- Each prompt is separated by a Carriage Return
|
||||
- Each line of prompt will be used for a different window
|
||||
- If more windows than prompt lines, last line repeats
|
||||
|
||||
## Recommended Settings
|
||||
|
||||
### Quality Settings
|
||||
- **Skip Layer Guidance**: Turn ON with default configuration for better results (useless with FusioniX of Causvid are there is no cfg)
|
||||
- **Long Prompts**: Use detailed descriptions, especially for background elements not in reference images
|
||||
- **Steps**: Use at least 15 steps for good quality, 30+ for best results if you use the original Vace model. But only 8-10 steps are sufficient with Vace Funsionix or if you use Loras such as Causvid or Self-Forcing.
|
||||
|
||||
### Sliding Window Settings
|
||||
For very long videos, configure sliding windows properly:
|
||||
|
||||
- **Window Size**: Set appropriate duration for your content
|
||||
- **Overlap Frames**: Long enough for motion continuity, short enough to avoid blur propagation
|
||||
- **Discard Last Frames**: Remove at least 4 frames from each window (VACE 1.3B tends to blur final frames)
|
||||
- **Add Overlapped Noise**: May or may not reduce quality degradation over time
|
||||
|
||||
### Background Removal
|
||||
WanGP includes automatic background removal options:
|
||||
- Use for reference images containing people/objects
|
||||
- **Don't use** this for landscape/setting reference images (the first reference image)
|
||||
- If you are not happy with the automatic background removal tool you can use the Image version of Matanyone for a precise background removal
|
||||
|
||||
## External Resources
|
||||
|
||||
### Official VACE Resources
|
||||
- **GitHub**: https://github.com/ali-vilab/VACE/tree/main/vace/gradios
|
||||
- **User Guide**: https://github.com/ali-vilab/VACE/blob/main/UserGuide.md
|
||||
- **Preprocessors**: Gradio tools for preparing materials
|
||||
|
||||
### Recommended External Tools
|
||||
- **Annotation Tools**: For creating precise masks
|
||||
- **Video Editors**: For preparing control videos
|
||||
- **Background Removal**: For cleaning reference images
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Poor Quality Results
|
||||
1. Use longer, more detailed prompts
|
||||
2. Enable Skip Layer Guidance
|
||||
3. Increase number of steps (30+)
|
||||
4. Check reference image quality
|
||||
5. Ensure proper mask creation
|
||||
|
||||
### Inconsistent Windows
|
||||
1. Increase overlap frames
|
||||
2. Use consistent prompting across windows
|
||||
3. Add noise to overlapped frames
|
||||
4. Reduce discard frames if losing too much content
|
||||
|
||||
### Memory Issues
|
||||
1. Use VACE 1.3B instead of 13B
|
||||
2. Reduce video length or resolution
|
||||
3. Decrease window size
|
||||
4. Enable quantization
|
||||
|
||||
### Blurry Results
|
||||
1. Reduce overlap frames
|
||||
2. Increase discard last frames
|
||||
3. Use higher resolution reference images
|
||||
4. Check control video quality
|
||||
|
||||
## Tips for Best Results
|
||||
1. **Detailed Prompts**: Describe everything in the scene, especially elements not in reference images
|
||||
2. **Quality Reference Images**: Use high-resolution, well-lit reference images
|
||||
3. **Proper Masking**: Take time to create precise masks with Matanyone
|
||||
4. **Iterative Approach**: Start with short videos, then extend successful results
|
||||
5. **Background Preparation**: Remove complex backgrounds from object/person reference images
|
||||
6. **Consistent Lighting**: Match lighting between reference images and intended scene
|
||||
BIN
examples/i2v_input.JPG
Normal file
BIN
examples/i2v_input.JPG
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
BIN
favicon.png
BIN
favicon.png
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
@@ -1,13 +0,0 @@
|
||||
try:
|
||||
from ._version import (
|
||||
version as __version__, # type: ignore
|
||||
version_tuple,
|
||||
)
|
||||
except ImportError:
|
||||
__version__ = "unknown (no version information available)"
|
||||
version_tuple = (0, 0, "unknown", "noinfo")
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
PACKAGE = __package__.replace("_", "-")
|
||||
PACKAGE_ROOT = Path(__file__).parent
|
||||
@@ -1,18 +0,0 @@
|
||||
from fire import Fire
|
||||
|
||||
from .cli import main as cli_main
|
||||
from .cli_control import main as control_main
|
||||
from .cli_fill import main as fill_main
|
||||
from .cli_kontext import main as kontext_main
|
||||
from .cli_redux import main as redux_main
|
||||
|
||||
if __name__ == "__main__":
|
||||
Fire(
|
||||
{
|
||||
"t2i": cli_main,
|
||||
"control": control_main,
|
||||
"fill": fill_main,
|
||||
"kontext": kontext_main,
|
||||
"redux": redux_main,
|
||||
}
|
||||
)
|
||||
@@ -1,21 +0,0 @@
|
||||
# file generated by setuptools-scm
|
||||
# don't change, don't track in version control
|
||||
|
||||
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
||||
|
||||
TYPE_CHECKING = False
|
||||
if TYPE_CHECKING:
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
||||
else:
|
||||
VERSION_TUPLE = object
|
||||
|
||||
version: str
|
||||
__version__: str
|
||||
__version_tuple__: VERSION_TUPLE
|
||||
version_tuple: VERSION_TUPLE
|
||||
|
||||
__version__ = version = '0.0.post58+g1371b2b'
|
||||
__version_tuple__ = version_tuple = (0, 0, 'post58', 'g1371b2b')
|
||||
@@ -1,152 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from glob import iglob
|
||||
from mmgp import offload as offload
|
||||
import torch
|
||||
from wan.utils.utils import calculate_new_dimensions
|
||||
from flux.sampling import denoise, get_schedule, prepare_kontext, unpack
|
||||
from flux.modules.layers import get_linear_split_map
|
||||
from flux.util import (
|
||||
aspect_ratio_to_height_width,
|
||||
load_ae,
|
||||
load_clip,
|
||||
load_flow_model,
|
||||
load_t5,
|
||||
save_image,
|
||||
)
|
||||
|
||||
from PIL import Image
|
||||
|
||||
def stitch_images(img1, img2):
|
||||
# Resize img2 to match img1's height
|
||||
width1, height1 = img1.size
|
||||
width2, height2 = img2.size
|
||||
new_width2 = int(width2 * height1 / height2)
|
||||
img2_resized = img2.resize((new_width2, height1), Image.Resampling.LANCZOS)
|
||||
|
||||
stitched = Image.new('RGB', (width1 + new_width2, height1))
|
||||
stitched.paste(img1, (0, 0))
|
||||
stitched.paste(img2_resized, (width1, 0))
|
||||
return stitched
|
||||
|
||||
class model_factory:
|
||||
def __init__(
|
||||
self,
|
||||
checkpoint_dir,
|
||||
model_filename = None,
|
||||
model_type = None,
|
||||
model_def = None,
|
||||
base_model_type = None,
|
||||
text_encoder_filename = None,
|
||||
quantizeTransformer = False,
|
||||
save_quantized = False,
|
||||
dtype = torch.bfloat16,
|
||||
VAE_dtype = torch.float32,
|
||||
mixed_precision_transformer = False
|
||||
):
|
||||
self.device = torch.device(f"cuda")
|
||||
self.VAE_dtype = VAE_dtype
|
||||
self.dtype = dtype
|
||||
torch_device = "cpu"
|
||||
# model_filename = ["c:/temp/flux1-schnell.safetensors"]
|
||||
|
||||
self.t5 = load_t5(torch_device, text_encoder_filename, max_length=512)
|
||||
self.clip = load_clip(torch_device)
|
||||
self.name = model_def.get("flux-model", "flux-dev")
|
||||
# self.name= "flux-dev-kontext"
|
||||
# self.name= "flux-dev"
|
||||
# self.name= "flux-schnell"
|
||||
self.model = load_flow_model(self.name, model_filename[0], torch_device)
|
||||
|
||||
self.vae = load_ae(self.name, device=torch_device)
|
||||
|
||||
# offload.change_dtype(self.model, dtype, True)
|
||||
# offload.save_model(self.model, "flux-dev.safetensors")
|
||||
if save_quantized:
|
||||
from wgp import save_quantized_model
|
||||
save_quantized_model(self.model, model_type, model_filename[0], dtype, None)
|
||||
|
||||
split_linear_modules_map = get_linear_split_map()
|
||||
self.model.split_linear_modules_map = split_linear_modules_map
|
||||
offload.split_linear_modules(self.model, split_linear_modules_map )
|
||||
|
||||
|
||||
def generate(
|
||||
self,
|
||||
seed: int | None = None,
|
||||
input_prompt: str = "replace the logo with the text 'Black Forest Labs'",
|
||||
sampling_steps: int = 20,
|
||||
input_ref_images = None,
|
||||
width= 832,
|
||||
height=480,
|
||||
embedded_guidance_scale: float = 2.5,
|
||||
fit_into_canvas = None,
|
||||
callback = None,
|
||||
loras_slists = None,
|
||||
batch_size = 1,
|
||||
video_prompt_type = "",
|
||||
**bbargs
|
||||
):
|
||||
|
||||
if self._interrupt:
|
||||
return None
|
||||
|
||||
device="cuda"
|
||||
if "I" in video_prompt_type and input_ref_images != None and len(input_ref_images) > 0:
|
||||
if "K" in video_prompt_type and False :
|
||||
# image latents tiling method
|
||||
w, h = input_ref_images[0].size
|
||||
height, width = calculate_new_dimensions(height, width, h, w, fit_into_canvas)
|
||||
else:
|
||||
# image stiching method
|
||||
stiched = input_ref_images[0]
|
||||
if "K" in video_prompt_type :
|
||||
w, h = input_ref_images[0].size
|
||||
height, width = calculate_new_dimensions(height, width, h, w, fit_into_canvas)
|
||||
|
||||
for new_img in input_ref_images[1:]:
|
||||
stiched = stitch_images(stiched, new_img)
|
||||
input_ref_images = [stiched]
|
||||
else:
|
||||
input_ref_images = None
|
||||
|
||||
inp, height, width = prepare_kontext(
|
||||
t5=self.t5,
|
||||
clip=self.clip,
|
||||
prompt=input_prompt,
|
||||
ae=self.vae,
|
||||
img_cond_list=input_ref_images,
|
||||
target_width=width,
|
||||
target_height=height,
|
||||
bs=batch_size,
|
||||
seed=seed,
|
||||
device=device,
|
||||
)
|
||||
|
||||
timesteps = get_schedule(sampling_steps, inp["img"].shape[1], shift=(self.name != "flux-schnell"))
|
||||
def unpack_latent(x):
|
||||
return unpack(x.float(), height, width)
|
||||
# denoise initial noise
|
||||
x = denoise(self.model, **inp, timesteps=timesteps, guidance=embedded_guidance_scale, callback=callback, pipeline=self, loras_slists= loras_slists, unpack_latent = unpack_latent)
|
||||
if x==None: return None
|
||||
# decode latents to pixel space
|
||||
x = unpack_latent(x)
|
||||
with torch.autocast(device_type=device, dtype=torch.bfloat16):
|
||||
x = self.vae.decode(x)
|
||||
|
||||
x = x.clamp(-1, 1)
|
||||
x = x.transpose(0, 1)
|
||||
return x
|
||||
|
||||
def query_model_def(model_type, model_def):
|
||||
flux_model = model_def.get("flux-model", "flux-dev")
|
||||
flux_schnell = flux_model == "flux-schnell"
|
||||
model_def_output = {
|
||||
"image_outputs" : True,
|
||||
}
|
||||
if flux_schnell:
|
||||
model_def_output["no_guidance"] = True
|
||||
|
||||
return model_def_output
|
||||
54
flux/math.py
54
flux/math.py
@@ -1,54 +0,0 @@
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from torch import Tensor
|
||||
from wan.modules.attention import pay_attention
|
||||
|
||||
|
||||
def attention(qkv_list, pe: Tensor) -> Tensor:
|
||||
q, k, v = qkv_list
|
||||
qkv_list.clear()
|
||||
q_list = [q]
|
||||
q = None
|
||||
q = apply_rope_(q_list, pe)
|
||||
k_list = [k]
|
||||
k = None
|
||||
k = apply_rope_(k_list, pe)
|
||||
qkv_list = [q.transpose(1,2), k.transpose(1,2) ,v.transpose(1,2)]
|
||||
del q,k, v
|
||||
x = pay_attention(qkv_list).transpose(1,2)
|
||||
# x = torch.nn.functional.scaled_dot_product_attention(q, k, v)
|
||||
x = rearrange(x, "B H L D -> B L (H D)")
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def rope(pos: Tensor, dim: int, theta: int) -> Tensor:
|
||||
assert dim % 2 == 0
|
||||
scale = torch.arange(0, dim, 2, dtype=pos.dtype, device=pos.device) / dim
|
||||
omega = 1.0 / (theta**scale)
|
||||
out = torch.einsum("...n,d->...nd", pos, omega)
|
||||
out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1)
|
||||
out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
|
||||
return out.float()
|
||||
|
||||
|
||||
def apply_rope_(q_list, freqs_cis: Tensor) -> tuple[Tensor, Tensor]:
|
||||
xq= q_list[0]
|
||||
xqshape = xq.shape
|
||||
xqdtype= xq.dtype
|
||||
q_list.clear()
|
||||
xq = xq.float().reshape(*xqshape[:-1], -1, 1, 2)
|
||||
xq_out = freqs_cis[..., 0] * xq[..., 0]
|
||||
xq = freqs_cis[..., 1] * xq[..., 1]
|
||||
|
||||
xq_out.add_(xq)
|
||||
# xq_out = freqs_cis[..., 0] * xq_[..., 0] + freqs_cis[..., 1] * xq_[..., 1]
|
||||
|
||||
return xq_out.reshape(*xqshape).to(xqdtype)
|
||||
|
||||
def apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor) -> tuple[Tensor, Tensor]:
|
||||
xq_ = xq.float().reshape(*xq.shape[:-1], -1, 1, 2)
|
||||
xk_ = xk.float().reshape(*xk.shape[:-1], -1, 1, 2)
|
||||
xq_out = freqs_cis[..., 0] * xq_[..., 0] + freqs_cis[..., 1] * xq_[..., 1]
|
||||
xk_out = freqs_cis[..., 0] * xk_[..., 0] + freqs_cis[..., 1] * xk_[..., 1]
|
||||
return xq_out.reshape(*xq.shape).type_as(xq), xk_out.reshape(*xk.shape).type_as(xk)
|
||||
220
flux/model.py
220
flux/model.py
@@ -1,220 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from torch import Tensor, nn
|
||||
|
||||
from flux.modules.layers import (
|
||||
DoubleStreamBlock,
|
||||
EmbedND,
|
||||
LastLayer,
|
||||
MLPEmbedder,
|
||||
SingleStreamBlock,
|
||||
timestep_embedding,
|
||||
)
|
||||
from flux.modules.lora import LinearLora, replace_linear_with_lora
|
||||
|
||||
|
||||
@dataclass
|
||||
class FluxParams:
|
||||
in_channels: int
|
||||
out_channels: int
|
||||
vec_in_dim: int
|
||||
context_in_dim: int
|
||||
hidden_size: int
|
||||
mlp_ratio: float
|
||||
num_heads: int
|
||||
depth: int
|
||||
depth_single_blocks: int
|
||||
axes_dim: list[int]
|
||||
theta: int
|
||||
qkv_bias: bool
|
||||
guidance_embed: bool
|
||||
|
||||
|
||||
class Flux(nn.Module):
|
||||
"""
|
||||
Transformer model for flow matching on sequences.
|
||||
"""
|
||||
|
||||
def __init__(self, params: FluxParams):
|
||||
super().__init__()
|
||||
|
||||
self.params = params
|
||||
self.in_channels = params.in_channels
|
||||
self.out_channels = params.out_channels
|
||||
if params.hidden_size % params.num_heads != 0:
|
||||
raise ValueError(
|
||||
f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
|
||||
)
|
||||
pe_dim = params.hidden_size // params.num_heads
|
||||
if sum(params.axes_dim) != pe_dim:
|
||||
raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
|
||||
self.hidden_size = params.hidden_size
|
||||
self.num_heads = params.num_heads
|
||||
self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
|
||||
self.img_in = nn.Linear(self.in_channels, self.hidden_size, bias=True)
|
||||
self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size)
|
||||
self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size)
|
||||
self.guidance_in = (
|
||||
MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size) if params.guidance_embed else nn.Identity()
|
||||
)
|
||||
self.txt_in = nn.Linear(params.context_in_dim, self.hidden_size)
|
||||
|
||||
self.double_blocks = nn.ModuleList(
|
||||
[
|
||||
DoubleStreamBlock(
|
||||
self.hidden_size,
|
||||
self.num_heads,
|
||||
mlp_ratio=params.mlp_ratio,
|
||||
qkv_bias=params.qkv_bias,
|
||||
)
|
||||
for _ in range(params.depth)
|
||||
]
|
||||
)
|
||||
|
||||
self.single_blocks = nn.ModuleList(
|
||||
[
|
||||
SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio)
|
||||
for _ in range(params.depth_single_blocks)
|
||||
]
|
||||
)
|
||||
|
||||
self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels)
|
||||
|
||||
def preprocess_loras(self, model_type, sd):
|
||||
new_sd = {}
|
||||
if len(sd) == 0: return sd
|
||||
|
||||
def swap_scale_shift(weight):
|
||||
shift, scale = weight.chunk(2, dim=0)
|
||||
new_weight = torch.cat([scale, shift], dim=0)
|
||||
return new_weight
|
||||
|
||||
first_key= next(iter(sd))
|
||||
if first_key.startswith("lora_unet_"):
|
||||
new_sd = {}
|
||||
print("Converting Lora Safetensors format to Lora Diffusers format")
|
||||
repl_list = ["linear1", "linear2", "modulation", "img_attn", "txt_attn", "img_mlp", "txt_mlp", "img_mod", "txt_mod"]
|
||||
src_list = ["_" + k + "." for k in repl_list]
|
||||
src_list2 = ["_" + k + "_" for k in repl_list]
|
||||
tgt_list = ["." + k + "." for k in repl_list]
|
||||
|
||||
for k,v in sd.items():
|
||||
k = k.replace("lora_unet_blocks_","diffusion_model.blocks.")
|
||||
k = k.replace("lora_unet__blocks_","diffusion_model.blocks.")
|
||||
k = k.replace("lora_unet_single_blocks_","diffusion_model.single_blocks.")
|
||||
k = k.replace("lora_unet_double_blocks_","diffusion_model.double_blocks.")
|
||||
|
||||
for s,s2, t in zip(src_list, src_list2, tgt_list):
|
||||
k = k.replace(s,t)
|
||||
k = k.replace(s2,t)
|
||||
|
||||
k = k.replace("lora_up","lora_B")
|
||||
k = k.replace("lora_down","lora_A")
|
||||
|
||||
new_sd[k] = v
|
||||
|
||||
elif first_key.startswith("transformer."):
|
||||
root_src = ["time_text_embed.timestep_embedder.linear_1", "time_text_embed.timestep_embedder.linear_2", "time_text_embed.text_embedder.linear_1", "time_text_embed.text_embedder.linear_2",
|
||||
"time_text_embed.guidance_embedder.linear_1", "time_text_embed.guidance_embedder.linear_2",
|
||||
"x_embedder", "context_embedder", "proj_out" ]
|
||||
|
||||
root_tgt = ["time_in.in_layer", "time_in.out_layer", "vector_in.in_layer", "vector_in.out_layer",
|
||||
"guidance_in.in_layer", "guidance_in.out_layer",
|
||||
"img_in", "txt_in", "final_layer.linear" ]
|
||||
|
||||
double_src = ["norm1.linear", "norm1_context.linear", "attn.norm_q", "attn.norm_k", "ff.net.0.proj", "ff.net.2", "ff_context.net.0.proj", "ff_context.net.2", "attn.to_out.0" ,"attn.to_add_out", "attn.to_out", ".attn.to_", ".attn.add_q_proj.", ".attn.add_k_proj.", ".attn.add_v_proj.", ]
|
||||
double_tgt = ["img_mod.lin", "txt_mod.lin", "img_attn.norm.query_norm", "img_attn.norm.key_norm", "img_mlp.0", "img_mlp.2", "txt_mlp.0", "txt_mlp.2", "img_attn.proj", "txt_attn.proj", "img_attn.proj", ".img_attn.", ".txt_attn.q.", ".txt_attn.k.", ".txt_attn.v."]
|
||||
|
||||
single_src = ["norm.linear", "attn.norm_q", "attn.norm_k", "proj_out",".attn.to_q.", ".attn.to_k.", ".attn.to_v.", ".proj_mlp."]
|
||||
single_tgt = ["modulation.lin","norm.query_norm", "norm.key_norm", "linear2", ".linear1_attn_q.", ".linear1_attn_k.", ".linear1_attn_v.", ".linear1_mlp."]
|
||||
|
||||
|
||||
for k,v in sd.items():
|
||||
if k.startswith("transformer.single_transformer_blocks"):
|
||||
k = k.replace("transformer.single_transformer_blocks", "diffusion_model.single_blocks")
|
||||
for src, tgt in zip(single_src, single_tgt):
|
||||
k = k.replace(src, tgt)
|
||||
elif k.startswith("transformer.transformer_blocks"):
|
||||
k = k.replace("transformer.transformer_blocks", "diffusion_model.double_blocks")
|
||||
for src, tgt in zip(double_src, double_tgt):
|
||||
k = k.replace(src, tgt)
|
||||
else:
|
||||
k = k.replace("transformer.", "diffusion_model.")
|
||||
for src, tgt in zip(root_src, root_tgt):
|
||||
k = k.replace(src, tgt)
|
||||
|
||||
if "norm_out.linear" in k:
|
||||
if "lora_B" in k:
|
||||
v = swap_scale_shift(v)
|
||||
k = k.replace("norm_out.linear", "final_layer.adaLN_modulation.1")
|
||||
new_sd[k] = v
|
||||
return new_sd
|
||||
|
||||
def forward(
|
||||
self,
|
||||
img: Tensor,
|
||||
img_ids: Tensor,
|
||||
txt: Tensor,
|
||||
txt_ids: Tensor,
|
||||
timesteps: Tensor,
|
||||
y: Tensor,
|
||||
guidance: Tensor | None = None,
|
||||
callback= None,
|
||||
pipeline =None,
|
||||
|
||||
) -> Tensor:
|
||||
if img.ndim != 3 or txt.ndim != 3:
|
||||
raise ValueError("Input img and txt tensors must have 3 dimensions.")
|
||||
|
||||
# running on sequences img
|
||||
img = self.img_in(img)
|
||||
vec = self.time_in(timestep_embedding(timesteps, 256))
|
||||
if self.params.guidance_embed:
|
||||
if guidance is None:
|
||||
raise ValueError("Didn't get guidance strength for guidance distilled model.")
|
||||
vec += self.guidance_in(timestep_embedding(guidance, 256))
|
||||
vec += self.vector_in(y)
|
||||
txt = self.txt_in(txt)
|
||||
|
||||
ids = torch.cat((txt_ids, img_ids), dim=1)
|
||||
pe = self.pe_embedder(ids)
|
||||
|
||||
for block in self.double_blocks:
|
||||
if callback != None:
|
||||
callback(-1, None, False, True)
|
||||
if pipeline._interrupt:
|
||||
return None
|
||||
img, txt = block(img=img, txt=txt, vec=vec, pe=pe)
|
||||
|
||||
img = torch.cat((txt, img), 1)
|
||||
for block in self.single_blocks:
|
||||
img = block(img, vec=vec, pe=pe)
|
||||
img = img[:, txt.shape[1] :, ...]
|
||||
|
||||
img = self.final_layer(img, vec) # (N, T, patch_size ** 2 * out_channels)
|
||||
return img
|
||||
|
||||
|
||||
class FluxLoraWrapper(Flux):
|
||||
def __init__(
|
||||
self,
|
||||
lora_rank: int = 128,
|
||||
lora_scale: float = 1.0,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.lora_rank = lora_rank
|
||||
|
||||
replace_linear_with_lora(
|
||||
self,
|
||||
max_rank=lora_rank,
|
||||
scale=lora_scale,
|
||||
)
|
||||
|
||||
def set_lora_scale(self, scale: float) -> None:
|
||||
for module in self.modules():
|
||||
if isinstance(module, LinearLora):
|
||||
module.set_scale(scale=scale)
|
||||
@@ -1,320 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from torch import Tensor, nn
|
||||
|
||||
|
||||
@dataclass
|
||||
class AutoEncoderParams:
|
||||
resolution: int
|
||||
in_channels: int
|
||||
ch: int
|
||||
out_ch: int
|
||||
ch_mult: list[int]
|
||||
num_res_blocks: int
|
||||
z_channels: int
|
||||
scale_factor: float
|
||||
shift_factor: float
|
||||
|
||||
|
||||
def swish(x: Tensor) -> Tensor:
|
||||
return x * torch.sigmoid(x)
|
||||
|
||||
|
||||
class AttnBlock(nn.Module):
|
||||
def __init__(self, in_channels: int):
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
|
||||
self.norm = nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
|
||||
|
||||
self.q = nn.Conv2d(in_channels, in_channels, kernel_size=1)
|
||||
self.k = nn.Conv2d(in_channels, in_channels, kernel_size=1)
|
||||
self.v = nn.Conv2d(in_channels, in_channels, kernel_size=1)
|
||||
self.proj_out = nn.Conv2d(in_channels, in_channels, kernel_size=1)
|
||||
|
||||
def attention(self, h_: Tensor) -> Tensor:
|
||||
h_ = self.norm(h_)
|
||||
q = self.q(h_)
|
||||
k = self.k(h_)
|
||||
v = self.v(h_)
|
||||
|
||||
b, c, h, w = q.shape
|
||||
q = rearrange(q, "b c h w -> b 1 (h w) c").contiguous()
|
||||
k = rearrange(k, "b c h w -> b 1 (h w) c").contiguous()
|
||||
v = rearrange(v, "b c h w -> b 1 (h w) c").contiguous()
|
||||
h_ = nn.functional.scaled_dot_product_attention(q, k, v)
|
||||
|
||||
return rearrange(h_, "b 1 (h w) c -> b c h w", h=h, w=w, c=c, b=b)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
return x + self.proj_out(self.attention(x))
|
||||
|
||||
|
||||
class ResnetBlock(nn.Module):
|
||||
def __init__(self, in_channels: int, out_channels: int):
|
||||
super().__init__()
|
||||
self.in_channels = in_channels
|
||||
out_channels = in_channels if out_channels is None else out_channels
|
||||
self.out_channels = out_channels
|
||||
|
||||
self.norm1 = nn.GroupNorm(num_groups=32, num_channels=in_channels, eps=1e-6, affine=True)
|
||||
self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1)
|
||||
self.norm2 = nn.GroupNorm(num_groups=32, num_channels=out_channels, eps=1e-6, affine=True)
|
||||
self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1)
|
||||
if self.in_channels != self.out_channels:
|
||||
self.nin_shortcut = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
|
||||
|
||||
def forward(self, x):
|
||||
h = x
|
||||
h = self.norm1(h)
|
||||
h = swish(h)
|
||||
h = self.conv1(h)
|
||||
|
||||
h = self.norm2(h)
|
||||
h = swish(h)
|
||||
h = self.conv2(h)
|
||||
|
||||
if self.in_channels != self.out_channels:
|
||||
x = self.nin_shortcut(x)
|
||||
|
||||
return x + h
|
||||
|
||||
|
||||
class Downsample(nn.Module):
|
||||
def __init__(self, in_channels: int):
|
||||
super().__init__()
|
||||
# no asymmetric padding in torch conv, must do it ourselves
|
||||
self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=2, padding=0)
|
||||
|
||||
def forward(self, x: Tensor):
|
||||
pad = (0, 1, 0, 1)
|
||||
x = nn.functional.pad(x, pad, mode="constant", value=0)
|
||||
x = self.conv(x)
|
||||
return x
|
||||
|
||||
|
||||
class Upsample(nn.Module):
|
||||
def __init__(self, in_channels: int):
|
||||
super().__init__()
|
||||
self.conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
def forward(self, x: Tensor):
|
||||
x = nn.functional.interpolate(x, scale_factor=2.0, mode="nearest")
|
||||
x = self.conv(x)
|
||||
return x
|
||||
|
||||
|
||||
class Encoder(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
resolution: int,
|
||||
in_channels: int,
|
||||
ch: int,
|
||||
ch_mult: list[int],
|
||||
num_res_blocks: int,
|
||||
z_channels: int,
|
||||
):
|
||||
super().__init__()
|
||||
self.ch = ch
|
||||
self.num_resolutions = len(ch_mult)
|
||||
self.num_res_blocks = num_res_blocks
|
||||
self.resolution = resolution
|
||||
self.in_channels = in_channels
|
||||
# downsampling
|
||||
self.conv_in = nn.Conv2d(in_channels, self.ch, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
curr_res = resolution
|
||||
in_ch_mult = (1,) + tuple(ch_mult)
|
||||
self.in_ch_mult = in_ch_mult
|
||||
self.down = nn.ModuleList()
|
||||
block_in = self.ch
|
||||
for i_level in range(self.num_resolutions):
|
||||
block = nn.ModuleList()
|
||||
attn = nn.ModuleList()
|
||||
block_in = ch * in_ch_mult[i_level]
|
||||
block_out = ch * ch_mult[i_level]
|
||||
for _ in range(self.num_res_blocks):
|
||||
block.append(ResnetBlock(in_channels=block_in, out_channels=block_out))
|
||||
block_in = block_out
|
||||
down = nn.Module()
|
||||
down.block = block
|
||||
down.attn = attn
|
||||
if i_level != self.num_resolutions - 1:
|
||||
down.downsample = Downsample(block_in)
|
||||
curr_res = curr_res // 2
|
||||
self.down.append(down)
|
||||
|
||||
# middle
|
||||
self.mid = nn.Module()
|
||||
self.mid.block_1 = ResnetBlock(in_channels=block_in, out_channels=block_in)
|
||||
self.mid.attn_1 = AttnBlock(block_in)
|
||||
self.mid.block_2 = ResnetBlock(in_channels=block_in, out_channels=block_in)
|
||||
|
||||
# end
|
||||
self.norm_out = nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
|
||||
self.conv_out = nn.Conv2d(block_in, 2 * z_channels, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
# downsampling
|
||||
hs = [self.conv_in(x)]
|
||||
for i_level in range(self.num_resolutions):
|
||||
for i_block in range(self.num_res_blocks):
|
||||
h = self.down[i_level].block[i_block](hs[-1])
|
||||
if len(self.down[i_level].attn) > 0:
|
||||
h = self.down[i_level].attn[i_block](h)
|
||||
hs.append(h)
|
||||
if i_level != self.num_resolutions - 1:
|
||||
hs.append(self.down[i_level].downsample(hs[-1]))
|
||||
|
||||
# middle
|
||||
h = hs[-1]
|
||||
h = self.mid.block_1(h)
|
||||
h = self.mid.attn_1(h)
|
||||
h = self.mid.block_2(h)
|
||||
# end
|
||||
h = self.norm_out(h)
|
||||
h = swish(h)
|
||||
h = self.conv_out(h)
|
||||
return h
|
||||
|
||||
|
||||
class Decoder(nn.Module):
|
||||
def __init__(
|
||||
self,
|
||||
ch: int,
|
||||
out_ch: int,
|
||||
ch_mult: list[int],
|
||||
num_res_blocks: int,
|
||||
in_channels: int,
|
||||
resolution: int,
|
||||
z_channels: int,
|
||||
):
|
||||
super().__init__()
|
||||
self.ch = ch
|
||||
self.num_resolutions = len(ch_mult)
|
||||
self.num_res_blocks = num_res_blocks
|
||||
self.resolution = resolution
|
||||
self.in_channels = in_channels
|
||||
self.ffactor = 2 ** (self.num_resolutions - 1)
|
||||
|
||||
# compute in_ch_mult, block_in and curr_res at lowest res
|
||||
block_in = ch * ch_mult[self.num_resolutions - 1]
|
||||
curr_res = resolution // 2 ** (self.num_resolutions - 1)
|
||||
self.z_shape = (1, z_channels, curr_res, curr_res)
|
||||
|
||||
# z to block_in
|
||||
self.conv_in = nn.Conv2d(z_channels, block_in, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
# middle
|
||||
self.mid = nn.Module()
|
||||
self.mid.block_1 = ResnetBlock(in_channels=block_in, out_channels=block_in)
|
||||
self.mid.attn_1 = AttnBlock(block_in)
|
||||
self.mid.block_2 = ResnetBlock(in_channels=block_in, out_channels=block_in)
|
||||
|
||||
# upsampling
|
||||
self.up = nn.ModuleList()
|
||||
for i_level in reversed(range(self.num_resolutions)):
|
||||
block = nn.ModuleList()
|
||||
attn = nn.ModuleList()
|
||||
block_out = ch * ch_mult[i_level]
|
||||
for _ in range(self.num_res_blocks + 1):
|
||||
block.append(ResnetBlock(in_channels=block_in, out_channels=block_out))
|
||||
block_in = block_out
|
||||
up = nn.Module()
|
||||
up.block = block
|
||||
up.attn = attn
|
||||
if i_level != 0:
|
||||
up.upsample = Upsample(block_in)
|
||||
curr_res = curr_res * 2
|
||||
self.up.insert(0, up) # prepend to get consistent order
|
||||
|
||||
# end
|
||||
self.norm_out = nn.GroupNorm(num_groups=32, num_channels=block_in, eps=1e-6, affine=True)
|
||||
self.conv_out = nn.Conv2d(block_in, out_ch, kernel_size=3, stride=1, padding=1)
|
||||
|
||||
def forward(self, z: Tensor) -> Tensor:
|
||||
# get dtype for proper tracing
|
||||
upscale_dtype = next(self.up.parameters()).dtype
|
||||
|
||||
# z to block_in
|
||||
h = self.conv_in(z)
|
||||
|
||||
# middle
|
||||
h = self.mid.block_1(h)
|
||||
h = self.mid.attn_1(h)
|
||||
h = self.mid.block_2(h)
|
||||
|
||||
# cast to proper dtype
|
||||
h = h.to(upscale_dtype)
|
||||
# upsampling
|
||||
for i_level in reversed(range(self.num_resolutions)):
|
||||
for i_block in range(self.num_res_blocks + 1):
|
||||
h = self.up[i_level].block[i_block](h)
|
||||
if len(self.up[i_level].attn) > 0:
|
||||
h = self.up[i_level].attn[i_block](h)
|
||||
if i_level != 0:
|
||||
h = self.up[i_level].upsample(h)
|
||||
|
||||
# end
|
||||
h = self.norm_out(h)
|
||||
h = swish(h)
|
||||
h = self.conv_out(h)
|
||||
return h
|
||||
|
||||
|
||||
class DiagonalGaussian(nn.Module):
|
||||
def __init__(self, sample: bool = True, chunk_dim: int = 1):
|
||||
super().__init__()
|
||||
self.sample = sample
|
||||
self.chunk_dim = chunk_dim
|
||||
|
||||
def forward(self, z: Tensor) -> Tensor:
|
||||
mean, logvar = torch.chunk(z, 2, dim=self.chunk_dim)
|
||||
if self.sample:
|
||||
std = torch.exp(0.5 * logvar)
|
||||
return mean + std * torch.randn_like(mean)
|
||||
else:
|
||||
return mean
|
||||
|
||||
|
||||
class AutoEncoder(nn.Module):
|
||||
def __init__(self, params: AutoEncoderParams, sample_z: bool = False):
|
||||
super().__init__()
|
||||
self.params = params
|
||||
self.encoder = Encoder(
|
||||
resolution=params.resolution,
|
||||
in_channels=params.in_channels,
|
||||
ch=params.ch,
|
||||
ch_mult=params.ch_mult,
|
||||
num_res_blocks=params.num_res_blocks,
|
||||
z_channels=params.z_channels,
|
||||
)
|
||||
self.decoder = Decoder(
|
||||
resolution=params.resolution,
|
||||
in_channels=params.in_channels,
|
||||
ch=params.ch,
|
||||
out_ch=params.out_ch,
|
||||
ch_mult=params.ch_mult,
|
||||
num_res_blocks=params.num_res_blocks,
|
||||
z_channels=params.z_channels,
|
||||
)
|
||||
self.reg = DiagonalGaussian(sample=sample_z)
|
||||
|
||||
self.scale_factor = params.scale_factor
|
||||
self.shift_factor = params.shift_factor
|
||||
|
||||
def get_VAE_tile_size(*args, **kwargs):
|
||||
return []
|
||||
def encode(self, x: Tensor) -> Tensor:
|
||||
z = self.reg(self.encoder(x))
|
||||
z = self.scale_factor * (z - self.shift_factor)
|
||||
return z
|
||||
|
||||
def decode(self, z: Tensor) -> Tensor:
|
||||
z = z / self.scale_factor + self.shift_factor
|
||||
return self.decoder(z)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
return self.decode(self.encode(x))
|
||||
@@ -1,38 +0,0 @@
|
||||
from torch import Tensor, nn
|
||||
from transformers import CLIPTextModel, CLIPTokenizer, T5EncoderModel, T5Tokenizer
|
||||
import os
|
||||
|
||||
class HFEmbedder(nn.Module):
|
||||
def __init__(self, version: str, text_encoder_filename, max_length: int, is_clip = False, **hf_kwargs):
|
||||
super().__init__()
|
||||
self.is_clip = is_clip
|
||||
self.max_length = max_length
|
||||
self.output_key = "pooler_output" if self.is_clip else "last_hidden_state"
|
||||
|
||||
if is_clip:
|
||||
self.tokenizer: CLIPTokenizer = CLIPTokenizer.from_pretrained(version, max_length=max_length)
|
||||
self.hf_module: CLIPTextModel = CLIPTextModel.from_pretrained(version, **hf_kwargs)
|
||||
else:
|
||||
from mmgp import offload as offloadobj
|
||||
self.tokenizer: T5Tokenizer = T5Tokenizer.from_pretrained(os.path.dirname(text_encoder_filename), max_length=max_length)
|
||||
self.hf_module: T5EncoderModel = offloadobj.fast_load_transformers_model(text_encoder_filename)
|
||||
|
||||
self.hf_module = self.hf_module.eval().requires_grad_(False)
|
||||
|
||||
def forward(self, text: list[str]) -> Tensor:
|
||||
batch_encoding = self.tokenizer(
|
||||
text,
|
||||
truncation=True,
|
||||
max_length=self.max_length,
|
||||
return_length=False,
|
||||
return_overflowing_tokens=False,
|
||||
padding="max_length",
|
||||
return_tensors="pt",
|
||||
)
|
||||
|
||||
outputs = self.hf_module(
|
||||
input_ids=batch_encoding["input_ids"].to(self.hf_module.device),
|
||||
attention_mask=None,
|
||||
output_hidden_states=False,
|
||||
)
|
||||
return outputs[self.output_key].bfloat16()
|
||||
@@ -1,99 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import torch
|
||||
from einops import rearrange, repeat
|
||||
from PIL import Image
|
||||
from safetensors.torch import load_file as load_sft
|
||||
from torch import nn
|
||||
from transformers import AutoModelForDepthEstimation, AutoProcessor, SiglipImageProcessor, SiglipVisionModel
|
||||
|
||||
from flux.util import print_load_warning
|
||||
|
||||
|
||||
class DepthImageEncoder:
|
||||
depth_model_name = "LiheYoung/depth-anything-large-hf"
|
||||
|
||||
def __init__(self, device):
|
||||
self.device = device
|
||||
self.depth_model = AutoModelForDepthEstimation.from_pretrained(self.depth_model_name).to(device)
|
||||
self.processor = AutoProcessor.from_pretrained(self.depth_model_name)
|
||||
|
||||
def __call__(self, img: torch.Tensor) -> torch.Tensor:
|
||||
hw = img.shape[-2:]
|
||||
|
||||
img = torch.clamp(img, -1.0, 1.0)
|
||||
img_byte = ((img + 1.0) * 127.5).byte()
|
||||
|
||||
img = self.processor(img_byte, return_tensors="pt")["pixel_values"]
|
||||
depth = self.depth_model(img.to(self.device)).predicted_depth
|
||||
depth = repeat(depth, "b h w -> b 3 h w")
|
||||
depth = torch.nn.functional.interpolate(depth, hw, mode="bicubic", antialias=True)
|
||||
|
||||
depth = depth / 127.5 - 1.0
|
||||
return depth
|
||||
|
||||
|
||||
class CannyImageEncoder:
|
||||
def __init__(
|
||||
self,
|
||||
device,
|
||||
min_t: int = 50,
|
||||
max_t: int = 200,
|
||||
):
|
||||
self.device = device
|
||||
self.min_t = min_t
|
||||
self.max_t = max_t
|
||||
|
||||
def __call__(self, img: torch.Tensor) -> torch.Tensor:
|
||||
assert img.shape[0] == 1, "Only batch size 1 is supported"
|
||||
|
||||
img = rearrange(img[0], "c h w -> h w c")
|
||||
img = torch.clamp(img, -1.0, 1.0)
|
||||
img_np = ((img + 1.0) * 127.5).numpy().astype(np.uint8)
|
||||
|
||||
# Apply Canny edge detection
|
||||
canny = cv2.Canny(img_np, self.min_t, self.max_t)
|
||||
|
||||
# Convert back to torch tensor and reshape
|
||||
canny = torch.from_numpy(canny).float() / 127.5 - 1.0
|
||||
canny = rearrange(canny, "h w -> 1 1 h w")
|
||||
canny = repeat(canny, "b 1 ... -> b 3 ...")
|
||||
return canny.to(self.device)
|
||||
|
||||
|
||||
class ReduxImageEncoder(nn.Module):
|
||||
siglip_model_name = "google/siglip-so400m-patch14-384"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
device,
|
||||
redux_path: str,
|
||||
redux_dim: int = 1152,
|
||||
txt_in_features: int = 4096,
|
||||
dtype=torch.bfloat16,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.redux_dim = redux_dim
|
||||
self.device = device if isinstance(device, torch.device) else torch.device(device)
|
||||
self.dtype = dtype
|
||||
|
||||
with self.device:
|
||||
self.redux_up = nn.Linear(redux_dim, txt_in_features * 3, dtype=dtype)
|
||||
self.redux_down = nn.Linear(txt_in_features * 3, txt_in_features, dtype=dtype)
|
||||
|
||||
sd = load_sft(redux_path, device=str(device))
|
||||
missing, unexpected = self.load_state_dict(sd, strict=False, assign=True)
|
||||
print_load_warning(missing, unexpected)
|
||||
|
||||
self.siglip = SiglipVisionModel.from_pretrained(self.siglip_model_name).to(dtype=dtype)
|
||||
self.normalize = SiglipImageProcessor.from_pretrained(self.siglip_model_name)
|
||||
|
||||
def __call__(self, x: Image.Image) -> torch.Tensor:
|
||||
imgs = self.normalize.preprocess(images=[x], do_resize=True, return_tensors="pt", do_convert_rgb=True)
|
||||
|
||||
_encoded_x = self.siglip(**imgs.to(device=self.device, dtype=self.dtype)).last_hidden_state
|
||||
|
||||
projected_x = self.redux_down(nn.functional.silu(self.redux_up(_encoded_x)))
|
||||
|
||||
return projected_x
|
||||
@@ -1,327 +0,0 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from torch import Tensor, nn
|
||||
|
||||
from flux.math import attention, rope
|
||||
|
||||
def get_linear_split_map():
|
||||
hidden_size = 3072
|
||||
_modules_map = {
|
||||
"qkv" : {"mapped_modules" : ["q", "k", "v"] , "split_sizes": [hidden_size, hidden_size, hidden_size]},
|
||||
"linear1" : {"mapped_modules" : ["linear1_attn_q", "linear1_attn_k", "linear1_attn_v", "linear1_mlp"] , "split_sizes": [hidden_size, hidden_size, hidden_size, 7*hidden_size- 3*hidden_size]}
|
||||
}
|
||||
return split_linear_modules_map
|
||||
|
||||
|
||||
class EmbedND(nn.Module):
|
||||
def __init__(self, dim: int, theta: int, axes_dim: list[int]):
|
||||
super().__init__()
|
||||
self.dim = dim
|
||||
self.theta = theta
|
||||
self.axes_dim = axes_dim
|
||||
|
||||
def forward(self, ids: Tensor) -> Tensor:
|
||||
n_axes = ids.shape[-1]
|
||||
emb = torch.cat(
|
||||
[rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
|
||||
dim=-3,
|
||||
)
|
||||
|
||||
return emb.unsqueeze(1)
|
||||
|
||||
|
||||
def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
|
||||
"""
|
||||
Create sinusoidal timestep embeddings.
|
||||
:param t: a 1-D Tensor of N indices, one per batch element.
|
||||
These may be fractional.
|
||||
:param dim: the dimension of the output.
|
||||
:param max_period: controls the minimum frequency of the embeddings.
|
||||
:return: an (N, D) Tensor of positional embeddings.
|
||||
"""
|
||||
t = time_factor * t
|
||||
half = dim // 2
|
||||
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half).to(
|
||||
t.device
|
||||
)
|
||||
|
||||
args = t[:, None].float() * freqs[None]
|
||||
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
||||
if dim % 2:
|
||||
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
||||
if torch.is_floating_point(t):
|
||||
embedding = embedding.to(t)
|
||||
return embedding
|
||||
|
||||
|
||||
class MLPEmbedder(nn.Module):
|
||||
def __init__(self, in_dim: int, hidden_dim: int):
|
||||
super().__init__()
|
||||
self.in_layer = nn.Linear(in_dim, hidden_dim, bias=True)
|
||||
self.silu = nn.SiLU()
|
||||
self.out_layer = nn.Linear(hidden_dim, hidden_dim, bias=True)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
return self.out_layer(self.silu(self.in_layer(x)))
|
||||
|
||||
|
||||
class RMSNorm(torch.nn.Module):
|
||||
def __init__(self, dim: int):
|
||||
super().__init__()
|
||||
self.scale = nn.Parameter(torch.ones(dim))
|
||||
|
||||
def forward(self, x: Tensor):
|
||||
x_dtype = x.dtype
|
||||
x = x.float()
|
||||
rrms = torch.rsqrt(torch.mean(x**2, dim=-1, keepdim=True) + 1e-6)
|
||||
return (x * rrms).to(dtype=x_dtype) * self.scale
|
||||
|
||||
|
||||
class QKNorm(torch.nn.Module):
|
||||
def __init__(self, dim: int):
|
||||
super().__init__()
|
||||
self.query_norm = RMSNorm(dim)
|
||||
self.key_norm = RMSNorm(dim)
|
||||
|
||||
def forward(self, q: Tensor, k: Tensor, v: Tensor) -> tuple[Tensor, Tensor]:
|
||||
q = self.query_norm(q)
|
||||
k = self.key_norm(k)
|
||||
return q.to(v), k.to(v)
|
||||
|
||||
|
||||
class SelfAttention(nn.Module):
|
||||
def __init__(self, dim: int, num_heads: int = 8, qkv_bias: bool = False):
|
||||
super().__init__()
|
||||
self.num_heads = num_heads
|
||||
head_dim = dim // num_heads
|
||||
|
||||
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
||||
self.norm = QKNorm(head_dim)
|
||||
self.proj = nn.Linear(dim, dim)
|
||||
|
||||
def forward(self, x: Tensor, pe: Tensor) -> Tensor:
|
||||
qkv = self.qkv(x)
|
||||
q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
||||
q, k = self.norm(q, k, v)
|
||||
x = attention(q, k, v, pe=pe)
|
||||
x = self.proj(x)
|
||||
return x
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModulationOut:
|
||||
shift: Tensor
|
||||
scale: Tensor
|
||||
gate: Tensor
|
||||
|
||||
|
||||
class Modulation(nn.Module):
|
||||
def __init__(self, dim: int, double: bool):
|
||||
super().__init__()
|
||||
self.is_double = double
|
||||
self.multiplier = 6 if double else 3
|
||||
self.lin = nn.Linear(dim, self.multiplier * dim, bias=True)
|
||||
|
||||
def forward(self, vec: Tensor) -> tuple[ModulationOut, ModulationOut | None]:
|
||||
out = self.lin(nn.functional.silu(vec))[:, None, :].chunk(self.multiplier, dim=-1)
|
||||
|
||||
return (
|
||||
ModulationOut(*out[:3]),
|
||||
ModulationOut(*out[3:]) if self.is_double else None,
|
||||
)
|
||||
|
||||
|
||||
class DoubleStreamBlock(nn.Module):
|
||||
def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False):
|
||||
super().__init__()
|
||||
|
||||
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
||||
self.num_heads = num_heads
|
||||
self.hidden_size = hidden_size
|
||||
self.img_mod = Modulation(hidden_size, double=True)
|
||||
self.img_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
||||
|
||||
self.img_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.img_mlp = nn.Sequential(
|
||||
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
||||
nn.GELU(approximate="tanh"),
|
||||
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
||||
)
|
||||
|
||||
self.txt_mod = Modulation(hidden_size, double=True)
|
||||
self.txt_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
||||
|
||||
self.txt_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.txt_mlp = nn.Sequential(
|
||||
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
||||
nn.GELU(approximate="tanh"),
|
||||
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
||||
)
|
||||
|
||||
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor) -> tuple[Tensor, Tensor]:
|
||||
img_mod1, img_mod2 = self.img_mod(vec)
|
||||
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
||||
|
||||
# prepare image for attention
|
||||
img_modulated = self.img_norm1(img)
|
||||
img_modulated.mul_(1 + img_mod1.scale)
|
||||
img_modulated.add_(img_mod1.shift)
|
||||
|
||||
shape = (*img_modulated.shape[:2], self.num_heads, int(img_modulated.shape[-1] / self.num_heads) )
|
||||
img_q = self.img_attn.q(img_modulated).view(*shape).transpose(1,2)
|
||||
img_k = self.img_attn.k(img_modulated).view(*shape).transpose(1,2)
|
||||
img_v = self.img_attn.v(img_modulated).view(*shape).transpose(1,2)
|
||||
del img_modulated
|
||||
|
||||
# img_qkv = self.img_attn.qkv(img_modulated)
|
||||
# img_q, img_k, img_v = rearrange(img_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
||||
|
||||
img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
|
||||
|
||||
# prepare txt for attention
|
||||
txt_modulated = self.txt_norm1(txt)
|
||||
txt_modulated.mul_(1 + txt_mod1.scale)
|
||||
txt_modulated.add_(txt_mod1.shift)
|
||||
# txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift
|
||||
|
||||
shape = (*txt_modulated.shape[:2], self.num_heads, int(txt_modulated.shape[-1] / self.num_heads) )
|
||||
txt_q = self.txt_attn.q(txt_modulated).view(*shape).transpose(1,2)
|
||||
txt_k = self.txt_attn.k(txt_modulated).view(*shape).transpose(1,2)
|
||||
txt_v = self.txt_attn.v(txt_modulated).view(*shape).transpose(1,2)
|
||||
del txt_modulated
|
||||
|
||||
# txt_qkv = self.txt_attn.qkv(txt_modulated)
|
||||
# txt_q, txt_k, txt_v = rearrange(txt_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
||||
|
||||
txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
|
||||
|
||||
# run actual attention
|
||||
q = torch.cat((txt_q, img_q), dim=2)
|
||||
k = torch.cat((txt_k, img_k), dim=2)
|
||||
v = torch.cat((txt_v, img_v), dim=2)
|
||||
|
||||
qkv_list = [q, k, v]
|
||||
del q, k, v
|
||||
attn = attention(qkv_list, pe=pe)
|
||||
|
||||
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1] :]
|
||||
|
||||
# calculate the img blocks
|
||||
img.addcmul_(self.img_attn.proj(img_attn), img_mod1.gate)
|
||||
img.addcmul_(self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift), img_mod2.gate)
|
||||
|
||||
# img = img + img_mod1.gate * self.img_attn.proj(img_attn)
|
||||
# img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift)
|
||||
|
||||
# calculate the txt blocks
|
||||
txt.addcmul_(self.txt_attn.proj(txt_attn), txt_mod1.gate)
|
||||
txt.addcmul_(self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift), txt_mod2.gate)
|
||||
# txt = txt + txt_mod1.gate * self.txt_attn.proj(txt_attn)
|
||||
# txt = txt + txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
|
||||
return img, txt
|
||||
|
||||
|
||||
class SingleStreamBlock(nn.Module):
|
||||
"""
|
||||
A DiT block with parallel linear layers as described in
|
||||
https://arxiv.org/abs/2302.05442 and adapted modulation interface.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hidden_size: int,
|
||||
num_heads: int,
|
||||
mlp_ratio: float = 4.0,
|
||||
qk_scale: float | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.hidden_dim = hidden_size
|
||||
self.num_heads = num_heads
|
||||
head_dim = hidden_size // num_heads
|
||||
self.scale = qk_scale or head_dim**-0.5
|
||||
|
||||
self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
||||
# qkv and mlp_in
|
||||
self.linear1 = nn.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim)
|
||||
# proj and mlp_out
|
||||
self.linear2 = nn.Linear(hidden_size + self.mlp_hidden_dim, hidden_size)
|
||||
|
||||
self.norm = QKNorm(head_dim)
|
||||
|
||||
self.hidden_size = hidden_size
|
||||
self.pre_norm = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
|
||||
self.mlp_act = nn.GELU(approximate="tanh")
|
||||
self.modulation = Modulation(hidden_size, double=False)
|
||||
|
||||
def forward(self, x: Tensor, vec: Tensor, pe: Tensor) -> Tensor:
|
||||
mod, _ = self.modulation(vec)
|
||||
x_mod = self.pre_norm(x)
|
||||
x_mod.mul_(1 + mod.scale)
|
||||
x_mod.add_(mod.shift)
|
||||
|
||||
##### More spagheti VRAM optimizations done by DeepBeepMeep !
|
||||
# I am sure you are a nice person and as you copy this code, you will give me proper credits:
|
||||
# Please link to https://github.com/deepbeepmeep/Wan2GP and @deepbeepmeep on twitter
|
||||
|
||||
# x_mod = (1 + mod.scale) * x + mod.shift
|
||||
|
||||
shape = (*x_mod.shape[:2], self.num_heads, int(x_mod.shape[-1] / self.num_heads) )
|
||||
q = self.linear1_attn_q(x_mod).view(*shape).transpose(1,2)
|
||||
k = self.linear1_attn_k(x_mod).view(*shape).transpose(1,2)
|
||||
v = self.linear1_attn_v(x_mod).view(*shape).transpose(1,2)
|
||||
|
||||
# shape = (*txt_mod.shape[:2], self.heads_num, int(txt_mod.shape[-1] / self.heads_num) )
|
||||
# txt_q = self.linear1_attn_q(txt_mod).view(*shape)
|
||||
# txt_k = self.linear1_attn_k(txt_mod).view(*shape)
|
||||
# txt_v = self.linear1_attn_v(txt_mod).view(*shape)
|
||||
|
||||
# qkv, mlp = torch.split(self.linear1(x_mod), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
|
||||
|
||||
# q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
|
||||
q, k = self.norm(q, k, v)
|
||||
|
||||
# compute attention
|
||||
qkv_list = [q, k, v]
|
||||
del q, k, v
|
||||
attn = attention(qkv_list, pe=pe)
|
||||
# compute activation in mlp stream, cat again and run second linear layer
|
||||
|
||||
x_mod_shape = x_mod.shape
|
||||
x_mod = x_mod.view(-1, x_mod.shape[-1])
|
||||
chunk_size = int(x_mod_shape[1]/6)
|
||||
x_chunks = torch.split(x_mod, chunk_size)
|
||||
attn = attn.view(-1, attn.shape[-1])
|
||||
attn_chunks =torch.split(attn, chunk_size)
|
||||
for x_chunk, attn_chunk in zip(x_chunks, attn_chunks):
|
||||
mlp_chunk = self.linear1_mlp(x_chunk)
|
||||
mlp_chunk = self.mlp_act(mlp_chunk)
|
||||
attn_mlp_chunk = torch.cat((attn_chunk, mlp_chunk), -1)
|
||||
del attn_chunk, mlp_chunk
|
||||
x_chunk[...] = self.linear2(attn_mlp_chunk)
|
||||
del attn_mlp_chunk
|
||||
x_mod = x_mod.view(x_mod_shape)
|
||||
x.addcmul_(x_mod, mod.gate)
|
||||
return x
|
||||
|
||||
# output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
|
||||
# return x + mod.gate * output
|
||||
|
||||
|
||||
class LastLayer(nn.Module):
|
||||
def __init__(self, hidden_size: int, patch_size: int, out_channels: int):
|
||||
super().__init__()
|
||||
self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
|
||||
self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
|
||||
|
||||
def forward(self, x: Tensor, vec: Tensor) -> Tensor:
|
||||
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1)
|
||||
x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :]
|
||||
x = self.linear(x)
|
||||
return x
|
||||
@@ -1,329 +0,0 @@
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from torch import Tensor, nn
|
||||
|
||||
from flux.math import attention, rope
|
||||
|
||||
def get_linear_split_map():
|
||||
hidden_size = 3072
|
||||
split_linear_modules_map = {
|
||||
"qkv" : {"mapped_modules" : ["q", "k", "v"] , "split_sizes": [hidden_size, hidden_size, hidden_size]},
|
||||
"linear1" : {"mapped_modules" : ["linear1_attn_q", "linear1_attn_k", "linear1_attn_v", "linear1_mlp"] , "split_sizes": [hidden_size, hidden_size, hidden_size, 7*hidden_size- 3*hidden_size]}
|
||||
}
|
||||
return split_linear_modules_map
|
||||
|
||||
|
||||
class EmbedND(nn.Module):
|
||||
def __init__(self, dim: int, theta: int, axes_dim: list[int]):
|
||||
super().__init__()
|
||||
self.dim = dim
|
||||
self.theta = theta
|
||||
self.axes_dim = axes_dim
|
||||
|
||||
def forward(self, ids: Tensor) -> Tensor:
|
||||
n_axes = ids.shape[-1]
|
||||
emb = torch.cat(
|
||||
[rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
|
||||
dim=-3,
|
||||
)
|
||||
|
||||
return emb.unsqueeze(1)
|
||||
|
||||
|
||||
def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
|
||||
"""
|
||||
Create sinusoidal timestep embeddings.
|
||||
:param t: a 1-D Tensor of N indices, one per batch element.
|
||||
These may be fractional.
|
||||
:param dim: the dimension of the output.
|
||||
:param max_period: controls the minimum frequency of the embeddings.
|
||||
:return: an (N, D) Tensor of positional embeddings.
|
||||
"""
|
||||
t = time_factor * t
|
||||
half = dim // 2
|
||||
freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half).to(
|
||||
t.device
|
||||
)
|
||||
|
||||
args = t[:, None].float() * freqs[None]
|
||||
embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
|
||||
if dim % 2:
|
||||
embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
|
||||
if torch.is_floating_point(t):
|
||||
embedding = embedding.to(t)
|
||||
return embedding
|
||||
|
||||
|
||||
class MLPEmbedder(nn.Module):
|
||||
def __init__(self, in_dim: int, hidden_dim: int):
|
||||
super().__init__()
|
||||
self.in_layer = nn.Linear(in_dim, hidden_dim, bias=True)
|
||||
self.silu = nn.SiLU()
|
||||
self.out_layer = nn.Linear(hidden_dim, hidden_dim, bias=True)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
return self.out_layer(self.silu(self.in_layer(x)))
|
||||
|
||||
|
||||
class RMSNorm(torch.nn.Module):
|
||||
def __init__(self, dim: int):
|
||||
super().__init__()
|
||||
self.scale = nn.Parameter(torch.ones(dim))
|
||||
|
||||
def forward(self, x: Tensor):
|
||||
x_dtype = x.dtype
|
||||
x = x.float()
|
||||
rrms = torch.rsqrt(torch.mean(x**2, dim=-1, keepdim=True) + 1e-6)
|
||||
return (x * rrms).to(dtype=x_dtype) * self.scale
|
||||
|
||||
|
||||
|
||||
class QKNorm(torch.nn.Module):
|
||||
def __init__(self, dim: int):
|
||||
super().__init__()
|
||||
self.query_norm = RMSNorm(dim)
|
||||
self.key_norm = RMSNorm(dim)
|
||||
|
||||
def forward(self, q: Tensor, k: Tensor, v: Tensor) -> tuple[Tensor, Tensor]:
|
||||
if k != None:
|
||||
return self.key_norm(k).to(v)
|
||||
else:
|
||||
return self.query_norm(q).to(v)
|
||||
# q = self.query_norm(q)
|
||||
# k = self.key_norm(k)
|
||||
# return q.to(v), k.to(v)
|
||||
|
||||
|
||||
class SelfAttention(nn.Module):
|
||||
def __init__(self, dim: int, num_heads: int = 8, qkv_bias: bool = False):
|
||||
super().__init__()
|
||||
self.num_heads = num_heads
|
||||
head_dim = dim // num_heads
|
||||
|
||||
self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
|
||||
self.norm = QKNorm(head_dim)
|
||||
self.proj = nn.Linear(dim, dim)
|
||||
|
||||
def forward(self, x: Tensor, pe: Tensor) -> Tensor:
|
||||
raise Exception("not implemented")
|
||||
|
||||
@dataclass
|
||||
class ModulationOut:
|
||||
shift: Tensor
|
||||
scale: Tensor
|
||||
gate: Tensor
|
||||
|
||||
|
||||
def split_mlp(mlp, x, divide = 8):
|
||||
x_shape = x.shape
|
||||
x = x.view(-1, x.shape[-1])
|
||||
chunk_size = int(x.shape[0]/divide)
|
||||
chunk_size = int(x_shape[1]/divide)
|
||||
x_chunks = torch.split(x, chunk_size)
|
||||
for i, x_chunk in enumerate(x_chunks):
|
||||
mlp_chunk = mlp[0](x_chunk)
|
||||
mlp_chunk = mlp[1](mlp_chunk)
|
||||
x_chunk[...] = mlp[2](mlp_chunk)
|
||||
return x.reshape(x_shape)
|
||||
|
||||
class Modulation(nn.Module):
|
||||
def __init__(self, dim: int, double: bool):
|
||||
super().__init__()
|
||||
self.is_double = double
|
||||
self.multiplier = 6 if double else 3
|
||||
self.lin = nn.Linear(dim, self.multiplier * dim, bias=True)
|
||||
|
||||
def forward(self, vec: Tensor) -> tuple[ModulationOut, ModulationOut | None]:
|
||||
out = self.lin(nn.functional.silu(vec))[:, None, :].chunk(self.multiplier, dim=-1)
|
||||
|
||||
return (
|
||||
ModulationOut(*out[:3]),
|
||||
ModulationOut(*out[3:]) if self.is_double else None,
|
||||
)
|
||||
|
||||
|
||||
class DoubleStreamBlock(nn.Module):
|
||||
def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False):
|
||||
super().__init__()
|
||||
|
||||
mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
||||
self.num_heads = num_heads
|
||||
self.hidden_size = hidden_size
|
||||
self.img_mod = Modulation(hidden_size, double=True)
|
||||
self.img_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
||||
|
||||
self.img_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.img_mlp = nn.Sequential(
|
||||
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
||||
nn.GELU(approximate="tanh"),
|
||||
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
||||
)
|
||||
|
||||
self.txt_mod = Modulation(hidden_size, double=True)
|
||||
self.txt_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
|
||||
|
||||
self.txt_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.txt_mlp = nn.Sequential(
|
||||
nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
|
||||
nn.GELU(approximate="tanh"),
|
||||
nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
|
||||
)
|
||||
|
||||
def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor) -> tuple[Tensor, Tensor]:
|
||||
img_mod1, img_mod2 = self.img_mod(vec)
|
||||
txt_mod1, txt_mod2 = self.txt_mod(vec)
|
||||
|
||||
# prepare image for attention
|
||||
img_modulated = self.img_norm1(img)
|
||||
img_modulated.mul_(1 + img_mod1.scale)
|
||||
img_modulated.add_(img_mod1.shift)
|
||||
|
||||
shape = (*img_modulated.shape[:2], self.num_heads, int(img_modulated.shape[-1] / self.num_heads) )
|
||||
img_q = self.img_attn.q(img_modulated).view(*shape).transpose(1,2)
|
||||
img_k = self.img_attn.k(img_modulated).view(*shape).transpose(1,2)
|
||||
img_v = self.img_attn.v(img_modulated).view(*shape).transpose(1,2)
|
||||
del img_modulated
|
||||
|
||||
|
||||
img_q= self.img_attn.norm(img_q, None, img_v)
|
||||
img_k = self.img_attn.norm(None, img_k, img_v)
|
||||
|
||||
# prepare txt for attention
|
||||
txt_modulated = self.txt_norm1(txt)
|
||||
txt_modulated.mul_(1 + txt_mod1.scale)
|
||||
txt_modulated.add_(txt_mod1.shift)
|
||||
|
||||
shape = (*txt_modulated.shape[:2], self.num_heads, int(txt_modulated.shape[-1] / self.num_heads) )
|
||||
txt_q = self.txt_attn.q(txt_modulated).view(*shape).transpose(1,2)
|
||||
txt_k = self.txt_attn.k(txt_modulated).view(*shape).transpose(1,2)
|
||||
txt_v = self.txt_attn.v(txt_modulated).view(*shape).transpose(1,2)
|
||||
del txt_modulated
|
||||
|
||||
|
||||
txt_q = self.txt_attn.norm(txt_q, None, txt_v)
|
||||
txt_k = self.txt_attn.norm(None, txt_k, txt_v)
|
||||
|
||||
# run actual attention
|
||||
q = torch.cat((txt_q, img_q), dim=2)
|
||||
del txt_q, img_q
|
||||
k = torch.cat((txt_k, img_k), dim=2)
|
||||
del txt_k, img_k
|
||||
v = torch.cat((txt_v, img_v), dim=2)
|
||||
del txt_v, img_v
|
||||
|
||||
qkv_list = [q, k, v]
|
||||
del q, k, v
|
||||
attn = attention(qkv_list, pe=pe)
|
||||
|
||||
txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1] :]
|
||||
|
||||
# calculate the img blocks
|
||||
img.addcmul_(self.img_attn.proj(img_attn), img_mod1.gate)
|
||||
mod_img = self.img_norm2(img)
|
||||
mod_img.mul_(1 + img_mod2.scale)
|
||||
mod_img.add_(img_mod2.shift)
|
||||
mod_img = split_mlp(self.img_mlp, mod_img)
|
||||
# mod_img = self.img_mlp(mod_img)
|
||||
img.addcmul_( mod_img, img_mod2.gate)
|
||||
mod_img = None
|
||||
|
||||
# calculate the txt blocks
|
||||
txt.addcmul_(self.txt_attn.proj(txt_attn), txt_mod1.gate)
|
||||
txt.addcmul_(self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift), txt_mod2.gate)
|
||||
return img, txt
|
||||
|
||||
|
||||
class SingleStreamBlock(nn.Module):
|
||||
"""
|
||||
A DiT block with parallel linear layers as described in
|
||||
https://arxiv.org/abs/2302.05442 and adapted modulation interface.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hidden_size: int,
|
||||
num_heads: int,
|
||||
mlp_ratio: float = 4.0,
|
||||
qk_scale: float | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.hidden_dim = hidden_size
|
||||
self.num_heads = num_heads
|
||||
head_dim = hidden_size // num_heads
|
||||
self.scale = qk_scale or head_dim**-0.5
|
||||
|
||||
self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
|
||||
# qkv and mlp_in
|
||||
self.linear1 = nn.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim)
|
||||
# proj and mlp_out
|
||||
self.linear2 = nn.Linear(hidden_size + self.mlp_hidden_dim, hidden_size)
|
||||
|
||||
self.norm = QKNorm(head_dim)
|
||||
|
||||
self.hidden_size = hidden_size
|
||||
self.pre_norm = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
|
||||
self.mlp_act = nn.GELU(approximate="tanh")
|
||||
self.modulation = Modulation(hidden_size, double=False)
|
||||
|
||||
def forward(self, x: Tensor, vec: Tensor, pe: Tensor) -> Tensor:
|
||||
mod, _ = self.modulation(vec)
|
||||
x_mod = self.pre_norm(x)
|
||||
x_mod.mul_(1 + mod.scale)
|
||||
x_mod.add_(mod.shift)
|
||||
|
||||
##### More spagheti VRAM optimizations done by DeepBeepMeep !
|
||||
# I am sure you are a nice person and as you copy this code, you will give me proper credits:
|
||||
# Please link to https://github.com/deepbeepmeep/Wan2GP and @deepbeepmeep on twitter
|
||||
|
||||
# x_mod = (1 + mod.scale) * x + mod.shift
|
||||
|
||||
shape = (*x_mod.shape[:2], self.num_heads, int(x_mod.shape[-1] / self.num_heads) )
|
||||
q = self.linear1_attn_q(x_mod).view(*shape).transpose(1,2)
|
||||
k = self.linear1_attn_k(x_mod).view(*shape).transpose(1,2)
|
||||
v = self.linear1_attn_v(x_mod).view(*shape).transpose(1,2)
|
||||
|
||||
q = self.norm(q, None, v)
|
||||
k = self.norm(None, k, v)
|
||||
|
||||
# compute attention
|
||||
qkv_list = [q, k, v]
|
||||
del q, k, v
|
||||
attn = attention(qkv_list, pe=pe)
|
||||
# compute activation in mlp stream, cat again and run second linear layer
|
||||
|
||||
x_mod_shape = x_mod.shape
|
||||
x_mod = x_mod.view(-1, x_mod.shape[-1])
|
||||
chunk_size = int(x_mod_shape[1]/6)
|
||||
x_chunks = torch.split(x_mod, chunk_size)
|
||||
attn = attn.view(-1, attn.shape[-1])
|
||||
attn_chunks =torch.split(attn, chunk_size)
|
||||
for x_chunk, attn_chunk in zip(x_chunks, attn_chunks):
|
||||
mlp_chunk = self.linear1_mlp(x_chunk)
|
||||
mlp_chunk = self.mlp_act(mlp_chunk)
|
||||
attn_mlp_chunk = torch.cat((attn_chunk, mlp_chunk), -1)
|
||||
del attn_chunk, mlp_chunk
|
||||
x_chunk[...] = self.linear2(attn_mlp_chunk)
|
||||
del attn_mlp_chunk
|
||||
x_mod = x_mod.view(x_mod_shape)
|
||||
x.addcmul_(x_mod, mod.gate)
|
||||
return x
|
||||
|
||||
|
||||
class LastLayer(nn.Module):
|
||||
def __init__(self, hidden_size: int, patch_size: int, out_channels: int):
|
||||
super().__init__()
|
||||
self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
|
||||
self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
|
||||
self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
|
||||
|
||||
def forward(self, x: Tensor, vec: Tensor) -> Tensor:
|
||||
shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1)
|
||||
x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :]
|
||||
x = self.linear(x)
|
||||
return x
|
||||
@@ -1,94 +0,0 @@
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
|
||||
def replace_linear_with_lora(
|
||||
module: nn.Module,
|
||||
max_rank: int,
|
||||
scale: float = 1.0,
|
||||
) -> None:
|
||||
for name, child in module.named_children():
|
||||
if isinstance(child, nn.Linear):
|
||||
new_lora = LinearLora(
|
||||
in_features=child.in_features,
|
||||
out_features=child.out_features,
|
||||
bias=child.bias,
|
||||
rank=max_rank,
|
||||
scale=scale,
|
||||
dtype=child.weight.dtype,
|
||||
device=child.weight.device,
|
||||
)
|
||||
|
||||
new_lora.weight = child.weight
|
||||
new_lora.bias = child.bias if child.bias is not None else None
|
||||
|
||||
setattr(module, name, new_lora)
|
||||
else:
|
||||
replace_linear_with_lora(
|
||||
module=child,
|
||||
max_rank=max_rank,
|
||||
scale=scale,
|
||||
)
|
||||
|
||||
|
||||
class LinearLora(nn.Linear):
|
||||
def __init__(
|
||||
self,
|
||||
in_features: int,
|
||||
out_features: int,
|
||||
bias: bool,
|
||||
rank: int,
|
||||
dtype: torch.dtype,
|
||||
device: torch.device,
|
||||
lora_bias: bool = True,
|
||||
scale: float = 1.0,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
in_features=in_features,
|
||||
out_features=out_features,
|
||||
bias=bias is not None,
|
||||
device=device,
|
||||
dtype=dtype,
|
||||
*args,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
assert isinstance(scale, float), "scale must be a float"
|
||||
|
||||
self.scale = scale
|
||||
self.rank = rank
|
||||
self.lora_bias = lora_bias
|
||||
self.dtype = dtype
|
||||
self.device = device
|
||||
|
||||
if rank > (new_rank := min(self.out_features, self.in_features)):
|
||||
self.rank = new_rank
|
||||
|
||||
self.lora_A = nn.Linear(
|
||||
in_features=in_features,
|
||||
out_features=self.rank,
|
||||
bias=False,
|
||||
dtype=dtype,
|
||||
device=device,
|
||||
)
|
||||
self.lora_B = nn.Linear(
|
||||
in_features=self.rank,
|
||||
out_features=out_features,
|
||||
bias=self.lora_bias,
|
||||
dtype=dtype,
|
||||
device=device,
|
||||
)
|
||||
|
||||
def set_scale(self, scale: float) -> None:
|
||||
assert isinstance(scale, float), "scalar value must be a float"
|
||||
self.scale = scale
|
||||
|
||||
def forward(self, input: torch.Tensor) -> torch.Tensor:
|
||||
base_out = super().forward(input)
|
||||
|
||||
_lora_out_B = self.lora_B(self.lora_A(input))
|
||||
lora_update = _lora_out_B * self.scale
|
||||
|
||||
return base_out + lora_update
|
||||
400
flux/sampling.py
400
flux/sampling.py
@@ -1,400 +0,0 @@
|
||||
import math
|
||||
from typing import Callable
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
from einops import rearrange, repeat
|
||||
from PIL import Image
|
||||
from torch import Tensor
|
||||
|
||||
from .model import Flux
|
||||
from .modules.autoencoder import AutoEncoder
|
||||
from .modules.conditioner import HFEmbedder
|
||||
from .modules.image_embedders import CannyImageEncoder, DepthImageEncoder, ReduxImageEncoder
|
||||
from .util import PREFERED_KONTEXT_RESOLUTIONS
|
||||
from einops import rearrange, repeat
|
||||
|
||||
|
||||
def get_noise(
|
||||
num_samples: int,
|
||||
height: int,
|
||||
width: int,
|
||||
device: torch.device,
|
||||
dtype: torch.dtype,
|
||||
seed: int,
|
||||
):
|
||||
return torch.randn(
|
||||
num_samples,
|
||||
16,
|
||||
# allow for packing
|
||||
2 * math.ceil(height / 16),
|
||||
2 * math.ceil(width / 16),
|
||||
dtype=dtype,
|
||||
device=device,
|
||||
generator=torch.Generator(device=device).manual_seed(seed),
|
||||
)
|
||||
|
||||
|
||||
def prepare(t5: HFEmbedder, clip: HFEmbedder, img: Tensor, prompt: str | list[str]) -> dict[str, Tensor]:
|
||||
bs, c, h, w = img.shape
|
||||
if bs == 1 and not isinstance(prompt, str):
|
||||
bs = len(prompt)
|
||||
|
||||
img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if img.shape[0] == 1 and bs > 1:
|
||||
img = repeat(img, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
img_ids = torch.zeros(h // 2, w // 2, 3)
|
||||
img_ids[..., 1] = img_ids[..., 1] + torch.arange(h // 2)[:, None]
|
||||
img_ids[..., 2] = img_ids[..., 2] + torch.arange(w // 2)[None, :]
|
||||
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
||||
|
||||
if isinstance(prompt, str):
|
||||
prompt = [prompt]
|
||||
txt = t5(prompt)
|
||||
if txt.shape[0] == 1 and bs > 1:
|
||||
txt = repeat(txt, "1 ... -> bs ...", bs=bs)
|
||||
txt_ids = torch.zeros(bs, txt.shape[1], 3)
|
||||
|
||||
vec = clip(prompt)
|
||||
if vec.shape[0] == 1 and bs > 1:
|
||||
vec = repeat(vec, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
return {
|
||||
"img": img,
|
||||
"img_ids": img_ids.to(img.device),
|
||||
"txt": txt.to(img.device),
|
||||
"txt_ids": txt_ids.to(img.device),
|
||||
"vec": vec.to(img.device),
|
||||
}
|
||||
|
||||
|
||||
def prepare_control(
|
||||
t5: HFEmbedder,
|
||||
clip: HFEmbedder,
|
||||
img: Tensor,
|
||||
prompt: str | list[str],
|
||||
ae: AutoEncoder,
|
||||
encoder: DepthImageEncoder | CannyImageEncoder,
|
||||
img_cond_path: str,
|
||||
) -> dict[str, Tensor]:
|
||||
# load and encode the conditioning image
|
||||
bs, _, h, w = img.shape
|
||||
if bs == 1 and not isinstance(prompt, str):
|
||||
bs = len(prompt)
|
||||
|
||||
img_cond = Image.open(img_cond_path).convert("RGB")
|
||||
|
||||
width = w * 8
|
||||
height = h * 8
|
||||
img_cond = img_cond.resize((width, height), Image.Resampling.LANCZOS)
|
||||
img_cond = np.array(img_cond)
|
||||
img_cond = torch.from_numpy(img_cond).float() / 127.5 - 1.0
|
||||
img_cond = rearrange(img_cond, "h w c -> 1 c h w")
|
||||
|
||||
with torch.no_grad():
|
||||
img_cond = encoder(img_cond)
|
||||
img_cond = ae.encode(img_cond)
|
||||
|
||||
img_cond = img_cond.to(torch.bfloat16)
|
||||
img_cond = rearrange(img_cond, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if img_cond.shape[0] == 1 and bs > 1:
|
||||
img_cond = repeat(img_cond, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
return_dict = prepare(t5, clip, img, prompt)
|
||||
return_dict["img_cond"] = img_cond
|
||||
return return_dict
|
||||
|
||||
|
||||
def prepare_fill(
|
||||
t5: HFEmbedder,
|
||||
clip: HFEmbedder,
|
||||
img: Tensor,
|
||||
prompt: str | list[str],
|
||||
ae: AutoEncoder,
|
||||
img_cond_path: str,
|
||||
mask_path: str,
|
||||
) -> dict[str, Tensor]:
|
||||
# load and encode the conditioning image and the mask
|
||||
bs, _, _, _ = img.shape
|
||||
if bs == 1 and not isinstance(prompt, str):
|
||||
bs = len(prompt)
|
||||
|
||||
img_cond = Image.open(img_cond_path).convert("RGB")
|
||||
img_cond = np.array(img_cond)
|
||||
img_cond = torch.from_numpy(img_cond).float() / 127.5 - 1.0
|
||||
img_cond = rearrange(img_cond, "h w c -> 1 c h w")
|
||||
|
||||
mask = Image.open(mask_path).convert("L")
|
||||
mask = np.array(mask)
|
||||
mask = torch.from_numpy(mask).float() / 255.0
|
||||
mask = rearrange(mask, "h w -> 1 1 h w")
|
||||
|
||||
with torch.no_grad():
|
||||
img_cond = img_cond.to(img.device)
|
||||
mask = mask.to(img.device)
|
||||
img_cond = img_cond * (1 - mask)
|
||||
img_cond = ae.encode(img_cond)
|
||||
mask = mask[:, 0, :, :]
|
||||
mask = mask.to(torch.bfloat16)
|
||||
mask = rearrange(
|
||||
mask,
|
||||
"b (h ph) (w pw) -> b (ph pw) h w",
|
||||
ph=8,
|
||||
pw=8,
|
||||
)
|
||||
mask = rearrange(mask, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if mask.shape[0] == 1 and bs > 1:
|
||||
mask = repeat(mask, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
img_cond = img_cond.to(torch.bfloat16)
|
||||
img_cond = rearrange(img_cond, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if img_cond.shape[0] == 1 and bs > 1:
|
||||
img_cond = repeat(img_cond, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
img_cond = torch.cat((img_cond, mask), dim=-1)
|
||||
|
||||
return_dict = prepare(t5, clip, img, prompt)
|
||||
return_dict["img_cond"] = img_cond.to(img.device)
|
||||
return return_dict
|
||||
|
||||
|
||||
def prepare_redux(
|
||||
t5: HFEmbedder,
|
||||
clip: HFEmbedder,
|
||||
img: Tensor,
|
||||
prompt: str | list[str],
|
||||
encoder: ReduxImageEncoder,
|
||||
img_cond_path: str,
|
||||
) -> dict[str, Tensor]:
|
||||
bs, _, h, w = img.shape
|
||||
if bs == 1 and not isinstance(prompt, str):
|
||||
bs = len(prompt)
|
||||
|
||||
img_cond = Image.open(img_cond_path).convert("RGB")
|
||||
with torch.no_grad():
|
||||
img_cond = encoder(img_cond)
|
||||
|
||||
img_cond = img_cond.to(torch.bfloat16)
|
||||
if img_cond.shape[0] == 1 and bs > 1:
|
||||
img_cond = repeat(img_cond, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
img = rearrange(img, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if img.shape[0] == 1 and bs > 1:
|
||||
img = repeat(img, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
img_ids = torch.zeros(h // 2, w // 2, 3)
|
||||
img_ids[..., 1] = img_ids[..., 1] + torch.arange(h // 2)[:, None]
|
||||
img_ids[..., 2] = img_ids[..., 2] + torch.arange(w // 2)[None, :]
|
||||
img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
|
||||
|
||||
if isinstance(prompt, str):
|
||||
prompt = [prompt]
|
||||
txt = t5(prompt)
|
||||
txt = torch.cat((txt, img_cond.to(txt)), dim=-2)
|
||||
if txt.shape[0] == 1 and bs > 1:
|
||||
txt = repeat(txt, "1 ... -> bs ...", bs=bs)
|
||||
txt_ids = torch.zeros(bs, txt.shape[1], 3)
|
||||
|
||||
vec = clip(prompt)
|
||||
if vec.shape[0] == 1 and bs > 1:
|
||||
vec = repeat(vec, "1 ... -> bs ...", bs=bs)
|
||||
|
||||
return {
|
||||
"img": img,
|
||||
"img_ids": img_ids.to(img.device),
|
||||
"txt": txt.to(img.device),
|
||||
"txt_ids": txt_ids.to(img.device),
|
||||
"vec": vec.to(img.device),
|
||||
}
|
||||
|
||||
|
||||
def prepare_kontext(
|
||||
t5: HFEmbedder,
|
||||
clip: HFEmbedder,
|
||||
prompt: str | list[str],
|
||||
ae: AutoEncoder,
|
||||
img_cond_list: list,
|
||||
seed: int,
|
||||
device: torch.device,
|
||||
target_width: int | None = None,
|
||||
target_height: int | None = None,
|
||||
bs: int = 1,
|
||||
) -> tuple[dict[str, Tensor], int, int]:
|
||||
# load and encode the conditioning image
|
||||
if bs == 1 and not isinstance(prompt, str):
|
||||
bs = len(prompt)
|
||||
|
||||
img_cond_seq = None
|
||||
img_cond_seq_ids = None
|
||||
|
||||
for cond_no, img_cond in enumerate(img_cond_list):
|
||||
width, height = img_cond.size
|
||||
aspect_ratio = width / height
|
||||
|
||||
# Kontext is trained on specific resolutions, using one of them is recommended
|
||||
_, width, height = min((abs(aspect_ratio - w / h), w, h) for w, h in PREFERED_KONTEXT_RESOLUTIONS)
|
||||
|
||||
width = 2 * int(width / 16)
|
||||
height = 2 * int(height / 16)
|
||||
|
||||
img_cond = img_cond.resize((8 * width, 8 * height), Image.Resampling.LANCZOS)
|
||||
img_cond = np.array(img_cond)
|
||||
img_cond = torch.from_numpy(img_cond).float() / 127.5 - 1.0
|
||||
img_cond = rearrange(img_cond, "h w c -> 1 c h w")
|
||||
with torch.no_grad():
|
||||
img_cond_latents = ae.encode(img_cond.to(device))
|
||||
|
||||
img_cond_latents = img_cond_latents.to(torch.bfloat16)
|
||||
img_cond_latents = rearrange(img_cond_latents, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=2, pw=2)
|
||||
if img_cond.shape[0] == 1 and bs > 1:
|
||||
img_cond_latents = repeat(img_cond_latents, "1 ... -> bs ...", bs=bs)
|
||||
img_cond = None
|
||||
|
||||
# image ids are the same as base image with the first dimension set to 1
|
||||
# instead of 0
|
||||
img_cond_ids = torch.zeros(height // 2, width // 2, 3)
|
||||
img_cond_ids[..., 0] = cond_no + 1
|
||||
img_cond_ids[..., 1] = img_cond_ids[..., 1] + torch.arange(height // 2)[:, None]
|
||||
img_cond_ids[..., 2] = img_cond_ids[..., 2] + torch.arange(width // 2)[None, :]
|
||||
img_cond_ids = repeat(img_cond_ids, "h w c -> b (h w) c", b=bs)
|
||||
|
||||
if target_width is None:
|
||||
target_width = 8 * width
|
||||
if target_height is None:
|
||||
target_height = 8 * height
|
||||
img_cond_ids = img_cond_ids.to(device)
|
||||
if cond_no == 0:
|
||||
img_cond_seq, img_cond_seq_ids = img_cond_latents, img_cond_ids
|
||||
else:
|
||||
img_cond_seq, img_cond_seq_ids = torch.cat([img_cond_seq, img_cond_latents], dim=1), torch.cat([img_cond_seq_ids, img_cond_ids], dim=1)
|
||||
|
||||
img = get_noise(
|
||||
bs,
|
||||
target_height,
|
||||
target_width,
|
||||
device=device,
|
||||
dtype=torch.bfloat16,
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
return_dict = prepare(t5, clip, img, prompt)
|
||||
return_dict["img_cond_seq"] = img_cond_seq
|
||||
return_dict["img_cond_seq_ids"] = img_cond_seq_ids
|
||||
return return_dict, target_height, target_width
|
||||
|
||||
|
||||
def time_shift(mu: float, sigma: float, t: Tensor):
|
||||
return math.exp(mu) / (math.exp(mu) + (1 / t - 1) ** sigma)
|
||||
|
||||
|
||||
def get_lin_function(
|
||||
x1: float = 256, y1: float = 0.5, x2: float = 4096, y2: float = 1.15
|
||||
) -> Callable[[float], float]:
|
||||
m = (y2 - y1) / (x2 - x1)
|
||||
b = y1 - m * x1
|
||||
return lambda x: m * x + b
|
||||
|
||||
|
||||
def get_schedule(
|
||||
num_steps: int,
|
||||
image_seq_len: int,
|
||||
base_shift: float = 0.5,
|
||||
max_shift: float = 1.15,
|
||||
shift: bool = True,
|
||||
) -> list[float]:
|
||||
# extra step for zero
|
||||
timesteps = torch.linspace(1, 0, num_steps + 1)
|
||||
|
||||
# shifting the schedule to favor high timesteps for higher signal images
|
||||
if shift:
|
||||
# estimate mu based on linear estimation between two points
|
||||
mu = get_lin_function(y1=base_shift, y2=max_shift)(image_seq_len)
|
||||
timesteps = time_shift(mu, 1.0, timesteps)
|
||||
|
||||
return timesteps.tolist()
|
||||
|
||||
|
||||
def denoise(
|
||||
model: Flux,
|
||||
# model input
|
||||
img: Tensor,
|
||||
img_ids: Tensor,
|
||||
txt: Tensor,
|
||||
txt_ids: Tensor,
|
||||
vec: Tensor,
|
||||
# sampling parameters
|
||||
timesteps: list[float],
|
||||
guidance: float = 4.0,
|
||||
# extra img tokens (channel-wise)
|
||||
img_cond: Tensor | None = None,
|
||||
# extra img tokens (sequence-wise)
|
||||
img_cond_seq: Tensor | None = None,
|
||||
img_cond_seq_ids: Tensor | None = None,
|
||||
callback=None,
|
||||
pipeline=None,
|
||||
loras_slists=None,
|
||||
unpack_latent = None,
|
||||
):
|
||||
|
||||
kwargs = {'pipeline': pipeline, 'callback': callback}
|
||||
if callback != None:
|
||||
callback(-1, None, True)
|
||||
|
||||
updated_num_steps= len(timesteps) -1
|
||||
if callback != None:
|
||||
from wgp import update_loras_slists
|
||||
update_loras_slists(model, loras_slists, updated_num_steps)
|
||||
callback(-1, None, True, override_num_inference_steps = updated_num_steps)
|
||||
from mmgp import offload
|
||||
# this is ignored for schnell
|
||||
guidance_vec = torch.full((img.shape[0],), guidance, device=img.device, dtype=img.dtype)
|
||||
for i, (t_curr, t_prev) in enumerate(zip(timesteps[:-1], timesteps[1:])):
|
||||
offload.set_step_no_for_lora(model, i)
|
||||
if pipeline._interrupt:
|
||||
return None
|
||||
|
||||
t_vec = torch.full((img.shape[0],), t_curr, dtype=img.dtype, device=img.device)
|
||||
img_input = img
|
||||
img_input_ids = img_ids
|
||||
if img_cond is not None:
|
||||
img_input = torch.cat((img, img_cond), dim=-1)
|
||||
if img_cond_seq is not None:
|
||||
assert (
|
||||
img_cond_seq_ids is not None
|
||||
), "You need to provide either both or neither of the sequence conditioning"
|
||||
img_input = torch.cat((img_input, img_cond_seq), dim=1)
|
||||
img_input_ids = torch.cat((img_input_ids, img_cond_seq_ids), dim=1)
|
||||
pred = model(
|
||||
img=img_input,
|
||||
img_ids=img_input_ids,
|
||||
txt=txt,
|
||||
txt_ids=txt_ids,
|
||||
y=vec,
|
||||
timesteps=t_vec,
|
||||
guidance=guidance_vec,
|
||||
**kwargs
|
||||
)
|
||||
if pred == None: return None
|
||||
|
||||
if img_input_ids is not None:
|
||||
pred = pred[:, : img.shape[1]]
|
||||
|
||||
img += (t_prev - t_curr) * pred
|
||||
if callback is not None:
|
||||
preview = unpack_latent(img).transpose(0,1)
|
||||
callback(i, preview, False)
|
||||
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def unpack(x: Tensor, height: int, width: int) -> Tensor:
|
||||
return rearrange(
|
||||
x,
|
||||
"b (h w) (c ph pw) -> b c (h ph) (w pw)",
|
||||
h=math.ceil(height / 16),
|
||||
w=math.ceil(width / 16),
|
||||
ph=2,
|
||||
pw=2,
|
||||
)
|
||||
702
flux/util.py
702
flux/util.py
@@ -1,702 +0,0 @@
|
||||
import getpass
|
||||
import math
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
import torch
|
||||
from einops import rearrange
|
||||
from huggingface_hub import hf_hub_download, login
|
||||
from PIL import ExifTags, Image
|
||||
from safetensors.torch import load_file as load_sft
|
||||
|
||||
from flux.model import Flux, FluxLoraWrapper, FluxParams
|
||||
from flux.modules.autoencoder import AutoEncoder, AutoEncoderParams
|
||||
from flux.modules.conditioner import HFEmbedder
|
||||
|
||||
CHECKPOINTS_DIR = Path("checkpoints")
|
||||
CHECKPOINTS_DIR.mkdir(exist_ok=True)
|
||||
BFL_API_KEY = os.getenv("BFL_API_KEY")
|
||||
|
||||
os.environ.setdefault("TRT_ENGINE_DIR", str(CHECKPOINTS_DIR / "trt_engines"))
|
||||
(CHECKPOINTS_DIR / "trt_engines").mkdir(exist_ok=True)
|
||||
|
||||
|
||||
def ensure_hf_auth():
|
||||
hf_token = os.environ.get("HF_TOKEN")
|
||||
if hf_token:
|
||||
print("Trying to authenticate to HuggingFace with the HF_TOKEN environment variable.")
|
||||
try:
|
||||
login(token=hf_token)
|
||||
print("Successfully authenticated with HuggingFace using HF_TOKEN")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to authenticate with HF_TOKEN: {e}")
|
||||
|
||||
if os.path.exists(os.path.expanduser("~/.cache/huggingface/token")):
|
||||
print("Already authenticated with HuggingFace")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def prompt_for_hf_auth():
|
||||
try:
|
||||
token = getpass.getpass("HF Token (hidden input): ").strip()
|
||||
if not token:
|
||||
print("No token provided. Aborting.")
|
||||
return False
|
||||
|
||||
login(token=token)
|
||||
print("Successfully authenticated!")
|
||||
return True
|
||||
except KeyboardInterrupt:
|
||||
print("\nAuthentication cancelled by user.")
|
||||
return False
|
||||
except Exception as auth_e:
|
||||
print(f"Authentication failed: {auth_e}")
|
||||
print("Tip: You can also run 'huggingface-cli login' or set HF_TOKEN environment variable")
|
||||
return False
|
||||
|
||||
|
||||
def get_checkpoint_path(repo_id: str, filename: str, env_var: str) -> Path:
|
||||
"""Get the local path for a checkpoint file, downloading if necessary."""
|
||||
# if os.environ.get(env_var) is not None:
|
||||
# local_path = os.environ[env_var]
|
||||
# if os.path.exists(local_path):
|
||||
# return Path(local_path)
|
||||
|
||||
# print(
|
||||
# f"Trying to load model {repo_id}, {filename} from environment "
|
||||
# f"variable {env_var}. But file {local_path} does not exist. "
|
||||
# "Falling back to default location."
|
||||
# )
|
||||
|
||||
# # Create a safe directory name from repo_id
|
||||
# safe_repo_name = repo_id.replace("/", "_")
|
||||
# checkpoint_dir = CHECKPOINTS_DIR / safe_repo_name
|
||||
# checkpoint_dir.mkdir(exist_ok=True)
|
||||
|
||||
# local_path = checkpoint_dir / filename
|
||||
|
||||
local_path = filename
|
||||
from mmgp import offload
|
||||
|
||||
if False:
|
||||
print(f"Downloading {filename} from {repo_id} to {local_path}")
|
||||
try:
|
||||
ensure_hf_auth()
|
||||
hf_hub_download(repo_id=repo_id, filename=filename, local_dir=checkpoint_dir)
|
||||
except Exception as e:
|
||||
if "gated repo" in str(e).lower() or "restricted" in str(e).lower():
|
||||
print(f"\nError: Cannot access {repo_id} -- this is a gated repository.")
|
||||
|
||||
# Try one more time to authenticate
|
||||
if prompt_for_hf_auth():
|
||||
# Retry the download after authentication
|
||||
print("Retrying download...")
|
||||
hf_hub_download(repo_id=repo_id, filename=filename, local_dir=checkpoint_dir)
|
||||
else:
|
||||
print("Authentication failed or cancelled.")
|
||||
print("You can also run 'huggingface-cli login' or set HF_TOKEN environment variable")
|
||||
raise RuntimeError(f"Authentication required for {repo_id}")
|
||||
else:
|
||||
raise e
|
||||
|
||||
return local_path
|
||||
|
||||
|
||||
def download_onnx_models_for_trt(model_name: str, trt_transformer_precision: str = "bf16") -> str | None:
|
||||
"""Download ONNX models for TRT to our checkpoints directory"""
|
||||
onnx_repo_map = {
|
||||
"flux-dev": "black-forest-labs/FLUX.1-dev-onnx",
|
||||
"flux-schnell": "black-forest-labs/FLUX.1-schnell-onnx",
|
||||
"flux-dev-canny": "black-forest-labs/FLUX.1-Canny-dev-onnx",
|
||||
"flux-dev-depth": "black-forest-labs/FLUX.1-Depth-dev-onnx",
|
||||
"flux-dev-redux": "black-forest-labs/FLUX.1-Redux-dev-onnx",
|
||||
"flux-dev-fill": "black-forest-labs/FLUX.1-Fill-dev-onnx",
|
||||
"flux-dev-kontext": "black-forest-labs/FLUX.1-Kontext-dev-onnx",
|
||||
}
|
||||
|
||||
if model_name not in onnx_repo_map:
|
||||
return None # No ONNX repository required for this model
|
||||
|
||||
repo_id = onnx_repo_map[model_name]
|
||||
safe_repo_name = repo_id.replace("/", "_")
|
||||
onnx_dir = CHECKPOINTS_DIR / safe_repo_name
|
||||
|
||||
# Map of module names to their ONNX file paths (using specified precision)
|
||||
onnx_file_map = {
|
||||
"clip": "clip.opt/model.onnx",
|
||||
"transformer": f"transformer.opt/{trt_transformer_precision}/model.onnx",
|
||||
"transformer_data": f"transformer.opt/{trt_transformer_precision}/backbone.onnx_data",
|
||||
"t5": "t5.opt/model.onnx",
|
||||
"t5_data": "t5.opt/backbone.onnx_data",
|
||||
"vae": "vae.opt/model.onnx",
|
||||
}
|
||||
|
||||
# If all files exist locally, return the custom_onnx_paths format
|
||||
if onnx_dir.exists():
|
||||
all_files_exist = True
|
||||
custom_paths = []
|
||||
for module, onnx_file in onnx_file_map.items():
|
||||
if module.endswith("_data"):
|
||||
continue # Skip data files
|
||||
local_path = onnx_dir / onnx_file
|
||||
if not local_path.exists():
|
||||
all_files_exist = False
|
||||
break
|
||||
custom_paths.append(f"{module}:{local_path}")
|
||||
|
||||
if all_files_exist:
|
||||
print(f"ONNX models ready in {onnx_dir}")
|
||||
return ",".join(custom_paths)
|
||||
|
||||
# If not all files exist, download them
|
||||
print(f"Downloading ONNX models from {repo_id} to {onnx_dir}")
|
||||
print(f"Using transformer precision: {trt_transformer_precision}")
|
||||
onnx_dir.mkdir(exist_ok=True)
|
||||
|
||||
# Download all ONNX files
|
||||
for module, onnx_file in onnx_file_map.items():
|
||||
local_path = onnx_dir / onnx_file
|
||||
if local_path.exists():
|
||||
continue # Already downloaded
|
||||
|
||||
# Create parent directories
|
||||
local_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
try:
|
||||
print(f"Downloading {onnx_file}")
|
||||
hf_hub_download(repo_id=repo_id, filename=onnx_file, local_dir=onnx_dir)
|
||||
except Exception as e:
|
||||
if "does not exist" in str(e).lower() or "not found" in str(e).lower():
|
||||
continue
|
||||
elif "gated repo" in str(e).lower() or "restricted" in str(e).lower():
|
||||
print(f"Cannot access {repo_id} - requires license acceptance")
|
||||
print("Please follow these steps:")
|
||||
print(f" 1. Visit: https://huggingface.co/{repo_id}")
|
||||
print(" 2. Log in to your HuggingFace account")
|
||||
print(" 3. Accept the license terms and conditions")
|
||||
print(" 4. Then retry this command")
|
||||
raise RuntimeError(f"License acceptance required for {model_name}")
|
||||
else:
|
||||
# Re-raise other errors
|
||||
raise
|
||||
|
||||
print(f"ONNX models ready in {onnx_dir}")
|
||||
|
||||
# Return the custom_onnx_paths format that TRT expects: "module1:path1,module2:path2"
|
||||
# Note: Only return the actual module paths, not the data file
|
||||
custom_paths = []
|
||||
for module, onnx_file in onnx_file_map.items():
|
||||
if module.endswith("_data"):
|
||||
continue # Skip the data file in the return paths
|
||||
full_path = onnx_dir / onnx_file
|
||||
if full_path.exists():
|
||||
custom_paths.append(f"{module}:{full_path}")
|
||||
|
||||
return ",".join(custom_paths)
|
||||
|
||||
|
||||
def check_onnx_access_for_trt(model_name: str, trt_transformer_precision: str = "bf16") -> str | None:
|
||||
"""Check ONNX access and download models for TRT - returns ONNX directory path"""
|
||||
return download_onnx_models_for_trt(model_name, trt_transformer_precision)
|
||||
|
||||
|
||||
def track_usage_via_api(name: str, n=1) -> None:
|
||||
"""
|
||||
Track usage of licensed models via the BFL API for commercial licensing compliance.
|
||||
|
||||
For more information on licensing BFL's models for commercial use and usage reporting,
|
||||
see the README.md or visit: https://dashboard.bfl.ai/licensing/subscriptions?showInstructions=true
|
||||
"""
|
||||
assert BFL_API_KEY is not None, "BFL_API_KEY is not set"
|
||||
|
||||
model_slug_map = {
|
||||
"flux-dev": "flux-1-dev",
|
||||
"flux-dev-kontext": "flux-1-kontext-dev",
|
||||
"flux-dev-fill": "flux-tools",
|
||||
"flux-dev-depth": "flux-tools",
|
||||
"flux-dev-canny": "flux-tools",
|
||||
"flux-dev-canny-lora": "flux-tools",
|
||||
"flux-dev-depth-lora": "flux-tools",
|
||||
"flux-dev-redux": "flux-tools",
|
||||
}
|
||||
|
||||
if name not in model_slug_map:
|
||||
print(f"Skipping tracking usage for {name}, as it cannot be tracked. Please check the model name.")
|
||||
return
|
||||
|
||||
model_slug = model_slug_map[name]
|
||||
url = f"https://api.bfl.ai/v1/licenses/models/{model_slug}/usage"
|
||||
headers = {"x-key": BFL_API_KEY, "Content-Type": "application/json"}
|
||||
payload = {"number_of_generations": n}
|
||||
|
||||
response = requests.post(url, headers=headers, json=payload)
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Failed to track usage: {response.status_code} {response.text}")
|
||||
else:
|
||||
print(f"Successfully tracked usage for {name} with {n} generations")
|
||||
|
||||
|
||||
def save_image(
|
||||
nsfw_classifier,
|
||||
name: str,
|
||||
output_name: str,
|
||||
idx: int,
|
||||
x: torch.Tensor,
|
||||
add_sampling_metadata: bool,
|
||||
prompt: str,
|
||||
nsfw_threshold: float = 0.85,
|
||||
track_usage: bool = False,
|
||||
) -> int:
|
||||
fn = output_name.format(idx=idx)
|
||||
print(f"Saving {fn}")
|
||||
# bring into PIL format and save
|
||||
x = x.clamp(-1, 1)
|
||||
x = rearrange(x[0], "c h w -> h w c")
|
||||
img = Image.fromarray((127.5 * (x + 1.0)).cpu().byte().numpy())
|
||||
|
||||
if nsfw_classifier is not None:
|
||||
nsfw_score = [x["score"] for x in nsfw_classifier(img) if x["label"] == "nsfw"][0]
|
||||
else:
|
||||
nsfw_score = nsfw_threshold - 1.0
|
||||
|
||||
if nsfw_score < nsfw_threshold:
|
||||
exif_data = Image.Exif()
|
||||
if name in ["flux-dev", "flux-schnell"]:
|
||||
exif_data[ExifTags.Base.Software] = "AI generated;txt2img;flux"
|
||||
else:
|
||||
exif_data[ExifTags.Base.Software] = "AI generated;img2img;flux"
|
||||
exif_data[ExifTags.Base.Make] = "Black Forest Labs"
|
||||
exif_data[ExifTags.Base.Model] = name
|
||||
if add_sampling_metadata:
|
||||
exif_data[ExifTags.Base.ImageDescription] = prompt
|
||||
img.save(fn, exif=exif_data, quality=95, subsampling=0)
|
||||
if track_usage:
|
||||
track_usage_via_api(name, 1)
|
||||
idx += 1
|
||||
else:
|
||||
print("Your generated image may contain NSFW content.")
|
||||
|
||||
return idx
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModelSpec:
|
||||
params: FluxParams
|
||||
ae_params: AutoEncoderParams
|
||||
repo_id: str
|
||||
repo_flow: str
|
||||
repo_ae: str
|
||||
lora_repo_id: str | None = None
|
||||
lora_filename: str | None = None
|
||||
|
||||
|
||||
configs = {
|
||||
"flux-dev": ModelSpec(
|
||||
repo_id="",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=64,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-schnell": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-schnell",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=64,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=False,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-canny": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-Canny-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=128,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-canny-lora": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
lora_repo_id="black-forest-labs/FLUX.1-Canny-dev-lora",
|
||||
lora_filename="flux1-canny-dev-lora.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=128,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-depth": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-Depth-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=128,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-depth-lora": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
lora_repo_id="black-forest-labs/FLUX.1-Depth-dev-lora",
|
||||
lora_filename="flux1-depth-dev-lora.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=128,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-redux": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-Redux-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=64,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-fill": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-Fill-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=384,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
"flux-dev-kontext": ModelSpec(
|
||||
repo_id="black-forest-labs/FLUX.1-Kontext-dev",
|
||||
repo_flow="",
|
||||
repo_ae="ckpts/flux_vae.safetensors",
|
||||
params=FluxParams(
|
||||
in_channels=64,
|
||||
out_channels=64,
|
||||
vec_in_dim=768,
|
||||
context_in_dim=4096,
|
||||
hidden_size=3072,
|
||||
mlp_ratio=4.0,
|
||||
num_heads=24,
|
||||
depth=19,
|
||||
depth_single_blocks=38,
|
||||
axes_dim=[16, 56, 56],
|
||||
theta=10_000,
|
||||
qkv_bias=True,
|
||||
guidance_embed=True,
|
||||
),
|
||||
ae_params=AutoEncoderParams(
|
||||
resolution=256,
|
||||
in_channels=3,
|
||||
ch=128,
|
||||
out_ch=3,
|
||||
ch_mult=[1, 2, 4, 4],
|
||||
num_res_blocks=2,
|
||||
z_channels=16,
|
||||
scale_factor=0.3611,
|
||||
shift_factor=0.1159,
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
PREFERED_KONTEXT_RESOLUTIONS = [
|
||||
(672, 1568),
|
||||
(688, 1504),
|
||||
(720, 1456),
|
||||
(752, 1392),
|
||||
(800, 1328),
|
||||
(832, 1248),
|
||||
(880, 1184),
|
||||
(944, 1104),
|
||||
(1024, 1024),
|
||||
(1104, 944),
|
||||
(1184, 880),
|
||||
(1248, 832),
|
||||
(1328, 800),
|
||||
(1392, 752),
|
||||
(1456, 720),
|
||||
(1504, 688),
|
||||
(1568, 672),
|
||||
]
|
||||
|
||||
|
||||
def aspect_ratio_to_height_width(aspect_ratio: str, area: int = 1024**2) -> tuple[int, int]:
|
||||
width = float(aspect_ratio.split(":")[0])
|
||||
height = float(aspect_ratio.split(":")[1])
|
||||
ratio = width / height
|
||||
width = round(math.sqrt(area * ratio))
|
||||
height = round(math.sqrt(area / ratio))
|
||||
return 16 * (width // 16), 16 * (height // 16)
|
||||
|
||||
|
||||
def print_load_warning(missing: list[str], unexpected: list[str]) -> None:
|
||||
if len(missing) > 0 and len(unexpected) > 0:
|
||||
print(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
|
||||
print("\n" + "-" * 79 + "\n")
|
||||
print(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
|
||||
elif len(missing) > 0:
|
||||
print(f"Got {len(missing)} missing keys:\n\t" + "\n\t".join(missing))
|
||||
elif len(unexpected) > 0:
|
||||
print(f"Got {len(unexpected)} unexpected keys:\n\t" + "\n\t".join(unexpected))
|
||||
|
||||
|
||||
def load_flow_model(name: str, model_filename, device: str | torch.device = "cuda", verbose: bool = True) -> Flux:
|
||||
# Loading Flux
|
||||
config = configs[name]
|
||||
|
||||
ckpt_path = model_filename #config.repo_flow
|
||||
|
||||
with torch.device("meta"):
|
||||
if config.lora_repo_id is not None and config.lora_filename is not None:
|
||||
model = FluxLoraWrapper(params=config.params).to(torch.bfloat16)
|
||||
else:
|
||||
model = Flux(config.params).to(torch.bfloat16)
|
||||
|
||||
# print(f"Loading checkpoint: {ckpt_path}")
|
||||
from mmgp import offload
|
||||
offload.load_model_data(model, model_filename )
|
||||
|
||||
# # load_sft doesn't support torch.device
|
||||
# sd = load_sft(ckpt_path, device=str(device))
|
||||
# sd = optionally_expand_state_dict(model, sd)
|
||||
# missing, unexpected = model.load_state_dict(sd, strict=False, assign=True)
|
||||
# if verbose:
|
||||
# print_load_warning(missing, unexpected)
|
||||
|
||||
# if config.lora_repo_id is not None and config.lora_filename is not None:
|
||||
# print("Loading LoRA")
|
||||
# lora_path = str(get_checkpoint_path(config.lora_repo_id, config.lora_filename, "FLUX_LORA"))
|
||||
# lora_sd = load_sft(lora_path, device=str(device))
|
||||
# # loading the lora params + overwriting scale values in the norms
|
||||
# missing, unexpected = model.load_state_dict(lora_sd, strict=False, assign=True)
|
||||
# if verbose:
|
||||
# print_load_warning(missing, unexpected)
|
||||
return model
|
||||
|
||||
|
||||
def load_t5(device: str | torch.device = "cuda", text_encoder_filename = None, max_length: int = 512) -> HFEmbedder:
|
||||
# max length 64, 128, 256 and 512 should work (if your sequence is short enough)
|
||||
return HFEmbedder("",text_encoder_filename, max_length=max_length, torch_dtype=torch.bfloat16).to(device)
|
||||
|
||||
|
||||
def load_clip(device: str | torch.device = "cuda") -> HFEmbedder:
|
||||
return HFEmbedder("ckpts/clip_vit_large_patch14", "", max_length=77, torch_dtype=torch.bfloat16, is_clip =True).to(device)
|
||||
|
||||
|
||||
def load_ae(name: str, device: str | torch.device = "cuda") -> AutoEncoder:
|
||||
config = configs[name]
|
||||
ckpt_path = str(get_checkpoint_path(config.repo_id, config.repo_ae, "FLUX_AE"))
|
||||
|
||||
# Loading the autoencoder
|
||||
with torch.device("meta"):
|
||||
ae = AutoEncoder(config.ae_params)
|
||||
|
||||
# print(f"Loading AE checkpoint: {ckpt_path}")
|
||||
sd = load_sft(ckpt_path, device=str(device))
|
||||
missing, unexpected = ae.load_state_dict(sd, strict=False, assign=True)
|
||||
print_load_warning(missing, unexpected)
|
||||
return ae
|
||||
|
||||
|
||||
def optionally_expand_state_dict(model: torch.nn.Module, state_dict: dict) -> dict:
|
||||
"""
|
||||
Optionally expand the state dict to match the model's parameters shapes.
|
||||
"""
|
||||
for name, param in model.named_parameters():
|
||||
if name in state_dict:
|
||||
if state_dict[name].shape != param.shape:
|
||||
print(
|
||||
f"Expanding '{name}' with shape {state_dict[name].shape} to model parameter with shape {param.shape}."
|
||||
)
|
||||
# expand with zeros:
|
||||
expanded_state_dict_weight = torch.zeros_like(param, device=state_dict[name].device)
|
||||
slices = tuple(slice(0, dim) for dim in state_dict[name].shape)
|
||||
expanded_state_dict_weight[slices] = state_dict[name]
|
||||
state_dict[name] = expanded_state_dict_weight
|
||||
|
||||
return state_dict
|
||||
|
||||
|
||||
306
gradio/i2v_14B_singleGPU.py
Normal file
306
gradio/i2v_14B_singleGPU.py
Normal file
@@ -0,0 +1,306 @@
|
||||
# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
|
||||
import argparse
|
||||
import gc
|
||||
import os.path as osp
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import gradio as gr
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
# Model
|
||||
sys.path.insert(0, os.path.sep.join(osp.realpath(__file__).split(os.path.sep)[:-2]))
|
||||
import wan
|
||||
from wan.configs import MAX_AREA_CONFIGS, WAN_CONFIGS
|
||||
from wan.utils.prompt_extend import DashScopePromptExpander, QwenPromptExpander
|
||||
from wan.utils.utils import cache_video
|
||||
|
||||
# Global Var
|
||||
prompt_expander = None
|
||||
wan_i2v_480P = None
|
||||
wan_i2v_720P = None
|
||||
|
||||
|
||||
# Button Func
|
||||
def load_i2v_model(value):
|
||||
global wan_i2v_480P, wan_i2v_720P
|
||||
from mmgp import offload
|
||||
|
||||
if value == '------':
|
||||
print("No model loaded")
|
||||
return '------'
|
||||
|
||||
if value == '720P':
|
||||
if args.ckpt_dir_720p is None:
|
||||
print("Please specify the checkpoint directory for 720P model")
|
||||
return '------'
|
||||
if wan_i2v_720P is not None:
|
||||
pass
|
||||
else:
|
||||
del wan_i2v_480P
|
||||
gc.collect()
|
||||
wan_i2v_480P = None
|
||||
|
||||
print("load 14B-720P i2v model...", end='', flush=True)
|
||||
cfg = WAN_CONFIGS['i2v-14B']
|
||||
wan_i2v_720P = wan.WanI2V(
|
||||
config=cfg,
|
||||
checkpoint_dir=args.ckpt_dir_720p,
|
||||
device_id=0,
|
||||
rank=0,
|
||||
t5_fsdp=False,
|
||||
dit_fsdp=False,
|
||||
use_usp=False,
|
||||
i2v720p= True
|
||||
)
|
||||
print("done", flush=True)
|
||||
pipe = {"transformer": wan_i2v_720P.model, "text_encoder" : wan_i2v_720P.text_encoder.model, "text_encoder_2": wan_i2v_720P.clip.model, "vae": wan_i2v_720P.vae.model } #
|
||||
offload.profile(pipe, profile_no=4, budgets = {"transformer":100, "*":3000}, verboseLevel=2, compile="transformer", quantizeTransformer = False, pinnedMemory = False)
|
||||
return '720P'
|
||||
|
||||
if value == '480P':
|
||||
if args.ckpt_dir_480p is None:
|
||||
print("Please specify the checkpoint directory for 480P model")
|
||||
return '------'
|
||||
if wan_i2v_480P is not None:
|
||||
pass
|
||||
else:
|
||||
del wan_i2v_720P
|
||||
gc.collect()
|
||||
wan_i2v_720P = None
|
||||
|
||||
print("load 14B-480P i2v model...", end='', flush=True)
|
||||
cfg = WAN_CONFIGS['i2v-14B']
|
||||
wan_i2v_480P = wan.WanI2V(
|
||||
config=cfg,
|
||||
checkpoint_dir=args.ckpt_dir_480p,
|
||||
device_id=0,
|
||||
rank=0,
|
||||
t5_fsdp=False,
|
||||
dit_fsdp=False,
|
||||
use_usp=False,
|
||||
i2v720p= False
|
||||
)
|
||||
print("done", flush=True)
|
||||
pipe = {"transformer": wan_i2v_480P.model, "text_encoder" : wan_i2v_480P.text_encoder.model, "text_encoder_2": wan_i2v_480P.clip.model, "vae": wan_i2v_480P.vae.model } #
|
||||
offload.profile(pipe, profile_no=4, budgets = {"model":100, "*":3000}, verboseLevel=2, compile="transformer" )
|
||||
|
||||
return '480P'
|
||||
|
||||
|
||||
|
||||
def prompt_enc(prompt, img, tar_lang):
|
||||
print('prompt extend...')
|
||||
if img is None:
|
||||
print('Please upload an image')
|
||||
return prompt
|
||||
global prompt_expander
|
||||
prompt_output = prompt_expander(
|
||||
prompt, image=img, tar_lang=tar_lang.lower())
|
||||
if prompt_output.status == False:
|
||||
return prompt
|
||||
else:
|
||||
return prompt_output.prompt
|
||||
|
||||
|
||||
def i2v_generation(img2vid_prompt, img2vid_image, res, sd_steps,
|
||||
guide_scale, shift_scale, seed, n_prompt):
|
||||
# print(f"{img2vid_prompt},{resolution},{sd_steps},{guide_scale},{shift_scale},{seed},{n_prompt}")
|
||||
global resolution
|
||||
from PIL import Image
|
||||
img2vid_image = Image.open("d:\mammoth2.jpg")
|
||||
if resolution == '------':
|
||||
print(
|
||||
'Please specify at least one resolution ckpt dir or specify the resolution'
|
||||
)
|
||||
return None
|
||||
|
||||
else:
|
||||
if resolution == '720P':
|
||||
global wan_i2v_720P
|
||||
video = wan_i2v_720P.generate(
|
||||
img2vid_prompt,
|
||||
img2vid_image,
|
||||
max_area=MAX_AREA_CONFIGS['720*1280'],
|
||||
shift=shift_scale,
|
||||
sampling_steps=sd_steps,
|
||||
guide_scale=guide_scale,
|
||||
n_prompt=n_prompt,
|
||||
seed=seed,
|
||||
offload_model=False)
|
||||
else:
|
||||
global wan_i2v_480P
|
||||
video = wan_i2v_480P.generate(
|
||||
img2vid_prompt,
|
||||
img2vid_image,
|
||||
max_area=MAX_AREA_CONFIGS['480*832'],
|
||||
shift=3.0, #shift_scale
|
||||
sampling_steps=sd_steps,
|
||||
guide_scale=guide_scale,
|
||||
n_prompt=n_prompt,
|
||||
seed=seed,
|
||||
offload_model=False)
|
||||
|
||||
cache_video(
|
||||
tensor=video[None],
|
||||
save_file="example.mp4",
|
||||
fps=16,
|
||||
nrow=1,
|
||||
normalize=True,
|
||||
value_range=(-1, 1))
|
||||
|
||||
return "example.mp4"
|
||||
|
||||
|
||||
# Interface
|
||||
def gradio_interface():
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown("""
|
||||
<div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
|
||||
Wan2.1 (I2V-14B)
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 16px; font-weight: normal; margin-bottom: 20px;">
|
||||
Wan: Open and Advanced Large-Scale Video Generative Models.
|
||||
</div>
|
||||
""")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
resolution = gr.Dropdown(
|
||||
label='Resolution',
|
||||
choices=['------', '720P', '480P'],
|
||||
value='------')
|
||||
|
||||
img2vid_image = gr.Image(
|
||||
type="pil",
|
||||
label="Upload Input Image",
|
||||
elem_id="image_upload",
|
||||
)
|
||||
img2vid_prompt = gr.Textbox(
|
||||
label="Prompt",
|
||||
value="Several giant wooly mammoths approach treading through a snowy meadow, their long wooly fur lightly blows in the wind as they walk, snow covered trees and dramatic snow capped mountains in the distance, mid afternoon light with wispy clouds and a sun high in the distance creates a warm glow, the low camera view is stunning capturing the large furry mammal with beautiful photography, depth of field.",
|
||||
placeholder="Describe the video you want to generate",
|
||||
)
|
||||
tar_lang = gr.Radio(
|
||||
choices=["CH", "EN"],
|
||||
label="Target language of prompt enhance",
|
||||
value="CH")
|
||||
run_p_button = gr.Button(value="Prompt Enhance")
|
||||
|
||||
with gr.Accordion("Advanced Options", open=True):
|
||||
with gr.Row():
|
||||
sd_steps = gr.Slider(
|
||||
label="Diffusion steps",
|
||||
minimum=1,
|
||||
maximum=1000,
|
||||
value=50,
|
||||
step=1)
|
||||
guide_scale = gr.Slider(
|
||||
label="Guide scale",
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=5.0,
|
||||
step=1)
|
||||
with gr.Row():
|
||||
shift_scale = gr.Slider(
|
||||
label="Shift scale",
|
||||
minimum=0,
|
||||
maximum=10,
|
||||
value=5.0,
|
||||
step=1)
|
||||
seed = gr.Slider(
|
||||
label="Seed",
|
||||
minimum=-1,
|
||||
maximum=2147483647,
|
||||
step=1,
|
||||
value=-1)
|
||||
n_prompt = gr.Textbox(
|
||||
label="Negative Prompt",
|
||||
placeholder="Describe the negative prompt you want to add"
|
||||
)
|
||||
|
||||
run_i2v_button = gr.Button("Generate Video")
|
||||
|
||||
with gr.Column():
|
||||
result_gallery = gr.Video(
|
||||
label='Generated Video', interactive=False, height=600)
|
||||
|
||||
resolution.input(
|
||||
fn=load_model, inputs=[resolution], outputs=[resolution])
|
||||
|
||||
run_p_button.click(
|
||||
fn=prompt_enc,
|
||||
inputs=[img2vid_prompt, img2vid_image, tar_lang],
|
||||
outputs=[img2vid_prompt])
|
||||
|
||||
run_i2v_button.click(
|
||||
fn=i2v_generation,
|
||||
inputs=[
|
||||
img2vid_prompt, img2vid_image, resolution, sd_steps,
|
||||
guide_scale, shift_scale, seed, n_prompt
|
||||
],
|
||||
outputs=[result_gallery],
|
||||
)
|
||||
|
||||
return demo
|
||||
|
||||
|
||||
# Main
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a video from a text prompt or image using Gradio")
|
||||
parser.add_argument(
|
||||
"--ckpt_dir_720p",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The path to the checkpoint directory.")
|
||||
parser.add_argument(
|
||||
"--ckpt_dir_480p",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The path to the checkpoint directory.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_method",
|
||||
type=str,
|
||||
default="local_qwen",
|
||||
choices=["dashscope", "local_qwen"],
|
||||
help="The prompt extend method to use.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_model",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The prompt extend model to use.")
|
||||
|
||||
args = parser.parse_args()
|
||||
args.ckpt_dir_720p = "../ckpts" # os.path.join("ckpt")
|
||||
args.ckpt_dir_480p = "../ckpts" # os.path.join("ckpt")
|
||||
assert args.ckpt_dir_720p is not None or args.ckpt_dir_480p is not None, "Please specify at least one checkpoint directory."
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _parse_args()
|
||||
global resolution
|
||||
# load_model('720P')
|
||||
# resolution = '720P'
|
||||
resolution = '480P'
|
||||
|
||||
load_i2v_model(resolution)
|
||||
|
||||
print("Step1: Init prompt_expander...", end='', flush=True)
|
||||
if args.prompt_extend_method == "dashscope":
|
||||
prompt_expander = DashScopePromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=True)
|
||||
elif args.prompt_extend_method == "local_qwen":
|
||||
prompt_expander = QwenPromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=True, device=0)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"Unsupport prompt_extend_method: {args.prompt_extend_method}")
|
||||
print("done", flush=True)
|
||||
|
||||
demo = gradio_interface()
|
||||
demo.launch(server_name="0.0.0.0", share=False, server_port=7860)
|
||||
206
gradio/t2i_14B_singleGPU.py
Normal file
206
gradio/t2i_14B_singleGPU.py
Normal file
@@ -0,0 +1,206 @@
|
||||
# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
|
||||
import argparse
|
||||
import os.path as osp
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import gradio as gr
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
# Model
|
||||
sys.path.insert(0, os.path.sep.join(osp.realpath(__file__).split(os.path.sep)[:-2]))
|
||||
import wan
|
||||
from wan.configs import WAN_CONFIGS
|
||||
from wan.utils.prompt_extend import DashScopePromptExpander, QwenPromptExpander
|
||||
from wan.utils.utils import cache_image
|
||||
|
||||
# Global Var
|
||||
prompt_expander = None
|
||||
wan_t2i = None
|
||||
|
||||
|
||||
# Button Func
|
||||
def prompt_enc(prompt, tar_lang):
|
||||
global prompt_expander
|
||||
prompt_output = prompt_expander(prompt, tar_lang=tar_lang.lower())
|
||||
if prompt_output.status == False:
|
||||
return prompt
|
||||
else:
|
||||
return prompt_output.prompt
|
||||
|
||||
|
||||
def t2i_generation(txt2img_prompt, resolution, sd_steps, guide_scale,
|
||||
shift_scale, seed, n_prompt):
|
||||
global wan_t2i
|
||||
# print(f"{txt2img_prompt},{resolution},{sd_steps},{guide_scale},{shift_scale},{seed},{n_prompt}")
|
||||
|
||||
W = int(resolution.split("*")[0])
|
||||
H = int(resolution.split("*")[1])
|
||||
video = wan_t2i.generate(
|
||||
txt2img_prompt,
|
||||
size=(W, H),
|
||||
frame_num=1,
|
||||
shift=shift_scale,
|
||||
sampling_steps=sd_steps,
|
||||
guide_scale=guide_scale,
|
||||
n_prompt=n_prompt,
|
||||
seed=seed,
|
||||
offload_model=True)
|
||||
|
||||
cache_image(
|
||||
tensor=video.squeeze(1)[None],
|
||||
save_file="example.png",
|
||||
nrow=1,
|
||||
normalize=True,
|
||||
value_range=(-1, 1))
|
||||
|
||||
return "example.png"
|
||||
|
||||
|
||||
# Interface
|
||||
def gradio_interface():
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown("""
|
||||
<div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
|
||||
Wan2.1 (T2I-14B)
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 16px; font-weight: normal; margin-bottom: 20px;">
|
||||
Wan: Open and Advanced Large-Scale Video Generative Models.
|
||||
</div>
|
||||
""")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
txt2img_prompt = gr.Textbox(
|
||||
label="Prompt",
|
||||
placeholder="Describe the image you want to generate",
|
||||
)
|
||||
tar_lang = gr.Radio(
|
||||
choices=["CH", "EN"],
|
||||
label="Target language of prompt enhance",
|
||||
value="CH")
|
||||
run_p_button = gr.Button(value="Prompt Enhance")
|
||||
|
||||
with gr.Accordion("Advanced Options", open=True):
|
||||
resolution = gr.Dropdown(
|
||||
label='Resolution(Width*Height)',
|
||||
choices=[
|
||||
'720*1280', '1280*720', '960*960', '1088*832',
|
||||
'832*1088', '480*832', '832*480', '624*624',
|
||||
'704*544', '544*704'
|
||||
],
|
||||
value='720*1280')
|
||||
|
||||
with gr.Row():
|
||||
sd_steps = gr.Slider(
|
||||
label="Diffusion steps",
|
||||
minimum=1,
|
||||
maximum=1000,
|
||||
value=50,
|
||||
step=1)
|
||||
guide_scale = gr.Slider(
|
||||
label="Guide scale",
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=5.0,
|
||||
step=1)
|
||||
with gr.Row():
|
||||
shift_scale = gr.Slider(
|
||||
label="Shift scale",
|
||||
minimum=0,
|
||||
maximum=10,
|
||||
value=5.0,
|
||||
step=1)
|
||||
seed = gr.Slider(
|
||||
label="Seed",
|
||||
minimum=-1,
|
||||
maximum=2147483647,
|
||||
step=1,
|
||||
value=-1)
|
||||
n_prompt = gr.Textbox(
|
||||
label="Negative Prompt",
|
||||
placeholder="Describe the negative prompt you want to add"
|
||||
)
|
||||
|
||||
run_t2i_button = gr.Button("Generate Image")
|
||||
|
||||
with gr.Column():
|
||||
result_gallery = gr.Image(
|
||||
label='Generated Image', interactive=False, height=600)
|
||||
|
||||
run_p_button.click(
|
||||
fn=prompt_enc,
|
||||
inputs=[txt2img_prompt, tar_lang],
|
||||
outputs=[txt2img_prompt])
|
||||
|
||||
run_t2i_button.click(
|
||||
fn=t2i_generation,
|
||||
inputs=[
|
||||
txt2img_prompt, resolution, sd_steps, guide_scale, shift_scale,
|
||||
seed, n_prompt
|
||||
],
|
||||
outputs=[result_gallery],
|
||||
)
|
||||
|
||||
return demo
|
||||
|
||||
|
||||
# Main
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a image from a text prompt or image using Gradio")
|
||||
parser.add_argument(
|
||||
"--ckpt_dir",
|
||||
type=str,
|
||||
default="cache",
|
||||
help="The path to the checkpoint directory.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_method",
|
||||
type=str,
|
||||
default="local_qwen",
|
||||
choices=["dashscope", "local_qwen"],
|
||||
help="The prompt extend method to use.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_model",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The prompt extend model to use.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _parse_args()
|
||||
|
||||
print("Step1: Init prompt_expander...", end='', flush=True)
|
||||
if args.prompt_extend_method == "dashscope":
|
||||
prompt_expander = DashScopePromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=False)
|
||||
elif args.prompt_extend_method == "local_qwen":
|
||||
prompt_expander = QwenPromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=False, device=0)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"Unsupport prompt_extend_method: {args.prompt_extend_method}")
|
||||
print("done", flush=True)
|
||||
|
||||
print("Step2: Init 14B t2i model...", end='', flush=True)
|
||||
cfg = WAN_CONFIGS['t2i-14B']
|
||||
# cfg = WAN_CONFIGS['t2v-1.3B']
|
||||
wan_t2i = wan.WanT2V(
|
||||
config=cfg,
|
||||
checkpoint_dir=args.ckpt_dir,
|
||||
device_id=0,
|
||||
rank=0,
|
||||
t5_fsdp=False,
|
||||
dit_fsdp=False,
|
||||
use_usp=False,
|
||||
)
|
||||
print("done", flush=True)
|
||||
|
||||
demo = gradio_interface()
|
||||
demo.launch(server_name="0.0.0.0", share=False, server_port=7860)
|
||||
207
gradio/t2v_1.3B_singleGPU.py
Normal file
207
gradio/t2v_1.3B_singleGPU.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
|
||||
import argparse
|
||||
import os.path as osp
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import gradio as gr
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
# Model
|
||||
sys.path.insert(0, os.path.sep.join(osp.realpath(__file__).split(os.path.sep)[:-2]))
|
||||
import wan
|
||||
from wan.configs import WAN_CONFIGS
|
||||
from wan.utils.prompt_extend import DashScopePromptExpander, QwenPromptExpander
|
||||
from wan.utils.utils import cache_video
|
||||
|
||||
# Global Var
|
||||
prompt_expander = None
|
||||
wan_t2v = None
|
||||
|
||||
|
||||
# Button Func
|
||||
def prompt_enc(prompt, tar_lang):
|
||||
global prompt_expander
|
||||
prompt_output = prompt_expander(prompt, tar_lang=tar_lang.lower())
|
||||
if prompt_output.status == False:
|
||||
return prompt
|
||||
else:
|
||||
return prompt_output.prompt
|
||||
|
||||
|
||||
def t2v_generation(txt2vid_prompt, resolution, sd_steps, guide_scale,
|
||||
shift_scale, seed, n_prompt):
|
||||
global wan_t2v
|
||||
# print(f"{txt2vid_prompt},{resolution},{sd_steps},{guide_scale},{shift_scale},{seed},{n_prompt}")
|
||||
|
||||
W = int(resolution.split("*")[0])
|
||||
H = int(resolution.split("*")[1])
|
||||
video = wan_t2v.generate(
|
||||
txt2vid_prompt,
|
||||
size=(W, H),
|
||||
shift=shift_scale,
|
||||
sampling_steps=sd_steps,
|
||||
guide_scale=guide_scale,
|
||||
n_prompt=n_prompt,
|
||||
seed=seed,
|
||||
offload_model=True)
|
||||
|
||||
cache_video(
|
||||
tensor=video[None],
|
||||
save_file="example.mp4",
|
||||
fps=16,
|
||||
nrow=1,
|
||||
normalize=True,
|
||||
value_range=(-1, 1))
|
||||
|
||||
return "example.mp4"
|
||||
|
||||
|
||||
# Interface
|
||||
def gradio_interface():
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown("""
|
||||
<div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
|
||||
Wan2.1 (T2V-1.3B)
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 16px; font-weight: normal; margin-bottom: 20px;">
|
||||
Wan: Open and Advanced Large-Scale Video Generative Models.
|
||||
</div>
|
||||
""")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
txt2vid_prompt = gr.Textbox(
|
||||
label="Prompt",
|
||||
placeholder="Describe the video you want to generate",
|
||||
)
|
||||
tar_lang = gr.Radio(
|
||||
choices=["CH", "EN"],
|
||||
label="Target language of prompt enhance",
|
||||
value="CH")
|
||||
run_p_button = gr.Button(value="Prompt Enhance")
|
||||
|
||||
with gr.Accordion("Advanced Options", open=True):
|
||||
resolution = gr.Dropdown(
|
||||
label='Resolution(Width*Height)',
|
||||
choices=[
|
||||
'480*832',
|
||||
'832*480',
|
||||
'624*624',
|
||||
'704*544',
|
||||
'544*704',
|
||||
],
|
||||
value='480*832')
|
||||
|
||||
with gr.Row():
|
||||
sd_steps = gr.Slider(
|
||||
label="Diffusion steps",
|
||||
minimum=1,
|
||||
maximum=1000,
|
||||
value=50,
|
||||
step=1)
|
||||
guide_scale = gr.Slider(
|
||||
label="Guide scale",
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=6.0,
|
||||
step=1)
|
||||
with gr.Row():
|
||||
shift_scale = gr.Slider(
|
||||
label="Shift scale",
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=8.0,
|
||||
step=1)
|
||||
seed = gr.Slider(
|
||||
label="Seed",
|
||||
minimum=-1,
|
||||
maximum=2147483647,
|
||||
step=1,
|
||||
value=-1)
|
||||
n_prompt = gr.Textbox(
|
||||
label="Negative Prompt",
|
||||
placeholder="Describe the negative prompt you want to add"
|
||||
)
|
||||
|
||||
run_t2v_button = gr.Button("Generate Video")
|
||||
|
||||
with gr.Column():
|
||||
result_gallery = gr.Video(
|
||||
label='Generated Video', interactive=False, height=600)
|
||||
|
||||
run_p_button.click(
|
||||
fn=prompt_enc,
|
||||
inputs=[txt2vid_prompt, tar_lang],
|
||||
outputs=[txt2vid_prompt])
|
||||
|
||||
run_t2v_button.click(
|
||||
fn=t2v_generation,
|
||||
inputs=[
|
||||
txt2vid_prompt, resolution, sd_steps, guide_scale, shift_scale,
|
||||
seed, n_prompt
|
||||
],
|
||||
outputs=[result_gallery],
|
||||
)
|
||||
|
||||
return demo
|
||||
|
||||
|
||||
# Main
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a video from a text prompt or image using Gradio")
|
||||
parser.add_argument(
|
||||
"--ckpt_dir",
|
||||
type=str,
|
||||
default="cache",
|
||||
help="The path to the checkpoint directory.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_method",
|
||||
type=str,
|
||||
default="local_qwen",
|
||||
choices=["dashscope", "local_qwen"],
|
||||
help="The prompt extend method to use.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_model",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The prompt extend model to use.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _parse_args()
|
||||
|
||||
print("Step1: Init prompt_expander...", end='', flush=True)
|
||||
if args.prompt_extend_method == "dashscope":
|
||||
prompt_expander = DashScopePromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=False)
|
||||
elif args.prompt_extend_method == "local_qwen":
|
||||
prompt_expander = QwenPromptExpander(
|
||||
model_name=args.prompt_extend_model, is_vl=False, device=0)
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
f"Unsupport prompt_extend_method: {args.prompt_extend_method}")
|
||||
print("done", flush=True)
|
||||
|
||||
print("Step2: Init 1.3B t2v model...", end='', flush=True)
|
||||
cfg = WAN_CONFIGS['t2v-1.3B']
|
||||
wan_t2v = wan.WanT2V(
|
||||
config=cfg,
|
||||
checkpoint_dir=args.ckpt_dir,
|
||||
device_id=0,
|
||||
rank=0,
|
||||
t5_fsdp=False,
|
||||
dit_fsdp=False,
|
||||
use_usp=False,
|
||||
)
|
||||
print("done", flush=True)
|
||||
|
||||
demo = gradio_interface()
|
||||
demo.launch(server_name="0.0.0.0", share=False, server_port=7860)
|
||||
216
gradio/t2v_14B_singleGPU.py
Normal file
216
gradio/t2v_14B_singleGPU.py
Normal file
@@ -0,0 +1,216 @@
|
||||
# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
|
||||
import argparse
|
||||
import os.path as osp
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import gradio as gr
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
# Model
|
||||
sys.path.insert(0, os.path.sep.join(osp.realpath(__file__).split(os.path.sep)[:-2]))
|
||||
import wan
|
||||
from wan.configs import WAN_CONFIGS
|
||||
from wan.utils.prompt_extend import DashScopePromptExpander, QwenPromptExpander
|
||||
from wan.utils.utils import cache_video
|
||||
|
||||
# Global Var
|
||||
prompt_expander = None
|
||||
wan_t2v = None
|
||||
|
||||
|
||||
# Button Func
|
||||
def prompt_enc(prompt, tar_lang):
|
||||
global prompt_expander
|
||||
prompt_output = prompt_expander(prompt, tar_lang=tar_lang.lower())
|
||||
if prompt_output.status == False:
|
||||
return prompt
|
||||
else:
|
||||
return prompt_output.prompt
|
||||
|
||||
|
||||
def t2v_generation(txt2vid_prompt, resolution, sd_steps, guide_scale,
|
||||
shift_scale, seed, n_prompt):
|
||||
global wan_t2v
|
||||
# print(f"{txt2vid_prompt},{resolution},{sd_steps},{guide_scale},{shift_scale},{seed},{n_prompt}")
|
||||
|
||||
W = int(resolution.split("*")[0])
|
||||
H = int(resolution.split("*")[1])
|
||||
video = wan_t2v.generate(
|
||||
txt2vid_prompt,
|
||||
size=(W, H),
|
||||
shift=shift_scale,
|
||||
sampling_steps=sd_steps,
|
||||
guide_scale=guide_scale,
|
||||
n_prompt=n_prompt,
|
||||
seed=seed,
|
||||
offload_model=False)
|
||||
|
||||
cache_video(
|
||||
tensor=video[None],
|
||||
save_file="example.mp4",
|
||||
fps=16,
|
||||
nrow=1,
|
||||
normalize=True,
|
||||
value_range=(-1, 1))
|
||||
|
||||
return "example.mp4"
|
||||
|
||||
|
||||
# Interface
|
||||
def gradio_interface():
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown("""
|
||||
<div style="text-align: center; font-size: 32px; font-weight: bold; margin-bottom: 20px;">
|
||||
Wan2.1 (T2V-14B)
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 16px; font-weight: normal; margin-bottom: 20px;">
|
||||
Wan: Open and Advanced Large-Scale Video Generative Models.
|
||||
</div>
|
||||
""")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column():
|
||||
txt2vid_prompt = gr.Textbox(
|
||||
label="Prompt",
|
||||
placeholder="Describe the video you want to generate",
|
||||
)
|
||||
tar_lang = gr.Radio(
|
||||
choices=["CH", "EN"],
|
||||
label="Target language of prompt enhance",
|
||||
value="CH")
|
||||
run_p_button = gr.Button(value="Prompt Enhance")
|
||||
|
||||
with gr.Accordion("Advanced Options", open=True):
|
||||
resolution = gr.Dropdown(
|
||||
label='Resolution(Width*Height)',
|
||||
choices=[
|
||||
'720*1280', '1280*720', '960*960', '1088*832',
|
||||
'832*1088', '480*832', '832*480', '624*624',
|
||||
'704*544', '544*704'
|
||||
],
|
||||
value='720*1280')
|
||||
|
||||
with gr.Row():
|
||||
sd_steps = gr.Slider(
|
||||
label="Diffusion steps",
|
||||
minimum=1,
|
||||
maximum=1000,
|
||||
value=50,
|
||||
step=1)
|
||||
guide_scale = gr.Slider(
|
||||
label="Guide scale",
|
||||
minimum=0,
|
||||
maximum=20,
|
||||
value=5.0,
|
||||
step=1)
|
||||
with gr.Row():
|
||||
shift_scale = gr.Slider(
|
||||
label="Shift scale",
|
||||
minimum=0,
|
||||
maximum=10,
|
||||
value=5.0,
|
||||
step=1)
|
||||
seed = gr.Slider(
|
||||
label="Seed",
|
||||
minimum=-1,
|
||||
maximum=2147483647,
|
||||
step=1,
|
||||
value=-1)
|
||||
n_prompt = gr.Textbox(
|
||||
label="Negative Prompt",
|
||||
placeholder="Describe the negative prompt you want to add"
|
||||
)
|
||||
|
||||
run_t2v_button = gr.Button("Generate Video")
|
||||
|
||||
with gr.Column():
|
||||
result_gallery = gr.Video(
|
||||
label='Generated Video', interactive=False, height=600)
|
||||
|
||||
run_p_button.click(
|
||||
fn=prompt_enc,
|
||||
inputs=[txt2vid_prompt, tar_lang],
|
||||
outputs=[txt2vid_prompt])
|
||||
|
||||
run_t2v_button.click(
|
||||
fn=t2v_generation,
|
||||
inputs=[
|
||||
txt2vid_prompt, resolution, sd_steps, guide_scale, shift_scale,
|
||||
seed, n_prompt
|
||||
],
|
||||
outputs=[result_gallery],
|
||||
)
|
||||
|
||||
return demo
|
||||
|
||||
|
||||
# Main
|
||||
def _parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate a video from a text prompt or image using Gradio")
|
||||
parser.add_argument(
|
||||
"--ckpt_dir",
|
||||
type=str,
|
||||
default="cache",
|
||||
help="The path to the checkpoint directory.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_method",
|
||||
type=str,
|
||||
default="local_qwen",
|
||||
choices=["dashscope", "local_qwen"],
|
||||
help="The prompt extend method to use.")
|
||||
parser.add_argument(
|
||||
"--prompt_extend_model",
|
||||
type=str,
|
||||
default=None,
|
||||
help="The prompt extend model to use.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
return args
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _parse_args()
|
||||
|
||||
print("Step1: Init prompt_expander...", end='', flush=True)
|
||||
prompt_expander = None
|
||||
# if args.prompt_extend_method == "dashscope":
|
||||
# prompt_expander = DashScopePromptExpander(
|
||||
# model_name=args.prompt_extend_model, is_vl=False)
|
||||
# elif args.prompt_extend_method == "local_qwen":
|
||||
# prompt_expander = QwenPromptExpander(
|
||||
# model_name=args.prompt_extend_model, is_vl=False, device=0)
|
||||
# else:
|
||||
# raise NotImplementedError(
|
||||
# f"Unsupport prompt_extend_method: {args.prompt_extend_method}")
|
||||
# print("done", flush=True)
|
||||
|
||||
from mmgp import offload
|
||||
|
||||
print("Step2: Init 14B t2v model...", end='', flush=True)
|
||||
cfg = WAN_CONFIGS['t2v-14B']
|
||||
# cfg = WAN_CONFIGS['t2v-1.3B']
|
||||
|
||||
wan_t2v = wan.WanT2V(
|
||||
config=cfg,
|
||||
checkpoint_dir="../ckpts",
|
||||
device_id=0,
|
||||
rank=0,
|
||||
t5_fsdp=False,
|
||||
dit_fsdp=False,
|
||||
use_usp=False,
|
||||
)
|
||||
|
||||
pipe = {"transformer": wan_t2v.model, "text_encoder" : wan_t2v.text_encoder.model, "vae": wan_t2v.vae.model } #
|
||||
# offload.profile(pipe, profile_no=4, budgets = {"transformer":100, "*":3000}, verboseLevel=2, quantizeTransformer = False, compile = "transformer") #
|
||||
offload.profile(pipe, profile_no=4, budgets = {"transformer":100, "*":3000}, verboseLevel=2, quantizeTransformer = False) #
|
||||
# offload.profile(pipe, profile_no=4, budgets = {"transformer":3000, "*":3000}, verboseLevel=2, quantizeTransformer = False)
|
||||
|
||||
print("done", flush=True)
|
||||
|
||||
demo = gradio_interface()
|
||||
demo.launch(server_name="0.0.0.0", share=False, server_port=7860)
|
||||
@@ -1,534 +0,0 @@
|
||||
import argparse
|
||||
from .constants import *
|
||||
import re
|
||||
from .modules.models import HUNYUAN_VIDEO_CONFIG
|
||||
|
||||
|
||||
def parse_args(namespace=None):
|
||||
parser = argparse.ArgumentParser(description="HunyuanVideo inference script")
|
||||
|
||||
parser = add_network_args(parser)
|
||||
parser = add_extra_models_args(parser)
|
||||
parser = add_denoise_schedule_args(parser)
|
||||
parser = add_inference_args(parser)
|
||||
parser = add_parallel_args(parser)
|
||||
|
||||
args = parser.parse_args(namespace=namespace)
|
||||
args = sanity_check_args(args)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def add_network_args(parser: argparse.ArgumentParser):
|
||||
group = parser.add_argument_group(title="HunyuanVideo network args")
|
||||
|
||||
|
||||
group.add_argument(
|
||||
"--quantize-transformer",
|
||||
action="store_true",
|
||||
help="On the fly 'transformer' quantization"
|
||||
)
|
||||
|
||||
|
||||
group.add_argument(
|
||||
"--lora-dir-i2v",
|
||||
type=str,
|
||||
default="loras_i2v",
|
||||
help="Path to a directory that contains Loras for i2v"
|
||||
)
|
||||
|
||||
|
||||
group.add_argument(
|
||||
"--lora-dir",
|
||||
type=str,
|
||||
default="",
|
||||
help="Path to a directory that contains Loras"
|
||||
)
|
||||
|
||||
|
||||
group.add_argument(
|
||||
"--lora-preset",
|
||||
type=str,
|
||||
default="",
|
||||
help="Lora preset to preload"
|
||||
)
|
||||
|
||||
# group.add_argument(
|
||||
# "--lora-preset-i2v",
|
||||
# type=str,
|
||||
# default="",
|
||||
# help="Lora preset to preload for i2v"
|
||||
# )
|
||||
|
||||
group.add_argument(
|
||||
"--profile",
|
||||
type=str,
|
||||
default=-1,
|
||||
help="Profile No"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--verbose",
|
||||
type=str,
|
||||
default=1,
|
||||
help="Verbose level"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--server-port",
|
||||
type=str,
|
||||
default=0,
|
||||
help="Server port"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--server-name",
|
||||
type=str,
|
||||
default="",
|
||||
help="Server name"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--open-browser",
|
||||
action="store_true",
|
||||
help="open browser"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--t2v",
|
||||
action="store_true",
|
||||
help="text to video mode"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--i2v",
|
||||
action="store_true",
|
||||
help="image to video mode"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--compile",
|
||||
action="store_true",
|
||||
help="Enable pytorch compilation"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--fast",
|
||||
action="store_true",
|
||||
help="use Fast HunyuanVideo model"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--fastest",
|
||||
action="store_true",
|
||||
help="activate the best config"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--attention",
|
||||
type=str,
|
||||
default="",
|
||||
help="attention mode"
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--vae-config",
|
||||
type=str,
|
||||
default="",
|
||||
help="vae config mode"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--share",
|
||||
action="store_true",
|
||||
help="Create a shared URL to access webserver remotely"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--lock-config",
|
||||
action="store_true",
|
||||
help="Prevent modifying the configuration from the web interface"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--preload",
|
||||
type=str,
|
||||
default="0",
|
||||
help="Megabytes of the diffusion model to preload in VRAM"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--multiple-images",
|
||||
action="store_true",
|
||||
help="Allow inputting multiple images with image to video"
|
||||
)
|
||||
|
||||
|
||||
# Main model
|
||||
group.add_argument(
|
||||
"--model",
|
||||
type=str,
|
||||
choices=list(HUNYUAN_VIDEO_CONFIG.keys()),
|
||||
default="HYVideo-T/2-cfgdistill",
|
||||
)
|
||||
group.add_argument(
|
||||
"--latent-channels",
|
||||
type=str,
|
||||
default=16,
|
||||
help="Number of latent channels of DiT. If None, it will be determined by `vae`. If provided, "
|
||||
"it still needs to match the latent channels of the VAE model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--precision",
|
||||
type=str,
|
||||
default="bf16",
|
||||
choices=PRECISIONS,
|
||||
help="Precision mode. Options: fp32, fp16, bf16. Applied to the backbone model and optimizer.",
|
||||
)
|
||||
|
||||
# RoPE
|
||||
group.add_argument(
|
||||
"--rope-theta", type=int, default=256, help="Theta used in RoPE."
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
def add_extra_models_args(parser: argparse.ArgumentParser):
|
||||
group = parser.add_argument_group(
|
||||
title="Extra models args, including vae, text encoders and tokenizers)"
|
||||
)
|
||||
|
||||
# - VAE
|
||||
group.add_argument(
|
||||
"--vae",
|
||||
type=str,
|
||||
default="884-16c-hy",
|
||||
choices=list(VAE_PATH),
|
||||
help="Name of the VAE model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--vae-precision",
|
||||
type=str,
|
||||
default="fp16",
|
||||
choices=PRECISIONS,
|
||||
help="Precision mode for the VAE model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--vae-tiling",
|
||||
action="store_true",
|
||||
help="Enable tiling for the VAE model to save GPU memory.",
|
||||
)
|
||||
group.set_defaults(vae_tiling=True)
|
||||
|
||||
group.add_argument(
|
||||
"--text-encoder",
|
||||
type=str,
|
||||
default="llm",
|
||||
choices=list(TEXT_ENCODER_PATH),
|
||||
help="Name of the text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-encoder-precision",
|
||||
type=str,
|
||||
default="fp16",
|
||||
choices=PRECISIONS,
|
||||
help="Precision mode for the text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-states-dim",
|
||||
type=int,
|
||||
default=4096,
|
||||
help="Dimension of the text encoder hidden states.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-len", type=int, default=256, help="Maximum length of the text input."
|
||||
)
|
||||
group.add_argument(
|
||||
"--tokenizer",
|
||||
type=str,
|
||||
default="llm",
|
||||
choices=list(TOKENIZER_PATH),
|
||||
help="Name of the tokenizer model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--prompt-template",
|
||||
type=str,
|
||||
default="dit-llm-encode",
|
||||
choices=PROMPT_TEMPLATE,
|
||||
help="Image prompt template for the decoder-only text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--prompt-template-video",
|
||||
type=str,
|
||||
default="dit-llm-encode-video",
|
||||
choices=PROMPT_TEMPLATE,
|
||||
help="Video prompt template for the decoder-only text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--hidden-state-skip-layer",
|
||||
type=int,
|
||||
default=2,
|
||||
help="Skip layer for hidden states.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--apply-final-norm",
|
||||
action="store_true",
|
||||
help="Apply final normalization to the used text encoder hidden states.",
|
||||
)
|
||||
|
||||
# - CLIP
|
||||
group.add_argument(
|
||||
"--text-encoder-2",
|
||||
type=str,
|
||||
default="clipL",
|
||||
choices=list(TEXT_ENCODER_PATH),
|
||||
help="Name of the second text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-encoder-precision-2",
|
||||
type=str,
|
||||
default="fp16",
|
||||
choices=PRECISIONS,
|
||||
help="Precision mode for the second text encoder model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-states-dim-2",
|
||||
type=int,
|
||||
default=768,
|
||||
help="Dimension of the second text encoder hidden states.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--tokenizer-2",
|
||||
type=str,
|
||||
default="clipL",
|
||||
choices=list(TOKENIZER_PATH),
|
||||
help="Name of the second tokenizer model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--text-len-2",
|
||||
type=int,
|
||||
default=77,
|
||||
help="Maximum length of the second text input.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def add_denoise_schedule_args(parser: argparse.ArgumentParser):
|
||||
group = parser.add_argument_group(title="Denoise schedule args")
|
||||
|
||||
group.add_argument(
|
||||
"--denoise-type",
|
||||
type=str,
|
||||
default="flow",
|
||||
help="Denoise type for noised inputs.",
|
||||
)
|
||||
|
||||
# Flow Matching
|
||||
group.add_argument(
|
||||
"--flow-shift",
|
||||
type=float,
|
||||
default=7.0,
|
||||
help="Shift factor for flow matching schedulers.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--flow-reverse",
|
||||
action="store_true",
|
||||
help="If reverse, learning/sampling from t=1 -> t=0.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--flow-solver",
|
||||
type=str,
|
||||
default="euler",
|
||||
help="Solver for flow matching.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--use-linear-quadratic-schedule",
|
||||
action="store_true",
|
||||
help="Use linear quadratic schedule for flow matching."
|
||||
"Following MovieGen (https://ai.meta.com/static-resource/movie-gen-research-paper)",
|
||||
)
|
||||
group.add_argument(
|
||||
"--linear-schedule-end",
|
||||
type=int,
|
||||
default=25,
|
||||
help="End step for linear quadratic schedule for flow matching.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def add_inference_args(parser: argparse.ArgumentParser):
|
||||
group = parser.add_argument_group(title="Inference args")
|
||||
|
||||
# ======================== Model loads ========================
|
||||
group.add_argument(
|
||||
"--model-base",
|
||||
type=str,
|
||||
default="ckpts",
|
||||
help="Root path of all the models, including t2v models and extra models.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--dit-weight",
|
||||
type=str,
|
||||
default="ckpts/hunyuan-video-t2v-720p/transformers/mp_rank_00_model_states.pt",
|
||||
help="Path to the HunyuanVideo model. If None, search the model in the args.model_root."
|
||||
"1. If it is a file, load the model directly."
|
||||
"2. If it is a directory, search the model in the directory. Support two types of models: "
|
||||
"1) named `pytorch_model_*.pt`"
|
||||
"2) named `*_model_states.pt`, where * can be `mp_rank_00`.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--model-resolution",
|
||||
type=str,
|
||||
default="540p",
|
||||
choices=["540p", "720p"],
|
||||
help="Root path of all the models, including t2v models and extra models.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--load-key",
|
||||
type=str,
|
||||
default="module",
|
||||
help="Key to load the model states. 'module' for the main model, 'ema' for the EMA model.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--use-cpu-offload",
|
||||
action="store_true",
|
||||
help="Use CPU offload for the model load.",
|
||||
)
|
||||
|
||||
# ======================== Inference general setting ========================
|
||||
group.add_argument(
|
||||
"--batch-size",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Batch size for inference and evaluation.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--infer-steps",
|
||||
type=int,
|
||||
default=50,
|
||||
help="Number of denoising steps for inference.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--disable-autocast",
|
||||
action="store_true",
|
||||
help="Disable autocast for denoising loop and vae decoding in pipeline sampling.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--save-path",
|
||||
type=str,
|
||||
default="./results",
|
||||
help="Path to save the generated samples.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--save-path-suffix",
|
||||
type=str,
|
||||
default="",
|
||||
help="Suffix for the directory of saved samples.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--name-suffix",
|
||||
type=str,
|
||||
default="",
|
||||
help="Suffix for the names of saved samples.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--num-videos",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Number of videos to generate for each prompt.",
|
||||
)
|
||||
# ---sample size---
|
||||
group.add_argument(
|
||||
"--video-size",
|
||||
type=int,
|
||||
nargs="+",
|
||||
default=(720, 1280),
|
||||
help="Video size for training. If a single value is provided, it will be used for both height "
|
||||
"and width. If two values are provided, they will be used for height and width "
|
||||
"respectively.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--video-length",
|
||||
type=int,
|
||||
default=129,
|
||||
help="How many frames to sample from a video. if using 3d vae, the number should be 4n+1",
|
||||
)
|
||||
# --- prompt ---
|
||||
group.add_argument(
|
||||
"--prompt",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Prompt for sampling during evaluation.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--seed-type",
|
||||
type=str,
|
||||
default="auto",
|
||||
choices=["file", "random", "fixed", "auto"],
|
||||
help="Seed type for evaluation. If file, use the seed from the CSV file. If random, generate a "
|
||||
"random seed. If fixed, use the fixed seed given by `--seed`. If auto, `csv` will use the "
|
||||
"seed column if available, otherwise use the fixed `seed` value. `prompt` will use the "
|
||||
"fixed `seed` value.",
|
||||
)
|
||||
group.add_argument("--seed", type=int, default=None, help="Seed for evaluation.")
|
||||
|
||||
# Classifier-Free Guidance
|
||||
group.add_argument(
|
||||
"--neg-prompt", type=str, default=None, help="Negative prompt for sampling."
|
||||
)
|
||||
group.add_argument(
|
||||
"--cfg-scale", type=float, default=1.0, help="Classifier free guidance scale."
|
||||
)
|
||||
group.add_argument(
|
||||
"--embedded-cfg-scale",
|
||||
type=float,
|
||||
default=6.0,
|
||||
help="Embeded classifier free guidance scale.",
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
"--reproduce",
|
||||
action="store_true",
|
||||
help="Enable reproducibility by setting random seeds and deterministic algorithms.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def add_parallel_args(parser: argparse.ArgumentParser):
|
||||
group = parser.add_argument_group(title="Parallel args")
|
||||
|
||||
# ======================== Model loads ========================
|
||||
group.add_argument(
|
||||
"--ulysses-degree",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Ulysses degree.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--ring-degree",
|
||||
type=int,
|
||||
default=1,
|
||||
help="Ulysses degree.",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def sanity_check_args(args):
|
||||
# VAE channels
|
||||
vae_pattern = r"\d{2,3}-\d{1,2}c-\w+"
|
||||
if not re.match(vae_pattern, args.vae):
|
||||
raise ValueError(
|
||||
f"Invalid VAE model: {args.vae}. Must be in the format of '{vae_pattern}'."
|
||||
)
|
||||
vae_channels = int(args.vae.split("-")[1][:-1])
|
||||
if args.latent_channels is None:
|
||||
args.latent_channels = vae_channels
|
||||
if vae_channels != args.latent_channels:
|
||||
raise ValueError(
|
||||
f"Latent channels ({args.latent_channels}) must match the VAE channels ({vae_channels})."
|
||||
)
|
||||
return args
|
||||
@@ -1,164 +0,0 @@
|
||||
import os
|
||||
import torch
|
||||
|
||||
__all__ = [
|
||||
"C_SCALE",
|
||||
"PROMPT_TEMPLATE",
|
||||
"MODEL_BASE",
|
||||
"PRECISIONS",
|
||||
"NORMALIZATION_TYPE",
|
||||
"ACTIVATION_TYPE",
|
||||
"VAE_PATH",
|
||||
"TEXT_ENCODER_PATH",
|
||||
"TOKENIZER_PATH",
|
||||
"TEXT_PROJECTION",
|
||||
"DATA_TYPE",
|
||||
"NEGATIVE_PROMPT",
|
||||
"NEGATIVE_PROMPT_I2V",
|
||||
"FLOW_PATH_TYPE",
|
||||
"FLOW_PREDICT_TYPE",
|
||||
"FLOW_LOSS_WEIGHT",
|
||||
"FLOW_SNR_TYPE",
|
||||
"FLOW_SOLVER",
|
||||
]
|
||||
|
||||
PRECISION_TO_TYPE = {
|
||||
'fp32': torch.float32,
|
||||
'fp16': torch.float16,
|
||||
'bf16': torch.bfloat16,
|
||||
}
|
||||
|
||||
# =================== Constant Values =====================
|
||||
# Computation scale factor, 1P = 1_000_000_000_000_000. Tensorboard will display the value in PetaFLOPS to avoid
|
||||
# overflow error when tensorboard logging values.
|
||||
C_SCALE = 1_000_000_000_000_000
|
||||
|
||||
# When using decoder-only models, we must provide a prompt template to instruct the text encoder
|
||||
# on how to generate the text.
|
||||
# --------------------------------------------------------------------
|
||||
PROMPT_TEMPLATE_ENCODE = (
|
||||
"<|start_header_id|>system<|end_header_id|>\n\nDescribe the image by detailing the color, shape, size, texture, "
|
||||
"quantity, text, spatial relationships of the objects and background:<|eot_id|>"
|
||||
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
||||
)
|
||||
PROMPT_TEMPLATE_ENCODE_VIDEO = (
|
||||
"<|start_header_id|>system<|end_header_id|>\n\nDescribe the video by detailing the following aspects: "
|
||||
"1. The main content and theme of the video."
|
||||
"2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects."
|
||||
"3. Actions, events, behaviors temporal relationships, physical movement changes of the objects."
|
||||
"4. background environment, light, style and atmosphere."
|
||||
"5. camera angles, movements, and transitions used in the video:<|eot_id|>"
|
||||
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
||||
)
|
||||
|
||||
PROMPT_TEMPLATE_ENCODE_I2V = (
|
||||
"<|start_header_id|>system<|end_header_id|>\n\n<image>\nDescribe the image by detailing the color, shape, size, texture, "
|
||||
"quantity, text, spatial relationships of the objects and background:<|eot_id|>"
|
||||
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
||||
"<|start_header_id|>assistant<|end_header_id|>\n\n"
|
||||
)
|
||||
|
||||
PROMPT_TEMPLATE_ENCODE_VIDEO_I2V = (
|
||||
"<|start_header_id|>system<|end_header_id|>\n\n<image>\nDescribe the video by detailing the following aspects according to the reference image: "
|
||||
"1. The main content and theme of the video."
|
||||
"2. The color, shape, size, texture, quantity, text, and spatial relationships of the objects."
|
||||
"3. Actions, events, behaviors temporal relationships, physical movement changes of the objects."
|
||||
"4. background environment, light, style and atmosphere."
|
||||
"5. camera angles, movements, and transitions used in the video:<|eot_id|>\n\n"
|
||||
"<|start_header_id|>user<|end_header_id|>\n\n{}<|eot_id|>"
|
||||
"<|start_header_id|>assistant<|end_header_id|>\n\n"
|
||||
)
|
||||
|
||||
NEGATIVE_PROMPT = "Aerial view, aerial view, overexposed, low quality, deformation, a poor composition, bad hands, bad teeth, bad eyes, bad limbs, distortion"
|
||||
NEGATIVE_PROMPT_I2V = "deformation, a poor composition and deformed video, bad teeth, bad eyes, bad limbs"
|
||||
|
||||
PROMPT_TEMPLATE = {
|
||||
"dit-llm-encode": {
|
||||
"template": PROMPT_TEMPLATE_ENCODE,
|
||||
"crop_start": 36,
|
||||
},
|
||||
"dit-llm-encode-video": {
|
||||
"template": PROMPT_TEMPLATE_ENCODE_VIDEO,
|
||||
"crop_start": 95,
|
||||
},
|
||||
"dit-llm-encode-i2v": {
|
||||
"template": PROMPT_TEMPLATE_ENCODE_I2V,
|
||||
"crop_start": 36,
|
||||
"image_emb_start": 5,
|
||||
"image_emb_end": 581,
|
||||
"image_emb_len": 576,
|
||||
"double_return_token_id": 271
|
||||
},
|
||||
"dit-llm-encode-video-i2v": {
|
||||
"template": PROMPT_TEMPLATE_ENCODE_VIDEO_I2V,
|
||||
"crop_start": 103,
|
||||
"image_emb_start": 5,
|
||||
"image_emb_end": 581,
|
||||
"image_emb_len": 576,
|
||||
"double_return_token_id": 271
|
||||
},
|
||||
}
|
||||
|
||||
# ======================= Model ======================
|
||||
PRECISIONS = {"fp32", "fp16", "bf16"}
|
||||
NORMALIZATION_TYPE = {"layer", "rms"}
|
||||
ACTIVATION_TYPE = {"relu", "silu", "gelu", "gelu_tanh"}
|
||||
|
||||
# =================== Model Path =====================
|
||||
MODEL_BASE = os.getenv("MODEL_BASE", "./ckpts")
|
||||
|
||||
# =================== Data =======================
|
||||
DATA_TYPE = {"image", "video", "image_video"}
|
||||
|
||||
# 3D VAE
|
||||
VAE_PATH = {"884-16c-hy": f"{MODEL_BASE}/hunyuan-video-t2v-720p/vae"}
|
||||
|
||||
# Text Encoder
|
||||
TEXT_ENCODER_PATH = {
|
||||
"clipL": f"{MODEL_BASE}/clip_vit_large_patch14",
|
||||
"llm": f"{MODEL_BASE}/llava-llama-3-8b",
|
||||
"llm-i2v": f"{MODEL_BASE}/llava-llama-3-8b",
|
||||
}
|
||||
|
||||
# Tokenizer
|
||||
TOKENIZER_PATH = {
|
||||
"clipL": f"{MODEL_BASE}/clip_vit_large_patch14",
|
||||
"llm": f"{MODEL_BASE}/llava-llama-3-8b",
|
||||
"llm-i2v": f"{MODEL_BASE}/llava-llama-3-8b",
|
||||
}
|
||||
|
||||
TEXT_PROJECTION = {
|
||||
"linear", # Default, an nn.Linear() layer
|
||||
"single_refiner", # Single TokenRefiner. Refer to LI-DiT
|
||||
}
|
||||
|
||||
# Flow Matching path type
|
||||
FLOW_PATH_TYPE = {
|
||||
"linear", # Linear trajectory between noise and data
|
||||
"gvp", # Generalized variance-preserving SDE
|
||||
"vp", # Variance-preserving SDE
|
||||
}
|
||||
|
||||
# Flow Matching predict type
|
||||
FLOW_PREDICT_TYPE = {
|
||||
"velocity", # Predict velocity
|
||||
"score", # Predict score
|
||||
"noise", # Predict noise
|
||||
}
|
||||
|
||||
# Flow Matching loss weight
|
||||
FLOW_LOSS_WEIGHT = {
|
||||
"velocity", # Weight loss by velocity
|
||||
"likelihood", # Weight loss by likelihood
|
||||
}
|
||||
|
||||
# Flow Matching SNR type
|
||||
FLOW_SNR_TYPE = {
|
||||
"lognorm", # Log-normal SNR
|
||||
"uniform", # Uniform SNR
|
||||
}
|
||||
|
||||
# Flow Matching solvers
|
||||
FLOW_SOLVER = {
|
||||
"euler", # Euler solver
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
import os
|
||||
import cv2
|
||||
import math
|
||||
import json
|
||||
import torch
|
||||
import random
|
||||
import librosa
|
||||
import traceback
|
||||
import torchvision
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from PIL import Image
|
||||
from einops import rearrange
|
||||
from torch.utils.data import Dataset
|
||||
from decord import VideoReader, cpu
|
||||
from transformers import CLIPImageProcessor
|
||||
import torchvision.transforms as transforms
|
||||
from torchvision.transforms import ToPILImage
|
||||
|
||||
|
||||
|
||||
def get_audio_feature(feature_extractor, audio_path):
|
||||
audio_input, sampling_rate = librosa.load(audio_path, sr=16000)
|
||||
assert sampling_rate == 16000
|
||||
|
||||
audio_features = []
|
||||
window = 750*640
|
||||
for i in range(0, len(audio_input), window):
|
||||
audio_feature = feature_extractor(audio_input[i:i+window],
|
||||
sampling_rate=sampling_rate,
|
||||
return_tensors="pt",
|
||||
).input_features
|
||||
audio_features.append(audio_feature)
|
||||
|
||||
audio_features = torch.cat(audio_features, dim=-1)
|
||||
return audio_features, len(audio_input) // 640
|
||||
|
||||
|
||||
class VideoAudioTextLoaderVal(Dataset):
|
||||
def __init__(
|
||||
self,
|
||||
image_size: int,
|
||||
meta_file: str,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__()
|
||||
self.meta_file = meta_file
|
||||
self.image_size = image_size
|
||||
self.text_encoder = kwargs.get("text_encoder", None) # llava_text_encoder
|
||||
self.text_encoder_2 = kwargs.get("text_encoder_2", None) # clipL_text_encoder
|
||||
self.feature_extractor = kwargs.get("feature_extractor", None)
|
||||
self.meta_files = []
|
||||
|
||||
csv_data = pd.read_csv(meta_file)
|
||||
for idx in range(len(csv_data)):
|
||||
self.meta_files.append(
|
||||
{
|
||||
"videoid": str(csv_data["videoid"][idx]),
|
||||
"image_path": str(csv_data["image"][idx]),
|
||||
"audio_path": str(csv_data["audio"][idx]),
|
||||
"prompt": str(csv_data["prompt"][idx]),
|
||||
"fps": float(csv_data["fps"][idx])
|
||||
}
|
||||
)
|
||||
|
||||
self.llava_transform = transforms.Compose(
|
||||
[
|
||||
transforms.Resize((336, 336), interpolation=transforms.InterpolationMode.BILINEAR),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize((0.48145466, 0.4578275, 0.4082107), (0.26862954, 0.26130258, 0.27577711)),
|
||||
]
|
||||
)
|
||||
self.clip_image_processor = CLIPImageProcessor()
|
||||
|
||||
self.device = torch.device("cuda")
|
||||
self.weight_dtype = torch.float16
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.meta_files)
|
||||
|
||||
@staticmethod
|
||||
def get_text_tokens(text_encoder, description, dtype_encode="video"):
|
||||
text_inputs = text_encoder.text2tokens(description, data_type=dtype_encode)
|
||||
text_ids = text_inputs["input_ids"].squeeze(0)
|
||||
text_mask = text_inputs["attention_mask"].squeeze(0)
|
||||
return text_ids, text_mask
|
||||
|
||||
def get_batch_data(self, idx):
|
||||
meta_file = self.meta_files[idx]
|
||||
videoid = meta_file["videoid"]
|
||||
image_path = meta_file["image_path"]
|
||||
audio_path = meta_file["audio_path"]
|
||||
prompt = "Authentic, Realistic, Natural, High-quality, Lens-Fixed, " + meta_file["prompt"]
|
||||
fps = meta_file["fps"]
|
||||
|
||||
img_size = self.image_size
|
||||
ref_image = Image.open(image_path).convert('RGB')
|
||||
|
||||
# Resize reference image
|
||||
w, h = ref_image.size
|
||||
scale = img_size / min(w, h)
|
||||
new_w = round(w * scale / 64) * 64
|
||||
new_h = round(h * scale / 64) * 64
|
||||
|
||||
if img_size == 704:
|
||||
img_size_long = 1216
|
||||
if new_w * new_h > img_size * img_size_long:
|
||||
import math
|
||||
scale = math.sqrt(img_size * img_size_long / w / h)
|
||||
new_w = round(w * scale / 64) * 64
|
||||
new_h = round(h * scale / 64) * 64
|
||||
|
||||
ref_image = ref_image.resize((new_w, new_h), Image.LANCZOS)
|
||||
|
||||
ref_image = np.array(ref_image)
|
||||
ref_image = torch.from_numpy(ref_image)
|
||||
|
||||
audio_input, audio_len = get_audio_feature(self.feature_extractor, audio_path)
|
||||
audio_prompts = audio_input[0]
|
||||
|
||||
motion_bucket_id_heads = np.array([25] * 4)
|
||||
motion_bucket_id_exps = np.array([30] * 4)
|
||||
motion_bucket_id_heads = torch.from_numpy(motion_bucket_id_heads)
|
||||
motion_bucket_id_exps = torch.from_numpy(motion_bucket_id_exps)
|
||||
fps = torch.from_numpy(np.array(fps))
|
||||
|
||||
to_pil = ToPILImage()
|
||||
pixel_value_ref = rearrange(ref_image.clone().unsqueeze(0), "b h w c -> b c h w") # (b c h w)
|
||||
|
||||
pixel_value_ref_llava = [self.llava_transform(to_pil(image)) for image in pixel_value_ref]
|
||||
pixel_value_ref_llava = torch.stack(pixel_value_ref_llava, dim=0)
|
||||
pixel_value_ref_clip = self.clip_image_processor(
|
||||
images=Image.fromarray((pixel_value_ref[0].permute(1,2,0)).data.cpu().numpy().astype(np.uint8)),
|
||||
return_tensors="pt"
|
||||
).pixel_values[0]
|
||||
pixel_value_ref_clip = pixel_value_ref_clip.unsqueeze(0)
|
||||
|
||||
# Encode text prompts
|
||||
|
||||
text_ids, text_mask = self.get_text_tokens(self.text_encoder, prompt)
|
||||
text_ids_2, text_mask_2 = self.get_text_tokens(self.text_encoder_2, prompt)
|
||||
|
||||
# Output batch
|
||||
batch = {
|
||||
"text_prompt": prompt, #
|
||||
"videoid": videoid,
|
||||
"pixel_value_ref": pixel_value_ref.to(dtype=torch.float16), # 参考图,用于vae提特征 (1, 3, h, w), 取值范围(0, 255)
|
||||
"pixel_value_ref_llava": pixel_value_ref_llava.to(dtype=torch.float16), # 参考图,用于llava提特征 (1, 3, 336, 336), 取值范围 = CLIP取值范围
|
||||
"pixel_value_ref_clip": pixel_value_ref_clip.to(dtype=torch.float16), # 参考图,用于clip_image_encoder提特征 (1, 3, 244, 244), 取值范围 = CLIP取值范围
|
||||
"audio_prompts": audio_prompts.to(dtype=torch.float16),
|
||||
"motion_bucket_id_heads": motion_bucket_id_heads.to(dtype=text_ids.dtype),
|
||||
"motion_bucket_id_exps": motion_bucket_id_exps.to(dtype=text_ids.dtype),
|
||||
"fps": fps.to(dtype=torch.float16),
|
||||
"text_ids": text_ids.clone(), # 对应llava_text_encoder
|
||||
"text_mask": text_mask.clone(), # 对应llava_text_encoder
|
||||
"text_ids_2": text_ids_2.clone(), # 对应clip_text_encoder
|
||||
"text_mask_2": text_mask_2.clone(), # 对应clip_text_encoder
|
||||
"audio_len": audio_len,
|
||||
"image_path": image_path,
|
||||
"audio_path": audio_path,
|
||||
}
|
||||
return batch
|
||||
|
||||
def __getitem__(self, idx):
|
||||
return self.get_batch_data(idx)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
|
||||
import os
|
||||
import cv2
|
||||
import json
|
||||
import time
|
||||
import decord
|
||||
import einops
|
||||
import librosa
|
||||
import torch
|
||||
import random
|
||||
import argparse
|
||||
import traceback
|
||||
import numpy as np
|
||||
from tqdm import tqdm
|
||||
from PIL import Image
|
||||
from einops import rearrange
|
||||
|
||||
|
||||
|
||||
def get_facemask(ref_image, align_instance, area=1.25):
|
||||
# ref_image: (b f c h w)
|
||||
bsz, f, c, h, w = ref_image.shape
|
||||
images = rearrange(ref_image, "b f c h w -> (b f) h w c").data.cpu().numpy().astype(np.uint8)
|
||||
face_masks = []
|
||||
for image in images:
|
||||
image_pil = Image.fromarray(image).convert("RGB")
|
||||
_, _, bboxes_list = align_instance(np.array(image_pil)[:,:,[2,1,0]], maxface=True)
|
||||
try:
|
||||
bboxSrc = bboxes_list[0]
|
||||
except:
|
||||
bboxSrc = [0, 0, w, h]
|
||||
x1, y1, ww, hh = bboxSrc
|
||||
x2, y2 = x1 + ww, y1 + hh
|
||||
ww, hh = (x2-x1) * area, (y2-y1) * area
|
||||
center = [(x2+x1)//2, (y2+y1)//2]
|
||||
x1 = max(center[0] - ww//2, 0)
|
||||
y1 = max(center[1] - hh//2, 0)
|
||||
x2 = min(center[0] + ww//2, w)
|
||||
y2 = min(center[1] + hh//2, h)
|
||||
|
||||
face_mask = np.zeros_like(np.array(image_pil))
|
||||
face_mask[int(y1):int(y2), int(x1):int(x2)] = 1.0
|
||||
face_masks.append(torch.from_numpy(face_mask[...,:1]))
|
||||
face_masks = torch.stack(face_masks, dim=0) # (b*f, h, w, c)
|
||||
face_masks = rearrange(face_masks, "(b f) h w c -> b c f h w", b=bsz, f=f)
|
||||
face_masks = face_masks.to(device=ref_image.device, dtype=ref_image.dtype)
|
||||
return face_masks
|
||||
|
||||
|
||||
def encode_audio(wav2vec, audio_feats, fps, num_frames=129):
|
||||
if fps == 25:
|
||||
start_ts = [0]
|
||||
step_ts = [1]
|
||||
elif fps == 12.5:
|
||||
start_ts = [0]
|
||||
step_ts = [2]
|
||||
else:
|
||||
start_ts = [0]
|
||||
step_ts = [1]
|
||||
|
||||
num_frames = min(num_frames, 400)
|
||||
audio_feats = wav2vec.encoder(audio_feats.unsqueeze(0)[:, :, :3000], output_hidden_states=True).hidden_states
|
||||
audio_feats = torch.stack(audio_feats, dim=2)
|
||||
audio_feats = torch.cat([torch.zeros_like(audio_feats[:,:4]), audio_feats], 1)
|
||||
|
||||
audio_prompts = []
|
||||
for bb in range(1):
|
||||
audio_feats_list = []
|
||||
for f in range(num_frames):
|
||||
cur_t = (start_ts[bb] + f * step_ts[bb]) * 2
|
||||
audio_clip = audio_feats[bb:bb+1, cur_t: cur_t+10]
|
||||
audio_feats_list.append(audio_clip)
|
||||
audio_feats_list = torch.stack(audio_feats_list, 1)
|
||||
audio_prompts.append(audio_feats_list)
|
||||
audio_prompts = torch.cat(audio_prompts)
|
||||
return audio_prompts
|
||||
@@ -1,41 +0,0 @@
|
||||
import os
|
||||
import cv2
|
||||
import torch
|
||||
import numpy as np
|
||||
import imageio
|
||||
import torchvision
|
||||
from einops import rearrange
|
||||
|
||||
|
||||
def save_videos_grid(videos: torch.Tensor, path: str, rescale=False, n_rows=6, fps=8, quality=8):
|
||||
videos = rearrange(videos, "b c t h w -> t b c h w")
|
||||
outputs = []
|
||||
for x in videos:
|
||||
x = torchvision.utils.make_grid(x, nrow=n_rows)
|
||||
x = x.transpose(0, 1).transpose(1, 2).squeeze(-1)
|
||||
if rescale:
|
||||
x = (x + 1.0) / 2.0 # -1,1 -> 0,1
|
||||
x = torch.clamp(x,0,1)
|
||||
x = (x * 255).numpy().astype(np.uint8)
|
||||
outputs.append(x)
|
||||
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
imageio.mimsave(path, outputs, fps=fps, quality=quality)
|
||||
|
||||
def pad_image(crop_img, size, color=(255, 255, 255), resize_ratio=1):
|
||||
crop_h, crop_w = crop_img.shape[:2]
|
||||
target_w, target_h = size
|
||||
scale_h, scale_w = target_h / crop_h, target_w / crop_w
|
||||
if scale_w > scale_h:
|
||||
resize_h = int(target_h*resize_ratio)
|
||||
resize_w = int(crop_w / crop_h * resize_h)
|
||||
else:
|
||||
resize_w = int(target_w*resize_ratio)
|
||||
resize_h = int(crop_h / crop_w * resize_w)
|
||||
crop_img = cv2.resize(crop_img, (resize_w, resize_h))
|
||||
pad_left = (target_w - resize_w) // 2
|
||||
pad_top = (target_h - resize_h) // 2
|
||||
pad_right = target_w - resize_w - pad_left
|
||||
pad_bottom = target_h - resize_h - pad_top
|
||||
crop_img = cv2.copyMakeBorder(crop_img, pad_top, pad_bottom, pad_left, pad_right, cv2.BORDER_CONSTANT, value=color)
|
||||
return crop_img
|
||||
@@ -1 +0,0 @@
|
||||
from .align import AlignImage
|
||||
@@ -1,34 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import torch
|
||||
from .detface import DetFace
|
||||
|
||||
class AlignImage(object):
|
||||
def __init__(self, device='cuda', det_path=''):
|
||||
self.facedet = DetFace(pt_path=det_path, confThreshold=0.5, nmsThreshold=0.45, device=device)
|
||||
|
||||
@torch.no_grad()
|
||||
def __call__(self, im, maxface=False):
|
||||
bboxes, kpss, scores = self.facedet.detect(im)
|
||||
face_num = bboxes.shape[0]
|
||||
|
||||
five_pts_list = []
|
||||
scores_list = []
|
||||
bboxes_list = []
|
||||
for i in range(face_num):
|
||||
five_pts_list.append(kpss[i].reshape(5,2))
|
||||
scores_list.append(scores[i])
|
||||
bboxes_list.append(bboxes[i])
|
||||
|
||||
if maxface and face_num>1:
|
||||
max_idx = 0
|
||||
max_area = (bboxes[0, 2])*(bboxes[0, 3])
|
||||
for i in range(1, face_num):
|
||||
area = (bboxes[i,2])*(bboxes[i,3])
|
||||
if area>max_area:
|
||||
max_idx = i
|
||||
five_pts_list = [five_pts_list[max_idx]]
|
||||
scores_list = [scores_list[max_idx]]
|
||||
bboxes_list = [bboxes_list[max_idx]]
|
||||
|
||||
return five_pts_list, scores_list, bboxes_list
|
||||
@@ -1,283 +0,0 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
import torch
|
||||
import torchvision
|
||||
|
||||
|
||||
def xyxy2xywh(x):
|
||||
# Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right
|
||||
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||
y[:, 0] = (x[:, 0] + x[:, 2]) / 2 # x center
|
||||
y[:, 1] = (x[:, 1] + x[:, 3]) / 2 # y center
|
||||
y[:, 2] = x[:, 2] - x[:, 0] # width
|
||||
y[:, 3] = x[:, 3] - x[:, 1] # height
|
||||
return y
|
||||
|
||||
|
||||
def xywh2xyxy(x):
|
||||
# Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
|
||||
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
||||
y[:, 0] = x[:, 0] - x[:, 2] / 2 # top left x
|
||||
y[:, 1] = x[:, 1] - x[:, 3] / 2 # top left y
|
||||
y[:, 2] = x[:, 0] + x[:, 2] / 2 # bottom right x
|
||||
y[:, 3] = x[:, 1] + x[:, 3] / 2 # bottom right y
|
||||
return y
|
||||
|
||||
|
||||
def box_iou(box1, box2):
|
||||
# https://github.com/pytorch/vision/blob/master/torchvision/ops/boxes.py
|
||||
"""
|
||||
Return intersection-over-union (Jaccard index) of boxes.
|
||||
Both sets of boxes are expected to be in (x1, y1, x2, y2) format.
|
||||
Arguments:
|
||||
box1 (Tensor[N, 4])
|
||||
box2 (Tensor[M, 4])
|
||||
Returns:
|
||||
iou (Tensor[N, M]): the NxM matrix containing the pairwise
|
||||
IoU values for every element in boxes1 and boxes2
|
||||
"""
|
||||
|
||||
def box_area(box):
|
||||
# box = 4xn
|
||||
return (box[2] - box[0]) * (box[3] - box[1])
|
||||
|
||||
area1 = box_area(box1.T)
|
||||
area2 = box_area(box2.T)
|
||||
|
||||
# inter(N,M) = (rb(N,M,2) - lt(N,M,2)).clamp(0).prod(2)
|
||||
inter = (torch.min(box1[:, None, 2:], box2[:, 2:]) -
|
||||
torch.max(box1[:, None, :2], box2[:, :2])).clamp(0).prod(2)
|
||||
# iou = inter / (area1 + area2 - inter)
|
||||
return inter / (area1[:, None] + area2 - inter)
|
||||
|
||||
|
||||
def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):
|
||||
# Rescale coords (xyxy) from img1_shape to img0_shape
|
||||
if ratio_pad is None: # calculate from img0_shape
|
||||
gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new
|
||||
pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
|
||||
else:
|
||||
gain = ratio_pad[0][0]
|
||||
pad = ratio_pad[1]
|
||||
|
||||
coords[:, [0, 2]] -= pad[0] # x padding
|
||||
coords[:, [1, 3]] -= pad[1] # y padding
|
||||
coords[:, :4] /= gain
|
||||
clip_coords(coords, img0_shape)
|
||||
return coords
|
||||
|
||||
|
||||
def clip_coords(boxes, img_shape):
|
||||
# Clip bounding xyxy bounding boxes to image shape (height, width)
|
||||
boxes[:, 0].clamp_(0, img_shape[1]) # x1
|
||||
boxes[:, 1].clamp_(0, img_shape[0]) # y1
|
||||
boxes[:, 2].clamp_(0, img_shape[1]) # x2
|
||||
boxes[:, 3].clamp_(0, img_shape[0]) # y2
|
||||
|
||||
|
||||
def scale_coords_landmarks(img1_shape, coords, img0_shape, ratio_pad=None):
|
||||
# Rescale coords (xyxy) from img1_shape to img0_shape
|
||||
if ratio_pad is None: # calculate from img0_shape
|
||||
gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new
|
||||
pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
|
||||
else:
|
||||
gain = ratio_pad[0][0]
|
||||
pad = ratio_pad[1]
|
||||
|
||||
coords[:, [0, 2, 4, 6, 8]] -= pad[0] # x padding
|
||||
coords[:, [1, 3, 5, 7, 9]] -= pad[1] # y padding
|
||||
coords[:, :10] /= gain
|
||||
#clip_coords(coords, img0_shape)
|
||||
coords[:, 0].clamp_(0, img0_shape[1]) # x1
|
||||
coords[:, 1].clamp_(0, img0_shape[0]) # y1
|
||||
coords[:, 2].clamp_(0, img0_shape[1]) # x2
|
||||
coords[:, 3].clamp_(0, img0_shape[0]) # y2
|
||||
coords[:, 4].clamp_(0, img0_shape[1]) # x3
|
||||
coords[:, 5].clamp_(0, img0_shape[0]) # y3
|
||||
coords[:, 6].clamp_(0, img0_shape[1]) # x4
|
||||
coords[:, 7].clamp_(0, img0_shape[0]) # y4
|
||||
coords[:, 8].clamp_(0, img0_shape[1]) # x5
|
||||
coords[:, 9].clamp_(0, img0_shape[0]) # y5
|
||||
return coords
|
||||
|
||||
|
||||
def show_results(img, xywh, conf, landmarks, class_num):
|
||||
h,w,c = img.shape
|
||||
tl = 1 or round(0.002 * (h + w) / 2) + 1 # line/font thickness
|
||||
x1 = int(xywh[0] * w - 0.5 * xywh[2] * w)
|
||||
y1 = int(xywh[1] * h - 0.5 * xywh[3] * h)
|
||||
x2 = int(xywh[0] * w + 0.5 * xywh[2] * w)
|
||||
y2 = int(xywh[1] * h + 0.5 * xywh[3] * h)
|
||||
cv2.rectangle(img, (x1,y1), (x2, y2), (0,255,0), thickness=tl, lineType=cv2.LINE_AA)
|
||||
|
||||
clors = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(0,255,255)]
|
||||
|
||||
for i in range(5):
|
||||
point_x = int(landmarks[2 * i] * w)
|
||||
point_y = int(landmarks[2 * i + 1] * h)
|
||||
cv2.circle(img, (point_x, point_y), tl+1, clors[i], -1)
|
||||
|
||||
tf = max(tl - 1, 1) # font thickness
|
||||
label = str(conf)[:5]
|
||||
cv2.putText(img, label, (x1, y1 - 2), 0, tl / 3, [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)
|
||||
return img
|
||||
|
||||
|
||||
def make_divisible(x, divisor):
|
||||
# Returns x evenly divisible by divisor
|
||||
return (x // divisor) * divisor
|
||||
|
||||
|
||||
def non_max_suppression_face(prediction, conf_thres=0.5, iou_thres=0.45, classes=None, agnostic=False, labels=()):
|
||||
"""Performs Non-Maximum Suppression (NMS) on inference results
|
||||
Returns:
|
||||
detections with shape: nx6 (x1, y1, x2, y2, conf, cls)
|
||||
"""
|
||||
|
||||
nc = prediction.shape[2] - 15 # number of classes
|
||||
xc = prediction[..., 4] > conf_thres # candidates
|
||||
|
||||
# Settings
|
||||
min_wh, max_wh = 2, 4096 # (pixels) minimum and maximum box width and height
|
||||
# time_limit = 10.0 # seconds to quit after
|
||||
redundant = True # require redundant detections
|
||||
multi_label = nc > 1 # multiple labels per box (adds 0.5ms/img)
|
||||
merge = False # use merge-NMS
|
||||
|
||||
# t = time.time()
|
||||
output = [torch.zeros((0, 16), device=prediction.device)] * prediction.shape[0]
|
||||
for xi, x in enumerate(prediction): # image index, image inference
|
||||
# Apply constraints
|
||||
# x[((x[..., 2:4] < min_wh) | (x[..., 2:4] > max_wh)).any(1), 4] = 0 # width-height
|
||||
x = x[xc[xi]] # confidence
|
||||
|
||||
# Cat apriori labels if autolabelling
|
||||
if labels and len(labels[xi]):
|
||||
l = labels[xi]
|
||||
v = torch.zeros((len(l), nc + 15), device=x.device)
|
||||
v[:, :4] = l[:, 1:5] # box
|
||||
v[:, 4] = 1.0 # conf
|
||||
v[range(len(l)), l[:, 0].long() + 15] = 1.0 # cls
|
||||
x = torch.cat((x, v), 0)
|
||||
|
||||
# If none remain process next image
|
||||
if not x.shape[0]:
|
||||
continue
|
||||
|
||||
# Compute conf
|
||||
x[:, 15:] *= x[:, 4:5] # conf = obj_conf * cls_conf
|
||||
|
||||
# Box (center x, center y, width, height) to (x1, y1, x2, y2)
|
||||
box = xywh2xyxy(x[:, :4])
|
||||
|
||||
# Detections matrix nx6 (xyxy, conf, landmarks, cls)
|
||||
if multi_label:
|
||||
i, j = (x[:, 15:] > conf_thres).nonzero(as_tuple=False).T
|
||||
x = torch.cat((box[i], x[i, j + 15, None], x[i, 5:15] ,j[:, None].float()), 1)
|
||||
else: # best class only
|
||||
conf, j = x[:, 15:].max(1, keepdim=True)
|
||||
x = torch.cat((box, conf, x[:, 5:15], j.float()), 1)[conf.view(-1) > conf_thres]
|
||||
|
||||
# Filter by class
|
||||
if classes is not None:
|
||||
x = x[(x[:, 5:6] == torch.tensor(classes, device=x.device)).any(1)]
|
||||
|
||||
# If none remain process next image
|
||||
n = x.shape[0] # number of boxes
|
||||
if not n:
|
||||
continue
|
||||
|
||||
# Batched NMS
|
||||
c = x[:, 15:16] * (0 if agnostic else max_wh) # classes
|
||||
boxes, scores = x[:, :4] + c, x[:, 4] # boxes (offset by class), scores
|
||||
i = torchvision.ops.nms(boxes, scores, iou_thres) # NMS
|
||||
#if i.shape[0] > max_det: # limit detections
|
||||
# i = i[:max_det]
|
||||
if merge and (1 < n < 3E3): # Merge NMS (boxes merged using weighted mean)
|
||||
# update boxes as boxes(i,4) = weights(i,n) * boxes(n,4)
|
||||
iou = box_iou(boxes[i], boxes) > iou_thres # iou matrix
|
||||
weights = iou * scores[None] # box weights
|
||||
x[i, :4] = torch.mm(weights, x[:, :4]).float() / weights.sum(1, keepdim=True) # merged boxes
|
||||
if redundant:
|
||||
i = i[iou.sum(1) > 1] # require redundancy
|
||||
|
||||
output[xi] = x[i]
|
||||
# if (time.time() - t) > time_limit:
|
||||
# break # time limit exceeded
|
||||
|
||||
return output
|
||||
|
||||
|
||||
class DetFace():
|
||||
def __init__(self, pt_path, confThreshold=0.5, nmsThreshold=0.45, device='cuda'):
|
||||
assert os.path.exists(pt_path)
|
||||
|
||||
self.inpSize = 416
|
||||
self.conf_thres = confThreshold
|
||||
self.iou_thres = nmsThreshold
|
||||
self.test_device = torch.device(device if torch.cuda.is_available() else "cpu")
|
||||
self.model = torch.jit.load(pt_path).to(self.test_device)
|
||||
self.last_w = 416
|
||||
self.last_h = 416
|
||||
self.grids = None
|
||||
|
||||
@torch.no_grad()
|
||||
def detect(self, srcimg):
|
||||
# t0=time.time()
|
||||
|
||||
h0, w0 = srcimg.shape[:2] # orig hw
|
||||
r = self.inpSize / min(h0, w0) # resize image to img_size
|
||||
h1 = int(h0*r+31)//32*32
|
||||
w1 = int(w0*r+31)//32*32
|
||||
|
||||
img = cv2.resize(srcimg, (w1,h1), interpolation=cv2.INTER_LINEAR)
|
||||
|
||||
# Convert
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # BGR to RGB
|
||||
|
||||
# Run inference
|
||||
img = torch.from_numpy(img).to(self.test_device).permute(2,0,1)
|
||||
img = img.float()/255 # uint8 to fp16/32 0-1
|
||||
if img.ndimension() == 3:
|
||||
img = img.unsqueeze(0)
|
||||
|
||||
# Inference
|
||||
if h1 != self.last_h or w1 != self.last_w or self.grids is None:
|
||||
grids = []
|
||||
for scale in [8,16,32]:
|
||||
ny = h1//scale
|
||||
nx = w1//scale
|
||||
yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
|
||||
grid = torch.stack((xv, yv), 2).view((1,1,ny, nx, 2)).float()
|
||||
grids.append(grid.to(self.test_device))
|
||||
self.grids = grids
|
||||
self.last_w = w1
|
||||
self.last_h = h1
|
||||
|
||||
pred = self.model(img, self.grids).cpu()
|
||||
|
||||
# Apply NMS
|
||||
det = non_max_suppression_face(pred, self.conf_thres, self.iou_thres)[0]
|
||||
# Process detections
|
||||
# det = pred[0]
|
||||
bboxes = np.zeros((det.shape[0], 4))
|
||||
kpss = np.zeros((det.shape[0], 5, 2))
|
||||
scores = np.zeros((det.shape[0]))
|
||||
# gn = torch.tensor([w0, h0, w0, h0]).to(pred) # normalization gain whwh
|
||||
# gn_lks = torch.tensor([w0, h0, w0, h0, w0, h0, w0, h0, w0, h0]).to(pred) # normalization gain landmarks
|
||||
det = det.cpu().numpy()
|
||||
|
||||
for j in range(det.shape[0]):
|
||||
# xywh = (xyxy2xywh(det[j, :4].view(1, 4)) / gn).view(4).cpu().numpy()
|
||||
bboxes[j, 0] = det[j, 0] * w0/w1
|
||||
bboxes[j, 1] = det[j, 1] * h0/h1
|
||||
bboxes[j, 2] = det[j, 2] * w0/w1 - bboxes[j, 0]
|
||||
bboxes[j, 3] = det[j, 3] * h0/h1 - bboxes[j, 1]
|
||||
scores[j] = det[j, 4]
|
||||
# landmarks = (det[j, 5:15].view(1, 10) / gn_lks).view(5,2).cpu().numpy()
|
||||
kpss[j, :, :] = det[j, 5:15].reshape(5, 2) * np.array([[w0/w1,h0/h1]])
|
||||
# class_num = det[j, 15].cpu().numpy()
|
||||
# orgimg = show_results(orgimg, xywh, conf, landmarks, class_num)
|
||||
return bboxes, kpss, scores
|
||||
@@ -1,2 +0,0 @@
|
||||
from .pipelines import HunyuanVideoPipeline
|
||||
from .schedulers import FlowMatchDiscreteScheduler
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user