saliacoel commited on
Commit
05e623a
·
verified ·
1 Parent(s): 7c57c5d

Upload salia_detailer_ezpz.py

Browse files
Files changed (1) hide show
  1. salia_detailer_ezpz.py +115 -31
salia_detailer_ezpz.py CHANGED
@@ -4,7 +4,7 @@ from typing import Any, Dict, Tuple, Optional
4
 
5
  import torch
6
  import numpy as np
7
- from PIL import Image
8
 
9
  import folder_paths
10
 
@@ -149,45 +149,131 @@ def _load_controlnet_cached(control_net_name: str):
149
 
150
 
151
  # -------------------------------------------------------------------------------------
152
- # Asset dropdown support (from comfyui-salia_online assets/images)
153
- # (We still lazy-call the user's LoadImage_SaliaOnline_Assets for consistent mask behavior.)
154
  # -------------------------------------------------------------------------------------
155
 
156
- def _list_asset_pngs_fallback():
157
- # Fallback scanner (if utils import fails)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  try:
159
- from pathlib import Path
160
- plugin_root = Path(__file__).resolve().parent.parent
161
- img_dir = plugin_root / "assets" / "images"
162
- if not img_dir.exists():
163
- return []
164
- files = sorted([p.name for p in img_dir.glob("*.png")])
165
  return files
166
  except Exception:
167
  return []
168
 
169
 
170
- def _list_asset_pngs():
171
- try:
172
- # Prefer your plugin's own list function (same dropdown as your node)
173
- from ..utils.io import list_pngs # type: ignore
174
- return list_pngs() or []
175
- except Exception:
176
- return _list_asset_pngs_fallback()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
 
179
- def _load_asset_mask(asset_name: str):
180
  """
181
- Lazy-import and run your LoadImage_SaliaOnline_Assets node.
182
- Returns: MASK
 
 
 
183
  """
184
- # NOTE: Keep this lazy so importing the plugin doesn't force-load anything.
185
- from .salia_loadimage_assets import LoadImage_SaliaOnline_Assets # lazy-ish (light)
 
 
 
 
 
 
 
 
 
 
186
 
187
- loader = LoadImage_SaliaOnline_Assets()
188
- img, mask = loader.run(asset_name)
189
- return mask
 
 
 
190
 
 
 
 
 
 
 
 
191
 
192
  def _run_salia_depth(image: torch.Tensor, resolution: int) -> torch.Tensor:
193
  """
@@ -394,12 +480,12 @@ class Salia_Detailer_EZPZ:
394
  crop_up = _resize_image_lanczos(crop_rgb, up_w, up_h)
395
 
396
  # -------------------------
397
- # 4) Load asset mask (dropdown) and resize it to match upscaled resolution
398
  # -------------------------
399
  if asset_image == "<no pngs found>":
400
- raise FileNotFoundError("No PNGs found in comfyui-salia_online/assets/images")
401
 
402
- asset_mask = _load_asset_mask(asset_image) # MASK
403
  if asset_mask.ndim == 2:
404
  asset_mask = asset_mask.unsqueeze(0)
405
  if asset_mask.ndim != 3:
@@ -466,7 +552,6 @@ class Salia_Detailer_EZPZ:
466
  # -------------------------
467
  # 9) KSampler
468
  # -------------------------
469
- # No seed input requested: derive a stable seed from inputs so changing anything changes seed.
470
  seed_material = (
471
  f"{ckpt_name}|{control_net_name}|{asset_image}|{x}|{y}|{s}|{up}|"
472
  f"{steps}|{cfg}|{sampler_name}|{scheduler}|{denoise}|"
@@ -503,7 +588,6 @@ class Salia_Detailer_EZPZ:
503
  join = nodes.JoinImageWithAlpha()
504
  join_fn = getattr(join, join.FUNCTION)
505
 
506
- # Some Comfy versions name the mask input "alpha", others "mask".
507
  try:
508
  (rgba_up,) = join_fn(image=decoded_rgb, alpha=asset_mask_up)
509
  except TypeError:
 
4
 
5
  import torch
6
  import numpy as np
7
+ from PIL import Image, ImageOps
8
 
9
  import folder_paths
10
 
 
149
 
150
 
151
  # -------------------------------------------------------------------------------------
152
+ # Assets/images dropdown + loader (INLINED, no LoadImage_SaliaOnline_Assets dependency)
 
153
  # -------------------------------------------------------------------------------------
154
 
155
+ _ASSETS_DIR_CACHE: Optional["object"] = None
156
+ _ASSETS_DIR_LOCK = threading.Lock()
157
+
158
+
159
+ def _find_assets_images_dir():
160
+ """
161
+ Find the plugin's assets/images folder by walking upward from this file.
162
+ This is robust even if Comfy imports modules in weird ways.
163
+ """
164
+ from pathlib import Path
165
+
166
+ here = Path(__file__).resolve()
167
+ # check a few levels up; plugin root should be near
168
+ for parent in [here.parent] + list(here.parents)[:8]:
169
+ candidate = parent / "assets" / "images"
170
+ if candidate.is_dir():
171
+ return candidate
172
+ return None
173
+
174
+
175
+ def _assets_images_dir():
176
+ global _ASSETS_DIR_CACHE
177
+ with _ASSETS_DIR_LOCK:
178
+ if _ASSETS_DIR_CACHE is not None:
179
+ # If it was found once, reuse.
180
+ try:
181
+ if _ASSETS_DIR_CACHE.is_dir():
182
+ return _ASSETS_DIR_CACHE
183
+ except Exception:
184
+ pass
185
+
186
+ found = _find_assets_images_dir()
187
+ _ASSETS_DIR_CACHE = found
188
+ return found
189
+
190
+
191
+ def _list_asset_pngs():
192
+ """
193
+ List PNGs inside assets/images (recursive), returning paths relative to assets/images.
194
+ """
195
+ img_dir = _assets_images_dir()
196
+ if img_dir is None:
197
+ return []
198
+
199
+ files = []
200
  try:
201
+ for p in img_dir.rglob("*"):
202
+ if p.is_file() and p.suffix.lower() == ".png":
203
+ rel = p.relative_to(img_dir).as_posix()
204
+ files.append(rel)
205
+ files.sort()
 
206
  return files
207
  except Exception:
208
  return []
209
 
210
 
211
+ def _safe_asset_path(asset_rel_path: str):
212
+ """
213
+ Resolve a selected dropdown entry to an actual file path inside assets/images.
214
+ Prevents path traversal.
215
+ """
216
+ from pathlib import Path
217
+
218
+ img_dir = _assets_images_dir()
219
+ if img_dir is None:
220
+ raise FileNotFoundError("assets/images folder not found (could not locate plugin assets).")
221
+
222
+ base = img_dir.resolve()
223
+ rel = Path(asset_rel_path)
224
+
225
+ if rel.is_absolute():
226
+ raise ValueError("Absolute paths are not allowed for asset_image.")
227
+
228
+ # Resolve and verify containment
229
+ full = (base / rel).resolve()
230
+ if base != full and base not in full.parents:
231
+ raise ValueError(f"Invalid asset path (path traversal blocked): {asset_rel_path}")
232
+
233
+ if not full.is_file():
234
+ raise FileNotFoundError(f"Asset PNG not found in assets/images: {asset_rel_path}")
235
+
236
+ if full.suffix.lower() != ".png":
237
+ raise ValueError(f"Asset is not a PNG: {asset_rel_path}")
238
+
239
+ return full
240
 
241
 
242
+ def _load_asset_image_and_mask(asset_rel_path: str):
243
  """
