model2 commited on
Commit
e9b2bf3
Β·
1 Parent(s): 21d1ef5

perf. updates

Browse files
Files changed (27) hide show
  1. app.py +167 -105
  2. app/custom_node_manager.py +26 -1
  3. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/.gitignore +0 -0
  4. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/LICENSE +0 -0
  5. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/README.md +0 -0
  6. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/__init__.py +0 -0
  7. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/carve.py +0 -0
  8. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/conditioning.py +0 -0
  9. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/fonts/ShareTechMono-Regular.ttf +0 -0
  10. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/fonts/put_font_files_here.txt +0 -0
  11. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/histogram_matching.py +0 -0
  12. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/image.py +0 -0
  13. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/js/DisplayAny.js +0 -0
  14. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/js/FluxAttentionSeeker.js +0 -0
  15. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/luts/put_luts_files_here.txt +0 -0
  16. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/mask.py +0 -0
  17. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/misc.py +0 -0
  18. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/pyproject.toml +0 -0
  19. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/requirements.txt +0 -0
  20. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/sampling.py +0 -0
  21. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/segmentation.py +0 -0
  22. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/text.py +0 -0
  23. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/utils.py +0 -0
  24. custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/workflow_all_nodes.json +0 -0
  25. requirements.txt +2 -2
  26. tests-unit/utils/json_util_test.py +26 -1
  27. utils/json_util.py +0 -25
app.py CHANGED
@@ -1,18 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import sys
3
-
4
- sys.path.insert(0, os.path.dirname(__file__))
5
-
6
  from typing import Any, Mapping, Sequence, Union
7
 
8
  import gradio as gr
9
  import spaces
10
  import torch
11
- from huggingface_hub import snapshot_download, hf_hub_download
12
-
13
 
 
 
14
  from nodes import NODE_CLASS_MAPPINGS
