root commited on
Commit
fd0683b
·
1 Parent(s): ff3fa65

Add facefusion directory as regular files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. facefusion +0 -1
  2. facefusion/.editorconfig +8 -0
  3. facefusion/.flake8 +3 -0
  4. facefusion/.github/FUNDING.yml +2 -0
  5. facefusion/.github/workflows/ci.yml +35 -0
  6. facefusion/.gitignore +3 -0
  7. facefusion/.install/LICENSE.md +3 -0
  8. facefusion/.install/facefusion.ico +0 -0
  9. facefusion/.install/facefusion.nsi +183 -0
  10. facefusion/LICENSE.md +3 -0
  11. facefusion/README.md +113 -0
  12. facefusion/core.py +443 -0
  13. facefusion/facefusion.ini +74 -0
  14. facefusion/facefusion/__init__.py +0 -0
  15. facefusion/facefusion/__pycache__/__init__.cpython-310.pyc +0 -0
  16. facefusion/facefusion/__pycache__/audio.cpython-310.pyc +0 -0
  17. facefusion/facefusion/__pycache__/choices.cpython-310.pyc +0 -0
  18. facefusion/facefusion/__pycache__/common_helper.cpython-310.pyc +0 -0
  19. facefusion/facefusion/__pycache__/config.cpython-310.pyc +0 -0
  20. facefusion/facefusion/__pycache__/content_analyser.cpython-310.pyc +0 -0
  21. facefusion/facefusion/__pycache__/core.cpython-310.pyc +0 -0
  22. facefusion/facefusion/__pycache__/download.cpython-310.pyc +0 -0
  23. facefusion/facefusion/__pycache__/execution.cpython-310.pyc +0 -0
  24. facefusion/facefusion/__pycache__/face_analyser.cpython-310.pyc +0 -0
  25. facefusion/facefusion/__pycache__/face_helper.cpython-310.pyc +0 -0
  26. facefusion/facefusion/__pycache__/face_masker.cpython-310.pyc +0 -0
  27. facefusion/facefusion/__pycache__/face_store.cpython-310.pyc +0 -0
  28. facefusion/facefusion/__pycache__/ffmpeg.cpython-310.pyc +0 -0
  29. facefusion/facefusion/__pycache__/filesystem.cpython-310.pyc +0 -0
  30. facefusion/facefusion/__pycache__/globals.cpython-310.pyc +0 -0
  31. facefusion/facefusion/__pycache__/logger.cpython-310.pyc +0 -0
  32. facefusion/facefusion/__pycache__/memory.cpython-310.pyc +0 -0
  33. facefusion/facefusion/__pycache__/metadata.cpython-310.pyc +0 -0
  34. facefusion/facefusion/__pycache__/my_typing.cpython-310.pyc +0 -0
  35. facefusion/facefusion/__pycache__/normalizer.cpython-310.pyc +0 -0
  36. facefusion/facefusion/__pycache__/process_manager.cpython-310.pyc +0 -0
  37. facefusion/facefusion/__pycache__/statistics.cpython-310.pyc +0 -0
  38. facefusion/facefusion/__pycache__/thread_helper.cpython-310.pyc +0 -0
  39. facefusion/facefusion/__pycache__/typing.cpython-310.pyc +0 -0
  40. facefusion/facefusion/__pycache__/vision.cpython-310.pyc +0 -0
  41. facefusion/facefusion/__pycache__/voice_extractor.cpython-310.pyc +0 -0
  42. facefusion/facefusion/__pycache__/wording.cpython-310.pyc +0 -0
  43. facefusion/facefusion/audio.py +137 -0
  44. facefusion/facefusion/choices.py +37 -0
  45. facefusion/facefusion/common_helper.py +46 -0
  46. facefusion/facefusion/config.py +91 -0
  47. facefusion/facefusion/content_analyser.py +112 -0
  48. facefusion/facefusion/download.py +48 -0
  49. facefusion/facefusion/execution.py +112 -0
  50. facefusion/facefusion/face_analyser.py +586 -0
