From c48f33b5721fb0c284a65ebd1eac0c01be9d4c05 Mon Sep 17 00:00:00 2001 From: JPPhoto Date: Fri, 16 Oct 2026 14:05:06 -0500 Subject: [PATCH 1/2] feat: add built-in image manipulation nodes Add Clarity, Texture, and Contrast Dehaze approximation invocations with shared numerical primitives, schemas, tests, and docs. --- .github/workflows/python-tests.yml | 5 +- .../08.Workflows/image-adjustments.mdx | 30 ++ .../Guides/image-manipulation-provenance.mdx | 34 ++ .../app/invocations/image_manipulation.py | 72 ++++ invokeai/backend/image_util/clarity.py | 47 ++ invokeai/backend/image_util/color.py | 130 ++++++ .../backend/image_util/contrast_dehaze.py | 49 +++ invokeai/backend/image_util/guided_filter.py | 71 ++++ invokeai/backend/image_util/multiscale.py | 38 ++ invokeai/backend/image_util/texture.py | 61 +++ invokeai/frontend/api/openapi.json | 402 ++++++++++++++++++ invokeai/frontend/api/schema.ts | 175 +++++++- .../shared_image_manipulation_numerics.json | 64 +++ .../generation/core/colorConversion.ts | 48 +++ .../features/generation/core/guidedFilter.ts | 118 +++++ .../features/generation/core/multiscale.ts | 24 ++ .../sharedImageManipulationNumerics.test.ts | 105 +++++ .../test_image_manipulation_invocations.py | 256 +++++++++++ tests/backend/image_util/test_clarity.py | 175 ++++++++ .../backend/image_util/test_color_adapter.py | 265 ++++++++++++ .../image_util/test_contrast_dehaze.py | 143 +++++++ tests/backend/image_util/test_guided.py | 89 ++++ tests/backend/image_util/test_multiscale.py | 66 +++ .../test_shared_numerical_fixtures.py | 48 +++ tests/backend/image_util/test_texture.py | 183 ++++++++ 25 files changed, 2690 insertions(+), 8 deletions(-) create mode 100644 docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx create mode 100644 docs/src/content/docs/development/Guides/image-manipulation-provenance.mdx create mode 100644 invokeai/app/invocations/image_manipulation.py create mode 100644 invokeai/backend/image_util/clarity.py create mode 100644 invokeai/backend/image_util/color.py create mode 100644 invokeai/backend/image_util/contrast_dehaze.py create mode 100644 invokeai/backend/image_util/guided_filter.py create mode 100644 invokeai/backend/image_util/multiscale.py create mode 100644 invokeai/backend/image_util/texture.py create mode 100644 invokeai/frontend/webv2/src/features/generation/core/__fixtures__/shared_image_manipulation_numerics.json create mode 100644 invokeai/frontend/webv2/src/features/generation/core/colorConversion.ts create mode 100644 invokeai/frontend/webv2/src/features/generation/core/guidedFilter.ts create mode 100644 invokeai/frontend/webv2/src/features/generation/core/multiscale.ts create mode 100644 invokeai/frontend/webv2/src/features/generation/core/sharedImageManipulationNumerics.test.ts create mode 100644 tests/app/invocations/test_image_manipulation_invocations.py create mode 100644 tests/backend/image_util/test_clarity.py create mode 100644 tests/backend/image_util/test_color_adapter.py create mode 100644 tests/backend/image_util/test_contrast_dehaze.py create mode 100644 tests/backend/image_util/test_guided.py create mode 100644 tests/backend/image_util/test_multiscale.py create mode 100644 tests/backend/image_util/test_shared_numerical_fixtures.py create mode 100644 tests/backend/image_util/test_texture.py diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index a801dbd5a89..a551a453185 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -127,9 +127,8 @@ jobs: # Cross-stack generation contracts: the frontend writes these, Python validates them # against the invocation and architecture registries. A frontend-only change that # regenerates one would otherwise pass vitest while skipping the backend half. - # `__fixtures__/` does not exist yet -- it arrives with the graph-builder fixtures. - # A pattern matching nothing costs nothing here, and listing it now means the branch - # that adds the directory does not also have to remember to add this line. + # Shared Generation numerical fixtures are read by Python tests, so fixture-only changes + # must trigger this suite. - 'invokeai/frontend/webv2/src/features/generation/core/__snapshots__/**' - 'invokeai/frontend/webv2/src/features/generation/core/__fixtures__/**' - 'invokeai/frontend/webv2/src/features/video/core/__snapshots__/**' diff --git a/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx b/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx new file mode 100644 index 00000000000..0c07785772f --- /dev/null +++ b/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx @@ -0,0 +1,30 @@ +--- +title: Image Detail and Contrast Nodes +description: Use Clarity, Texture, and Contrast Dehaze in workflows, with their controls and image-format limits. +sidebar: + order: 6 +--- + +Clarity, Texture, and Contrast Dehaze are image nodes for making local detail or broad contrast adjustments in a workflow. Add one from the node picker and connect an image; each node outputs a processed image. + +## Choose a node + +| Node | What it adjusts | Controls | +| --- | --- | --- | +| **Clarity** | Midtone detail at a broader scale. Positive amounts emphasize local detail; negative amounts soften it. | **Amount:** −100 to 100, default 0. **Size:** 4 to 1000, default 40. | +| **Texture** | Fine-scale detail. Positive amounts emphasize texture; negative amounts reduce it. | **Amount:** −100 to 100, default 0. **Size:** 1 to 100, default 8. | +| **Contrast Dehaze (approximation)** | Broad luminance separation and color intensity. Positive amounts increase both; negative amounts reduce both. | **Amount:** −100 to 100, default 0. | + +For Clarity and Texture, **Size** is a scale in pixels of the input image. It controls the size of detail structures affected, not the output dimensions. Larger values affect broader structures; smaller values target finer detail. The amount is a signed strength control, not a percentage guarantee of a particular visual change. Results depend on the image. + +## Image formats and alpha + +These nodes accept 8-bit RGB, RGBA, grayscale (`L`), and grayscale with alpha (`LA`) images. They preserve the input dimensions and mode. When an image has alpha, the alpha channel is preserved. Fully transparent pixels retain their original hidden color values; nearby visible colors are used while processing so hidden colors do not introduce filtering edges. + +An **Amount** of `0` returns an unchanged copy of the input. This is useful when wiring a node into a workflow before tuning it. + +## Contrast Dehaze limits + +Contrast Dehaze is an aesthetic contrast and chroma adjustment. It does not estimate atmospheric haze, recover scene depth or lost detail, or model the physical causes of haze. It is not Dark Channel Prior (DCP) and does not claim parity with proprietary dehazing tools. Strong settings can clip tonal or color detail, so compare the output with the input and use a lower amount if needed. + +The implementation is first-party and built from documented image-processing equations and standard operations. No patent search or qualified patent clearance has been performed; see the [implementation provenance and licensing record](/development/guides/image-manipulation-provenance/) for details. diff --git a/docs/src/content/docs/development/Guides/image-manipulation-provenance.mdx b/docs/src/content/docs/development/Guides/image-manipulation-provenance.mdx new file mode 100644 index 00000000000..328f0f61a02 --- /dev/null +++ b/docs/src/content/docs/development/Guides/image-manipulation-provenance.mdx @@ -0,0 +1,34 @@ +--- +title: Image manipulation provenance +description: Sources, dependencies, licensing, and patent review status for the image manipulation nodes. +--- + +This record covers the image manipulation invocation and its shared numerical helpers. The table is the per-file provenance manifest; each row has the same origin, notice, and license status stated below. + +## Sources and implementation + +The image color-space conversion follows the published [Oklab model and reference equations](https://bottosson.github.io/posts/oklab/). +The scalar edge-aware smoothing follows the guided-filter formulation of He, Sun, and Tang, [Guided Image Filtering (ECCV 2010)](https://doi.org/10.1007/978-3-642-15549-9_1), using normalized box means and reflected boundaries. The sRGB transfer function follows the [CSS Color Module Level 4 definition](https://www.w3.org/TR/css-color-4/#predefined-sRGB). + +The clarity, texture, and contrast-dehaze controls are first-party image adjustments built from these documented equations and standard multiscale image-processing operations. Contrast Dehaze is an aesthetic approximation: it is not a physical haze estimator or scene-recovery method. The candidate contains no Dark Channel Prior (DCP) implementation or DCP control. + +The references below identify mathematical and specification foundations only. They are not code sources. + +| Implementation file | Purpose and algorithm/source basis | Libraries and owner | +| --- | --- | --- | +| `invokeai/app/invocations/image_manipulation.py` | InvokeAI node adapters for Clarity, Texture, and Contrast Dehaze; no external algorithm code. | InvokeAI invocation/image services and the three first-party backend helpers. | +| `invokeai/backend/image_util/clarity.py` | Midtone-gated, edge-aware multiscale detail adjustment; guided-filter formulation and Oklab lightness. | NumPy, Pillow; delegates to the `color.py` and `multiscale.py` helpers. | +| `invokeai/backend/image_util/color.py` | Oklab lightness conversion and image restoration; Oklab equations and sRGB transfer function. | NumPy, Pillow, PyTorch, OpenCV core APIs (`cv2`). | +| `invokeai/backend/image_util/contrast_dehaze.py` | First-party broad-scale contrast and chroma adjustment using guided-filter bands and Oklab. | NumPy, Pillow; delegates to the `color.py` and `multiscale.py` helpers. | +| `invokeai/backend/image_util/guided_filter.py` | Scalar guided image filter; He, Sun, and Tang's guided-filter formulation, implemented with normalized OpenCV box filters. | NumPy, OpenCV core APIs (`cv2`). | +| `invokeai/backend/image_util/multiscale.py` | Difference between two self-guided smooths; built on the guided-filter formulation. | NumPy; delegates to `guided_filter.py`. | +| `invokeai/backend/image_util/texture.py` | Fine-scale detail adjustment with a local RMS gate; guided-filter bands and Oklab lightness. | NumPy, Pillow, OpenCV core APIs (`cv2`); delegates to the `color.py` and `multiscale.py` helpers. | +| `invokeai/frontend/webv2/src/features/generation/core/colorConversion.ts` | sRGB-to-Oklab conversion; published Oklab equations and sRGB transfer function. | TypeScript and standard ECMAScript numeric arrays/math. | +| `invokeai/frontend/webv2/src/features/generation/core/guidedFilter.ts` | Scalar guided filter with reflected boundaries; He, Sun, and Tang's guided-filter formulation. | TypeScript and standard ECMAScript numeric arrays/math. | +| `invokeai/frontend/webv2/src/features/generation/core/multiscale.ts` | Difference between guided-filter smooths; guided-filter formulation. | TypeScript and standard ECMAScript numeric arrays/math; delegates to `guidedFilter.ts`. | + +For **every file in the table**, the implementation was authored for InvokeAI, with no individual author asserted. No code was copied or adapted from the cited works, their reference implementations, or other external code. Each file is licensed under `Apache-2.0` (SPDX identifier in the source); no third-party code or third-party notices are included in these files. The Python modules call OpenCV core APIs only. The project already declares the `opencv-contrib-python` distribution, but this feature uses no contrib-only API and adds no contrib requirement or other dependency. + +## License and patent status + +No patent search or qualified patent clearance has been performed for the files in this manifest; this record makes no representation that the described techniques are patent-free. None of the listed files implements or exposes Dark Channel Prior (DCP). Any future inclusion or distribution of DCP, or upstream submission containing DCP, is gated on qualified patent and provenance clearance, as required by the project checklist. diff --git a/invokeai/app/invocations/image_manipulation.py b/invokeai/app/invocations/image_manipulation.py new file mode 100644 index 00000000000..abefe1b9fca --- /dev/null +++ b/invokeai/app/invocations/image_manipulation.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: Apache-2.0 +"""First-party image manipulation invocations.""" + +from invokeai.app.invocations.baseinvocation import BaseInvocation, invocation +from invokeai.app.invocations.fields import ImageField, InputField, WithBoard, WithMetadata +from invokeai.app.invocations.primitives import ImageOutput +from invokeai.app.services.shared.invocation_context import InvocationContext +from invokeai.backend.image_util.clarity import clarity +from invokeai.backend.image_util.contrast_dehaze import contrast_dehaze +from invokeai.backend.image_util.texture import texture + + +@invocation( + "clarity", + title="Clarity", + tags=["image", "detail", "clarity", "enhance"], + category="image", + version="1.0.0", +) +class ClarityInvocation(BaseInvocation, WithMetadata, WithBoard): + """Adjust local midtone detail in an image.""" + + image: ImageField = InputField(description="The image to process") + amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the clarity adjustment") + size: float = InputField(default=40, ge=4, le=1000, description="Scale of the detail adjustment") + + def invoke(self, context: InvocationContext) -> ImageOutput: + image = context.images.get_pil(self.image.image_name) + result = clarity(image, self.amount, self.size) + image_dto = context.images.save(image=result) + return ImageOutput.build(image_dto) + + +@invocation( + "texture", + title="Texture", + tags=["image", "detail", "texture", "enhance"], + category="image", + version="1.0.0", +) +class TextureInvocation(BaseInvocation, WithMetadata, WithBoard): + """Adjust fine-scale texture detail in an image.""" + + image: ImageField = InputField(description="The image to process") + amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the texture adjustment") + size: float = InputField(default=8, ge=1, le=100, description="Scale of the texture detail") + + def invoke(self, context: InvocationContext) -> ImageOutput: + image = context.images.get_pil(self.image.image_name) + result = texture(image, self.amount, self.size) + image_dto = context.images.save(image=result) + return ImageOutput.build(image_dto) + + +@invocation( + "contrast_dehaze", + title="Contrast Dehaze (approximation)", + tags=["image", "contrast", "dehaze", "enhance"], + category="image", + version="1.0.0", +) +class ContrastDehazeInvocation(BaseInvocation, WithMetadata, WithBoard): + """Adjust broad-scale contrast and chroma as an aesthetic dehazing approximation.""" + + image: ImageField = InputField(description="The image to process") + amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the contrast and dehaze adjustment") + + def invoke(self, context: InvocationContext) -> ImageOutput: + image = context.images.get_pil(self.image.image_name) + result = contrast_dehaze(image, self.amount) + image_dto = context.images.save(image=result) + return ImageOutput.build(image_dto) diff --git a/invokeai/backend/image_util/clarity.py b/invokeai/backend/image_util/clarity.py new file mode 100644 index 00000000000..a6742d44a1e --- /dev/null +++ b/invokeai/backend/image_util/clarity.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Edge-aware midtone detail adjustment.""" + +from __future__ import annotations + +from math import isfinite +from numbers import Real + +import numpy as np +from PIL import Image + +from invokeai.backend.image_util.color import prepare_lightness, restore_lightness +from invokeai.backend.image_util.multiscale import edge_aware_band + +_MIN_AMOUNT = -100 +_MAX_AMOUNT = 100 +_MIN_SIZE = 4 +_MAX_SIZE = 1000 +_GUIDED_FILTER_EPSILON = 0.002 + + +def clarity(source: Image.Image, amount: float = 0, size: float = 40) -> Image.Image: + """Adjust local midtone detail while preserving the source image's color and alpha.""" + if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): + raise ValueError("amount must be a finite number between -100 and 100") + amount_value = float(amount) + if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: + raise ValueError("amount must be between -100 and 100") + + if isinstance(size, bool) or not isinstance(size, Real) or not isfinite(size): + raise ValueError("size must be a finite number between 4 and 1000") + size_value = float(size) + if not _MIN_SIZE <= size_value <= _MAX_SIZE: + raise ValueError("size must be between 4 and 1000") + + if amount_value == 0: + return source.copy() + + color_tensor, lightness, alpha = prepare_lightness(source) + inner_radius = max(1, round(size_value / 4)) + outer_radius = max(inner_radius + 1, round(size_value)) + detail = edge_aware_band(lightness, inner_radius, outer_radius, _GUIDED_FILTER_EPSILON) + clipped_lightness = np.clip(lightness, 0.0, 1.0) + midtone_gate = 4.0 * clipped_lightness * (1.0 - clipped_lightness) + adjusted_lightness = np.asarray(lightness + (amount_value / 100.0) * midtone_gate * detail, dtype=np.float32) + + return restore_lightness(source, color_tensor, adjusted_lightness, alpha) diff --git a/invokeai/backend/image_util/color.py b/invokeai/backend/image_util/color.py new file mode 100644 index 00000000000..3b247ed87dd --- /dev/null +++ b/invokeai/backend/image_util/color.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: Apache-2.0 +from __future__ import annotations + +import numpy as np +import torch +from PIL import Image + +from invokeai.backend.image_util.color_conversion import ( + gamut_clip_tensor, + linear_srgb_from_oklab, + oklab_from_srgb, + srgb_from_linear_srgb, +) + +_SUPPORTED_MODES = {"RGB", "RGBA", "L", "LA"} +_RESTORE_TILE_ROWS = 256 + + +def _check_image(image: Image.Image) -> None: + if not isinstance(image, Image.Image): + raise TypeError("image must be a Pillow Image") + if image.mode not in _SUPPORTED_MODES: + raise ValueError(f"Unsupported image mode: {image.mode}") + + +def prepare_lightness(image: Image.Image) -> tuple[torch.Tensor, np.ndarray, np.ndarray | None]: + """Convert an 8-bit Pillow image to Oklab, lightness, and optional alpha.""" + try: + _check_image(image) + width, height = image.size + rgb = np.asarray(image.convert("RGB"), dtype=np.uint8).copy() + alpha: np.ndarray | None = None + if image.mode in {"RGBA", "LA"}: + alpha = np.asarray(image.getchannel("A"), dtype=np.uint8).copy() + hidden = alpha == 0 + visible = ~hidden + if hidden.any(): + rgb[hidden] = 0 + if visible.any(): + import cv2 + + _, labels = cv2.distanceTransformWithLabels( + np.ascontiguousarray(hidden, dtype=np.uint8), + cv2.DIST_L2, + 5, + labelType=cv2.DIST_LABEL_PIXEL, + ) + visible_labels = labels[visible] + label_to_rgb = np.zeros((int(labels.max()) + 1, 3), dtype=np.uint8) + label_to_rgb[visible_labels] = rgb[visible] + rgb[hidden] = label_to_rgb[labels[hidden]] + + rgb_tensor = torch.from_numpy(rgb).permute(2, 0, 1).contiguous().to(dtype=torch.float32).div_(255.0) + oklab = oklab_from_srgb(rgb_tensor).contiguous() + lightness = np.ascontiguousarray(oklab[0].numpy(), dtype=np.float32) + if oklab.shape != (3, height, width): + raise RuntimeError("Color conversion returned an unexpected image shape") + return oklab, lightness, alpha + except MemoryError as exc: + raise RuntimeError("Not enough memory to prepare image color data") from exc + + +def restore_lightness( + image: Image.Image, + color_tensor: torch.Tensor, + lightness: np.ndarray, + alpha: np.ndarray | None, +) -> Image.Image: + """Replace Oklab lightness and restore the result in the image's original mode.""" + try: + _check_image(image) + width, height = image.size + if not isinstance(color_tensor, torch.Tensor) or color_tensor.shape != (3, height, width): + raise ValueError("color_tensor must have shape (3, H, W) matching the image") + if color_tensor.device.type != "cpu" or color_tensor.dtype != torch.float32: + raise ValueError("color_tensor must be a CPU float32 tensor") + if not isinstance(lightness, np.ndarray) or lightness.shape != (height, width) or lightness.dtype != np.float32: + raise ValueError("lightness must be an HxW float32 NumPy array matching the image") + + source_alpha: np.ndarray | None = None + if image.mode in {"RGBA", "LA"}: + if not isinstance(alpha, np.ndarray) or alpha.shape != (height, width) or alpha.dtype != np.uint8: + raise ValueError("alpha must be an HxW uint8 array matching the image") + source_alpha = np.ascontiguousarray(alpha) + elif alpha is not None: + raise ValueError("alpha must be None for images without an alpha channel") + + original = np.asarray(image, dtype=np.uint8) if source_alpha is not None else None + grayscale = image.mode in {"L", "LA"} + restored = np.empty((height, width) if grayscale else (height, width, 3), dtype=np.uint8) + for row_start in range(0, height, _RESTORE_TILE_ROWS): + row_end = min(row_start + _RESTORE_TILE_ROWS, height) + tile_color = color_tensor[:, row_start:row_end] + if not torch.isfinite(tile_color).all(): + raise ValueError("color_tensor must contain only finite values") + tile_lightness = lightness[row_start:row_end] + if not np.isfinite(tile_lightness).all(): + raise ValueError("lightness must contain only finite values") + + adjusted = tile_color.clone() + adjusted[0] = torch.from_numpy(np.ascontiguousarray(tile_lightness)).clamp(0.0, 1.0) + linear_rgb = linear_srgb_from_oklab(adjusted) + if not torch.isfinite(linear_rgb).all(): + raise ValueError("lightness produced non-finite RGB values") + if torch.any((linear_rgb < 0.0) | (linear_rgb > 1.0)): + linear_rgb = gamut_clip_tensor(linear_rgb) + srgb = srgb_from_linear_srgb(linear_rgb).clamp(0.0, 1.0) + if grayscale: + gray = srgb[0] * 0.299 + srgb[1] * 0.587 + srgb[2] * 0.114 + restored[row_start:row_end] = gray.mul(255.0).round().to(torch.uint8).numpy() + if source_alpha is not None and original is not None: + hidden = source_alpha[row_start:row_end] == 0 + restored[row_start:row_end][hidden] = original[row_start:row_end][hidden, 0] + else: + restored[row_start:row_end] = ( + srgb.mul(255.0).round().to(torch.uint8).permute(1, 2, 0).contiguous().numpy() + ) + if source_alpha is not None and original is not None: + hidden = source_alpha[row_start:row_end] == 0 + restored[row_start:row_end][hidden] = original[row_start:row_end][hidden, :3] + + if grayscale: + if source_alpha is not None: + return Image.fromarray(np.dstack((restored, source_alpha)), mode="LA") + return Image.fromarray(restored, mode="L") + if source_alpha is not None: + return Image.fromarray(np.dstack((restored, source_alpha)), mode="RGBA") + return Image.fromarray(restored, mode="RGB") + except MemoryError as exc: + raise RuntimeError("Not enough memory to restore image color data") from exc diff --git a/invokeai/backend/image_util/contrast_dehaze.py b/invokeai/backend/image_util/contrast_dehaze.py new file mode 100644 index 00000000000..ef5b4555618 --- /dev/null +++ b/invokeai/backend/image_util/contrast_dehaze.py @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Broad-scale contrast and chroma adjustment inspired by dehazed imagery.""" + +from __future__ import annotations + +from math import isfinite +from numbers import Real + +import numpy as np +from PIL import Image + +from invokeai.backend.image_util.color import prepare_lightness, restore_lightness +from invokeai.backend.image_util.multiscale import edge_aware_band + +_MIN_AMOUNT = -100 +_MAX_AMOUNT = 100 +_GUIDED_FILTER_EPSILON = 0.002 + + +def contrast_dehaze(source: Image.Image, amount: float = 0) -> Image.Image: + """Adjust broad-scale contrast and Oklab chroma as an aesthetic approximation. + + Positive amounts increase broad luminance separation and chroma; negative + amounts reduce both. This is a global image adjustment, not physical haze + estimation or scene recovery. + """ + if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): + raise ValueError("amount must be a finite number between -100 and 100") + amount_value = float(amount) + if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: + raise ValueError("amount must be between -100 and 100") + + if amount_value == 0: + return source.copy() + + try: + color_tensor, lightness, alpha = prepare_lightness(source) + width, height = source.size + radius = max(2, min(1000, round(min(width, height) / 8))) + broad_scale = edge_aware_band(lightness, 0, radius, _GUIDED_FILTER_EPSILON) + + strength = amount_value / 100.0 + contrast = 1.0 + 0.2 * strength + adjusted_lightness = np.asarray(0.5 + contrast * (lightness - 0.5) + strength * broad_scale, dtype=np.float32) + color_tensor[1:] *= 1.0 + 0.2 * strength + + return restore_lightness(source, color_tensor, adjusted_lightness, alpha) + except MemoryError as exc: + raise RuntimeError("Not enough memory to adjust image contrast") from exc diff --git a/invokeai/backend/image_util/guided_filter.py b/invokeai/backend/image_util/guided_filter.py new file mode 100644 index 00000000000..a3385aa8246 --- /dev/null +++ b/invokeai/backend/image_util/guided_filter.py @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Scalar guided image filtering using normalized OpenCV box filters.""" + +from __future__ import annotations + +from numbers import Integral + +import cv2 +import numpy as np +import numpy.typing as npt + + +def guided_filter( + guide: npt.ArrayLike, + signal: npt.ArrayLike, + radius: int, + epsilon: float, +) -> npt.NDArray[np.float32]: + """Filter a 2D signal while preserving edges present in a 2D guide. + + Local means use OpenCV box filters with ``BORDER_REFLECT_101``. This + reflects samples around the edge without repeating the edge pixel itself. + The implementation uses a fixed number of array operations and box filters, + so its work is O(width × height), independent of the filter radius. + """ + if isinstance(radius, bool) or not isinstance(radius, Integral) or radius < 0: + raise ValueError("radius must be a nonnegative integer") + if not np.isscalar(epsilon): + raise ValueError("epsilon must be finite and positive") + try: + epsilon_value = float(epsilon) + except (TypeError, ValueError, OverflowError) as exc: + raise ValueError("epsilon must be finite and positive") from exc + if not np.isfinite(epsilon_value) or epsilon_value <= 0: + raise ValueError("epsilon must be finite and positive") + + guide_array = np.asarray(guide, dtype=np.float32) + signal_array = np.asarray(signal, dtype=np.float32) + if guide_array.ndim != 2 or signal_array.ndim != 2: + raise ValueError("guide and signal must be 2D arrays") + if guide_array.shape != signal_array.shape: + raise ValueError("guide and signal must have equal shapes") + + if radius == 0: + return signal_array.copy() + + kernel_size = 2 * int(radius) + 1 + + def box_mean(array: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]: + return cv2.boxFilter( + array, + ddepth=cv2.CV_32F, + ksize=(kernel_size, kernel_size), + normalize=True, + borderType=cv2.BORDER_REFLECT_101, + ) + + mean_guide = box_mean(guide_array) + mean_signal = box_mean(signal_array) + mean_guide_squared = box_mean(guide_array * guide_array) + mean_guide_signal = box_mean(guide_array * signal_array) + + variance = mean_guide_squared - mean_guide * mean_guide + np.maximum(variance, 0, out=variance) + covariance = mean_guide_signal - mean_guide * mean_signal + coefficient_a = covariance / (variance + epsilon_value) + coefficient_b = mean_signal - coefficient_a * mean_guide + + mean_a = box_mean(coefficient_a) + mean_b = box_mean(coefficient_b) + return np.asarray(mean_a * guide_array + mean_b, dtype=np.float32) diff --git a/invokeai/backend/image_util/multiscale.py b/invokeai/backend/image_util/multiscale.py new file mode 100644 index 00000000000..2de4f8174ef --- /dev/null +++ b/invokeai/backend/image_util/multiscale.py @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Edge-aware difference-of-smooths image bands.""" + +from __future__ import annotations + +from numbers import Integral + +import numpy as np +import numpy.typing as npt + +from invokeai.backend.image_util.guided_filter import guided_filter + + +def edge_aware_band( + lightness: npt.ArrayLike, + inner_radius: int, + outer_radius: int, + epsilon: float, +) -> npt.NDArray[np.float32]: + """Return detail between two self-guided smooths of a 2D lightness image. + + The band is the inner-radius smooth minus the outer-radius smooth. The + inner radius may be zero, in which case the fine smooth is the input. + """ + if ( + isinstance(inner_radius, bool) + or not isinstance(inner_radius, Integral) + or isinstance(outer_radius, bool) + or not isinstance(outer_radius, Integral) + or inner_radius < 0 + or outer_radius <= inner_radius + ): + raise ValueError("radii must satisfy 0 <= inner_radius < outer_radius") + + image = np.asarray(lightness, dtype=np.float32) + fine = guided_filter(image, image, int(inner_radius), epsilon) + coarse = guided_filter(image, image, int(outer_radius), epsilon) + return np.asarray(fine - coarse, dtype=np.float32) diff --git a/invokeai/backend/image_util/texture.py b/invokeai/backend/image_util/texture.py new file mode 100644 index 00000000000..dfb57f0d933 --- /dev/null +++ b/invokeai/backend/image_util/texture.py @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Edge-aware fine texture adjustment.""" + +from __future__ import annotations + +from math import isfinite +from numbers import Real + +import cv2 +import numpy as np +from PIL import Image + +from invokeai.backend.image_util.color import prepare_lightness, restore_lightness +from invokeai.backend.image_util.multiscale import edge_aware_band + +_MIN_AMOUNT = -100 +_MAX_AMOUNT = 100 +_MIN_SIZE = 1 +_MAX_SIZE = 100 +_GUIDED_FILTER_EPSILON = 0.0008 + + +def texture(source: Image.Image, amount: float = 0, size: float = 8) -> Image.Image: + """Adjust fine-scale detail while preserving the source image's color and alpha.""" + if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): + raise ValueError("amount must be a finite number between -100 and 100") + amount_value = float(amount) + if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: + raise ValueError("amount must be between -100 and 100") + + if isinstance(size, bool) or not isinstance(size, Real) or not isfinite(size): + raise ValueError("size must be a finite number between 1 and 100") + size_value = float(size) + if not _MIN_SIZE <= size_value <= _MAX_SIZE: + raise ValueError("size must be between 1 and 100") + + if amount_value == 0: + return source.copy() + + color_tensor, lightness, alpha = prepare_lightness(source) + inner_radius = max(1, round(size_value / 4)) + outer_radius = max(inner_radius + 1, round(size_value)) + detail = edge_aware_band(lightness, inner_radius, outer_radius, _GUIDED_FILTER_EPSILON) + + if amount_value > 0: + rms_radius = max(2, round(size_value / 3)) + kernel_size = 2 * rms_radius + 1 + mean_square = cv2.boxFilter( + detail * detail, + ddepth=cv2.CV_32F, + ksize=(kernel_size, kernel_size), + normalize=True, + borderType=cv2.BORDER_REFLECT_101, + ) + rms = np.sqrt(np.maximum(mean_square, 0.0)) + weight = np.clip((rms - 0.002) / (0.010 - 0.002), 0.0, 1.0) + gate = weight * weight * (3.0 - 2.0 * weight) + detail = detail * gate + + adjusted_lightness = np.asarray(lightness + (amount_value / 100.0) * detail, dtype=np.float32) + return restore_lightness(source, color_tensor, adjusted_lightness, alpha) diff --git a/invokeai/frontend/api/openapi.json b/invokeai/frontend/api/openapi.json index 311280d66b8..d5e52349720 100644 --- a/invokeai/frontend/api/openapi.json +++ b/invokeai/frontend/api/openapi.json @@ -25252,6 +25252,125 @@ "$ref": "#/components/schemas/ImageOutput" } }, + "ClarityInvocation": { + "category": "image", + "class": "invocation", + "classification": "stable", + "description": "Adjust local midtone detail in an image.", + "node_pack": "invokeai", + "properties": { + "board": { + "anyOf": [ + { + "$ref": "#/components/schemas/BoardField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The board to save the image to", + "field_kind": "internal", + "input": "direct", + "orig_required": false, + "ui_hidden": false + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional metadata to be saved with the image", + "field_kind": "internal", + "input": "connection", + "orig_required": false, + "ui_hidden": false + }, + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "image": { + "anyOf": [ + { + "$ref": "#/components/schemas/ImageField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The image to process", + "field_kind": "input", + "input": "any", + "orig_required": true + }, + "amount": { + "default": 0, + "description": "Strength of the clarity adjustment", + "field_kind": "input", + "input": "any", + "maximum": 100, + "minimum": -100, + "orig_default": 0, + "orig_required": false, + "title": "Amount", + "type": "number" + }, + "size": { + "default": 40, + "description": "Scale of the detail adjustment", + "field_kind": "input", + "input": "any", + "maximum": 1000, + "minimum": 4, + "orig_default": 40, + "orig_required": false, + "title": "Size", + "type": "number" + }, + "type": { + "const": "clarity", + "default": "clarity", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["image", "detail", "clarity", "enhance"], + "title": "Clarity", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/ImageOutput" + } + }, "Classification": { "description": "The classification of an Invocation.\n- `Stable`: The invocation, including its inputs/outputs and internal logic, is stable. You may build workflows with it, having confidence that they will not break because of a change in this invocation.\n- `Beta`: The invocation is not yet stable, but is planned to be stable in the future. Workflows built around this invocation may break, but we are committed to supporting this invocation long-term.\n- `Prototype`: The invocation is not yet stable and may be removed from the application at any time. Workflows built around this invocation may break, and we are *not* committed to supporting this invocation.\n- `Deprecated`: The invocation is deprecated and may be removed in a future version.\n- `Internal`: The invocation is not intended for use by end-users. It may be changed or removed at any time, but is exposed for users to play with.\n- `Special`: The invocation is a special case and does not fit into any of the other classifications.", "enum": ["stable", "beta", "prototype", "deprecated", "internal", "special"], @@ -27142,6 +27261,113 @@ "$ref": "#/components/schemas/ImageOutput" } }, + "ContrastDehazeInvocation": { + "category": "image", + "class": "invocation", + "classification": "stable", + "description": "Adjust broad-scale contrast and chroma as an aesthetic dehazing approximation.", + "node_pack": "invokeai", + "properties": { + "board": { + "anyOf": [ + { + "$ref": "#/components/schemas/BoardField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The board to save the image to", + "field_kind": "internal", + "input": "direct", + "orig_required": false, + "ui_hidden": false + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional metadata to be saved with the image", + "field_kind": "internal", + "input": "connection", + "orig_required": false, + "ui_hidden": false + }, + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "image": { + "anyOf": [ + { + "$ref": "#/components/schemas/ImageField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The image to process", + "field_kind": "input", + "input": "any", + "orig_required": true + }, + "amount": { + "default": 0, + "description": "Strength of the contrast and dehaze adjustment", + "field_kind": "input", + "input": "any", + "maximum": 100, + "minimum": -100, + "orig_default": 0, + "orig_required": false, + "title": "Amount", + "type": "number" + }, + "type": { + "const": "contrast_dehaze", + "default": "contrast_dehaze", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["image", "contrast", "dehaze", "enhance"], + "title": "Contrast Dehaze (approximation)", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/ImageOutput" + } + }, "ControlAdapterDefaultSettings": { "properties": { "preprocessor": { @@ -43328,6 +43554,9 @@ { "$ref": "#/components/schemas/CenterPadCropInvocation" }, + { + "$ref": "#/components/schemas/ClarityInvocation" + }, { "$ref": "#/components/schemas/CogView4DenoiseInvocation" }, @@ -43376,6 +43605,9 @@ { "$ref": "#/components/schemas/ContentShuffleInvocation" }, + { + "$ref": "#/components/schemas/ContrastDehazeInvocation" + }, { "$ref": "#/components/schemas/ControlNetInvocation" }, @@ -44186,6 +44418,9 @@ { "$ref": "#/components/schemas/TextLLMWithPresetInvocation" }, + { + "$ref": "#/components/schemas/TextureInvocation" + }, { "$ref": "#/components/schemas/TileToPropertiesInvocation" }, @@ -53586,6 +53821,9 @@ { "$ref": "#/components/schemas/CenterPadCropInvocation" }, + { + "$ref": "#/components/schemas/ClarityInvocation" + }, { "$ref": "#/components/schemas/CogView4DenoiseInvocation" }, @@ -53634,6 +53872,9 @@ { "$ref": "#/components/schemas/ContentShuffleInvocation" }, + { + "$ref": "#/components/schemas/ContrastDehazeInvocation" + }, { "$ref": "#/components/schemas/ControlNetInvocation" }, @@ -54444,6 +54685,9 @@ { "$ref": "#/components/schemas/TextLLMWithPresetInvocation" }, + { + "$ref": "#/components/schemas/TextureInvocation" + }, { "$ref": "#/components/schemas/TileToPropertiesInvocation" }, @@ -55201,6 +55445,9 @@ { "$ref": "#/components/schemas/CenterPadCropInvocation" }, + { + "$ref": "#/components/schemas/ClarityInvocation" + }, { "$ref": "#/components/schemas/CogView4DenoiseInvocation" }, @@ -55249,6 +55496,9 @@ { "$ref": "#/components/schemas/ContentShuffleInvocation" }, + { + "$ref": "#/components/schemas/ContrastDehazeInvocation" + }, { "$ref": "#/components/schemas/ControlNetInvocation" }, @@ -56059,6 +56309,9 @@ { "$ref": "#/components/schemas/TextLLMWithPresetInvocation" }, + { + "$ref": "#/components/schemas/TextureInvocation" + }, { "$ref": "#/components/schemas/TileToPropertiesInvocation" }, @@ -56329,6 +56582,9 @@ "canvas_v2_mask_and_crop": { "$ref": "#/components/schemas/ImageOutput" }, + "clarity": { + "$ref": "#/components/schemas/ImageOutput" + }, "clip_skip": { "$ref": "#/components/schemas/CLIPSkipInvocationOutput" }, @@ -56380,6 +56636,9 @@ "content_shuffle": { "$ref": "#/components/schemas/ImageOutput" }, + "contrast_dehaze": { + "$ref": "#/components/schemas/ImageOutput" + }, "controlnet": { "$ref": "#/components/schemas/ControlOutput" }, @@ -57193,6 +57452,9 @@ "text_llm_with_preset": { "$ref": "#/components/schemas/StringOutput" }, + "texture": { + "$ref": "#/components/schemas/ImageOutput" + }, "tile_to_properties": { "$ref": "#/components/schemas/TileToPropertiesOutput" }, @@ -57328,6 +57590,7 @@ "canvas_output", "canvas_paste_back", "canvas_v2_mask_and_crop", + "clarity", "clip_skip", "cogview4_denoise", "cogview4_i2l", @@ -57345,6 +57608,7 @@ "conditioning", "conditioning_collection", "content_shuffle", + "contrast_dehaze", "controlnet", "core_metadata", "create_denoise_mask", @@ -57616,6 +57880,7 @@ "tensor_mask_to_image", "text_llm", "text_llm_with_preset", + "texture", "tile_to_properties", "tiled_multi_diffusion_denoise_latents", "tomask", @@ -57812,6 +58077,9 @@ { "$ref": "#/components/schemas/CenterPadCropInvocation" }, + { + "$ref": "#/components/schemas/ClarityInvocation" + }, { "$ref": "#/components/schemas/CogView4DenoiseInvocation" }, @@ -57860,6 +58128,9 @@ { "$ref": "#/components/schemas/ContentShuffleInvocation" }, + { + "$ref": "#/components/schemas/ContrastDehazeInvocation" + }, { "$ref": "#/components/schemas/ControlNetInvocation" }, @@ -58670,6 +58941,9 @@ { "$ref": "#/components/schemas/TextLLMWithPresetInvocation" }, + { + "$ref": "#/components/schemas/TextureInvocation" + }, { "$ref": "#/components/schemas/TileToPropertiesInvocation" }, @@ -59057,6 +59331,9 @@ { "$ref": "#/components/schemas/CenterPadCropInvocation" }, + { + "$ref": "#/components/schemas/ClarityInvocation" + }, { "$ref": "#/components/schemas/CogView4DenoiseInvocation" }, @@ -59105,6 +59382,9 @@ { "$ref": "#/components/schemas/ContentShuffleInvocation" }, + { + "$ref": "#/components/schemas/ContrastDehazeInvocation" + }, { "$ref": "#/components/schemas/ControlNetInvocation" }, @@ -59915,6 +60195,9 @@ { "$ref": "#/components/schemas/TextLLMWithPresetInvocation" }, + { + "$ref": "#/components/schemas/TextureInvocation" + }, { "$ref": "#/components/schemas/TileToPropertiesInvocation" }, @@ -107497,6 +107780,125 @@ "title": "TextLLM_Diffusers_Config", "description": "Model config for text-only causal language models (e.g. Llama, Phi, Qwen, Mistral)." }, + "TextureInvocation": { + "category": "image", + "class": "invocation", + "classification": "stable", + "description": "Adjust fine-scale texture detail in an image.", + "node_pack": "invokeai", + "properties": { + "board": { + "anyOf": [ + { + "$ref": "#/components/schemas/BoardField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The board to save the image to", + "field_kind": "internal", + "input": "direct", + "orig_required": false, + "ui_hidden": false + }, + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/MetadataField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "Optional metadata to be saved with the image", + "field_kind": "internal", + "input": "connection", + "orig_required": false, + "ui_hidden": false + }, + "id": { + "description": "The id of this instance of an invocation. Must be unique among all instances of invocations.", + "field_kind": "node_attribute", + "title": "Id", + "type": "string" + }, + "is_intermediate": { + "default": false, + "description": "Whether or not this is an intermediate invocation.", + "field_kind": "node_attribute", + "input": "direct", + "orig_required": true, + "title": "Is Intermediate", + "type": "boolean", + "ui_hidden": false, + "ui_type": "IsIntermediate" + }, + "use_cache": { + "default": true, + "description": "Whether or not to use the cache", + "field_kind": "node_attribute", + "title": "Use Cache", + "type": "boolean" + }, + "image": { + "anyOf": [ + { + "$ref": "#/components/schemas/ImageField" + }, + { + "type": "null" + } + ], + "default": null, + "description": "The image to process", + "field_kind": "input", + "input": "any", + "orig_required": true + }, + "amount": { + "default": 0, + "description": "Strength of the texture adjustment", + "field_kind": "input", + "input": "any", + "maximum": 100, + "minimum": -100, + "orig_default": 0, + "orig_required": false, + "title": "Amount", + "type": "number" + }, + "size": { + "default": 8, + "description": "Scale of the texture detail", + "field_kind": "input", + "input": "any", + "maximum": 100, + "minimum": 1, + "orig_default": 8, + "orig_required": false, + "title": "Size", + "type": "number" + }, + "type": { + "const": "texture", + "default": "texture", + "field_kind": "node_attribute", + "title": "type", + "type": "string" + } + }, + "required": ["type", "id"], + "tags": ["image", "detail", "texture", "enhance"], + "title": "Texture", + "type": "object", + "version": "1.0.0", + "output": { + "$ref": "#/components/schemas/ImageOutput" + } + }, "Tile": { "properties": { "coords": { diff --git a/invokeai/frontend/api/schema.ts b/invokeai/frontend/api/schema.ts index e78cd2786a2..d9a6c6727fd 100644 --- a/invokeai/frontend/api/schema.ts +++ b/invokeai/frontend/api/schema.ts @@ -8334,6 +8334,62 @@ export type components = { */ type: "img_pad_crop"; }; + /** + * Clarity + * @description Adjust local midtone detail in an image. + */ + ClarityInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Amount + * @description Strength of the clarity adjustment + * @default 0 + */ + amount?: number; + /** + * Size + * @description Scale of the detail adjustment + * @default 40 + */ + size?: number; + /** + * type + * @default clarity + * @constant + */ + type: "clarity"; + }; /** * Classification * @description The classification of an Invocation. @@ -9337,6 +9393,56 @@ export type components = { */ type: "content_shuffle"; }; + /** + * Contrast Dehaze (approximation) + * @description Adjust broad-scale contrast and chroma as an aesthetic dehazing approximation. + */ + ContrastDehazeInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Amount + * @description Strength of the contrast and dehaze adjustment + * @default 0 + */ + amount?: number; + /** + * type + * @default contrast_dehaze + * @constant + */ + type: "contrast_dehaze"; + }; /** ControlAdapterDefaultSettings */ ControlAdapterDefaultSettings: { /** Preprocessor */ @@ -17400,7 +17506,7 @@ export type components = { * @description The nodes in this graph */ nodes?: { - [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + [key: string]: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["ClarityInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ContrastDehazeInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TextureInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; }; /** * Edges @@ -22231,7 +22337,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["ClarityInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ContrastDehazeInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TextureInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -22313,7 +22419,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["ClarityInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ContrastDehazeInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TextureInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -22382,6 +22488,7 @@ export type components = { canvas_output: components["schemas"]["ImageOutput"]; canvas_paste_back: components["schemas"]["ImageOutput"]; canvas_v2_mask_and_crop: components["schemas"]["ImageOutput"]; + clarity: components["schemas"]["ImageOutput"]; clip_skip: components["schemas"]["CLIPSkipInvocationOutput"]; cogview4_denoise: components["schemas"]["LatentsOutput"]; cogview4_i2l: components["schemas"]["LatentsOutput"]; @@ -22399,6 +22506,7 @@ export type components = { conditioning: components["schemas"]["ConditioningOutput"]; conditioning_collection: components["schemas"]["ConditioningCollectionOutput"]; content_shuffle: components["schemas"]["ImageOutput"]; + contrast_dehaze: components["schemas"]["ImageOutput"]; controlnet: components["schemas"]["ControlOutput"]; core_metadata: components["schemas"]["MetadataOutput"]; create_denoise_mask: components["schemas"]["DenoiseMaskOutput"]; @@ -22670,6 +22778,7 @@ export type components = { tensor_mask_to_image: components["schemas"]["ImageOutput"]; text_llm: components["schemas"]["StringOutput"]; text_llm_with_preset: components["schemas"]["StringOutput"]; + texture: components["schemas"]["ImageOutput"]; tile_to_properties: components["schemas"]["TileToPropertiesOutput"]; tiled_multi_diffusion_denoise_latents: components["schemas"]["LatentsOutput"]; tomask: components["schemas"]["ImageOutput"]; @@ -22758,7 +22867,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["ClarityInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ContrastDehazeInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TextureInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -22863,7 +22972,7 @@ export type components = { * Invocation * @description The ID of the invocation */ - invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; + invocation: components["schemas"]["AddInvocation"] | components["schemas"]["AlibabaCloudImageGenerationInvocation"] | components["schemas"]["AlphaMaskToTensorInvocation"] | components["schemas"]["AnimaDenoiseInvocation"] | components["schemas"]["AnimaDenoiseMetaInvocation"] | components["schemas"]["AnimaImageToLatentsInvocation"] | components["schemas"]["AnimaLLLiteInvocation"] | components["schemas"]["AnimaLatentsToImageInvocation"] | components["schemas"]["AnimaLoRACollectionLoader"] | components["schemas"]["AnimaLoRALoaderInvocation"] | components["schemas"]["AnimaModelLoaderInvocation"] | components["schemas"]["AnimaTextEncoderInvocation"] | components["schemas"]["ApplyMaskTensorToImageInvocation"] | components["schemas"]["ApplyMaskToImageInvocation"] | components["schemas"]["AtlasCloudImageGenerationInvocation"] | components["schemas"]["BlankImageInvocation"] | components["schemas"]["BlendLatentsInvocation"] | components["schemas"]["BooleanCollectionInvocation"] | components["schemas"]["BooleanInvocation"] | components["schemas"]["BooleanLogicInvocation"] | components["schemas"]["BoundingBoxInvocation"] | components["schemas"]["CLIPSkipInvocation"] | components["schemas"]["CV2InfillInvocation"] | components["schemas"]["CalculateImageTilesEvenSplitInvocation"] | components["schemas"]["CalculateImageTilesInvocation"] | components["schemas"]["CalculateImageTilesMinimumOverlapInvocation"] | components["schemas"]["CallSavedWorkflowInvocation"] | components["schemas"]["CannyEdgeDetectionInvocation"] | components["schemas"]["CanvasOutputInvocation"] | components["schemas"]["CanvasPasteBackInvocation"] | components["schemas"]["CanvasV2MaskAndCropInvocation"] | components["schemas"]["CenterPadCropInvocation"] | components["schemas"]["ClarityInvocation"] | components["schemas"]["CogView4DenoiseInvocation"] | components["schemas"]["CogView4ImageToLatentsInvocation"] | components["schemas"]["CogView4LatentsToImageInvocation"] | components["schemas"]["CogView4ModelLoaderInvocation"] | components["schemas"]["CogView4TextEncoderInvocation"] | components["schemas"]["CollectInvocation"] | components["schemas"]["CollectionCartesianInvocation"] | components["schemas"]["CollectionConcatInvocation"] | components["schemas"]["CollectionZipInvocation"] | components["schemas"]["ColorCorrectInvocation"] | components["schemas"]["ColorInvocation"] | components["schemas"]["ColorMapInvocation"] | components["schemas"]["CompelInvocation"] | components["schemas"]["ConditioningCollectionInvocation"] | components["schemas"]["ConditioningInvocation"] | components["schemas"]["ContentShuffleInvocation"] | components["schemas"]["ContrastDehazeInvocation"] | components["schemas"]["ControlNetInvocation"] | components["schemas"]["CoreMetadataInvocation"] | components["schemas"]["CreateDenoiseMaskInvocation"] | components["schemas"]["CreateGradientMaskInvocation"] | components["schemas"]["CropImageToBoundingBoxInvocation"] | components["schemas"]["CropLatentsCoreInvocation"] | components["schemas"]["CvInpaintInvocation"] | components["schemas"]["DWOpenposeDetectionInvocation"] | components["schemas"]["DecodeInvisibleWatermarkInvocation"] | components["schemas"]["DenoiseLatentsInvocation"] | components["schemas"]["DenoiseLatentsMetaInvocation"] | components["schemas"]["DepthAnythingDepthEstimationInvocation"] | components["schemas"]["DivideInvocation"] | components["schemas"]["DynamicPromptInvocation"] | components["schemas"]["ESRGANInvocation"] | components["schemas"]["ErnieImageDenoiseInvocation"] | components["schemas"]["ErnieImageModelLoaderInvocation"] | components["schemas"]["ErnieImagePromptEnhancerInvocation"] | components["schemas"]["ErnieImageTextEncoderInvocation"] | components["schemas"]["ErnieImageVaeDecodeInvocation"] | components["schemas"]["ExpandMaskWithFadeInvocation"] | components["schemas"]["ExtractVideoRangeInvocation"] | components["schemas"]["FLUXLoRACollectionLoader"] | components["schemas"]["FaceIdentifierInvocation"] | components["schemas"]["FaceMaskInvocation"] | components["schemas"]["FaceOffInvocation"] | components["schemas"]["FloatBatchInvocation"] | components["schemas"]["FloatCollectionInvocation"] | components["schemas"]["FloatGenerator"] | components["schemas"]["FloatInvocation"] | components["schemas"]["FloatLinearRangeInvocation"] | components["schemas"]["FloatMathInvocation"] | components["schemas"]["FloatToIntegerInvocation"] | components["schemas"]["Flux2DenoiseInvocation"] | components["schemas"]["Flux2DevLoRACollectionLoader"] | components["schemas"]["Flux2DevLoRALoaderInvocation"] | components["schemas"]["Flux2DevModelLoaderInvocation"] | components["schemas"]["Flux2DevTextEncoderInvocation"] | components["schemas"]["Flux2KleinLoRACollectionLoader"] | components["schemas"]["Flux2KleinLoRALoaderInvocation"] | components["schemas"]["Flux2KleinModelLoaderInvocation"] | components["schemas"]["Flux2KleinTextEncoderInvocation"] | components["schemas"]["Flux2PiDDecodeInvocation"] | components["schemas"]["Flux2VaeDecodeInvocation"] | components["schemas"]["Flux2VaeEncodeInvocation"] | components["schemas"]["FluxControlLoRALoaderInvocation"] | components["schemas"]["FluxControlNetInvocation"] | components["schemas"]["FluxDenoiseInvocation"] | components["schemas"]["FluxDenoiseLatentsMetaInvocation"] | components["schemas"]["FluxFillInvocation"] | components["schemas"]["FluxIPAdapterInvocation"] | components["schemas"]["FluxKontextConcatenateImagesInvocation"] | components["schemas"]["FluxKontextInvocation"] | components["schemas"]["FluxLoRALoaderInvocation"] | components["schemas"]["FluxModelLoaderInvocation"] | components["schemas"]["FluxPiDDecodeInvocation"] | components["schemas"]["FluxReduxInvocation"] | components["schemas"]["FluxTextEncoderInvocation"] | components["schemas"]["FluxVaeDecodeInvocation"] | components["schemas"]["FluxVaeEncodeInvocation"] | components["schemas"]["ForInvocation"] | components["schemas"]["ForReturnInvocation"] | components["schemas"]["FreeUInvocation"] | components["schemas"]["GeminiImageGenerationInvocation"] | components["schemas"]["Gemma2EncoderLoaderInvocation"] | components["schemas"]["GetMaskBoundingBoxInvocation"] | components["schemas"]["GroundingDinoInvocation"] | components["schemas"]["HEDEdgeDetectionInvocation"] | components["schemas"]["HeuristicResizeInvocation"] | components["schemas"]["IPAdapterInvocation"] | components["schemas"]["IdealSizeInvocation"] | components["schemas"]["Ideogram4CaptionBuilderInvocation"] | components["schemas"]["Ideogram4DenoiseInvocation"] | components["schemas"]["Ideogram4LatentsToImageInvocation"] | components["schemas"]["Ideogram4ModelLoaderInvocation"] | components["schemas"]["Ideogram4TextEncoderInvocation"] | components["schemas"]["IfInvocation"] | components["schemas"]["ImageBatchInvocation"] | components["schemas"]["ImageBlurInvocation"] | components["schemas"]["ImageChannelInvocation"] | components["schemas"]["ImageChannelMultiplyInvocation"] | components["schemas"]["ImageChannelOffsetInvocation"] | components["schemas"]["ImageCollectionInvocation"] | components["schemas"]["ImageConvertInvocation"] | components["schemas"]["ImageCropInvocation"] | components["schemas"]["ImageGenerator"] | components["schemas"]["ImageHueAdjustmentInvocation"] | components["schemas"]["ImageInverseLerpInvocation"] | components["schemas"]["ImageInvocation"] | components["schemas"]["ImageLerpInvocation"] | components["schemas"]["ImageMaskToTensorInvocation"] | components["schemas"]["ImageMultiplyInvocation"] | components["schemas"]["ImageNSFWBlurInvocation"] | components["schemas"]["ImageNoiseInvocation"] | components["schemas"]["ImagePanelLayoutInvocation"] | components["schemas"]["ImagePasteInvocation"] | components["schemas"]["ImageResizeInvocation"] | components["schemas"]["ImageScaleInvocation"] | components["schemas"]["ImageToLatentsInvocation"] | components["schemas"]["ImageWatermarkInvocation"] | components["schemas"]["InfillColorInvocation"] | components["schemas"]["InfillPatchMatchInvocation"] | components["schemas"]["InfillTileInvocation"] | components["schemas"]["IntegerBatchInvocation"] | components["schemas"]["IntegerCollectionInvocation"] | components["schemas"]["IntegerGenerator"] | components["schemas"]["IntegerInvocation"] | components["schemas"]["IntegerMathInvocation"] | components["schemas"]["InvertTensorMaskInvocation"] | components["schemas"]["InvokeAdjustImageHuePlusInvocation"] | components["schemas"]["InvokeEquivalentAchromaticLightnessInvocation"] | components["schemas"]["InvokeImageBlendInvocation"] | components["schemas"]["InvokeImageCompositorInvocation"] | components["schemas"]["InvokeImageDilateOrErodeInvocation"] | components["schemas"]["InvokeImageEnhanceInvocation"] | components["schemas"]["InvokeImageValueThresholdsInvocation"] | components["schemas"]["IterateInvocation"] | components["schemas"]["Krea2ConditioningRebalanceInvocation"] | components["schemas"]["Krea2DenoiseInvocation"] | components["schemas"]["Krea2LoRACollectionLoader"] | components["schemas"]["Krea2LoRALoaderInvocation"] | components["schemas"]["Krea2ModelLoaderInvocation"] | components["schemas"]["Krea2SeedVarianceInvocation"] | components["schemas"]["Krea2StyleReferenceInvocation"] | components["schemas"]["Krea2TextEncoderInvocation"] | components["schemas"]["LTX2AudioConditioningInvocation"] | components["schemas"]["LTX2DenoiseInvocation"] | components["schemas"]["LTX2DurationInvocation"] | components["schemas"]["LTX2ExtendConditioningInvocation"] | components["schemas"]["LTX2IdealDimensionsInvocation"] | components["schemas"]["LTX2ImageConditioningInvocation"] | components["schemas"]["LTX2LatentUpsampleInvocation"] | components["schemas"]["LTX2LatentsToVideoInvocation"] | components["schemas"]["LTX2LoRACollectionLoader"] | components["schemas"]["LTX2LoRALoaderInvocation"] | components["schemas"]["LTX2ModelLoaderInvocation"] | components["schemas"]["LTX2TextEncoderInvocation"] | components["schemas"]["LTX2VideoConditioningInvocation"] | components["schemas"]["LaMaInfillInvocation"] | components["schemas"]["LatentsCollectionInvocation"] | components["schemas"]["LatentsInvocation"] | components["schemas"]["LatentsToImageInvocation"] | components["schemas"]["LineartAnimeEdgeDetectionInvocation"] | components["schemas"]["LineartEdgeDetectionInvocation"] | components["schemas"]["LlavaOnevisionVllmInvocation"] | components["schemas"]["LoRACollectionLoader"] | components["schemas"]["LoRALoaderInvocation"] | components["schemas"]["LoRASelectorInvocation"] | components["schemas"]["MLSDDetectionInvocation"] | components["schemas"]["MainModelLoaderInvocation"] | components["schemas"]["MaskCombineInvocation"] | components["schemas"]["MaskEdgeInvocation"] | components["schemas"]["MaskFromAlphaInvocation"] | components["schemas"]["MaskFromIDInvocation"] | components["schemas"]["MaskTensorToImageInvocation"] | components["schemas"]["MediaPipeFaceDetectionInvocation"] | components["schemas"]["MergeMetadataInvocation"] | components["schemas"]["MergeTilesToImageInvocation"] | components["schemas"]["MetadataFieldExtractorInvocation"] | components["schemas"]["MetadataFromImageInvocation"] | components["schemas"]["MetadataInvocation"] | components["schemas"]["MetadataItemInvocation"] | components["schemas"]["MetadataItemLinkedInvocation"] | components["schemas"]["MetadataToBoolCollectionInvocation"] | components["schemas"]["MetadataToBoolInvocation"] | components["schemas"]["MetadataToControlnetsInvocation"] | components["schemas"]["MetadataToFloatCollectionInvocation"] | components["schemas"]["MetadataToFloatInvocation"] | components["schemas"]["MetadataToIPAdaptersInvocation"] | components["schemas"]["MetadataToIntegerCollectionInvocation"] | components["schemas"]["MetadataToIntegerInvocation"] | components["schemas"]["MetadataToLorasCollectionInvocation"] | components["schemas"]["MetadataToLorasInvocation"] | components["schemas"]["MetadataToModelInvocation"] | components["schemas"]["MetadataToSDXLLorasInvocation"] | components["schemas"]["MetadataToSDXLModelInvocation"] | components["schemas"]["MetadataToSchedulerInvocation"] | components["schemas"]["MetadataToStringCollectionInvocation"] | components["schemas"]["MetadataToStringInvocation"] | components["schemas"]["MetadataToT2IAdaptersInvocation"] | components["schemas"]["MetadataToVAEInvocation"] | components["schemas"]["MiniMaxH3DenoiseInvocation"] | components["schemas"]["MiniMaxH3FrameConditioningInvocation"] | components["schemas"]["MiniMaxH3HybridOverlayInvocation"] | components["schemas"]["MiniMaxH3IdealDimensionsInvocation"] | components["schemas"]["MiniMaxH3ImageReferenceInvocation"] | components["schemas"]["MiniMaxH3LatentsToImageInvocation"] | components["schemas"]["MiniMaxH3LatentsToVideoInvocation"] | components["schemas"]["MiniMaxH3LoRACollectionLoader"] | components["schemas"]["MiniMaxH3LoRALoaderInvocation"] | components["schemas"]["MiniMaxH3ModelLoaderInvocation"] | components["schemas"]["MiniMaxH3ReferenceConditioningInvocation"] | components["schemas"]["MiniMaxH3TextEncoderInvocation"] | components["schemas"]["MiniMaxH3VideoReferenceInvocation"] | components["schemas"]["ModelIdentifierInvocation"] | components["schemas"]["MultiplyInvocation"] | components["schemas"]["NoiseInvocation"] | components["schemas"]["NormalMapInvocation"] | components["schemas"]["OklabUnsharpMaskInvocation"] | components["schemas"]["OklchImageHueAdjustmentInvocation"] | components["schemas"]["OpenAIImageGenerationInvocation"] | components["schemas"]["PBRMapsInvocation"] | components["schemas"]["PairTileImageInvocation"] | components["schemas"]["PasteImageIntoBoundingBoxInvocation"] | components["schemas"]["PiDDecoderLoaderInvocation"] | components["schemas"]["PiDUpscaleInvocation"] | components["schemas"]["PiDiNetEdgeDetectionInvocation"] | components["schemas"]["PromptTemplateInvocation"] | components["schemas"]["PromptsFromFileInvocation"] | components["schemas"]["QwenImage21DenoiseInvocation"] | components["schemas"]["QwenImage21ImageToLatentsInvocation"] | components["schemas"]["QwenImage21LatentsToImageInvocation"] | components["schemas"]["QwenImage21ModelLoaderInvocation"] | components["schemas"]["QwenImage21TextEncoderInvocation"] | components["schemas"]["QwenImageDenoiseInvocation"] | components["schemas"]["QwenImageImageToLatentsInvocation"] | components["schemas"]["QwenImageLatentsToImageInvocation"] | components["schemas"]["QwenImageLoRACollectionLoader"] | components["schemas"]["QwenImageLoRALoaderInvocation"] | components["schemas"]["QwenImageModelLoaderInvocation"] | components["schemas"]["QwenImagePiDDecodeInvocation"] | components["schemas"]["QwenImageTextEncoderInvocation"] | components["schemas"]["RandomFloatInvocation"] | components["schemas"]["RandomIntInvocation"] | components["schemas"]["RandomRangeInvocation"] | components["schemas"]["RangeInvocation"] | components["schemas"]["RangeOfSizeInvocation"] | components["schemas"]["RectangleMaskInvocation"] | components["schemas"]["ResizeLatentsInvocation"] | components["schemas"]["RoundInvocation"] | components["schemas"]["SD3DenoiseInvocation"] | components["schemas"]["SD3ImageToLatentsInvocation"] | components["schemas"]["SD3LatentsToImageInvocation"] | components["schemas"]["SD3PiDDecodeInvocation"] | components["schemas"]["SDXLCompelPromptInvocation"] | components["schemas"]["SDXLLoRACollectionLoader"] | components["schemas"]["SDXLLoRALoaderInvocation"] | components["schemas"]["SDXLModelLoaderInvocation"] | components["schemas"]["SDXLPiDDecodeInvocation"] | components["schemas"]["SDXLRefinerCompelPromptInvocation"] | components["schemas"]["SDXLRefinerModelLoaderInvocation"] | components["schemas"]["SaveImageInvocation"] | components["schemas"]["SaveImageToFileInvocation"] | components["schemas"]["ScaleLatentsInvocation"] | components["schemas"]["SchedulerInvocation"] | components["schemas"]["Sd3ModelLoaderInvocation"] | components["schemas"]["Sd3TextEncoderInvocation"] | components["schemas"]["SeamlessModeInvocation"] | components["schemas"]["SeedreamImageGenerationInvocation"] | components["schemas"]["SegmentAnythingInvocation"] | components["schemas"]["ShowImageInvocation"] | components["schemas"]["SpandrelImageToImageAutoscaleInvocation"] | components["schemas"]["SpandrelImageToImageInvocation"] | components["schemas"]["StateEmptyInvocation"] | components["schemas"]["StateGetInvocation"] | components["schemas"]["StateMergeInvocation"] | components["schemas"]["StateSetInvocation"] | components["schemas"]["StringBatchInvocation"] | components["schemas"]["StringCollectionInvocation"] | components["schemas"]["StringGenerator"] | components["schemas"]["StringInvocation"] | components["schemas"]["StringJoinInvocation"] | components["schemas"]["StringJoinThreeInvocation"] | components["schemas"]["StringReplaceInvocation"] | components["schemas"]["StringSplitInvocation"] | components["schemas"]["StringSplitNegInvocation"] | components["schemas"]["SubtractInvocation"] | components["schemas"]["T2IAdapterInvocation"] | components["schemas"]["TextLLMInvocation"] | components["schemas"]["TextLLMWithPresetInvocation"] | components["schemas"]["TextureInvocation"] | components["schemas"]["TileToPropertiesInvocation"] | components["schemas"]["TiledMultiDiffusionDenoiseLatents"] | components["schemas"]["UnsharpMaskInvocation"] | components["schemas"]["VAELoaderInvocation"] | components["schemas"]["VideoConcatInvocation"] | components["schemas"]["VideoFrameExtractInvocation"] | components["schemas"]["VideoInvocation"] | components["schemas"]["WanDenoiseInvocation"] | components["schemas"]["WanI2VIdealDimensionsInvocation"] | components["schemas"]["WanImageToLatentsInvocation"] | components["schemas"]["WanLatentsToImageInvocation"] | components["schemas"]["WanLatentsToVideoInvocation"] | components["schemas"]["WanLoRACollectionLoader"] | components["schemas"]["WanLoRALoaderInvocation"] | components["schemas"]["WanModelLoaderInvocation"] | components["schemas"]["WanRefImageEncoderInvocation"] | components["schemas"]["WanTI2VIdealDimensionsInvocation"] | components["schemas"]["WanTextEncoderInvocation"] | components["schemas"]["WanVideoDenoiseInvocation"] | components["schemas"]["WorkflowReturnGetInvocation"] | components["schemas"]["WorkflowReturnInvocation"] | components["schemas"]["WorkflowReturnValueInvocation"] | components["schemas"]["ZImageControlInvocation"] | components["schemas"]["ZImageDenoiseInvocation"] | components["schemas"]["ZImageDenoiseMetaInvocation"] | components["schemas"]["ZImageImageToLatentsInvocation"] | components["schemas"]["ZImageLatentsToImageInvocation"] | components["schemas"]["ZImageLoRACollectionLoader"] | components["schemas"]["ZImageLoRALoaderInvocation"] | components["schemas"]["ZImageModelLoaderInvocation"] | components["schemas"]["ZImagePiDDecodeInvocation"] | components["schemas"]["ZImageSeedVarianceEnhancerInvocation"] | components["schemas"]["ZImageTextEncoderInvocation"]; /** * Invocation Source Id * @description The ID of the prepared invocation's source node @@ -47484,6 +47593,62 @@ export type components = { */ supports_images: boolean; }; + /** + * Texture + * @description Adjust fine-scale texture detail in an image. + */ + TextureInvocation: { + /** + * @description The board to save the image to + * @default null + */ + board?: components["schemas"]["BoardField"] | null; + /** + * @description Optional metadata to be saved with the image + * @default null + */ + metadata?: components["schemas"]["MetadataField"] | null; + /** + * Id + * @description The id of this instance of an invocation. Must be unique among all instances of invocations. + */ + id: string; + /** + * Is Intermediate + * @description Whether or not this is an intermediate invocation. + * @default false + */ + is_intermediate?: boolean; + /** + * Use Cache + * @description Whether or not to use the cache + * @default true + */ + use_cache?: boolean; + /** + * @description The image to process + * @default null + */ + image?: components["schemas"]["ImageField"] | null; + /** + * Amount + * @description Strength of the texture adjustment + * @default 0 + */ + amount?: number; + /** + * Size + * @description Scale of the texture detail + * @default 8 + */ + size?: number; + /** + * type + * @default texture + * @constant + */ + type: "texture"; + }; /** Tile */ Tile: { /** @description The coordinates of this tile relative to its parent image. */ diff --git a/invokeai/frontend/webv2/src/features/generation/core/__fixtures__/shared_image_manipulation_numerics.json b/invokeai/frontend/webv2/src/features/generation/core/__fixtures__/shared_image_manipulation_numerics.json new file mode 100644 index 00000000000..915ad962034 --- /dev/null +++ b/invokeai/frontend/webv2/src/features/generation/core/__fixtures__/shared_image_manipulation_numerics.json @@ -0,0 +1,64 @@ +{ + "guided_filter": { + "guide": [ + [0, 0, 1], + [0, 1, 1], + [0, 1, 1] + ], + "signal": [ + [0.1, 0.2, 0.9], + [0, 0.8, 1], + [0.2, 0.7, 0.95] + ], + "radius": 1, + "epsilon": 0.1, + "expected": [ + [0.22415975, 0.27506159, 0.78571646], + [0.22110961, 0.75936384, 0.79556096], + [0.23253705, 0.74957011, 0.78752683] + ], + "atol": 0.00001, + "expected_derivation": "Independent scalar evaluation of the guided-filter equations; each normalized 3x3 box mean enumerates samples with BORDER_REFLECT_101, and coefficient means use a second scalar box pass." + }, + "multiscale_band": { + "lightness": [ + [0.15, 0.2, 0.25], + [0.2, 0.85, 0.9], + [0.25, 0.9, 0.95] + ], + "inner_radius": 0, + "outer_radius": 1, + "epsilon": 0.1, + "expected": [ + [-0.16519206, -0.17117175, -0.14883187], + [-0.17117175, 0.11232731, 0.13516251], + [-0.14883187, 0.13516251, 0.15758353] + ], + "atol": 0.00001, + "expected_derivation": "Input minus an independently evaluated radius-1 self-guided smooth, using scalar reflected box means and the variance/covariance equations. The radius-0 fine smooth is the input by definition." + }, + "color_adapter": { + "rgb": [ + [ + [255, 0, 0], + [0, 255, 0] + ], + [ + [0, 0, 255], + [255, 255, 255] + ] + ], + "expected_oklab": [ + [ + [0.62795536, 0.22486306, 0.1258463], + [0.86643961, -0.23388757, 0.17949848] + ], + [ + [0.45201372, -0.03245698, -0.31152815], + [1.0, 0.0, 0.0] + ] + ], + "atol": 0.00002, + "expected_derivation": "Canonical Oklab equations applied independently to the four sRGB primaries/white: piecewise sRGB decoding, the published linear-sRGB-to-LMS and cube-root-LMS-to-Oklab matrices, evaluated in float64." + } +} diff --git a/invokeai/frontend/webv2/src/features/generation/core/colorConversion.ts b/invokeai/frontend/webv2/src/features/generation/core/colorConversion.ts new file mode 100644 index 00000000000..b45d16d44a3 --- /dev/null +++ b/invokeai/frontend/webv2/src/features/generation/core/colorConversion.ts @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +export type RgbImage = { + readonly width: number; + readonly height: number; + /** Interleaved byte-valued red, green, blue samples in row-major pixel order. */ + readonly data: Uint8Array; +}; + +export type OklabImage = { + readonly width: number; + readonly height: number; + /** Interleaved lightness, a, b samples in row-major pixel order. */ + readonly data: Float64Array; +}; + +const decodeSrgb = (channel: number): number => { + const normalized = channel / 255; + return normalized <= 0.04045 ? normalized / 12.92 : ((normalized + 0.055) / 1.055) ** 2.4; +}; + +/** Converts interleaved byte-valued sRGB pixels to compact interleaved Oklab samples. */ +export const oklabFromSrgb = (rgb: RgbImage): OklabImage => { + const { width, height, data: input } = rgb; + if ( + !Number.isInteger(width) || + width < 1 || + !Number.isInteger(height) || + height < 1 || + !(input instanceof Uint8Array) || + input.length !== width * height * 3 + ) { + throw new Error('rgb must be a non-empty image with three interleaved byte channels per pixel'); + } + + const data = new Float64Array(input.length); + for (let i = 0; i < input.length; i += 3) { + const r = decodeSrgb(input[i]); + const g = decodeSrgb(input[i + 1]); + const b = decodeSrgb(input[i + 2]); + const l = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b); + const m = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b); + const s = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b); + data[i] = 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s; + data[i + 1] = 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s; + data[i + 2] = 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s; + } + return { width, height, data }; +}; diff --git a/invokeai/frontend/webv2/src/features/generation/core/guidedFilter.ts b/invokeai/frontend/webv2/src/features/generation/core/guidedFilter.ts new file mode 100644 index 00000000000..980348dadc5 --- /dev/null +++ b/invokeai/frontend/webv2/src/features/generation/core/guidedFilter.ts @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: Apache-2.0 +export type ScalarImage = { + readonly width: number; + readonly height: number; + readonly data: Float64Array; +}; + +const validateImage = (image: ScalarImage, name: string): void => { + if ( + !Number.isInteger(image.width) || + image.width < 1 || + !Number.isInteger(image.height) || + image.height < 1 || + !(image.data instanceof Float64Array) || + image.data.length !== image.width * image.height + ) { + throw new TypeError(`${name} must be a non-empty image with width * height Float64 samples`); + } + for (let i = 0; i < image.data.length; i++) { + if (!Number.isFinite(image.data[i])) { + throw new TypeError(`${name} must contain finite samples`); + } + } +}; + +const reflect101 = (index: number, length: number): number => { + if (length <= 1) { + return 0; + } + const period = 2 * length - 2; + const wrapped = ((index % period) + period) % period; + return wrapped < length ? wrapped : period - wrapped; +}; + +/** Computes a normalized square box mean with OpenCV BORDER_REFLECT_101 boundaries. */ +const boxMean = ( + first: Float64Array, + width: number, + height: number, + radius: number, + second?: Float64Array, + square = false +): Float64Array => { + const valueAt = (index: number) => + square ? first[index] * first[index] : second ? first[index] * second[index] : first[index]; + const windowSize = radius * 2 + 1; + const horizontal = new Float64Array(first.length); + const output = new Float64Array(first.length); + for (let y = 0; y < height; y++) { + const rowOffset = y * width; + let sum = 0; + for (let offset = -radius; offset <= radius; offset++) { + const index = rowOffset + reflect101(offset, width); + sum += valueAt(index); + } + for (let x = 0; x < width; x++) { + horizontal[rowOffset + x] = sum / windowSize; + const leaving = rowOffset + reflect101(x - radius, width); + const entering = rowOffset + reflect101(x + radius + 1, width); + sum -= valueAt(leaving); + sum += valueAt(entering); + } + } + for (let x = 0; x < width; x++) { + let sum = 0; + for (let offset = -radius; offset <= radius; offset++) { + const index = reflect101(offset, height) * width + x; + sum += horizontal[index]; + } + for (let y = 0; y < height; y++) { + const index = y * width + x; + output[index] = sum / windowSize; + sum -= horizontal[reflect101(y - radius, height) * width + x]; + sum += horizontal[reflect101(y + radius + 1, height) * width + x]; + } + } + return output; +}; + +/** Applies the classic single-channel guided filter to equally sized row-major images. */ +export const guidedFilter = (guide: ScalarImage, signal: ScalarImage, radius: number, epsilon: number): ScalarImage => { + validateImage(guide, 'guide'); + validateImage(signal, 'signal'); + if (guide.width !== signal.width || guide.height !== signal.height) { + throw new Error('guide and signal must have the same dimensions'); + } + if (!Number.isInteger(radius) || radius < 0) { + throw new Error('radius must be a non-negative integer'); + } + if (!Number.isFinite(epsilon) || epsilon <= 0) { + throw new Error('epsilon must be a finite positive number'); + } + const { width, height } = guide; + const count = guide.data.length; + if (radius === 0) { + return { width, height, data: signal.data.slice() }; + } + + const meanGuide = boxMean(guide.data, width, height, radius); + const meanSignal = boxMean(signal.data, width, height, radius); + const meanGuideSquared = boxMean(guide.data, width, height, radius, undefined, true); + const meanGuideSignal = boxMean(guide.data, width, height, radius, signal.data); + const a = new Float64Array(count); + const b = new Float64Array(count); + for (let i = 0; i < count; i++) { + const variance = Math.max(0, meanGuideSquared[i] - meanGuide[i] * meanGuide[i]); + const covariance = meanGuideSignal[i] - meanGuide[i] * meanSignal[i]; + a[i] = covariance / (variance + epsilon); + b[i] = meanSignal[i] - a[i] * meanGuide[i]; + } + const meanA = boxMean(a, width, height, radius); + const meanB = boxMean(b, width, height, radius); + const data = new Float64Array(count); + for (let i = 0; i < count; i++) { + data[i] = meanA[i] * guide.data[i] + meanB[i]; + } + return { width, height, data }; +}; diff --git a/invokeai/frontend/webv2/src/features/generation/core/multiscale.ts b/invokeai/frontend/webv2/src/features/generation/core/multiscale.ts new file mode 100644 index 00000000000..0741036e287 --- /dev/null +++ b/invokeai/frontend/webv2/src/features/generation/core/multiscale.ts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +import { guidedFilter, type ScalarImage } from './guidedFilter'; + +/** Returns the detail band between two guided-filter scales of the same lightness image. */ +export const edgeAwareBand = ( + lightness: ScalarImage, + innerRadius: number, + outerRadius: number, + epsilon: number +): ScalarImage => { + if (!Number.isInteger(innerRadius) || innerRadius < 0) { + throw new Error('innerRadius must be a non-negative integer'); + } + if (!Number.isInteger(outerRadius) || outerRadius <= innerRadius) { + throw new Error('outerRadius must be greater than innerRadius'); + } + const inner = innerRadius === 0 ? lightness.data : guidedFilter(lightness, lightness, innerRadius, epsilon).data; + const outer = guidedFilter(lightness, lightness, outerRadius, epsilon).data; + const data = new Float64Array(lightness.data.length); + for (let i = 0; i < data.length; i++) { + data[i] = inner[i] - outer[i]; + } + return { width: lightness.width, height: lightness.height, data }; +}; diff --git a/invokeai/frontend/webv2/src/features/generation/core/sharedImageManipulationNumerics.test.ts b/invokeai/frontend/webv2/src/features/generation/core/sharedImageManipulationNumerics.test.ts new file mode 100644 index 00000000000..baa67ada400 --- /dev/null +++ b/invokeai/frontend/webv2/src/features/generation/core/sharedImageManipulationNumerics.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, it } from 'vitest'; + +import fixtures from './__fixtures__/shared_image_manipulation_numerics.json'; +import { oklabFromSrgb } from './colorConversion'; +import { guidedFilter, type ScalarImage } from './guidedFilter'; +import { edgeAwareBand } from './multiscale'; + +const scalarImage = (rows: number[][]): ScalarImage => ({ + width: rows[0].length, + height: rows.length, + data: new Float64Array(rows.flat()), +}); + +const rgbImage = (rows: number[][][]): { width: number; height: number; data: Uint8Array } => ({ + width: rows[0].length, + height: rows.length, + data: new Uint8Array(rows.flat(2)), +}); + +/** Compare compact buffers against independently derived float64 references, with no relative tolerance. */ +const expectImageClose = (actual: ScalarImage, expected: number[][], atol: number) => { + expect(actual.width).toBe(expected[0].length); + expect(actual.height).toBe(expected.length); + expect(actual.data).toHaveLength(expected.length * expected[0].length); + expected.flat().forEach((value, i) => expect(Math.abs(actual.data[i] - value)).toBeLessThanOrEqual(atol)); +}; + +describe('shared image manipulation numerical fixtures', () => { + it('matches the independently derived guided-filter fixture and treats radius zero as identity', () => { + const { guide, signal, radius, epsilon, expected, atol } = fixtures.guided_filter; + expectImageClose(guidedFilter(scalarImage(guide), scalarImage(signal), radius, epsilon), expected, atol); + + const singleton = scalarImage([[0.375]]); + const result = guidedFilter(scalarImage([[1]]), singleton, 0, epsilon); + expect(Array.from(result.data)).toEqual([0.375]); + expect(result.data).not.toBe(singleton.data); + }); + + it('keeps a constant guide numerically constant for near-zero regularization', () => { + const guide = scalarImage(Array.from({ length: 3 }, () => Array(3).fill(0.1))); + const signal = scalarImage([ + [0, 1, 0], + [1, 0, 1], + [0, 1, 0], + ]); + const expected = [ + [40 / 81, 41 / 81, 40 / 81], + [41 / 81, 40 / 81, 41 / 81], + [40 / 81, 41 / 81, 40 / 81], + ]; + const actual = guidedFilter(guide, signal, 1, 1e-18); + + expect(Array.from(actual.data).every(Number.isFinite)).toBe(true); + expectImageClose(actual, expected, 1e-10); + }); + + it('matches the edge-aware band fixture where the fine scale has radius zero', () => { + const { lightness, inner_radius, outer_radius, epsilon, expected, atol } = fixtures.multiscale_band; + expectImageClose(edgeAwareBand(scalarImage(lightness), inner_radius, outer_radius, epsilon), expected, atol); + + expect(() => edgeAwareBand(scalarImage(lightness), 1, 1, epsilon)).toThrow(); + }); + + it('matches Oklab values for sRGB primaries and white, including black and channel endpoints', () => { + const { rgb, expected_oklab, atol } = fixtures.color_adapter; + const image = oklabFromSrgb(rgbImage(rgb)); + expect(image).toMatchObject({ width: expected_oklab[0].length, height: expected_oklab.length }); + expect(image.data).toHaveLength(expected_oklab.length * expected_oklab[0].length * 3); + expected_oklab.flat(2).forEach((value, i) => expect(Math.abs(image.data[i] - value)).toBeLessThanOrEqual(atol)); + + expect(Array.from(oklabFromSrgb({ width: 1, height: 1, data: new Uint8Array([0, 0, 0]) }).data)).toEqual([0, 0, 0]); + }); +}); + +describe('compact image buffer contract', () => { + it('uses row-major scalar buffers and preserves radius-zero identity values', () => { + const input = { width: 2, height: 2, data: new Float64Array([0.1, 0.2, 0.3, 0.4]) }; + const result = guidedFilter(input, input, 0, 0.1); + + expect(result).toMatchObject({ width: 2, height: 2 }); + expect(result.data).toBeInstanceOf(Float64Array); + expect(Array.from(result.data)).toEqual(Array.from(input.data)); + expect(result.data).not.toBe(input.data); + }); + + it('stores RGB channels interleaved in row-major order and returns compact Oklab data', () => { + const rgb = { width: 2, height: 1, data: new Uint8Array([255, 0, 0, 0, 255, 0]) }; + const result = oklabFromSrgb(rgb); + + expect(result).toMatchObject({ width: 2, height: 1 }); + expect(result.data).toBeInstanceOf(Float64Array); + expect(result.data).toHaveLength(6); + expect(Math.abs(result.data[0] - 0.62795536)).toBeLessThanOrEqual(0.00002); + expect(Math.abs(result.data[3] - 0.86643961)).toBeLessThanOrEqual(0.00002); + }); + + it('returns compact bands and rejects malformed buffer dimensions', () => { + const lightness = { width: 2, height: 2, data: new Float64Array([0.1, 0.2, 0.3, 0.4]) }; + const result = edgeAwareBand(lightness, 0, 1, 0.1); + expect(result.data).toBeInstanceOf(Float64Array); + expect(result.data).toHaveLength(4); + expect(() => guidedFilter({ ...lightness, data: new Float64Array(3) }, lightness, 1, 0.1)).toThrow(); + expect(() => oklabFromSrgb({ width: 2, height: 1, data: new Uint8Array(5) })).toThrow(); + }); +}); diff --git a/tests/app/invocations/test_image_manipulation_invocations.py b/tests/app/invocations/test_image_manipulation_invocations.py new file mode 100644 index 00000000000..3c10759952c --- /dev/null +++ b/tests/app/invocations/test_image_manipulation_invocations.py @@ -0,0 +1,256 @@ +"""First-party graph and execution contracts for image manipulation nodes.""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import Any + +import numpy as np +import pytest +from PIL import Image +from pydantic import ValidationError + +from invokeai.app.invocations import load_all_modules +from invokeai.app.invocations.baseinvocation import InvocationRegistry +from invokeai.app.invocations.fields import ImageField +from invokeai.app.invocations.primitives import ImageOutput +from invokeai.app.services.shared.graph import Graph, GraphExecutionState +from invokeai.backend.image_util.clarity import clarity +from invokeai.backend.image_util.contrast_dehaze import contrast_dehaze +from invokeai.backend.image_util.texture import texture + +NODE_CONTRACTS = ( + ("clarity", "Clarity", clarity, 40, (4, 1000), True), + ("texture", "Texture", texture, 8, (1, 100), True), + ("contrast_dehaze", "Contrast Dehaze (approximation)", contrast_dehaze, None, None, True), +) + + +def _registered_nodes() -> dict[str, type[Any]]: + load_all_modules() + return {node.get_type(): node for node in InvocationRegistry.get_invocation_classes()} + + +@pytest.fixture(scope="module") +def nodes() -> dict[str, type[Any]]: + return _registered_nodes() + + +@pytest.mark.parametrize(("node_id", "title", "effect", "size_default", "size_bounds", "metadata"), NODE_CONTRACTS) +def test_node_is_registered_with_stable_identity_and_defaults( + nodes: dict[str, type[Any]], + node_id: str, + title: str, + effect: Callable[..., Image.Image], + size_default: int | None, + size_bounds: tuple[int, int] | None, + metadata: bool, +) -> None: + del effect + node_class = nodes[node_id] + assert node_id in nodes + assert node_class.get_type() == node_id + assert node_class.UIConfig.title == title + assert node_class.UIConfig.tags + assert node_class.UIConfig.category == "image" + assert node_class.UIConfig.version == "1.0.0" + assert node_class.UIConfig.node_pack == "invokeai" + assert "image" in node_class.model_fields + assert node_class.model_fields["amount"].default == 0 + if size_default is not None: + assert node_class.model_fields["size"].default == size_default + assert node_class.model_fields["size"].metadata + lower, upper = size_bounds + for invalid_size in (lower - 1, upper + 1, float("nan")): + with pytest.raises(ValidationError): + node_class.model_validate({"image": {"image_name": "input"}, "size": invalid_size}) + assert ("board" in node_class.model_fields) is metadata + assert ("metadata" in node_class.model_fields) is metadata + if metadata: + node_class.model_validate( + { + "image": {"image_name": "input"}, + "board": {"board_id": "board-id"}, + "metadata": {}, + } + ) + + +@pytest.mark.parametrize(("node_id", "title", "effect", "size_default", "size_bounds", "metadata"), NODE_CONTRACTS) +def test_amount_bounds_and_non_finite_values_are_rejected( + nodes: dict[str, type[Any]], + node_id: str, + title: str, + effect: Callable[..., Image.Image], + size_default: int | None, + size_bounds: tuple[int, int] | None, + metadata: bool, +) -> None: + del title, effect, size_default, size_bounds, metadata + node_class = nodes[node_id] + for amount in (-100, 0, 100): + node_class.model_validate({"image": {"image_name": "input"}, "amount": amount}) + for amount in (-100.01, 100.01, float("nan"), float("inf"), -float("inf")): + with pytest.raises(ValidationError): + node_class.model_validate({"image": {"image_name": "input"}, "amount": amount}) + + +class _ImageStore: + """Small real PIL image store implementing the image service methods wrappers need.""" + + def __init__(self, source: Image.Image) -> None: + self.images = {"source": source.copy()} + self.saved: list[Image.Image] = [] + + def get_pil(self, image_name: str) -> Image.Image: + return self.images[image_name].copy() + + def save(self, image: Image.Image, **_: Any) -> Any: + name = f"saved-{len(self.saved)}" + self.saved.append(image.copy()) + self.images[name] = image.copy() + return type("ImageRecord", (), {"image_name": name, "width": image.width, "height": image.height})() + + +class _Context: + def __init__(self, images: _ImageStore) -> None: + self.images = images + + +def _source_image() -> Image.Image: + y, x = np.mgrid[:48, :64] + rgb = np.stack(((x * 7 + y * 3) % 256, (x * 2 + y * 9) % 256, (x * 11 + y * 5) % 256), axis=-1) + alpha = ((x * 3 + y * 5) % 256)[..., None] + rgba = np.concatenate((rgb, alpha), axis=-1) + return Image.fromarray(rgba.astype(np.uint8), mode="RGBA") + + +@pytest.mark.parametrize(("node_id", "title", "effect", "size_default", "size_bounds", "metadata"), NODE_CONTRACTS) +def test_wrapper_reads_applies_and_saves_once( + nodes: dict[str, type[Any]], + node_id: str, + title: str, + effect: Callable[..., Image.Image], + size_default: int | None, + size_bounds: tuple[int, int] | None, + metadata: bool, +) -> None: + del title, size_bounds, metadata + source = _source_image() + store = _ImageStore(source) + values: dict[str, Any] = {"image": ImageField(image_name="source"), "amount": 35} + if size_default is not None: + values["size"] = size_default + invocation = nodes[node_id](**values) + output = invocation.invoke(_Context(store)) + expected = effect(source, 35, size_default) if size_default is not None else effect(source, 35) + assert isinstance(output, ImageOutput) + assert (output.width, output.height) == source.size + assert len(store.saved) == 1 + assert output.image.image_name == "saved-0" + assert np.array_equal(np.asarray(store.saved[0]), np.asarray(expected)) + + +@pytest.mark.parametrize(("node_id", "title", "effect", "size_default", "size_bounds", "metadata"), NODE_CONTRACTS) +def test_zero_amount_saves_an_unchanged_image( + nodes: dict[str, type[Any]], + node_id: str, + title: str, + effect: Callable[..., Image.Image], + size_default: int | None, + size_bounds: tuple[int, int] | None, + metadata: bool, +) -> None: + del title, effect, size_bounds, metadata + source = _source_image() + store = _ImageStore(source) + values: dict[str, Any] = {"image": ImageField(image_name="source"), "amount": 0} + if size_default is not None: + values["size"] = size_default + output = nodes[node_id](**values).invoke(_Context(store)) + assert isinstance(output, ImageOutput) + assert len(store.saved) == 1 + assert np.array_equal(np.asarray(store.saved[0]), np.asarray(source)) + + +def test_image_manipulation_nodes_are_registered(nodes: dict[str, type[Any]]) -> None: + assert all(node_id in nodes for node_id, *_ in NODE_CONTRACTS) + + +def test_image_manipulation_graph_executes_with_connected_images(nodes: dict[str, type[Any]]) -> None: + source = _source_image() + store = _ImageStore(source) + graph = Graph.model_validate( + { + "nodes": { + "clarity": { + "id": "clarity", + "type": "clarity", + "image": {"image_name": "source"}, + "amount": -40, + "size": 12, + }, + "texture": { + "id": "texture", + "type": "texture", + "image": {"image_name": "unused"}, + "amount": 30, + "size": 6, + }, + "dehaze": { + "id": "dehaze", + "type": "contrast_dehaze", + "image": {"image_name": "unused"}, + "amount": -25, + }, + }, + "edges": [ + { + "source": {"node_id": "clarity", "field": "image"}, + "destination": {"node_id": "texture", "field": "image"}, + }, + { + "source": {"node_id": "texture", "field": "image"}, + "destination": {"node_id": "dehaze", "field": "image"}, + }, + ], + } + ) + graph.validate_self() + state = GraphExecutionState(graph=graph) + context = _Context(store) + completed: list[tuple[str, str]] = [] + + while (invocation := state.next()) is not None: + output = invocation.invoke(context) + assert isinstance(output, ImageOutput) + completed.append((state.prepared_source_mapping[invocation.id], output.image.image_name)) + state.complete(invocation.id, output) + + assert completed == [("clarity", "saved-0"), ("texture", "saved-1"), ("dehaze", "saved-2")] + assert state.is_complete() + assert len(store.saved) == 3 + expected = contrast_dehaze(texture(clarity(source, -40, 12), 30, 6), -25) + final_image = store.images["saved-2"] + assert (final_image.width, final_image.height) == source.size + assert final_image.mode == "RGBA" + assert np.array_equal(np.asarray(final_image), np.asarray(expected)) + assert np.array_equal(np.asarray(final_image.getchannel("A")), np.asarray(source.getchannel("A"))) + + +def test_graph_rejects_non_finite_image_manipulation_input(nodes: dict[str, type[Any]]) -> None: + del nodes + with pytest.raises(ValidationError): + Graph.model_validate( + { + "nodes": { + "clarity": { + "id": "clarity", + "type": "clarity", + "image": {"image_name": "source"}, + "amount": float("nan"), + } + }, + "edges": [], + } + ) diff --git a/tests/backend/image_util/test_clarity.py b/tests/backend/image_util/test_clarity.py new file mode 100644 index 00000000000..26a96de1488 --- /dev/null +++ b/tests/backend/image_util/test_clarity.py @@ -0,0 +1,175 @@ +from __future__ import annotations + +import cv2 +import numpy as np +import pytest +import torch +from PIL import Image + +from invokeai.backend.image_util.clarity import clarity +from invokeai.backend.image_util.color_conversion import oklab_from_srgb + + +def _gray_image(values: np.ndarray) -> Image.Image: + return Image.fromarray(np.asarray(values, dtype=np.uint8), mode="L") + + +def _gray_pattern(level: int, amplitude: int, shape: tuple[int, int] = (129, 129)) -> np.ndarray: + y, x = np.indices(shape) + pattern = ((x + y) % 2) * 2 - 1 + return np.clip(level + amplitude * pattern, 0, 255).astype(np.uint8) + + +def _gray_sinusoid(level: int, amplitude: int, wavelength: int, shape: tuple[int, int] = (129, 129)) -> np.ndarray: + _, x = np.indices(shape) + pattern = np.sin(2 * np.pi * x / wavelength) + return np.clip(np.rint(level + amplitude * pattern), 0, 255).astype(np.uint8) + + +def test_zero_amount_returns_an_exact_copy() -> None: + source = Image.fromarray( + np.array([[[255, 0, 10, 0], [2, 128, 254, 73]], [[8, 9, 10, 255], [11, 12, 13, 1]]], dtype=np.uint8), + mode="RGBA", + ) + + result = clarity(source, amount=0) + + assert result is not source + assert result.mode == source.mode + assert result.size == source.size + assert result.tobytes() == source.tobytes() + + +def test_zero_amount_skips_color_conversion(monkeypatch: pytest.MonkeyPatch) -> None: + source = Image.new("RGB", (3, 2), (24, 128, 222)) + + def fail_if_called(_: Image.Image) -> tuple[torch.Tensor, np.ndarray, np.ndarray | None]: + raise AssertionError("zero amount must skip image color conversion") + + monkeypatch.setattr("invokeai.backend.image_util.clarity.prepare_lightness", fail_if_called) + + result = clarity(source, amount=0) + + assert result is not source + assert result.tobytes() == source.tobytes() + + +def test_constant_color_is_preserved() -> None: + source = Image.new("RGB", (37, 29), (74, 121, 193)) + + result = clarity(source, amount=100, size=40) + + assert result.mode == source.mode + delta = np.abs(np.asarray(result, dtype=np.int16) - np.asarray(source, dtype=np.int16)) + assert int(delta.max()) <= 1 + + +def test_positive_and_negative_amount_separate_midtone_detail_in_opposite_directions() -> None: + source = _gray_image(_gray_sinusoid(level=128, amplitude=16, wavelength=16)) + center = (slice(32, -32), slice(32, -32)) + + positive = np.asarray(clarity(source, amount=100, size=12), dtype=np.float32) + negative = np.asarray(clarity(source, amount=-100, size=12), dtype=np.float32) + original = np.asarray(source, dtype=np.float32) + + positive_contrast = float(positive[center].std()) + negative_contrast = float(negative[center].std()) + source_contrast = float(original[center].std()) + assert positive_contrast > source_contrast + 1.0 + assert negative_contrast < source_contrast - 1.0 + + +def test_tone_gate_reduces_effect_near_black_and_white() -> None: + regions = np.concatenate( + [ + _gray_sinusoid(level=5, amplitude=16, wavelength=16), + _gray_sinusoid(level=128, amplitude=16, wavelength=16), + _gray_sinusoid(level=250, amplitude=16, wavelength=16), + ], + axis=1, + ) + source = _gray_image(regions) + + result = np.asarray(clarity(source, amount=100, size=12), dtype=np.float32) + original = regions.astype(np.float32) + dark_delta = float(np.mean(np.abs(result[32:-32, 32:97] - original[32:-32, 32:97]))) + mid_delta = float(np.mean(np.abs(result[32:-32, 161:226] - original[32:-32, 161:226]))) + bright_delta = float(np.mean(np.abs(result[32:-32, 290:355] - original[32:-32, 290:355]))) + + assert mid_delta > 1.0 + assert dark_delta < mid_delta * 0.2 + assert bright_delta < mid_delta * 0.2 + + +def test_size_is_measured_in_source_pixels_independent_of_canvas_dimensions() -> None: + small = np.zeros((129, 129), dtype=np.uint8) + large = np.zeros((257, 257), dtype=np.uint8) + small[64, 64] = 160 + large[128, 128] = 160 + + result_small = np.asarray(clarity(_gray_image(small), amount=100, size=20), dtype=np.int16) + result_large = np.asarray(clarity(_gray_image(large), amount=100, size=20), dtype=np.int16) + + np.testing.assert_allclose(result_small[44:85, 44:85], result_large[108:149, 108:149], atol=1) + + +def test_clarity_changes_lightness_without_introducing_chroma_detail() -> None: + _, x = np.indices((129, 129)) + levels = (112.0 + 32.0 * np.sin(2 * np.pi * x / 24)).astype(np.float32) / 255.0 + # Keep a single hue while varying lightness so the test can detect color fringes. + rgb = np.stack((levels, levels * 0.62, levels * 0.43), axis=-1) + source = Image.fromarray(np.round(rgb * 255).astype(np.uint8), mode="RGB") + + result = clarity(source, amount=100, size=12) + source_tensor = torch.from_numpy(np.asarray(source).copy()).permute(2, 0, 1).float() / 255.0 + result_tensor = torch.from_numpy(np.asarray(result).copy()).permute(2, 0, 1).float() / 255.0 + source_oklab = oklab_from_srgb(source_tensor) + result_oklab = oklab_from_srgb(result_tensor) + interior = (slice(32, -32), slice(32, -32)) + chroma_delta = torch.linalg.vector_norm( + result_oklab[1:, interior[0], interior[1]] - source_oklab[1:, interior[0], interior[1]], dim=0 + ) + lightness_delta = result_oklab[0, interior[0], interior[1]] - source_oklab[0, interior[0], interior[1]] + + assert float(lightness_delta.abs().mean()) > 0.005 + assert float(chroma_delta.max()) < 0.015 + + +def test_alpha_and_hidden_rgb_are_preserved() -> None: + rgba = np.zeros((65, 65, 4), dtype=np.uint8) + rgba[:, :, :3] = np.asarray(_gray_pattern(128, 6, (65, 65)))[..., None] + rgba[:, :, 3] = 255 + rgba[20:25, 20:25, :3] = (255, 0, 255) + rgba[20:25, 20:25, 3] = 0 + source = Image.fromarray(rgba, mode="RGBA") + + result = np.asarray(clarity(source, amount=100, size=12)) + + np.testing.assert_array_equal(result[:, :, 3], rgba[:, :, 3]) + np.testing.assert_array_equal(result[rgba[:, :, 3] == 0], rgba[rgba[:, :, 3] == 0]) + + +def test_sharp_edge_has_less_overshoot_than_large_radius_gaussian_unsharp_mask() -> None: + edge = np.full((129, 129), 112, dtype=np.uint8) + edge[:, 64:] = 144 + source = _gray_image(edge) + source_float = edge.astype(np.float32) / 255.0 + blurred = cv2.GaussianBlur(source_float, (0, 0), sigmaX=20, sigmaY=20, borderType=cv2.BORDER_REFLECT_101) + gaussian_unsharp = source_float + (source_float - blurred) + + clarity_result = np.asarray(clarity(source, amount=100, size=40), dtype=np.float32) / 255.0 + interior = (slice(32, -32), slice(32, -32)) + lower, upper = float(source_float.min()), float(source_float.max()) + clarity_overshoot = max( + lower - float(clarity_result[interior].min()), + float(clarity_result[interior].max()) - upper, + 0.0, + ) + gaussian_overshoot = max( + lower - float(gaussian_unsharp[interior].min()), + float(gaussian_unsharp[interior].max()) - upper, + 0.0, + ) + + assert gaussian_overshoot > 0.02 + assert clarity_overshoot < gaussian_overshoot * 0.5 diff --git a/tests/backend/image_util/test_color_adapter.py b/tests/backend/image_util/test_color_adapter.py new file mode 100644 index 00000000000..637e2cbe9d2 --- /dev/null +++ b/tests/backend/image_util/test_color_adapter.py @@ -0,0 +1,265 @@ +from __future__ import annotations + +import numpy as np +import pytest +import torch +from PIL import Image + +from invokeai.backend.image_util import color +from invokeai.backend.image_util.color_conversion import ( + gamut_clip_tensor, + linear_srgb_from_oklab, + linear_srgb_from_srgb, + oklab_from_linear_srgb, + oklab_from_srgb, + srgb_from_linear_srgb, +) + + +def _image(mode: str, size: tuple[int, int] = (3, 2)) -> Image.Image: + width, height = size + rgb = np.arange(width * height * 3, dtype=np.uint8).reshape(height, width, 3) * 11 + if mode == "RGB": + return Image.fromarray(rgb) + if mode == "RGBA": + alpha = np.arange(width * height, dtype=np.uint8).reshape(height, width) * 37 + return Image.fromarray(np.dstack((rgb, alpha))) + if mode == "L": + return Image.fromarray(rgb[..., 0]) + if mode == "LA": + alpha = np.arange(width * height, dtype=np.uint8).reshape(height, width) * 37 + return Image.fromarray(np.dstack((rgb[..., 0], alpha)), mode="LA") + raise AssertionError(mode) + + +@pytest.mark.parametrize("mode", ["RGB", "RGBA", "L", "LA"]) +def test_prepare_restore_round_trip_preserves_mode_size_and_alpha(mode: str) -> None: + source = _image(mode) + + color_tensor, lightness, alpha = color.prepare_lightness(source) + result = color.restore_lightness(source, color_tensor, lightness, alpha) + + assert result.mode == source.mode + assert result.size == source.size + if "A" in source.getbands(): + assert np.array_equal(np.asarray(result.getchannel("A")), np.asarray(source.getchannel("A"))) + assert ( + np.max( + np.abs( + np.asarray(result.convert("RGB"), dtype=np.int16) - np.asarray(source.convert("RGB"), dtype=np.int16) + ) + ) + <= 1 + ) + + +@pytest.mark.parametrize("mode", ["RGB", "RGBA", "L", "LA"]) +def test_prepare_returns_finite_contiguous_cpu_rgb_tensor_and_lightness(mode: str) -> None: + source = _image(mode, (7, 5)) + + color_tensor, lightness, _ = color.prepare_lightness(source) + + assert color_tensor.device.type == "cpu" + assert color_tensor.dtype == torch.float32 + assert color_tensor.shape == (3, 5, 7) + assert color_tensor.is_contiguous() + assert lightness.shape == (5, 7) + assert lightness.dtype == np.float32 + assert lightness.flags.c_contiguous + assert np.isfinite(lightness).all() + assert np.isfinite(color_tensor.numpy()).all() + + +def test_prepare_uses_core_oklab_conversion_with_rgb_channel_order() -> None: + source = Image.new("RGB", (1, 1), (11, 73, 201)) + rgb_chw = torch.tensor([11.0, 73.0, 201.0], dtype=torch.float32).view(3, 1, 1) / 255.0 + expected_oklab = oklab_from_srgb(rgb_chw) + + color_tensor, _, _ = color.prepare_lightness(source) + + assert torch.allclose(color_tensor, expected_oklab, atol=1e-6) + + +@pytest.mark.parametrize("size", [(1, 1), (5, 3), (257, 1), (1, 257)]) +def test_prepare_restore_supports_tiny_odd_ultrawide_and_tall_images(size: tuple[int, int]) -> None: + source = _image("RGBA", size) + + color_tensor, lightness, alpha = color.prepare_lightness(source) + result = color.restore_lightness(source, color_tensor, lightness, alpha) + + assert result.size == source.size + assert np.isfinite(lightness).all() + assert np.array_equal(np.asarray(result.getchannel("A")), np.asarray(source.getchannel("A"))) + source_rgba = np.asarray(source) + result_rgba = np.asarray(result) + visible = source_rgba[..., 3] > 0 + hidden = ~visible + if visible.any(): + assert ( + np.max(np.abs(result_rgba[visible, :3].astype(np.int16) - source_rgba[visible, :3].astype(np.int16))) <= 1 + ) + assert np.array_equal(result_rgba[hidden], source_rgba[hidden]) + + +def test_restore_isolates_hidden_rgba_rgb_from_visible_pixels() -> None: + visible = np.array([[[220, 30, 10, 255], [0, 0, 0, 0], [25, 100, 210, 255]]], dtype=np.uint8) + hidden_variant = visible.copy() + hidden_variant[0, 1, :3] = [255, 0, 255] + first = Image.fromarray(visible, mode="RGBA") + second = Image.fromarray(hidden_variant, mode="RGBA") + + prepared = [] + restored = [] + for source in (first, second): + color_tensor, lightness, alpha = color.prepare_lightness(source) + prepared.append(lightness) + restored.append(color.restore_lightness(source, color_tensor, lightness, alpha)) + + assert np.array_equal(prepared[0], prepared[1]) + assert np.array_equal(np.asarray(restored[0])[0, [0, 2]], np.asarray(restored[1])[0, [0, 2]]) + assert np.array_equal(np.asarray(restored[0])[0, 1], visible[0, 1]) + assert np.array_equal(np.asarray(restored[1])[0, 1], hidden_variant[0, 1]) + + +def test_prepare_prefills_transparent_lightness_from_nearest_visible_pixel() -> None: + transparent_black = np.array([[[255, 255, 255, 255], [0, 0, 0, 0], [255, 255, 255, 255]]], dtype=np.uint8) + transparent_magenta = transparent_black.copy() + transparent_magenta[0, 1, :3] = [255, 0, 255] + + first = color.prepare_lightness(Image.fromarray(transparent_black, mode="RGBA"))[1] + second = color.prepare_lightness(Image.fromarray(transparent_magenta, mode="RGBA"))[1] + + assert first[0, 1] == pytest.approx(first[0, 0], abs=1e-6) + assert np.array_equal(first, second) + + +def test_restore_uses_core_gamut_mapper_and_measures_chroma_shift() -> None: + source = Image.new("RGB", (1, 1), (255, 80, 20)) + color_tensor, lightness, alpha = color.prepare_lightness(source) + adjusted = lightness.copy() + adjusted[0, 0] = 0.5 + + expected_oklab = color_tensor.clone() + expected_oklab[0, 0, 0] = 0.5 + out_of_gamut = linear_srgb_from_oklab(expected_oklab) + assert torch.any((out_of_gamut < 0.0) | (out_of_gamut > 1.0)) + gamut_mapped = gamut_clip_tensor(out_of_gamut) + expected_rgb = (srgb_from_linear_srgb(gamut_mapped).clamp(0.0, 1.0) * 255.0).round().to(torch.uint8) + + result = color.restore_lightness(source, color_tensor, adjusted, alpha) + + pixels = np.asarray(result.convert("RGB"), dtype=np.float32) / 255.0 + actual_rgb = torch.from_numpy(pixels.copy()).permute(2, 0, 1).contiguous() + actual_oklab = oklab_from_srgb(actual_rgb) + raw_chroma = torch.linalg.vector_norm(expected_oklab[1:, 0, 0]) + mapped_chroma = torch.linalg.vector_norm(actual_oklab[1:, 0, 0]) + raw_hue = torch.atan2(expected_oklab[2, 0, 0], expected_oklab[1, 0, 0]) + mapped_hue = torch.atan2(actual_oklab[2, 0, 0], actual_oklab[1, 0, 0]) + hue_delta = torch.remainder(mapped_hue - raw_hue + torch.pi, 2.0 * torch.pi) - torch.pi + + assert result.mode == "RGB" + assert np.isfinite(pixels).all() + assert np.all((pixels >= 0.0) & (pixels <= 1.0)) + assert torch.equal(torch.from_numpy(np.asarray(result).copy()).permute(2, 0, 1), expected_rgb) + assert mapped_chroma < raw_chroma + assert raw_chroma - mapped_chroma == pytest.approx(0.0508, abs=0.002) + assert abs(hue_delta.item()) < 0.003 + + +def test_restore_clips_out_of_range_lightness_before_color_conversion(monkeypatch: pytest.MonkeyPatch) -> None: + source = Image.new("RGB", (2, 1), (128, 128, 128)) + color_tensor, lightness, alpha = color.prepare_lightness(source) + lightness[:] = [-0.25, 1.25] + converted_lightness: list[torch.Tensor] = [] + convert = color.linear_srgb_from_oklab + + def capture_conversion(oklab: torch.Tensor) -> torch.Tensor: + converted_lightness.append(oklab[0].clone()) + return convert(oklab) + + monkeypatch.setattr(color, "linear_srgb_from_oklab", capture_conversion) + + color.restore_lightness(source, color_tensor, lightness, alpha) + + assert len(converted_lightness) == 1 + assert torch.equal(converted_lightness[0], torch.tensor([[0.0, 1.0]], dtype=torch.float32)) + + +def test_restore_converts_large_images_in_bounded_row_tiles(monkeypatch: pytest.MonkeyPatch) -> None: + source = Image.new("RGB", (8, 600), (128, 128, 128)) + color_tensor, lightness, alpha = color.prepare_lightness(source) + original_color_tensor = color_tensor.clone() + converted_shapes: list[tuple[int, ...]] = [] + convert = color.linear_srgb_from_oklab + + def capture_conversion(oklab: torch.Tensor) -> torch.Tensor: + converted_shapes.append(tuple(oklab.shape)) + return convert(oklab) + + monkeypatch.setattr(color, "linear_srgb_from_oklab", capture_conversion) + + result = color.restore_lightness(source, color_tensor, lightness, alpha) + + assert result.size == source.size + assert converted_shapes + assert all(shape[1] <= 256 for shape in converted_shapes) + assert sum(shape[1] for shape in converted_shapes) == source.height + assert torch.equal(color_tensor, original_color_tensor) + + +@pytest.mark.parametrize("invalid_input", ["color_tensor", "lightness"]) +def test_restore_rejects_non_finite_tile_inputs(invalid_input: str) -> None: + source = Image.new("RGB", (2, 300), (128, 128, 128)) + color_tensor, lightness, alpha = color.prepare_lightness(source) + if invalid_input == "color_tensor": + color_tensor[0, 257, 0] = float("nan") + else: + lightness[257, 0] = np.nan + + with pytest.raises(ValueError, match="finite"): + color.restore_lightness(source, color_tensor, lightness, alpha) + + +def test_core_oklab_gamut_clip_reduces_out_of_gamut_chroma_without_nonfinite_values() -> None: + red = torch.tensor([[[1.0]], [[0.0]], [[0.0]]], dtype=torch.float32) + oklab = oklab_from_linear_srgb(linear_srgb_from_srgb(red)) + oklab[1:] *= 2.0 + out_of_gamut = linear_srgb_from_oklab(oklab) + + clipped = gamut_clip_tensor(out_of_gamut) + clipped_oklab = oklab_from_linear_srgb(clipped) + + assert torch.any((out_of_gamut < 0.0) | (out_of_gamut > 1.0)) + assert torch.isfinite(clipped).all() + assert torch.all((clipped >= 0.0) & (clipped <= 1.0)) + assert torch.linalg.vector_norm(clipped_oklab[1:, 0, 0]) < torch.linalg.vector_norm(oklab[1:, 0, 0]) + + +def test_prepare_rejects_16_bit_gray_without_saturating_through_pillow_rgb_conversion() -> None: + values = np.array([[0, 32768, 65535]], dtype=np.uint16) + source = Image.fromarray(values) + assert source.mode == "I;16" + + # Reject rather than treating Pillow's saturating RGB conversion as a faithful decode. + with pytest.raises(ValueError, match="16-bit|I;16|unsupported mode"): + color.prepare_lightness(source) + + +@pytest.mark.parametrize("mode", ["CMYK", "F", "P"]) +def test_prepare_rejects_unusual_modes_explicitly(mode: str) -> None: + source = Image.new(mode, (2, 2)) + + with pytest.raises(ValueError, match="unsupported|mode"): + color.prepare_lightness(source) + + +def test_prepare_reports_clear_error_when_memory_allocation_fails(monkeypatch: pytest.MonkeyPatch) -> None: + source = _image("RGB") + + def fail_allocation(*args: object, **kwargs: object) -> None: + raise MemoryError + + monkeypatch.setattr(color.torch, "from_numpy", fail_allocation) + + with pytest.raises(RuntimeError, match="memory|allocate|image"): + color.prepare_lightness(source) diff --git a/tests/backend/image_util/test_contrast_dehaze.py b/tests/backend/image_util/test_contrast_dehaze.py new file mode 100644 index 00000000000..1ffde51b1f9 --- /dev/null +++ b/tests/backend/image_util/test_contrast_dehaze.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +import numpy as np +import pytest +import torch +from PIL import Image + +from invokeai.backend.image_util.color_conversion import oklab_from_srgb +from invokeai.backend.image_util.contrast_dehaze import contrast_dehaze + + +def _veiled_gray_bands() -> Image.Image: + """Broad image regions with reduced luminance separation, as from a light veil.""" + levels = np.array([112, 122, 132, 142], dtype=np.uint8) + luminance = np.broadcast_to(np.repeat(levels, 32)[None, :], (96, 128)) + return Image.fromarray(np.repeat(luminance[..., None], 3, axis=2), mode="RGB") + + +def _broad_luminance_spread(image: Image.Image) -> float: + grayscale = np.asarray(image.convert("L"), dtype=np.float32) + return float(np.percentile(grayscale, 90) - np.percentile(grayscale, 10)) + + +def _mean_oklab_chroma(image: Image.Image) -> float: + srgb = torch.from_numpy(np.asarray(image.convert("RGB")).copy()).permute(2, 0, 1).float() / 255.0 + oklab = oklab_from_srgb(srgb) + return float(torch.linalg.vector_norm(oklab[1:], dim=0).mean()) + + +def test_zero_amount_returns_an_exact_copy() -> None: + source = Image.fromarray( + np.array([[[255, 0, 10, 0], [2, 128, 254, 73]], [[8, 9, 10, 255], [11, 12, 13, 1]]], dtype=np.uint8), + mode="RGBA", + ) + + result = contrast_dehaze(source, amount=0) + + assert result is not source + assert result.mode == source.mode + assert result.size == source.size + assert result.tobytes() == source.tobytes() + + +def test_positive_amount_increases_broad_scale_luminance_separation() -> None: + source = _veiled_gray_bands() + + result = contrast_dehaze(source, amount=60) + + assert _broad_luminance_spread(result) > _broad_luminance_spread(source) + 2.0 + + +def test_negative_amount_reduces_broad_scale_separation_like_a_veil() -> None: + source = _veiled_gray_bands() + + result = contrast_dehaze(source, amount=-60) + + assert _broad_luminance_spread(result) < _broad_luminance_spread(source) - 2.0 + + +def test_amount_moves_chroma_in_the_same_direction_on_a_safe_color_patch() -> None: + source = Image.new("RGB", (24, 24), (130, 90, 70)) + source_chroma = _mean_oklab_chroma(source) + + positive_chroma = _mean_oklab_chroma(contrast_dehaze(source, amount=60)) + negative_chroma = _mean_oklab_chroma(contrast_dehaze(source, amount=-60)) + + assert positive_chroma > source_chroma + assert negative_chroma < source_chroma + + +@pytest.mark.parametrize("mode", ["RGB", "RGBA", "L", "LA"]) +def test_supported_modes_keep_mode_size_and_exact_alpha(mode: str) -> None: + gray = np.broadcast_to(np.tile(np.array([90, 120, 150], dtype=np.uint8), 11)[:32], (24, 32)) + if mode in {"RGB", "RGBA"}: + rgb = np.repeat(gray[..., None], 3, axis=2) + if mode == "RGB": + source = Image.fromarray(rgb, mode=mode) + else: + alpha = np.tile(np.array([0, 64, 255], dtype=np.uint8), (24, 11))[:, :32] + source = Image.fromarray(np.dstack((rgb, alpha)), mode=mode) + elif mode == "L": + source = Image.fromarray(gray, mode=mode) + else: + alpha = np.tile(np.array([0, 64, 255], dtype=np.uint8), (24, 11))[:, :32] + source = Image.fromarray(np.stack((gray, alpha), axis=-1), mode=mode) + + source_bytes = source.tobytes() + result = contrast_dehaze(source, amount=50) + + assert source.tobytes() == source_bytes + assert result.mode == source.mode + assert result.size == source.size + if "A" in source.getbands(): + np.testing.assert_array_equal(np.asarray(result.getchannel("A")), np.asarray(source.getchannel("A"))) + + +def test_transparent_pixels_keep_their_hidden_color() -> None: + pixels = np.empty((16, 16, 4), dtype=np.uint8) + pixels[..., :3] = (110, 125, 140) + pixels[..., 3] = 255 + pixels[4:9, 5:10, :3] = (255, 0, 211) + pixels[4:9, 5:10, 3] = 0 + source = Image.fromarray(pixels, mode="RGBA") + + result = np.asarray(contrast_dehaze(source, amount=70)) + + np.testing.assert_array_equal(result[pixels[..., 3] == 0], pixels[pixels[..., 3] == 0]) + + +@pytest.mark.parametrize("amount", [float("nan"), float("inf"), float("-inf"), -100.1, 100.1, True]) +def test_invalid_amount_is_rejected(amount: float) -> None: + with pytest.raises(ValueError): + contrast_dehaze(Image.new("RGB", (3, 2), (120, 130, 140)), amount=amount) + + +def test_output_is_finite_and_stays_within_byte_bounds() -> None: + source = Image.fromarray( + np.array( + [ + [[0, 255, 8], [255, 0, 247], [1, 128, 254]], + [[250, 2, 0], [5, 250, 255], [128, 127, 126]], + ], + dtype=np.uint8, + ), + mode="RGB", + ) + + result = contrast_dehaze(source, amount=100) + pixels = np.asarray(result) + + assert np.isfinite(pixels).all() + assert int(pixels.min()) >= 0 + assert int(pixels.max()) <= 255 + + +def test_core_memory_error_is_reported_as_a_clear_runtime_error(monkeypatch: pytest.MonkeyPatch) -> None: + def raise_memory_error(*args: object, **kwargs: object) -> np.ndarray: + raise MemoryError("allocation failed") + + monkeypatch.setattr("invokeai.backend.image_util.contrast_dehaze.edge_aware_band", raise_memory_error) + + with pytest.raises(RuntimeError, match="(?i)(memory|image)"): + contrast_dehaze(_veiled_gray_bands(), amount=60) diff --git a/tests/backend/image_util/test_guided.py b/tests/backend/image_util/test_guided.py new file mode 100644 index 00000000000..70387c1847a --- /dev/null +++ b/tests/backend/image_util/test_guided.py @@ -0,0 +1,89 @@ +import cv2 +import numpy as np +import pytest + +from invokeai.backend.image_util.guided_filter import guided_filter + + +def test_guided_filter_preserves_constant_signal() -> None: + guide = np.arange(35, dtype=np.float32).reshape(5, 7) + signal = np.full((5, 7), 0.375, dtype=np.float32) + + result = guided_filter(guide, signal, radius=2, epsilon=1e-3) + + np.testing.assert_allclose(result, signal, atol=1e-6) + + +def test_guided_filter_clamps_negative_float32_variance() -> None: + rng = np.random.default_rng(12) + guide = (0.5 + rng.standard_normal((5, 7)).astype(np.float32) * 1e-6).astype(np.float32) + signal = rng.random((5, 7), dtype=np.float32) + + mean_guide = cv2.boxFilter(guide, ddepth=-1, ksize=(5, 5), normalize=True, borderType=cv2.BORDER_REFLECT_101) + raw_variance = ( + cv2.boxFilter(guide * guide, ddepth=-1, ksize=(5, 5), normalize=True, borderType=cv2.BORDER_REFLECT_101) + - mean_guide * mean_guide + ) + assert float(raw_variance.min()) < 0 + epsilon = float(-raw_variance.min() + 1e-12) + + first_mean = cv2.boxFilter(signal, ddepth=-1, ksize=(5, 5), normalize=True, borderType=cv2.BORDER_REFLECT_101) + expected = cv2.boxFilter(first_mean, ddepth=-1, ksize=(5, 5), normalize=True, borderType=cv2.BORDER_REFLECT_101) + result = guided_filter(guide, signal, radius=2, epsilon=epsilon) + + np.testing.assert_allclose(result, expected, atol=1e-4) + + +def test_guided_filter_smooths_noise_without_blurring_a_step_edge() -> None: + guide = np.zeros((15, 15), dtype=np.float32) + guide[:, 7:] = 1.0 + noise = ((np.indices(guide.shape).sum(axis=0) % 2) * 2 - 1).astype(np.float32) * 0.2 + signal = guide + noise + + result = guided_filter(guide, signal, radius=2, epsilon=1e-2) + + assert np.mean((result - guide) ** 2) < np.mean((signal - guide) ** 2) + assert float(result[:, 7].mean() - result[:, 6].mean()) > 0.7 + + +def test_guided_filter_uses_reflect_101_at_borders() -> None: + guide = np.ones((2, 3), dtype=np.float32) + signal = np.array([[0.0, 1.0, 4.0], [2.0, 8.0, 3.0]], dtype=np.float32) + + first_mean = cv2.boxFilter(signal, ddepth=-1, ksize=(3, 3), normalize=True, borderType=cv2.BORDER_REFLECT_101) + expected = cv2.boxFilter(first_mean, ddepth=-1, ksize=(3, 3), normalize=True, borderType=cv2.BORDER_REFLECT_101) + result = guided_filter(guide, signal, radius=1, epsilon=1e-3) + + np.testing.assert_allclose(result, expected, atol=1e-5) + + +@pytest.mark.parametrize("shape", [(1, 1), (1, 7), (6, 1), (2, 3), (5, 7)]) +def test_guided_filter_handles_degenerate_and_odd_shapes(shape: tuple[int, int]) -> None: + guide = np.arange(np.prod(shape), dtype=np.float32).reshape(shape) + signal = np.flip(guide, axis=1).copy() / max(1, guide.size) + + result = guided_filter(guide, signal, radius=2, epsilon=1e-3) + + assert result.shape == shape + assert result.dtype == np.float32 + assert np.isfinite(result).all() + + +def test_guided_filter_radius_zero_is_identity() -> None: + guide = np.array([[0.0, 1.0], [3.0, -2.0]], dtype=np.float32) + signal = np.array([[4.0, 0.5], [-1.0, 7.0]], dtype=np.float32) + + result = guided_filter(guide, signal, radius=0, epsilon=0.25) + + np.testing.assert_array_equal(result, signal) + + +@pytest.mark.parametrize( + ("radius", "epsilon"), + [(-1, 1e-3), (1, 0.0), (1, -1e-3)], +) +def test_guided_filter_rejects_out_of_range_parameters(radius: int, epsilon: float) -> None: + image = np.ones((3, 3), dtype=np.float32) + + with pytest.raises(ValueError): + guided_filter(image, image, radius=radius, epsilon=epsilon) diff --git a/tests/backend/image_util/test_multiscale.py b/tests/backend/image_util/test_multiscale.py new file mode 100644 index 00000000000..7c045bbd31b --- /dev/null +++ b/tests/backend/image_util/test_multiscale.py @@ -0,0 +1,66 @@ +import numpy as np +import pytest + +from invokeai.backend.image_util.multiscale import edge_aware_band + + +def test_edge_aware_band_of_constant_lightness_is_zero() -> None: + lightness = np.full((9, 13), 0.42, dtype=np.float32) + + band = edge_aware_band(lightness, inner_radius=1, outer_radius=5, epsilon=1e-3) + + np.testing.assert_allclose(band, 0, atol=1e-6) + + +def test_edge_aware_band_is_deterministic_and_finite() -> None: + y, x = np.indices((19, 27)) + lightness = (0.5 + 0.2 * np.sin(x * 0.7) + 0.1 * np.cos(y * 0.4)).astype(np.float32) + + first = edge_aware_band(lightness, inner_radius=1, outer_radius=6, epsilon=1e-2) + second = edge_aware_band(lightness, inner_radius=1, outer_radius=6, epsilon=1e-2) + + assert first.shape == lightness.shape + assert np.isfinite(first).all() + np.testing.assert_array_equal(first, second) + + +@pytest.mark.parametrize(("polarity", "expected_sign"), [(1.0, 1.0), (-1.0, -1.0)]) +def test_edge_aware_band_sign_tracks_bright_and_dark_impulses(polarity: float, expected_sign: float) -> None: + lightness = np.zeros((41, 41), dtype=np.float32) + lightness[20, 20] = polarity + + band = edge_aware_band(lightness, inner_radius=1, outer_radius=5, epsilon=0.1) + + assert expected_sign * float(band[20, 20]) > 0 + + +def test_edge_aware_band_responds_to_the_requested_scales() -> None: + y, x = np.indices((73, 193)) + + def band_energy(period: int, inner_radius: int, outer_radius: int) -> float: + lightness = np.sin(2 * np.pi * x / period).astype(np.float32) + band = edge_aware_band( + lightness, + inner_radius=inner_radius, + outer_radius=outer_radius, + epsilon=0.03, + ) + return float(np.sqrt(np.mean(band[:, 32:-32] ** 2))) + + fine_feature_at_fine_scale = band_energy(period=8, inner_radius=1, outer_radius=6) + same_feature_at_coarser_scale = band_energy(period=8, inner_radius=3, outer_radius=10) + + assert fine_feature_at_fine_scale > 1e-3 + assert fine_feature_at_fine_scale > 5 * same_feature_at_coarser_scale + + +@pytest.mark.parametrize("shape", [(1, 1), (2, 3), (5, 7), (9, 13), (3, 257)]) +def test_edge_aware_band_handles_tiny_odd_and_ultrawide_inputs(shape: tuple[int, int]) -> None: + lightness = np.zeros(shape, dtype=np.float32) + lightness[0, 0] = 1 + lightness[-1, -1] = -0.5 + + band = edge_aware_band(lightness, inner_radius=2, outer_radius=7, epsilon=1e-2) + + assert band.shape == shape + assert np.isfinite(band).all() diff --git a/tests/backend/image_util/test_shared_numerical_fixtures.py b/tests/backend/image_util/test_shared_numerical_fixtures.py new file mode 100644 index 00000000000..94d3ec21936 --- /dev/null +++ b/tests/backend/image_util/test_shared_numerical_fixtures.py @@ -0,0 +1,48 @@ +import json +from pathlib import Path + +import numpy as np +import torch +from PIL import Image + +from invokeai.backend.image_util.color import prepare_lightness +from invokeai.backend.image_util.guided_filter import guided_filter +from invokeai.backend.image_util.multiscale import edge_aware_band + +REPO_ROOT = Path(__file__).resolve().parents[3] +FIXTURE_PATH = ( + REPO_ROOT + / "invokeai/frontend/webv2/src/features/generation/core/__fixtures__/shared_image_manipulation_numerics.json" +) + + +def test_shared_numerical_fixtures_match_python_cores() -> None: + fixtures = json.loads(FIXTURE_PATH.read_text(encoding="utf-8")) + + guided = fixtures["guided_filter"] + guided_result = guided_filter( + np.asarray(guided["guide"], dtype=np.float32), + np.asarray(guided["signal"], dtype=np.float32), + radius=guided["radius"], + epsilon=guided["epsilon"], + ) + np.testing.assert_allclose(guided_result, guided["expected"], atol=guided["atol"], rtol=0) + + multiscale = fixtures["multiscale_band"] + band_result = edge_aware_band( + np.asarray(multiscale["lightness"], dtype=np.float32), + inner_radius=multiscale["inner_radius"], + outer_radius=multiscale["outer_radius"], + epsilon=multiscale["epsilon"], + ) + np.testing.assert_allclose(band_result, multiscale["expected"], atol=multiscale["atol"], rtol=0) + + adapter = fixtures["color_adapter"] + source = Image.fromarray(np.asarray(adapter["rgb"], dtype=np.uint8), mode="RGB") + color_tensor, lightness, alpha = prepare_lightness(source) + assert alpha is None + np.testing.assert_allclose( + color_tensor.numpy().transpose(1, 2, 0), adapter["expected_oklab"], atol=adapter["atol"], rtol=0 + ) + np.testing.assert_allclose(lightness, np.asarray(adapter["expected_oklab"])[:, :, 0], atol=adapter["atol"], rtol=0) + assert color_tensor.dtype == torch.float32 diff --git a/tests/backend/image_util/test_texture.py b/tests/backend/image_util/test_texture.py new file mode 100644 index 00000000000..34854aab8ff --- /dev/null +++ b/tests/backend/image_util/test_texture.py @@ -0,0 +1,183 @@ +from __future__ import annotations + +import numpy as np +import pytest +from PIL import Image + +from invokeai.backend.image_util.clarity import clarity +from invokeai.backend.image_util.texture import texture + + +def _gray_image(values: np.ndarray) -> Image.Image: + return Image.fromarray(np.asarray(values, dtype=np.uint8), mode="L") + + +def _gray_sinusoid( + wavelength: int, + amplitude: float = 20, + level: float = 128, + shape: tuple[int, int] = (257, 257), +) -> np.ndarray: + _, x = np.indices(shape) + pattern = np.sin(2 * np.pi * x / wavelength) + return np.clip(np.rint(level + amplitude * pattern), 0, 255).astype(np.uint8) + + +def _interior(values: np.ndarray, border: int = 48) -> np.ndarray: + return values[border:-border, border:-border] + + +def _sinusoid_amplitude(values: np.ndarray, wavelength: int, border: int = 48) -> float: + """Measure one repeated spatial frequency, ignoring the image border.""" + region = _interior(np.asarray(values, dtype=np.float32), border=border) + _, x = np.indices(region.shape) + angle = 2 * np.pi * x / wavelength + design = np.column_stack((np.ones(region.size), np.sin(angle).ravel(), np.cos(angle).ravel())) + coefficients = np.linalg.lstsq(design, region.ravel(), rcond=None)[0] + return float(np.hypot(coefficients[1], coefficients[2])) + + +def test_zero_amount_returns_an_exact_copy() -> None: + source = Image.fromarray( + np.array([[[255, 0, 10, 0], [2, 128, 254, 73]], [[8, 9, 10, 255], [11, 12, 13, 1]]], dtype=np.uint8), + mode="RGBA", + ) + + result = texture(source, amount=0) + + assert result is not source + assert result.mode == source.mode + assert result.size == source.size + assert result.tobytes() == source.tobytes() + + +def test_zero_amount_copies_without_requiring_a_supported_filter_mode() -> None: + source = Image.new("CMYK", (3, 2), (10, 20, 30, 40)) + + result = texture(source, amount=0) + + assert result is not source + assert result.mode == source.mode + assert result.tobytes() == source.tobytes() + + +def test_constant_color_is_preserved() -> None: + source = Image.new("RGB", (37, 29), (74, 121, 193)) + + result = texture(source, amount=100, size=8) + + assert result.mode == source.mode + delta = np.abs(np.asarray(result, dtype=np.int16) - np.asarray(source, dtype=np.int16)) + assert int(delta.max()) <= 1 + + +def test_positive_texture_gates_low_level_noise_in_smooth_regions() -> None: + fine_texture = _gray_sinusoid(wavelength=24, amplitude=20, shape=(257, 257)) + smooth_noise = _gray_sinusoid(wavelength=24, amplitude=1, shape=(257, 257)) + source = _gray_image(np.concatenate((fine_texture, smooth_noise), axis=1)) + + result = np.asarray(texture(source, amount=100, size=8), dtype=np.float32) + original = np.asarray(source, dtype=np.float32) + texture_delta = float(np.mean(np.abs(result[48:-48, 48:209] - original[48:-48, 48:209]))) + noise_delta = float(np.mean(np.abs(result[48:-48, 305:466] - original[48:-48, 305:466]))) + + assert texture_delta > 1.0 + assert noise_delta < texture_delta * 0.2 + + +def test_negative_texture_softens_fine_detail_without_blurring_broad_detail() -> None: + _, x = np.indices((512, 512)) + values = np.rint(128 + 20 * np.sin(2 * np.pi * x / 24) + 24 * np.sin(2 * np.pi * x / 128)) + source = _gray_image(np.clip(values, 0, 255).astype(np.uint8)) + + result = np.asarray(texture(source, amount=-100, size=8), dtype=np.float32) + original = np.asarray(source, dtype=np.float32) + fine_before = _sinusoid_amplitude(original, wavelength=24, border=64) + fine_after = _sinusoid_amplitude(result, wavelength=24, border=64) + broad_before = _sinusoid_amplitude(original, wavelength=128, border=64) + broad_after = _sinusoid_amplitude(result, wavelength=128, border=64) + + assert fine_after < fine_before - 1.0 + assert abs(broad_after - broad_before) < (fine_before - fine_after) * 0.5 + + +def test_amount_sign_enhances_or_softens_the_same_fine_detail() -> None: + source = _gray_image(_gray_sinusoid(wavelength=24, amplitude=20)) + + positive = np.asarray(texture(source, amount=100, size=8), dtype=np.float32) + negative = np.asarray(texture(source, amount=-100, size=8), dtype=np.float32) + original = np.asarray(source, dtype=np.float32) + + original_contrast = float(_interior(original).std()) + positive_contrast = float(_interior(positive).std()) + negative_contrast = float(_interior(negative).std()) + assert positive_contrast > original_contrast + 1.0 + assert negative_contrast < original_contrast - 1.0 + + +def test_texture_has_a_measurable_response_to_reproducible_sinusoidal_detail() -> None: + source = _gray_image(_gray_sinusoid(wavelength=24, amplitude=20)) + + result = np.asarray(texture(source, amount=100, size=8), dtype=np.float32) + original = np.asarray(source, dtype=np.float32) + + assert float(np.sqrt(np.mean((_interior(result) - _interior(original)) ** 2))) > 0.5 + + +def test_texture_targets_smaller_wavelengths_than_medium_scale_clarity() -> None: + # Size 8 targets fine detail; Clarity size 40 is calibrated for medium scales. + # Formula-based calibration shows Texture size 8 responds around 24–32 px, while + # Clarity size 40 responds around 96–128 px. The 64 px crop leaves 384 px, an + # integer number of periods at every wavelength, while a 2x margin avoids snapshots. + # These response ranges are provisional until rerun against the implementation. + wavelengths = (8, 12, 16, 24, 32, 48, 64, 96, 128) + texture_response: list[float] = [] + clarity_response: list[float] = [] + + for wavelength in wavelengths: + source = _gray_image(_gray_sinusoid(wavelength=wavelength, amplitude=20, shape=(512, 512))) + original = np.asarray(source, dtype=np.float32) + interior_original = _interior(original, border=64) + texture_result = np.asarray(texture(source, amount=100, size=8), dtype=np.float32) + clarity_result = np.asarray(clarity(source, amount=100, size=40), dtype=np.float32) + texture_response.append( + float(np.sqrt(np.mean((_interior(texture_result, border=64) - interior_original) ** 2))) + ) + clarity_response.append( + float(np.sqrt(np.mean((_interior(clarity_result, border=64) - interior_original) ** 2))) + ) + + texture_peak_wavelength = wavelengths[int(np.argmax(texture_response))] + clarity_peak_wavelength = wavelengths[int(np.argmax(clarity_response))] + assert max(texture_response) > 0.5 + assert max(clarity_response) > 1.0 + assert texture_peak_wavelength * 2 <= clarity_peak_wavelength + + +@pytest.mark.parametrize("mode", ["L", "LA", "RGB", "RGBA"]) +def test_supported_modes_and_alpha_are_preserved(mode: str) -> None: + gray = _gray_sinusoid(wavelength=24, amplitude=20, shape=(65, 65)) + if mode == "L": + array = gray + elif mode == "LA": + array = np.dstack((gray, np.full_like(gray, 255))) + array[20:25, 20:25, 1] = 0 + array[20:25, 20:25, 0] = 37 + elif mode == "RGB": + array = np.repeat(gray[..., None], 3, axis=2) + else: + array = np.dstack((gray, gray, gray, np.full_like(gray, 255))) + array[20:25, 20:25, :3] = (255, 0, 170) + array[20:25, 20:25, 3] = 0 + source = Image.fromarray(array.astype(np.uint8), mode=mode) + + result = texture(source, amount=100, size=8) + + assert result.mode == mode + result_array = np.asarray(result) + if mode in {"LA", "RGBA"}: + source_array = np.asarray(source) + np.testing.assert_array_equal(result_array[..., -1], source_array[..., -1]) + np.testing.assert_array_equal( + result_array[source_array[..., -1] == 0], source_array[source_array[..., -1] == 0] + ) From 20f852d7cd29f13d0746670d38c75774434f0932 Mon Sep 17 00:00:00 2001 From: JPPhoto Date: Fri, 16 Oct 2026 15:12:00 -0500 Subject: [PATCH 2/2] feat(nodes): extend image adjustment strength range Allow Clarity, Texture, and Contrast Dehaze amounts from -200 to 200. --- .../08.Workflows/image-adjustments.mdx | 6 ++--- .../app/invocations/image_manipulation.py | 12 ++++----- invokeai/backend/image_util/clarity.py | 8 +++--- .../backend/image_util/contrast_dehaze.py | 8 +++--- invokeai/backend/image_util/texture.py | 8 +++--- invokeai/frontend/api/openapi.json | 18 ++++++------- .../test_image_manipulation_invocations.py | 6 ++--- tests/backend/image_util/test_clarity.py | 21 +++++++++++++++ .../image_util/test_contrast_dehaze.py | 15 ++++++++++- tests/backend/image_util/test_texture.py | 27 +++++++++++++++++++ 10 files changed, 95 insertions(+), 34 deletions(-) diff --git a/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx b/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx index 0c07785772f..8b5b36bc4f9 100644 --- a/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx +++ b/docs/src/content/docs/Users Guide/08.Workflows/image-adjustments.mdx @@ -11,9 +11,9 @@ Clarity, Texture, and Contrast Dehaze are image nodes for making local detail or | Node | What it adjusts | Controls | | --- | --- | --- | -| **Clarity** | Midtone detail at a broader scale. Positive amounts emphasize local detail; negative amounts soften it. | **Amount:** −100 to 100, default 0. **Size:** 4 to 1000, default 40. | -| **Texture** | Fine-scale detail. Positive amounts emphasize texture; negative amounts reduce it. | **Amount:** −100 to 100, default 0. **Size:** 1 to 100, default 8. | -| **Contrast Dehaze (approximation)** | Broad luminance separation and color intensity. Positive amounts increase both; negative amounts reduce both. | **Amount:** −100 to 100, default 0. | +| **Clarity** | Midtone detail at a broader scale. Positive amounts emphasize local detail; negative amounts soften it. | **Amount:** −200 to 200, default 0. **Size:** 4 to 1000, default 40. | +| **Texture** | Fine-scale detail. Positive amounts emphasize texture; negative amounts reduce it. | **Amount:** −200 to 200, default 0. **Size:** 1 to 100, default 8. | +| **Contrast Dehaze (approximation)** | Broad luminance separation and color intensity. Positive amounts increase both; negative amounts reduce both. | **Amount:** −200 to 200, default 0. | For Clarity and Texture, **Size** is a scale in pixels of the input image. It controls the size of detail structures affected, not the output dimensions. Larger values affect broader structures; smaller values target finer detail. The amount is a signed strength control, not a percentage guarantee of a particular visual change. Results depend on the image. diff --git a/invokeai/app/invocations/image_manipulation.py b/invokeai/app/invocations/image_manipulation.py index abefe1b9fca..3c2bb16c498 100644 --- a/invokeai/app/invocations/image_manipulation.py +++ b/invokeai/app/invocations/image_manipulation.py @@ -15,13 +15,13 @@ title="Clarity", tags=["image", "detail", "clarity", "enhance"], category="image", - version="1.0.0", + version="1.0.1", ) class ClarityInvocation(BaseInvocation, WithMetadata, WithBoard): """Adjust local midtone detail in an image.""" image: ImageField = InputField(description="The image to process") - amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the clarity adjustment") + amount: float = InputField(default=0, ge=-200, le=200, description="Strength of the clarity adjustment") size: float = InputField(default=40, ge=4, le=1000, description="Scale of the detail adjustment") def invoke(self, context: InvocationContext) -> ImageOutput: @@ -36,13 +36,13 @@ def invoke(self, context: InvocationContext) -> ImageOutput: title="Texture", tags=["image", "detail", "texture", "enhance"], category="image", - version="1.0.0", + version="1.0.1", ) class TextureInvocation(BaseInvocation, WithMetadata, WithBoard): """Adjust fine-scale texture detail in an image.""" image: ImageField = InputField(description="The image to process") - amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the texture adjustment") + amount: float = InputField(default=0, ge=-200, le=200, description="Strength of the texture adjustment") size: float = InputField(default=8, ge=1, le=100, description="Scale of the texture detail") def invoke(self, context: InvocationContext) -> ImageOutput: @@ -57,13 +57,13 @@ def invoke(self, context: InvocationContext) -> ImageOutput: title="Contrast Dehaze (approximation)", tags=["image", "contrast", "dehaze", "enhance"], category="image", - version="1.0.0", + version="1.0.1", ) class ContrastDehazeInvocation(BaseInvocation, WithMetadata, WithBoard): """Adjust broad-scale contrast and chroma as an aesthetic dehazing approximation.""" image: ImageField = InputField(description="The image to process") - amount: float = InputField(default=0, ge=-100, le=100, description="Strength of the contrast and dehaze adjustment") + amount: float = InputField(default=0, ge=-200, le=200, description="Strength of the contrast and dehaze adjustment") def invoke(self, context: InvocationContext) -> ImageOutput: image = context.images.get_pil(self.image.image_name) diff --git a/invokeai/backend/image_util/clarity.py b/invokeai/backend/image_util/clarity.py index a6742d44a1e..f01c640a511 100644 --- a/invokeai/backend/image_util/clarity.py +++ b/invokeai/backend/image_util/clarity.py @@ -12,8 +12,8 @@ from invokeai.backend.image_util.color import prepare_lightness, restore_lightness from invokeai.backend.image_util.multiscale import edge_aware_band -_MIN_AMOUNT = -100 -_MAX_AMOUNT = 100 +_MIN_AMOUNT = -200 +_MAX_AMOUNT = 200 _MIN_SIZE = 4 _MAX_SIZE = 1000 _GUIDED_FILTER_EPSILON = 0.002 @@ -22,10 +22,10 @@ def clarity(source: Image.Image, amount: float = 0, size: float = 40) -> Image.Image: """Adjust local midtone detail while preserving the source image's color and alpha.""" if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): - raise ValueError("amount must be a finite number between -100 and 100") + raise ValueError("amount must be a finite number between -200 and 200") amount_value = float(amount) if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: - raise ValueError("amount must be between -100 and 100") + raise ValueError("amount must be between -200 and 200") if isinstance(size, bool) or not isinstance(size, Real) or not isfinite(size): raise ValueError("size must be a finite number between 4 and 1000") diff --git a/invokeai/backend/image_util/contrast_dehaze.py b/invokeai/backend/image_util/contrast_dehaze.py index ef5b4555618..0a675340fd3 100644 --- a/invokeai/backend/image_util/contrast_dehaze.py +++ b/invokeai/backend/image_util/contrast_dehaze.py @@ -12,8 +12,8 @@ from invokeai.backend.image_util.color import prepare_lightness, restore_lightness from invokeai.backend.image_util.multiscale import edge_aware_band -_MIN_AMOUNT = -100 -_MAX_AMOUNT = 100 +_MIN_AMOUNT = -200 +_MAX_AMOUNT = 200 _GUIDED_FILTER_EPSILON = 0.002 @@ -25,10 +25,10 @@ def contrast_dehaze(source: Image.Image, amount: float = 0) -> Image.Image: estimation or scene recovery. """ if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): - raise ValueError("amount must be a finite number between -100 and 100") + raise ValueError("amount must be a finite number between -200 and 200") amount_value = float(amount) if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: - raise ValueError("amount must be between -100 and 100") + raise ValueError("amount must be between -200 and 200") if amount_value == 0: return source.copy() diff --git a/invokeai/backend/image_util/texture.py b/invokeai/backend/image_util/texture.py index dfb57f0d933..2d56e6d48f3 100644 --- a/invokeai/backend/image_util/texture.py +++ b/invokeai/backend/image_util/texture.py @@ -13,8 +13,8 @@ from invokeai.backend.image_util.color import prepare_lightness, restore_lightness from invokeai.backend.image_util.multiscale import edge_aware_band -_MIN_AMOUNT = -100 -_MAX_AMOUNT = 100 +_MIN_AMOUNT = -200 +_MAX_AMOUNT = 200 _MIN_SIZE = 1 _MAX_SIZE = 100 _GUIDED_FILTER_EPSILON = 0.0008 @@ -23,10 +23,10 @@ def texture(source: Image.Image, amount: float = 0, size: float = 8) -> Image.Image: """Adjust fine-scale detail while preserving the source image's color and alpha.""" if isinstance(amount, bool) or not isinstance(amount, Real) or not isfinite(amount): - raise ValueError("amount must be a finite number between -100 and 100") + raise ValueError("amount must be a finite number between -200 and 200") amount_value = float(amount) if not _MIN_AMOUNT <= amount_value <= _MAX_AMOUNT: - raise ValueError("amount must be between -100 and 100") + raise ValueError("amount must be between -200 and 200") if isinstance(size, bool) or not isinstance(size, Real) or not isfinite(size): raise ValueError("size must be a finite number between 1 and 100") diff --git a/invokeai/frontend/api/openapi.json b/invokeai/frontend/api/openapi.json index d5e52349720..152b32ccb3d 100644 --- a/invokeai/frontend/api/openapi.json +++ b/invokeai/frontend/api/openapi.json @@ -25335,8 +25335,8 @@ "description": "Strength of the clarity adjustment", "field_kind": "input", "input": "any", - "maximum": 100, - "minimum": -100, + "maximum": 200, + "minimum": -200, "orig_default": 0, "orig_required": false, "title": "Amount", @@ -25366,7 +25366,7 @@ "tags": ["image", "detail", "clarity", "enhance"], "title": "Clarity", "type": "object", - "version": "1.0.0", + "version": "1.0.1", "output": { "$ref": "#/components/schemas/ImageOutput" } @@ -27344,8 +27344,8 @@ "description": "Strength of the contrast and dehaze adjustment", "field_kind": "input", "input": "any", - "maximum": 100, - "minimum": -100, + "maximum": 200, + "minimum": -200, "orig_default": 0, "orig_required": false, "title": "Amount", @@ -27363,7 +27363,7 @@ "tags": ["image", "contrast", "dehaze", "enhance"], "title": "Contrast Dehaze (approximation)", "type": "object", - "version": "1.0.0", + "version": "1.0.1", "output": { "$ref": "#/components/schemas/ImageOutput" } @@ -107863,8 +107863,8 @@ "description": "Strength of the texture adjustment", "field_kind": "input", "input": "any", - "maximum": 100, - "minimum": -100, + "maximum": 200, + "minimum": -200, "orig_default": 0, "orig_required": false, "title": "Amount", @@ -107894,7 +107894,7 @@ "tags": ["image", "detail", "texture", "enhance"], "title": "Texture", "type": "object", - "version": "1.0.0", + "version": "1.0.1", "output": { "$ref": "#/components/schemas/ImageOutput" } diff --git a/tests/app/invocations/test_image_manipulation_invocations.py b/tests/app/invocations/test_image_manipulation_invocations.py index 3c10759952c..24e2474c7e1 100644 --- a/tests/app/invocations/test_image_manipulation_invocations.py +++ b/tests/app/invocations/test_image_manipulation_invocations.py @@ -53,7 +53,7 @@ def test_node_is_registered_with_stable_identity_and_defaults( assert node_class.UIConfig.title == title assert node_class.UIConfig.tags assert node_class.UIConfig.category == "image" - assert node_class.UIConfig.version == "1.0.0" + assert node_class.UIConfig.version == "1.0.1" assert node_class.UIConfig.node_pack == "invokeai" assert "image" in node_class.model_fields assert node_class.model_fields["amount"].default == 0 @@ -88,9 +88,9 @@ def test_amount_bounds_and_non_finite_values_are_rejected( ) -> None: del title, effect, size_default, size_bounds, metadata node_class = nodes[node_id] - for amount in (-100, 0, 100): + for amount in (-200, -100, 0, 100, 200): node_class.model_validate({"image": {"image_name": "input"}, "amount": amount}) - for amount in (-100.01, 100.01, float("nan"), float("inf"), -float("inf")): + for amount in (-200.01, 200.01, float("nan"), float("inf"), -float("inf")): with pytest.raises(ValidationError): node_class.model_validate({"image": {"image_name": "input"}, "amount": amount}) diff --git a/tests/backend/image_util/test_clarity.py b/tests/backend/image_util/test_clarity.py index 26a96de1488..83e11e117a7 100644 --- a/tests/backend/image_util/test_clarity.py +++ b/tests/backend/image_util/test_clarity.py @@ -79,6 +79,27 @@ def test_positive_and_negative_amount_separate_midtone_detail_in_opposite_direct assert negative_contrast < source_contrast - 1.0 +def test_extended_amounts_produce_stronger_clarity_adjustments() -> None: + source = _gray_image(_gray_sinusoid(level=128, amplitude=16, wavelength=16)) + center = (slice(32, -32), slice(32, -32)) + + original = np.asarray(source, dtype=np.float32) + source_contrast = float(original[center].std()) + positive_100 = np.asarray(clarity(source, amount=100, size=12), dtype=np.float32) + positive_200 = np.asarray(clarity(source, amount=200, size=12), dtype=np.float32) + negative_100 = np.asarray(clarity(source, amount=-100, size=12), dtype=np.float32) + negative_200 = np.asarray(clarity(source, amount=-200, size=12), dtype=np.float32) + + assert float(positive_200[center].std()) > float(positive_100[center].std()) > source_contrast + assert float(negative_200[center].std()) < float(negative_100[center].std()) < source_contrast + + +@pytest.mark.parametrize("amount", [float("nan"), float("inf"), float("-inf"), -200.01, 200.01, True]) +def test_invalid_amount_is_rejected(amount: float) -> None: + with pytest.raises(ValueError): + clarity(Image.new("RGB", (3, 2), (120, 130, 140)), amount=amount) + + def test_tone_gate_reduces_effect_near_black_and_white() -> None: regions = np.concatenate( [ diff --git a/tests/backend/image_util/test_contrast_dehaze.py b/tests/backend/image_util/test_contrast_dehaze.py index 1ffde51b1f9..1405ec4cf7b 100644 --- a/tests/backend/image_util/test_contrast_dehaze.py +++ b/tests/backend/image_util/test_contrast_dehaze.py @@ -57,6 +57,19 @@ def test_negative_amount_reduces_broad_scale_separation_like_a_veil() -> None: assert _broad_luminance_spread(result) < _broad_luminance_spread(source) - 2.0 +def test_extended_amounts_produce_stronger_contrast_dehaze_adjustments() -> None: + source = _veiled_gray_bands() + source_spread = _broad_luminance_spread(source) + + positive_100 = _broad_luminance_spread(contrast_dehaze(source, amount=100)) + positive_200 = _broad_luminance_spread(contrast_dehaze(source, amount=200)) + negative_100 = _broad_luminance_spread(contrast_dehaze(source, amount=-100)) + negative_200 = _broad_luminance_spread(contrast_dehaze(source, amount=-200)) + + assert positive_200 > positive_100 > source_spread + assert negative_200 < negative_100 < source_spread + + def test_amount_moves_chroma_in_the_same_direction_on_a_safe_color_patch() -> None: source = Image.new("RGB", (24, 24), (130, 90, 70)) source_chroma = _mean_oklab_chroma(source) @@ -107,7 +120,7 @@ def test_transparent_pixels_keep_their_hidden_color() -> None: np.testing.assert_array_equal(result[pixels[..., 3] == 0], pixels[pixels[..., 3] == 0]) -@pytest.mark.parametrize("amount", [float("nan"), float("inf"), float("-inf"), -100.1, 100.1, True]) +@pytest.mark.parametrize("amount", [float("nan"), float("inf"), float("-inf"), -200.01, 200.01, True]) def test_invalid_amount_is_rejected(amount: float) -> None: with pytest.raises(ValueError): contrast_dehaze(Image.new("RGB", (3, 2), (120, 130, 140)), amount=amount) diff --git a/tests/backend/image_util/test_texture.py b/tests/backend/image_util/test_texture.py index 34854aab8ff..8780ea22562 100644 --- a/tests/backend/image_util/test_texture.py +++ b/tests/backend/image_util/test_texture.py @@ -115,6 +115,33 @@ def test_amount_sign_enhances_or_softens_the_same_fine_detail() -> None: assert negative_contrast < original_contrast - 1.0 +def test_extended_amounts_produce_stronger_texture_adjustments() -> None: + source = _gray_image(_gray_sinusoid(wavelength=24, amplitude=20)) + + source_amplitude = _sinusoid_amplitude(np.asarray(source), wavelength=24) + positive_100 = texture(source, amount=100, size=8) + positive_200 = texture(source, amount=200, size=8) + negative_100 = texture(source, amount=-100, size=8) + negative_200 = texture(source, amount=-200, size=8) + + assert ( + _sinusoid_amplitude(np.asarray(positive_200), wavelength=24) + > _sinusoid_amplitude(np.asarray(positive_100), wavelength=24) + > source_amplitude + ) + assert ( + _sinusoid_amplitude(np.asarray(negative_200), wavelength=24) + < _sinusoid_amplitude(np.asarray(negative_100), wavelength=24) + < source_amplitude + ) + + +@pytest.mark.parametrize("amount", [float("nan"), float("inf"), float("-inf"), -200.01, 200.01, True]) +def test_invalid_amount_is_rejected(amount: float) -> None: + with pytest.raises(ValueError): + texture(Image.new("RGB", (3, 2), (120, 130, 140)), amount=amount) + + def test_texture_has_a_measurable_response_to_reproducible_sinusoidal_detail() -> None: source = _gray_image(_gray_sinusoid(wavelength=24, amplitude=20))