From b2b258098d721d29bc42bf8bc0cc95f0662e829b Mon Sep 17 00:00:00 2001 From: georgi Date: Sun, 25 May 2025 13:43:17 +0200 Subject: [PATCH] Add image processing nodes and DSL --- src/nodetool/dsl/lib/image.py | 166 ++ src/nodetool/dsl/nodetool/image.py | 150 ++ src/nodetool/dsl/nodetool/output.py | 22 + src/nodetool/nodes/lib/image.py | 154 ++ src/nodetool/nodes/nodetool/__init__.py | 2 + src/nodetool/nodes/nodetool/image.py | 10 + src/nodetool/nodes/nodetool/output.py | 3 + .../package_metadata/nodetool-lib-image.json | 1395 +++++++++++------ tests/conftest.py | 8 +- 9 files changed, 1390 insertions(+), 520 deletions(-) create mode 100644 src/nodetool/dsl/lib/image.py create mode 100644 src/nodetool/dsl/nodetool/image.py create mode 100644 src/nodetool/dsl/nodetool/output.py create mode 100644 src/nodetool/nodes/lib/image.py create mode 100644 src/nodetool/nodes/nodetool/__init__.py create mode 100644 src/nodetool/nodes/nodetool/image.py create mode 100644 src/nodetool/nodes/nodetool/output.py diff --git a/src/nodetool/dsl/lib/image.py b/src/nodetool/dsl/lib/image.py new file mode 100644 index 0000000..cf436e2 --- /dev/null +++ b/src/nodetool/dsl/lib/image.py @@ -0,0 +1,166 @@ +from pydantic import BaseModel, Field +import typing +from typing import Any +import nodetool.metadata.types +import nodetool.metadata.types as types +from nodetool.dsl.graph import GraphNode + + +class BatchToList(GraphNode): + """Convert a batch image into a list of images.""" + + batch: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Batch of images", + ) + + @classmethod + def get_node_type(cls): + return "lib.image.BatchToList" + + +class Crop(GraphNode): + """Crop an image region.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to crop", + ) + left: int | GraphNode | tuple[GraphNode, str] = Field(default=0, description="Left") + top: int | GraphNode | tuple[GraphNode, str] = Field(default=0, description="Top") + right: int | GraphNode | tuple[GraphNode, str] = Field( + default=1, description="Right" + ) + bottom: int | GraphNode | tuple[GraphNode, str] = Field( + default=1, description="Bottom" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Crop" + + +class Fit(GraphNode): + """Resize and crop an image to fit the given size.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to fit", + ) + width: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Target width" + ) + height: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Target height" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Fit" + + +class GetMetadata(GraphNode): + """Return basic image metadata.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to inspect.", + ) + + @classmethod + def get_node_type(cls): + return "lib.image.GetMetadata" + + +class ImageOutput(GraphNode): + """Workflow output for images.""" + + value: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image value", + ) + name: str | GraphNode | tuple[GraphNode, str] = Field( + default="", description="The parameter name for the workflow." + ) + + @classmethod + def get_node_type(cls): + return "lib.image.ImageOutput" + + +class Paste(GraphNode): + """Paste one image onto another.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Base image", + ) + paste: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to paste", + ) + left: int | GraphNode | tuple[GraphNode, str] = Field( + default=0, description="Left position" + ) + top: int | GraphNode | tuple[GraphNode, str] = Field( + default=0, description="Top position" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Paste" + + +class Resize(GraphNode): + """Resize an image to specific dimensions.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to resize", + ) + width: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Width" + ) + height: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Height" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Resize" + + +class SaveImage(GraphNode): + """Save an image reference.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to save.", + ) + folder: types.FolderRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.FolderRef(type="folder", uri="", asset_id=None, data=None), + description="Target folder.", + ) + name: str | GraphNode | tuple[GraphNode, str] = Field( + default="image.png", description="File name" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.SaveImage" + + +class Scale(GraphNode): + """Scale an image by a factor.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to scale", + ) + scale: float | GraphNode | tuple[GraphNode, str] = Field( + default=1.0, description="Scale factor" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Scale" diff --git a/src/nodetool/dsl/nodetool/image.py b/src/nodetool/dsl/nodetool/image.py new file mode 100644 index 0000000..dcfc913 --- /dev/null +++ b/src/nodetool/dsl/nodetool/image.py @@ -0,0 +1,150 @@ +from pydantic import BaseModel, Field +import typing +from typing import Any +import nodetool.metadata.types +import nodetool.metadata.types as types +from nodetool.dsl.graph import GraphNode + + +class BatchToList(GraphNode): + """Convert a batch image into a list of images.""" + + batch: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Batch of images", + ) + + @classmethod + def get_node_type(cls): + return "lib.image.BatchToList" + + +class Crop(GraphNode): + """Crop an image region.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to crop", + ) + left: int | GraphNode | tuple[GraphNode, str] = Field(default=0, description="Left") + top: int | GraphNode | tuple[GraphNode, str] = Field(default=0, description="Top") + right: int | GraphNode | tuple[GraphNode, str] = Field( + default=1, description="Right" + ) + bottom: int | GraphNode | tuple[GraphNode, str] = Field( + default=1, description="Bottom" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Crop" + + +class Fit(GraphNode): + """Resize and crop an image to fit the given size.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to fit", + ) + width: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Target width" + ) + height: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Target height" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Fit" + + +class GetMetadata(GraphNode): + """Return basic image metadata.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to inspect.", + ) + + @classmethod + def get_node_type(cls): + return "lib.image.GetMetadata" + + +class Paste(GraphNode): + """Paste one image onto another.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Base image", + ) + paste: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to paste", + ) + left: int | GraphNode | tuple[GraphNode, str] = Field( + default=0, description="Left position" + ) + top: int | GraphNode | tuple[GraphNode, str] = Field( + default=0, description="Top position" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Paste" + + +class Resize(GraphNode): + """Resize an image to specific dimensions.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to resize", + ) + width: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Width" + ) + height: int | GraphNode | tuple[GraphNode, str] = Field( + default=512, description="Height" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Resize" + + +class SaveImage(GraphNode): + """Save an image reference.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to save.", + ) + folder: types.FolderRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.FolderRef(type="folder", uri="", asset_id=None, data=None), + description="Target folder.", + ) + name: str | GraphNode | tuple[GraphNode, str] = Field( + default="image.png", description="File name" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.SaveImage" + + +class Scale(GraphNode): + """Scale an image by a factor.""" + + image: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image to scale", + ) + scale: float | GraphNode | tuple[GraphNode, str] = Field( + default=1.0, description="Scale factor" + ) + + @classmethod + def get_node_type(cls): + return "lib.image.Scale" diff --git a/src/nodetool/dsl/nodetool/output.py b/src/nodetool/dsl/nodetool/output.py new file mode 100644 index 0000000..4ff0fcf --- /dev/null +++ b/src/nodetool/dsl/nodetool/output.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel, Field +import typing +from typing import Any +import nodetool.metadata.types +import nodetool.metadata.types as types +from nodetool.dsl.graph import GraphNode + + +class ImageOutput(GraphNode): + """Workflow output for images.""" + + value: types.ImageRef | GraphNode | tuple[GraphNode, str] = Field( + default=types.ImageRef(type="image", uri="", asset_id=None, data=None), + description="Image value", + ) + name: str | GraphNode | tuple[GraphNode, str] = Field( + default="", description="The parameter name for the workflow." + ) + + @classmethod + def get_node_type(cls): + return "lib.image.ImageOutput" diff --git a/src/nodetool/nodes/lib/image.py b/src/nodetool/nodes/lib/image.py new file mode 100644 index 0000000..4a19644 --- /dev/null +++ b/src/nodetool/nodes/lib/image.py @@ -0,0 +1,154 @@ +from PIL import Image, ImageOps +from pydantic import Field + +from nodetool.metadata.types import ImageRef, FolderRef +from nodetool.workflows.base_node import BaseNode, OutputNode +from nodetool.workflows.processing_context import ProcessingContext + + +class SaveImage(BaseNode): + """Save an image reference.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to save.") + folder: FolderRef = Field(default=FolderRef(), description="Target folder.") + name: str = Field(default="image.png", description="File name") + + async def process(self, context: ProcessingContext) -> ImageRef: + # In this simplified implementation we just return the original image. + return self.image + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image"] + + +class GetMetadata(BaseNode): + """Return basic image metadata.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to inspect.") + + async def process(self, context: ProcessingContext) -> dict: + img = await context.image_to_pil(self.image) + width, height = img.size + return {"width": width, "height": height} + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image"] + + +class BatchToList(BaseNode): + """Convert a batch image into a list of images.""" + + batch: ImageRef = Field(default=ImageRef(), description="Batch of images") + + async def process(self, context: ProcessingContext) -> list[ImageRef]: + if isinstance(self.batch.data, list): + results: list[ImageRef] = [] + for data in self.batch.data: + if isinstance(data, bytes): + results.append(await context.image_from_bytes(data)) + return results + return [] + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["batch"] + + +class Paste(BaseNode): + """Paste one image onto another.""" + + image: ImageRef = Field(default=ImageRef(), description="Base image") + paste: ImageRef = Field(default=ImageRef(), description="Image to paste") + left: int = Field(default=0, description="Left position") + top: int = Field(default=0, description="Top position") + + async def process(self, context: ProcessingContext) -> ImageRef: + base = await context.image_to_pil(self.image) + overlay = await context.image_to_pil(self.paste) + base.paste(overlay, (self.left, self.top)) + return await context.image_from_pil(base) + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image", "paste"] + + +class Fit(BaseNode): + """Resize and crop an image to fit the given size.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to fit") + width: int = Field(default=512, ge=1, description="Target width") + height: int = Field(default=512, ge=1, description="Target height") + + async def process(self, context: ProcessingContext) -> ImageRef: + img = await context.image_to_pil(self.image) + img = ImageOps.fit(img, (self.width, self.height)) + return await context.image_from_pil(img) + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image", "width", "height"] + + +class Scale(BaseNode): + """Scale an image by a factor.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to scale") + scale: float = Field(default=1.0, ge=0.0, description="Scale factor") + + async def process(self, context: ProcessingContext) -> ImageRef: + img = await context.image_to_pil(self.image) + w, h = img.size + img = img.resize((int(w * self.scale), int(h * self.scale))) + return await context.image_from_pil(img) + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image", "scale"] + + +class Resize(BaseNode): + """Resize an image to specific dimensions.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to resize") + width: int = Field(default=512, ge=1, description="Width") + height: int = Field(default=512, ge=1, description="Height") + + async def process(self, context: ProcessingContext) -> ImageRef: + img = await context.image_to_pil(self.image) + img = img.resize((self.width, self.height)) + return await context.image_from_pil(img) + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image", "width", "height"] + + +class Crop(BaseNode): + """Crop an image region.""" + + image: ImageRef = Field(default=ImageRef(), description="Image to crop") + left: int = Field(default=0, ge=0, description="Left") + top: int = Field(default=0, ge=0, description="Top") + right: int = Field(default=1, ge=1, description="Right") + bottom: int = Field(default=1, ge=1, description="Bottom") + + async def process(self, context: ProcessingContext) -> ImageRef: + img = await context.image_to_pil(self.image) + img = img.crop((self.left, self.top, self.right, self.bottom)) + return await context.image_from_pil(img) + + @classmethod + def get_basic_fields(cls) -> list[str]: + return ["image", "left", "top", "right", "bottom"] + + +class ImageOutput(OutputNode): + """Workflow output for images.""" + + value: ImageRef = Field(default=ImageRef(), description="Image value") + + async def process(self, context: ProcessingContext) -> ImageRef: + return self.value diff --git a/src/nodetool/nodes/nodetool/__init__.py b/src/nodetool/nodes/nodetool/__init__.py new file mode 100644 index 0000000..6ae6948 --- /dev/null +++ b/src/nodetool/nodes/nodetool/__init__.py @@ -0,0 +1,2 @@ +from .image import * # noqa: F401,F403 +from .output import * # noqa: F401,F403 diff --git a/src/nodetool/nodes/nodetool/image.py b/src/nodetool/nodes/nodetool/image.py new file mode 100644 index 0000000..90d7ecc --- /dev/null +++ b/src/nodetool/nodes/nodetool/image.py @@ -0,0 +1,10 @@ +from ..lib.image import ( + SaveImage, + GetMetadata, + BatchToList, + Paste, + Fit, + Scale, + Resize, + Crop, +) diff --git a/src/nodetool/nodes/nodetool/output.py b/src/nodetool/nodes/nodetool/output.py new file mode 100644 index 0000000..9ff263d --- /dev/null +++ b/src/nodetool/nodes/nodetool/output.py @@ -0,0 +1,3 @@ +from ..lib.image import ImageOutput + +__all__ = ["ImageOutput"] diff --git a/src/nodetool/package_metadata/nodetool-lib-image.json b/src/nodetool/package_metadata/nodetool-lib-image.json index e437a3c..b5932ad 100644 --- a/src/nodetool/package_metadata/nodetool-lib-image.json +++ b/src/nodetool/package_metadata/nodetool-lib-image.json @@ -115,66 +115,20 @@ "is_dynamic": false }, { - "title": "Paddle OCR", - "description": "Performs Optical Character Recognition (OCR) on images using PaddleOCR.\n image, text, ocr, document\n\n Use cases:\n - Text extraction from images\n - Document digitization\n - Receipt/invoice processing\n - Handwriting recognition", - "namespace": "lib.ocr", - "node_type": "lib.ocr.PaddleOCR", + "title": "Batch To List", + "description": "Convert a batch image into a list of images.", + "namespace": "lib.image", + "node_type": "lib.image.BatchToList", "layout": "default", "properties": [ { - "name": "image", + "name": "batch", "type": { "type": "image" }, "default": {}, - "title": "Input Image", - "description": "The image to perform OCR on" - }, - { - "name": "language", - "type": { - "type": "enum", - "values": [ - "en", - "fr", - "de", - "es", - "it", - "pt", - "nl", - "pl", - "ro", - "hr", - "cs", - "hu", - "sk", - "sl", - "tr", - "vi", - "id", - "ms", - "la", - "ru", - "bg", - "uk", - "be", - "mn", - "ch", - "ja", - "ko", - "ar", - "fa", - "ur", - "hi", - "mr", - "ne", - "sa" - ], - "type_name": "nodetool.nodes.lib.ocr.OCRLanguage" - }, - "default": "en", - "title": "Language", - "description": "Language code for OCR" + "title": "Batch", + "description": "Batch of images" } ], "outputs": [ @@ -183,24 +137,427 @@ "type": "list", "type_args": [ { - "type": "ocr_result" + "type": "image" } ] }, - "name": "boxes" + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "batch" + ], + "is_dynamic": false + }, + { + "title": "Crop", + "description": "Crop an image region.", + "namespace": "lib.image", + "node_type": "lib.image.Crop", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to crop" + }, + { + "name": "left", + "type": { + "type": "int" + }, + "default": 0, + "title": "Left", + "description": "Left", + "min": 0.0 + }, + { + "name": "top", + "type": { + "type": "int" + }, + "default": 0, + "title": "Top", + "description": "Top", + "min": 0.0 + }, + { + "name": "right", + "type": { + "type": "int" + }, + "default": 1, + "title": "Right", + "description": "Right", + "min": 1.0 + }, + { + "name": "bottom", + "type": { + "type": "int" + }, + "default": 1, + "title": "Bottom", + "description": "Bottom", + "min": 1.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image", + "left", + "top", + "right", + "bottom" + ], + "is_dynamic": false + }, + { + "title": "Fit", + "description": "Resize and crop an image to fit the given size.", + "namespace": "lib.image", + "node_type": "lib.image.Fit", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to fit" + }, + { + "name": "width", + "type": { + "type": "int" + }, + "default": 512, + "title": "Width", + "description": "Target width", + "min": 1.0 + }, + { + "name": "height", + "type": { + "type": "int" + }, + "default": 512, + "title": "Height", + "description": "Target height", + "min": 1.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image", + "width", + "height" + ], + "is_dynamic": false + }, + { + "title": "Get Metadata", + "description": "Return basic image metadata.", + "namespace": "lib.image", + "node_type": "lib.image.GetMetadata", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to inspect." + } + ], + "outputs": [ + { + "type": { + "type": "dict" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image" + ], + "is_dynamic": false + }, + { + "title": "Image Output", + "description": "Workflow output for images.", + "namespace": "lib.image", + "node_type": "lib.image.ImageOutput", + "layout": "default", + "properties": [ + { + "name": "value", + "type": { + "type": "image" + }, + "default": {}, + "title": "Value", + "description": "Image value" + }, + { + "name": "name", + "type": { + "type": "str" + }, + "default": "", + "title": "Name", + "description": "The parameter name for the workflow." + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "value" + ], + "is_dynamic": false + }, + { + "title": "Paste", + "description": "Paste one image onto another.", + "namespace": "lib.image", + "node_type": "lib.image.Paste", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Base image" + }, + { + "name": "paste", + "type": { + "type": "image" + }, + "default": {}, + "title": "Paste", + "description": "Image to paste" + }, + { + "name": "left", + "type": { + "type": "int" + }, + "default": 0, + "title": "Left", + "description": "Left position" + }, + { + "name": "top", + "type": { + "type": "int" + }, + "default": 0, + "title": "Top", + "description": "Top position" + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image", + "paste" + ], + "is_dynamic": false + }, + { + "title": "Resize", + "description": "Resize an image to specific dimensions.", + "namespace": "lib.image", + "node_type": "lib.image.Resize", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to resize" + }, + { + "name": "width", + "type": { + "type": "int" + }, + "default": 512, + "title": "Width", + "description": "Width", + "min": 1.0 + }, + { + "name": "height", + "type": { + "type": "int" + }, + "default": 512, + "title": "Height", + "description": "Height", + "min": 1.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image", + "width", + "height" + ], + "is_dynamic": false + }, + { + "title": "Save Image", + "description": "Save an image reference.", + "namespace": "lib.image", + "node_type": "lib.image.SaveImage", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to save." + }, + { + "name": "folder", + "type": { + "type": "folder" + }, + "default": {}, + "title": "Folder", + "description": "Target folder." }, { + "name": "name", "type": { "type": "str" }, - "name": "text" + "default": "image.png", + "title": "Name", + "description": "File name" + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image" + ], + "is_dynamic": false + }, + { + "title": "Scale", + "description": "Scale an image by a factor.", + "namespace": "lib.image", + "node_type": "lib.image.Scale", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "Image to scale" + }, + { + "name": "scale", + "type": { + "type": "float" + }, + "default": 1.0, + "title": "Scale", + "description": "Scale factor", + "min": 0.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" } ], "the_model_info": {}, "recommended_models": [], "basic_fields": [ "image", - "language" + "scale" ], "is_dynamic": false }, @@ -1266,168 +1623,14 @@ "type": "float" }, "default": 1, - "title": "Scale Y", - "description": "Y scale factor" - } - ], - "outputs": [ - { - "type": { - "type": "svg_element" - }, - "name": "output" - } - ], - "the_model_info": {}, - "recommended_models": [], - "basic_fields": [ - "content", - "translate_x", - "translate_y", - "rotate", - "scale_x", - "scale_y" - ], - "is_dynamic": false - }, - { - "title": "Adaptive Contrast", - "description": "Applies localized contrast enhancement using adaptive techniques.\n image, contrast, enhance\n\n Use cases:\n - Improve visibility in images with varying lighting conditions\n - Prepare images for improved feature detection in computer vision", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.AdaptiveContrast", - "layout": "default", - "properties": [ - { - "name": "image", - "type": { - "type": "image" - }, - "default": {}, - "title": "Image", - "description": "The image to adjust the contrast for." - }, - { - "name": "clip_limit", - "type": { - "type": "float" - }, - "default": 2.0, - "title": "Clip Limit", - "description": "Clip limit for adaptive contrast.", - "min": 0.0, - "max": 100.0 - }, - { - "name": "grid_size", - "type": { - "type": "int" - }, - "default": 8, - "title": "Grid Size", - "description": "Grid size for adaptive contrast.", - "min": 1.0, - "max": 64.0 - } - ], - "outputs": [ - { - "type": { - "type": "image" - }, - "name": "output" - } - ], - "the_model_info": {}, - "recommended_models": [], - "basic_fields": [ - "image", - "clip_limit", - "grid_size" - ], - "is_dynamic": false - }, - { - "title": "Auto Contrast", - "description": "Automatically adjusts image contrast for enhanced visual quality.\n image, contrast, balance\n\n Use cases:\n - Enhance image clarity for better visual perception\n - Pre-process images for computer vision tasks\n - Improve photo aesthetics in editing workflows", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.AutoContrast", - "layout": "default", - "properties": [ - { - "name": "image", - "type": { - "type": "image" - }, - "default": {}, - "title": "Image", - "description": "The image to adjust the contrast for." - }, - { - "name": "cutoff", - "type": { - "type": "int" - }, - "default": 0, - "title": "Cutoff", - "description": "Represents the percentage of pixels to ignore at both the darkest and lightest ends of the histogram. A cutoff value of 5 means ignoring the darkest 5% and the lightest 5% of pixels, enhancing overall contrast by stretching the remaining pixel values across the full brightness range.", - "min": 0.0, - "max": 255.0 - } - ], - "outputs": [ - { - "type": { - "type": "image" - }, - "name": "output" - } - ], - "the_model_info": {}, - "recommended_models": [], - "basic_fields": [ - "image", - "cutoff" - ], - "is_dynamic": false - }, - { - "title": "Brightness", - "description": "Adjusts overall image brightness to lighten or darken.\n image, brightness, enhance\n\n Use cases:\n - Correct underexposed or overexposed photographs\n - Enhance visibility of dark image regions\n - Prepare images for consistent display across devices", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Brightness", - "layout": "default", - "properties": [ - { - "name": "image", - "type": { - "type": "image" - }, - "default": {}, - "title": "Image", - "description": "The image to adjust the brightness for." - }, - { - "name": "factor", - "type": { - "type": "union", - "type_args": [ - { - "type": "float" - }, - { - "type": "int" - } - ] - }, - "default": 1.0, - "title": "Factor", - "description": "Factor to adjust the brightness. 1.0 means no change." + "title": "Scale Y", + "description": "Y scale factor" } ], "outputs": [ { "type": { - "type": "image" + "type": "svg_element" }, "name": "output" } @@ -1435,16 +1638,20 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image", - "factor" + "content", + "translate_x", + "translate_y", + "rotate", + "scale_x", + "scale_y" ], "is_dynamic": false }, { - "title": "Color", - "description": "Adjusts color intensity of an image.\n image, color, enhance\n\n Use cases:\n - Enhance color vibrancy in photographs\n - Correct color imbalances in digital images\n - Prepare images for consistent brand color representation", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Color", + "title": "Blur", + "description": "Apply a Gaussian blur effect to an image.\n image, filter, blur\n\n - Soften images or reduce noise and detail\n - Make focal areas stand out by blurring surroundings\n - Protect privacy by blurring sensitive information", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Blur", "layout": "default", "properties": [ { @@ -1454,16 +1661,18 @@ }, "default": {}, "title": "Image", - "description": "The image to adjust the brightness for." + "description": "The image to blur." }, { - "name": "factor", + "name": "radius", "type": { - "type": "float" + "type": "int" }, - "default": 1.0, - "title": "Factor", - "description": "Factor to adjust the contrast. 1.0 means no change." + "default": 2, + "title": "Radius", + "description": "Blur radius.", + "min": 0.0, + "max": 128.0 } ], "outputs": [ @@ -1478,15 +1687,15 @@ "recommended_models": [], "basic_fields": [ "image", - "factor" + "radius" ], "is_dynamic": false }, { - "title": "Contrast", - "description": "Adjusts image contrast to modify light-dark differences.\n image, contrast, enhance\n\n Use cases:\n - Enhance visibility of details in low-contrast images\n - Prepare images for visual analysis or recognition tasks\n - Create dramatic effects in artistic photography", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Contrast", + "title": "Canny", + "description": "Apply Canny edge detection to an image.\n image, filter, edges\n\n - Highlight areas of rapid intensity change\n - Outline object boundaries and structure\n - Enhance inputs for object detection and image segmentation", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Canny", "layout": "default", "properties": [ { @@ -1496,16 +1705,29 @@ }, "default": {}, "title": "Image", - "description": "The image to adjust the brightness for." + "description": "The image to canny." }, { - "name": "factor", + "name": "low_threshold", "type": { - "type": "float" + "type": "int" }, - "default": 1.0, - "title": "Factor", - "description": "Factor to adjust the contrast. 1.0 means no change." + "default": 100, + "title": "Low Threshold", + "description": "Low threshold.", + "min": 0.0, + "max": 255.0 + }, + { + "name": "high_threshold", + "type": { + "type": "int" + }, + "default": 200, + "title": "High Threshold", + "description": "High threshold.", + "min": 0.0, + "max": 255.0 } ], "outputs": [ @@ -1520,15 +1742,16 @@ "recommended_models": [], "basic_fields": [ "image", - "factor" + "low_threshold", + "high_threshold" ], "is_dynamic": false }, { - "title": "Detail", - "description": "Enhances fine details in images.\n image, detail, enhance\n\n Use cases:\n - Improve clarity of textural elements in photographs\n - Enhance visibility of small features for analysis\n - Prepare images for high-resolution display or printing", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Detail", + "title": "Contour", + "description": "Apply a contour filter to highlight image edges.\n image, filter, contour\n\n - Extract key features from complex images\n - Aid pattern recognition and object detection\n - Create stylized contour sketch art effects", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Contour", "layout": "default", "properties": [ { @@ -1538,7 +1761,7 @@ }, "default": {}, "title": "Image", - "description": "The image to detail." + "description": "The image to contour." } ], "outputs": [ @@ -1557,10 +1780,10 @@ "is_dynamic": false }, { - "title": "Edge Enhance", - "description": "Enhances edge visibility by increasing contrast along boundaries.\n image, edge, enhance\n\n Use cases:\n - Improve object boundary detection for computer vision\n - Highlight structural elements in technical drawings\n - Prepare images for feature extraction in image analysis", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.EdgeEnhance", + "title": "Convert To Grayscale", + "description": "Convert an image to grayscale.\n image, grayscale\n\n - Simplify images for feature and edge detection\n - Prepare images for shape-based machine learning\n - Create vintage or monochrome aesthetic effects", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.ConvertToGrayscale", "layout": "default", "properties": [ { @@ -1570,7 +1793,7 @@ }, "default": {}, "title": "Image", - "description": "The image to edge enhance." + "description": "The image to convert." } ], "outputs": [ @@ -1589,10 +1812,10 @@ "is_dynamic": false }, { - "title": "Equalize", - "description": "Enhances image contrast by equalizing intensity distribution.\n image, contrast, histogram\n\n Use cases:\n - Improve visibility in poorly lit images\n - Enhance details for image analysis tasks\n - Normalize image data for machine learning", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Equalize", + "title": "Emboss", + "description": "Apply an emboss filter for a 3D raised effect.\n image, filter, emboss\n\n - Add texture and depth to photos\n - Create visually interesting graphics\n - Incorporate unique effects in digital artwork", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Emboss", "layout": "default", "properties": [ { @@ -1602,7 +1825,7 @@ }, "default": {}, "title": "Image", - "description": "The image to equalize." + "description": "The image to emboss." } ], "outputs": [ @@ -1621,10 +1844,10 @@ "is_dynamic": false }, { - "title": "Rank Filter", - "description": "Applies rank-based filtering to enhance or smooth image features.\n image, filter, enhance\n\n Use cases:\n - Reduce noise while preserving edges in images\n - Enhance specific image features based on local intensity\n - Pre-process images for improved segmentation results", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.RankFilter", + "title": "Expand", + "description": "Add a border around an image to increase its size.\n image, border, expand\n\n - Make images stand out by adding a colored border\n - Create framed photo effects\n - Separate image content from surroundings", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Expand", "layout": "default", "properties": [ { @@ -1634,29 +1857,29 @@ }, "default": {}, "title": "Image", - "description": "The image to rank filter." + "description": "The image to expand." }, { - "name": "size", + "name": "border", "type": { "type": "int" }, - "default": 3, - "title": "Size", - "description": "Rank filter size.", - "min": 1.0, + "default": 0, + "title": "Border", + "description": "Border size.", + "min": 0.0, "max": 512.0 }, { - "name": "rank", + "name": "fill", "type": { "type": "int" }, - "default": 3, - "title": "Rank", - "description": "Rank filter rank.", - "min": 1.0, - "max": 512.0 + "default": 0, + "title": "Fill", + "description": "Fill color.", + "min": 0.0, + "max": 255.0 } ], "outputs": [ @@ -1671,16 +1894,16 @@ "recommended_models": [], "basic_fields": [ "image", - "size", - "rank" + "border", + "fill" ], "is_dynamic": false }, { - "title": "Sharpen", - "description": "Enhances image detail by intensifying local pixel contrast.\n image, sharpen, clarity\n\n Use cases:\n - Improve clarity of photographs for print or display\n - Refine texture details in product photography\n - Enhance readability of text in document images", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Sharpen", + "title": "Find Edges", + "description": "Detect and highlight edges in an image.\n image, filter, edges\n\n - Analyze structural patterns in images\n - Aid object detection in computer vision\n - Detect important features like corners and ridges", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.FindEdges", "layout": "default", "properties": [ { @@ -1690,7 +1913,7 @@ }, "default": {}, "title": "Image", - "description": "The image to sharpen." + "description": "The image to find edges." } ], "outputs": [ @@ -1709,10 +1932,10 @@ "is_dynamic": false }, { - "title": "Sharpness", - "description": "Adjusts image sharpness to enhance or reduce detail clarity.\n image, clarity, sharpness\n\n Use cases:\n - Enhance photo details for improved visual appeal\n - Refine images for object detection tasks\n - Correct slightly blurred images", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.Sharpness", + "title": "Get Channel", + "description": "Extract a specific color channel from an image.\n image, color, channel, isolate, extract\n\n - Isolate color information for image analysis\n - Manipulate specific color components in graphic design\n - Enhance or reduce visibility of certain colors", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.GetChannel", "layout": "default", "properties": [ { @@ -1722,16 +1945,21 @@ }, "default": {}, "title": "Image", - "description": "The image to adjust the brightness for." + "description": "The image to get the channel from." }, { - "name": "factor", + "name": "channel", "type": { - "type": "float" + "type": "enum", + "values": [ + "R", + "G", + "B" + ], + "type_name": "nodetool.nodes.lib.pillow.filter.ChannelEnum" }, - "default": 1.0, - "title": "Factor", - "description": "Factor to adjust the contrast. 1.0 means no change." + "default": "R", + "title": "Channel" } ], "outputs": [ @@ -1746,15 +1974,15 @@ "recommended_models": [], "basic_fields": [ "image", - "factor" + "channel" ], "is_dynamic": false }, { - "title": "Unsharp Mask", - "description": "Sharpens images using the unsharp mask technique.\n image, sharpen, enhance\n\n Use cases:\n - Enhance edge definition in photographs\n - Improve perceived sharpness of digital artwork\n - Prepare images for high-quality printing or display", - "namespace": "lib.pillow.enhance", - "node_type": "lib.pillow.enhance.UnsharpMask", + "title": "Invert", + "description": "Invert the colors of an image.\n image, filter, invert\n\n - Create negative versions of images for visual effects\n - Analyze image data by bringing out hidden details\n - Preprocess images for operations that work better on inverted colors", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Invert", "layout": "default", "properties": [ { @@ -1764,40 +1992,83 @@ }, "default": {}, "title": "Image", - "description": "The image to unsharp mask." - }, + "description": "The image to adjust the brightness for." + } + ], + "outputs": [ { - "name": "radius", "type": { - "type": "int" + "type": "image" }, - "default": 2, - "title": "Radius", - "description": "Unsharp mask radius.", - "min": 0.0, - "max": 512.0 + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image" + ], + "is_dynamic": false + }, + { + "title": "Posterize", + "description": "Reduce the number of colors in an image for a poster-like effect.\n image, filter, posterize\n\n - Create graphic art by simplifying image colors\n - Apply artistic effects to photographs\n - Generate visually compelling content for advertising", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Posterize", + "layout": "default", + "properties": [ + { + "name": "image", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image", + "description": "The image to posterize." }, { - "name": "percent", + "name": "bits", "type": { "type": "int" }, - "default": 150, - "title": "Percent", - "description": "Unsharp mask percent.", - "min": 0.0, - "max": 1000.0 - }, + "default": 4, + "title": "Bits", + "description": "Number of bits to posterize to.", + "min": 1.0, + "max": 8.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image", + "bits" + ], + "is_dynamic": false + }, + { + "title": "Smooth", + "description": "Apply smoothing to reduce image noise and detail.\n image, filter, smooth\n\n - Enhance visual aesthetics of images\n - Improve object detection by reducing irrelevant details\n - Aid facial recognition by simplifying images", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Smooth", + "layout": "default", + "properties": [ { - "name": "threshold", + "name": "image", "type": { - "type": "int" + "type": "image" }, - "default": 3, - "title": "Threshold", - "description": "Unsharp mask threshold.", - "min": 0.0, - "max": 512.0 + "default": {}, + "title": "Image", + "description": "The image to smooth." } ], "outputs": [ @@ -1811,48 +2082,36 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image", - "radius", - "percent", - "threshold" + "image" ], "is_dynamic": false }, { - "title": "Blend", - "description": "Blend two images with adjustable alpha mixing.\n blend, mix, fade, transition\n\n Use cases:\n - Create smooth transitions between images\n - Adjust opacity of overlays\n - Combine multiple exposures or effects", - "namespace": "lib.pillow.__init__", - "node_type": "lib.pillow.__init__.Blend", + "title": "Solarize", + "description": "Apply a solarize effect to partially invert image tones.\n image, filter, solarize\n\n - Create surreal artistic photo effects\n - Enhance visual data by making certain elements more prominent\n - Add a unique style to images for graphic design", + "namespace": "lib.pillow.filter", + "node_type": "lib.pillow.filter.Solarize", "layout": "default", "properties": [ { - "name": "image1", - "type": { - "type": "image" - }, - "default": {}, - "title": "Image1", - "description": "The first image to blend." - }, - { - "name": "image2", + "name": "image", "type": { "type": "image" }, "default": {}, - "title": "Image2", - "description": "The second image to blend." + "title": "Image", + "description": "The image to solarize." }, { - "name": "alpha", + "name": "threshold", "type": { - "type": "float" + "type": "int" }, - "default": 0.5, - "title": "Alpha", - "description": "The mix ratio.", + "default": 128, + "title": "Threshold", + "description": "Threshold for solarization.", "min": 0.0, - "max": 1.0 + "max": 255.0 } ], "outputs": [ @@ -1866,45 +2125,48 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image1", - "image2", - "alpha" + "image", + "threshold" ], "is_dynamic": false }, { - "title": "Composite", - "description": "Combine two images using a mask for advanced compositing.\n composite, mask, blend, layering\n\n Use cases:\n - Create complex image compositions\n - Apply selective blending or effects\n - Implement advanced photo editing techniques", - "namespace": "lib.pillow.__init__", - "node_type": "lib.pillow.__init__.Composite", + "title": "Adaptive Contrast", + "description": "Applies localized contrast enhancement using adaptive techniques.\n image, contrast, enhance\n\n Use cases:\n - Improve visibility in images with varying lighting conditions\n - Prepare images for improved feature detection in computer vision", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.AdaptiveContrast", "layout": "default", "properties": [ { - "name": "image1", + "name": "image", "type": { "type": "image" }, "default": {}, - "title": "Image1", - "description": "The first image to composite." + "title": "Image", + "description": "The image to adjust the contrast for." }, { - "name": "image2", + "name": "clip_limit", "type": { - "type": "image" + "type": "float" }, - "default": {}, - "title": "Image2", - "description": "The second image to composite." + "default": 2.0, + "title": "Clip Limit", + "description": "Clip limit for adaptive contrast.", + "min": 0.0, + "max": 100.0 }, { - "name": "mask", + "name": "grid_size", "type": { - "type": "image" + "type": "int" }, - "default": {}, - "title": "Mask", - "description": "The mask to composite with." + "default": 8, + "title": "Grid Size", + "description": "Grid size for adaptive contrast.", + "min": 1.0, + "max": 64.0 } ], "outputs": [ @@ -1918,17 +2180,17 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image1", - "image2", - "mask" + "image", + "clip_limit", + "grid_size" ], "is_dynamic": false }, { - "title": "Blur", - "description": "Apply a Gaussian blur effect to an image.\n image, filter, blur\n\n - Soften images or reduce noise and detail\n - Make focal areas stand out by blurring surroundings\n - Protect privacy by blurring sensitive information", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Blur", + "title": "Auto Contrast", + "description": "Automatically adjusts image contrast for enhanced visual quality.\n image, contrast, balance\n\n Use cases:\n - Enhance image clarity for better visual perception\n - Pre-process images for computer vision tasks\n - Improve photo aesthetics in editing workflows", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.AutoContrast", "layout": "default", "properties": [ { @@ -1938,18 +2200,18 @@ }, "default": {}, "title": "Image", - "description": "The image to blur." + "description": "The image to adjust the contrast for." }, { - "name": "radius", + "name": "cutoff", "type": { "type": "int" }, - "default": 2, - "title": "Radius", - "description": "Blur radius.", + "default": 0, + "title": "Cutoff", + "description": "Represents the percentage of pixels to ignore at both the darkest and lightest ends of the histogram. A cutoff value of 5 means ignoring the darkest 5% and the lightest 5% of pixels, enhancing overall contrast by stretching the remaining pixel values across the full brightness range.", "min": 0.0, - "max": 128.0 + "max": 255.0 } ], "outputs": [ @@ -1964,15 +2226,15 @@ "recommended_models": [], "basic_fields": [ "image", - "radius" + "cutoff" ], "is_dynamic": false }, { - "title": "Canny", - "description": "Apply Canny edge detection to an image.\n image, filter, edges\n\n - Highlight areas of rapid intensity change\n - Outline object boundaries and structure\n - Enhance inputs for object detection and image segmentation", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Canny", + "title": "Brightness", + "description": "Adjusts overall image brightness to lighten or darken.\n image, brightness, enhance\n\n Use cases:\n - Correct underexposed or overexposed photographs\n - Enhance visibility of dark image regions\n - Prepare images for consistent display across devices", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Brightness", "layout": "default", "properties": [ { @@ -1982,29 +2244,24 @@ }, "default": {}, "title": "Image", - "description": "The image to canny." - }, - { - "name": "low_threshold", - "type": { - "type": "int" - }, - "default": 100, - "title": "Low Threshold", - "description": "Low threshold.", - "min": 0.0, - "max": 255.0 + "description": "The image to adjust the brightness for." }, { - "name": "high_threshold", + "name": "factor", "type": { - "type": "int" + "type": "union", + "type_args": [ + { + "type": "float" + }, + { + "type": "int" + } + ] }, - "default": 200, - "title": "High Threshold", - "description": "High threshold.", - "min": 0.0, - "max": 255.0 + "default": 1.0, + "title": "Factor", + "description": "Factor to adjust the brightness. 1.0 means no change." } ], "outputs": [ @@ -2019,16 +2276,15 @@ "recommended_models": [], "basic_fields": [ "image", - "low_threshold", - "high_threshold" + "factor" ], "is_dynamic": false }, { - "title": "Contour", - "description": "Apply a contour filter to highlight image edges.\n image, filter, contour\n\n - Extract key features from complex images\n - Aid pattern recognition and object detection\n - Create stylized contour sketch art effects", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Contour", + "title": "Color", + "description": "Adjusts color intensity of an image.\n image, color, enhance\n\n Use cases:\n - Enhance color vibrancy in photographs\n - Correct color imbalances in digital images\n - Prepare images for consistent brand color representation", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Color", "layout": "default", "properties": [ { @@ -2038,7 +2294,16 @@ }, "default": {}, "title": "Image", - "description": "The image to contour." + "description": "The image to adjust the brightness for." + }, + { + "name": "factor", + "type": { + "type": "float" + }, + "default": 1.0, + "title": "Factor", + "description": "Factor to adjust the contrast. 1.0 means no change." } ], "outputs": [ @@ -2052,15 +2317,16 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image" + "image", + "factor" ], "is_dynamic": false }, { - "title": "Convert To Grayscale", - "description": "Convert an image to grayscale.\n image, grayscale\n\n - Simplify images for feature and edge detection\n - Prepare images for shape-based machine learning\n - Create vintage or monochrome aesthetic effects", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.ConvertToGrayscale", + "title": "Contrast", + "description": "Adjusts image contrast to modify light-dark differences.\n image, contrast, enhance\n\n Use cases:\n - Enhance visibility of details in low-contrast images\n - Prepare images for visual analysis or recognition tasks\n - Create dramatic effects in artistic photography", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Contrast", "layout": "default", "properties": [ { @@ -2070,7 +2336,16 @@ }, "default": {}, "title": "Image", - "description": "The image to convert." + "description": "The image to adjust the brightness for." + }, + { + "name": "factor", + "type": { + "type": "float" + }, + "default": 1.0, + "title": "Factor", + "description": "Factor to adjust the contrast. 1.0 means no change." } ], "outputs": [ @@ -2084,15 +2359,16 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image" + "image", + "factor" ], "is_dynamic": false }, { - "title": "Emboss", - "description": "Apply an emboss filter for a 3D raised effect.\n image, filter, emboss\n\n - Add texture and depth to photos\n - Create visually interesting graphics\n - Incorporate unique effects in digital artwork", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Emboss", + "title": "Detail", + "description": "Enhances fine details in images.\n image, detail, enhance\n\n Use cases:\n - Improve clarity of textural elements in photographs\n - Enhance visibility of small features for analysis\n - Prepare images for high-resolution display or printing", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Detail", "layout": "default", "properties": [ { @@ -2102,7 +2378,7 @@ }, "default": {}, "title": "Image", - "description": "The image to emboss." + "description": "The image to detail." } ], "outputs": [ @@ -2121,10 +2397,10 @@ "is_dynamic": false }, { - "title": "Expand", - "description": "Add a border around an image to increase its size.\n image, border, expand\n\n - Make images stand out by adding a colored border\n - Create framed photo effects\n - Separate image content from surroundings", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Expand", + "title": "Edge Enhance", + "description": "Enhances edge visibility by increasing contrast along boundaries.\n image, edge, enhance\n\n Use cases:\n - Improve object boundary detection for computer vision\n - Highlight structural elements in technical drawings\n - Prepare images for feature extraction in image analysis", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.EdgeEnhance", "layout": "default", "properties": [ { @@ -2134,29 +2410,7 @@ }, "default": {}, "title": "Image", - "description": "The image to expand." - }, - { - "name": "border", - "type": { - "type": "int" - }, - "default": 0, - "title": "Border", - "description": "Border size.", - "min": 0.0, - "max": 512.0 - }, - { - "name": "fill", - "type": { - "type": "int" - }, - "default": 0, - "title": "Fill", - "description": "Fill color.", - "min": 0.0, - "max": 255.0 + "description": "The image to edge enhance." } ], "outputs": [ @@ -2170,17 +2424,15 @@ "the_model_info": {}, "recommended_models": [], "basic_fields": [ - "image", - "border", - "fill" + "image" ], "is_dynamic": false }, { - "title": "Find Edges", - "description": "Detect and highlight edges in an image.\n image, filter, edges\n\n - Analyze structural patterns in images\n - Aid object detection in computer vision\n - Detect important features like corners and ridges", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.FindEdges", + "title": "Equalize", + "description": "Enhances image contrast by equalizing intensity distribution.\n image, contrast, histogram\n\n Use cases:\n - Improve visibility in poorly lit images\n - Enhance details for image analysis tasks\n - Normalize image data for machine learning", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Equalize", "layout": "default", "properties": [ { @@ -2190,7 +2442,7 @@ }, "default": {}, "title": "Image", - "description": "The image to find edges." + "description": "The image to equalize." } ], "outputs": [ @@ -2209,10 +2461,10 @@ "is_dynamic": false }, { - "title": "Get Channel", - "description": "Extract a specific color channel from an image.\n image, color, channel, isolate, extract\n\n - Isolate color information for image analysis\n - Manipulate specific color components in graphic design\n - Enhance or reduce visibility of certain colors", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.GetChannel", + "title": "Rank Filter", + "description": "Applies rank-based filtering to enhance or smooth image features.\n image, filter, enhance\n\n Use cases:\n - Reduce noise while preserving edges in images\n - Enhance specific image features based on local intensity\n - Pre-process images for improved segmentation results", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.RankFilter", "layout": "default", "properties": [ { @@ -2222,21 +2474,29 @@ }, "default": {}, "title": "Image", - "description": "The image to get the channel from." + "description": "The image to rank filter." }, { - "name": "channel", + "name": "size", "type": { - "type": "enum", - "values": [ - "R", - "G", - "B" - ], - "type_name": "nodetool.nodes.lib.pillow.filter.ChannelEnum" + "type": "int" }, - "default": "R", - "title": "Channel" + "default": 3, + "title": "Size", + "description": "Rank filter size.", + "min": 1.0, + "max": 512.0 + }, + { + "name": "rank", + "type": { + "type": "int" + }, + "default": 3, + "title": "Rank", + "description": "Rank filter rank.", + "min": 1.0, + "max": 512.0 } ], "outputs": [ @@ -2251,15 +2511,16 @@ "recommended_models": [], "basic_fields": [ "image", - "channel" + "size", + "rank" ], "is_dynamic": false }, { - "title": "Invert", - "description": "Invert the colors of an image.\n image, filter, invert\n\n - Create negative versions of images for visual effects\n - Analyze image data by bringing out hidden details\n - Preprocess images for operations that work better on inverted colors", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Invert", + "title": "Sharpen", + "description": "Enhances image detail by intensifying local pixel contrast.\n image, sharpen, clarity\n\n Use cases:\n - Improve clarity of photographs for print or display\n - Refine texture details in product photography\n - Enhance readability of text in document images", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Sharpen", "layout": "default", "properties": [ { @@ -2269,7 +2530,7 @@ }, "default": {}, "title": "Image", - "description": "The image to adjust the brightness for." + "description": "The image to sharpen." } ], "outputs": [ @@ -2288,10 +2549,10 @@ "is_dynamic": false }, { - "title": "Posterize", - "description": "Reduce the number of colors in an image for a poster-like effect.\n image, filter, posterize\n\n - Create graphic art by simplifying image colors\n - Apply artistic effects to photographs\n - Generate visually compelling content for advertising", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Posterize", + "title": "Sharpness", + "description": "Adjusts image sharpness to enhance or reduce detail clarity.\n image, clarity, sharpness\n\n Use cases:\n - Enhance photo details for improved visual appeal\n - Refine images for object detection tasks\n - Correct slightly blurred images", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.Sharpness", "layout": "default", "properties": [ { @@ -2301,18 +2562,16 @@ }, "default": {}, "title": "Image", - "description": "The image to posterize." + "description": "The image to adjust the brightness for." }, { - "name": "bits", + "name": "factor", "type": { - "type": "int" + "type": "float" }, - "default": 4, - "title": "Bits", - "description": "Number of bits to posterize to.", - "min": 1.0, - "max": 8.0 + "default": 1.0, + "title": "Factor", + "description": "Factor to adjust the contrast. 1.0 means no change." } ], "outputs": [ @@ -2327,15 +2586,15 @@ "recommended_models": [], "basic_fields": [ "image", - "bits" + "factor" ], "is_dynamic": false }, { - "title": "Smooth", - "description": "Apply smoothing to reduce image noise and detail.\n image, filter, smooth\n\n - Enhance visual aesthetics of images\n - Improve object detection by reducing irrelevant details\n - Aid facial recognition by simplifying images", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Smooth", + "title": "Unsharp Mask", + "description": "Sharpens images using the unsharp mask technique.\n image, sharpen, enhance\n\n Use cases:\n - Enhance edge definition in photographs\n - Improve perceived sharpness of digital artwork\n - Prepare images for high-quality printing or display", + "namespace": "lib.pillow.enhance", + "node_type": "lib.pillow.enhance.UnsharpMask", "layout": "default", "properties": [ { @@ -2345,50 +2604,40 @@ }, "default": {}, "title": "Image", - "description": "The image to smooth." - } - ], - "outputs": [ + "description": "The image to unsharp mask." + }, { + "name": "radius", "type": { - "type": "image" + "type": "int" }, - "name": "output" - } - ], - "the_model_info": {}, - "recommended_models": [], - "basic_fields": [ - "image" - ], - "is_dynamic": false - }, - { - "title": "Solarize", - "description": "Apply a solarize effect to partially invert image tones.\n image, filter, solarize\n\n - Create surreal artistic photo effects\n - Enhance visual data by making certain elements more prominent\n - Add a unique style to images for graphic design", - "namespace": "lib.pillow.filter", - "node_type": "lib.pillow.filter.Solarize", - "layout": "default", - "properties": [ + "default": 2, + "title": "Radius", + "description": "Unsharp mask radius.", + "min": 0.0, + "max": 512.0 + }, { - "name": "image", + "name": "percent", "type": { - "type": "image" + "type": "int" }, - "default": {}, - "title": "Image", - "description": "The image to solarize." + "default": 150, + "title": "Percent", + "description": "Unsharp mask percent.", + "min": 0.0, + "max": 1000.0 }, { "name": "threshold", "type": { "type": "int" }, - "default": 128, + "default": 3, "title": "Threshold", - "description": "Threshold for solarization.", + "description": "Unsharp mask threshold.", "min": 0.0, - "max": 255.0 + "max": 512.0 } ], "outputs": [ @@ -2403,6 +2652,8 @@ "recommended_models": [], "basic_fields": [ "image", + "radius", + "percent", "threshold" ], "is_dynamic": false @@ -2656,6 +2907,112 @@ "image" ], "is_dynamic": false + }, + { + "title": "Blend", + "description": "Blend two images with adjustable alpha mixing.\n blend, mix, fade, transition\n\n Use cases:\n - Create smooth transitions between images\n - Adjust opacity of overlays\n - Combine multiple exposures or effects", + "namespace": "lib.pillow.__init__", + "node_type": "lib.pillow.__init__.Blend", + "layout": "default", + "properties": [ + { + "name": "image1", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image1", + "description": "The first image to blend." + }, + { + "name": "image2", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image2", + "description": "The second image to blend." + }, + { + "name": "alpha", + "type": { + "type": "float" + }, + "default": 0.5, + "title": "Alpha", + "description": "The mix ratio.", + "min": 0.0, + "max": 1.0 + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image1", + "image2", + "alpha" + ], + "is_dynamic": false + }, + { + "title": "Composite", + "description": "Combine two images using a mask for advanced compositing.\n composite, mask, blend, layering\n\n Use cases:\n - Create complex image compositions\n - Apply selective blending or effects\n - Implement advanced photo editing techniques", + "namespace": "lib.pillow.__init__", + "node_type": "lib.pillow.__init__.Composite", + "layout": "default", + "properties": [ + { + "name": "image1", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image1", + "description": "The first image to composite." + }, + { + "name": "image2", + "type": { + "type": "image" + }, + "default": {}, + "title": "Image2", + "description": "The second image to composite." + }, + { + "name": "mask", + "type": { + "type": "image" + }, + "default": {}, + "title": "Mask", + "description": "The mask to composite with." + } + ], + "outputs": [ + { + "type": { + "type": "image" + }, + "name": "output" + } + ], + "the_model_info": {}, + "recommended_models": [], + "basic_fields": [ + "image1", + "image2", + "mask" + ], + "is_dynamic": false } ], "assets": [ diff --git a/tests/conftest.py b/tests/conftest.py index 4855a2c..309a9d9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,14 @@ +import sys +from pathlib import Path + import pytest from nodetool.workflows.processing_context import ProcessingContext +# Ensure local sources are importable +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) + @pytest.fixture def context(): """Provide a ProcessingContext fixture for tests.""" - return ProcessingContext() \ No newline at end of file + return ProcessingContext()