| | from __future__ import annotations |
| |
|
| | import numpy as np |
| | import torch |
| |
|
| | try: |
| | from scipy.ndimage import gaussian_filter as _scipy_gaussian_filter |
| | _HAVE_SCIPY = True |
| | except Exception: |
| | _HAVE_SCIPY = False |
| |
|
| |
|
| | def _torch_gaussian_blur(image: torch.Tensor, sigma: float) -> torch.Tensor: |
| | |
| | if sigma <= 0.0: |
| | return image |
| | device = image.device |
| | dtype = image.dtype |
| | radius = max(1, int(3.0 * float(sigma))) |
| | ksize = radius * 2 + 1 |
| | x = torch.arange(-radius, radius + 1, device=device, dtype=dtype) |
| | g1 = torch.exp(-(x * x) / (2.0 * (sigma ** 2))) |
| | g1 = (g1 / g1.sum()).view(1, 1, 1, -1) |
| | g2 = g1.transpose(2, 3) |
| | xch = image.movedim(-1, 1) |
| | pad = (radius, radius, radius, radius) |
| | out = torch.nn.functional.conv2d(torch.nn.functional.pad(xch, pad, mode="reflect"), g1.repeat(xch.shape[1], 1, 1, 1), groups=xch.shape[1]) |
| | out = torch.nn.functional.conv2d(torch.nn.functional.pad(out, pad, mode="reflect"), g2.repeat(out.shape[1], 1, 1, 1), groups=out.shape[1]) |
| | return out.movedim(1, -1) |
| |
|
| |
|
| | class IntelligentDetailStabilizer: |
| | """Alias-preserving move of IDS into mod/ as mg_ids.py. |
| | Keeps class/key name for backward compatibility. |
| | """ |
| |
|
| | @classmethod |
| | def INPUT_TYPES(cls): |
| | return { |
| | "required": { |
| | "image": ("IMAGE", {}), |
| | "ids_strength": ( |
| | "FLOAT", |
| | {"default": 0.5, "min": -1.0, "max": 1.0, "step": 0.01}, |
| | ), |
| | } |
| | } |
| |
|
| | RETURN_TYPES = ("IMAGE",) |
| | RETURN_NAMES = ("IMAGE",) |
| | FUNCTION = "stabilize" |
| | CATEGORY = "MagicNodes" |
| |
|
| | def stabilize(self, image: torch.Tensor, ids_strength: float = 0.5): |
| | sigma = max(float(ids_strength) * 2.0, 1e-3) |
| | if _HAVE_SCIPY: |
| | img_np = image.detach().cpu().numpy() |
| | denoised = _scipy_gaussian_filter(img_np, sigma=(0, sigma, sigma, 0)) |
| | blurred = _scipy_gaussian_filter(denoised, sigma=(0, 1.0, 1.0, 0)) |
| | sharpen = denoised + ids_strength * (denoised - blurred) |
| | sharpen = np.clip(sharpen, 0.0, 1.0) |
| | out = torch.from_numpy(sharpen).to(image.device, dtype=image.dtype) |
| | else: |
| | denoised = _torch_gaussian_blur(image, sigma=sigma) |
| | blurred = _torch_gaussian_blur(denoised, sigma=1.0) |
| | out = (denoised + ids_strength * (denoised - blurred)).clamp(0, 1) |
| | return (out,) |
| |
|