244
+ Load PNG from assets/images and return (IMAGE, MASK) in ComfyUI formats.
245
+
246
+ IMPORTANT: Mask semantics match ComfyUI core LoadImage:
247
+ - If PNG has alpha: mask = 1 - alpha
248
+ - If no alpha: mask = 0 (opaque)
249
  """
250
+ p = _safe_asset_path(asset_rel_path)
251
+
252
+ im = Image.open(p)
253
+ im = ImageOps.exif_transpose(im)
254
+
255
+ # Ensure we can extract alpha if present
256
+ had_alpha = ("A" in im.getbands())
257
+ rgba = im.convert("RGBA")
258
+ rgb = rgba.convert("RGB")
259
+
260
+ rgb_arr = np.array(rgb).astype(np.float32) / 255.0 # [H,W,3]
261
+ img_t = torch.from_numpy(rgb_arr)[None, ...]
262
 
263
+ if had_alpha:
264
+ alpha = np.array(rgba.getchannel("A")).astype(np.float32) / 255.0 # [H,W], 1=opaque
265
+ mask = 1.0 - alpha # Comfy MASK convention
266
+ else:
267
+ h, w = rgb.size[1], rgb.size[0]
268
+ mask = np.zeros((h, w), dtype=np.float32)
269
 
270
+ mask_t = torch.from_numpy(mask)[None, ...]
271
+ return img_t, mask_t
272
+
273
+
274
+ # -------------------------------------------------------------------------------------
275
+ # Salia_Depth (still lazy import, unchanged)
276
+ # -------------------------------------------------------------------------------------
277
 
278
  def _run_salia_depth(image: torch.Tensor, resolution: int) -> torch.Tensor:
279
  """
 
480
  crop_up = _resize_image_lanczos(crop_rgb, up_w, up_h)
481
 
482
  # -------------------------
483
+ # 4) Load asset mask (INLINE assets loader) and resize to match upscaled resolution
484
  # -------------------------
485
  if asset_image == "<no pngs found>":
486
+ raise FileNotFoundError("No PNGs found in assets/images for this plugin.")
487
 
488
+ _asset_img_unused, asset_mask = _load_asset_image_and_mask(asset_image) # MASK is what we need
489
  if asset_mask.ndim == 2:
490
  asset_mask = asset_mask.unsqueeze(0)
491
  if asset_mask.ndim != 3:
 
552
  # -------------------------
553
  # 9) KSampler
554
  # -------------------------
 
555
  seed_material = (
556
  f"{ckpt_name}|{control_net_name}|{asset_image}|{x}|{y}|{s}|{up}|"
557
  f"{steps}|{cfg}|{sampler_name}|{scheduler}|{denoise}|"
 
588
  join = nodes.JoinImageWithAlpha()
589
  join_fn = getattr(join, join.FUNCTION)
590
 
 
591
  try:
592
  (rgba_up,) = join_fn(image=decoded_rgb, alpha=asset_mask_up)
593
  except TypeError: