diff --git a/README.md b/README.md index 8c4c201..ac4ecd0 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ There's also a helper node, 'Split String by Commas' that can be used if you wan *New in 1.1.6* - `Pick from List` to allow you to select matching items from any list -- `Batch from Image List` to work with image batches +- `Batch from Image List` and `Image List from Batch` to work with image batches - hover mouse over image and press space to zoom, space again to close the zoom ## Examples of what you might do with them @@ -98,6 +98,8 @@ generated. Here's a trivial workflow: --- +# A few more things + ## Extras 'Text Image Filter' and 'Image Filter', each provide three extra text fields, intended for short form - like specifying the denoising you want on the next step, or a prefix to save the file with. @@ -112,7 +114,7 @@ If you use the optional 'tip' input, the contents will be displayed under the ex The `Image Filter` node outputs a string, `indexes`, which is a comma separated list of the indexes (0 based) of the images selected. Connect this to a `Pick from List` node, and connect a list of anything to the `anything` input, and the `Pick from List` node will output a list corresponding to the selected images. -For instance, if you create a set of images using a list of prompts, this can be used to select the prompts that correspond to the selected images. +For instance, if you create a set of images using a list of prompts, this can be used to select the prompts that correspond to the selected images. But you might well want to batch the images if you did use lists - see below. So something like this: @@ -120,6 +122,14 @@ So something like this: --- +## Batch and List + +If you are using lists, each node will run once for each list. This means that normally you will want a single batch of images for the `Image Filter` (so if you generate a load of images using lists of, for instance, prompts, you will want to combine them with the helper node `Batch from Image List`) + +If you generate a batch of images and want to use the `Mask Image Filter` or `Text Image Filter`, which only show a single image, you will want to do the opposite - convert the batch to a list with `Image List From Batch`. Then the images will be shown sequentially. + +--- + # Example Workflow ![image](images/three%20filters.png) diff --git a/__init__.py b/__init__.py index 153d559..d6f983b 100644 --- a/__init__.py +++ b/__init__.py @@ -5,8 +5,9 @@ @description: A custom node that pauses the flow while you choose which image or images to pass on to the rest of the workflow. Simplified and improved version of cg-image-picker. """ -from .image_filter import ImageFilter, MaskImageFilter, TextImageFilterWithExtras, PickFromList, BatchFromImageList -from .split_by_commas import SplitByCommas, StringToFloat, StringToInt +from .image_filter import ImageFilter, MaskImageFilter, TextImageFilterWithExtras +from .list_utility_nodes import PickFromList, BatchFromImageList, ImageListFromBatch +from .string_utility_nodes import SplitByCommas, StringToFloat, StringToInt VERSION = "1.1.6" WEB_DIRECTORY = "./js" @@ -21,6 +22,7 @@ NODE_CLASS_MAPPINGS= { "String to Float": StringToFloat, "Pick from List": PickFromList, "Batch from Image List": BatchFromImageList, + "Image List From Batch": ImageListFromBatch, } __all__ = ["NODE_CLASS_MAPPINGS", "WEB_DIRECTORY"] diff --git a/image_filter.py b/image_filter.py index 838ff34..3e0524e 100644 --- a/image_filter.py +++ b/image_filter.py @@ -65,72 +65,6 @@ HIDDEN = { "uid":"UNIQUE_ID" } -from json import JSONEncoder - -def _default(self, obj): - return getattr(obj.__class__, "to_json", _default.default)(obj) - -_default.default = JSONEncoder().default -JSONEncoder.default = _default - -class AlwaysEqual: - def to_json(self): - return "*" - - def __ne__(self, __value: object) -> bool: - return False - - def __eq__(self, __value: object) -> bool: - return True - - -class BatchFromImageList: - @classmethod - def INPUT_TYPES(s): - return {"required": { - "images": ("IMAGE", ), - } - } - - INPUT_IS_LIST = True - - RETURN_TYPES = ("IMAGE", ) - FUNCTION = "func" - - CATEGORY = "image_filter/helpers" - - def func(self, images): - if len(images) <= 1: - return (images[0],) - else: - return (torch.cat(list(i for i in images), dim=0),) - - -class PickFromList: - @classmethod - def INPUT_TYPES(s): - return { - "required": { - "anything" : (AlwaysEqual(), ), - "indexes": ("STRING", {"default": ""}) - }, - } - RETURN_TYPES = (AlwaysEqual(),) - RETURN_NAMES = ("picks",) - - FUNCTION = "func" - CATEGORY = "image_filter/helpers" - INPUT_IS_LIST = True - OUTPUT_IS_LIST = [True,] - - def func(self, anything, indexes): - try: - indexes = [int(x.strip()) for x in indexes[0].split(',') if x.strip()] - except Exception as e: - print(e) - indexes = [] - - return ([anything[i] for i in indexes], ) class ImageFilter(PreviewImage): diff --git a/list_utility_nodes.py b/list_utility_nodes.py new file mode 100644 index 0000000..0fad3ee --- /dev/null +++ b/list_utility_nodes.py @@ -0,0 +1,69 @@ +import torch +from json import JSONEncoder + +def _default(self, obj): return getattr(obj.__class__, "to_json", _default.default)(obj) +_default.default = JSONEncoder().default +JSONEncoder.default = _default + +class AlwaysEqual: + def to_json(self): return "*" + def __ne__(self, __value: object) -> bool: return False + def __eq__(self, __value: object) -> bool: return True + +class BatchFromImageList: + @classmethod + def INPUT_TYPES(s): + return {"required": { "images": ("IMAGE", ), } } + INPUT_IS_LIST = True + RETURN_TYPES = ("IMAGE", ) + FUNCTION = "func" + + CATEGORY = "image_filter/helpers" + + def func(self, images): + if len(images) <= 1: + return (images[0],) + else: + return (torch.cat(list(i for i in images), dim=0),) + +class ImageListFromBatch: + @classmethod + def INPUT_TYPES(s): + return {"required": { "images": ("IMAGE", ), } } + INPUT_IS_LIST = False + OUTPUT_IS_LIST = [True,] + RETURN_TYPES = ("IMAGE", ) + FUNCTION = "func" + + CATEGORY = "image_filter/helpers" + + def func(self, images): + image_list = list( i.unsqueeze(0) for i in images ) + return (image_list,) + + +class PickFromList: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "anything" : (AlwaysEqual(), ), + "indexes": ("STRING", {"default": ""}) + }, + } + RETURN_TYPES = (AlwaysEqual(),) + RETURN_NAMES = ("picks",) + + FUNCTION = "func" + CATEGORY = "image_filter/helpers" + INPUT_IS_LIST = True + OUTPUT_IS_LIST = [True,] + + def func(self, anything, indexes): + try: + indexes = [int(x.strip()) for x in indexes[0].split(',') if x.strip()] + except Exception as e: + print(e) + indexes = [] + + return ([anything[i] for i in indexes], ) \ No newline at end of file diff --git a/split_by_commas.py b/string_utility_nodes.py similarity index 100% rename from split_by_commas.py rename to string_utility_nodes.py