facefusion DELETED
@@ -1 +0,0 @@
1
- Subproject commit 126845c24c5c60699311d1f822de1a056d83b3bd
 
 
facefusion/.editorconfig ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ root = true
2
+
3
+ [*]
4
+ end_of_line = lf
5
+ insert_final_newline = true
6
+ indent_size = 4
7
+ indent_style = tab
8
+ trim_trailing_whitespace = true
facefusion/.flake8 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ [flake8]
2
+ select = E3, E4, F
3
+ per-file-ignores = facefusion/core.py:E402
facefusion/.github/FUNDING.yml ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ github: henryruhs
2
+ custom: [ buymeacoffee.com/henryruhs, paypal.me/henryruhs ]
facefusion/.github/workflows/ci.yml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: ci
2
+
3
+ on: [ push, pull_request ]
4
+
5
+ jobs:
6
+ lint:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - name: Checkout
10
+ uses: actions/checkout@v4
11
+ - name: Set up Python 3.10
12
+ uses: actions/setup-python@v5
13
+ with:
14
+ python-version: '3.10'
15
+ - run: pip install flake8
16
+ - run: pip install mypy
17
+ - run: flake8 run.py facefusion tests
18
+ - run: mypy run.py facefusion tests
19
+ test:
20
+ strategy:
21
+ matrix:
22
+ os: [ macos-13, ubuntu-latest, windows-latest ]
23
+ runs-on: ${{ matrix.os }}
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v4
27
+ - name: Set up FFMpeg
28
+ uses: FedericoCarboni/setup-ffmpeg@v3
29
+ - name: Set up Python 3.10
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: '3.10'
33
+ - run: python install.py --onnxruntime default --skip-conda
34
+ - run: pip install pytest
35
+ - run: pytest
facefusion/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .assets
2
+ .idea
3
+ .vscode
facefusion/.install/LICENSE.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ CC-BY-4.0 license
2
+
3
+ Copyright (c) 2024 Henry Ruhs
facefusion/.install/facefusion.ico ADDED
facefusion/.install/facefusion.nsi ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ !include MUI2.nsh
2
+ !include nsDialogs.nsh
3
+ !include LogicLib.nsh
4
+
5
+ RequestExecutionLevel admin
6
+ ManifestDPIAware true
7
+
8
+ Name 'FaceFusion 2.6.0'
9
+ OutFile 'FaceFusion_2.6.0.exe'
10
+
11
+ !define MUI_ICON 'facefusion.ico'
12
+
13
+ !insertmacro MUI_PAGE_DIRECTORY
14
+ Page custom InstallPage PostInstallPage
15
+ !insertmacro MUI_PAGE_INSTFILES
16
+ !insertmacro MUI_LANGUAGE English
17
+
18
+ Var UseDefault
19
+ Var UseCuda
20
+ Var UseDirectMl
21
+ Var UseOpenVino
22
+
23
+ Function .onInit
24
+ StrCpy $INSTDIR 'C:\FaceFusion'
25
+ FunctionEnd
26
+
27
+ Function InstallPage
28
+ nsDialogs::Create 1018
29
+ !insertmacro MUI_HEADER_TEXT 'Choose Your Accelerator' 'Choose your accelerator based on the graphics card.'
30
+
31
+ ${NSD_CreateRadioButton} 0 40u 100% 10u 'Default'
32
+ Pop $UseDefault
33
+
34
+ ${NSD_CreateRadioButton} 0 55u 100% 10u 'CUDA (NVIDIA)'
35
+ Pop $UseCuda
36
+
37
+ ${NSD_CreateRadioButton} 0 70u 100% 10u 'DirectML (AMD, Intel, NVIDIA)'
38
+ Pop $UseDirectMl
39
+
40
+ ${NSD_CreateRadioButton} 0 85u 100% 10u 'OpenVINO (Intel)'
41
+ Pop $UseOpenVino
42
+
43
+ ${NSD_Check} $UseDefault
44
+
45
+ nsDialogs::Show
46
+ FunctionEnd
47
+
48
+ Function PostInstallPage
49
+ ${NSD_GetState} $UseDefault $UseDefault
50
+ ${NSD_GetState} $UseCuda $UseCuda
51
+ ${NSD_GetState} $UseDirectMl $UseDirectMl
52
+ ${NSD_GetState} $UseOpenVino $UseOpenVino
53
+ FunctionEnd
54
+
55
+ Function Destroy
56
+ ${If} ${Silent}
57
+ Quit
58
+ ${Else}
59
+ Abort
60
+ ${EndIf}
61
+ FunctionEnd
62
+
63
+ Section 'Prepare Your Platform'
64
+ DetailPrint 'Install GIT'
65
+ inetc::get 'https://github.com/git-for-windows/git/releases/download/v2.45.1.windows.1/Git-2.45.1-64-bit.exe' '$TEMP\Git.exe'
66
+ ExecWait '$TEMP\Git.exe /CURRENTUSER /VERYSILENT /DIR=$LOCALAPPDATA\Programs\Git' $0
67
+ Delete '$TEMP\Git.exe'
68
+
69
+ ${If} $0 > 0
70
+ DetailPrint 'Git installation aborted with error code $0'
71
+ Call Destroy
72
+ ${EndIf}
73
+
74
+ DetailPrint 'Uninstall Conda'
75
+ ExecWait '$LOCALAPPDATA\Programs\Miniconda3\Uninstall-Miniconda3.exe /S _?=$LOCALAPPDATA\Programs\Miniconda3'
76
+ RMDir /r '$LOCALAPPDATA\Programs\Miniconda3'
77
+
78
+ DetailPrint 'Install Conda'
79
+ inetc::get 'https://repo.anaconda.com/miniconda/Miniconda3-py310_24.3.0-0-Windows-x86_64.exe' '$TEMP\Miniconda3.exe'
80
+ ExecWait '$TEMP\Miniconda3.exe /InstallationType=JustMe /AddToPath=1 /S /D=$LOCALAPPDATA\Programs\Miniconda3' $1
81
+ Delete '$TEMP\Miniconda3.exe'
82
+
83
+ ${If} $1 > 0
84
+ DetailPrint 'Conda installation aborted with error code $1'
85
+ Call Destroy
86
+ ${EndIf}
87
+ SectionEnd
88
+
89
+ Section 'Download Your Copy'
90
+ SetOutPath $INSTDIR
91
+
92
+ DetailPrint 'Download Your Copy'
93
+ RMDir /r $INSTDIR
94
+ nsExec::Exec '$LOCALAPPDATA\Programs\Git\cmd\git.exe clone https://github.com/facefusion/facefusion --branch 2.6.0 .'
95
+ SectionEnd
96
+
97
+ Section 'Setup Your Environment'
98
+ DetailPrint 'Setup Your Environment'
99
+ nsExec::Exec '$LOCALAPPDATA\Programs\Miniconda3\Scripts\conda.exe init --all'
100
+ nsExec::Exec '$LOCALAPPDATA\Programs\Miniconda3\Scripts\conda.exe create --name facefusion python=3.10 --yes'
101
+ SectionEnd
102
+
103
+ Section 'Create Install Batch'
104
+ SetOutPath $INSTDIR
105
+
106
+ FileOpen $0 install-ffmpeg.bat w
107
+ FileOpen $1 install-accelerator.bat w
108
+ FileOpen $2 install-application.bat w
109
+
110
+ FileWrite $0 '@echo off && conda activate facefusion && conda install conda-forge::ffmpeg=7.0.0 --yes'
111
+ ${If} $UseCuda == 1
112
+ FileWrite $1 '@echo off && conda activate facefusion && conda install cudatoolkit=11.8 cudnn=8.9.2.26 conda-forge::gputil=1.4.0 conda-forge::zlib-wapi --yes'
113
+ FileWrite $2 '@echo off && conda activate facefusion && python install.py --onnxruntime cuda-11.8'
114
+ ${ElseIf} $UseDirectMl == 1
115
+ FileWrite $2 '@echo off && conda activate facefusion && python install.py --onnxruntime directml'
116
+ ${ElseIf} $UseOpenVino == 1
117
+ FileWrite $1 '@echo off && conda activate facefusion && conda install conda-forge::openvino=2023.1.0 --yes'
118
+ FileWrite $2 '@echo off && conda activate facefusion && python install.py --onnxruntime openvino'
119
+ ${Else}
120
+ FileWrite $2 '@echo off && conda activate facefusion && python install.py --onnxruntime default'
121
+ ${EndIf}
122
+
123
+ FileClose $0
124
+ FileClose $1
125
+ FileClose $2
126
+ SectionEnd
127
+
128
+ Section 'Install Your FFmpeg'
129
+ SetOutPath $INSTDIR
130
+
131
+ DetailPrint 'Install Your FFmpeg'
132
+ nsExec::ExecToLog 'install-ffmpeg.bat'
133
+ SectionEnd
134
+
135
+ Section 'Install Your Accelerator'
136
+ SetOutPath $INSTDIR
137
+
138
+ DetailPrint 'Install Your Accelerator'
139
+ nsExec::ExecToLog 'install-accelerator.bat'
140
+ SectionEnd
141
+
142
+ Section 'Install The Application'
143
+ SetOutPath $INSTDIR
144
+
145
+ DetailPrint 'Install The Application'
146
+ nsExec::ExecToLog 'install-application.bat'
147
+ SectionEnd
148
+
149
+ Section 'Create Run Batch'
150
+ SetOutPath $INSTDIR
151
+ FileOpen $0 run.bat w
152
+ FileWrite $0 '@echo off && conda activate facefusion && python run.py %*'
153
+ FileClose $0
154
+ SectionEnd
155
+
156
+ Section 'Register The Application'
157
+ DetailPrint 'Register The Application'
158
+
159
+ CreateDirectory $SMPROGRAMS\FaceFusion
160
+ CreateShortcut '$SMPROGRAMS\FaceFusion\FaceFusion.lnk' $INSTDIR\run.bat '--open-browser' $INSTDIR\.install\facefusion.ico
161
+ CreateShortcut '$SMPROGRAMS\FaceFusion\FaceFusion Benchmark.lnk' $INSTDIR\run.bat '--ui-layouts benchmark --open-browser' $INSTDIR\.install\facefusion.ico
162
+ CreateShortcut '$SMPROGRAMS\FaceFusion\FaceFusion Webcam.lnk' $INSTDIR\run.bat '--ui-layouts webcam --open-browser' $INSTDIR\.install\facefusion.ico
163
+
164
+ CreateShortcut $DESKTOP\FaceFusion.lnk $INSTDIR\run.bat '--open-browser' $INSTDIR\.install\facefusion.ico
165
+
166
+ WriteUninstaller $INSTDIR\Uninstall.exe
167
+
168
+ WriteRegStr HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion DisplayName 'FaceFusion'
169
+ WriteRegStr HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion DisplayVersion '2.6.0'
170
+ WriteRegStr HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion Publisher 'Henry Ruhs'
171
+ WriteRegStr HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion InstallLocation $INSTDIR
172
+ WriteRegStr HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion UninstallString $INSTDIR\uninstall.exe
173
+ SectionEnd
174
+
175
+ Section 'Uninstall'
176
+ nsExec::Exec '$LOCALAPPDATA\Programs\Miniconda3\Scripts\conda.exe env remove --name facefusion --yes'
177
+
178
+ Delete $DESKTOP\FaceFusion.lnk
179
+ RMDir /r $SMPROGRAMS\FaceFusion
180
+ RMDir /r $INSTDIR
181
+
182
+ DeleteRegKey HKLM SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\FaceFusion
183
+ SectionEnd
facefusion/LICENSE.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ MIT license
2
+
3
+ Copyright (c) 2024 Henry Ruhs
facefusion/README.md ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FaceFusion
2
+ ==========
3
+
4
+ > Next generation face swapper and enhancer.
5
+
6
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/facefusion/facefusion/ci.yml.svg?branch=master)](https://github.com/facefusion/facefusion/actions?query=workflow:ci)
7
+ ![License](https://img.shields.io/badge/license-MIT-green)
8
+
9
+
10
+ Preview
11
+ -------
12
+
13
+ ![Preview](https://raw.githubusercontent.com/facefusion/facefusion/master/.github/preview.png?sanitize=true)
14
+
15
+
16
+ Installation
17
+ ------------
18
+
19
+ Be aware, the [installation](https://docs.facefusion.io/installation) needs technical skills and is not recommended for beginners. In case you are not comfortable using a terminal, our [Windows Installer](https://buymeacoffee.com/henryruhs/e/251939) can have you up and running in minutes.
20
+
21
+
22
+ Usage
23
+ -----
24
+
25
+ Run the command:
26
+
27
+ ```
28
+ python run.py [options]
29
+
30
+ options:
31
+ -h, --help show this help message and exit
32
+ -c CONFIG_PATH, --config CONFIG_PATH choose the config file to override defaults
33
+ -s SOURCE_PATHS, --source SOURCE_PATHS choose single or multiple source images or audios
34
+ -t TARGET_PATH, --target TARGET_PATH choose single target image or video
35
+ -o OUTPUT_PATH, --output OUTPUT_PATH specify the output file or directory
36
+ -v, --version show program's version number and exit
37
+
38
+ misc:
39
+ --force-download force automate downloads and exit
40
+ --skip-download omit automate downloads and remote lookups
41
+ --headless run the program without a user interface
42
+ --log-level {error,warn,info,debug} adjust the message severity displayed in the terminal
43
+
44
+ execution:
45
+ --execution-device-id EXECUTION_DEVICE_ID specify the device used for processing
46
+ --execution-providers EXECUTION_PROVIDERS [EXECUTION_PROVIDERS ...] accelerate the model inference using different providers (choices: cpu, ...)
47
+ --execution-thread-count [1-128] specify the amount of parallel threads while processing
48
+ --execution-queue-count [1-32] specify the amount of frames each thread is processing
49
+
50
+ memory:
51
+ --video-memory-strategy {strict,moderate,tolerant} balance fast frame processing and low VRAM usage
52
+ --system-memory-limit [0-128] limit the available RAM that can be used while processing
53
+
54
+ face analyser:
55
+ --face-analyser-order {left-right,right-left,top-bottom,bottom-top,small-large,large-small,best-worst,worst-best} specify the order in which the face analyser detects faces
56
+ --face-analyser-age {child,teen,adult,senior} filter the detected faces based on their age
57
+ --face-analyser-gender {female,male} filter the detected faces based on their gender
58
+ --face-detector-model {many,retinaface,scrfd,yoloface,yunet} choose the model responsible for detecting the face
59
+ --face-detector-size FACE_DETECTOR_SIZE specify the size of the frame provided to the face detector
60
+ --face-detector-score [0.0-0.95] filter the detected faces base on the confidence score
61
+ --face-landmarker-score [0.0-0.95] filter the detected landmarks base on the confidence score
62
+
63
+ face selector:
64
+ --face-selector-mode {many,one,reference} use reference based tracking or simple matching
65
+ --reference-face-position REFERENCE_FACE_POSITION specify the position used to create the reference face
66
+ --reference-face-distance [0.0-1.45] specify the desired similarity between the reference face and target face
67
+ --reference-frame-number REFERENCE_FRAME_NUMBER specify the frame used to create the reference face
68
+
69
+ face mask:
70
+ --face-mask-types FACE_MASK_TYPES [FACE_MASK_TYPES ...] mix and match different face mask types (choices: box, occlusion, region)
71
+ --face-mask-blur [0.0-0.95] specify the degree of blur applied the box mask
72
+ --face-mask-padding FACE_MASK_PADDING [FACE_MASK_PADDING ...] apply top, right, bottom and left padding to the box mask
73
+ --face-mask-regions FACE_MASK_REGIONS [FACE_MASK_REGIONS ...] choose the facial features used for the region mask (choices: skin, left-eyebrow, right-eyebrow, left-eye, right-eye, glasses, nose, mouth, upper-lip, lower-lip)
74
+
75
+ frame extraction:
76
+ --trim-frame-start TRIM_FRAME_START specify the the start frame of the target video
77
+ --trim-frame-end TRIM_FRAME_END specify the the end frame of the target video
78
+ --temp-frame-format {bmp,jpg,png} specify the temporary resources format
79
+ --keep-temp keep the temporary resources after processing
80
+
81
+ output creation:
82
+ --output-image-quality [0-100] specify the image quality which translates to the compression factor
83
+ --output-image-resolution OUTPUT_IMAGE_RESOLUTION specify the image output resolution based on the target image
84
+ --output-video-encoder {libx264,libx265,libvpx-vp9,h264_nvenc,hevc_nvenc,h264_amf,hevc_amf} specify the encoder use for the video compression
85
+ --output-video-preset {ultrafast,superfast,veryfast,faster,fast,medium,slow,slower,veryslow} balance fast video processing and video file size
86
+ --output-video-quality [0-100] specify the video quality which translates to the compression factor
87
+ --output-video-resolution OUTPUT_VIDEO_RESOLUTION specify the video output resolution based on the target video
88
+ --output-video-fps OUTPUT_VIDEO_FPS specify the video output fps based on the target video
89
+ --skip-audio omit the audio from the target video
90
+
91
+ frame processors:
92
+ --frame-processors FRAME_PROCESSORS [FRAME_PROCESSORS ...] load a single or multiple frame processors. (choices: face_debugger, face_enhancer, face_swapper, frame_colorizer, frame_enhancer, lip_syncer, ...)
93
+ --face-debugger-items FACE_DEBUGGER_ITEMS [FACE_DEBUGGER_ITEMS ...] load a single or multiple frame processors (choices: bounding-box, face-landmark-5, face-landmark-5/68, face-landmark-68, face-landmark-68/5, face-mask, face-detector-score, face-landmarker-score, age, gender)
94
+ --face-enhancer-model {codeformer,gfpgan_1.2,gfpgan_1.3,gfpgan_1.4,gpen_bfr_256,gpen_bfr_512,gpen_bfr_1024,gpen_bfr_2048,restoreformer_plus_plus} choose the model responsible for enhancing the face
95
+ --face-enhancer-blend [0-100] blend the enhanced into the previous face
96
+ --face-swapper-model {blendswap_256,inswapper_128,inswapper_128_fp16,simswap_256,simswap_512_unofficial,uniface_256} choose the model responsible for swapping the face
97
+ --frame-colorizer-model {ddcolor,ddcolor_artistic,deoldify,deoldify_artistic,deoldify_stable} choose the model responsible for colorizing the frame
98
+ --frame-colorizer-blend [0-100] blend the colorized into the previous frame
99
+ --frame-colorizer-size {192x192,256x256,384x384,512x512} specify the size of the frame provided to the frame colorizer
100
+ --frame-enhancer-model {clear_reality_x4,lsdir_x4,nomos8k_sc_x4,real_esrgan_x2,real_esrgan_x2_fp16,real_esrgan_x4,real_esrgan_x4_fp16,real_hatgan_x4,span_kendata_x4,ultra_sharp_x4} choose the model responsible for enhancing the frame
101
+ --frame-enhancer-blend [0-100] blend the enhanced into the previous frame
102
+ --lip-syncer-model {wav2lip_gan} choose the model responsible for syncing the lips
103
+
104
+ uis:
105
+ --open-browser open the browser once the program is ready
106
+ --ui-layouts UI_LAYOUTS [UI_LAYOUTS ...] launch a single or multiple UI layouts (choices: benchmark, default, webcam, ...)
107
+ ```
108
+
109
+
110
+ Documentation
111
+ -------------
112
+
113
+ Read the [documentation](https://docs.facefusion.io) for a deep dive.
facefusion/core.py ADDED
@@ -0,0 +1,443 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ['OMP_NUM_THREADS'] = '1'
4
+
5
+ import signal
6
+ import sys
7
+ import warnings
8
+ import shutil
9
+ import numpy
10
+ import onnxruntime
11
+ from time import sleep, time
12
+ from argparse import ArgumentParser, HelpFormatter
13
+
14
+ import facefusion.choices
15
+ import facefusion.globals
16
+ from facefusion.face_analyser import get_one_face, get_average_face
17
+ from facefusion.face_store import get_reference_faces, append_reference_face
18
+ from facefusion import face_analyser, face_masker, content_analyser, config, process_manager, metadata, logger, wording, voice_extractor
19
+ from facefusion.content_analyser import analyse_image, analyse_video
20
+ from facefusion.processors.frame.core import get_frame_processors_modules, load_frame_processor_module
21
+ from facefusion.common_helper import create_metavar, get_first
22
+ from facefusion.execution import encode_execution_providers, decode_execution_providers
23
+ from facefusion.normalizer import normalize_output_path, normalize_padding, normalize_fps
24
+ from facefusion.memory import limit_system_memory
25
+ from facefusion.statistics import conditional_log_statistics
26
+ from facefusion.download import conditional_download
27
+ from facefusion.filesystem import get_temp_frame_paths, get_temp_file_path, create_temp, move_temp, clear_temp, is_image, is_video, filter_audio_paths, resolve_relative_path, list_directory
28
+ from facefusion.ffmpeg import extract_frames, merge_video, copy_image, finalize_image, restore_audio, replace_audio
29
+ from facefusion.vision import read_image, read_static_images, detect_image_resolution, restrict_video_fps, create_image_resolutions, get_video_frame, detect_video_resolution, detect_video_fps, restrict_video_resolution, restrict_image_resolution, create_video_resolutions, pack_resolution, unpack_resolution
30
+
31
+ onnxruntime.set_default_logger_severity(3)
32
+ warnings.filterwarnings('ignore', category = UserWarning, module = 'gradio')
33
+
34
+
35
+ def cli() -> None:
36
+ signal.signal(signal.SIGINT, lambda signal_number, frame: destroy())
37
+ program = ArgumentParser(formatter_class=lambda prog: HelpFormatter(prog, max_help_position=200), add_help=False)
38
+ # general
39
+ program.add_argument('-c', '--config', help=wording.get('help.config'), dest='config_path', default='facefusion.ini')
40
+ apply_config(program)
41
+ program.add_argument('-s', '--source', help=wording.get('help.source'), action='append', dest='source_paths', default=config.get_str_list('general.source_paths'))
42
+ program.add_argument('-t', '--target', help=wording.get('help.target'), dest='target_path', default=config.get_str_value('general.target_path'))
43
+ program.add_argument('-o', '--output', help=wording.get('help.output'), dest='output_path', default=config.get_str_value('general.output_path'))
44
+ program.add_argument('-v', '--version', version=metadata.get('name') + ' ' + metadata.get('version'), action='version')
45
+ # misc
46
+ group_misc = program.add_argument_group('misc')
47
+ group_misc.add_argument('--force-download', help=wording.get('help.force_download'), action='store_true', default=config.get_bool_value('misc.force_download'))
48
+ group_misc.add_argument('--skip-download', help=wording.get('help.skip_download'), action='store_true', default=config.get_bool_value('misc.skip_download'))
49
+ group_misc.add_argument('--headless', help=wording.get('help.headless'), action='store_true', default=config.get_bool_value('misc.headless'))
50
+ group_misc.add_argument('--log-level', help=wording.get('help.log_level'), default=config.get_str_value('misc.log_level', 'info'), choices=logger.get_log_levels())
51
+ # execution
52
+ execution_providers = encode_execution_providers(onnxruntime.get_available_providers())
53
+ group_execution = program.add_argument_group('execution')
54
+ group_execution.add_argument('--execution-device-id', help=wording.get('help.execution_device_id'), default=config.get_str_value('execution.face_detector_size', '0'))
55
+ group_execution.add_argument('--execution-providers', help=wording.get('help.execution_providers').format(choices=', '.join(execution_providers)), default=config.get_str_list('execution.execution_providers', 'cpu'), choices=execution_providers, nargs='+', metavar='EXECUTION_PROVIDERS')
56
+ group_execution.add_argument('--execution-thread-count', help=wording.get('help.execution_thread_count'), type=int, default=config.get_int_value('execution.execution_thread_count', '4'), choices=facefusion.choices.execution_thread_count_range, metavar=create_metavar(facefusion.choices.execution_thread_count_range))
57
+ group_execution.add_argument('--execution-queue-count', help=wording.get('help.execution_queue_count'), type=int, default=config.get_int_value('execution.execution_queue_count', '1'), choices=facefusion.choices.execution_queue_count_range, metavar=create_metavar(facefusion.choices.execution_queue_count_range))
58
+ # memory
59
+ group_memory = program.add_argument_group('memory')
60
+ group_memory.add_argument('--video-memory-strategy', help=wording.get('help.video_memory_strategy'), default=config.get_str_value('memory.video_memory_strategy', 'strict'), choices=facefusion.choices.video_memory_strategies)
61
+ group_memory.add_argument('--system-memory-limit', help=wording.get('help.system_memory_limit'), type=int, default=config.get_int_value('memory.system_memory_limit', '0'), choices=facefusion.choices.system_memory_limit_range, metavar=create_metavar(facefusion.choices.system_memory_limit_range))
62
+ # face analyser
63
+ group_face_analyser = program.add_argument_group('face analyser')
64
+ group_face_analyser.add_argument('--face-analyser-order', help=wording.get('help.face_analyser_order'), default=config.get_str_value('face_analyser.face_analyser_order', 'left-right'), choices=facefusion.choices.face_analyser_orders)
65
+ group_face_analyser.add_argument('--face-analyser-age', help=wording.get('help.face_analyser_age'), default=config.get_str_value('face_analyser.face_analyser_age'), choices=facefusion.choices.face_analyser_ages)
66
+ group_face_analyser.add_argument('--face-analyser-gender', help=wording.get('help.face_analyser_gender'), default=config.get_str_value('face_analyser.face_analyser_gender'), choices=facefusion.choices.face_analyser_genders)
67
+ group_face_analyser.add_argument('--face-detector-model', help=wording.get('help.face_detector_model'), default=config.get_str_value('face_analyser.face_detector_model', 'yoloface'), choices=facefusion.choices.face_detector_set.keys())
68
+ group_face_analyser.add_argument('--face-detector-size', help=wording.get('help.face_detector_size'), default=config.get_str_value('face_analyser.face_detector_size', '640x640'))
69
+ group_face_analyser.add_argument('--face-detector-score', help=wording.get('help.face_detector_score'), type=float, default=config.get_float_value('face_analyser.face_detector_score', '0.5'), choices=facefusion.choices.face_detector_score_range, metavar=create_metavar(facefusion.choices.face_detector_score_range))
70
+ group_face_analyser.add_argument('--face-landmarker-score', help=wording.get('help.face_landmarker_score'), type=float, default=config.get_float_value('face_analyser.face_landmarker_score', '0.5'), choices=facefusion.choices.face_landmarker_score_range, metavar=create_metavar(facefusion.choices.face_landmarker_score_range))
71
+ # face selector
72
+ group_face_selector = program.add_argument_group('face selector')
73
+ group_face_selector.add_argument('--face-selector-mode', help=wording.get('help.face_selector_mode'), default=config.get_str_value('face_selector.face_selector_mode', 'reference'), choices=facefusion.choices.face_selector_modes)
74
+ group_face_selector.add_argument('--reference-face-position', help=wording.get('help.reference_face_position'), type=int, default=config.get_int_value('face_selector.reference_face_position', '0'))
75
+ group_face_selector.add_argument('--reference-face-distance', help=wording.get('help.reference_face_distance'), type=float, default=config.get_float_value('face_selector.reference_face_distance', '0.6'), choices=facefusion.choices.reference_face_distance_range, metavar=create_metavar(facefusion.choices.reference_face_distance_range))
76
+ group_face_selector.add_argument('--reference-frame-number', help=wording.get('help.reference_frame_number'), type=int, default=config.get_int_value('face_selector.reference_frame_number', '0'))
77
+ # face mask
78
+ group_face_mask = program.add_argument_group('face mask')
79
+ group_face_mask.add_argument('--face-mask-types', help=wording.get('help.face_mask_types').format(choices=', '.join(facefusion.choices.face_mask_types)), default=config.get_str_list('face_mask.face_mask_types', 'box'), choices=facefusion.choices.face_mask_types, nargs='+', metavar='FACE_MASK_TYPES')
80
+ group_face_mask.add_argument('--face-mask-blur', help=wording.get('help.face_mask_blur'), type=float, default=config.get_float_value('face_mask.face_mask_blur', '0.3'), choices=facefusion.choices.face_mask_blur_range, metavar=create_metavar(facefusion.choices.face_mask_blur_range))
81
+ group_face_mask.add_argument('--face-mask-padding', help=wording.get('help.face_mask_padding'), type=int, default=config.get_int_list('face_mask.face_mask_padding', '0 0 0 0'), nargs='+')
82
+ group_face_mask.add_argument('--face-mask-regions', help=wording.get('help.face_mask_regions').format(choices=', '.join(facefusion.choices.face_mask_regions)), default=config.get_str_list('face_mask.face_mask_regions', ' '.join(facefusion.choices.face_mask_regions)), choices=facefusion.choices.face_mask_regions, nargs='+', metavar='FACE_MASK_REGIONS')
83
+ # frame extraction
84
+ group_frame_extraction = program.add_argument_group('frame extraction')
85
+ group_frame_extraction.add_argument('--trim-frame-start', help=wording.get('help.trim_frame_start'), type=int, default=facefusion.config.get_int_value('frame_extraction.trim_frame_start'))
86
+ group_frame_extraction.add_argument('--trim-frame-end', help=wording.get('help.trim_frame_end'), type=int, default=facefusion.config.get_int_value('frame_extraction.trim_frame_end'))
87
+ group_frame_extraction.add_argument('--temp-frame-format', help=wording.get('help.temp_frame_format'), default=config.get_str_value('frame_extraction.temp_frame_format', 'png'), choices=facefusion.choices.temp_frame_formats)
88
+ group_frame_extraction.add_argument('--keep-temp', help=wording.get('help.keep_temp'), action='store_true', default=config.get_bool_value('frame_extraction.keep_temp'))
89
+ # output creation
90
+ group_output_creation = program.add_argument_group('output creation')
91
+ group_output_creation.add_argument('--output-image-quality', help=wording.get('help.output_image_quality'), type=int, default=config.get_int_value('output_creation.output_image_quality', '80'), choices=facefusion.choices.output_image_quality_range, metavar=create_metavar(facefusion.choices.output_image_quality_range))
92
+ group_output_creation.add_argument('--output-image-resolution', help=wording.get('help.output_image_resolution'), default=config.get_str_value('output_creation.output_image_resolution'))
93
+ group_output_creation.add_argument('--output-video-encoder', help=wording.get('help.output_video_encoder'), default=config.get_str_value('output_creation.output_video_encoder', 'libx264'), choices=facefusion.choices.output_video_encoders)
94
+ group_output_creation.add_argument('--output-video-preset', help=wording.get('help.output_video_preset'), default=config.get_str_value('output_creation.output_video_preset', 'veryfast'), choices=facefusion.choices.output_video_presets)
95
+ group_output_creation.add_argument('--output-video-quality', help=wording.get('help.output_video_quality'), type=int, default=config.get_int_value('output_creation.output_video_quality', '80'), choices=facefusion.choices.output_video_quality_range, metavar=create_metavar(facefusion.choices.output_video_quality_range))
96
+ group_output_creation.add_argument('--output-video-resolution', help=wording.get('help.output_video_resolution'), default=config.get_str_value('output_creation.output_video_resolution'))
97
+ group_output_creation.add_argument('--output-video-fps', help=wording.get('help.output_video_fps'), type=float, default=config.get_str_value('output_creation.output_video_fps'))
98
+ group_output_creation.add_argument('--skip-audio', help=wording.get('help.skip_audio'), action='store_true', default=config.get_bool_value('output_creation.skip_audio'))
99
+ # frame processors
100
+ base_dir = os.path.dirname(os.path.abspath(__file__))
101
+ modules_path = os.path.join(base_dir, "facefusion", "processors", "frame", "modules")
102
+ available_frame_processors = list_directory(modules_path)
103
+
104
+ # Debug statement to print available frame processors
105
+ print(f"Available frame processors: {available_frame_processors}")
106
+
107
+ if not isinstance(available_frame_processors, list):
108
+ available_frame_processors = list(available_frame_processors)
109
+
110
+ program = ArgumentParser(parents=[program], formatter_class=program.formatter_class, add_help=True)
111
+ group_frame_processors = program.add_argument_group('frame processors')
112
+ group_frame_processors.add_argument('--frame-processors', help=wording.get('help.frame_processors').format(choices=', '.join(available_frame_processors)), default=config.get_str_list('frame_processors.frame_processors', 'face_swapper'), nargs='+')
113
+ for frame_processor in available_frame_processors:
114
+ frame_processor_module = load_frame_processor_module(frame_processor)
115
+ frame_processor_module.register_args(group_frame_processors)
116
+ run(program)
117
+
118
+
119
+ def apply_config(program: ArgumentParser) -> None:
120
+ known_args = program.parse_known_args()
121
+ facefusion.globals.config_path = get_first(known_args).config_path
122
+
123
+
124
+ def validate_args(program: ArgumentParser) -> None:
125
+ try:
126
+ for action in program._actions:
127
+ if action.default:
128
+ if isinstance(action.default, list):
129
+ for default in action.default:
130
+ program._check_value(action, default)
131
+ else:
132
+ program._check_value(action, action.default)
133
+ except Exception as exception:
134
+ program.error(str(exception))
135
+
136
+
137
+ def apply_args(program: ArgumentParser) -> None:
138
+ args = program.parse_args()
139
+ # general
140
+ facefusion.globals.source_paths = args.source_paths
141
+ facefusion.globals.target_path = args.target_path
142
+ facefusion.globals.output_path = args.output_path
143
+ # misc
144
+ facefusion.globals.force_download = args.force_download
145
+ facefusion.globals.skip_download = args.skip_download
146
+ facefusion.globals.headless = args.headless
147
+ facefusion.globals.log_level = args.log_level
148
+ # execution
149
+ facefusion.globals.execution_device_id = args.execution_device_id
150
+ facefusion.globals.execution_providers = decode_execution_providers(args.execution_providers)
151
+ facefusion.globals.execution_thread_count = args.execution_thread_count
152
+ facefusion.globals.execution_queue_count = args.execution_queue_count
153
+ # memory
154
+ facefusion.globals.video_memory_strategy = args.video_memory_strategy
155
+ facefusion.globals.system_memory_limit = args.system_memory_limit
156
+ # face analyser
157
+ facefusion.globals.face_analyser_order = args.face_analyser_order
158
+ facefusion.globals.face_analyser_age = args.face_analyser_age
159
+ facefusion.globals.face_analyser_gender = args.face_analyser_gender
160
+ facefusion.globals.face_detector_model = args.face_detector_model
161
+ if args.face_detector_size in facefusion.choices.face_detector_set[args.face_detector_model]:
162
+ facefusion.globals.face_detector_size = args.face_detector_size
163
+ else:
164
+ facefusion.globals.face_detector_size = '640x640'
165
+ facefusion.globals.face_detector_score = args.face_detector_score
166
+ facefusion.globals.face_landmarker_score = args.face_landmarker_score
167
+ # face selector
168
+ facefusion.globals.face_selector_mode = args.face_selector_mode
169
+ facefusion.globals.reference_face_position = args.reference_face_position
170
+ facefusion.globals.reference_face_distance = args.reference_face_distance
171
+ facefusion.globals.reference_frame_number = args.reference_frame_number
172
+ # face mask
173
+ facefusion.globals.face_mask_types = args.face_mask_types
174
+ facefusion.globals.face_mask_blur = args.face_mask_blur
175
+ facefusion.globals.face_mask_padding = normalize_padding(args.face_mask_padding)
176
+ facefusion.globals.face_mask_regions = args.face_mask_regions
177
+ # frame extraction
178
+ facefusion.globals.trim_frame_start = args.trim_frame_start
179
+ facefusion.globals.trim_frame_end = args.trim_frame_end
180
+ facefusion.globals.temp_frame_format = args.temp_frame_format
181
+ facefusion.globals.keep_temp = args.keep_temp
182
+ # output creation
183
+ facefusion.globals.output_image_quality = args.output_image_quality
184
+ if is_image(args.target_path):
185
+ output_image_resolution = detect_image_resolution(args.target_path)
186
+ output_image_resolutions = create_image_resolutions(output_image_resolution)
187
+ if args.output_image_resolution in output_image_resolutions:
188
+ facefusion.globals.output_image_resolution = args.output_image_resolution
189
+ else:
190
+ facefusion.globals.output_image_resolution = pack_resolution(output_image_resolution)
191
+ facefusion.globals.output_video_encoder = args.output_video_encoder
192
+ facefusion.globals.output_video_preset = args.output_video_preset
193
+ facefusion.globals.output_video_quality = args.output_video_quality
194
+ if is_video(args.target_path):
195
+ output_video_resolution = detect_video_resolution(args.target_path)
196
+ output_video_resolutions = create_video_resolutions(output_video_resolution)
197
+ if args.output_video_resolution in output_video_resolutions:
198
+ facefusion.globals.output_video_resolution = args.output_video_resolution
199
+ else:
200
+ facefusion.globals.output_video_resolution = pack_resolution(output_video_resolution)
201
+ if args.output_video_fps or is_video(args.target_path):
202
+ facefusion.globals.output_video_fps = normalize_fps(args.output_video_fps) or detect_video_fps(args.target_path)
203
+ facefusion.globals.skip_audio = args.skip_audio
204
+ # frame processors
205
+ base_dir = os.path.dirname(os.path.abspath(__file__))
206
+ modules_path = os.path.join(base_dir, "facefusion", "processors", "frame", "modules")
207
+ available_frame_processors = list_directory(modules_path)
208
+ facefusion.globals.frame_processors = args.frame_processors
209
+ for frame_processor in available_frame_processors:
210
+ frame_processor_module = load_frame_processor_module(frame_processor)
211
+ frame_processor_module.apply_args(program)
212
+ # uis
213
+ # facefusion.globals.open_browser = args.open_browser
214
+ # facefusion.globals.ui_layouts = args.ui_layouts
215
+
216
+
217
+ def run(program: ArgumentParser) -> None:
218
+ validate_args(program)
219
+ apply_args(program)
220
+ logger.init(facefusion.globals.log_level)
221
+
222
+ if facefusion.globals.system_memory_limit > 0:
223
+ limit_system_memory(facefusion.globals.system_memory_limit)
224
+ if facefusion.globals.force_download:
225
+ force_download()
226
+ return
227
+ if not pre_check() or not content_analyser.pre_check() or not face_analyser.pre_check() or not face_masker.pre_check() or not voice_extractor.pre_check():
228
+ return
229
+ for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
230
+ if not frame_processor_module.pre_check():
231
+ return
232
+ if facefusion.globals.headless:
233
+ conditional_process()
234
+ else:
235
+ conditional_process() # Run process directly without UI
236
+
237
+
238
+ def destroy() -> None:
239
+ process_manager.stop()
240
+ while process_manager.is_processing():
241
+ sleep(0.5)
242
+ if facefusion.globals.target_path:
243
+ clear_temp(facefusion.globals.target_path)
244
+ sys.exit(0)
245
+
246
+
247
+ def pre_check() -> bool:
248
+ if sys.version_info < (3, 9):
249
+ logger.error(wording.get('python_not_supported').format(version='3.9'), __name__.upper())
250
+ return False
251
+ if not shutil.which('ffmpeg'):
252
+ logger.error(wording.get('ffmpeg_not_installed'), __name__.upper())
253
+ return False
254
+ return True
255
+
256
+
257
+ def conditional_process() -> None:
258
+ start_time = time()
259
+ for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
260
+ while not frame_processor_module.post_check():
261
+ logger.disable()
262
+ sleep(0.5)
263
+ logger.enable()
264
+ if not frame_processor_module.pre_process('output'):
265
+ return
266
+ conditional_append_reference_faces()
267
+ if is_image(facefusion.globals.target_path):
268
+ process_image(start_time)
269
+ if is_video(facefusion.globals.target_path):
270
+ process_video(start_time)
271
+
272
+
273
+ def conditional_append_reference_faces() -> None:
274
+ if 'reference' in facefusion.globals.face_selector_mode and not get_reference_faces():
275
+ source_frames = read_static_images(facefusion.globals.source_paths)
276
+ source_face = get_average_face(source_frames)
277
+ if is_video(facefusion.globals.target_path):
278
+ reference_frame = get_video_frame(facefusion.globals.target_path, facefusion.globals.reference_frame_number)
279
+ else:
280
+ reference_frame = read_image(facefusion.globals.target_path)
281
+ reference_face = get_one_face(reference_frame, facefusion.globals.reference_face_position)
282
+ append_reference_face('origin', reference_face)
283
+ if source_face and reference_face:
284
+ for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
285
+ abstract_reference_frame = frame_processor_module.get_reference_frame(source_face, reference_face, reference_frame)
286
+ if numpy.any(abstract_reference_frame):
287
+ reference_frame = abstract_reference_frame
288
+ reference_face = get_one_face(reference_frame, facefusion.globals.reference_face_position)
289
+ append_reference_face(frame_processor_module.__name__, reference_face)
290
+
291
+
292
+ def force_download() -> None:
293
+ base_dir = os.path.dirname(os.path.abspath(__file__))
294
+ download_directory_path = os.path.join(base_dir, "assets", "models")
295
+ modules_path = os.path.join(base_dir, "facefusion", "processors", "frame", "modules")
296
+ available_frame_processors = list_directory(modules_path)
297
+ model_list = [
298
+ content_analyser.MODELS,
299
+ face_analyser.MODELS,
300
+ face_masker.MODELS,
301
+ voice_extractor.MODELS
302
+ ]
303
+
304
+ for frame_processor_module in get_frame_processors_modules(available_frame_processors):
305
+ if hasattr(frame_processor_module, 'MODELS'):
306
+ model_list.append(frame_processor_module.MODELS)
307
+ model_urls = [models[model].get('url') for models in model_list for model in models]
308
+ conditional_download(download_directory_path, model_urls)
309
+
310
+
311
+ def process_image(start_time: float) -> None:
312
+ normed_output_path = normalize_output_path(facefusion.globals.target_path, facefusion.globals.output_path)
313
+ if analyse_image(facefusion.globals.target_path):
314
+ return
315
+ # clear temp
316
+ logger.debug(wording.get('clearing_temp'), __name__.upper())
317
+ clear_temp(facefusion.globals.target_path)
318
+ # create temp
319
+ logger.debug(wording.get('creating_temp'), __name__.upper())
320
+ create_temp(facefusion.globals.target_path)
321
+ # copy image
322
+ process_manager.start()
323
+ temp_image_resolution = pack_resolution(restrict_image_resolution(facefusion.globals.target_path, unpack_resolution(facefusion.globals.output_image_resolution)))
324
+ logger.info(wording.get('copying_image').format(resolution=temp_image_resolution), __name__.upper())
325
+ if copy_image(facefusion.globals.target_path, temp_image_resolution):
326
+ logger.debug(wording.get('copying_image_succeed'), __name__.upper())
327
+ else:
328
+ logger.error(wording.get('copying_image_failed'), __name__.upper())
329
+ return
330
+ # process image
331
+ temp_file_path = get_temp_file_path(facefusion.globals.target_path)
332
+ for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
333
+ logger.info(wording.get('processing'), frame_processor_module.NAME)
334
+ frame_processor_module.process_image(facefusion.globals.source_paths, temp_file_path, temp_file_path)
335
+ frame_processor_module.post_process()
336
+ if is_process_stopping():
337
+ return
338
+ # finalize image
339
+ logger.info(wording.get('finalizing_image').format(resolution=facefusion.globals.output_image_resolution), __name__.upper())
340
+ if finalize_image(facefusion.globals.target_path, normed_output_path, facefusion.globals.output_image_resolution):
341
+ logger.debug(wording.get('finalizing_image_succeed'), __name__.upper())
342
+ else:
343
+ logger.warn(wording.get('finalizing_image_skipped'), __name__.upper())
344
+ # clear temp
345
+ logger.debug(wording.get('clearing_temp'), __name__.upper())
346
+ clear_temp(facefusion.globals.target_path)
347
+ # validate image
348
+ if is_image(normed_output_path):
349
+ seconds = '{:.2f}'.format((time() - start_time) % 60)
350
+ logger.info(wording.get('processing_image_succeed').format(seconds=seconds), __name__.upper())
351
+ conditional_log_statistics()
352
+ else:
353
+ logger.error(wording.get('processing_image_failed'), __name__.upper())
354
+ process_manager.end()
355
+
356
+
357
+ def process_video(start_time: float) -> None:
358
+ normed_output_path = normalize_output_path(facefusion.globals.target_path, facefusion.globals.output_path)
359
+ if analyse_video(facefusion.globals.target_path, facefusion.globals.trim_frame_start, facefusion.globals.trim_frame_end):
360
+ return
361
+ # clear temp
362
+ logger.debug(wording.get('clearing_temp'), __name__.upper())
363
+ clear_temp(facefusion.globals.target_path)
364
+ # create temp
365
+ logger.debug(wording.get('creating_temp'), __name__.upper())
366
+ create_temp(facefusion.globals.target_path)
367
+ # extract frames
368
+ process_manager.start()
369
+ temp_video_resolution = pack_resolution(restrict_video_resolution(facefusion.globals.target_path, unpack_resolution(facefusion.globals.output_video_resolution)))
370
+ temp_video_fps = restrict_video_fps(facefusion.globals.target_path, facefusion.globals.output_video_fps)
371
+ logger.info(wording.get('extracting_frames').format(resolution=temp_video_resolution, fps=temp_video_fps), __name__.upper())
372
+ if extract_frames(facefusion.globals.target_path, temp_video_resolution, temp_video_fps):
373
+ logger.debug(wording.get('extracting_frames_succeed'), __name__.upper())
374
+ else:
375
+ if is_process_stopping():
376
+ return
377
+ logger.error(wording.get('extracting_frames_failed'), __name__.upper())
378
+ return
379
+ # process frames
380
+ temp_frame_paths = get_temp_frame_paths(facefusion.globals.target_path)
381
+ if temp_frame_paths:
382
+ for frame_processor_module in get_frame_processors_modules(facefusion.globals.frame_processors):
383
+ logger.info(wording.get('processing'), frame_processor_module.NAME)
384
+ frame_processor_module.process_video(facefusion.globals.source_paths, temp_frame_paths)
385
+ frame_processor_module.post_process()
386
+ if is_process_stopping():
387
+ return
388
+ else:
389
+ logger.error(wording.get('temp_frames_not_found'), __name__.upper())
390
+ return
391
+ # merge video
392
+ logger.info(wording.get('merging_video').format(resolution=facefusion.globals.output_video_resolution, fps=facefusion.globals.output_video_fps), __name__.upper())
393
+ if merge_video(facefusion.globals.target_path, facefusion.globals.output_video_resolution, facefusion.globals.output_video_fps):
394
+ logger.debug(wording.get('merging_video_succeed'), __name__.upper())
395
+ else:
396
+ if is_process_stopping():
397
+ return
398
+ logger.error(wording.get('merging_video_failed'), __name__.upper())
399
+ return
400
+ # handle audio
401
+ if facefusion.globals.skip_audio:
402
+ logger.info(wording.get('skipping_audio'), __name__.upper())
403
+ move_temp(facefusion.globals.target_path, normed_output_path)
404
+ else:
405
+ if 'lip_syncer' in facefusion.globals.frame_processors:
406
+ source_audio_path = get_first(filter_audio_paths(facefusion.globals.source_paths))
407
+ if source_audio_path and replace_audio(facefusion.globals.target_path, source_audio_path, normed_output_path):
408
+ logger.debug(wording.get('restoring_audio_succeed'), __name__.upper())
409
+ else:
410
+ if is_process_stopping():
411
+ return
412
+ logger.warn(wording.get('restoring_audio_skipped'), __name__.upper())
413
+ move_temp(facefusion.globals.target_path, normed_output_path)
414
+ else:
415
+ if restore_audio(facefusion.globals.target_path, normed_output_path, facefusion.globals.output_video_fps):
416
+ logger.debug(wording.get('restoring_audio_succeed'), __name__.upper())
417
+ else:
418
+ if is_process_stopping():
419
+ return
420
+ logger.warn(wording.get('restoring_audio_skipped'), __name__.upper())
421
+ move_temp(facefusion.globals.target_path, normed_output_path)
422
+ # clear temp
423
+ logger.debug(wording.get('clearing_temp'), __name__.upper())
424
+ clear_temp(facefusion.globals.target_path)
425
+ # validate video
426
+ if is_video(normed_output_path):
427
+ seconds = '{:.2f}'.format((time() - start_time))
428
+ logger.info(wording.get('processing_video_succeed').format(seconds=seconds), __name__.upper())
429
+ conditional_log_statistics()
430
+ else:
431
+ logger.error(wording.get('processing_video_failed'), __name__.upper())
432
+ process_manager.end()
433
+
434
+
435
+ def is_process_stopping() -> bool:
436
+ if process_manager.is_stopping():
437
+ process_manager.end()
438
+ logger.info(wording.get('processing_stopped'), __name__.upper())
439
+ return process_manager.is_pending()
440
+
441
+
442
+ if __name__ == "__main__":
443
+ cli()
facefusion/facefusion.ini ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [general]
2
+ source_paths =
3
+ target_path =
4
+ output_path =
5
+
6
+ [misc]
7
+ force_download =
8
+ skip_download =
9
+ headless =
10
+ log_level =
11
+
12
+ [execution]
13
+ execution_device_id =
14
+ execution_providers =
15
+ execution_thread_count =
16
+ execution_queue_count =
17
+
18
+ [memory]
19
+ video_memory_strategy =
20
+ system_memory_limit =
21
+
22
+ [face_analyser]
23
+ face_analyser_order =
24
+ face_analyser_age =
25
+ face_analyser_gender =
26
+ face_detector_model =
27
+ face_detector_size =
28
+ face_detector_score =
29
+ face_landmarker_score =
30
+
31
+ [face_selector]
32
+ face_selector_mode =
33
+ reference_face_position =
34
+ reference_face_distance =
35
+ reference_frame_number =
36
+
37
+ [face_mask]
38
+ face_mask_types =
39
+ face_mask_blur =
40
+ face_mask_padding =
41
+ face_mask_regions =
42
+
43
+ [frame_extraction]
44
+ trim_frame_start =
45
+ trim_frame_end =
46
+ temp_frame_format =
47
+ keep_temp =
48
+
49
+ [output_creation]
50
+ output_image_quality =
51
+ output_image_resolution =
52
+ output_video_encoder =
53
+ output_video_preset =
54
+ output_video_quality =
55
+ output_video_resolution =
56
+ output_video_fps =
57
+ skip_audio =
58
+
59
+ [frame_processors]
60
+ frame_processors =
61
+ face_debugger_items =
62
+ face_enhancer_model =
63
+ face_enhancer_blend =
64
+ face_swapper_model =
65
+ frame_colorizer_model =
66
+ frame_colorizer_blend =
67
+ frame_colorizer_size =
68
+ frame_enhancer_model =
69
+ frame_enhancer_blend =
70
+ lip_syncer_model =
71
+
72
+ [uis]
73
+ open_browser =
74
+ ui_layouts =
facefusion/facefusion/__init__.py ADDED
File without changes
facefusion/facefusion/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (146 Bytes). View file
 
facefusion/facefusion/__pycache__/audio.cpython-310.pyc ADDED
Binary file (4.51 kB). View file
 
facefusion/facefusion/__pycache__/choices.cpython-310.pyc ADDED
Binary file (2.74 kB). View file
 
facefusion/facefusion/__pycache__/common_helper.cpython-310.pyc ADDED
Binary file (1.59 kB). View file
 
facefusion/facefusion/__pycache__/config.cpython-310.pyc ADDED
Binary file (2.45 kB). View file
 
facefusion/facefusion/__pycache__/content_analyser.cpython-310.pyc ADDED
Binary file (4.01 kB). View file
 
facefusion/facefusion/__pycache__/core.cpython-310.pyc ADDED
Binary file (18.6 kB). View file
 
facefusion/facefusion/__pycache__/download.cpython-310.pyc ADDED
Binary file (1.99 kB). View file
 
facefusion/facefusion/__pycache__/execution.cpython-310.pyc ADDED
Binary file (4.02 kB). View file
 
facefusion/facefusion/__pycache__/face_analyser.cpython-310.pyc ADDED
Binary file (18.2 kB). View file
 
facefusion/facefusion/__pycache__/face_helper.cpython-310.pyc ADDED
Binary file (6.37 kB). View file
 
facefusion/facefusion/__pycache__/face_masker.cpython-310.pyc ADDED
Binary file (5.65 kB). View file
 
facefusion/facefusion/__pycache__/face_store.cpython-310.pyc ADDED
Binary file (1.65 kB). View file
 
facefusion/facefusion/__pycache__/ffmpeg.cpython-310.pyc ADDED
Binary file (5.49 kB). View file
 
facefusion/facefusion/__pycache__/filesystem.cpython-310.pyc ADDED
Binary file (5.18 kB). View file
 
facefusion/facefusion/__pycache__/globals.cpython-310.pyc ADDED
Binary file (2.54 kB). View file
 
facefusion/facefusion/__pycache__/logger.cpython-310.pyc ADDED
Binary file (1.66 kB). View file
 
facefusion/facefusion/__pycache__/memory.cpython-310.pyc ADDED
Binary file (713 Bytes). View file
 
facefusion/facefusion/__pycache__/metadata.cpython-310.pyc ADDED
Binary file (473 Bytes). View file
 
facefusion/facefusion/__pycache__/my_typing.cpython-310.pyc ADDED
Binary file (3.23 kB). View file
 
facefusion/facefusion/__pycache__/normalizer.cpython-310.pyc ADDED
Binary file (1.55 kB). View file
 
facefusion/facefusion/__pycache__/process_manager.cpython-310.pyc ADDED
Binary file (1.71 kB). View file
 
facefusion/facefusion/__pycache__/statistics.cpython-310.pyc ADDED
Binary file (1.72 kB). View file
 
facefusion/facefusion/__pycache__/thread_helper.cpython-310.pyc ADDED
Binary file (915 Bytes). View file
 
facefusion/facefusion/__pycache__/typing.cpython-310.pyc ADDED
Binary file (3.23 kB). View file
 
facefusion/facefusion/__pycache__/vision.cpython-310.pyc ADDED
Binary file (6.89 kB). View file
 
facefusion/facefusion/__pycache__/voice_extractor.cpython-310.pyc ADDED
Binary file (4.81 kB). View file
 
facefusion/facefusion/__pycache__/wording.cpython-310.pyc ADDED
Binary file (11.4 kB). View file
 
facefusion/facefusion/audio.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Any, List
2
+ from functools import lru_cache
3
+ import numpy
4
+ import scipy
5
+
6
+ from facefusion.filesystem import is_audio
7
+ from facefusion.ffmpeg import read_audio_buffer
8
+ from facefusion.my_typing import Fps, Audio, AudioFrame, Spectrogram, MelFilterBank
9
+ from facefusion.voice_extractor import batch_extract_voice
10
+
11
+
12
+ @lru_cache(maxsize = 128)
13
+ def read_static_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
14
+ return read_audio(audio_path, fps)
15
+
16
+
17
+ def read_audio(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
18
+ sample_rate = 48000
19
+ channel_total = 2
20
+
21
+ if is_audio(audio_path):
22
+ audio_buffer = read_audio_buffer(audio_path, sample_rate, channel_total)
23
+ audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
24
+ audio = prepare_audio(audio)
25
+ spectrogram = create_spectrogram(audio)
26
+ audio_frames = extract_audio_frames(spectrogram, fps)
27
+ return audio_frames
28
+ return None
29
+
30
+
31
+ @lru_cache(maxsize = 128)
32
+ def read_static_voice(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
33
+ return read_voice(audio_path, fps)
34
+
35
+
36
+ def read_voice(audio_path : str, fps : Fps) -> Optional[List[AudioFrame]]:
37
+ sample_rate = 48000
38
+ channel_total = 2
39
+ chunk_size = 1024 * 240
40
+ step_size = 1024 * 180
41
+
42
+ if is_audio(audio_path):
43
+ audio_buffer = read_audio_buffer(audio_path, sample_rate, channel_total)
44
+ audio = numpy.frombuffer(audio_buffer, dtype = numpy.int16).reshape(-1, 2)
45
+ audio = batch_extract_voice(audio, chunk_size, step_size)
46
+ audio = prepare_voice(audio)
47
+ spectrogram = create_spectrogram(audio)
48
+ audio_frames = extract_audio_frames(spectrogram, fps)
49
+ return audio_frames
50
+ return None
51
+
52
+
53
+ def get_audio_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Optional[AudioFrame]:
54
+ if is_audio(audio_path):
55
+ audio_frames = read_static_audio(audio_path, fps)
56
+ if frame_number in range(len(audio_frames)):
57
+ return audio_frames[frame_number]
58
+ return None
59
+
60
+
61
+ def get_voice_frame(audio_path : str, fps : Fps, frame_number : int = 0) -> Optional[AudioFrame]:
62
+ if is_audio(audio_path):
63
+ voice_frames = read_static_voice(audio_path, fps)
64
+ if frame_number in range(len(voice_frames)):
65
+ return voice_frames[frame_number]
66
+ return None
67
+
68
+
69
+ def create_empty_audio_frame() -> AudioFrame:
70
+ mel_filter_total = 80
71
+ step_size = 16
72
+ audio_frame = numpy.zeros((mel_filter_total, step_size)).astype(numpy.int16)
73
+ return audio_frame
74
+
75
+
76
+ def prepare_audio(audio : numpy.ndarray[Any, Any]) -> Audio:
77
+ if audio.ndim > 1:
78
+ audio = numpy.mean(audio, axis = 1)
79
+ audio = audio / numpy.max(numpy.abs(audio), axis = 0)
80
+ audio = scipy.signal.lfilter([ 1.0, -0.97 ], [ 1.0 ], audio)
81
+ return audio
82
+
83
+
84
+ def prepare_voice(audio : numpy.ndarray[Any, Any]) -> Audio:
85
+ sample_rate = 48000
86
+ resample_rate = 16000
87
+
88
+ audio = scipy.signal.resample(audio, int(len(audio) * resample_rate / sample_rate))
89
+ audio = prepare_audio(audio)
90
+ return audio
91
+
92
+
93
+ def convert_hertz_to_mel(hertz : float) -> float:
94
+ return 2595 * numpy.log10(1 + hertz / 700)
95
+
96
+
97
+ def convert_mel_to_hertz(mel : numpy.ndarray[Any, Any]) -> numpy.ndarray[Any, Any]:
98
+ return 700 * (10 ** (mel / 2595) - 1)
99
+
100
+
101
+ def create_mel_filter_bank() -> MelFilterBank:
102
+ mel_filter_total = 80
103
+ mel_bin_total = 800
104
+ sample_rate = 16000
105
+ min_frequency = 55.0
106
+ max_frequency = 7600.0
107
+ mel_filter_bank = numpy.zeros((mel_filter_total, mel_bin_total // 2 + 1))
108
+ mel_frequency_range = numpy.linspace(convert_hertz_to_mel(min_frequency), convert_hertz_to_mel(max_frequency), mel_filter_total + 2)
109
+ indices = numpy.floor((mel_bin_total + 1) * convert_mel_to_hertz(mel_frequency_range) / sample_rate).astype(numpy.int16)
110
+
111
+ for index in range(mel_filter_total):
112
+ start = indices[index]
113
+ end = indices[index + 1]
114
+ mel_filter_bank[index, start:end] = scipy.signal.windows.triang(end - start)
115
+ return mel_filter_bank
116
+
117
+
118
+ def create_spectrogram(audio : Audio) -> Spectrogram:
119
+ mel_bin_total = 800
120
+ mel_bin_overlap = 600
121
+ mel_filter_bank = create_mel_filter_bank()
122
+ spectrogram = scipy.signal.stft(audio, nperseg = mel_bin_total, nfft = mel_bin_total, noverlap = mel_bin_overlap)[2]
123
+ spectrogram = numpy.dot(mel_filter_bank, numpy.abs(spectrogram))
124
+ return spectrogram
125
+
126
+
127
+ def extract_audio_frames(spectrogram : Spectrogram, fps : Fps) -> List[AudioFrame]:
128
+ mel_filter_total = 80
129
+ step_size = 16
130
+ audio_frames = []
131
+ indices = numpy.arange(0, spectrogram.shape[1], mel_filter_total / fps).astype(numpy.int16)
132
+ indices = indices[indices >= step_size]
133
+
134
+ for index in indices:
135
+ start = max(0, index - step_size)
136
+ audio_frames.append(spectrogram[:, start:index])
137
+ return audio_frames
facefusion/facefusion/choices.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Dict
2
+
3
+ from facefusion.my_typing import VideoMemoryStrategy, FaceSelectorMode, FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, FaceDetectorModel, FaceMaskType, FaceMaskRegion, TempFrameFormat, OutputVideoEncoder, OutputVideoPreset
4
+ from facefusion.common_helper import create_int_range, create_float_range
5
+
6
+ video_memory_strategies : List[VideoMemoryStrategy] = [ 'strict', 'moderate', 'tolerant' ]
7
+ face_analyser_orders : List[FaceAnalyserOrder] = [ 'left-right', 'right-left', 'top-bottom', 'bottom-top', 'small-large', 'large-small', 'best-worst', 'worst-best' ]
8
+ face_analyser_ages : List[FaceAnalyserAge] = [ 'child', 'teen', 'adult', 'senior' ]
9
+ face_analyser_genders : List[FaceAnalyserGender] = [ 'female', 'male' ]
10
+ face_detector_set : Dict[FaceDetectorModel, List[str]] =\
11
+ {
12
+ 'many': [ '640x640' ],
13
+ 'retinaface': [ '160x160', '320x320', '480x480', '512x512', '640x640' ],
14
+ 'scrfd': [ '160x160', '320x320', '480x480', '512x512', '640x640' ],
15
+ 'yoloface': [ '640x640' ],
16
+ 'yunet': [ '160x160', '320x320', '480x480', '512x512', '640x640', '768x768', '960x960', '1024x1024' ]
17
+ }
18
+ face_selector_modes : List[FaceSelectorMode] = [ 'many', 'one', 'reference' ]
19
+ face_mask_types : List[FaceMaskType] = [ 'box', 'occlusion', 'region' ]
20
+ face_mask_regions : List[FaceMaskRegion] = [ 'skin', 'left-eyebrow', 'right-eyebrow', 'left-eye', 'right-eye', 'glasses', 'nose', 'mouth', 'upper-lip', 'lower-lip' ]
21
+ temp_frame_formats : List[TempFrameFormat] = [ 'bmp', 'jpg', 'png' ]
22
+ output_video_encoders : List[OutputVideoEncoder] = [ 'libx264', 'libx265', 'libvpx-vp9', 'h264_nvenc', 'hevc_nvenc', 'h264_amf', 'hevc_amf' ]
23
+ output_video_presets : List[OutputVideoPreset] = [ 'ultrafast', 'superfast', 'veryfast', 'faster', 'fast', 'medium', 'slow', 'slower', 'veryslow' ]
24
+
25
+ image_template_sizes : List[float] = [ 0.25, 0.5, 0.75, 1, 1.5, 2, 2.5, 3, 3.5, 4 ]
26
+ video_template_sizes : List[int] = [ 240, 360, 480, 540, 720, 1080, 1440, 2160, 4320 ]
27
+
28
+ execution_thread_count_range : List[int] = create_int_range(1, 128, 1)
29
+ execution_queue_count_range : List[int] = create_int_range(1, 32, 1)
30
+ system_memory_limit_range : List[int] = create_int_range(0, 128, 1)
31
+ face_detector_score_range : List[float] = create_float_range(0.0, 1.0, 0.05)
32
+ face_landmarker_score_range : List[float] = create_float_range(0.0, 1.0, 0.05)
33
+ face_mask_blur_range : List[float] = create_float_range(0.0, 1.0, 0.05)
34
+ face_mask_padding_range : List[int] = create_int_range(0, 100, 1)
35
+ reference_face_distance_range : List[float] = create_float_range(0.0, 1.5, 0.05)
36
+ output_image_quality_range : List[int] = create_int_range(0, 100, 1)
37
+ output_video_quality_range : List[int] = create_int_range(0, 100, 1)
facefusion/facefusion/common_helper.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Any
2
+ import platform
3
+
4
+
5
+ def create_metavar(ranges : List[Any]) -> str:
6
+ return '[' + str(ranges[0]) + '-' + str(ranges[-1]) + ']'
7
+
8
+
9
+ def create_int_range(start : int, end : int, step : int) -> List[int]:
10
+ int_range = []
11
+ current = start
12
+
13
+ while current <= end:
14
+ int_range.append(current)
15
+ current += step
16
+ return int_range
17
+
18
+
19
+ def create_float_range(start : float, end : float, step : float) -> List[float]:
20
+ float_range = []
21
+ current = start
22
+
23
+ while current <= end:
24
+ float_range.append(round(current, 2))
25
+ current = round(current + step, 2)
26
+ return float_range
27
+
28
+
29
+ def is_linux() -> bool:
30
+ return to_lower_case(platform.system()) == 'linux'
31
+
32
+
33
+ def is_macos() -> bool:
34
+ return to_lower_case(platform.system()) == 'darwin'
35
+
36
+
37
+ def is_windows() -> bool:
38
+ return to_lower_case(platform.system()) == 'windows'
39
+
40
+
41
+ def to_lower_case(__string__ : Any) -> str:
42
+ return str(__string__).lower()
43
+
44
+
45
+ def get_first(__list__ : Any) -> Any:
46
+ return next(iter(__list__), None)
facefusion/facefusion/config.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from configparser import ConfigParser
2
+ from typing import Any, Optional, List
3
+
4
+ import facefusion.globals
5
+
6
+ CONFIG = None
7
+
8
+
9
+ def get_config() -> ConfigParser:
10
+ global CONFIG
11
+
12
+ if CONFIG is None:
13
+ CONFIG = ConfigParser()
14
+ CONFIG.read(facefusion.globals.config_path, encoding = 'utf-8')
15
+ return CONFIG
16
+
17
+
18
+ def clear_config() -> None:
19
+ global CONFIG
20
+
21
+ CONFIG = None
22
+
23
+
24
+ def get_str_value(key : str, fallback : Optional[str] = None) -> Optional[str]:
25
+ value = get_value_by_notation(key)
26
+
27
+ if value or fallback:
28
+ return str(value or fallback)
29
+ return None
30
+
31
+
32
+ def get_int_value(key : str, fallback : Optional[str] = None) -> Optional[int]:
33
+ value = get_value_by_notation(key)
34
+
35
+ if value or fallback:
36
+ return int(value or fallback)
37
+ return None
38
+
39
+
40
+ def get_float_value(key : str, fallback : Optional[str] = None) -> Optional[float]:
41
+ value = get_value_by_notation(key)
42
+
43
+ if value or fallback:
44
+ return float(value or fallback)
45
+ return None
46
+
47
+
48
+ def get_bool_value(key : str, fallback : Optional[str] = None) -> Optional[bool]:
49
+ value = get_value_by_notation(key)
50
+
51
+ if value == 'True' or fallback == 'True':
52
+ return True
53
+ if value == 'False' or fallback == 'False':
54
+ return False
55
+ return None
56
+
57
+
58
+ def get_str_list(key : str, fallback : Optional[str] = None) -> Optional[List[str]]:
59
+ value = get_value_by_notation(key)
60
+
61
+ if value or fallback:
62
+ return [ str(value) for value in (value or fallback).split(' ') ]
63
+ return None
64
+
65
+
66
+ def get_int_list(key : str, fallback : Optional[str] = None) -> Optional[List[int]]:
67
+ value = get_value_by_notation(key)
68
+
69
+ if value or fallback:
70
+ return [ int(value) for value in (value or fallback).split(' ') ]
71
+ return None
72
+
73
+
74
+ def get_float_list(key : str, fallback : Optional[str] = None) -> Optional[List[float]]:
75
+ value = get_value_by_notation(key)
76
+
77
+ if value or fallback:
78
+ return [ float(value) for value in (value or fallback).split(' ') ]
79
+ return None
80
+
81
+
82
+ def get_value_by_notation(key : str) -> Optional[Any]:
83
+ config = get_config()
84
+
85
+ if '.' in key:
86
+ section, name = key.split('.')
87
+ if section in config and name in config[section]:
88
+ return config[section][name]
89
+ if key in config:
90
+ return config[key]
91
+ return None
facefusion/facefusion/content_analyser.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any
2
+ from functools import lru_cache
3
+ from time import sleep
4
+ import cv2
5
+ import numpy
6
+ import onnxruntime
7
+ from tqdm import tqdm
8
+
9
+ import facefusion.globals
10
+ from facefusion import process_manager, wording
11
+ from facefusion.thread_helper import thread_lock, conditional_thread_semaphore
12
+ from facefusion.my_typing import VisionFrame, ModelSet, Fps
13
+ from facefusion.execution import apply_execution_provider_options
14
+ from facefusion.vision import get_video_frame, count_video_frame_total, read_image, detect_video_fps
15
+ from facefusion.filesystem import resolve_relative_path, is_file
16
+ from facefusion.download import conditional_download
17
+
18
+ CONTENT_ANALYSER = None
19
+ MODELS : ModelSet =\
20
+ {
21
+ 'open_nsfw':
22
+ {
23
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/open_nsfw.onnx',
24
+ 'path': resolve_relative_path('../.assets/models/open_nsfw.onnx')
25
+ }
26
+ }
27
+ PROBABILITY_LIMIT = 0.80
28
+ RATE_LIMIT = 10
29
+ STREAM_COUNTER = 0
30
+
31
+
32
+ def get_content_analyser() -> Any:
33
+ global CONTENT_ANALYSER
34
+
35
+ with thread_lock():
36
+ while process_manager.is_checking():
37
+ sleep(0.5)
38
+ if CONTENT_ANALYSER is None:
39
+ model_path = MODELS.get('open_nsfw').get('path')
40
+ CONTENT_ANALYSER = onnxruntime.InferenceSession(model_path, providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
41
+ return CONTENT_ANALYSER
42
+
43
+
44
+ def clear_content_analyser() -> None:
45
+ global CONTENT_ANALYSER
46
+
47
+ CONTENT_ANALYSER = None
48
+
49
+
50
+ def pre_check() -> bool:
51
+ download_directory_path = resolve_relative_path('../.assets/models')
52
+ model_url = MODELS.get('open_nsfw').get('url')
53
+ model_path = MODELS.get('open_nsfw').get('path')
54
+
55
+ if not facefusion.globals.skip_download:
56
+ process_manager.check()
57
+ conditional_download(download_directory_path, [ model_url ])
58
+ process_manager.end()
59
+ return is_file(model_path)
60
+
61
+
62
+ def analyse_stream(vision_frame : VisionFrame, video_fps : Fps) -> bool:
63
+ global STREAM_COUNTER
64
+
65
+ STREAM_COUNTER = STREAM_COUNTER + 1
66
+ if STREAM_COUNTER % int(video_fps) == 0:
67
+ return analyse_frame(vision_frame)
68
+ return False
69
+
70
+
71
+ def analyse_frame(vision_frame : VisionFrame) -> bool:
72
+ content_analyser = get_content_analyser()
73
+ vision_frame = prepare_frame(vision_frame)
74
+ with conditional_thread_semaphore(facefusion.globals.execution_providers):
75
+ probability = content_analyser.run(None,
76
+ {
77
+ content_analyser.get_inputs()[0].name: vision_frame
78
+ })[0][0][1]
79
+ return probability > PROBABILITY_LIMIT
80
+
81
+
82
+ def prepare_frame(vision_frame : VisionFrame) -> VisionFrame:
83
+ vision_frame = cv2.resize(vision_frame, (224, 224)).astype(numpy.float32)
84
+ vision_frame -= numpy.array([ 104, 117, 123 ]).astype(numpy.float32)
85
+ vision_frame = numpy.expand_dims(vision_frame, axis = 0)
86
+ return vision_frame
87
+
88
+
89
+ @lru_cache(maxsize = None)
90
+ def analyse_image(image_path : str) -> bool:
91
+ frame = read_image(image_path)
92
+ return analyse_frame(frame)
93
+
94
+
95
+ @lru_cache(maxsize = None)
96
+ def analyse_video(video_path : str, start_frame : int, end_frame : int) -> bool:
97
+ video_frame_total = count_video_frame_total(video_path)
98
+ video_fps = detect_video_fps(video_path)
99
+ frame_range = range(start_frame or 0, end_frame or video_frame_total)
100
+ rate = 0.0
101
+ counter = 0
102
+
103
+ with tqdm(total = len(frame_range), desc = wording.get('analysing'), unit = 'frame', ascii = ' =', disable = facefusion.globals.log_level in [ 'warn', 'error' ]) as progress:
104
+ for frame_number in frame_range:
105
+ if frame_number % int(video_fps) == 0:
106
+ frame = get_video_frame(video_path, frame_number)
107
+ if analyse_frame(frame):
108
+ counter += 1
109
+ rate = counter * int(video_fps) / len(frame_range) * 100
110
+ progress.update()
111
+ progress.set_postfix(rate = rate)
112
+ return rate > RATE_LIMIT
facefusion/facefusion/download.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ import ssl
4
+ import urllib.request
5
+ from typing import List
6
+ from functools import lru_cache
7
+ from tqdm import tqdm
8
+
9
+ import facefusion.globals
10
+ from facefusion import wording
11
+ from facefusion.common_helper import is_macos
12
+ from facefusion.filesystem import get_file_size, is_file
13
+
14
+ if is_macos():
15
+ ssl._create_default_https_context = ssl._create_unverified_context
16
+
17
+
18
+ def conditional_download(download_directory_path : str, urls : List[str]) -> None:
19
+ for url in urls:
20
+ download_file_path = os.path.join(download_directory_path, os.path.basename(url))
21
+ initial_size = get_file_size(download_file_path)
22
+ download_size = get_download_size(url)
23
+ if initial_size < download_size:
24
+ with tqdm(total = download_size, initial = initial_size, desc = wording.get('downloading'), unit = 'B', unit_scale = True, unit_divisor = 1024, ascii = ' =', disable = facefusion.globals.log_level in [ 'warn', 'error' ]) as progress:
25
+ subprocess.Popen([ 'curl', '--create-dirs', '--silent', '--insecure', '--location', '--continue-at', '-', '--output', download_file_path, url ])
26
+ current_size = initial_size
27
+ while current_size < download_size:
28
+ if is_file(download_file_path):
29
+ current_size = get_file_size(download_file_path)
30
+ progress.update(current_size - progress.n)
31
+ if download_size and not is_download_done(url, download_file_path):
32
+ os.remove(download_file_path)
33
+ conditional_download(download_directory_path, [ url ])
34
+
35
+
36
+ @lru_cache(maxsize = None)
37
+ def get_download_size(url : str) -> int:
38
+ try:
39
+ response = urllib.request.urlopen(url, timeout = 10)
40
+ return int(response.getheader('Content-Length'))
41
+ except (OSError, ValueError):
42
+ return 0
43
+
44
+
45
+ def is_download_done(url : str, file_path : str) -> bool:
46
+ if is_file(file_path):
47
+ return get_download_size(url) == get_file_size(file_path)
48
+ return False
facefusion/facefusion/execution.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Any
2
+ from functools import lru_cache
3
+ import subprocess
4
+ import xml.etree.ElementTree as ElementTree
5
+ import onnxruntime
6
+
7
+ from facefusion.my_typing import ExecutionDevice, ValueAndUnit
8
+
9
+
10
+ def encode_execution_providers(execution_providers : List[str]) -> List[str]:
11
+ return [ execution_provider.replace('ExecutionProvider', '').lower() for execution_provider in execution_providers ]
12
+
13
+
14
+ def decode_execution_providers(execution_providers : List[str]) -> List[str]:
15
+ available_execution_providers = onnxruntime.get_available_providers()
16
+ encoded_execution_providers = encode_execution_providers(available_execution_providers)
17
+
18
+ return [ execution_provider for execution_provider, encoded_execution_provider in zip(available_execution_providers, encoded_execution_providers) if any(execution_provider in encoded_execution_provider for execution_provider in execution_providers) ]
19
+
20
+
21
+ def has_execution_provider(execution_provider : str) -> bool:
22
+ return execution_provider in onnxruntime.get_available_providers()
23
+
24
+
25
+ def apply_execution_provider_options(execution_device_id : str, execution_providers : List[str]) -> List[Any]:
26
+ execution_providers_with_options : List[Any] = []
27
+
28
+ for execution_provider in execution_providers:
29
+ if execution_provider == 'CUDAExecutionProvider':
30
+ execution_providers_with_options.append((execution_provider,
31
+ {
32
+ 'device_id': execution_device_id,
33
+ 'cudnn_conv_algo_search': 'EXHAUSTIVE' if use_exhaustive() else 'DEFAULT'
34
+ }))
35
+ elif execution_provider == 'OpenVINOExecutionProvider':
36
+ execution_providers_with_options.append((execution_provider,
37
+ {
38
+ 'device_id': execution_device_id,
39
+ 'device_type': execution_device_id + '_FP32'
40
+ }))
41
+ elif execution_provider in [ 'DmlExecutionProvider', 'ROCMExecutionProvider' ]:
42
+ execution_providers_with_options.append((execution_provider,
43
+ {
44
+ 'device_id': execution_device_id
45
+ }))
46
+ else:
47
+ execution_providers_with_options.append(execution_provider)
48
+ return execution_providers_with_options
49
+
50
+
51
+ def use_exhaustive() -> bool:
52
+ execution_devices = detect_static_execution_devices()
53
+ product_names = ('GeForce GTX 1630', 'GeForce GTX 1650', 'GeForce GTX 1660')
54
+
55
+ return any(execution_device.get('product').get('name').startswith(product_names) for execution_device in execution_devices)
56
+
57
+
58
+ def run_nvidia_smi() -> subprocess.Popen[bytes]:
59
+ commands = [ 'nvidia-smi', '--query', '--xml-format' ]
60
+ return subprocess.Popen(commands, stdout = subprocess.PIPE)
61
+
62
+
63
+ @lru_cache(maxsize = None)
64
+ def detect_static_execution_devices() -> List[ExecutionDevice]:
65
+ return detect_execution_devices()
66
+
67
+
68
+ def detect_execution_devices() -> List[ExecutionDevice]:
69
+ execution_devices : List[ExecutionDevice] = []
70
+ try:
71
+ output, _ = run_nvidia_smi().communicate()
72
+ root_element = ElementTree.fromstring(output)
73
+ except Exception:
74
+ root_element = ElementTree.Element('xml')
75
+
76
+ for gpu_element in root_element.findall('gpu'):
77
+ execution_devices.append(
78
+ {
79
+ 'driver_version': root_element.find('driver_version').text,
80
+ 'framework':
81
+ {
82
+ 'name': 'CUDA',
83
+ 'version': root_element.find('cuda_version').text
84
+ },
85
+ 'product':
86
+ {
87
+ 'vendor': 'NVIDIA',
88
+ 'name': gpu_element.find('product_name').text.replace('NVIDIA ', '')
89
+ },
90
+ 'video_memory':
91
+ {
92
+ 'total': create_value_and_unit(gpu_element.find('fb_memory_usage/total').text),
93
+ 'free': create_value_and_unit(gpu_element.find('fb_memory_usage/free').text)
94
+ },
95
+ 'utilization':
96
+ {
97
+ 'gpu': create_value_and_unit(gpu_element.find('utilization/gpu_util').text),
98
+ 'memory': create_value_and_unit(gpu_element.find('utilization/memory_util').text)
99
+ }
100
+ })
101
+ return execution_devices
102
+
103
+
104
+ def create_value_and_unit(text : str) -> ValueAndUnit:
105
+ value, unit = text.split()
106
+ value_and_unit : ValueAndUnit =\
107
+ {
108
+ 'value': value,
109
+ 'unit': unit
110
+ }
111
+
112
+ return value_and_unit
facefusion/facefusion/face_analyser.py ADDED
@@ -0,0 +1,586 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Optional, List, Tuple
2
+ from time import sleep
3
+ import cv2
4
+ import numpy
5
+ import onnxruntime
6
+
7
+ import facefusion.globals
8
+ from facefusion import process_manager
9
+ from facefusion.common_helper import get_first
10
+ from facefusion.face_helper import estimate_matrix_by_face_landmark_5, warp_face_by_face_landmark_5, warp_face_by_translation, create_static_anchors, distance_to_face_landmark_5, distance_to_bounding_box, convert_face_landmark_68_to_5, apply_nms, categorize_age, categorize_gender
11
+ from facefusion.face_store import get_static_faces, set_static_faces
12
+ from facefusion.execution import apply_execution_provider_options
13
+ from facefusion.download import conditional_download
14
+ from facefusion.filesystem import resolve_relative_path, is_file
15
+ from facefusion.thread_helper import thread_lock, thread_semaphore, conditional_thread_semaphore
16
+ from facefusion.my_typing import VisionFrame, Face, FaceSet, FaceAnalyserOrder, FaceAnalyserAge, FaceAnalyserGender, ModelSet, BoundingBox, FaceLandmarkSet, FaceLandmark5, FaceLandmark68, Score, FaceScoreSet, Embedding
17
+ from facefusion.vision import resize_frame_resolution, unpack_resolution
18
+
19
+ FACE_ANALYSER = None
20
+ MODELS : ModelSet =\
21
+ {
22
+ 'face_detector_retinaface':
23
+ {
24
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/retinaface_10g.onnx',
25
+ 'path': resolve_relative_path('../.assets/models/retinaface_10g.onnx')
26
+ },
27
+ 'face_detector_scrfd':
28
+ {
29
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/scrfd_2.5g.onnx',
30
+ 'path': resolve_relative_path('../.assets/models/scrfd_2.5g.onnx')
31
+ },
32
+ 'face_detector_yoloface':
33
+ {
34
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/yoloface_8n.onnx',
35
+ 'path': resolve_relative_path('../.assets/models/yoloface_8n.onnx')
36
+ },
37
+ 'face_detector_yunet':
38
+ {
39
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/yunet_2023mar.onnx',
40
+ 'path': resolve_relative_path('../.assets/models/yunet_2023mar.onnx')
41
+ },
42
+ 'face_recognizer_arcface_blendswap':
43
+ {
44
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_w600k_r50.onnx',
45
+ 'path': resolve_relative_path('../.assets/models/arcface_w600k_r50.onnx')
46
+ },
47
+ 'face_recognizer_arcface_inswapper':
48
+ {
49
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_w600k_r50.onnx',
50
+ 'path': resolve_relative_path('../.assets/models/arcface_w600k_r50.onnx')
51
+ },
52
+ 'face_recognizer_arcface_simswap':
53
+ {
54
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_simswap.onnx',
55
+ 'path': resolve_relative_path('../.assets/models/arcface_simswap.onnx')
56
+ },
57
+ 'face_recognizer_arcface_uniface':
58
+ {
59
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/arcface_w600k_r50.onnx',
60
+ 'path': resolve_relative_path('../.assets/models/arcface_w600k_r50.onnx')
61
+ },
62
+ 'face_landmarker_68':
63
+ {
64
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/2dfan4.onnx',
65
+ 'path': resolve_relative_path('../.assets/models/2dfan4.onnx')
66
+ },
67
+ 'face_landmarker_68_5':
68
+ {
69
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/face_landmarker_68_5.onnx',
70
+ 'path': resolve_relative_path('../.assets/models/face_landmarker_68_5.onnx')
71
+ },
72
+ 'gender_age':
73
+ {
74
+ 'url': 'https://github.com/facefusion/facefusion-assets/releases/download/models/gender_age.onnx',
75
+ 'path': resolve_relative_path('../.assets/models/gender_age.onnx')
76
+ }
77
+ }
78
+
79
+
80
+ def get_face_analyser() -> Any:
81
+ global FACE_ANALYSER
82
+
83
+ face_detectors = {}
84
+ face_landmarkers = {}
85
+
86
+ with thread_lock():
87
+ while process_manager.is_checking():
88
+ sleep(0.5)
89
+ if FACE_ANALYSER is None:
90
+ if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]:
91
+ face_detectors['retinaface'] = onnxruntime.InferenceSession(MODELS.get('face_detector_retinaface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
92
+ if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]:
93
+ face_detectors['scrfd'] = onnxruntime.InferenceSession(MODELS.get('face_detector_scrfd').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
94
+ if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]:
95
+ face_detectors['yoloface'] = onnxruntime.InferenceSession(MODELS.get('face_detector_yoloface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
96
+ if facefusion.globals.face_detector_model in [ 'yunet' ]:
97
+ face_detectors['yunet'] = cv2.FaceDetectorYN.create(MODELS.get('face_detector_yunet').get('path'), '', (0, 0))
98
+ if facefusion.globals.face_recognizer_model == 'arcface_blendswap':
99
+ face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_blendswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
100
+ if facefusion.globals.face_recognizer_model == 'arcface_inswapper':
101
+ face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_inswapper').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
102
+ if facefusion.globals.face_recognizer_model == 'arcface_simswap':
103
+ face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_simswap').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
104
+ if facefusion.globals.face_recognizer_model == 'arcface_uniface':
105
+ face_recognizer = onnxruntime.InferenceSession(MODELS.get('face_recognizer_arcface_uniface').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
106
+ face_landmarkers['68'] = onnxruntime.InferenceSession(MODELS.get('face_landmarker_68').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
107
+ face_landmarkers['68_5'] = onnxruntime.InferenceSession(MODELS.get('face_landmarker_68_5').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
108
+ gender_age = onnxruntime.InferenceSession(MODELS.get('gender_age').get('path'), providers = apply_execution_provider_options(facefusion.globals.execution_device_id, facefusion.globals.execution_providers))
109
+ FACE_ANALYSER =\
110
+ {
111
+ 'face_detectors': face_detectors,
112
+ 'face_recognizer': face_recognizer,
113
+ 'face_landmarkers': face_landmarkers,
114
+ 'gender_age': gender_age
115
+ }
116
+ return FACE_ANALYSER
117
+
118
+
119
+ def clear_face_analyser() -> Any:
120
+ global FACE_ANALYSER
121
+
122
+ FACE_ANALYSER = None
123
+
124
+
125
+ def pre_check() -> bool:
126
+ download_directory_path = resolve_relative_path('../.assets/models')
127
+ model_urls =\
128
+ [
129
+ MODELS.get('face_landmarker_68').get('url'),
130
+ MODELS.get('face_landmarker_68_5').get('url'),
131
+ MODELS.get('gender_age').get('url')
132
+ ]
133
+ model_paths =\
134
+ [
135
+ MODELS.get('face_landmarker_68').get('path'),
136
+ MODELS.get('face_landmarker_68_5').get('path'),
137
+ MODELS.get('gender_age').get('path')
138
+ ]
139
+
140
+ if facefusion.globals.face_detector_model in [ 'many', 'retinaface' ]:
141
+ model_urls.append(MODELS.get('face_detector_retinaface').get('url'))
142
+ model_paths.append(MODELS.get('face_detector_retinaface').get('path'))
143
+ if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]:
144
+ model_urls.append(MODELS.get('face_detector_scrfd').get('url'))
145
+ model_paths.append(MODELS.get('face_detector_scrfd').get('path'))
146
+ if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]:
147
+ model_urls.append(MODELS.get('face_detector_yoloface').get('url'))
148
+ model_paths.append(MODELS.get('face_detector_yoloface').get('path'))
149
+ if facefusion.globals.face_detector_model in [ 'yunet' ]:
150
+ model_urls.append(MODELS.get('face_detector_yunet').get('url'))
151
+ model_paths.append(MODELS.get('face_detector_yunet').get('path'))
152
+ if facefusion.globals.face_recognizer_model == 'arcface_blendswap':
153
+ model_urls.append(MODELS.get('face_recognizer_arcface_blendswap').get('url'))
154
+ model_paths.append(MODELS.get('face_recognizer_arcface_blendswap').get('path'))
155
+ if facefusion.globals.face_recognizer_model == 'arcface_inswapper':
156
+ model_urls.append(MODELS.get('face_recognizer_arcface_inswapper').get('url'))
157
+ model_paths.append(MODELS.get('face_recognizer_arcface_inswapper').get('path'))
158
+ if facefusion.globals.face_recognizer_model == 'arcface_simswap':
159
+ model_urls.append(MODELS.get('face_recognizer_arcface_simswap').get('url'))
160
+ model_paths.append(MODELS.get('face_recognizer_arcface_simswap').get('path'))
161
+ if facefusion.globals.face_recognizer_model == 'arcface_uniface':
162
+ model_urls.append(MODELS.get('face_recognizer_arcface_uniface').get('url'))
163
+ model_paths.append(MODELS.get('face_recognizer_arcface_uniface').get('path'))
164
+
165
+ if not facefusion.globals.skip_download:
166
+ process_manager.check()
167
+ conditional_download(download_directory_path, model_urls)
168
+ process_manager.end()
169
+ return all(is_file(model_path) for model_path in model_paths)
170
+
171
+
172
+ def detect_with_retinaface(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]:
173
+ face_detector = get_face_analyser().get('face_detectors').get('retinaface')
174
+ face_detector_width, face_detector_height = unpack_resolution(face_detector_size)
175
+ temp_vision_frame = resize_frame_resolution(vision_frame, (face_detector_width, face_detector_height))
176
+ ratio_height = vision_frame.shape[0] / temp_vision_frame.shape[0]
177
+ ratio_width = vision_frame.shape[1] / temp_vision_frame.shape[1]
178
+ feature_strides = [ 8, 16, 32 ]
179
+ feature_map_channel = 3
180
+ anchor_total = 2
181
+ bounding_box_list = []
182
+ face_landmark_5_list = []
183
+ score_list = []
184
+
185
+ detect_vision_frame = prepare_detect_frame(temp_vision_frame, face_detector_size)
186
+ with thread_semaphore():
187
+ detections = face_detector.run(None,
188
+ {
189
+ face_detector.get_inputs()[0].name: detect_vision_frame
190
+ })
191
+ for index, feature_stride in enumerate(feature_strides):
192
+ keep_indices = numpy.where(detections[index] >= facefusion.globals.face_detector_score)[0]
193
+ if keep_indices.any():
194
+ stride_height = face_detector_height // feature_stride
195
+ stride_width = face_detector_width // feature_stride
196
+ anchors = create_static_anchors(feature_stride, anchor_total, stride_height, stride_width)
197
+ bounding_box_raw = detections[index + feature_map_channel] * feature_stride
198
+ face_landmark_5_raw = detections[index + feature_map_channel * 2] * feature_stride
199
+ for bounding_box in distance_to_bounding_box(anchors, bounding_box_raw)[keep_indices]:
200
+ bounding_box_list.append(numpy.array(
201
+ [
202
+ bounding_box[0] * ratio_width,
203
+ bounding_box[1] * ratio_height,
204
+ bounding_box[2] * ratio_width,
205
+ bounding_box[3] * ratio_height
206
+ ]))
207
+ for face_landmark_5 in distance_to_face_landmark_5(anchors, face_landmark_5_raw)[keep_indices]:
208
+ face_landmark_5_list.append(face_landmark_5 * [ ratio_width, ratio_height ])
209
+ for score in detections[index][keep_indices]:
210
+ score_list.append(score[0])
211
+ return bounding_box_list, face_landmark_5_list, score_list
212
+
213
+
214
+ def detect_with_scrfd(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]:
215
+ face_detector = get_face_analyser().get('face_detectors').get('scrfd')
216
+ face_detector_width, face_detector_height = unpack_resolution(face_detector_size)
217
+ temp_vision_frame = resize_frame_resolution(vision_frame, (face_detector_width, face_detector_height))
218
+ ratio_height = vision_frame.shape[0] / temp_vision_frame.shape[0]
219
+ ratio_width = vision_frame.shape[1] / temp_vision_frame.shape[1]
220
+ feature_strides = [ 8, 16, 32 ]
221
+ feature_map_channel = 3
222
+ anchor_total = 2
223
+ bounding_box_list = []
224
+ face_landmark_5_list = []
225
+ score_list = []
226
+
227
+ detect_vision_frame = prepare_detect_frame(temp_vision_frame, face_detector_size)
228
+ with thread_semaphore():
229
+ detections = face_detector.run(None,
230
+ {
231
+ face_detector.get_inputs()[0].name: detect_vision_frame
232
+ })
233
+ for index, feature_stride in enumerate(feature_strides):
234
+ keep_indices = numpy.where(detections[index] >= facefusion.globals.face_detector_score)[0]
235
+ if keep_indices.any():
236
+ stride_height = face_detector_height // feature_stride
237
+ stride_width = face_detector_width // feature_stride
238
+ anchors = create_static_anchors(feature_stride, anchor_total, stride_height, stride_width)
239
+ bounding_box_raw = detections[index + feature_map_channel] * feature_stride
240
+ face_landmark_5_raw = detections[index + feature_map_channel * 2] * feature_stride
241
+ for bounding_box in distance_to_bounding_box(anchors, bounding_box_raw)[keep_indices]:
242
+ bounding_box_list.append(numpy.array(
243
+ [
244
+ bounding_box[0] * ratio_width,
245
+ bounding_box[1] * ratio_height,
246
+ bounding_box[2] * ratio_width,
247
+ bounding_box[3] * ratio_height
248
+ ]))
249
+ for face_landmark_5 in distance_to_face_landmark_5(anchors, face_landmark_5_raw)[keep_indices]:
250
+ face_landmark_5_list.append(face_landmark_5 * [ ratio_width, ratio_height ])
251
+ for score in detections[index][keep_indices]:
252
+ score_list.append(score[0])
253
+ return bounding_box_list, face_landmark_5_list, score_list
254
+
255
+
256
+ def detect_with_yoloface(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]:
257
+ face_detector = get_face_analyser().get('face_detectors').get('yoloface')
258
+ face_detector_width, face_detector_height = unpack_resolution(face_detector_size)
259
+ temp_vision_frame = resize_frame_resolution(vision_frame, (face_detector_width, face_detector_height))
260
+ ratio_height = vision_frame.shape[0] / temp_vision_frame.shape[0]
261
+ ratio_width = vision_frame.shape[1] / temp_vision_frame.shape[1]
262
+ bounding_box_list = []
263
+ face_landmark_5_list = []
264
+ score_list = []
265
+
266
+ detect_vision_frame = prepare_detect_frame(temp_vision_frame, face_detector_size)
267
+ with thread_semaphore():
268
+ detections = face_detector.run(None,
269
+ {
270
+ face_detector.get_inputs()[0].name: detect_vision_frame
271
+ })
272
+ detections = numpy.squeeze(detections).T
273
+ bounding_box_raw, score_raw, face_landmark_5_raw = numpy.split(detections, [ 4, 5 ], axis = 1)
274
+ keep_indices = numpy.where(score_raw > facefusion.globals.face_detector_score)[0]
275
+ if keep_indices.any():
276
+ bounding_box_raw, face_landmark_5_raw, score_raw = bounding_box_raw[keep_indices], face_landmark_5_raw[keep_indices], score_raw[keep_indices]
277
+ for bounding_box in bounding_box_raw:
278
+ bounding_box_list.append(numpy.array(
279
+ [
280
+ (bounding_box[0] - bounding_box[2] / 2) * ratio_width,
281
+ (bounding_box[1] - bounding_box[3] / 2) * ratio_height,
282
+ (bounding_box[0] + bounding_box[2] / 2) * ratio_width,
283
+ (bounding_box[1] + bounding_box[3] / 2) * ratio_height
284
+ ]))
285
+ face_landmark_5_raw[:, 0::3] = (face_landmark_5_raw[:, 0::3]) * ratio_width
286
+ face_landmark_5_raw[:, 1::3] = (face_landmark_5_raw[:, 1::3]) * ratio_height
287
+ for face_landmark_5 in face_landmark_5_raw:
288
+ face_landmark_5_list.append(numpy.array(face_landmark_5.reshape(-1, 3)[:, :2]))
289
+ score_list = score_raw.ravel().tolist()
290
+ return bounding_box_list, face_landmark_5_list, score_list
291
+
292
+
293
+ def detect_with_yunet(vision_frame : VisionFrame, face_detector_size : str) -> Tuple[List[BoundingBox], List[FaceLandmark5], List[Score]]:
294
+ face_detector = get_face_analyser().get('face_detectors').get('yunet')
295
+ face_detector_width, face_detector_height = unpack_resolution(face_detector_size)
296
+ temp_vision_frame = resize_frame_resolution(vision_frame, (face_detector_width, face_detector_height))
297
+ ratio_height = vision_frame.shape[0] / temp_vision_frame.shape[0]
298
+ ratio_width = vision_frame.shape[1] / temp_vision_frame.shape[1]
299
+ bounding_box_list = []
300
+ face_landmark_5_list = []
301
+ score_list = []
302
+
303
+ face_detector.setInputSize((temp_vision_frame.shape[1], temp_vision_frame.shape[0]))
304
+ face_detector.setScoreThreshold(facefusion.globals.face_detector_score)
305
+ with thread_semaphore():
306
+ _, detections = face_detector.detect(temp_vision_frame)
307
+ if numpy.any(detections):
308
+ for detection in detections:
309
+ bounding_box_list.append(numpy.array(
310
+ [
311
+ detection[0] * ratio_width,
312
+ detection[1] * ratio_height,
313
+ (detection[0] + detection[2]) * ratio_width,
314
+ (detection[1] + detection[3]) * ratio_height
315
+ ]))
316
+ face_landmark_5_list.append(detection[4:14].reshape((5, 2)) * [ ratio_width, ratio_height ])
317
+ score_list.append(detection[14])
318
+ return bounding_box_list, face_landmark_5_list, score_list
319
+
320
+
321
+ def prepare_detect_frame(temp_vision_frame : VisionFrame, face_detector_size : str) -> VisionFrame:
322
+ face_detector_width, face_detector_height = unpack_resolution(face_detector_size)
323
+ detect_vision_frame = numpy.zeros((face_detector_height, face_detector_width, 3))
324
+ detect_vision_frame[:temp_vision_frame.shape[0], :temp_vision_frame.shape[1], :] = temp_vision_frame
325
+ detect_vision_frame = (detect_vision_frame - 127.5) / 128.0
326
+ detect_vision_frame = numpy.expand_dims(detect_vision_frame.transpose(2, 0, 1), axis = 0).astype(numpy.float32)
327
+ return detect_vision_frame
328
+
329
+
330
+ def create_faces(vision_frame : VisionFrame, bounding_box_list : List[BoundingBox], face_landmark_5_list : List[FaceLandmark5], score_list : List[Score]) -> List[Face]:
331
+ faces = []
332
+ if facefusion.globals.face_detector_score > 0:
333
+ sort_indices = numpy.argsort(-numpy.array(score_list))
334
+ bounding_box_list = [ bounding_box_list[index] for index in sort_indices ]
335
+ face_landmark_5_list = [face_landmark_5_list[index] for index in sort_indices]
336
+ score_list = [ score_list[index] for index in sort_indices ]
337
+ iou_threshold = 0.1 if facefusion.globals.face_detector_model == 'many' else 0.4
338
+ keep_indices = apply_nms(bounding_box_list, iou_threshold)
339
+ for index in keep_indices:
340
+ bounding_box = bounding_box_list[index]
341
+ face_landmark_5_68 = face_landmark_5_list[index]
342
+ face_landmark_68_5 = expand_face_landmark_68_from_5(face_landmark_5_68)
343
+ face_landmark_68 = face_landmark_68_5
344
+ face_landmark_68_score = 0.0
345
+ if facefusion.globals.face_landmarker_score > 0:
346
+ face_landmark_68, face_landmark_68_score = detect_face_landmark_68(vision_frame, bounding_box)
347
+ if face_landmark_68_score > facefusion.globals.face_landmarker_score:
348
+ face_landmark_5_68 = convert_face_landmark_68_to_5(face_landmark_68)
349
+ landmarks : FaceLandmarkSet =\
350
+ {
351
+ '5': face_landmark_5_list[index],
352
+ '5/68': face_landmark_5_68,
353
+ '68': face_landmark_68,
354
+ '68/5': face_landmark_68_5
355
+ }
356
+ scores : FaceScoreSet = \
357
+ {
358
+ 'detector': score_list[index],
359
+ 'landmarker': face_landmark_68_score
360
+ }
361
+ embedding, normed_embedding = calc_embedding(vision_frame, landmarks.get('5/68'))
362
+ gender, age = detect_gender_age(vision_frame, bounding_box)
363
+ faces.append(Face(
364
+ bounding_box = bounding_box,
365
+ landmarks = landmarks,
366
+ scores = scores,
367
+ embedding = embedding,
368
+ normed_embedding = normed_embedding,
369
+ gender = gender,
370
+ age = age
371
+ ))
372
+ return faces
373
+
374
+
375
+ def calc_embedding(temp_vision_frame : VisionFrame, face_landmark_5 : FaceLandmark5) -> Tuple[Embedding, Embedding]:
376
+ face_recognizer = get_face_analyser().get('face_recognizer')
377
+ crop_vision_frame, matrix = warp_face_by_face_landmark_5(temp_vision_frame, face_landmark_5, 'arcface_112_v2', (112, 112))
378
+ crop_vision_frame = crop_vision_frame / 127.5 - 1
379
+ crop_vision_frame = crop_vision_frame[:, :, ::-1].transpose(2, 0, 1).astype(numpy.float32)
380
+ crop_vision_frame = numpy.expand_dims(crop_vision_frame, axis = 0)
381
+ with conditional_thread_semaphore(facefusion.globals.execution_providers):
382
+ embedding = face_recognizer.run(None,
383
+ {
384
+ face_recognizer.get_inputs()[0].name: crop_vision_frame
385
+ })[0]
386
+ embedding = embedding.ravel()
387
+ normed_embedding = embedding / numpy.linalg.norm(embedding)
388
+ return embedding, normed_embedding
389
+
390
+
391
+ def detect_face_landmark_68(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[FaceLandmark68, Score]:
392
+ face_landmarker = get_face_analyser().get('face_landmarkers').get('68')
393
+ scale = 195 / numpy.subtract(bounding_box[2:], bounding_box[:2]).max()
394
+ translation = (256 - numpy.add(bounding_box[2:], bounding_box[:2]) * scale) * 0.5
395
+ crop_vision_frame, affine_matrix = warp_face_by_translation(temp_vision_frame, translation, scale, (256, 256))
396
+ crop_vision_frame = cv2.cvtColor(crop_vision_frame, cv2.COLOR_RGB2Lab)
397
+ if numpy.mean(crop_vision_frame[:, :, 0]) < 30:
398
+ crop_vision_frame[:, :, 0] = cv2.createCLAHE(clipLimit = 2).apply(crop_vision_frame[:, :, 0])
399
+ crop_vision_frame = cv2.cvtColor(crop_vision_frame, cv2.COLOR_Lab2RGB)
400
+ crop_vision_frame = crop_vision_frame.transpose(2, 0, 1).astype(numpy.float32) / 255.0
401
+ with conditional_thread_semaphore(facefusion.globals.execution_providers):
402
+ face_landmark_68, face_heatmap = face_landmarker.run(None,
403
+ {
404
+ face_landmarker.get_inputs()[0].name: [ crop_vision_frame ]
405
+ })
406
+ face_landmark_68 = face_landmark_68[:, :, :2][0] / 64
407
+ face_landmark_68 = face_landmark_68.reshape(1, -1, 2) * 256
408
+ face_landmark_68 = cv2.transform(face_landmark_68, cv2.invertAffineTransform(affine_matrix))
409
+ face_landmark_68 = face_landmark_68.reshape(-1, 2)
410
+ face_landmark_68_score = numpy.amax(face_heatmap, axis = (2, 3))
411
+ face_landmark_68_score = numpy.mean(face_landmark_68_score)
412
+ return face_landmark_68, face_landmark_68_score
413
+
414
+
415
+ def expand_face_landmark_68_from_5(face_landmark_5 : FaceLandmark5) -> FaceLandmark68:
416
+ face_landmarker = get_face_analyser().get('face_landmarkers').get('68_5')
417
+ affine_matrix = estimate_matrix_by_face_landmark_5(face_landmark_5, 'ffhq_512', (1, 1))
418
+ face_landmark_5 = cv2.transform(face_landmark_5.reshape(1, -1, 2), affine_matrix).reshape(-1, 2)
419
+ with conditional_thread_semaphore(facefusion.globals.execution_providers):
420
+ face_landmark_68_5 = face_landmarker.run(None,
421
+ {
422
+ face_landmarker.get_inputs()[0].name: [ face_landmark_5 ]
423
+ })[0][0]
424
+ face_landmark_68_5 = cv2.transform(face_landmark_68_5.reshape(1, -1, 2), cv2.invertAffineTransform(affine_matrix)).reshape(-1, 2)
425
+ return face_landmark_68_5
426
+
427
+
428
+ def detect_gender_age(temp_vision_frame : VisionFrame, bounding_box : BoundingBox) -> Tuple[int, int]:
429
+ gender_age = get_face_analyser().get('gender_age')
430
+ bounding_box = bounding_box.reshape(2, -1)
431
+ scale = 64 / numpy.subtract(*bounding_box[::-1]).max()
432
+ translation = 48 - bounding_box.sum(axis = 0) * scale * 0.5
433
+ crop_vision_frame, affine_matrix = warp_face_by_translation(temp_vision_frame, translation, scale, (96, 96))
434
+ crop_vision_frame = crop_vision_frame[:, :, ::-1].transpose(2, 0, 1).astype(numpy.float32)
435
+ crop_vision_frame = numpy.expand_dims(crop_vision_frame, axis = 0)
436
+ with conditional_thread_semaphore(facefusion.globals.execution_providers):
437
+ prediction = gender_age.run(None,
438
+ {
439
+ gender_age.get_inputs()[0].name: crop_vision_frame
440
+ })[0][0]
441
+ gender = int(numpy.argmax(prediction[:2]))
442
+ age = int(numpy.round(prediction[2] * 100))
443
+ return gender, age
444
+
445
+
446
+ def get_one_face(vision_frame : VisionFrame, position : int = 0) -> Optional[Face]:
447
+ many_faces = get_many_faces(vision_frame)
448
+ if many_faces:
449
+ try:
450
+ return many_faces[position]
451
+ except IndexError:
452
+ return many_faces[-1]
453
+ return None
454
+
455
+
456
+ def get_average_face(vision_frames : List[VisionFrame], position : int = 0) -> Optional[Face]:
457
+ average_face = None
458
+ faces = []
459
+ embedding_list = []
460
+ normed_embedding_list = []
461
+
462
+ for vision_frame in vision_frames:
463
+ face = get_one_face(vision_frame, position)
464
+ if face:
465
+ faces.append(face)
466
+ embedding_list.append(face.embedding)
467
+ normed_embedding_list.append(face.normed_embedding)
468
+ if faces:
469
+ first_face = get_first(faces)
470
+ average_face = Face(
471
+ bounding_box = first_face.bounding_box,
472
+ landmarks = first_face.landmarks,
473
+ scores = first_face.scores,
474
+ embedding = numpy.mean(embedding_list, axis = 0),
475
+ normed_embedding = numpy.mean(normed_embedding_list, axis = 0),
476
+ gender = first_face.gender,
477
+ age = first_face.age
478
+ )
479
+ return average_face
480
+
481
+
482
+ def get_many_faces(vision_frame : VisionFrame) -> List[Face]:
483
+ faces = []
484
+ try:
485
+ faces_cache = get_static_faces(vision_frame)
486
+ if faces_cache:
487
+ faces = faces_cache
488
+ else:
489
+ bounding_box_list = []
490
+ face_landmark_5_list = []
491
+ score_list = []
492
+
493
+ if facefusion.globals.face_detector_model in [ 'many', 'retinaface']:
494
+ bounding_box_list_retinaface, face_landmark_5_list_retinaface, score_list_retinaface = detect_with_retinaface(vision_frame, facefusion.globals.face_detector_size)
495
+ bounding_box_list.extend(bounding_box_list_retinaface)
496
+ face_landmark_5_list.extend(face_landmark_5_list_retinaface)
497
+ score_list.extend(score_list_retinaface)
498
+ if facefusion.globals.face_detector_model in [ 'many', 'scrfd' ]:
499
+ bounding_box_list_scrfd, face_landmark_5_list_scrfd, score_list_scrfd = detect_with_scrfd(vision_frame, facefusion.globals.face_detector_size)
500
+ bounding_box_list.extend(bounding_box_list_scrfd)
501
+ face_landmark_5_list.extend(face_landmark_5_list_scrfd)
502
+ score_list.extend(score_list_scrfd)
503
+ if facefusion.globals.face_detector_model in [ 'many', 'yoloface' ]:
504
+ bounding_box_list_yoloface, face_landmark_5_list_yoloface, score_list_yoloface = detect_with_yoloface(vision_frame, facefusion.globals.face_detector_size)
505
+ bounding_box_list.extend(bounding_box_list_yoloface)
506
+ face_landmark_5_list.extend(face_landmark_5_list_yoloface)
507
+ score_list.extend(score_list_yoloface)
508
+ if facefusion.globals.face_detector_model in [ 'yunet' ]:
509
+ bounding_box_list_yunet, face_landmark_5_list_yunet, score_list_yunet = detect_with_yunet(vision_frame, facefusion.globals.face_detector_size)
510
+ bounding_box_list.extend(bounding_box_list_yunet)
511
+ face_landmark_5_list.extend(face_landmark_5_list_yunet)
512
+ score_list.extend(score_list_yunet)
513
+ if bounding_box_list and face_landmark_5_list and score_list:
514
+ faces = create_faces(vision_frame, bounding_box_list, face_landmark_5_list, score_list)
515
+ if faces:
516
+ set_static_faces(vision_frame, faces)
517
+ if facefusion.globals.face_analyser_order:
518
+ faces = sort_by_order(faces, facefusion.globals.face_analyser_order)
519
+ if facefusion.globals.face_analyser_age:
520
+ faces = filter_by_age(faces, facefusion.globals.face_analyser_age)
521
+ if facefusion.globals.face_analyser_gender:
522
+ faces = filter_by_gender(faces, facefusion.globals.face_analyser_gender)
523
+ except (AttributeError, ValueError):
524
+ pass
525
+ return faces
526
+
527
+
528
+ def find_similar_faces(reference_faces : FaceSet, vision_frame : VisionFrame, face_distance : float) -> List[Face]:
529
+ similar_faces : List[Face] = []
530
+ many_faces = get_many_faces(vision_frame)
531
+
532
+ if reference_faces:
533
+ for reference_set in reference_faces:
534
+ if not similar_faces:
535
+ for reference_face in reference_faces[reference_set]:
536
+ for face in many_faces:
537
+ if compare_faces(face, reference_face, face_distance):
538
+ similar_faces.append(face)
539
+ return similar_faces
540
+
541
+
542
+ def compare_faces(face : Face, reference_face : Face, face_distance : float) -> bool:
543
+ current_face_distance = calc_face_distance(face, reference_face)
544
+ return current_face_distance < face_distance
545
+
546
+
547
+ def calc_face_distance(face : Face, reference_face : Face) -> float:
548
+ if hasattr(face, 'normed_embedding') and hasattr(reference_face, 'normed_embedding'):
549
+ return 1 - numpy.dot(face.normed_embedding, reference_face.normed_embedding)
550
+ return 0
551
+
552
+
553
+ def sort_by_order(faces : List[Face], order : FaceAnalyserOrder) -> List[Face]:
554
+ if order == 'left-right':
555
+ return sorted(faces, key = lambda face: face.bounding_box[0])
556
+ if order == 'right-left':
557
+ return sorted(faces, key = lambda face: face.bounding_box[0], reverse = True)
558
+ if order == 'top-bottom':
559
+ return sorted(faces, key = lambda face: face.bounding_box[1])
560
+ if order == 'bottom-top':
561
+ return sorted(faces, key = lambda face: face.bounding_box[1], reverse = True)
562
+ if order == 'small-large':
563
+ return sorted(faces, key = lambda face: (face.bounding_box[2] - face.bounding_box[0]) * (face.bounding_box[3] - face.bounding_box[1]))
564
+ if order == 'large-small':
565
+ return sorted(faces, key = lambda face: (face.bounding_box[2] - face.bounding_box[0]) * (face.bounding_box[3] - face.bounding_box[1]), reverse = True)
566
+ if order == 'best-worst':
567
+ return sorted(faces, key = lambda face: face.scores.get('detector'), reverse = True)
568
+ if order == 'worst-best':
569
+ return sorted(faces, key = lambda face: face.scores.get('detector'))
570
+ return faces
571
+
572
+
573
+ def filter_by_age(faces : List[Face], age : FaceAnalyserAge) -> List[Face]:
574
+ filter_faces = []
575
+ for face in faces:
576
+ if categorize_age(face.age) == age:
577
+ filter_faces.append(face)
578
+ return filter_faces
579
+
580
+
581
+ def filter_by_gender(faces : List[Face], gender : FaceAnalyserGender) -> List[Face]:
582
+ filter_faces = []
583
+ for face in faces:
584
+ if categorize_gender(face.gender) == gender:
585
+ filter_faces.append(face)
586
+ return filter_faces