15
 
 
16
  hf_hub_download(
17
  repo_id="uwg/upscaler",
18
  filename="ESRGAN/4x_NMKD-Siax_200k.pth",
@@ -28,12 +39,90 @@ hf_hub_download(
28
  filename="codeformer-v0.1.0.pth",
29
  local_dir="models/facerestore_models",
30
  )
31
- snapshot_download(
32
- repo_id="multimodalart/flux-style-shaping",
33
- repo_type="space", # It's a Space repository [oai_citation_attribution:3‑github.com](https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/_snapshot_download.py#:~:text=repo_type%20%28%60str%60%2C%20)
34
- local_dir=".", # Download to current working directory (Space runtime)
35
- allow_patterns="custom_nodes/ComfyUI-KJNodes/*", # Only files in this folder [oai_citation_attribution:4‑huggingface.co](https://huggingface.co/docs/huggingface_hub/en/guides/download#:~:text=,nlp%22%2C%20allow_patterns%3D%22%2A.json)
36
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  def get_value_at_index(obj: Union[Sequence, Mapping], index: int) -> Any:
39
  """Returns the value at the given index of a sequence or mapping.
@@ -99,8 +188,6 @@ def add_extra_model_paths() -> None:
99
  """
100
  Parse the optional extra_model_paths.yaml file and add the parsed paths to the sys.path.
101
  """
102
- from utils.extra_config import load_extra_path_config
103
-
104
  extra_model_paths = find_path("extra_model_paths.yaml")
105
 
106
  if extra_model_paths is not None:
@@ -112,36 +199,9 @@ def add_extra_model_paths() -> None:
112
  add_comfyui_directory_to_sys_path()
113
  add_extra_model_paths()
114
 
115
-
116
- def import_custom_nodes() -> None:
117
- """Find all custom nodes in the custom_nodes folder and add those node objects to NODE_CLASS_MAPPINGS
118
-
119
- This function sets up a new asyncio event loop, initializes the PromptServer,
120
- creates a PromptQueue, and initializes the custom nodes.
121
- """
122
- import asyncio
123
-
124
- import execution
125
- import server
126
- from nodes import init_extra_nodes
127
-
128
- # Creating a new event loop and setting it as the default loop
129
- loop = asyncio.new_event_loop()
130
- asyncio.set_event_loop(loop)
131
-
132
- # Creating an instance of PromptServer with the loop
133
- server_instance = server.PromptServer(loop)
134
- execution.PromptQueue(server_instance)
135
-
136
- # Initializing custom nodes
137
- init_extra_nodes()
138
-
139
-
140
- @spaces.GPU(duration=360)
141
  def advance_blur(input_image):
142
- import_custom_nodes()
143
  with torch.inference_mode():
144
- load_images_node = NODE_CLASS_MAPPINGS["LoadImagesFromFolderKJ"]()
145
  source_images_batch = load_images_node.load_images(
146
  folder="source_faces/",
147
  width=1024,
@@ -152,91 +212,91 @@ def advance_blur(input_image):
152
  include_subfolders=False,
153
  )
154
 
155
- loadimage = NODE_CLASS_MAPPINGS["LoadImage"]()
156
  loaded_input_image = loadimage.load_image(
157
  image=input_image,
158
  )
159
 
160
- upscalemodelloader = NODE_CLASS_MAPPINGS["UpscaleModelLoader"]()
161
- upscale_model = upscalemodelloader.load_model(
162
- model_name="ESRGAN/4x_NMKD-Siax_200k.pth"
 
 
 
163
  )
164
 
165
- reactorbuildfacemodel = NODE_CLASS_MAPPINGS["ReActorBuildFaceModel"]()
166
- imageresize = NODE_CLASS_MAPPINGS["ImageResize+"]()
167
- reactorfaceswap = NODE_CLASS_MAPPINGS["ReActorFaceSwap"]()
168
- imageupscalewithmodel = NODE_CLASS_MAPPINGS["ImageUpscaleWithModel"]()
169
- saveimage = NODE_CLASS_MAPPINGS["SaveImage"]()
170
-
171
- for q in range(1):
172
- face_model = reactorbuildfacemodel.blend_faces(
173
- save_mode=True,
174
- send_only=False,
175
- face_model_name="default",
176
- compute_method="Mean",
177
- images=get_value_at_index(source_images_batch, 0),
178
- )
179
-
180
- resized_input_image = imageresize.execute(
181
- width=2560,
182
- height=2560,
183
- interpolation="bicubic",
184
- method="keep proportion",
185
- condition="downscale if bigger",
186
- multiple_of=0,
187
- image=get_value_at_index(loaded_input_image, 0),
188
- )
189
 
190
- swapped_image = reactorfaceswap.execute(
191
- enabled=True,
192
- swap_model="inswapper_128.onnx",
193
- facedetection="retinaface_resnet50",
194
- face_restore_model="codeformer-v0.1.0.pth",
195
- face_restore_visibility=1,
196
- codeformer_weight=1,
197
- detect_gender_input="no",
198
- detect_gender_source="no",
199
- input_faces_index="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99",
200
- source_faces_index="0",
201
- console_log_level=2,
202
- input_image=get_value_at_index(resized_input_image, 0),
203
- face_model=get_value_at_index(face_model, 0),
204
- )
205
 
206
- upscaled_image = imageupscalewithmodel.upscale(
207
- upscale_model=get_value_at_index(upscale_model, 0),
208
- image=get_value_at_index(swapped_image, 0),
209
- )
210
 
211
- final_image = imageresize.execute(
212
- width=2560,
213
- height=2560,
214
- interpolation="lanczos",
215
- method="keep proportion",
216
- condition="downscale if bigger",
217
- multiple_of=0,
218
- image=get_value_at_index(upscaled_image, 0),
219
- )
220
 
221
- saved_image = saveimage.save_images(
222
- filename_prefix="advance_blur",
223
- images=get_value_at_index(final_image, 0),
224
- )
225
 
226
- saved_path = f"output/{saved_image['ui']['images'][0]['filename']}"
227
- return saved_path
228
 
229
 
230
  if __name__ == "__main__":
231
  # Start your Gradio app
232
  with gr.Blocks() as app:
233
  # Add a title
234
- gr.Markdown("# Advance Blur")
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
  with gr.Row():
237
  with gr.Column():
238
  input_image = gr.Image(label="Input Image", type="filepath")
239
- generate_btn = gr.Button("Generate")
240
 
241
  with gr.Column():
242
  # The output image
@@ -248,3 +308,5 @@ if __name__ == "__main__":
248
  fn=advance_blur, inputs=[input_image], outputs=[output_image]
249
  )
250
  app.launch(share=True)
 
 
 
1
+ # TODO: Replace face images with face model to stop creating it every time
2
+ # TODO: Figure out .cache error
3
+ # TODO: Figure out comfyui-frontend error
4
+ # TODO: Preload retinaface_resnet50
5
+ # TODO: Preload nsfw model
6
+ # TODO: Max out final image size as original image size
7
+ # TODO: Double-check inclusion of all necessary custom nodes in repo
8
+ # TODO: UI/UX: Better display on mobile so folks don't miss the final output
9
+ # TODO: Upgrade gradio
10
+
11
+ import logging
12
  import os
13
  import sys
 
 
 
14
  from typing import Any, Mapping, Sequence, Union
15
 
16
  import gradio as gr
17
  import spaces
18
  import torch
19
+ import yaml
20
+ from huggingface_hub import hf_hub_download
21
 
22
+ import folder_paths
23
+ from comfy import model_management
24
  from nodes import NODE_CLASS_MAPPINGS
25
 
26
+ # Load available models from HF
27
  hf_hub_download(
28
  repo_id="uwg/upscaler",
29
  filename="ESRGAN/4x_NMKD-Siax_200k.pth",
 
39
  filename="codeformer-v0.1.0.pth",
40
  local_dir="models/facerestore_models",
41
  )
42
+
43
+ # ReActor has its own special snowflake installation
44
+ os.system("cd custom_nodes/ComfyUI-ReActor && python install.py")
45
+
46
+ def import_custom_nodes() -> None:
47
+ """Find all custom nodes in the custom_nodes folder and add those node objects to NODE_CLASS_MAPPINGS
48
+
49
+ This function sets up a new asyncio event loop, initializes the PromptServer,
50
+ creates a PromptQueue, and initializes the custom nodes.
51
+ """
52
+ import asyncio
53
+
54
+ import execution
55
+ import server
56
+ from nodes import init_extra_nodes
57
+
58
+ # Creating a new event loop and setting it as the default loop
59
+ loop = asyncio.new_event_loop()
60
+ asyncio.set_event_loop(loop)
61
+
62
+ # Creating an instance of PromptServer with the loop
63
+ server_instance = server.PromptServer(loop)
64
+ execution.PromptQueue(server_instance)
65
+
66
+ # Initializing custom nodes
67
+ init_extra_nodes()
68
+
69
+
70
+ # Preload nodes, models.
71
+ import_custom_nodes()
72
+ load_images_node = NODE_CLASS_MAPPINGS["LoadImagesFromFolderKJ"]()
73
+ loadimage = NODE_CLASS_MAPPINGS["LoadImage"]()
74
+ upscalemodelloader = NODE_CLASS_MAPPINGS["UpscaleModelLoader"]()
75
+ reactorbuildfacemodel = NODE_CLASS_MAPPINGS["ReActorBuildFaceModel"]()
76
+ imageresize = NODE_CLASS_MAPPINGS["ImageResize+"]()
77
+ reactorfaceswap = NODE_CLASS_MAPPINGS["ReActorFaceSwap"]()
78
+ imageupscalewithmodel = NODE_CLASS_MAPPINGS["ImageUpscaleWithModel"]()
79
+ saveimage = NODE_CLASS_MAPPINGS["SaveImage"]()
80
+ upscale_model = upscalemodelloader.load_model(model_name="ESRGAN/4x_NMKD-Siax_200k.pth")
81
+ model_loaders = [
82
+ upscalemodelloader,
83
+ reactorfaceswap,
84
+ imageupscalewithmodel,
85
+ ]
86
+ valid_models = [
87
+ getattr(loader[0], "patcher", loader[0])
88
+ for loader in model_loaders
89
+ if not isinstance(getattr(loader[0], "patcher", None), dict)
90
+ ]
91
+ model_management.load_models_gpu(valid_models)
92
+
93
+
94
+ def load_extra_path_config(yaml_path):
95
+ with open(yaml_path, "r", encoding="utf-8") as stream:
96
+ config = yaml.safe_load(stream)
97
+ yaml_dir = os.path.dirname(os.path.abspath(yaml_path))
98
+ for c in config:
99
+ conf = config[c]
100
+ if conf is None:
101
+ continue
102
+ base_path = None
103
+ if "base_path" in conf:
104
+ base_path = conf.pop("base_path")
105
+ base_path = os.path.expandvars(os.path.expanduser(base_path))
106
+ if not os.path.isabs(base_path):
107
+ base_path = os.path.abspath(os.path.join(yaml_dir, base_path))
108
+ is_default = False
109
+ if "is_default" in conf:
110
+ is_default = conf.pop("is_default")
111
+ for x in conf:
112
+ for y in conf[x].split("\n"):
113
+ if len(y) == 0:
114
+ continue
115
+ full_path = y
116
+ if base_path:
117
+ full_path = os.path.join(base_path, full_path)
118
+ elif not os.path.isabs(full_path):
119
+ full_path = os.path.abspath(os.path.join(yaml_dir, y))
120
+ normalized_path = os.path.normpath(full_path)
121
+ logging.info(
122
+ "Adding extra search path {} {}".format(x, normalized_path)
123
+ )
124
+ folder_paths.add_model_folder_path(x, normalized_path, is_default)
125
+
126
 
127
  def get_value_at_index(obj: Union[Sequence, Mapping], index: int) -> Any:
128
  """Returns the value at the given index of a sequence or mapping.
 
188
  """
189
  Parse the optional extra_model_paths.yaml file and add the parsed paths to the sys.path.
190
  """
 
 
191
  extra_model_paths = find_path("extra_model_paths.yaml")
192
 
193
  if extra_model_paths is not None:
 
199
  add_comfyui_directory_to_sys_path()
200
  add_extra_model_paths()
201
 
202
+ @spaces.GPU(duration=60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  def advance_blur(input_image):
 
204
  with torch.inference_mode():
 
205
  source_images_batch = load_images_node.load_images(
206
  folder="source_faces/",
207
  width=1024,
 
212
  include_subfolders=False,
213
  )
214
 
 
215
  loaded_input_image = loadimage.load_image(
216
  image=input_image,
217
  )
218
 
219
+ face_model = reactorbuildfacemodel.blend_faces(
220
+ save_mode=True,
221
+ send_only=False,
222
+ face_model_name="default",
223
+ compute_method="Mean",
224
+ images=get_value_at_index(source_images_batch, 0),
225
  )
226
 
227
+ resized_input_image = imageresize.execute(
228
+ width=2560,
229
+ height=2560,
230
+ interpolation="bicubic",
231
+ method="keep proportion",
232
+ condition="downscale if bigger",
233
+ multiple_of=0,
234
+ image=get_value_at_index(loaded_input_image, 0),
235
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
+ swapped_image = reactorfaceswap.execute(
238
+ enabled=True,
239
+ swap_model="inswapper_128.onnx",
240
+ facedetection="retinaface_resnet50",
241
+ face_restore_model="codeformer-v0.1.0.pth",
242
+ face_restore_visibility=1,
243
+ codeformer_weight=1,
244
+ detect_gender_input="no",
245
+ detect_gender_source="no",
246
+ input_faces_index="0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99",
247
+ source_faces_index="0",
248
+ console_log_level=2,
249
+ input_image=get_value_at_index(resized_input_image, 0),
250
+ face_model=get_value_at_index(face_model, 0),
251
+ )
252
 
253
+ upscaled_image = imageupscalewithmodel.upscale(
254
+ upscale_model=get_value_at_index(upscale_model, 0),
255
+ image=get_value_at_index(swapped_image, 0),
256
+ )
257
 
258
+ final_image = imageresize.execute(
259
+ width=2560,
260
+ height=2560,
261
+ interpolation="lanczos",
262
+ method="keep proportion",
263
+ condition="downscale if bigger",
264
+ multiple_of=0,
265
+ image=get_value_at_index(upscaled_image, 0),
266
+ )
267
 
268
+ saved_image = saveimage.save_images(
269
+ filename_prefix="advance_blur",
270
+ images=get_value_at_index(final_image, 0),
271
+ )
272
 
273
+ saved_path = f"output/{saved_image['ui']['images'][0]['filename']}"
274
+ return saved_path
275
 
276
 
277
  if __name__ == "__main__":
278
  # Start your Gradio app
279
  with gr.Blocks() as app:
280
  # Add a title
281
+ gr.Markdown(
282
+ "# Advance Blur"
283
+ ""
284
+ 'Advance Blur uses a sophisticated technique called "Vance Blurring"'
285
+ " to anonymize images of people. This process also removes identifiable"
286
+ " metadata. Uploaded images and data are permanently deleted after processing."
287
+ ""
288
+ "Advance Blur works best when subjects face the camera. Any similarity to"
289
+ " persons, living or dead, is purely coincidental, comedic, karmic justice,"
290
+ " and/or parody."
291
+ ""
292
+ "_No sofas, couches, chaises, or other living-room furniture have been harmed in "
293
+ " the production of this application._"
294
+ )
295
 
296
  with gr.Row():
297
  with gr.Column():
298
  input_image = gr.Image(label="Input Image", type="filepath")
299
+ generate_btn = gr.Button("Submit")
300
 
301
  with gr.Column():
302
  # The output image
 
308
  fn=advance_blur, inputs=[input_image], outputs=[output_image]
309
  )
310
  app.launch(share=True)
311
+
312
+ gr.Markdown('#### Have you even said "Thank you"?')
app/custom_node_manager.py CHANGED
@@ -8,8 +8,33 @@ import json
8
  import logging
9
  from functools import lru_cache
10
 
11
- from utils.json_util import merge_json_recursive
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  # Extra locale files to load into main.json
15
  EXTRA_LOCALE_FILES = [
 
8
  import logging
9
  from functools import lru_cache
10
 
 
11
 
12
+ def merge_json_recursive(base, update):
13
+ """Recursively merge two JSON-like objects.
14
+ - Dictionaries are merged recursively
15
+ - Lists are concatenated
16
+ - Other types are overwritten by the update value
17
+
18
+ Args:
19
+ base: Base JSON-like object
20
+ update: Update JSON-like object to merge into base
21
+
22
+ Returns:
23
+ Merged JSON-like object
24
+ """
25
+ if not isinstance(base, dict) or not isinstance(update, dict):
26
+ if isinstance(base, list) and isinstance(update, list):
27
+ return base + update
28
+ return update
29
+
30
+ merged = base.copy()
31
+ for key, value in update.items():
32
+ if key in merged:
33
+ merged[key] = merge_json_recursive(merged[key], value)
34
+ else:
35
+ merged[key] = value
36
+
37
+ return merged
38
 
39
  # Extra locale files to load into main.json
40
  EXTRA_LOCALE_FILES = [
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/.gitignore RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/LICENSE RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/README.md RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/__init__.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/carve.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/conditioning.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/fonts/ShareTechMono-Regular.ttf RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/fonts/put_font_files_here.txt RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/histogram_matching.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/image.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/js/DisplayAny.js RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/js/FluxAttentionSeeker.js RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/luts/put_luts_files_here.txt RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/mask.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/misc.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/pyproject.toml RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/requirements.txt RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/sampling.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/segmentation.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/text.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/utils.py RENAMED
File without changes
custom_nodes/{ComfyUI-essentials-main β†’ ComfyUI-essentials}/workflow_all_nodes.json RENAMED
File without changes
requirements.txt CHANGED
@@ -3,7 +3,7 @@ albumentations>=1.4.16
3
  av
4
  color-matcher
5
  colour-science
6
- comfyui-frontend-package==1.14.5
7
  dill
8
  einops
9
  gradio
@@ -14,7 +14,7 @@ matplotlib
14
  mss
15
  numba
16
  numpy>=1.25.0
17
- onnx>=1.14.0
18
  opencv-python>=4.7.0.72
19
  piexif
20
  pillow>=10.3.0
 
3
  av
4
  color-matcher
5
  colour-science
6
+ comfyui-frontend-package
7
  dill
8
  einops
9
  gradio
 
14
  mss
15
  numba
16
  numpy>=1.25.0
17
+ onnx
18
  opencv-python>=4.7.0.72
19
  piexif
20
  pillow>=10.3.0
tests-unit/utils/json_util_test.py CHANGED
@@ -1,4 +1,29 @@
1
- from utils.json_util import merge_json_recursive
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
 
4
  def test_merge_simple_dicts():
 
1
+ def merge_json_recursive(base, update):
2
+ """Recursively merge two JSON-like objects.
3
+ - Dictionaries are merged recursively
4
+ - Lists are concatenated
5
+ - Other types are overwritten by the update value
6
+
7
+ Args:
8
+ base: Base JSON-like object
9
+ update: Update JSON-like object to merge into base
10
+
11
+ Returns:
12
+ Merged JSON-like object
13
+ """
14
+ if not isinstance(base, dict) or not isinstance(update, dict):
15
+ if isinstance(base, list) and isinstance(update, list):
16
+ return base + update
17
+ return update
18
+
19
+ merged = base.copy()
20
+ for key, value in update.items():
21
+ if key in merged:
22
+ merged[key] = merge_json_recursive(merged[key], value)
23
+ else:
24
+ merged[key] = value
25
+
26
+ return merged
27
 
28
 
29
  def test_merge_simple_dicts():
utils/json_util.py CHANGED
@@ -1,26 +1 @@
1
- def merge_json_recursive(base, update):
2
- """Recursively merge two JSON-like objects.
3
- - Dictionaries are merged recursively
4
- - Lists are concatenated
5
- - Other types are overwritten by the update value
6
 
7
- Args:
8
- base: Base JSON-like object
9
- update: Update JSON-like object to merge into base
10
-
11
- Returns:
12
- Merged JSON-like object
13
- """
14
- if not isinstance(base, dict) or not isinstance(update, dict):
15
- if isinstance(base, list) and isinstance(update, list):
16
- return base + update
17
- return update
18
-
19
- merged = base.copy()
20
- for key, value in update.items():
21
- if key in merged:
22
- merged[key] = merge_json_recursive(merged[key], value)
23
- else:
24
- merged[key] = value
25
-
26
- return merged
 
 
 
 
 
 
1