mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(nodes): add WithBoard
field helper class
This class works the same way as `WithMetadata` - it simply adds a `board` field to the node. The context wrapper function is able to pull the board id from this. This allows image-outputting nodes to get a board field "for free", and have their outputs automatically saved to it. This is a breaking change for node authors who may have a field called `board`, because it makes `board` a reserved field name. I'll look into how to avoid this - maybe by naming this invoke-managed field `_board` to avoid collisions? Supporting changes: - `WithBoard` is added to all image-outputting nodes, giving them the ability to save to board. - Unused, duplicate `WithMetadata` and `WithWorkflow` classes are deleted from `baseinvocation.py`. The "real" versions are in `fields.py`. - Remove `LinearUIOutputInvocation`. Now that all nodes that output images also have a `board` field by default, this node is no longer necessary. See comment here for context: https://github.com/invoke-ai/InvokeAI/pull/5491#discussion_r1480760629 - Without `LinearUIOutputInvocation`, the `ImagesInferface.update` method is no longer needed, and removed. Note: This commit does not bump all node versions. I will ensure that is done correctly before merging the PR of which this commit is a part. Note: A followup commit will implement the frontend changes to support this change.
This commit is contained in:
parent
1a191c4655
commit
c16eba78ab
@ -17,11 +17,8 @@ from pydantic.fields import FieldInfo
|
|||||||
from pydantic_core import PydanticUndefined
|
from pydantic_core import PydanticUndefined
|
||||||
|
|
||||||
from invokeai.app.invocations.fields import (
|
from invokeai.app.invocations.fields import (
|
||||||
FieldDescriptions,
|
|
||||||
FieldKind,
|
FieldKind,
|
||||||
Input,
|
Input,
|
||||||
InputFieldJSONSchemaExtra,
|
|
||||||
MetadataField,
|
|
||||||
)
|
)
|
||||||
from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
||||||
from invokeai.app.services.shared.invocation_context import InvocationContext
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
||||||
@ -306,9 +303,7 @@ RESERVED_NODE_ATTRIBUTE_FIELD_NAMES = {
|
|||||||
"workflow",
|
"workflow",
|
||||||
}
|
}
|
||||||
|
|
||||||
RESERVED_INPUT_FIELD_NAMES = {
|
RESERVED_INPUT_FIELD_NAMES = {"metadata", "board"}
|
||||||
"metadata",
|
|
||||||
}
|
|
||||||
|
|
||||||
RESERVED_OUTPUT_FIELD_NAMES = {"type"}
|
RESERVED_OUTPUT_FIELD_NAMES = {"type"}
|
||||||
|
|
||||||
@ -518,29 +513,3 @@ def invocation_output(
|
|||||||
return cls
|
return cls
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class WithMetadata(BaseModel):
|
|
||||||
"""
|
|
||||||
Inherit from this class if your node needs a metadata input field.
|
|
||||||
"""
|
|
||||||
|
|
||||||
metadata: Optional[MetadataField] = Field(
|
|
||||||
default=None,
|
|
||||||
description=FieldDescriptions.metadata,
|
|
||||||
json_schema_extra=InputFieldJSONSchemaExtra(
|
|
||||||
field_kind=FieldKind.Internal,
|
|
||||||
input=Input.Connection,
|
|
||||||
orig_required=False,
|
|
||||||
).model_dump(exclude_none=True),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class WithWorkflow:
|
|
||||||
workflow = None
|
|
||||||
|
|
||||||
def __init_subclass__(cls) -> None:
|
|
||||||
logger.warn(
|
|
||||||
f"{cls.__module__.split('.')[0]}.{cls.__name__}: WithWorkflow is deprecated. Use `context.workflow` to access the workflow."
|
|
||||||
)
|
|
||||||
super().__init_subclass__()
|
|
||||||
|
@ -25,7 +25,15 @@ from controlnet_aux.util import HWC3, ade_palette
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||||
|
|
||||||
from invokeai.app.invocations.fields import FieldDescriptions, ImageField, Input, InputField, OutputField, WithMetadata
|
from invokeai.app.invocations.fields import (
|
||||||
|
FieldDescriptions,
|
||||||
|
ImageField,
|
||||||
|
Input,
|
||||||
|
InputField,
|
||||||
|
OutputField,
|
||||||
|
WithBoard,
|
||||||
|
WithMetadata,
|
||||||
|
)
|
||||||
from invokeai.app.invocations.primitives import ImageOutput
|
from invokeai.app.invocations.primitives import ImageOutput
|
||||||
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
|
from invokeai.app.invocations.util import validate_begin_end_step, validate_weights
|
||||||
from invokeai.app.services.shared.invocation_context import InvocationContext
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
||||||
@ -135,7 +143,7 @@ class ControlNetInvocation(BaseInvocation):
|
|||||||
|
|
||||||
|
|
||||||
# This invocation exists for other invocations to subclass it - do not register with @invocation!
|
# This invocation exists for other invocations to subclass it - do not register with @invocation!
|
||||||
class ImageProcessorInvocation(BaseInvocation, WithMetadata):
|
class ImageProcessorInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Base class for invocations that preprocess images for ControlNet"""
|
"""Base class for invocations that preprocess images for ControlNet"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to process")
|
image: ImageField = InputField(description="The image to process")
|
||||||
|
@ -10,11 +10,11 @@ from invokeai.app.invocations.primitives import ImageOutput
|
|||||||
from invokeai.app.services.shared.invocation_context import InvocationContext
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, invocation
|
from .baseinvocation import BaseInvocation, invocation
|
||||||
from .fields import InputField, WithMetadata
|
from .fields import InputField, WithBoard, WithMetadata
|
||||||
|
|
||||||
|
|
||||||
@invocation("cv_inpaint", title="OpenCV Inpaint", tags=["opencv", "inpaint"], category="inpaint", version="1.2.1")
|
@invocation("cv_inpaint", title="OpenCV Inpaint", tags=["opencv", "inpaint"], category="inpaint", version="1.2.1")
|
||||||
class CvInpaintInvocation(BaseInvocation, WithMetadata):
|
class CvInpaintInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Simple inpaint using opencv."""
|
"""Simple inpaint using opencv."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to inpaint")
|
image: ImageField = InputField(description="The image to inpaint")
|
||||||
|
@ -16,7 +16,7 @@ from invokeai.app.invocations.baseinvocation import (
|
|||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.fields import ImageField, InputField, OutputField, WithMetadata
|
from invokeai.app.invocations.fields import ImageField, InputField, OutputField, WithBoard, WithMetadata
|
||||||
from invokeai.app.invocations.primitives import ImageOutput
|
from invokeai.app.invocations.primitives import ImageOutput
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory
|
from invokeai.app.services.image_records.image_records_common import ImageCategory
|
||||||
from invokeai.app.services.shared.invocation_context import InvocationContext
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
||||||
@ -619,7 +619,7 @@ class FaceMaskInvocation(BaseInvocation, WithMetadata):
|
|||||||
@invocation(
|
@invocation(
|
||||||
"face_identifier", title="FaceIdentifier", tags=["image", "face", "identifier"], category="image", version="1.2.1"
|
"face_identifier", title="FaceIdentifier", tags=["image", "face", "identifier"], category="image", version="1.2.1"
|
||||||
)
|
)
|
||||||
class FaceIdentifierInvocation(BaseInvocation, WithMetadata):
|
class FaceIdentifierInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Outputs an image with detected face IDs printed on each face. For use with other FaceTools."""
|
"""Outputs an image with detected face IDs printed on each face. For use with other FaceTools."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="Image to face detect")
|
image: ImageField = InputField(description="Image to face detect")
|
||||||
|
@ -280,6 +280,22 @@ class WithWorkflow:
|
|||||||
super().__init_subclass__()
|
super().__init_subclass__()
|
||||||
|
|
||||||
|
|
||||||
|
class WithBoard(BaseModel):
|
||||||
|
"""
|
||||||
|
Inherit from this class if your node needs a board input field.
|
||||||
|
"""
|
||||||
|
|
||||||
|
board: Optional["BoardField"] = Field(
|
||||||
|
default=None,
|
||||||
|
description=FieldDescriptions.board,
|
||||||
|
json_schema_extra=InputFieldJSONSchemaExtra(
|
||||||
|
field_kind=FieldKind.Internal,
|
||||||
|
input=Input.Direct,
|
||||||
|
orig_required=False,
|
||||||
|
).model_dump(exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OutputFieldJSONSchemaExtra(BaseModel):
|
class OutputFieldJSONSchemaExtra(BaseModel):
|
||||||
"""
|
"""
|
||||||
Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor
|
Extra attributes to be added to input fields and their OpenAPI schema. Used by the workflow editor
|
||||||
|
@ -8,12 +8,11 @@ import numpy
|
|||||||
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
from PIL import Image, ImageChops, ImageFilter, ImageOps
|
||||||
|
|
||||||
from invokeai.app.invocations.fields import (
|
from invokeai.app.invocations.fields import (
|
||||||
BoardField,
|
|
||||||
ColorField,
|
ColorField,
|
||||||
FieldDescriptions,
|
FieldDescriptions,
|
||||||
ImageField,
|
ImageField,
|
||||||
Input,
|
|
||||||
InputField,
|
InputField,
|
||||||
|
WithBoard,
|
||||||
WithMetadata,
|
WithMetadata,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.primitives import ImageOutput
|
from invokeai.app.invocations.primitives import ImageOutput
|
||||||
@ -55,7 +54,7 @@ class ShowImageInvocation(BaseInvocation):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class BlankImageInvocation(BaseInvocation, WithMetadata):
|
class BlankImageInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Creates a blank image and forwards it to the pipeline"""
|
"""Creates a blank image and forwards it to the pipeline"""
|
||||||
|
|
||||||
width: int = InputField(default=512, description="The width of the image")
|
width: int = InputField(default=512, description="The width of the image")
|
||||||
@ -78,7 +77,7 @@ class BlankImageInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageCropInvocation(BaseInvocation, WithMetadata):
|
class ImageCropInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Crops an image to a specified box. The box can be outside of the image."""
|
"""Crops an image to a specified box. The box can be outside of the image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to crop")
|
image: ImageField = InputField(description="The image to crop")
|
||||||
@ -149,7 +148,7 @@ class CenterPadCropInvocation(BaseInvocation):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImagePasteInvocation(BaseInvocation, WithMetadata):
|
class ImagePasteInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Pastes an image into another image."""
|
"""Pastes an image into another image."""
|
||||||
|
|
||||||
base_image: ImageField = InputField(description="The base image")
|
base_image: ImageField = InputField(description="The base image")
|
||||||
@ -196,7 +195,7 @@ class ImagePasteInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class MaskFromAlphaInvocation(BaseInvocation, WithMetadata):
|
class MaskFromAlphaInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Extracts the alpha channel of an image as a mask."""
|
"""Extracts the alpha channel of an image as a mask."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to create the mask from")
|
image: ImageField = InputField(description="The image to create the mask from")
|
||||||
@ -221,7 +220,7 @@ class MaskFromAlphaInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageMultiplyInvocation(BaseInvocation, WithMetadata):
|
class ImageMultiplyInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Multiplies two images together using `PIL.ImageChops.multiply()`."""
|
"""Multiplies two images together using `PIL.ImageChops.multiply()`."""
|
||||||
|
|
||||||
image1: ImageField = InputField(description="The first image to multiply")
|
image1: ImageField = InputField(description="The first image to multiply")
|
||||||
@ -248,7 +247,7 @@ IMAGE_CHANNELS = Literal["A", "R", "G", "B"]
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageChannelInvocation(BaseInvocation, WithMetadata):
|
class ImageChannelInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Gets a channel from an image."""
|
"""Gets a channel from an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to get the channel from")
|
image: ImageField = InputField(description="The image to get the channel from")
|
||||||
@ -274,7 +273,7 @@ IMAGE_MODES = Literal["L", "RGB", "RGBA", "CMYK", "YCbCr", "LAB", "HSV", "I", "F
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageConvertInvocation(BaseInvocation, WithMetadata):
|
class ImageConvertInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Converts an image to a different mode."""
|
"""Converts an image to a different mode."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to convert")
|
image: ImageField = InputField(description="The image to convert")
|
||||||
@ -297,7 +296,7 @@ class ImageConvertInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageBlurInvocation(BaseInvocation, WithMetadata):
|
class ImageBlurInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Blurs an image"""
|
"""Blurs an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to blur")
|
image: ImageField = InputField(description="The image to blur")
|
||||||
@ -326,7 +325,7 @@ class ImageBlurInvocation(BaseInvocation, WithMetadata):
|
|||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
classification=Classification.Beta,
|
classification=Classification.Beta,
|
||||||
)
|
)
|
||||||
class UnsharpMaskInvocation(BaseInvocation, WithMetadata):
|
class UnsharpMaskInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Applies an unsharp mask filter to an image"""
|
"""Applies an unsharp mask filter to an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to use")
|
image: ImageField = InputField(description="The image to use")
|
||||||
@ -394,7 +393,7 @@ PIL_RESAMPLING_MAP = {
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageResizeInvocation(BaseInvocation, WithMetadata):
|
class ImageResizeInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Resizes an image to specific dimensions"""
|
"""Resizes an image to specific dimensions"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to resize")
|
image: ImageField = InputField(description="The image to resize")
|
||||||
@ -424,7 +423,7 @@ class ImageResizeInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageScaleInvocation(BaseInvocation, WithMetadata):
|
class ImageScaleInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Scales an image by a factor"""
|
"""Scales an image by a factor"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to scale")
|
image: ImageField = InputField(description="The image to scale")
|
||||||
@ -459,7 +458,7 @@ class ImageScaleInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageLerpInvocation(BaseInvocation, WithMetadata):
|
class ImageLerpInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Linear interpolation of all pixels of an image"""
|
"""Linear interpolation of all pixels of an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to lerp")
|
image: ImageField = InputField(description="The image to lerp")
|
||||||
@ -486,7 +485,7 @@ class ImageLerpInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageInverseLerpInvocation(BaseInvocation, WithMetadata):
|
class ImageInverseLerpInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Inverse linear interpolation of all pixels of an image"""
|
"""Inverse linear interpolation of all pixels of an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to lerp")
|
image: ImageField = InputField(description="The image to lerp")
|
||||||
@ -513,7 +512,7 @@ class ImageInverseLerpInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata):
|
class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Add blur to NSFW-flagged images"""
|
"""Add blur to NSFW-flagged images"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to check")
|
image: ImageField = InputField(description="The image to check")
|
||||||
@ -548,7 +547,7 @@ class ImageNSFWBlurInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageWatermarkInvocation(BaseInvocation, WithMetadata):
|
class ImageWatermarkInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Add an invisible watermark to an image"""
|
"""Add an invisible watermark to an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to check")
|
image: ImageField = InputField(description="The image to check")
|
||||||
@ -569,7 +568,7 @@ class ImageWatermarkInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class MaskEdgeInvocation(BaseInvocation, WithMetadata):
|
class MaskEdgeInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Applies an edge mask to an image"""
|
"""Applies an edge mask to an image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to apply the mask to")
|
image: ImageField = InputField(description="The image to apply the mask to")
|
||||||
@ -608,7 +607,7 @@ class MaskEdgeInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class MaskCombineInvocation(BaseInvocation, WithMetadata):
|
class MaskCombineInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`."""
|
"""Combine two masks together by multiplying them using `PIL.ImageChops.multiply()`."""
|
||||||
|
|
||||||
mask1: ImageField = InputField(description="The first mask to combine")
|
mask1: ImageField = InputField(description="The first mask to combine")
|
||||||
@ -632,7 +631,7 @@ class MaskCombineInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ColorCorrectInvocation(BaseInvocation, WithMetadata):
|
class ColorCorrectInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""
|
"""
|
||||||
Shifts the colors of a target image to match the reference image, optionally
|
Shifts the colors of a target image to match the reference image, optionally
|
||||||
using a mask to only color-correct certain regions of the target image.
|
using a mask to only color-correct certain regions of the target image.
|
||||||
@ -736,7 +735,7 @@ class ColorCorrectInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageHueAdjustmentInvocation(BaseInvocation, WithMetadata):
|
class ImageHueAdjustmentInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Adjusts the Hue of an image."""
|
"""Adjusts the Hue of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@ -825,7 +824,7 @@ CHANNEL_FORMATS = {
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageChannelOffsetInvocation(BaseInvocation, WithMetadata):
|
class ImageChannelOffsetInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Add or subtract a value from a specific color channel of an image."""
|
"""Add or subtract a value from a specific color channel of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@ -881,7 +880,7 @@ class ImageChannelOffsetInvocation(BaseInvocation, WithMetadata):
|
|||||||
category="image",
|
category="image",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class ImageChannelMultiplyInvocation(BaseInvocation, WithMetadata):
|
class ImageChannelMultiplyInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Scale a specific color channel of an image."""
|
"""Scale a specific color channel of an image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to adjust")
|
image: ImageField = InputField(description="The image to adjust")
|
||||||
@ -926,41 +925,14 @@ class ImageChannelMultiplyInvocation(BaseInvocation, WithMetadata):
|
|||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
use_cache=False,
|
use_cache=False,
|
||||||
)
|
)
|
||||||
class SaveImageInvocation(BaseInvocation, WithMetadata):
|
class SaveImageInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
"""Saves an image. Unlike an image primitive, this invocation stores a copy of the image."""
|
||||||
|
|
||||||
image: ImageField = InputField(description=FieldDescriptions.image)
|
image: ImageField = InputField(description=FieldDescriptions.image)
|
||||||
board: BoardField = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
image = context.images.get_pil(self.image.image_name)
|
image = context.images.get_pil(self.image.image_name)
|
||||||
|
|
||||||
image_dto = context.images.save(image=image, board_id=self.board.board_id if self.board else None)
|
image_dto = context.images.save(image=image)
|
||||||
|
|
||||||
return ImageOutput.build(image_dto)
|
|
||||||
|
|
||||||
|
|
||||||
@invocation(
|
|
||||||
"linear_ui_output",
|
|
||||||
title="Linear UI Image Output",
|
|
||||||
tags=["primitives", "image"],
|
|
||||||
category="primitives",
|
|
||||||
version="1.0.2",
|
|
||||||
use_cache=False,
|
|
||||||
)
|
|
||||||
class LinearUIOutputInvocation(BaseInvocation, WithMetadata):
|
|
||||||
"""Handles Linear UI Image Outputting tasks."""
|
|
||||||
|
|
||||||
image: ImageField = InputField(description=FieldDescriptions.image)
|
|
||||||
board: Optional[BoardField] = InputField(default=None, description=FieldDescriptions.board, input=Input.Direct)
|
|
||||||
|
|
||||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
|
||||||
image_dto = context.images.get_dto(self.image.image_name)
|
|
||||||
|
|
||||||
image_dto = context.images.update(
|
|
||||||
image_name=self.image.image_name,
|
|
||||||
board_id=self.board.board_id if self.board else None,
|
|
||||||
is_intermediate=self.is_intermediate,
|
|
||||||
)
|
|
||||||
|
|
||||||
return ImageOutput.build(image_dto)
|
return ImageOutput.build(image_dto)
|
||||||
|
@ -15,7 +15,7 @@ from invokeai.backend.image_util.lama import LaMA
|
|||||||
from invokeai.backend.image_util.patchmatch import PatchMatch
|
from invokeai.backend.image_util.patchmatch import PatchMatch
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, invocation
|
from .baseinvocation import BaseInvocation, invocation
|
||||||
from .fields import InputField, WithMetadata
|
from .fields import InputField, WithBoard, WithMetadata
|
||||||
from .image import PIL_RESAMPLING_MAP, PIL_RESAMPLING_MODES
|
from .image import PIL_RESAMPLING_MAP, PIL_RESAMPLING_MODES
|
||||||
|
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ def tile_fill_missing(im: Image.Image, tile_size: int = 16, seed: Optional[int]
|
|||||||
|
|
||||||
|
|
||||||
@invocation("infill_rgba", title="Solid Color Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
@invocation("infill_rgba", title="Solid Color Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
||||||
class InfillColorInvocation(BaseInvocation, WithMetadata):
|
class InfillColorInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Infills transparent areas of an image with a solid color"""
|
"""Infills transparent areas of an image with a solid color"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@ -144,7 +144,7 @@ class InfillColorInvocation(BaseInvocation, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("infill_tile", title="Tile Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.2")
|
@invocation("infill_tile", title="Tile Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.2")
|
||||||
class InfillTileInvocation(BaseInvocation, WithMetadata):
|
class InfillTileInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Infills transparent areas of an image with tiles of the image"""
|
"""Infills transparent areas of an image with tiles of the image"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@ -170,7 +170,7 @@ class InfillTileInvocation(BaseInvocation, WithMetadata):
|
|||||||
@invocation(
|
@invocation(
|
||||||
"infill_patchmatch", title="PatchMatch Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1"
|
"infill_patchmatch", title="PatchMatch Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1"
|
||||||
)
|
)
|
||||||
class InfillPatchMatchInvocation(BaseInvocation, WithMetadata):
|
class InfillPatchMatchInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Infills transparent areas of an image using the PatchMatch algorithm"""
|
"""Infills transparent areas of an image using the PatchMatch algorithm"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@ -209,7 +209,7 @@ class InfillPatchMatchInvocation(BaseInvocation, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("infill_lama", title="LaMa Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
@invocation("infill_lama", title="LaMa Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
||||||
class LaMaInfillInvocation(BaseInvocation, WithMetadata):
|
class LaMaInfillInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Infills transparent areas of an image using the LaMa model"""
|
"""Infills transparent areas of an image using the LaMa model"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
@ -225,7 +225,7 @@ class LaMaInfillInvocation(BaseInvocation, WithMetadata):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("infill_cv2", title="CV2 Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
@invocation("infill_cv2", title="CV2 Infill", tags=["image", "inpaint"], category="inpaint", version="1.2.1")
|
||||||
class CV2InfillInvocation(BaseInvocation, WithMetadata):
|
class CV2InfillInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Infills transparent areas of an image using OpenCV Inpainting"""
|
"""Infills transparent areas of an image using OpenCV Inpainting"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to infill")
|
image: ImageField = InputField(description="The image to infill")
|
||||||
|
@ -33,6 +33,7 @@ from invokeai.app.invocations.fields import (
|
|||||||
LatentsField,
|
LatentsField,
|
||||||
OutputField,
|
OutputField,
|
||||||
UIType,
|
UIType,
|
||||||
|
WithBoard,
|
||||||
WithMetadata,
|
WithMetadata,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.ip_adapter import IPAdapterField
|
from invokeai.app.invocations.ip_adapter import IPAdapterField
|
||||||
@ -762,7 +763,7 @@ class DenoiseLatentsInvocation(BaseInvocation):
|
|||||||
category="latents",
|
category="latents",
|
||||||
version="1.2.1",
|
version="1.2.1",
|
||||||
)
|
)
|
||||||
class LatentsToImageInvocation(BaseInvocation, WithMetadata):
|
class LatentsToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Generates an image from latents."""
|
"""Generates an image from latents."""
|
||||||
|
|
||||||
latents: LatentsField = InputField(
|
latents: LatentsField = InputField(
|
||||||
|
@ -255,9 +255,7 @@ class ImageCollectionOutput(BaseInvocationOutput):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("image", title="Image Primitive", tags=["primitives", "image"], category="primitives", version="1.0.1")
|
@invocation("image", title="Image Primitive", tags=["primitives", "image"], category="primitives", version="1.0.1")
|
||||||
class ImageInvocation(
|
class ImageInvocation(BaseInvocation):
|
||||||
BaseInvocation,
|
|
||||||
):
|
|
||||||
"""An image primitive value"""
|
"""An image primitive value"""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The image to load")
|
image: ImageField = InputField(description="The image to load")
|
||||||
|
@ -11,7 +11,7 @@ from invokeai.app.invocations.baseinvocation import (
|
|||||||
invocation,
|
invocation,
|
||||||
invocation_output,
|
invocation_output,
|
||||||
)
|
)
|
||||||
from invokeai.app.invocations.fields import ImageField, Input, InputField, OutputField, WithMetadata
|
from invokeai.app.invocations.fields import ImageField, Input, InputField, OutputField, WithBoard, WithMetadata
|
||||||
from invokeai.app.invocations.primitives import ImageOutput
|
from invokeai.app.invocations.primitives import ImageOutput
|
||||||
from invokeai.app.services.shared.invocation_context import InvocationContext
|
from invokeai.app.services.shared.invocation_context import InvocationContext
|
||||||
from invokeai.backend.tiles.tiles import (
|
from invokeai.backend.tiles.tiles import (
|
||||||
@ -232,7 +232,7 @@ BLEND_MODES = Literal["Linear", "Seam"]
|
|||||||
version="1.1.0",
|
version="1.1.0",
|
||||||
classification=Classification.Beta,
|
classification=Classification.Beta,
|
||||||
)
|
)
|
||||||
class MergeTilesToImageInvocation(BaseInvocation, WithMetadata):
|
class MergeTilesToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Merge multiple tile images into a single image."""
|
"""Merge multiple tile images into a single image."""
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
@ -16,7 +16,7 @@ from invokeai.backend.image_util.realesrgan.realesrgan import RealESRGAN
|
|||||||
from invokeai.backend.util.devices import choose_torch_device
|
from invokeai.backend.util.devices import choose_torch_device
|
||||||
|
|
||||||
from .baseinvocation import BaseInvocation, invocation
|
from .baseinvocation import BaseInvocation, invocation
|
||||||
from .fields import InputField, WithMetadata
|
from .fields import InputField, WithBoard, WithMetadata
|
||||||
|
|
||||||
# TODO: Populate this from disk?
|
# TODO: Populate this from disk?
|
||||||
# TODO: Use model manager to load?
|
# TODO: Use model manager to load?
|
||||||
@ -32,7 +32,7 @@ if choose_torch_device() == torch.device("mps"):
|
|||||||
|
|
||||||
|
|
||||||
@invocation("esrgan", title="Upscale (RealESRGAN)", tags=["esrgan", "upscale"], category="esrgan", version="1.3.1")
|
@invocation("esrgan", title="Upscale (RealESRGAN)", tags=["esrgan", "upscale"], category="esrgan", version="1.3.1")
|
||||||
class ESRGANInvocation(BaseInvocation, WithMetadata):
|
class ESRGANInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||||
"""Upscales an image using RealESRGAN."""
|
"""Upscales an image using RealESRGAN."""
|
||||||
|
|
||||||
image: ImageField = InputField(description="The input image")
|
image: ImageField = InputField(description="The input image")
|
||||||
|
@ -5,10 +5,10 @@ from deprecated import deprecated
|
|||||||
from PIL.Image import Image
|
from PIL.Image import Image
|
||||||
from torch import Tensor
|
from torch import Tensor
|
||||||
|
|
||||||
from invokeai.app.invocations.fields import MetadataField, WithMetadata
|
from invokeai.app.invocations.fields import MetadataField, WithBoard, WithMetadata
|
||||||
from invokeai.app.services.boards.boards_common import BoardDTO
|
from invokeai.app.services.boards.boards_common import BoardDTO
|
||||||
from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
from invokeai.app.services.config.config_default import InvokeAIAppConfig
|
||||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ImageRecordChanges, ResourceOrigin
|
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
||||||
from invokeai.app.services.images.images_common import ImageDTO
|
from invokeai.app.services.images.images_common import ImageDTO
|
||||||
from invokeai.app.services.invocation_services import InvocationServices
|
from invokeai.app.services.invocation_services import InvocationServices
|
||||||
from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID
|
from invokeai.app.services.workflow_records.workflow_records_common import WorkflowWithoutID
|
||||||
@ -158,7 +158,9 @@ class ImagesInterface(InvocationContextInterface):
|
|||||||
If the current queue item has a workflow or metadata, it is automatically saved with the image.
|
If the current queue item has a workflow or metadata, it is automatically saved with the image.
|
||||||
|
|
||||||
:param image: The image to save, as a PIL image.
|
:param image: The image to save, as a PIL image.
|
||||||
:param board_id: The board ID to add the image to, if it should be added.
|
:param board_id: The board ID to add the image to, if it should be added. It the invocation \
|
||||||
|
inherits from `WithBoard`, that board will be used automatically. **Use this only if \
|
||||||
|
you want to override or provide a board manually!**
|
||||||
:param image_category: The category of the image. Only the GENERAL category is added \
|
:param image_category: The category of the image. Only the GENERAL category is added \
|
||||||
to the gallery.
|
to the gallery.
|
||||||
:param metadata: The metadata to save with the image, if it should have any. If the \
|
:param metadata: The metadata to save with the image, if it should have any. If the \
|
||||||
@ -173,11 +175,15 @@ class ImagesInterface(InvocationContextInterface):
|
|||||||
else metadata
|
else metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# If the invocation inherits WithBoard, use that. Else, use the board_id passed in.
|
||||||
|
board_ = self._context_data.invocation.board if isinstance(self._context_data.invocation, WithBoard) else None
|
||||||
|
board_id_ = board_.board_id if board_ is not None else board_id
|
||||||
|
|
||||||
return self._services.images.create(
|
return self._services.images.create(
|
||||||
image=image,
|
image=image,
|
||||||
is_intermediate=self._context_data.invocation.is_intermediate,
|
is_intermediate=self._context_data.invocation.is_intermediate,
|
||||||
image_category=image_category,
|
image_category=image_category,
|
||||||
board_id=board_id,
|
board_id=board_id_,
|
||||||
metadata=metadata_,
|
metadata=metadata_,
|
||||||
image_origin=ResourceOrigin.INTERNAL,
|
image_origin=ResourceOrigin.INTERNAL,
|
||||||
workflow=self._context_data.workflow,
|
workflow=self._context_data.workflow,
|
||||||
@ -209,32 +215,6 @@ class ImagesInterface(InvocationContextInterface):
|
|||||||
"""
|
"""
|
||||||
return self._services.images.get_dto(image_name)
|
return self._services.images.get_dto(image_name)
|
||||||
|
|
||||||
def update(
|
|
||||||
self,
|
|
||||||
image_name: str,
|
|
||||||
board_id: Optional[str] = None,
|
|
||||||
is_intermediate: Optional[bool] = False,
|
|
||||||
) -> ImageDTO:
|
|
||||||
"""
|
|
||||||
Updates an image, returning its updated DTO.
|
|
||||||
|
|
||||||
It is not suggested to update images saved by earlier nodes, as this can cause confusion for users.
|
|
||||||
|
|
||||||
If you use this method, you *must* return the image as an :class:`ImageOutput` for the gallery to
|
|
||||||
get the updated image.
|
|
||||||
|
|
||||||
:param image_name: The name of the image to update.
|
|
||||||
:param board_id: The board ID to add the image to, if it should be added.
|
|
||||||
:param is_intermediate: Whether the image is an intermediate. Intermediate images aren't added to the gallery.
|
|
||||||
"""
|
|
||||||
if is_intermediate is not None:
|
|
||||||
self._services.images.update(image_name, ImageRecordChanges(is_intermediate=is_intermediate))
|
|
||||||
if board_id is None:
|
|
||||||
self._services.board_images.remove_image_from_board(image_name)
|
|
||||||
else:
|
|
||||||
self._services.board_images.add_image_to_board(image_name, board_id)
|
|
||||||
return self._services.images.get_dto(image_name)
|
|
||||||
|
|
||||||
|
|
||||||
class LatentsInterface(InvocationContextInterface):
|
class LatentsInterface(InvocationContextInterface):
|
||||||
def save(self, tensor: Tensor) -> str:
|
def save(self, tensor: Tensor) -> str:
|
||||||
|
Loading…
Reference in New Issue
Block a user