docs: update docs and add example workflows

This commit is contained in:
Jeffrey Wu
2024-07-29 15:48:35 +08:00
parent 46f045e570
commit 23184414fd
11 changed files with 773 additions and 12 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 127 KiB

+34 -8
View File
@@ -8,16 +8,34 @@ This project is under development. There may be compatibility issues in future u
### Install custom node
Install custom node in directory `ComfyUI/custom_nodes`.
```bash
git clone https://github.com/jeffy5/comfyui-faceless-node
pip install -r requirements.txt
```
### Setup conda
```bash
# Install conda
curl -LO https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
bash Miniconda3-latest-Linux-x86_64.sh
# Setup conda env
conda init --all
conda create --name facefusion python=3.10
conda activate facefusion
# Setup cuda
conda install conda-forge::cuda-runtime=12.4.1 cudnn=8.9.2.26 conda-forge::gputil=1.4.0
```
### Download model
All models is same as facefusion which can be found in [facefusion assets](https://github.com/facefusion/facefusion-assets).
Now it will use the following model by default. In the future, dynamic model selection will be supported. Please download the model based on the following directory structure.
Now it will use the following models by default. In the future, dynamic model selection will be supported. Please download the model based on the directory structure.
Put all into directory `CmofyUI/models/faceless`.
@@ -40,7 +58,7 @@ Put all into directory `CmofyUI/models/faceless`.
You can download models by running `download_models.py` script under directory `CmofyUI/custom_nodes/comfyui-faceless-node`.
```bash
# Install required models
# Install default required models
python download_models.py
# Install all models
@@ -49,17 +67,25 @@ python download_models.py --all
## Example workflows
### Image face swap
You can find same example workflows in directory `examples`.
![Image Face Swap](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_swap_image.jpg)
### Image face swap and face restore
### Video face swap
![Image Face Swap And Face Restore](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_swap_image.jpg)
![Video Face Swap](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_swap_video.jpg)
[image_face_swap_and_restore.json](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/examples/image_face_swap_and_restore.json)
### Load frames to preview image
### Video face swap and face restore
![Load Frames To Preview](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_load_frames.jpg)
![Video Face Swap And Face Restore](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_swap_video.jpg)
[video_face_swap_and_restore.json](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/examples/video_face_swap_and_restore.json)
### Preview video frames
![Preview Video Frames](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/workflow_preview_video_frames.jpg)
[preview_video_frames.json](https://raw.githubusercontent.com/jeffy5/comfyui-faceless-node/main/.github/examples/preview_video_frames.json)
It doesn't support preview video for now. All video will be save in directory `output/faceless`.
+232
View File
@@ -0,0 +1,232 @@
{
"last_node_id": 5,
"last_link_id": 4,
"nodes": [
{
"id": 1,
"type": "LoadImage",
"pos": [
333,
193
],
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
1
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"majic-girl.jpeg",
"image"
]
},
{
"id": 2,
"type": "LoadImage",
"pos": [
348,
613
],
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
2
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"sd3-girl.jpeg",
"image"
]
},
{
"id": 4,
"type": "FacelessFaceRestore",
"pos": [
1263,
432
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 3
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
4
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessFaceRestore"
},
"widgets_values": [
"gfpgan_1.4.onnx"
]
},
{
"id": 5,
"type": "PreviewImage",
"pos": [
1680,
430
],
"size": {
"0": 210,
"1": 250
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 4
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
},
{
"id": 3,
"type": "FacelessFaceSwap",
"pos": [
825,
434
],
"size": {
"0": 315,
"1": 126
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 1
},
{
"name": "face_image",
"type": "IMAGE",
"link": 2
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
3
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessFaceSwap"
},
"widgets_values": [
"inswapper_128.onnx",
"yoloface_8n.onnx",
"arcface_w600k_r50.onnx"
]
}
],
"links": [
[
1,
1,
0,
3,
0,
"IMAGE"
],
[
2,
2,
0,
3,
1,
"IMAGE"
],
[
3,
3,
0,
4,
0,
"IMAGE"
],
[
4,
4,
0,
5,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 462 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

+273
View File
@@ -0,0 +1,273 @@
{
"last_node_id": 15,
"last_link_id": 17,
"nodes": [
{
"id": 1,
"type": "LoadImage",
"pos": [
327,
133
],
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
9
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"majic-girl.jpeg",
"image"
]
},
{
"id": 6,
"type": "FacelessVideoFaceSwap",
"pos": [
830,
443
],
"size": {
"0": 315,
"1": 126
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "source_image",
"type": "IMAGE",
"link": 9
},
{
"name": "target_video",
"type": "FACELESS_VIDEO",
"link": 10
}
],
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
8
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessVideoFaceSwap"
},
"widgets_values": [
"inswapper_128.onnx",
"yoloface_8n.onnx",
"arcface_w600k_r50.onnx"
]
},
{
"id": 9,
"type": "FacelessLoadVideo",
"pos": [
390,
680
],
"size": {
"0": 210,
"1": 154
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
10
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessLoadVideo"
},
"widgets_values": [
"pexels-girl.mp4",
true,
-1,
2,
"image"
]
},
{
"id": 7,
"type": "FacelessVideoFaceRestore",
"pos": [
1275,
446
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"link": 8
}
],
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
16
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessVideoFaceRestore"
},
"widgets_values": [
"gfpgan_1.4.onnx"
]
},
{
"id": 15,
"type": "FacelessLoadVideoImages",
"pos": [
1687,
443
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"link": 16
}
],
"outputs": [
{
"name": "images",
"type": "IMAGE",
"links": [
17
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessLoadVideoImages"
}
},
{
"id": 11,
"type": "PreviewImage",
"pos": [
1985,
442
],
"size": [
210,
246
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 17
}
],
"properties": {
"Node name for S&R": "PreviewImage"
}
}
],
"links": [
[
8,
6,
0,
7,
0,
"FACELESS_VIDEO"
],
[
9,
1,
0,
6,
0,
"IMAGE"
],
[
10,
9,
0,
6,
1,
"FACELESS_VIDEO"
],
[
16,
7,
0,
15,
0,
"FACELESS_VIDEO"
],
[
17,
15,
0,
11,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
+229
View File
@@ -0,0 +1,229 @@
{
"last_node_id": 9,
"last_link_id": 10,
"nodes": [
{
"id": 8,
"type": "FacelessSaveVideo",
"pos": [
1752,
449
],
"size": {
"0": 210,
"1": 26
},
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"link": 7
}
],
"properties": {
"Node name for S&R": "FacelessSaveVideo"
}
},
{
"id": 1,
"type": "LoadImage",
"pos": [
327,
133
],
"size": {
"0": 315,
"1": 314
},
"flags": {},
"order": 0,
"mode": 0,
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
9
],
"shape": 3,
"slot_index": 0
},
{
"name": "MASK",
"type": "MASK",
"links": null,
"shape": 3
}
],
"properties": {
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"majic-girl.jpeg",
"image"
]
},
{
"id": 9,
"type": "FacelessLoadVideo",
"pos": [
386,
682
],
"size": {
"0": 210,
"1": 154
},
"flags": {},
"order": 1,
"mode": 0,
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
10
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessLoadVideo"
},
"widgets_values": [
"pexels-girl.mp4",
true,
-1,
-1,
"image"
]
},
{
"id": 7,
"type": "FacelessVideoFaceRestore",
"pos": [
1275,
446
],
"size": {
"0": 315,
"1": 58
},
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"link": 8
}
],
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
7
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessVideoFaceRestore"
},
"widgets_values": [
"gfpgan_1.4.onnx"
]
},
{
"id": 6,
"type": "FacelessVideoFaceSwap",
"pos": [
830,
443
],
"size": {
"0": 315,
"1": 126
},
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "source_image",
"type": "IMAGE",
"link": 9
},
{
"name": "target_video",
"type": "FACELESS_VIDEO",
"link": 10
}
],
"outputs": [
{
"name": "video",
"type": "FACELESS_VIDEO",
"links": [
8
],
"shape": 3,
"slot_index": 0
}
],
"properties": {
"Node name for S&R": "FacelessVideoFaceSwap"
},
"widgets_values": [
"inswapper_128.onnx",
"yoloface_8n.onnx",
"arcface_w600k_r50.onnx"
]
}
],
"links": [
[
7,
7,
0,
8,
0,
"FACELESS_VIDEO"
],
[
8,
6,
0,
7,
0,
"FACELESS_VIDEO"
],
[
9,
1,
0,
6,
0,
"IMAGE"
],
[
10,
9,
0,
6,
1,
"FACELESS_VIDEO"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}
+3 -3
View File
@@ -1,7 +1,7 @@
from .nodes_load_video import NodesLoadVideo
from .nodes_load_video_url import NodesLoadVideoUrl
from .nodes_load_image_url import NodesLoadImageUrl
from .nodes_load_frames import NodesLoadFrames
from .nodes_load_video_images import NodesLoadVideoImages
from .nodes_save_video import NodesSaveVideo
from .nodes_merge_videos import NodesMergeVideos
from .nodes_upload_video import NodesUploadVideo
@@ -15,7 +15,7 @@ from .nodes_video_remove_background import NodesVideoRemoveBackground
NODE_CLASS_MAPPINGS = {
"FacelessLoadVideo": NodesLoadVideo,
"FacelessLoadFrames": NodesLoadFrames,
"FacelessLoadVideoImages": NodesLoadVideoImages,
"FacelessLoadImageUrl": NodesLoadImageUrl,
"FacelessLoadVideoUrl": NodesLoadVideoUrl,
"FacelessMergeVideos": NodesMergeVideos,
@@ -33,7 +33,7 @@ NODE_CLASS_MAPPINGS = {
NODE_DISPLAY_NAME_MAPPINGS = {
"FacelessLoadVideo": "Load Video",
"FacelessLoadFrames": "Load Frames",
"FacelessLoadVideoImages": "Load Images (Video)",
"FacelessLoadImageUrl": "Load Image (Url)",
"FacelessLoadVideoUrl": "Load Video (Url)",
"FacelessMergeVideos": "Merge Videos",
@@ -7,7 +7,7 @@ import numpy as np
from ..filesystem import is_image
from ..typing import FacelessVideo
class NodesLoadFrames:
class NodesLoadVideoImages:
@classmethod
def INPUT_TYPES(cls):
return {
@@ -18,6 +18,7 @@ class NodesLoadFrames:
CATEGORY = "faceless"
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("images",)
FUNCTION = "load_frames"
def load_frames(self, video: FacelessVideo):