Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0104f7f6a9 | ||
|
|
6b1f5cbf69 | ||
|
|
f888e3d75d | ||
|
|
16631d21d9 | ||
|
|
ccb4ba08fc | ||
|
|
0daf114fe8 | ||
|
|
4e9c9c897c | ||
|
|
31fde1ae34 | ||
|
|
aadbb0b389 | ||
|
|
52a8e7faf3 | ||
|
|
3893873085 | ||
|
|
037080ac39 | ||
|
|
4738313b64 | ||
|
|
e842c3bd06 | ||
|
|
fa73da5a00 | ||
|
|
ffe26e8571 |
@@ -52,6 +52,11 @@ git clone https://github.com/yolain/ComfyUI-Easy-Use
|
||||
|
||||
## 📜 更新日志
|
||||
|
||||
**v1.2.8**
|
||||
|
||||
- 修复了一些BUG (😹)
|
||||
- 增加了多语言目录
|
||||
|
||||
**v1.2.7**
|
||||
|
||||
- 优化管理节点组显示
|
||||
|
||||
@@ -47,10 +47,16 @@ Double-click install.bat to install the required dependencies
|
||||
|
||||
## 📜 Changelog
|
||||
|
||||
**v1.2.8**
|
||||
|
||||
- Added the multi-language catalog
|
||||
- Fix CLIP vision model download URLs for IPAdapter and DynamiCrafter
|
||||
- Improve error handling for model downloads with clearer error messages and better handling of download failures
|
||||
|
||||
**v1.2.7**
|
||||
|
||||
- Optimize display of the node maps
|
||||
- Add `ben2` on `easy imageRemBg`
|
||||
- Added `ben2` on `easy imageRemBg`
|
||||
- Using a new way to display the models thumbnails in the loaders (supported diffusion_models、lors、checkpoints)
|
||||
|
||||
**v1.2.6**
|
||||
@@ -486,4 +492,4 @@ If my custom nodes has added value to your day, consider indulging in a coffee t
|
||||
|
||||
My gratitude extends to the generous souls who bestow a star. Your support is much appreciated!
|
||||
|
||||
[](https://github.com/yolain/ComfyUI-Easy-Use/stargazers)
|
||||
[](https://github.com/yolain/ComfyUI-Easy-Use/stargazers)
|
||||
|
||||
+30
-3
@@ -1,23 +1,50 @@
|
||||
__version__ = "1.2.7"
|
||||
__version__ = "1.2.8"
|
||||
|
||||
import yaml
|
||||
import json
|
||||
import os
|
||||
import folder_paths
|
||||
import importlib
|
||||
|
||||
cwd_path = os.path.dirname(os.path.realpath(__file__))
|
||||
comfy_path = folder_paths.base_path
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
importlib.import_module('.py.routes', __name__)
|
||||
importlib.import_module('.py.server', __name__)
|
||||
nodes_list = ["util", "seed", "prompt", "loaders", "adapter", "inpaint", "preSampling", "samplers", "fix", "pipe", "xyplot", "image", "logic", "api", "deprecated"]
|
||||
# locale = {}
|
||||
for module_name in nodes_list:
|
||||
imported_module = importlib.import_module(".py.nodes.{}".format(module_name), __name__)
|
||||
NODE_CLASS_MAPPINGS = {**NODE_CLASS_MAPPINGS, **imported_module.NODE_CLASS_MAPPINGS}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {**NODE_DISPLAY_NAME_MAPPINGS, **imported_module.NODE_DISPLAY_NAME_MAPPINGS}
|
||||
# transfer python nodes to locale file
|
||||
# for i in imported_module.NODE_CLASS_MAPPINGS:
|
||||
# locale[i] = {
|
||||
# "display_name": imported_module.NODE_DISPLAY_NAME_MAPPINGS[i] if i in imported_module.NODE_DISPLAY_NAME_MAPPINGS else i,
|
||||
# "inputs":{},
|
||||
# "outputs":{},
|
||||
# }
|
||||
# node_class = imported_module.NODE_CLASS_MAPPINGS[i]
|
||||
# input_types = node_class.INPUT_TYPES()
|
||||
# if "required" in input_types:
|
||||
# for j in input_types["required"]:
|
||||
# locale[i]['inputs'][j] = {"name": j}
|
||||
# if "optional" in input_types:
|
||||
# for j in input_types["optional"]:
|
||||
# locale[i]['inputs'][j] = {"name": j}
|
||||
# count = 0
|
||||
# if "RETURN_NAMES" in node_class.__dict__:
|
||||
# for j in node_class.RETURN_NAMES:
|
||||
# locale[i]['outputs'][str(count)] = {"name": j}
|
||||
# count+=1
|
||||
|
||||
# en_json_path = os.path.join(cwd_path,'locales/en/nodeDefs.json')
|
||||
# with open(en_json_path, 'w', encoding='utf-8') as f:
|
||||
# json.dump(locale, f, ensure_ascii=False, indent=2)
|
||||
|
||||
cwd_path = os.path.dirname(os.path.realpath(__file__))
|
||||
comfy_path = folder_paths.base_path
|
||||
|
||||
#Wildcards
|
||||
from .py.libs.wildcards import read_wildcard_dict
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "Hotkeys",
|
||||
"Nodes": "Nodes",
|
||||
"NodesMap": "NodesMap"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "Util",
|
||||
"Seed": "Seed",
|
||||
"Prompt": "Prompt",
|
||||
"Loaders": "Loaders",
|
||||
"Adapter": "Adapter",
|
||||
"Inpaint": "Inpaint",
|
||||
"PreSampling": "PreSampling",
|
||||
"Sampler": "Sampler",
|
||||
"Fix": "Fix",
|
||||
"Pipe": "Pipe",
|
||||
"XY Inputs": "XY Inputs",
|
||||
"Image": "Image",
|
||||
"Segmentation": "Segmentation",
|
||||
"\uD83D\uDEAB Deprecated": "\uD83D\uDEAB Deprecated",
|
||||
"Type": "Type",
|
||||
"Math": "Math",
|
||||
"Switch": "Switch",
|
||||
"Index Switch": "Index Switch",
|
||||
"While Loop": "While Loop",
|
||||
"For Loop": "For Loop",
|
||||
"LoadImage": "Load Image"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "Enable Shift+g to add the selected nodes to a group",
|
||||
"tooltip": "From v1.2.39, you can use Ctrl+g instead"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "Enable Shift+r to unload model and node cache"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "Enable Shift+m to toggle nodes map"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "Enable Shift+Up/Down/Left/Right and Shift+Ctrl+Alt+Left/Right to align selected nodes",
|
||||
"tooltip": "Shift+Up/Down/Left/Right can align selected nodes, Shift+Ctrl+Alt+Left/Right can distribute nodes horizontally/vertically"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "Enable Shift+Ctrl+Left/Right to normalize selected nodes",
|
||||
"tooltip": "Enable Shift+Ctrl+Left to normalize width and Shift+Ctrl+Right to normalize height"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "Enable Alt+1~9 to paste node templates into the workflow"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "Enable Up/Down/Left/Right to jump to the nearest node"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "Enable automatic nesting of subdirectories in the context menu"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "Enable model preview thumbnails"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "Enable A~Z sorting of new nodes in the context menu"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "Use three quick buttons in the context menu",
|
||||
"options": {
|
||||
"At the forefront": "At the forefront",
|
||||
"At the end": "At the end",
|
||||
"Disable": "Disable"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "Enable node runtime display"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "Enable chaining of get and set points with the parent node"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "Manage nodes group sorting mode",
|
||||
"tooltip": "Automatically sort by default. If set to manual, groups can be drag and dropped and the order will be saved.",
|
||||
"options": {
|
||||
"Auto sorting": "Auto sorting",
|
||||
"Manual drag&drop sorting": "Manual drag&drop sorting"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "Enable node ID display"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "Show groups only"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "Enable Group Map",
|
||||
"tooltip": "You need to refresh the page to update successfully"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "Raccourcis",
|
||||
"Nodes": "Nœuds",
|
||||
"NodesMap": "Carte des nœuds"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "Utilitaire",
|
||||
"Seed": "Graine",
|
||||
"Prompt": "Prompt",
|
||||
"Loaders": "Chargeurs",
|
||||
"Adapter": "Adaptateur",
|
||||
"Inpaint": "Retouche",
|
||||
"PreSampling": "Pré-échantillonnage",
|
||||
"Sampler": "Échantillonneur",
|
||||
"Fix": "Correction",
|
||||
"Pipe": "Pipeline",
|
||||
"XY Inputs": "Entrées XY",
|
||||
"Image": "Image",
|
||||
"Segmentation": "Segmentation",
|
||||
"\uD83D\uDEAB Deprecated": "\uD83D\uDEAB Obsolète",
|
||||
"Type": "Type",
|
||||
"Math": "Mathématiques",
|
||||
"Switch": "Interrupteur",
|
||||
"Index Switch": "Interrupteur d'index",
|
||||
"While Loop": "Boucle While",
|
||||
"For Loop": "Boucle For",
|
||||
"LoadImage": "Charger l'image"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "Activer Shift+g pour ajouter les nœuds sélectionnés à un groupe",
|
||||
"tooltip": "Depuis la v1.2.39, vous pouvez utiliser Ctrl+g à la place"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "Activer Shift+r pour décharger le cache du modèle et des nœuds"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "Activer Shift+m pour basculer la carte des nœuds"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "Activer Shift+Up/Down/Left/Right et Shift+Ctrl+Alt+Left/Right pour aligner les nœuds sélectionnés",
|
||||
"tooltip": "Shift+Up/Down/Left/Right peut aligner les nœuds sélectionnés, Shift+Ctrl+Alt+Left/Right peut les répartir horizontalement/verticalement"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "Activer Shift+Ctrl+Left/Right pour normaliser les nœuds sélectionnés",
|
||||
"tooltip": "Activer Shift+Ctrl+Left pour normaliser la largeur et Shift+Ctrl+Right pour normaliser la hauteur"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "Activer Alt+1~9 pour coller les modèles de nœuds dans le workflow"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "Activer Up/Down/Left/Right pour passer au nœud le plus proche"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "Activer l'imbrication automatique des sous-répertoires dans le menu contextuel"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "Activer les vignettes d'aperçu du modèle"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "Activer le tri A~Z des nouveaux nœuds dans le menu contextuel"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "Utiliser trois boutons rapides dans le menu contextuel",
|
||||
"options": {
|
||||
"At the forefront": "À l'avant-plan",
|
||||
"At the end": "À la fin",
|
||||
"Disable": "Désactiver"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "Activer l'affichage du temps d'exécution des nœuds"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "Activer le chaînage des points get et set avec le nœud parent"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "Gérer le mode de tri des groupes de nœuds",
|
||||
"tooltip": "Tri automatique par défaut. Si défini sur manuel, les groupes peuvent être glissés-déposés et l'ordre sera sauvegardé.",
|
||||
"options": {
|
||||
"Auto sorting": "Tri automatique",
|
||||
"Manual drag&drop sorting": "Tri manuel par glisser-déposer"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "Activer l'affichage de l'ID du nœud"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "Afficher uniquement les groupes"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "Activer la carte des groupes",
|
||||
"tooltip": "Vous devez actualiser la page pour mettre à jour"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "ショートカットキー",
|
||||
"Nodes": "ノード",
|
||||
"NodesMap": "ノードマップ"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "ユーティリティ",
|
||||
"Seed": "シード",
|
||||
"Prompt": "プロンプト",
|
||||
"Loaders": "ローダー",
|
||||
"Adapter": "アダプター",
|
||||
"Inpaint": "インペイント",
|
||||
"PreSampling": "プリサンプリング",
|
||||
"Sampler": "サンプラー",
|
||||
"Fix": "フィックス",
|
||||
"Pipe": "パイプ",
|
||||
"XY Inputs": "XY入力",
|
||||
"Image": "画像",
|
||||
"Segmentation": "セグメンテーション",
|
||||
"\uD83D\uDEAB Deprecated": "🚫 非推奨",
|
||||
"Type": "タイプ",
|
||||
"Math": "数学",
|
||||
"Switch": "スイッチ",
|
||||
"Index Switch": "インデックススイッチ",
|
||||
"While Loop": "Whileループ",
|
||||
"For Loop": "Forループ",
|
||||
"LoadImage": "画像読み込み"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "Shift+gを使用して選択したノードをグループに追加する",
|
||||
"tooltip": "v1.2.39以降、Ctrl+gが使用できます"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "Shift+rを使用してモデルおよびノードキャッシュをアンロードする"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "Shift+mを使用してノードマップを表示/非表示にします"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "Shift+上/下/左/右およびShift+Ctrl+Alt+左/右を使用して選択したノードを整列する",
|
||||
"tooltip": "Shift+上/下/左/右で選択したノードを整列し、Shift+Ctrl+Alt+左/右で水平方向/垂直方向に分布させる"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "Shift+Ctrl+左/右を使用して選択したノードのサイズを正規化する",
|
||||
"tooltip": "Shift+Ctrl+左で幅を、Shift+Ctrl+右で高さを正規化する"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "Alt+1~9を使用してワークフローにノードテンプレートを貼り付ける"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "上/下/左/右を使用して最も近いノードにジャンプする"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "コンテキストメニューでサブディレクトリを自動でネストする"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "モデルプレビューサムネイルを有効にする"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "コンテキストメニューで新規ノードをA~Z順に並べ替える"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "コンテキストメニューで3つのクイックボタンを使用する",
|
||||
"options": {
|
||||
"At the forefront": "最前面に",
|
||||
"At the end": "最後に",
|
||||
"Disable": "無効"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "ノードの実行時間表示を有効にする"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "親ノードと取得/設定ポイントを連結することを有効にする"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "ノードグループの並べ替えモードを管理する",
|
||||
"tooltip": "デフォルトで自動的に並べ替えます。マニュアルに設定した場合、グループをドラッグアンドドロップで並べ替え、順序が保存されます。",
|
||||
"options": {
|
||||
"Auto sorting": "自動並べ替え",
|
||||
"Manual drag&drop sorting": "手動ドラッグアンドドロップによる並べ替え"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "ノードIDの表示を有効にする"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "グループのみ表示する"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "グループマップを有効にする",
|
||||
"tooltip": "ページを更新する必要があります"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "단축키",
|
||||
"Nodes": "노드",
|
||||
"NodesMap": "노드 맵"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "유틸",
|
||||
"Seed": "시드",
|
||||
"Prompt": "프롬프트",
|
||||
"Loaders": "로더",
|
||||
"Adapter": "어댑터",
|
||||
"Inpaint": "인페인트",
|
||||
"PreSampling": "사전 샘플링",
|
||||
"Sampler": "샘플러",
|
||||
"Fix": "픽스",
|
||||
"Pipe": "파이프",
|
||||
"XY Inputs": "XY 입력",
|
||||
"Image": "이미지",
|
||||
"Segmentation": "분할",
|
||||
"\uD83D\uDEAB Deprecated": "\uD83D\uDEAB 사용 중단",
|
||||
"Type": "유형",
|
||||
"Math": "수학",
|
||||
"Switch": "스위치",
|
||||
"Index Switch": "인덱스 스위치",
|
||||
"While Loop": "while 루프",
|
||||
"For Loop": "for 루프",
|
||||
"LoadImage": "이미지 로드"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "Shift+g 를 사용하여 선택된 노드를 그룹에 추가합니다",
|
||||
"tooltip": "v1.2.39부터는 Ctrl+g 를 사용할 수 있습니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "Shift+r 를 사용하여 모델 및 노드 캐시를 언로드합니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "Shift+m 를 사용하여 노드 맵을 전환합니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "Shift+Up/Down/Left/Right 와 Shift+Ctrl+Alt+Left/Right 를 사용하여 선택된 노드를 정렬합니다",
|
||||
"tooltip": "Shift+Up/Down/Left/Right 는 선택된 노드를 정렬하며, Shift+Ctrl+Alt+Left/Right 는 노드를 수평/수직으로 분배합니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "Shift+Ctrl+Left/Right 를 사용하여 선택된 노드를 정규화합니다",
|
||||
"tooltip": "Shift+Ctrl+Left 는 너비를, Shift+Ctrl+Right 는 높이를 정규화합니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "Alt+1~9 를 사용하여 워크플로우에 노드 템플릿을 붙여넣습니다"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "Up/Down/Left/Right 를 사용하여 가장 가까운 노드로 이동합니다"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "컨텍스트 메뉴에서 자동으로 하위 디렉토리를 중첩합니다"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "모델 미리보기 썸네일을 활성화합니다"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "컨텍스트 메뉴에서 새로운 노드를 A~Z 순으로 정렬합니다"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "컨텍스트 메뉴에 3개의 빠른 옵션 버튼을 사용합니다",
|
||||
"options": {
|
||||
"At the forefront": "앞쪽에",
|
||||
"At the end": "뒤쪽에",
|
||||
"Disable": "비활성화"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "노드 실행 시간 표시를 활성화합니다"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "부모 노드와 연결된 get/ set 포인트 체이닝을 활성화합니다"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "노드 그룹 정렬 모드를 관리합니다",
|
||||
"tooltip": "기본값은 자동 정렬입니다. 수동으로 설정하면 그룹을 드래그 앤 드롭할 수 있으며 순서가 저장됩니다.",
|
||||
"options": {
|
||||
"Auto sorting": "자동 정렬",
|
||||
"Manual drag&drop sorting": "수동 드래그 앤 드롭 정렬"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "노드 ID 표시를 활성화합니다"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "그룹만 표시합니다"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "그룹 맵을 활성화합니다",
|
||||
"tooltip": "업데이트를 위해 페이지를 새로고침해야 합니다"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "Горячие клавиши",
|
||||
"Nodes": "Узлы",
|
||||
"NodesMap": "Карта узлов"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "Утилиты",
|
||||
"Seed": "Сид",
|
||||
"Prompt": "Подсказка",
|
||||
"Loaders": "Загрузчики",
|
||||
"Adapter": "Адаптер",
|
||||
"Inpaint": "Ретушь",
|
||||
"PreSampling": "Предвыборка",
|
||||
"Sampler": "Сэмплер",
|
||||
"Fix": "Исправление",
|
||||
"Pipe": "Конвейер",
|
||||
"XY Inputs": "Ввод XY",
|
||||
"Image": "Изображение",
|
||||
"Segmentation": "Сегментация",
|
||||
"\uD83D\uDEAB Deprecated": "\uD83D\uDEAB Устарело",
|
||||
"Type": "Тип",
|
||||
"Math": "Математика",
|
||||
"Switch": "Переключатель",
|
||||
"Index Switch": "Переключатель индексов",
|
||||
"While Loop": "Цикл while",
|
||||
"For Loop": "Цикл for",
|
||||
"LoadImage": "Загрузка изображения"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "Включить Shift+g для добавления выделенных узлов в группу",
|
||||
"tooltip": "Начиная с версии v1.2.39, можно использовать Ctrl+g"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "Включить Shift+r для выгрузки модели и кэша узлов"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "Включить Shift+m для переключения карты узлов"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "Включить Shift+Стрелки для выравнивания выделенных узлов и Shift+Ctrl+Alt+Стрелки для распределения узлов по горизонтали/вертикали",
|
||||
"tooltip": "Shift+Стрелки выравнивают выделенные узлы, Shift+Ctrl+Alt+Стрелки распределяют узлы по горизонтали/вертикали"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "Включить Shift+Ctrl+Стрелки для нормализации выделенных узлов",
|
||||
"tooltip": "Включить Shift+Ctrl+Лево для нормализации ширины и Shift+Ctrl+Право для нормализации высоты"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "Включить Alt+1~9 для вставки шаблонов узлов в рабочий процесс"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "Включить Стрелки для перехода к ближайшему узлу"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "Включить автоматическое вложение подкаталогов в контекстном меню"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "Включить превью миниатюр моделей"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "Включить A~Z сортировку новых узлов в контекстном меню"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "Использовать три быстрых кнопки в контекстном меню",
|
||||
"options": {
|
||||
"At the forefront": "В начале",
|
||||
"At the end": "В конце",
|
||||
"Disable": "Отключено"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "Включить отображение времени выполнения узлов"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "Включить связывание точек получения и установки с родительским узлом"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "Управление режимом сортировки групп узлов",
|
||||
"tooltip": "По умолчанию автоматическая сортировка. При ручном режиме группы можно перемещать методом перетаскивания, и порядок будет сохранён.",
|
||||
"options": {
|
||||
"Auto sorting": "Автоматическая сортировка",
|
||||
"Manual drag&drop sorting": "Ручная сортировка перетаскиванием"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "Включить отображение ID узлов"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "Показывать только группы"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "Включить карту групп",
|
||||
"tooltip": "Необходимо обновить страницу для успешного обновления"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"settingsCategories": {
|
||||
"Hotkeys": "快捷键",
|
||||
"Nodes": "节点相关",
|
||||
"NodesMap": "管理节点组"
|
||||
},
|
||||
"nodeCategories": {
|
||||
"Util": "工具",
|
||||
"Seed": "随机种",
|
||||
|
||||
+75
-43
@@ -360,7 +360,7 @@
|
||||
"name": "VAE名称"
|
||||
},
|
||||
"clip_skip": {
|
||||
"name": "Clip停止层"
|
||||
"name": "CLIP停止层"
|
||||
},
|
||||
"lora_name": {
|
||||
"name": "LoRA名称"
|
||||
@@ -412,11 +412,20 @@
|
||||
"1": {
|
||||
"name": "模型"
|
||||
},
|
||||
"2": {
|
||||
"name": "VAE"
|
||||
},
|
||||
"3": {
|
||||
"name": "CLIP"
|
||||
},
|
||||
"4": {
|
||||
"name": "正面条件"
|
||||
},
|
||||
"5": {
|
||||
"name": "负面条件"
|
||||
},
|
||||
"6": {
|
||||
"name": "LATENT"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -436,7 +445,7 @@
|
||||
"name": "VAE"
|
||||
},
|
||||
"clip_skip": {
|
||||
"name": "Clip停止层"
|
||||
"name": "CLIP停止层"
|
||||
},
|
||||
"lora_name": {
|
||||
"name": "LoRA模型"
|
||||
@@ -475,6 +484,9 @@
|
||||
},
|
||||
"1": {
|
||||
"name": "模型"
|
||||
},
|
||||
"2": {
|
||||
"name": "VAE"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -494,7 +506,7 @@
|
||||
"name": "VAE"
|
||||
},
|
||||
"clip_skip": {
|
||||
"name": "Clip停止层"
|
||||
"name": "CLIP停止层"
|
||||
},
|
||||
"lora_name": {
|
||||
"name": "LoRA模型"
|
||||
@@ -1574,7 +1586,7 @@
|
||||
}
|
||||
},
|
||||
"easy latentNoisy": {
|
||||
"display_name": "噪波Latent(Sigma乘积)",
|
||||
"display_name": "噪波LATENT(Sigma乘积)",
|
||||
"inputs": {
|
||||
"pipe": {
|
||||
"name": "节点束"
|
||||
@@ -1583,7 +1595,7 @@
|
||||
"name": "模型(可选)"
|
||||
},
|
||||
"optional_latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
},
|
||||
"sample_name": {
|
||||
"name": "采样器"
|
||||
@@ -1618,7 +1630,7 @@
|
||||
"name": "节点束"
|
||||
},
|
||||
"1": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"2": {
|
||||
"name": "伽马值"
|
||||
@@ -1635,7 +1647,7 @@
|
||||
"name": "文字拼接"
|
||||
},
|
||||
"source_latent": {
|
||||
"name": "Latent(源)"
|
||||
"name": "LATENT(源)"
|
||||
},
|
||||
"source_mask": {
|
||||
"name": "遮罩(源)"
|
||||
@@ -1655,7 +1667,7 @@
|
||||
"name": "节点束"
|
||||
},
|
||||
"1": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"2": {
|
||||
"name": "条件"
|
||||
@@ -1672,7 +1684,7 @@
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"noise": {
|
||||
"name": "噪波"
|
||||
@@ -1695,7 +1707,7 @@
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1794,7 +1806,7 @@
|
||||
"name": "图像(可选)"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -1849,7 +1861,7 @@
|
||||
"name": "图像(可选)"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -1865,7 +1877,7 @@
|
||||
"name": "节点束"
|
||||
},
|
||||
"optional_latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
},
|
||||
"optional_noise_seed": {
|
||||
"name": "随机种(可选)"
|
||||
@@ -1914,7 +1926,7 @@
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"optional_sampler": {
|
||||
"name": "采样器(可选)"
|
||||
@@ -1993,7 +2005,7 @@
|
||||
"name": "图像(可选)"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -2232,7 +2244,7 @@
|
||||
"name": "图像(可选)"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -2260,7 +2272,7 @@
|
||||
"name": "图像(可选)"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
},
|
||||
"steps": {
|
||||
"name": "步数"
|
||||
@@ -2313,13 +2325,16 @@
|
||||
"name": "负面条件"
|
||||
},
|
||||
"5": {
|
||||
"name": "图像转Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"6": {
|
||||
"name": "Latent"
|
||||
"name": "VAE"
|
||||
},
|
||||
"7": {
|
||||
"name": "随机种"
|
||||
"name": "CLIP"
|
||||
},
|
||||
"8": {
|
||||
"name": "seed"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2594,7 +2609,7 @@
|
||||
"name": "负面条件(可选)"
|
||||
},
|
||||
"optional_latent": {
|
||||
"name": "Latent(可选)"
|
||||
"name": "LATENT(可选)"
|
||||
},
|
||||
"steps": {
|
||||
"name": "步数"
|
||||
@@ -2620,7 +2635,7 @@
|
||||
"name": "节点束"
|
||||
},
|
||||
"1": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2643,7 +2658,7 @@
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"image": {
|
||||
"name": "图像"
|
||||
@@ -2679,7 +2694,7 @@
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"5": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"6": {
|
||||
"name": "图像"
|
||||
@@ -2739,17 +2754,17 @@
|
||||
"neg": {
|
||||
"name": "负面条件"
|
||||
},
|
||||
"vae": {
|
||||
"VAE": {
|
||||
"name": "VAE"
|
||||
},
|
||||
"clip": {
|
||||
"CLIP": {
|
||||
"name": "CLIP"
|
||||
},
|
||||
"image_to_latent": {
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"image": {
|
||||
"name": "图像"
|
||||
@@ -2811,7 +2826,7 @@
|
||||
"name": "图像转Latent"
|
||||
},
|
||||
"9": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"10": {
|
||||
"name": "图像"
|
||||
@@ -3097,34 +3112,34 @@
|
||||
"name": "模型名称10"
|
||||
},
|
||||
"clip_skip_1": {
|
||||
"name": "Clip停止层1"
|
||||
"name": "CLIP停止层1"
|
||||
},
|
||||
"clip_skip_2": {
|
||||
"name": "Clip停止层2"
|
||||
"name": "CLIP停止层2"
|
||||
},
|
||||
"clip_skip_3": {
|
||||
"name": "Clip停止层3"
|
||||
"name": "CLIP停止层3"
|
||||
},
|
||||
"clip_skip_4": {
|
||||
"name": "Clip停止层4"
|
||||
"name": "CLIP停止层4"
|
||||
},
|
||||
"clip_skip_5": {
|
||||
"name": "Clip停止层5"
|
||||
"name": "CLIP停止层5"
|
||||
},
|
||||
"clip_skip_6": {
|
||||
"name": "Clip停止层6"
|
||||
"name": "CLIP停止层6"
|
||||
},
|
||||
"clip_skip_7": {
|
||||
"name": "Clip停止层7"
|
||||
"name": "CLIP停止层7"
|
||||
},
|
||||
"clip_skip_8": {
|
||||
"name": "Clip停止层8"
|
||||
"name": "CLIP停止层8"
|
||||
},
|
||||
"clip_skip_9": {
|
||||
"name": "Clip停止层9"
|
||||
"name": "CLIP停止层9"
|
||||
},
|
||||
"clip_skip_10": {
|
||||
"name": "Clip停止层10"
|
||||
"name": "CLIP停止层10"
|
||||
},
|
||||
"vae_name_1": {
|
||||
"name": "VAE名称1"
|
||||
@@ -3847,7 +3862,7 @@
|
||||
"name": "图像"
|
||||
},
|
||||
"2": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -4017,10 +4032,10 @@
|
||||
"model": {
|
||||
"name": "模型"
|
||||
},
|
||||
"clip": {
|
||||
"CLIP": {
|
||||
"name": "CLIP"
|
||||
},
|
||||
"vae": {
|
||||
"VAE": {
|
||||
"name": "VAE"
|
||||
},
|
||||
"positive": {
|
||||
@@ -4168,6 +4183,12 @@
|
||||
},
|
||||
"save_prefix": {
|
||||
"name": "保存前缀"
|
||||
},
|
||||
"add_background": {
|
||||
"name": "添加背景"
|
||||
},
|
||||
"refine_foreground": {
|
||||
"name": "优化前景"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -4922,7 +4943,7 @@
|
||||
"name": "模型"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"mode": {
|
||||
"name": "模式"
|
||||
@@ -5511,6 +5532,12 @@
|
||||
},
|
||||
"image_output": {
|
||||
"name": "图像输出"
|
||||
},
|
||||
"add_background": {
|
||||
"name": "添加背景"
|
||||
},
|
||||
"refine_foreground": {
|
||||
"name": "优化前景"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -5729,7 +5756,7 @@
|
||||
"name": "模型"
|
||||
},
|
||||
"latent": {
|
||||
"name": "Latent"
|
||||
"name": "LATENT"
|
||||
},
|
||||
"head": {
|
||||
"name": "head"
|
||||
@@ -6511,6 +6538,11 @@
|
||||
"anything": {
|
||||
"name": "输入任何"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "输出"
|
||||
}
|
||||
}
|
||||
},
|
||||
"easy showTensorShape": {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"EasyUse_Hotkeys_AddGroup": {
|
||||
"name": "启用 Shift+g 键将选中的节点添加一个组",
|
||||
"tooltip": "从v1.2.39开始,可以使用Ctrl+g代替"
|
||||
},
|
||||
"EasyUse_Hotkeys_cleanVRAMUsed": {
|
||||
"name": "启用 Shift+r 键卸载模型和节点缓存"
|
||||
},
|
||||
"EasyUse_Hotkeys_toggleNodesMap": {
|
||||
"name": "启用 Shift+m 键显隐管理节点组"
|
||||
},
|
||||
"EasyUse_Hotkeys_AlignSelectedNodes": {
|
||||
"name": "启用 Shift+上/下/左/右 和 Shift+Ctrl+Alt+左/右 键对齐选中的节点",
|
||||
"tooltip": "Shift+上/下/左/右 可以对齐选中的节点, Shift+Ctrl+Alt+左/右 可以水平/垂直分布节点"
|
||||
},
|
||||
"EasyUse_Hotkeys_NormalizeSelectedNodes": {
|
||||
"name": "启用 Shift+Ctrl+左/右 键规范化选中的节点",
|
||||
"tooltip": "启用 Shift+Ctrl+左 键规范化宽度和 Shift+Ctrl+右 键规范化高度"
|
||||
},
|
||||
"EasyUse_Hotkeys_NodesTemplate": {
|
||||
"name": "启用 Alt+1~9 从节点模板粘贴到工作流中"
|
||||
},
|
||||
"EasyUse_Hotkeys_JumpNearestNodes": {
|
||||
"name": "启用 上/下/左/右 键跳转到最近的前后节点"
|
||||
},
|
||||
"EasyUse_ContextMenu_SubDirectories": {
|
||||
"name": "启用上下文菜单自动嵌套子目录"
|
||||
},
|
||||
"EasyUse_ContextMenu_ModelsThumbnails": {
|
||||
"name": "启动模型预览图显示"
|
||||
},
|
||||
"EasyUse_ContextMenu_NodesSort": {
|
||||
"name": "启用右键菜单中新建节点A~Z排序"
|
||||
},
|
||||
"EasyUse_ContextMenu_QuickOptions": {
|
||||
"name": "在右键菜单中使用三个快捷按钮",
|
||||
"options": {
|
||||
"At the forefront": "在最前面",
|
||||
"At the end": "在最后面",
|
||||
"Disable": "禁用"
|
||||
}
|
||||
},
|
||||
"EasyUse_Nodes_Runtime": {
|
||||
"name": "启动节点运行时间显示"
|
||||
},
|
||||
"EasyUse_Nodes_ChainGetSet": {
|
||||
"name": "启用将获取点和设置点与父节点链在一起"
|
||||
},
|
||||
"EasyUse_NodesMap_Sorting": {
|
||||
"name": "管理节点组排序模式",
|
||||
"tooltip": "默认自动排序,如果设置为手动,组可以拖放并保存排序结果。",
|
||||
"options": {
|
||||
"Auto sorting": "自动排序",
|
||||
"Manual drag&drop sorting": "手动拖拽排序"
|
||||
}
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayNodeID": {
|
||||
"name": "启用节点ID显示"
|
||||
},
|
||||
"EasyUse_NodesMap_DisplayGroupOnly": {
|
||||
"name": "仅显示组"
|
||||
},
|
||||
"EasyUse_NodesMap_Enable": {
|
||||
"name": "启用管理节点组",
|
||||
"tooltip": "您需要刷新页面以成功更新"
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -195,7 +195,7 @@ REMBG_MODELS = {
|
||||
"model_url": "briaai/RMBG-2.0"
|
||||
},
|
||||
"BEN2": {
|
||||
"model_url": "https://huggingface.co/PramaLLC/BEN/resolve/main/BEN_Base.pth"
|
||||
"model_url": "https://huggingface.co/PramaLLC/BEN2/resolve/main/BEN2_Base.pth"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ IPADAPTER_CLIPVISION_MODELS = {
|
||||
"model_url": "https://huggingface.co/openai/clip-vit-large-patch14-336/resolve/main/pytorch_model.bin"
|
||||
},
|
||||
"clip-vit-h-14-laion2B-s32B-b79K":{
|
||||
"model_url": "https://huggingface.co/laion/CLIP-ViT-H-14-laion2B-s32B-b79K/resolve/main/open_clip_pytorch_model.safetensors"
|
||||
"model_url": "https://huggingface.co/laion/CLIP-ViT-H-14-laion2B-s32B-b79K/resolve/main/open_clip_model.safetensors"
|
||||
},
|
||||
"sigclip_vision_patch14_384":{
|
||||
"model_url": "https://huggingface.co/Comfy-Org/sigclip_vision_384/resolve/main/sigclip_vision_patch14_384.safetensors"
|
||||
@@ -350,7 +350,7 @@ DYNAMICRAFTER_MODELS = {
|
||||
"model_url": "https://huggingface.co/ExponentialML/DynamiCrafterUNet/resolve/main/dynamicrafter_unet_512.safetensors",
|
||||
"vae_url": "https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors",
|
||||
"clip_url": "https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/text_encoder/model.safetensors",
|
||||
"clip_vision_url": "https://huggingface.co/laion/CLIP-ViT-H-14-laion2B-s32B-b79K/resolve/main/open_clip_pytorch_model.safetensors",
|
||||
"clip_vision_url": "https://huggingface.co/laion/CLIP-ViT-H-14-laion2B-s32B-b79K/resolve/main/open_clip_model.safetensors",
|
||||
},
|
||||
"dynamicrafter_unet_512_interp (2.98GB)": {
|
||||
"model_url": "https://huggingface.co/ExponentialML/DynamiCrafterUNet/resolve/main/dynamicrafter_unet_512_interp.safetensors"
|
||||
@@ -395,4 +395,4 @@ PROMPT_TEMPLATE = {
|
||||
"nsfw": ["nude", "breast", "small breast", "middle breast", "large breast", "nipples", "clothes lift", "pussy juice trail", "pussy juice puddle", "small testicles", "medium testicles", "large testicles", "disembodied penis", "cum on body", "cum inside", "cum outside", "fingering", "handjob", "fellatio", "licking penis", "paizuri", "doggystyle", "cowgirl", "reversed cowgirl", "piledriver", "suspended congress", "full nelson",],
|
||||
}
|
||||
|
||||
NEW_SCHEDULERS = ['align_your_steps', 'gits']
|
||||
NEW_SCHEDULERS = ['align_your_steps', 'gits']
|
||||
|
||||
+7
-5
@@ -82,6 +82,7 @@ def compare_revision(num):
|
||||
if not comfy_ui_revision:
|
||||
comfy_ui_revision = get_comfyui_revision()
|
||||
return True if comfy_ui_revision == 'Unknown' or int(comfy_ui_revision) >= num else False
|
||||
|
||||
def find_tags(string: str, sep="/") -> list[str]:
|
||||
"""
|
||||
find tags from string use the sep for split
|
||||
@@ -217,14 +218,15 @@ def get_local_filepath(url, dirname, local_file_name=None):
|
||||
except Exception as e:
|
||||
use_mirror = True
|
||||
url = url.replace('huggingface.co', 'hf-mirror.com')
|
||||
print(f'无法从huggingface下载,正在尝试从 {url} 下载...')
|
||||
PromptServer.instance.send_sync("easyuse-toast", {'content': f'无法连接huggingface,正在尝试从 {url} 下载...', 'duration': 10000})
|
||||
print(f'Unable to download from huggingface, trying mirror: {url}')
|
||||
PromptServer.instance.send_sync("easyuse-toast", {'content': f'Unable to connect to huggingface, trying mirror: {url}', 'duration': 10000})
|
||||
try:
|
||||
download_url_to_file(url, destination)
|
||||
except Exception as err:
|
||||
error_msg = str(err.args[0]) if err.args else str(err)
|
||||
PromptServer.instance.send_sync("easyuse-toast",
|
||||
{'content': f'无法从 {url} 下载模型', 'type':'error'})
|
||||
raise Exception(f'无法从 {url} 下载,错误信息:{str(err.args[0])}')
|
||||
{'content': f'Unable to download model from {url}', 'type':'error'})
|
||||
raise Exception(f'Download failed. Original URL and mirror both failed.\nError: {error_msg}')
|
||||
return destination
|
||||
|
||||
def to_lora_patch_dict(state_dict: dict) -> dict:
|
||||
@@ -277,4 +279,4 @@ def getMetadata(filepath):
|
||||
def cleanGPUUsedForce():
|
||||
gc.collect()
|
||||
mm.unload_all_models()
|
||||
mm.soft_empty_cache()
|
||||
mm.soft_empty_cache()
|
||||
|
||||
@@ -34,11 +34,11 @@ def read_wildcard_dict(wildcard_path):
|
||||
key = os.path.splitext(rel_path)[0].replace('\\', '/').lower()
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding="ISO-8859-1") as f:
|
||||
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
lines = f.read().splitlines()
|
||||
easy_wildcard_dict[key] = lines
|
||||
except UnicodeDecodeError:
|
||||
with open(file_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
with open(file_path, 'r', encoding="ISO-8859-1") as f:
|
||||
lines = f.read().splitlines()
|
||||
easy_wildcard_dict[key] = lines
|
||||
elif file.endswith('.yaml'):
|
||||
|
||||
+445
-82
@@ -1,13 +1,36 @@
|
||||
import math
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
from einops import rearrange
|
||||
from PIL import Image, ImageFilter, ImageOps
|
||||
from timm.layers import DropPath, to_2tuple, trunc_normal_
|
||||
import torch.utils.checkpoint as checkpoint
|
||||
import numpy as np
|
||||
from timm.models.layers import DropPath, to_2tuple, trunc_normal_
|
||||
from PIL import Image, ImageOps
|
||||
from torchvision import transforms
|
||||
import numpy as np
|
||||
import random
|
||||
import cv2
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
import tempfile
|
||||
|
||||
|
||||
def set_random_seed(seed):
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
torch.cuda.manual_seed(seed)
|
||||
torch.cuda.manual_seed_all(seed)
|
||||
torch.backends.cudnn.deterministic = True
|
||||
torch.backends.cudnn.benchmark = False
|
||||
|
||||
|
||||
set_random_seed(9)
|
||||
|
||||
torch.set_float32_matmul_precision('highest')
|
||||
|
||||
|
||||
class Mlp(nn.Module):
|
||||
""" Multilayer perceptron."""
|
||||
@@ -247,6 +270,7 @@ class PatchMerging(nn.Module):
|
||||
dim (int): Number of input channels.
|
||||
norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm
|
||||
"""
|
||||
|
||||
def __init__(self, dim, norm_layer=nn.LayerNorm):
|
||||
super().__init__()
|
||||
self.dim = dim
|
||||
@@ -495,7 +519,8 @@ class SwinTransformer(nn.Module):
|
||||
patch_size = to_2tuple(patch_size)
|
||||
patches_resolution = [pretrain_img_size[0] // patch_size[0], pretrain_img_size[1] // patch_size[1]]
|
||||
|
||||
self.absolute_pos_embed = nn.Parameter(torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1]))
|
||||
self.absolute_pos_embed = nn.Parameter(
|
||||
torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1]))
|
||||
trunc_normal_(self.absolute_pos_embed, std=.02)
|
||||
|
||||
self.pos_drop = nn.Dropout(p=drop_rate)
|
||||
@@ -550,7 +575,6 @@ class SwinTransformer(nn.Module):
|
||||
for param in m.parameters():
|
||||
param.requires_grad = False
|
||||
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
x = self.patch_embed(x)
|
||||
@@ -559,18 +583,16 @@ class SwinTransformer(nn.Module):
|
||||
if self.ape:
|
||||
# interpolate the position embedding to the corresponding size
|
||||
absolute_pos_embed = F.interpolate(self.absolute_pos_embed, size=(Wh, Ww), mode='bicubic')
|
||||
x = (x + absolute_pos_embed) # B Wh*Ww C
|
||||
x = (x + absolute_pos_embed) # B Wh*Ww C
|
||||
|
||||
outs = [x.contiguous()]
|
||||
x = x.flatten(2).transpose(1, 2)
|
||||
x = self.pos_drop(x)
|
||||
|
||||
|
||||
for i in range(self.num_layers):
|
||||
layer = self.layers[i]
|
||||
x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww)
|
||||
|
||||
|
||||
if i in self.out_indices:
|
||||
norm_layer = getattr(self, f'norm{i}')
|
||||
x_out = norm_layer(x_out)
|
||||
@@ -578,16 +600,9 @@ class SwinTransformer(nn.Module):
|
||||
out = x_out.view(-1, H, W, self.num_features[i]).permute(0, 3, 1, 2).contiguous()
|
||||
outs.append(out)
|
||||
|
||||
|
||||
|
||||
return tuple(outs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_activation_fn(activation):
|
||||
"""Return an activation function given a string"""
|
||||
if activation == "gelu":
|
||||
@@ -622,6 +637,43 @@ def patches2image(x):
|
||||
"""(hg wg b) c h w -> b c (hg h) (wg w)"""
|
||||
x = rearrange(x, '(hg wg b) c h w -> b c (hg h) (wg w)', hg=2, wg=2)
|
||||
return x
|
||||
|
||||
|
||||
class PositionEmbeddingSine:
|
||||
def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
|
||||
super().__init__()
|
||||
self.num_pos_feats = num_pos_feats
|
||||
self.temperature = temperature
|
||||
self.normalize = normalize
|
||||
if scale is not None and normalize is False:
|
||||
raise ValueError("normalize should be True if scale is passed")
|
||||
if scale is None:
|
||||
scale = 2 * math.pi
|
||||
self.scale = scale
|
||||
self.dim_t = torch.arange(0, self.num_pos_feats, dtype=torch.float32)
|
||||
|
||||
def __call__(self, b, h, w):
|
||||
device = self.dim_t.device
|
||||
mask = torch.zeros([b, h, w], dtype=torch.bool, device=device)
|
||||
assert mask is not None
|
||||
not_mask = ~mask
|
||||
y_embed = not_mask.cumsum(dim=1, dtype=torch.float32)
|
||||
x_embed = not_mask.cumsum(dim=2, dtype=torch.float32)
|
||||
if self.normalize:
|
||||
eps = 1e-6
|
||||
y_embed = (y_embed - 0.5) / (y_embed[:, -1:, :] + eps) * self.scale
|
||||
x_embed = (x_embed - 0.5) / (x_embed[:, :, -1:] + eps) * self.scale
|
||||
|
||||
dim_t = self.temperature ** (2 * (self.dim_t.to(device) // 2) / self.num_pos_feats)
|
||||
pos_x = x_embed[:, :, :, None] / dim_t
|
||||
pos_y = y_embed[:, :, :, None] / dim_t
|
||||
|
||||
pos_x = torch.stack((pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4).flatten(3)
|
||||
pos_y = torch.stack((pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4).flatten(3)
|
||||
|
||||
return torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2)
|
||||
|
||||
|
||||
class PositionEmbeddingSine:
|
||||
def __init__(self, num_pos_feats=64, temperature=10000, normalize=False, scale=None):
|
||||
super().__init__()
|
||||
@@ -688,13 +740,15 @@ class MCLM(nn.Module):
|
||||
l: 4,c,h,w
|
||||
g: 1,c,h,w
|
||||
"""
|
||||
self.p_poses = []
|
||||
self.g_pos = None
|
||||
b, c, h, w = l.size()
|
||||
# 4,c,h,w -> 1,c,2h,2w
|
||||
concated_locs = rearrange(l, '(hg wg b) c h w -> b c (hg h) (wg w)', hg=2, wg=2)
|
||||
|
||||
pools = []
|
||||
for pool_ratio in self.pool_ratios:
|
||||
# b,c,h,w
|
||||
# b,c,h,w
|
||||
tgt_hw = (round(h / pool_ratio), round(w / pool_ratio))
|
||||
pool = F.adaptive_avg_pool2d(concated_locs, tgt_hw)
|
||||
pools.append(rearrange(pool, 'b c h w -> (h w) b c'))
|
||||
@@ -712,11 +766,9 @@ class MCLM(nn.Module):
|
||||
self.p_poses = self.p_poses.to(device)
|
||||
self.g_pos = self.g_pos.to(device)
|
||||
|
||||
|
||||
# attention between glb (q) & multisensory concated-locs (k,v)
|
||||
g_hw_b_c = rearrange(g, 'b c h w -> (h w) b c')
|
||||
|
||||
|
||||
g_hw_b_c = g_hw_b_c + self.dropout1(self.attention[0](g_hw_b_c + self.g_pos, pools + self.p_poses, pools)[0])
|
||||
g_hw_b_c = self.norm1(g_hw_b_c)
|
||||
g_hw_b_c = g_hw_b_c + self.dropout2(self.linear2(self.dropout(self.activation(self.linear1(g_hw_b_c)).clone())))
|
||||
@@ -740,13 +792,6 @@ class MCLM(nn.Module):
|
||||
return rearrange(l, "(h w) b c -> b c h w", h=h, w=w) ## (5,c,h*w)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class MCRM(nn.Module):
|
||||
def __init__(self, d_model, num_heads, pool_ratios=[4, 8, 16], h=None):
|
||||
super(MCRM, self).__init__()
|
||||
@@ -852,91 +897,390 @@ class BEN_Base(nn.Module):
|
||||
if isinstance(m, nn.GELU) or isinstance(m, nn.Dropout):
|
||||
m.inplace = True
|
||||
|
||||
@torch.inference_mode()
|
||||
@torch.autocast(device_type="cuda", dtype=torch.float16)
|
||||
def forward(self, x):
|
||||
device = x.device
|
||||
shallow = self.shallow(x)
|
||||
glb = rescale_to(x, scale_factor=0.5, interpolation='bilinear')
|
||||
loc = image2patches(x)
|
||||
input = torch.cat((loc, glb), dim=0)
|
||||
feature = self.backbone(input)
|
||||
e5 = self.output5(feature[4]) # (5,128,16,16)
|
||||
e4 = self.output4(feature[3]) # (5,128,32,32)
|
||||
e3 = self.output3(feature[2]) # (5,128,64,64)
|
||||
e2 = self.output2(feature[1]) # (5,128,128,128)
|
||||
e1 = self.output1(feature[0]) # (5,128,128,128)
|
||||
loc_e5, glb_e5 = e5.split([4, 1], dim=0)
|
||||
e5 = self.multifieldcrossatt(loc_e5, glb_e5) # (4,128,16,16)
|
||||
real_batch = x.size(0)
|
||||
|
||||
e4, tokenattmap4 = self.dec_blk4(e4 + resize_as(e5, e4))
|
||||
e4 = self.conv4(e4)
|
||||
e3, tokenattmap3 = self.dec_blk3(e3 + resize_as(e4, e3))
|
||||
e3 = self.conv3(e3)
|
||||
e2, tokenattmap2 = self.dec_blk2(e2 + resize_as(e3, e2))
|
||||
e2 = self.conv2(e2)
|
||||
e1, tokenattmap1 = self.dec_blk1(e1 + resize_as(e2, e1))
|
||||
e1 = self.conv1(e1)
|
||||
loc_e1, glb_e1 = e1.split([4, 1], dim=0)
|
||||
output1_cat = patches2image(loc_e1) # (1,128,256,256)
|
||||
output1_cat = output1_cat + resize_as(glb_e1, output1_cat)
|
||||
final_output = self.insmask_head(output1_cat) # (1,128,256,256)
|
||||
final_output = final_output + resize_as(shallow, final_output)
|
||||
final_output = self.upsample1(rescale_to(final_output))
|
||||
final_output = rescale_to(final_output + resize_as(shallow, final_output))
|
||||
final_output = self.upsample2(final_output)
|
||||
final_output = self.output(final_output)
|
||||
shallow_batch = self.shallow(x)
|
||||
glb_batch = rescale_to(x, scale_factor=0.5, interpolation='bilinear')
|
||||
|
||||
return final_output.sigmoid()
|
||||
final_input = None
|
||||
for i in range(real_batch):
|
||||
start = i * 4
|
||||
end = (i + 1) * 4
|
||||
loc_batch = image2patches(x[i, :, :, :].unsqueeze(dim=0))
|
||||
input_ = torch.cat((loc_batch, glb_batch[i, :, :, :].unsqueeze(dim=0)), dim=0)
|
||||
|
||||
@torch.no_grad()
|
||||
def inference(self,image):
|
||||
image, h, w,original_image = rgb_loader_refiner(image)
|
||||
if final_input == None:
|
||||
final_input = input_
|
||||
else:
|
||||
final_input = torch.cat((final_input, input_), dim=0)
|
||||
|
||||
img_tensor = img_transform(image).unsqueeze(0).to(next(self.parameters()).device)
|
||||
features = self.backbone(final_input)
|
||||
outputs = []
|
||||
|
||||
res = self.forward(img_tensor)
|
||||
for i in range(real_batch):
|
||||
start = i * 5
|
||||
end = (i + 1) * 5
|
||||
|
||||
pred_array = postprocess_image(res, im_size=[w, h])
|
||||
f4 = features[4][start:end, :, :, :] # shape: [5, C, H, W]
|
||||
f3 = features[3][start:end, :, :, :]
|
||||
f2 = features[2][start:end, :, :, :]
|
||||
f1 = features[1][start:end, :, :, :]
|
||||
f0 = features[0][start:end, :, :, :]
|
||||
e5 = self.output5(f4)
|
||||
e4 = self.output4(f3)
|
||||
e3 = self.output3(f2)
|
||||
e2 = self.output2(f1)
|
||||
e1 = self.output1(f0)
|
||||
loc_e5, glb_e5 = e5.split([4, 1], dim=0)
|
||||
e5 = self.multifieldcrossatt(loc_e5, glb_e5) # (4,128,16,16)
|
||||
|
||||
mask_image = Image.fromarray(pred_array, mode='L')
|
||||
e4, tokenattmap4 = self.dec_blk4(e4 + resize_as(e5, e4))
|
||||
e4 = self.conv4(e4)
|
||||
e3, tokenattmap3 = self.dec_blk3(e3 + resize_as(e4, e3))
|
||||
e3 = self.conv3(e3)
|
||||
e2, tokenattmap2 = self.dec_blk2(e2 + resize_as(e3, e2))
|
||||
e2 = self.conv2(e2)
|
||||
e1, tokenattmap1 = self.dec_blk1(e1 + resize_as(e2, e1))
|
||||
e1 = self.conv1(e1)
|
||||
|
||||
blurred_mask = mask_image.filter(ImageFilter.GaussianBlur(radius=1))
|
||||
loc_e1, glb_e1 = e1.split([4, 1], dim=0)
|
||||
|
||||
original_image_rgba = original_image.convert("RGBA")
|
||||
output1_cat = patches2image(loc_e1) # (1,128,256,256)
|
||||
|
||||
foreground = original_image_rgba.copy()
|
||||
# add glb feat in
|
||||
output1_cat = output1_cat + resize_as(glb_e1, output1_cat)
|
||||
# merge
|
||||
final_output = self.insmask_head(output1_cat) # (1,128,256,256)
|
||||
# shallow feature merge
|
||||
shallow = shallow_batch[i, :, :, :].unsqueeze(dim=0)
|
||||
final_output = final_output + resize_as(shallow, final_output)
|
||||
final_output = self.upsample1(rescale_to(final_output))
|
||||
final_output = rescale_to(final_output + resize_as(shallow, final_output))
|
||||
final_output = self.upsample2(final_output)
|
||||
final_output = self.output(final_output)
|
||||
mask = final_output.sigmoid()
|
||||
outputs.append(mask)
|
||||
|
||||
foreground.putalpha(blurred_mask)
|
||||
return torch.cat(outputs, dim=0)
|
||||
|
||||
return blurred_mask, foreground
|
||||
|
||||
def loadcheckpoints(self,model_path):
|
||||
def loadcheckpoints(self, model_path):
|
||||
model_dict = torch.load(model_path, map_location="cpu", weights_only=True)
|
||||
self.load_state_dict(model_dict['model_state_dict'], strict=True)
|
||||
del model_path
|
||||
|
||||
def inference(self, image, refine_foreground=False):
|
||||
|
||||
set_random_seed(9)
|
||||
# image = ImageOps.exif_transpose(image)
|
||||
if isinstance(image, Image.Image):
|
||||
image, h, w, original_image = rgb_loader_refiner(image)
|
||||
if torch.cuda.is_available():
|
||||
|
||||
img_tensor = img_transform(image).unsqueeze(0).to(next(self.parameters()).device)
|
||||
else:
|
||||
img_tensor = img_transform32(image).unsqueeze(0).to(next(self.parameters()).device)
|
||||
|
||||
with torch.no_grad():
|
||||
res = self.forward(img_tensor)
|
||||
|
||||
# Show Results
|
||||
if refine_foreground == True:
|
||||
|
||||
pred_pil = transforms.ToPILImage()(res.squeeze())
|
||||
image_masked = refine_foreground_process(original_image, pred_pil)
|
||||
mask = pred_pil.resize(original_image.size)
|
||||
image_masked.putalpha(mask)
|
||||
return mask, image_masked
|
||||
|
||||
else:
|
||||
alpha = postprocess_image(res, im_size=[w, h])
|
||||
pred_pil = transforms.ToPILImage()(alpha)
|
||||
mask = pred_pil.resize(original_image.size)
|
||||
original_image.putalpha(mask)
|
||||
# mask = Image.fromarray(alpha)
|
||||
|
||||
return mask, original_image
|
||||
|
||||
def segment_video(self, video_path, output_path="./", fps=0, refine_foreground=False, batch=1,
|
||||
print_frames_processed=True, webm=False, rgb_value=(0, 255, 0)):
|
||||
|
||||
"""
|
||||
Segments the given video to extract the foreground (with alpha) from each frame
|
||||
and saves the result as either a WebM video (with alpha channel) or MP4 (with a
|
||||
color background).
|
||||
Args:
|
||||
video_path (str):
|
||||
Path to the input video file.
|
||||
output_path (str, optional):
|
||||
Directory (or full path) where the output video and/or files will be saved.
|
||||
Defaults to "./".
|
||||
fps (int, optional):
|
||||
The frames per second (FPS) to use for the output video. If 0 (default), the
|
||||
original FPS of the input video is used. Otherwise, overrides it.
|
||||
refine_foreground (bool, optional):
|
||||
Whether to run an additional “refine foreground” process on each frame.
|
||||
Defaults to False.
|
||||
batch (int, optional):
|
||||
Number of frames to process at once (inference batch size). Large batch sizes
|
||||
may require more GPU memory. Defaults to 1.
|
||||
print_frames_processed (bool, optional):
|
||||
If True (default), prints progress (how many frames have been processed) to
|
||||
the console.
|
||||
webm (bool, optional):
|
||||
If True (default), exports a WebM video with alpha channel (VP9 / yuva420p).
|
||||
If False, exports an MP4 video composited over a solid color background.
|
||||
rgb_value (tuple, optional):
|
||||
The RGB background color (e.g., green screen) used to composite frames when
|
||||
saving to MP4. Defaults to (0, 255, 0).
|
||||
Returns:
|
||||
None. Writes the output video(s) to disk in the specified format.
|
||||
"""
|
||||
|
||||
cap = cv2.VideoCapture(video_path)
|
||||
if not cap.isOpened():
|
||||
raise IOError(f"Cannot open video: {video_path}")
|
||||
|
||||
original_fps = cap.get(cv2.CAP_PROP_FPS)
|
||||
original_fps = 30 if original_fps == 0 else original_fps
|
||||
fps = original_fps if fps == 0 else fps
|
||||
|
||||
ret, first_frame = cap.read()
|
||||
if not ret:
|
||||
raise ValueError("No frames found in the video.")
|
||||
height, width = first_frame.shape[:2]
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
|
||||
|
||||
foregrounds = []
|
||||
frame_idx = 0
|
||||
processed_count = 0
|
||||
batch_frames = []
|
||||
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||
|
||||
while True:
|
||||
ret, frame = cap.read()
|
||||
if not ret:
|
||||
if batch_frames:
|
||||
batch_results = self.inference(batch_frames, refine_foreground)
|
||||
if isinstance(batch_results, Image.Image):
|
||||
foregrounds.append(batch_results)
|
||||
else:
|
||||
foregrounds.extend(batch_results)
|
||||
if print_frames_processed:
|
||||
print(f"Processed frames {frame_idx - len(batch_frames) + 1} to {frame_idx} of {total_frames}")
|
||||
break
|
||||
|
||||
# Process every frame instead of using intervals
|
||||
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||
pil_frame = Image.fromarray(frame_rgb)
|
||||
batch_frames.append(pil_frame)
|
||||
|
||||
if len(batch_frames) == batch:
|
||||
batch_results = self.inference(batch_frames, refine_foreground)
|
||||
if isinstance(batch_results, Image.Image):
|
||||
foregrounds.append(batch_results)
|
||||
else:
|
||||
foregrounds.extend(batch_results)
|
||||
if print_frames_processed:
|
||||
print(f"Processed frames {frame_idx - batch + 1} to {frame_idx} of {total_frames}")
|
||||
batch_frames = []
|
||||
processed_count += batch
|
||||
|
||||
frame_idx += 1
|
||||
|
||||
if webm:
|
||||
alpha_webm_path = os.path.join(output_path, "foreground.webm")
|
||||
pil_images_to_webm_alpha(foregrounds, alpha_webm_path, fps=original_fps)
|
||||
|
||||
else:
|
||||
cap.release()
|
||||
fg_output = os.path.join(output_path, 'foreground.mp4')
|
||||
|
||||
pil_images_to_mp4(foregrounds, fg_output, fps=original_fps, rgb_value=rgb_value)
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
try:
|
||||
fg_audio_output = os.path.join(output_path, 'foreground_output_with_audio.mp4')
|
||||
add_audio_to_video(fg_output, video_path, fg_audio_output)
|
||||
except Exception as e:
|
||||
print("No audio found in the original video")
|
||||
print(e)
|
||||
|
||||
|
||||
def rgb_loader_refiner(original_image):
|
||||
h, w = original_image.size
|
||||
|
||||
def rgb_loader_refiner( original_image):
|
||||
h, w = original_image.size
|
||||
# # Apply EXIF orientation
|
||||
image = ImageOps.exif_transpose(original_image)
|
||||
# Convert to RGB if necessary
|
||||
if image.mode != 'RGB':
|
||||
image = image.convert('RGB')
|
||||
image = original_image
|
||||
# Convert to RGB if necessary
|
||||
if image.mode != 'RGB':
|
||||
image = image.convert('RGB')
|
||||
|
||||
# Resize the image
|
||||
image = image.resize((1024, 1024), resample=Image.LANCZOS)
|
||||
# Resize the image
|
||||
image = image.resize((1024, 1024), resample=Image.LANCZOS)
|
||||
|
||||
return image.convert('RGB'), h, w, original_image
|
||||
|
||||
return image.convert('RGB'), h, w,original_image
|
||||
|
||||
# Define the image transformation
|
||||
img_transform = transforms.Compose([
|
||||
transforms.ToTensor(),
|
||||
transforms.ConvertImageDtype(torch.float16),
|
||||
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
||||
])
|
||||
|
||||
img_transform32 = transforms.Compose([
|
||||
transforms.ToTensor(),
|
||||
transforms.ConvertImageDtype(torch.float32),
|
||||
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
|
||||
])
|
||||
|
||||
|
||||
def pil_images_to_mp4(images, output_path, fps=24, rgb_value=(0, 255, 0)):
|
||||
"""
|
||||
Converts an array of PIL images to an MP4 video.
|
||||
|
||||
Args:
|
||||
images: List of PIL images
|
||||
output_path: Path to save the MP4 file
|
||||
fps: Frames per second (default: 24)
|
||||
rgb_value: Background RGB color tuple (default: green (0, 255, 0))
|
||||
"""
|
||||
if not images:
|
||||
raise ValueError("No images provided to convert to MP4.")
|
||||
|
||||
width, height = images[0].size
|
||||
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
||||
video_writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
|
||||
|
||||
for image in images:
|
||||
# If image has alpha channel, composite onto the specified background color
|
||||
if image.mode == 'RGBA':
|
||||
# Create background image with specified RGB color
|
||||
background = Image.new('RGB', image.size, rgb_value)
|
||||
background = background.convert('RGBA')
|
||||
# Composite the image onto the background
|
||||
image = Image.alpha_composite(background, image)
|
||||
image = image.convert('RGB')
|
||||
else:
|
||||
# Ensure RGB format for non-alpha images
|
||||
image = image.convert('RGB')
|
||||
|
||||
# Convert to OpenCV format and write
|
||||
open_cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
||||
video_writer.write(open_cv_image)
|
||||
|
||||
video_writer.release()
|
||||
|
||||
|
||||
def pil_images_to_webm_alpha(images, output_path, fps=30):
|
||||
"""
|
||||
Converts a list of PIL RGBA images to a VP9 .webm video with alpha channel.
|
||||
NOTE: Not all players will display alpha in WebM.
|
||||
Browsers like Chrome/Firefox typically do support VP9 alpha.
|
||||
"""
|
||||
if not images:
|
||||
raise ValueError("No images provided for WebM with alpha.")
|
||||
|
||||
# Ensure output directory exists
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Save frames as PNG (with alpha)
|
||||
for idx, img in enumerate(images):
|
||||
if img.mode != "RGBA":
|
||||
img = img.convert("RGBA")
|
||||
out_path = os.path.join(tmpdir, f"{idx:06d}.png")
|
||||
img.save(out_path, "PNG")
|
||||
|
||||
# Construct ffmpeg command
|
||||
# -c:v libvpx-vp9 => VP9 encoder
|
||||
# -pix_fmt yuva420p => alpha-enabled pixel format
|
||||
# -auto-alt-ref 0 => helps preserve alpha frames (libvpx quirk)
|
||||
ffmpeg_cmd = [
|
||||
"ffmpeg", "-y",
|
||||
"-framerate", str(fps),
|
||||
"-i", os.path.join(tmpdir, "%06d.png"),
|
||||
"-c:v", "libvpx-vp9",
|
||||
"-pix_fmt", "yuva420p",
|
||||
"-auto-alt-ref", "0",
|
||||
output_path
|
||||
]
|
||||
|
||||
subprocess.run(ffmpeg_cmd, check=True)
|
||||
|
||||
print(f"WebM with alpha saved to {output_path}")
|
||||
|
||||
|
||||
def add_audio_to_video(video_without_audio_path, original_video_path, output_path):
|
||||
"""
|
||||
Check if the original video has an audio stream. If yes, add it. If not, skip.
|
||||
"""
|
||||
# 1) Probe original video for audio streams
|
||||
probe_command = [
|
||||
'ffprobe', '-v', 'error',
|
||||
'-select_streams', 'a:0',
|
||||
'-show_entries', 'stream=index',
|
||||
'-of', 'csv=p=0',
|
||||
original_video_path
|
||||
]
|
||||
result = subprocess.run(probe_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
# result.stdout is empty if no audio stream found
|
||||
if not result.stdout.strip():
|
||||
print("No audio track found in original video, skipping audio addition.")
|
||||
return
|
||||
|
||||
print("Audio track detected; proceeding to mux audio.")
|
||||
# 2) If audio found, run ffmpeg to add it
|
||||
command = [
|
||||
'ffmpeg', '-y',
|
||||
'-i', video_without_audio_path,
|
||||
'-i', original_video_path,
|
||||
'-c', 'copy',
|
||||
'-map', '0:v:0',
|
||||
'-map', '1:a:0', # we know there's an audio track now
|
||||
output_path
|
||||
]
|
||||
subprocess.run(command, check=True)
|
||||
print(f"Audio added successfully => {output_path}")
|
||||
|
||||
|
||||
### Thanks to the source: https://huggingface.co/ZhengPeng7/BiRefNet/blob/main/handler.py
|
||||
def refine_foreground_process(image, mask, r=90):
|
||||
if mask.size != image.size:
|
||||
mask = mask.resize(image.size)
|
||||
image = np.array(image) / 255.0
|
||||
mask = np.array(mask) / 255.0
|
||||
estimated_foreground = FB_blur_fusion_foreground_estimator_2(image, mask, r=r)
|
||||
image_masked = Image.fromarray((estimated_foreground * 255.0).astype(np.uint8))
|
||||
return image_masked
|
||||
|
||||
|
||||
def FB_blur_fusion_foreground_estimator_2(image, alpha, r=90):
|
||||
# Thanks to the source: https://github.com/Photoroom/fast-foreground-estimation
|
||||
alpha = alpha[:, :, None]
|
||||
F, blur_B = FB_blur_fusion_foreground_estimator(image, image, image, alpha, r)
|
||||
return FB_blur_fusion_foreground_estimator(image, F, blur_B, alpha, r=6)[0]
|
||||
|
||||
|
||||
def FB_blur_fusion_foreground_estimator(image, F, B, alpha, r=90):
|
||||
if isinstance(image, Image.Image):
|
||||
image = np.array(image) / 255.0
|
||||
blurred_alpha = cv2.blur(alpha, (r, r))[:, :, None]
|
||||
|
||||
blurred_FA = cv2.blur(F * alpha, (r, r))
|
||||
blurred_F = blurred_FA / (blurred_alpha + 1e-5)
|
||||
|
||||
blurred_B1A = cv2.blur(B * (1 - alpha), (r, r))
|
||||
blurred_B = blurred_B1A / ((1 - blurred_alpha) + 1e-5)
|
||||
F = blurred_F + alpha * \
|
||||
(image - alpha * blurred_F - (1 - alpha) * blurred_B)
|
||||
F = np.clip(F, 0, 1)
|
||||
return F, blurred_B
|
||||
|
||||
|
||||
def postprocess_image(result: torch.Tensor, im_size: list) -> np.ndarray:
|
||||
result = torch.squeeze(F.interpolate(result, size=im_size, mode='bilinear'), 0)
|
||||
ma = torch.max(result)
|
||||
@@ -944,4 +1288,23 @@ def postprocess_image(result: torch.Tensor, im_size: list) -> np.ndarray:
|
||||
result = (result - mi) / (ma - mi)
|
||||
im_array = (result * 255).permute(1, 2, 0).cpu().data.numpy().astype(np.uint8)
|
||||
im_array = np.squeeze(im_array)
|
||||
return im_array
|
||||
return im_array
|
||||
|
||||
|
||||
def rgb_loader_refiner(original_image):
|
||||
h, w = original_image.size
|
||||
# # Apply EXIF orientation
|
||||
|
||||
image = ImageOps.exif_transpose(original_image)
|
||||
|
||||
if original_image.mode != 'RGB':
|
||||
original_image = original_image.convert('RGB')
|
||||
|
||||
image = original_image
|
||||
# Convert to RGB if necessary
|
||||
|
||||
# Resize the image
|
||||
image = image.resize((1024, 1024), resample=Image.LANCZOS)
|
||||
|
||||
return image, h, w, original_image
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import numpy as np
|
||||
import os
|
||||
import json
|
||||
import torch
|
||||
import folder_paths
|
||||
import comfy
|
||||
import comfy.model_management
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
from nodes import ConditioningSetMask, RepeatLatentBatch
|
||||
from comfy_extras.nodes_mask import LatentCompositeMasked
|
||||
|
||||
+5
-4
@@ -828,7 +828,8 @@ class imageRemBg:
|
||||
},
|
||||
"optional":{
|
||||
"torchscript_jit": ("BOOLEAN", {"default": False}),
|
||||
"add_background": (["none", "white", "black"], {"default": "none"})
|
||||
"add_background": (["none", "white", "black"], {"default": "none"}),
|
||||
"refine_foreground": ("BOOLEAN", {"default": False}),
|
||||
},
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
@@ -841,7 +842,7 @@ class imageRemBg:
|
||||
CATEGORY = "EasyUse/Image"
|
||||
|
||||
|
||||
def remove(self, rem_mode, images, image_output, save_prefix, torchscript_jit=False, add_background='none',prompt=None, extra_pnginfo=None):
|
||||
def remove(self, rem_mode, images, image_output, save_prefix, torchscript_jit=False, add_background='none', refine_foreground=False, prompt=None, extra_pnginfo=None):
|
||||
new_images = list()
|
||||
masks = list()
|
||||
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
||||
@@ -941,7 +942,7 @@ class imageRemBg:
|
||||
if input_image.mode != 'RGBA':
|
||||
input_image = input_image.convert("RGBA")
|
||||
|
||||
mask, new_im = model.inference(input_image)
|
||||
mask, new_im = model.inference(input_image, refine_foreground)
|
||||
|
||||
new_im_tensor = pil2tensor(new_im)
|
||||
mask_tensor = pil2tensor(mask)
|
||||
@@ -1745,7 +1746,7 @@ class imageToBase64:
|
||||
pil_image = tensor2pil(image)
|
||||
|
||||
buffered = BytesIO()
|
||||
pil_image.save(buffered, format="JPEG")
|
||||
pil_image.save(buffered, format="PNG")
|
||||
image_bytes = buffered.getvalue()
|
||||
|
||||
base64_str = base64.b64encode(image_bytes).decode("utf-8")
|
||||
|
||||
+5
-2
@@ -28,7 +28,7 @@ class fullLoader:
|
||||
a1111_prompt_style_default = False
|
||||
|
||||
return {"required": {
|
||||
"ckpt_name": (folder_paths.get_filename_list("checkpoints"),),
|
||||
"ckpt_name": (folder_paths.get_filename_list("checkpoints") + ['None'],),
|
||||
"config_name": (["Default", ] + folder_paths.get_filename_list("configs"), {"default": "Default"}),
|
||||
"vae_name": (["Baked VAE"] + folder_paths.get_filename_list("vae"),),
|
||||
"clip_skip": ("INT", {"default": -2, "min": -24, "max": 0, "step": 1}),
|
||||
@@ -71,6 +71,9 @@ class fullLoader:
|
||||
my_unique_id=None
|
||||
):
|
||||
|
||||
if ckpt_name == 'None' and model_override is None:
|
||||
raise Exception("Please select a checkpoint or provide a model override.")
|
||||
|
||||
# Clean models from loaded_objects
|
||||
easyCache.update_loaded_objects(prompt)
|
||||
|
||||
@@ -923,7 +926,7 @@ class fluxLoader(fullLoader):
|
||||
loras = ["None"] + folder_paths.get_filename_list("loras")
|
||||
return {
|
||||
"required": {
|
||||
"ckpt_name": (checkpoints,),
|
||||
"ckpt_name": (checkpoints + ['None'],),
|
||||
"vae_name": (["Baked VAE"] + folder_paths.get_filename_list("vae"),),
|
||||
"lora_name": (loras,),
|
||||
"lora_model_strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
||||
|
||||
@@ -12,6 +12,14 @@ from .libs.utils import getMetadata, cleanGPUUsedForce, get_local_filepath
|
||||
from .libs.cache import remove_cache
|
||||
from .libs.translate import has_chinese, zh_to_en
|
||||
|
||||
@PromptServer.instance.routes.get('/easyuse/version')
|
||||
def get_version(request):
|
||||
try:
|
||||
from .. import __version__
|
||||
return web.json_response({"version": __version__})
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return web.Response(status=500)
|
||||
|
||||
@PromptServer.instance.routes.post("/easyuse/cleangpu")
|
||||
def cleanGPU(request):
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-easy-use"
|
||||
description = "To enhance the usability of ComfyUI, optimizations and integrations have been implemented for several commonly used nodes."
|
||||
version = "1.2.7"
|
||||
version = "1.2.8"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["diffusers", "accelerate", "clip_interrogator>=0.6.0", "sentencepiece", "lark", "onnxruntime", "spandrel", "opencv-python", "matplotlib", "peft"]
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 开发人员使用(请勿运行)
|
||||
# 将 https://github.com/AIGODLIKE/AIGODLIKE-ComfyUI-Translation 的翻译文件转换格式以适配 ComfyUI核心 locales
|
||||
# 将 https://github.com/AIGODLIKE/AIGODLIKE-ComfyUI-Translation 的翻译文件转换格式以适配 ComfyUI locales
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user