├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .idea ├── .gitignore ├── images-grid-comfy-plugin.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── .readme └── preview.png ├── README.md ├── __init__.py ├── pyproject.toml ├── src ├── __init__.py ├── base.py ├── nodes │ ├── grid_annotation.py │ ├── image_combine.py │ ├── images_grid.py │ └── latent_combine.py └── utils │ ├── __init__.py │ ├── images_grid.py │ └── tensor_convert.py ├── static └── Roboto-Regular.ttf └── workflows ├── base.json ├── base.png ├── efficiency.json ├── efficiency.png ├── mini.json └── mini.png /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Comfy registry 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "pyproject.toml" 9 | 10 | jobs: 11 | publish-node: 12 | name: Publish Custom Node to registry 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v4 17 | - name: Publish Custom Node 18 | uses: Comfy-Org/publish-node-action@main 19 | with: 20 | ## Add your own personal access token to your Github Repository secrets and reference it here. 21 | personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Spyder project settings 132 | .spyderproject 133 | .spyproject 134 | 135 | # Rope project settings 136 | .ropeproject 137 | 138 | # mkdocs documentation 139 | /site 140 | 141 | # mypy 142 | .mypy_cache/ 143 | .dmypy.json 144 | dmypy.json 145 | 146 | # Pyre type checker 147 | .pyre/ 148 | 149 | # pytype static type analyzer 150 | .pytype/ 151 | 152 | # Cython debug symbols 153 | cython_debug/ 154 | 155 | # PyCharm 156 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 157 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 158 | # and can be added to the global gitignore or merged into this file. For a more nuclear 159 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 160 | #.idea/ 161 | 162 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/images-grid-comfy-plugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.readme/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LEv145/images-grid-comfy-plugin/852db490ef93702e1c68fe9774bdf65aaa7d3574/.readme/preview.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImagesGrid: Comfy plugin 2 | 3 | 4 | ## Preview 5 | 6 | ![Image](https://github.com/LEv145/images-grid-comfy-plugin/blob/main/.readme/preview.png?raw=true) 7 | 8 | ### Simple grid of images 9 | 10 | ![Image](https://github.com/LEv145/images-grid-comfy-plugin/blob/main/workflows/mini.png?raw=true) 11 | 12 | ### XYZPlot, like in auto1111, but with more settings 13 | 14 | ![Image](https://github.com/LEv145/images-grid-comfy-plugin/blob/main/workflows/base.png?raw=true) 15 | 16 | ### Integration with [`efficiency`](https://github.com/LucianoCirino/efficiency-nodes-comfyui) 17 | 18 | ![Image](https://github.com/LEv145/images-grid-comfy-plugin/blob/main/workflows/efficiency.png?raw=true) 19 | 20 | 21 | Workflows: https://github.com/LEv145/images-grid-comfy-plugin/tree/main/workflows 22 | 23 | 24 | ## How to use 25 | 26 | 1. Download the latest stable release: 27 | https://github.com/LEv145/images-grid-comfy-plugin/archive/refs/heads/main.zip 28 | 29 | 2. Unpack the node to `custom_nodes`, for example in a folder `custom_nodes/ImagesGrid/` 30 | 31 | 32 | ## Source 33 | 34 | https://github.com/LEv145/images-grid-comfy-plugin 35 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from .src import ( 2 | LatentCombineNode, 3 | ImagesGridByColumnsNode, 4 | ImagesGridByRowsNode, 5 | ImageCombineNode, 6 | GridAnnotationNode, 7 | ) 8 | 9 | 10 | NODE_CLASS_MAPPINGS = { 11 | "LatentCombine": LatentCombineNode, 12 | "ImagesGridByColumns": ImagesGridByColumnsNode, 13 | "ImagesGridByRows": ImagesGridByRowsNode, 14 | "ImageCombine": ImageCombineNode, 15 | "GridAnnotation": GridAnnotationNode, 16 | } 17 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "images-grid-comfy-plugin" 3 | description = "This tool provides a viewer node that allows for checking multiple outputs in a grid, similar to the X/Y Plot extension." 4 | version = "2.6.0" 5 | license = "MIT" 6 | 7 | # Comfy UI 8 | [project.urls] 9 | Repository = "https://github.com/LEv145/images-grid-comfy-plugin" 10 | 11 | [tool.comfy] 12 | PublisherId = "lev145" 13 | DisplayName = "images-grid-comfy-plugin" 14 | Icon = "https://img10.joyreactor.cc/pics/comment/Anime-%D1%84%D1%8D%D0%BD%D0%B4%D0%BE%D0%BC%D1%8B-vtuber-Neuro-sama-4808746.png" 15 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from .nodes.images_grid import ImagesGridByColumnsNode, ImagesGridByRowsNode 2 | from .nodes.latent_combine import LatentCombineNode 3 | from .nodes.image_combine import ImageCombineNode 4 | from .nodes.grid_annotation import GridAnnotationNode 5 | -------------------------------------------------------------------------------- /src/base.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | from pathlib import Path 3 | 4 | 5 | STATIC_PATH = Path(__file__).parent.parent / "static" 6 | 7 | 8 | class BaseNode(): 9 | CATEGORY: str = "ImagesGrid" 10 | FUNCTION: str = "execute" 11 | -------------------------------------------------------------------------------- /src/nodes/grid_annotation.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from PIL import ImageFont 4 | 5 | from ..base import BaseNode, STATIC_PATH 6 | from ..utils import Annotation 7 | 8 | 9 | class GridAnnotationNode(BaseNode): 10 | RETURN_TYPES: tuple[str, ...] = ("GRID_ANNOTATION",) 11 | 12 | @classmethod 13 | def INPUT_TYPES(cls) -> dict[str, t.Any]: 14 | return { 15 | "required": { 16 | "column_texts": ("STRING", {"multiline": True}), 17 | "row_texts": ("STRING", {"multiline": True}), 18 | "font_size": ("INT", {"default": 50, "min": 1}), 19 | }, 20 | } 21 | 22 | def execute( 23 | self, 24 | column_texts: str, 25 | row_texts: str, 26 | font_size: int, 27 | ) -> tuple[Annotation]: 28 | font = ImageFont.truetype(str(STATIC_PATH / "Roboto-Regular.ttf"), size=font_size) 29 | column_texts_list = self._get_texts_from_string(column_texts) 30 | row_texts_list = self._get_texts_from_string(row_texts) 31 | 32 | result = Annotation(column_texts=column_texts_list, row_texts=row_texts_list, font=font) 33 | return (result,) 34 | 35 | def _get_texts_from_string(self, string: str) -> list[str]: 36 | return [ 37 | result 38 | for i in string.split(";") 39 | if (result := i.strip()) != "" 40 | ] 41 | -------------------------------------------------------------------------------- /src/nodes/image_combine.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import torch 4 | 5 | from ..base import BaseNode 6 | 7 | 8 | class ImageCombineNode(BaseNode): 9 | RETURN_TYPES: tuple[str, ...] = ("IMAGE",) 10 | 11 | @classmethod 12 | def INPUT_TYPES(cls) -> dict[str, t.Any]: 13 | return { 14 | "required": { 15 | "image_1": ("IMAGE",), 16 | "image_2": ("IMAGE",), 17 | }, 18 | } 19 | 20 | def execute( 21 | self, 22 | image_1: torch.Tensor, 23 | image_2: torch.Tensor, 24 | ) -> tuple[torch.Tensor]: 25 | result = torch.cat((image_1, image_2), 0) 26 | 27 | return (result,) 28 | -------------------------------------------------------------------------------- /src/nodes/images_grid.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import torch 4 | 5 | from ..base import BaseNode 6 | from ..utils import ( 7 | tensor_to_pillow, 8 | pillow_to_tensor, 9 | create_images_grid_by_columns, 10 | create_images_grid_by_rows, 11 | Annotation, 12 | ) 13 | 14 | class BaseImagesGridNode(BaseNode): 15 | RETURN_TYPES: tuple[str, ...] = ("IMAGE",) 16 | 17 | @classmethod 18 | def _create_input_types(cls, coordinate_name: str) -> dict[str, t.Any]: 19 | return { 20 | "required": { 21 | "images": ("IMAGE",), 22 | "gap": ("INT", {"default": 0, "min": 0}), 23 | coordinate_name: ("INT", {"default": 1, "min": 1}), 24 | }, 25 | "optional": { 26 | "annotation": ("GRID_ANNOTATION",), 27 | } 28 | } 29 | 30 | def _create_execute( 31 | self, 32 | function: t.Callable, 33 | \ 34 | images: torch.Tensor, 35 | gap: int, 36 | annotation: Annotation | None = None, 37 | **kw, 38 | ) -> tuple[torch.Tensor]: 39 | pillow_images = [tensor_to_pillow(i) for i in images] 40 | pillow_grid = function( 41 | images=pillow_images, 42 | gap=gap, 43 | annotation=annotation, 44 | **kw, 45 | ) 46 | tensor_grid = pillow_to_tensor(pillow_grid) 47 | 48 | return (tensor_grid,) 49 | 50 | 51 | class ImagesGridByColumnsNode(BaseImagesGridNode): 52 | @classmethod 53 | def INPUT_TYPES(cls) -> dict[str, t.Any]: 54 | return cls._create_input_types("max_columns") 55 | 56 | def execute(self, **kw) -> tuple[torch.Tensor]: 57 | return self._create_execute(create_images_grid_by_columns, **kw) 58 | 59 | 60 | class ImagesGridByRowsNode(BaseImagesGridNode): 61 | @classmethod 62 | def INPUT_TYPES(cls) -> dict[str, t.Any]: 63 | return cls._create_input_types("max_rows") 64 | 65 | def execute(self, **kw) -> tuple[torch.Tensor]: 66 | return self._create_execute(create_images_grid_by_rows, **kw) 67 | -------------------------------------------------------------------------------- /src/nodes/latent_combine.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import torch 4 | 5 | from ..base import BaseNode 6 | 7 | 8 | class LatentCombineNode(BaseNode): 9 | RETURN_TYPES: tuple[str, ...] = ("LATENT",) 10 | 11 | @classmethod 12 | def INPUT_TYPES(cls) -> dict[str, t.Any]: 13 | return { 14 | "required": { 15 | "latent_1": ("LATENT",), 16 | "latent_2": ("LATENT",), 17 | }, 18 | } 19 | 20 | def execute( 21 | self, 22 | latent_1: dict[str, torch.Tensor], 23 | latent_2: dict[str, torch.Tensor], 24 | ) -> tuple[dict[str, torch.Tensor]]: 25 | samples = torch.cat((latent_1["samples"], latent_2["samples"]), 0) 26 | 27 | return ({"samples": samples},) 28 | -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .images_grid import ( 2 | create_images_grid_by_columns, 3 | create_images_grid_by_rows, 4 | Annotation, 5 | ) 6 | from .tensor_convert import tensor_to_pillow, pillow_to_tensor 7 | -------------------------------------------------------------------------------- /src/utils/images_grid.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | from dataclasses import dataclass 3 | from contextlib import suppress 4 | 5 | from PIL import Image, ImageDraw, ImageFont 6 | 7 | 8 | WIDEST_LETTER = "W" 9 | 10 | 11 | @dataclass 12 | class Annotation(): 13 | column_texts: list[str] 14 | row_texts: list[str] 15 | font: ImageFont.FreeTypeFont 16 | 17 | 18 | def create_images_grid_by_columns( 19 | images: list[Image.Image], 20 | gap: int, 21 | max_columns: int, 22 | annotation: Annotation | None = None, 23 | ) -> Image.Image: 24 | max_rows = (len(images) + max_columns - 1) // max_columns 25 | return _create_images_grid(images, gap, max_columns, max_rows, annotation) 26 | 27 | 28 | def create_images_grid_by_rows( 29 | images: list[Image.Image], 30 | gap: int, 31 | max_rows: int, 32 | annotation: Annotation | None = None, 33 | ) -> Image.Image: 34 | max_columns = (len(images) + max_rows - 1) // max_rows 35 | return _create_images_grid(images, gap, max_columns, max_rows, annotation) 36 | 37 | 38 | @dataclass 39 | class _GridInfo(): 40 | image: Image.Image 41 | gap: int 42 | one_image_size: tuple[int, int] 43 | 44 | 45 | def _create_images_grid( 46 | images: list[Image.Image], 47 | gap: int, 48 | max_columns: int, 49 | max_rows: int, 50 | annotation: Annotation | None, 51 | ) -> Image.Image: 52 | size = images[0].size 53 | grid_width = size[0] * max_columns + (max_columns - 1) * gap 54 | grid_height = size[1] * max_rows + (max_rows - 1) * gap 55 | 56 | grid_image = Image.new("RGB", (grid_width, grid_height), color="white") 57 | 58 | _arrange_images_on_grid(grid_image, images=images, size=size, max_columns=max_columns, gap=gap) 59 | 60 | if annotation is None: 61 | return grid_image 62 | return _create_grid_annotation( 63 | grid_info=_GridInfo( 64 | image=grid_image, 65 | gap=gap, 66 | one_image_size=size, 67 | ), 68 | column_texts=annotation.column_texts, 69 | row_texts=annotation.row_texts, 70 | font=annotation.font, 71 | ) 72 | 73 | 74 | def _arrange_images_on_grid( 75 | grid_image: Image.Image, 76 | /, 77 | images: list[Image.Image], 78 | size: tuple[int, int], 79 | max_columns: int, 80 | gap: int, 81 | ): 82 | for i, image in enumerate(images): 83 | x = (i % max_columns) * (size[0] + gap) 84 | y = (i // max_columns) * (size[1] + gap) 85 | 86 | grid_image.paste(image, (x, y)) 87 | 88 | 89 | def _create_grid_annotation( 90 | grid_info: _GridInfo, 91 | column_texts: list[str], 92 | row_texts: list[str], 93 | font: ImageFont.FreeTypeFont, 94 | ) -> Image.Image: 95 | if not column_texts and not row_texts: 96 | raise ValueError("Column text and row text is empty") 97 | 98 | grid = grid_info.image 99 | left_padding = 0 100 | top_padding = 0 101 | 102 | if row_texts: 103 | left_padding = int( 104 | max( 105 | font.getlength(splitted_text) 106 | for raw_text in row_texts 107 | for splitted_text in raw_text.split("\n") 108 | ) 109 | + font.getlength(WIDEST_LETTER)*2 110 | ) 111 | if column_texts: 112 | top_padding = max(elem.count("\n") for elem in column_texts) * int(font.size) + int(font.size * 2) 113 | 114 | image = Image.new( 115 | "RGB", 116 | (grid.size[0] + left_padding, grid.size[1] + top_padding), 117 | color="white", 118 | ) 119 | draw = ImageDraw.Draw(image) 120 | # https://github.com/python-pillow/Pillow/blob/9.5.x/docs/reference/ImageDraw.rst 121 | draw.font = font # type: ignore 122 | 123 | _paste_image_to_lower_left_corner(image, grid) 124 | if column_texts: 125 | _draw_column_text( 126 | draw=draw, 127 | texts=column_texts, 128 | grid_info=grid_info, 129 | left_padding=left_padding, 130 | top_padding=top_padding, 131 | ) 132 | if row_texts: 133 | _draw_row_text( 134 | draw=draw, 135 | texts=row_texts, 136 | grid_info=grid_info, 137 | left_padding=left_padding, 138 | top_padding=top_padding, 139 | ) 140 | 141 | return image 142 | 143 | 144 | def _draw_column_text( 145 | draw: ImageDraw.ImageDraw, 146 | texts: list[str], 147 | grid_info: _GridInfo, 148 | left_padding: int, 149 | top_padding: int, 150 | ) -> None: 151 | i = 0 152 | x0 = left_padding 153 | y0 = 0 154 | x1 = left_padding + grid_info.one_image_size[0] 155 | y1 = top_padding 156 | while x0 != grid_info.image.size[0] + left_padding + grid_info.gap: 157 | i = _draw_text_by_xy((x0, y0, x1, y1), i, draw=draw, texts=texts) 158 | x0 += grid_info.one_image_size[0] + grid_info.gap 159 | x1 += grid_info.one_image_size[0] + grid_info.gap 160 | 161 | 162 | def _draw_row_text( 163 | draw: ImageDraw.ImageDraw, 164 | texts: list[str], 165 | grid_info: _GridInfo, 166 | left_padding: int, 167 | top_padding: int, 168 | ) -> None: 169 | i = 0 170 | x0 = 0 171 | y0 = top_padding 172 | x1 = left_padding 173 | y1 = top_padding + grid_info.one_image_size[1] 174 | while y0 != grid_info.image.size[1] + top_padding + grid_info.gap: 175 | i = _draw_text_by_xy((x0, y0, x1, y1), i, draw=draw, texts=texts) 176 | y0 += grid_info.one_image_size[1] + grid_info.gap 177 | y1 += grid_info.one_image_size[1] + grid_info.gap 178 | 179 | 180 | def _draw_text_by_xy( 181 | xy: tuple[int, int, int, int], 182 | index: int, 183 | \ 184 | draw: ImageDraw.ImageDraw, 185 | texts: list[str], 186 | ) -> int: 187 | with suppress(IndexError): 188 | _draw_center_text(draw, xy, texts[index]) 189 | return index + 1 190 | 191 | 192 | def _draw_center_text( 193 | draw: ImageDraw.ImageDraw, 194 | xy: tuple[int, int, int, int], 195 | text: str, 196 | fill: t.Any = "black", 197 | ) -> None: 198 | _, _, *text_size = draw.textbbox((0, 0), text) 199 | draw.multiline_text( 200 | ( 201 | (xy[2] - text_size[0] + xy[0]) / 2, 202 | (xy[3] - text_size[1] + xy[1]) / 2, 203 | ), 204 | text, 205 | fill=fill, 206 | ) 207 | 208 | 209 | def _paste_image_to_lower_left_corner(base: Image.Image, image: Image.Image) -> None: 210 | base.paste(image, (base.size[0] - image.size[0], base.size[1] - image.size[1])) 211 | -------------------------------------------------------------------------------- /src/utils/tensor_convert.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | import torch 4 | import numpy as np 5 | from PIL import Image 6 | 7 | 8 | def tensor_to_pillow(image: t.Any) -> Image.Image: 9 | return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) 10 | 11 | 12 | def pillow_to_tensor(image: Image.Image) -> t.Any: 13 | return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) 14 | -------------------------------------------------------------------------------- /static/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LEv145/images-grid-comfy-plugin/852db490ef93702e1c68fe9774bdf65aaa7d3574/static/Roboto-Regular.ttf -------------------------------------------------------------------------------- /workflows/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 68, 3 | "last_link_id": 112, 4 | "nodes": [ 5 | { 6 | "id": 36, 7 | "type": "KSampler", 8 | "pos": [ 9 | -200, 10 | 900 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 262 15 | }, 16 | "flags": { 17 | "collapsed": true, 18 | "pinned": true 19 | }, 20 | "order": 12, 21 | "mode": 0, 22 | "inputs": [ 23 | { 24 | "name": "model", 25 | "type": "MODEL", 26 | "link": 72 27 | }, 28 | { 29 | "name": "positive", 30 | "type": "CONDITIONING", 31 | "link": 54 32 | }, 33 | { 34 | "name": "negative", 35 | "type": "CONDITIONING", 36 | "link": 55 37 | }, 38 | { 39 | "name": "latent_image", 40 | "type": "LATENT", 41 | "link": 60 42 | } 43 | ], 44 | "outputs": [ 45 | { 46 | "name": "LATENT", 47 | "type": "LATENT", 48 | "links": [ 49 | 79 50 | ], 51 | "slot_index": 0 52 | } 53 | ], 54 | "properties": { 55 | "Node name for S&R": "KSampler" 56 | }, 57 | "widgets_values": [ 58 | 1, 59 | false, 60 | 20, 61 | 12, 62 | "dpmpp_sde", 63 | "karras", 64 | 1 65 | ] 66 | }, 67 | { 68 | "id": 29, 69 | "type": "Reroute", 70 | "pos": [ 71 | -380, 72 | 120 73 | ], 74 | "size": [ 75 | 82, 76 | 26 77 | ], 78 | "flags": { 79 | "pinned": true 80 | }, 81 | "order": 8, 82 | "mode": 0, 83 | "inputs": [ 84 | { 85 | "name": "", 86 | "type": "*", 87 | "link": 44, 88 | "slot_index": 0 89 | } 90 | ], 91 | "outputs": [ 92 | { 93 | "name": "MODEL", 94 | "type": "MODEL", 95 | "links": [ 96 | 70, 97 | 71, 98 | 72 99 | ], 100 | "slot_index": 0 101 | } 102 | ], 103 | "properties": { 104 | "showOutputText": true, 105 | "horizontal": false 106 | } 107 | }, 108 | { 109 | "id": 27, 110 | "type": "KSampler", 111 | "pos": [ 112 | -200, 113 | 300 114 | ], 115 | "size": { 116 | "0": 315, 117 | "1": 262 118 | }, 119 | "flags": { 120 | "collapsed": true 121 | }, 122 | "order": 10, 123 | "mode": 0, 124 | "inputs": [ 125 | { 126 | "name": "model", 127 | "type": "MODEL", 128 | "link": 70 129 | }, 130 | { 131 | "name": "positive", 132 | "type": "CONDITIONING", 133 | "link": 41 134 | }, 135 | { 136 | "name": "negative", 137 | "type": "CONDITIONING", 138 | "link": 40 139 | }, 140 | { 141 | "name": "latent_image", 142 | "type": "LATENT", 143 | "link": 62 144 | } 145 | ], 146 | "outputs": [ 147 | { 148 | "name": "LATENT", 149 | "type": "LATENT", 150 | "links": [ 151 | 73 152 | ], 153 | "slot_index": 0 154 | } 155 | ], 156 | "properties": { 157 | "Node name for S&R": "KSampler" 158 | }, 159 | "widgets_values": [ 160 | 1, 161 | false, 162 | 20, 163 | 8, 164 | "dpmpp_sde", 165 | "karras", 166 | 1 167 | ] 168 | }, 169 | { 170 | "id": 31, 171 | "type": "KSampler", 172 | "pos": [ 173 | -200, 174 | 600 175 | ], 176 | "size": { 177 | "0": 315, 178 | "1": 262 179 | }, 180 | "flags": { 181 | "collapsed": true 182 | }, 183 | "order": 11, 184 | "mode": 0, 185 | "inputs": [ 186 | { 187 | "name": "model", 188 | "type": "MODEL", 189 | "link": 71 190 | }, 191 | { 192 | "name": "positive", 193 | "type": "CONDITIONING", 194 | "link": 48 195 | }, 196 | { 197 | "name": "negative", 198 | "type": "CONDITIONING", 199 | "link": 47 200 | }, 201 | { 202 | "name": "latent_image", 203 | "type": "LATENT", 204 | "link": 61 205 | } 206 | ], 207 | "outputs": [ 208 | { 209 | "name": "LATENT", 210 | "type": "LATENT", 211 | "links": [ 212 | 74 213 | ], 214 | "slot_index": 0 215 | } 216 | ], 217 | "properties": { 218 | "Node name for S&R": "KSampler" 219 | }, 220 | "widgets_values": [ 221 | 1, 222 | false, 223 | 20, 224 | 10, 225 | "dpmpp_sde", 226 | "karras", 227 | 1 228 | ] 229 | }, 230 | { 231 | "id": 24, 232 | "type": "CheckpointLoaderSimple", 233 | "pos": [ 234 | -1117, 235 | 311 236 | ], 237 | "size": { 238 | "0": 315, 239 | "1": 98 240 | }, 241 | "flags": { 242 | "collapsed": true 243 | }, 244 | "order": 0, 245 | "mode": 0, 246 | "outputs": [ 247 | { 248 | "name": "MODEL", 249 | "type": "MODEL", 250 | "links": [ 251 | 69 252 | ], 253 | "slot_index": 0 254 | }, 255 | { 256 | "name": "CLIP", 257 | "type": "CLIP", 258 | "links": [ 259 | 35, 260 | 36 261 | ], 262 | "slot_index": 1 263 | }, 264 | { 265 | "name": "VAE", 266 | "type": "VAE", 267 | "links": [ 268 | 63 269 | ], 270 | "slot_index": 2 271 | } 272 | ], 273 | "properties": { 274 | "Node name for S&R": "CheckpointLoaderSimple" 275 | }, 276 | "widgets_values": [ 277 | "Counterfeit-v2_5.safetensors" 278 | ] 279 | }, 280 | { 281 | "id": 28, 282 | "type": "Reroute", 283 | "pos": [ 284 | -770, 285 | 120 286 | ], 287 | "size": [ 288 | 75, 289 | 26 290 | ], 291 | "flags": {}, 292 | "order": 3, 293 | "mode": 0, 294 | "inputs": [ 295 | { 296 | "name": "", 297 | "type": "*", 298 | "link": 69, 299 | "slot_index": 0 300 | } 301 | ], 302 | "outputs": [ 303 | { 304 | "name": "", 305 | "type": "MODEL", 306 | "links": [ 307 | 44 308 | ] 309 | } 310 | ], 311 | "properties": { 312 | "showOutputText": false, 313 | "horizontal": false 314 | } 315 | }, 316 | { 317 | "id": 39, 318 | "type": "Reroute", 319 | "pos": [ 320 | -770, 321 | 90 322 | ], 323 | "size": [ 324 | 75, 325 | 26 326 | ], 327 | "flags": {}, 328 | "order": 6, 329 | "mode": 0, 330 | "inputs": [ 331 | { 332 | "name": "", 333 | "type": "*", 334 | "link": 63 335 | } 336 | ], 337 | "outputs": [ 338 | { 339 | "name": "", 340 | "type": "VAE", 341 | "links": [ 342 | 108 343 | ], 344 | "slot_index": 0 345 | } 346 | ], 347 | "properties": { 348 | "showOutputText": false, 349 | "horizontal": false 350 | } 351 | }, 352 | { 353 | "id": 25, 354 | "type": "CLIPTextEncode", 355 | "pos": [ 356 | -700, 357 | 330 358 | ], 359 | "size": { 360 | "0": 400, 361 | "1": 200 362 | }, 363 | "flags": { 364 | "collapsed": true 365 | }, 366 | "order": 4, 367 | "mode": 0, 368 | "inputs": [ 369 | { 370 | "name": "clip", 371 | "type": "CLIP", 372 | "link": 35 373 | } 374 | ], 375 | "outputs": [ 376 | { 377 | "name": "CONDITIONING", 378 | "type": "CONDITIONING", 379 | "links": [ 380 | 41, 381 | 48, 382 | 54 383 | ], 384 | "slot_index": 0 385 | } 386 | ], 387 | "properties": { 388 | "Node name for S&R": "CLIPTextEncode" 389 | }, 390 | "widgets_values": [ 391 | "masterpiece, best quality, 1girl, golden hair," 392 | ] 393 | }, 394 | { 395 | "id": 42, 396 | "type": "LatentCombine", 397 | "pos": [ 398 | 250, 399 | 430 400 | ], 401 | "size": { 402 | "0": 210, 403 | "1": 46 404 | }, 405 | "flags": {}, 406 | "order": 13, 407 | "mode": 0, 408 | "inputs": [ 409 | { 410 | "name": "latent_1", 411 | "type": "LATENT", 412 | "link": 73, 413 | "slot_index": 0 414 | }, 415 | { 416 | "name": "latent_2", 417 | "type": "LATENT", 418 | "link": 74, 419 | "slot_index": 1 420 | } 421 | ], 422 | "outputs": [ 423 | { 424 | "name": "LATENT", 425 | "type": "LATENT", 426 | "links": [ 427 | 80 428 | ], 429 | "slot_index": 0 430 | } 431 | ], 432 | "properties": { 433 | "Node name for S&R": "LatentCombine" 434 | }, 435 | "color": "#322", 436 | "bgcolor": "#533" 437 | }, 438 | { 439 | "id": 44, 440 | "type": "LatentCombine", 441 | "pos": [ 442 | 250, 443 | 510 444 | ], 445 | "size": { 446 | "0": 210, 447 | "1": 46 448 | }, 449 | "flags": {}, 450 | "order": 14, 451 | "mode": 0, 452 | "inputs": [ 453 | { 454 | "name": "latent_1", 455 | "type": "LATENT", 456 | "link": 80 457 | }, 458 | { 459 | "name": "latent_2", 460 | "type": "LATENT", 461 | "link": 79 462 | } 463 | ], 464 | "outputs": [ 465 | { 466 | "name": "LATENT", 467 | "type": "LATENT", 468 | "links": [ 469 | 88 470 | ], 471 | "slot_index": 0 472 | } 473 | ], 474 | "properties": { 475 | "Node name for S&R": "LatentCombine" 476 | }, 477 | "color": "#322", 478 | "bgcolor": "#533" 479 | }, 480 | { 481 | "id": 15, 482 | "type": "PreviewImage", 483 | "pos": [ 484 | 1250, 485 | 410 486 | ], 487 | "size": { 488 | "0": 601.5753173828125, 489 | "1": 479.24884033203125 490 | }, 491 | "flags": {}, 492 | "order": 17, 493 | "mode": 0, 494 | "inputs": [ 495 | { 496 | "name": "images", 497 | "type": "IMAGE", 498 | "link": 111 499 | } 500 | ], 501 | "properties": { 502 | "Node name for S&R": "PreviewImage" 503 | } 504 | }, 505 | { 506 | "id": 38, 507 | "type": "EmptyLatentImage", 508 | "pos": [ 509 | -620, 510 | 810 511 | ], 512 | "size": { 513 | "0": 315, 514 | "1": 106 515 | }, 516 | "flags": {}, 517 | "order": 7, 518 | "mode": 0, 519 | "inputs": [ 520 | { 521 | "name": "batch_size", 522 | "type": "INT", 523 | "link": 107, 524 | "widget": { 525 | "name": "batch_size", 526 | "config": [ 527 | "INT", 528 | { 529 | "default": 1, 530 | "min": 1, 531 | "max": 64 532 | } 533 | ] 534 | } 535 | } 536 | ], 537 | "outputs": [ 538 | { 539 | "name": "LATENT", 540 | "type": "LATENT", 541 | "links": [ 542 | 60, 543 | 61, 544 | 62 545 | ], 546 | "slot_index": 0 547 | } 548 | ], 549 | "properties": { 550 | "Node name for S&R": "EmptyLatentImage" 551 | }, 552 | "widgets_values": [ 553 | 512, 554 | 512, 555 | 4 556 | ] 557 | }, 558 | { 559 | "id": 40, 560 | "type": "Reroute", 561 | "pos": [ 562 | 380, 563 | 90 564 | ], 565 | "size": [ 566 | 75, 567 | 26 568 | ], 569 | "flags": {}, 570 | "order": 9, 571 | "mode": 0, 572 | "inputs": [ 573 | { 574 | "name": "", 575 | "type": "*", 576 | "link": 108, 577 | "slot_index": 0 578 | } 579 | ], 580 | "outputs": [ 581 | { 582 | "name": "VAE", 583 | "type": "VAE", 584 | "links": [ 585 | 76 586 | ], 587 | "slot_index": 0 588 | } 589 | ], 590 | "properties": { 591 | "showOutputText": true, 592 | "horizontal": false 593 | } 594 | }, 595 | { 596 | "id": 43, 597 | "type": "VAEDecode", 598 | "pos": [ 599 | 530, 600 | 510 601 | ], 602 | "size": { 603 | "0": 210, 604 | "1": 46 605 | }, 606 | "flags": { 607 | "collapsed": true 608 | }, 609 | "order": 15, 610 | "mode": 0, 611 | "inputs": [ 612 | { 613 | "name": "samples", 614 | "type": "LATENT", 615 | "link": 88 616 | }, 617 | { 618 | "name": "vae", 619 | "type": "VAE", 620 | "link": 76 621 | } 622 | ], 623 | "outputs": [ 624 | { 625 | "name": "IMAGE", 626 | "type": "IMAGE", 627 | "links": [ 628 | 109 629 | ], 630 | "slot_index": 0 631 | } 632 | ], 633 | "properties": { 634 | "Node name for S&R": "VAEDecode" 635 | } 636 | }, 637 | { 638 | "id": 59, 639 | "type": "PrimitiveNode", 640 | "pos": [ 641 | -1010, 642 | 460 643 | ], 644 | "size": { 645 | "0": 210, 646 | "1": 82 647 | }, 648 | "flags": { 649 | "collapsed": false 650 | }, 651 | "order": 1, 652 | "mode": 0, 653 | "outputs": [ 654 | { 655 | "name": "INT", 656 | "type": "INT", 657 | "links": [ 658 | 107, 659 | 110 660 | ], 661 | "slot_index": 0, 662 | "widget": { 663 | "name": "max_columns", 664 | "config": [ 665 | "INT", 666 | { 667 | "default": 1, 668 | "min": 1 669 | } 670 | ] 671 | } 672 | } 673 | ], 674 | "title": "Batch size", 675 | "properties": {}, 676 | "widgets_values": [ 677 | 4, 678 | false 679 | ], 680 | "color": "#232", 681 | "bgcolor": "#353" 682 | }, 683 | { 684 | "id": 26, 685 | "type": "CLIPTextEncode", 686 | "pos": [ 687 | -700, 688 | 570 689 | ], 690 | "size": { 691 | "0": 400, 692 | "1": 200 693 | }, 694 | "flags": { 695 | "collapsed": true 696 | }, 697 | "order": 5, 698 | "mode": 0, 699 | "inputs": [ 700 | { 701 | "name": "clip", 702 | "type": "CLIP", 703 | "link": 36 704 | } 705 | ], 706 | "outputs": [ 707 | { 708 | "name": "CONDITIONING", 709 | "type": "CONDITIONING", 710 | "links": [ 711 | 40, 712 | 47, 713 | 55 714 | ], 715 | "slot_index": 0 716 | } 717 | ], 718 | "properties": { 719 | "Node name for S&R": "CLIPTextEncode" 720 | }, 721 | "widgets_values": [ 722 | "(worst quality, low quality:1.4), monochrome, zombie" 723 | ] 724 | }, 725 | { 726 | "id": 68, 727 | "type": "GridAnnotation", 728 | "pos": [ 729 | 430, 730 | 640 731 | ], 732 | "size": { 733 | "0": 315, 734 | "1": 106 735 | }, 736 | "flags": {}, 737 | "order": 2, 738 | "mode": 0, 739 | "outputs": [ 740 | { 741 | "name": "GRID_ANNOTATION", 742 | "type": "GRID_ANNOTATION", 743 | "links": [ 744 | 112 745 | ], 746 | "slot_index": 0 747 | } 748 | ], 749 | "properties": { 750 | "Node name for S&R": "GridAnnotation" 751 | }, 752 | "widgets_values": [ 753 | "1; 2; 3; 4", 754 | "CFG: 8; CFG: 10; CFG: 12", 755 | 100 756 | ], 757 | "color": "#322", 758 | "bgcolor": "#533" 759 | }, 760 | { 761 | "id": 67, 762 | "type": "ImagesGridByColumns", 763 | "pos": [ 764 | 820, 765 | 540 766 | ], 767 | "size": [ 768 | 320, 769 | 100 770 | ], 771 | "flags": {}, 772 | "order": 16, 773 | "mode": 0, 774 | "inputs": [ 775 | { 776 | "name": "images", 777 | "type": "IMAGE", 778 | "link": 109, 779 | "slot_index": 0 780 | }, 781 | { 782 | "name": "annotation", 783 | "type": "GRID_ANNOTATION", 784 | "link": 112 785 | }, 786 | { 787 | "name": "max_columns", 788 | "type": "INT", 789 | "link": 110, 790 | "widget": { 791 | "name": "max_columns", 792 | "config": [ 793 | "INT", 794 | { 795 | "default": 1, 796 | "min": 1 797 | } 798 | ] 799 | } 800 | } 801 | ], 802 | "outputs": [ 803 | { 804 | "name": "IMAGE", 805 | "type": "IMAGE", 806 | "links": [ 807 | 111 808 | ], 809 | "slot_index": 0 810 | } 811 | ], 812 | "properties": { 813 | "Node name for S&R": "ImagesGridByColumns" 814 | }, 815 | "widgets_values": [ 816 | 0, 817 | 4 818 | ], 819 | "color": "#322", 820 | "bgcolor": "#533" 821 | } 822 | ], 823 | "links": [ 824 | [ 825 | 35, 826 | 24, 827 | 1, 828 | 25, 829 | 0, 830 | "CLIP" 831 | ], 832 | [ 833 | 36, 834 | 24, 835 | 1, 836 | 26, 837 | 0, 838 | "CLIP" 839 | ], 840 | [ 841 | 40, 842 | 26, 843 | 0, 844 | 27, 845 | 2, 846 | "CONDITIONING" 847 | ], 848 | [ 849 | 41, 850 | 25, 851 | 0, 852 | 27, 853 | 1, 854 | "CONDITIONING" 855 | ], 856 | [ 857 | 44, 858 | 28, 859 | 0, 860 | 29, 861 | 0, 862 | "*" 863 | ], 864 | [ 865 | 47, 866 | 26, 867 | 0, 868 | 31, 869 | 2, 870 | "CONDITIONING" 871 | ], 872 | [ 873 | 48, 874 | 25, 875 | 0, 876 | 31, 877 | 1, 878 | "CONDITIONING" 879 | ], 880 | [ 881 | 54, 882 | 25, 883 | 0, 884 | 36, 885 | 1, 886 | "CONDITIONING" 887 | ], 888 | [ 889 | 55, 890 | 26, 891 | 0, 892 | 36, 893 | 2, 894 | "CONDITIONING" 895 | ], 896 | [ 897 | 60, 898 | 38, 899 | 0, 900 | 36, 901 | 3, 902 | "LATENT" 903 | ], 904 | [ 905 | 61, 906 | 38, 907 | 0, 908 | 31, 909 | 3, 910 | "LATENT" 911 | ], 912 | [ 913 | 62, 914 | 38, 915 | 0, 916 | 27, 917 | 3, 918 | "LATENT" 919 | ], 920 | [ 921 | 63, 922 | 24, 923 | 2, 924 | 39, 925 | 0, 926 | "*" 927 | ], 928 | [ 929 | 69, 930 | 24, 931 | 0, 932 | 28, 933 | 0, 934 | "*" 935 | ], 936 | [ 937 | 70, 938 | 29, 939 | 0, 940 | 27, 941 | 0, 942 | "MODEL" 943 | ], 944 | [ 945 | 71, 946 | 29, 947 | 0, 948 | 31, 949 | 0, 950 | "MODEL" 951 | ], 952 | [ 953 | 72, 954 | 29, 955 | 0, 956 | 36, 957 | 0, 958 | "MODEL" 959 | ], 960 | [ 961 | 73, 962 | 27, 963 | 0, 964 | 42, 965 | 0, 966 | "LATENT" 967 | ], 968 | [ 969 | 74, 970 | 31, 971 | 0, 972 | 42, 973 | 1, 974 | "LATENT" 975 | ], 976 | [ 977 | 76, 978 | 40, 979 | 0, 980 | 43, 981 | 1, 982 | "VAE" 983 | ], 984 | [ 985 | 79, 986 | 36, 987 | 0, 988 | 44, 989 | 1, 990 | "LATENT" 991 | ], 992 | [ 993 | 80, 994 | 42, 995 | 0, 996 | 44, 997 | 0, 998 | "LATENT" 999 | ], 1000 | [ 1001 | 88, 1002 | 44, 1003 | 0, 1004 | 43, 1005 | 0, 1006 | "LATENT" 1007 | ], 1008 | [ 1009 | 107, 1010 | 59, 1011 | 0, 1012 | 38, 1013 | 0, 1014 | "INT" 1015 | ], 1016 | [ 1017 | 108, 1018 | 39, 1019 | 0, 1020 | 40, 1021 | 0, 1022 | "*" 1023 | ], 1024 | [ 1025 | 109, 1026 | 43, 1027 | 0, 1028 | 67, 1029 | 0, 1030 | "IMAGE" 1031 | ], 1032 | [ 1033 | 110, 1034 | 59, 1035 | 0, 1036 | 67, 1037 | 2, 1038 | "INT" 1039 | ], 1040 | [ 1041 | 111, 1042 | 67, 1043 | 0, 1044 | 15, 1045 | 0, 1046 | "IMAGE" 1047 | ], 1048 | [ 1049 | 112, 1050 | 68, 1051 | 0, 1052 | 67, 1053 | 1, 1054 | "GRID_ANNOTATION" 1055 | ] 1056 | ], 1057 | "groups": [], 1058 | "config": {}, 1059 | "extra": {}, 1060 | "version": 0.4 1061 | } -------------------------------------------------------------------------------- /workflows/base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LEv145/images-grid-comfy-plugin/852db490ef93702e1c68fe9774bdf65aaa7d3574/workflows/base.png -------------------------------------------------------------------------------- /workflows/efficiency.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 19, 3 | "last_link_id": 36, 4 | "nodes": [ 5 | { 6 | "id": 6, 7 | "type": "VAEDecode", 8 | "pos": [ 9 | 1083, 10 | 263 11 | ], 12 | "size": { 13 | "0": 210, 14 | "1": 46 15 | }, 16 | "flags": { 17 | "pinned": true, 18 | "collapsed": true 19 | }, 20 | "order": 4, 21 | "mode": 0, 22 | "inputs": [ 23 | { 24 | "name": "samples", 25 | "type": "LATENT", 26 | "link": 11, 27 | "slot_index": 0 28 | }, 29 | { 30 | "name": "vae", 31 | "type": "VAE", 32 | "link": 12 33 | } 34 | ], 35 | "outputs": [ 36 | { 37 | "name": "IMAGE", 38 | "type": "IMAGE", 39 | "links": [ 40 | 29 41 | ], 42 | "slot_index": 0 43 | } 44 | ], 45 | "properties": { 46 | "Node name for S&R": "VAE Decode" 47 | } 48 | }, 49 | { 50 | "id": 3, 51 | "type": "XY Plot", 52 | "pos": [ 53 | 529, 54 | 778 55 | ], 56 | "size": { 57 | "0": 225.0937042236328, 58 | "1": 244 59 | }, 60 | "flags": {}, 61 | "order": 0, 62 | "mode": 0, 63 | "outputs": [ 64 | { 65 | "name": "script", 66 | "type": "SCRIPT", 67 | "links": [ 68 | 23 69 | ], 70 | "slot_index": 0 71 | } 72 | ], 73 | "properties": { 74 | "Node name for S&R": "XY Plot" 75 | }, 76 | "widgets_values": [ 77 | "Steps", 78 | "20;30;40", 79 | "CFG Scale", 80 | "7;9;11", 81 | 10, 82 | "False", 83 | 0, 84 | "____________EXAMPLES____________\n(X/Y_types) (X/Y_values)\nLatent Batch n/a\nSeeds++ Batch 3\nSteps 15;20;25\nCFG Scale 5;10;15;20\nSampler(1) dpmpp_2s_ancestral;euler;ddim\nSampler(2) dpmpp_2m,karras;heun,normal\nDenoise .3;.4;.5;.6;.7\nVAE vae_1; vae_2; vae_3\n\n____________SAMPLERS____________\neuler;\neuler_ancestral;\nheun;\ndpm_2;\ndpm_2_ancestral;\nlms;\ndpm_fast;\ndpm_adaptive;\ndpmpp_2s_ancestral;\ndpmpp_sde;\ndpmpp_2m;\nddim;\nuni_pc;\nuni_pc_bh2\n\n___________SCHEDULERS___________\nkarras;\nnormal;\nsimple;\nddim_uniform\n\n______________VAE_______________\nkl-f8-anime2.ckpt;\nnovelai.vae.pt;\nvae-ft-mse-840000-ema-pruned.ckpt\n\n_____________NOTES______________\n- During a 'Latent Batch', the corresponding X/Y_value is ignored.\n- During a 'Latent Batch', the latent_id is ignored.\n- For a 'Seeds++ Batch', starting seed is defined by the KSampler.\n- Trailing semicolons are ignored in the X/Y_values.\n- Parameter types not set by this node are defined in the KSampler." 85 | ], 86 | "color": "#223", 87 | "bgcolor": "#335" 88 | }, 89 | { 90 | "id": 2, 91 | "type": "KSampler (Efficient)", 92 | "pos": [ 93 | 767, 94 | 252 95 | ], 96 | "size": { 97 | "0": 288.36614990234375, 98 | "1": 374 99 | }, 100 | "flags": {}, 101 | "order": 3, 102 | "mode": 0, 103 | "inputs": [ 104 | { 105 | "name": "model", 106 | "type": "MODEL", 107 | "link": 1 108 | }, 109 | { 110 | "name": "positive", 111 | "type": "CONDITIONING", 112 | "link": 2 113 | }, 114 | { 115 | "name": "negative", 116 | "type": "CONDITIONING", 117 | "link": 3 118 | }, 119 | { 120 | "name": "latent_image", 121 | "type": "LATENT", 122 | "link": 4 123 | }, 124 | { 125 | "name": "optional_vae", 126 | "type": "VAE", 127 | "link": 5 128 | }, 129 | { 130 | "name": "script", 131 | "type": "SCRIPT", 132 | "link": 23, 133 | "slot_index": 5 134 | } 135 | ], 136 | "outputs": [ 137 | { 138 | "name": "MODEL", 139 | "type": "MODEL", 140 | "links": [] 141 | }, 142 | { 143 | "name": "CONDITIONING+", 144 | "type": "CONDITIONING", 145 | "links": [] 146 | }, 147 | { 148 | "name": "CONDITIONING-", 149 | "type": "CONDITIONING", 150 | "links": null 151 | }, 152 | { 153 | "name": "LATENT", 154 | "type": "LATENT", 155 | "links": [ 156 | 11 157 | ], 158 | "slot_index": 3 159 | }, 160 | { 161 | "name": "VAE", 162 | "type": "VAE", 163 | "links": [ 164 | 12 165 | ], 166 | "slot_index": 4 167 | }, 168 | { 169 | "name": "IMAGE", 170 | "type": "IMAGE", 171 | "links": null 172 | } 173 | ], 174 | "properties": { 175 | "Node name for S&R": "KSampler (Efficient)" 176 | }, 177 | "widgets_values": [ 178 | "Script", 179 | 0, 180 | 428398671204662, 181 | false, 182 | 20, 183 | 7, 184 | "dpmpp_2m", 185 | "karras", 186 | 1, 187 | "Enabled" 188 | ], 189 | "color": "#223", 190 | "bgcolor": "#335" 191 | }, 192 | { 193 | "id": 1, 194 | "type": "Efficient Loader", 195 | "pos": [ 196 | 529, 197 | 251 198 | ], 199 | "size": { 200 | "0": 222.70794677734375, 201 | "1": 490.5440673828125 202 | }, 203 | "flags": {}, 204 | "order": 1, 205 | "mode": 0, 206 | "outputs": [ 207 | { 208 | "name": "MODEL", 209 | "type": "MODEL", 210 | "links": [ 211 | 1 212 | ], 213 | "slot_index": 0 214 | }, 215 | { 216 | "name": "CONDITIONING+", 217 | "type": "CONDITIONING", 218 | "links": [ 219 | 2 220 | ], 221 | "slot_index": 1 222 | }, 223 | { 224 | "name": "CONDITIONING-", 225 | "type": "CONDITIONING", 226 | "links": [ 227 | 3 228 | ], 229 | "slot_index": 2 230 | }, 231 | { 232 | "name": "LATENT", 233 | "type": "LATENT", 234 | "links": [ 235 | 4 236 | ], 237 | "slot_index": 3 238 | }, 239 | { 240 | "name": "VAE", 241 | "type": "VAE", 242 | "links": [ 243 | 5 244 | ], 245 | "slot_index": 4 246 | }, 247 | { 248 | "name": "CLIP", 249 | "type": "CLIP", 250 | "links": null 251 | } 252 | ], 253 | "properties": { 254 | "Node name for S&R": "Efficient Loader" 255 | }, 256 | "widgets_values": [ 257 | "Meina-v9.safetensors", 258 | "novelai.vae.pt", 259 | -2, 260 | "1girl, (hanfu), sidelighting, wallpaper", 261 | "(worst quality:2, low quality:2), (zombie, sketch, interlocked fingers, comic)", 262 | 512, 263 | 512, 264 | 1 265 | ], 266 | "color": "#223", 267 | "bgcolor": "#335" 268 | }, 269 | { 270 | "id": 5, 271 | "type": "ImagesGridByColumns", 272 | "pos": [ 273 | 1293, 274 | 247 275 | ], 276 | "size": [ 277 | 315, 278 | 102 279 | ], 280 | "flags": {}, 281 | "order": 5, 282 | "mode": 0, 283 | "inputs": [ 284 | { 285 | "name": "images", 286 | "type": "IMAGE", 287 | "link": 29, 288 | "slot_index": 0 289 | }, 290 | { 291 | "name": "annotation", 292 | "type": "GRID_ANNOTATION", 293 | "link": 36 294 | } 295 | ], 296 | "outputs": [ 297 | { 298 | "name": "IMAGE", 299 | "type": "IMAGE", 300 | "links": [ 301 | 15 302 | ], 303 | "slot_index": 0 304 | } 305 | ], 306 | "properties": { 307 | "Node name for S&R": "ImagesGridByColumns" 308 | }, 309 | "widgets_values": [ 310 | 5, 311 | 3 312 | ], 313 | "color": "#322", 314 | "bgcolor": "#533" 315 | }, 316 | { 317 | "id": 7, 318 | "type": "PreviewImage", 319 | "pos": [ 320 | 1290, 321 | 388 322 | ], 323 | "size": { 324 | "0": 737.5480346679688, 325 | "1": 635.1529541015625 326 | }, 327 | "flags": { 328 | "collapsed": false 329 | }, 330 | "order": 6, 331 | "mode": 0, 332 | "inputs": [ 333 | { 334 | "name": "images", 335 | "type": "IMAGE", 336 | "link": 15 337 | } 338 | ], 339 | "properties": { 340 | "Node name for S&R": "Preview Image" 341 | } 342 | }, 343 | { 344 | "id": 19, 345 | "type": "GridAnnotation", 346 | "pos": [ 347 | 1070, 348 | 305 349 | ], 350 | "size": [ 351 | 210, 352 | 326 353 | ], 354 | "flags": {}, 355 | "order": 2, 356 | "mode": 0, 357 | "outputs": [ 358 | { 359 | "name": "GRID_ANNOTATION", 360 | "type": "GRID_ANNOTATION", 361 | "links": [ 362 | 36 363 | ], 364 | "slot_index": 0 365 | } 366 | ], 367 | "properties": { 368 | "Node name for S&R": "GridAnnotation" 369 | }, 370 | "widgets_values": [ 371 | "Steps: 20;Steps: 30;Steps: 40", 372 | "CGF: 7\nQuality: 10/10;\nCGF: 9\nQuality: 9/10;\nCGF: 11\nQuality: 5/10;", 373 | 50 374 | ], 375 | "color": "#322", 376 | "bgcolor": "#533" 377 | } 378 | ], 379 | "links": [ 380 | [ 381 | 1, 382 | 1, 383 | 0, 384 | 2, 385 | 0, 386 | "MODEL" 387 | ], 388 | [ 389 | 2, 390 | 1, 391 | 1, 392 | 2, 393 | 1, 394 | "CONDITIONING" 395 | ], 396 | [ 397 | 3, 398 | 1, 399 | 2, 400 | 2, 401 | 2, 402 | "CONDITIONING" 403 | ], 404 | [ 405 | 4, 406 | 1, 407 | 3, 408 | 2, 409 | 3, 410 | "LATENT" 411 | ], 412 | [ 413 | 5, 414 | 1, 415 | 4, 416 | 2, 417 | 4, 418 | "VAE" 419 | ], 420 | [ 421 | 11, 422 | 2, 423 | 3, 424 | 6, 425 | 0, 426 | "LATENT" 427 | ], 428 | [ 429 | 12, 430 | 2, 431 | 4, 432 | 6, 433 | 1, 434 | "VAE" 435 | ], 436 | [ 437 | 15, 438 | 5, 439 | 0, 440 | 7, 441 | 0, 442 | "IMAGE" 443 | ], 444 | [ 445 | 23, 446 | 3, 447 | 0, 448 | 2, 449 | 5, 450 | "SCRIPT" 451 | ], 452 | [ 453 | 29, 454 | 6, 455 | 0, 456 | 5, 457 | 0, 458 | "IMAGE" 459 | ], 460 | [ 461 | 36, 462 | 19, 463 | 0, 464 | 5, 465 | 1, 466 | "GRID_ANNOTATION" 467 | ] 468 | ], 469 | "groups": [], 470 | "config": {}, 471 | "extra": {}, 472 | "version": 0.4 473 | } -------------------------------------------------------------------------------- /workflows/efficiency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LEv145/images-grid-comfy-plugin/852db490ef93702e1c68fe9774bdf65aaa7d3574/workflows/efficiency.png -------------------------------------------------------------------------------- /workflows/mini.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 74, 3 | "last_link_id": 126, 4 | "nodes": [ 5 | { 6 | "id": 68, 7 | "type": "LoadImage", 8 | "pos": [ 9 | -30, 10 | 70 11 | ], 12 | "size": { 13 | "0": 315, 14 | "1": 102 15 | }, 16 | "flags": { 17 | "collapsed": true 18 | }, 19 | "order": 0, 20 | "mode": 0, 21 | "outputs": [ 22 | { 23 | "name": "IMAGE", 24 | "type": "IMAGE", 25 | "links": [ 26 | 119 27 | ], 28 | "slot_index": 0 29 | }, 30 | { 31 | "name": "MASK", 32 | "type": "MASK", 33 | "links": null 34 | } 35 | ], 36 | "properties": { 37 | "Node name for S&R": "LoadImage" 38 | }, 39 | "widgets_values": [ 40 | "394102.png", 41 | "image" 42 | ] 43 | }, 44 | { 45 | "id": 41, 46 | "type": "LoadImage", 47 | "pos": [ 48 | -30, 49 | 210 50 | ], 51 | "size": { 52 | "0": 315, 53 | "1": 102 54 | }, 55 | "flags": { 56 | "collapsed": true 57 | }, 58 | "order": 1, 59 | "mode": 0, 60 | "outputs": [ 61 | { 62 | "name": "IMAGE", 63 | "type": "IMAGE", 64 | "links": [ 65 | 120 66 | ], 67 | "slot_index": 0 68 | }, 69 | { 70 | "name": "MASK", 71 | "type": "MASK", 72 | "links": null 73 | } 74 | ], 75 | "properties": { 76 | "Node name for S&R": "LoadImage" 77 | }, 78 | "widgets_values": [ 79 | "394102.png", 80 | "image" 81 | ] 82 | }, 83 | { 84 | "id": 42, 85 | "type": "LoadImage", 86 | "pos": [ 87 | -30, 88 | 350 89 | ], 90 | "size": { 91 | "0": 315, 92 | "1": 102 93 | }, 94 | "flags": { 95 | "collapsed": true 96 | }, 97 | "order": 2, 98 | "mode": 0, 99 | "outputs": [ 100 | { 101 | "name": "IMAGE", 102 | "type": "IMAGE", 103 | "links": [ 104 | 112 105 | ], 106 | "slot_index": 0 107 | }, 108 | { 109 | "name": "MASK", 110 | "type": "MASK", 111 | "links": null 112 | } 113 | ], 114 | "properties": { 115 | "Node name for S&R": "LoadImage" 116 | }, 117 | "widgets_values": [ 118 | "394102.png", 119 | "image" 120 | ] 121 | }, 122 | { 123 | "id": 43, 124 | "type": "LoadImage", 125 | "pos": [ 126 | -30, 127 | 490 128 | ], 129 | "size": { 130 | "0": 315, 131 | "1": 102 132 | }, 133 | "flags": { 134 | "collapsed": true 135 | }, 136 | "order": 3, 137 | "mode": 0, 138 | "outputs": [ 139 | { 140 | "name": "IMAGE", 141 | "type": "IMAGE", 142 | "links": [ 143 | 106 144 | ], 145 | "slot_index": 0 146 | }, 147 | { 148 | "name": "MASK", 149 | "type": "MASK", 150 | "links": null 151 | } 152 | ], 153 | "properties": { 154 | "Node name for S&R": "LoadImage" 155 | }, 156 | "widgets_values": [ 157 | "394102.png", 158 | "image" 159 | ] 160 | }, 161 | { 162 | "id": 62, 163 | "type": "LoadImage", 164 | "pos": [ 165 | -30, 166 | 630 167 | ], 168 | "size": { 169 | "0": 315, 170 | "1": 102 171 | }, 172 | "flags": { 173 | "collapsed": true 174 | }, 175 | "order": 4, 176 | "mode": 0, 177 | "outputs": [ 178 | { 179 | "name": "IMAGE", 180 | "type": "IMAGE", 181 | "links": [ 182 | 105 183 | ], 184 | "slot_index": 0 185 | }, 186 | { 187 | "name": "MASK", 188 | "type": "MASK", 189 | "links": null 190 | } 191 | ], 192 | "properties": { 193 | "Node name for S&R": "LoadImage" 194 | }, 195 | "widgets_values": [ 196 | "394102.png", 197 | "image" 198 | ] 199 | }, 200 | { 201 | "id": 63, 202 | "type": "LoadImage", 203 | "pos": [ 204 | -30, 205 | 770 206 | ], 207 | "size": { 208 | "0": 315, 209 | "1": 102 210 | }, 211 | "flags": { 212 | "collapsed": true 213 | }, 214 | "order": 5, 215 | "mode": 0, 216 | "outputs": [ 217 | { 218 | "name": "IMAGE", 219 | "type": "IMAGE", 220 | "links": [ 221 | 104 222 | ], 223 | "slot_index": 0 224 | }, 225 | { 226 | "name": "MASK", 227 | "type": "MASK", 228 | "links": null 229 | } 230 | ], 231 | "properties": { 232 | "Node name for S&R": "LoadImage" 233 | }, 234 | "widgets_values": [ 235 | "394102.png", 236 | "image" 237 | ] 238 | }, 239 | { 240 | "id": 65, 241 | "type": "ImageCombine", 242 | "pos": [ 243 | 460, 244 | 430 245 | ], 246 | "size": { 247 | "0": 210, 248 | "1": 46 249 | }, 250 | "flags": {}, 251 | "order": 9, 252 | "mode": 0, 253 | "inputs": [ 254 | { 255 | "name": "image_1", 256 | "type": "IMAGE", 257 | "link": 113 258 | }, 259 | { 260 | "name": "image_2", 261 | "type": "IMAGE", 262 | "link": 106 263 | } 264 | ], 265 | "outputs": [ 266 | { 267 | "name": "IMAGE", 268 | "type": "IMAGE", 269 | "links": [ 270 | 114 271 | ], 272 | "slot_index": 0 273 | } 274 | ], 275 | "properties": { 276 | "Node name for S&R": "ImageCombine" 277 | }, 278 | "color": "#322", 279 | "bgcolor": "#533" 280 | }, 281 | { 282 | "id": 67, 283 | "type": "ImageCombine", 284 | "pos": [ 285 | 460, 286 | 350 287 | ], 288 | "size": { 289 | "0": 210, 290 | "1": 46 291 | }, 292 | "flags": {}, 293 | "order": 8, 294 | "mode": 0, 295 | "inputs": [ 296 | { 297 | "name": "image_1", 298 | "type": "IMAGE", 299 | "link": 118 300 | }, 301 | { 302 | "name": "image_2", 303 | "type": "IMAGE", 304 | "link": 112 305 | } 306 | ], 307 | "outputs": [ 308 | { 309 | "name": "IMAGE", 310 | "type": "IMAGE", 311 | "links": [ 312 | 113 313 | ], 314 | "slot_index": 0 315 | } 316 | ], 317 | "properties": { 318 | "Node name for S&R": "ImageCombine" 319 | }, 320 | "color": "#322", 321 | "bgcolor": "#533" 322 | }, 323 | { 324 | "id": 69, 325 | "type": "ImageCombine", 326 | "pos": [ 327 | 460, 328 | 270 329 | ], 330 | "size": { 331 | "0": 210, 332 | "1": 46 333 | }, 334 | "flags": {}, 335 | "order": 7, 336 | "mode": 0, 337 | "inputs": [ 338 | { 339 | "name": "image_1", 340 | "type": "IMAGE", 341 | "link": 119 342 | }, 343 | { 344 | "name": "image_2", 345 | "type": "IMAGE", 346 | "link": 120 347 | } 348 | ], 349 | "outputs": [ 350 | { 351 | "name": "IMAGE", 352 | "type": "IMAGE", 353 | "links": [ 354 | 118 355 | ], 356 | "slot_index": 0 357 | } 358 | ], 359 | "properties": { 360 | "Node name for S&R": "ImageCombine" 361 | }, 362 | "color": "#322", 363 | "bgcolor": "#533" 364 | }, 365 | { 366 | "id": 55, 367 | "type": "ImageCombine", 368 | "pos": [ 369 | 460, 370 | 510 371 | ], 372 | "size": { 373 | "0": 210, 374 | "1": 46 375 | }, 376 | "flags": {}, 377 | "order": 10, 378 | "mode": 0, 379 | "inputs": [ 380 | { 381 | "name": "image_1", 382 | "type": "IMAGE", 383 | "link": 114 384 | }, 385 | { 386 | "name": "image_2", 387 | "type": "IMAGE", 388 | "link": 105 389 | } 390 | ], 391 | "outputs": [ 392 | { 393 | "name": "IMAGE", 394 | "type": "IMAGE", 395 | "links": [ 396 | 110 397 | ], 398 | "slot_index": 0 399 | } 400 | ], 401 | "properties": { 402 | "Node name for S&R": "ImageCombine" 403 | }, 404 | "color": "#322", 405 | "bgcolor": "#533" 406 | }, 407 | { 408 | "id": 54, 409 | "type": "ImageCombine", 410 | "pos": [ 411 | 460, 412 | 590 413 | ], 414 | "size": { 415 | "0": 210, 416 | "1": 46 417 | }, 418 | "flags": {}, 419 | "order": 11, 420 | "mode": 0, 421 | "inputs": [ 422 | { 423 | "name": "image_1", 424 | "type": "IMAGE", 425 | "link": 110 426 | }, 427 | { 428 | "name": "image_2", 429 | "type": "IMAGE", 430 | "link": 104 431 | } 432 | ], 433 | "outputs": [ 434 | { 435 | "name": "IMAGE", 436 | "type": "IMAGE", 437 | "links": [ 438 | 115, 439 | 122 440 | ], 441 | "slot_index": 0 442 | } 443 | ], 444 | "properties": { 445 | "Node name for S&R": "ImageCombine" 446 | }, 447 | "color": "#322", 448 | "bgcolor": "#533" 449 | }, 450 | { 451 | "id": 73, 452 | "type": "Reroute", 453 | "pos": [ 454 | 1370, 455 | 240 456 | ], 457 | "size": [ 458 | 75, 459 | 26 460 | ], 461 | "flags": {}, 462 | "order": 15, 463 | "mode": 0, 464 | "inputs": [ 465 | { 466 | "name": "", 467 | "type": "*", 468 | "link": 125, 469 | "slot_index": 0 470 | } 471 | ], 472 | "outputs": [ 473 | { 474 | "name": "", 475 | "type": "IMAGE", 476 | "links": [ 477 | 124 478 | ], 479 | "slot_index": 0 480 | } 481 | ], 482 | "properties": { 483 | "showOutputText": false, 484 | "horizontal": false 485 | } 486 | }, 487 | { 488 | "id": 74, 489 | "type": "PreviewImage", 490 | "pos": [ 491 | 1098, 492 | 383 493 | ], 494 | "size": { 495 | "0": 332.1975402832031, 496 | "1": 513.37060546875 497 | }, 498 | "flags": {}, 499 | "order": 14, 500 | "mode": 0, 501 | "inputs": [ 502 | { 503 | "name": "images", 504 | "type": "IMAGE", 505 | "link": 126 506 | } 507 | ], 508 | "properties": { 509 | "Node name for S&R": "PreviewImage" 510 | } 511 | }, 512 | { 513 | "id": 72, 514 | "type": "PreviewImage", 515 | "pos": [ 516 | 1460, 517 | 380 518 | ], 519 | "size": { 520 | "0": 353, 521 | "1": 510 522 | }, 523 | "flags": {}, 524 | "order": 16, 525 | "mode": 0, 526 | "inputs": [ 527 | { 528 | "name": "images", 529 | "type": "IMAGE", 530 | "link": 124 531 | } 532 | ], 533 | "properties": { 534 | "Node name for S&R": "PreviewImage" 535 | } 536 | }, 537 | { 538 | "id": 61, 539 | "type": "ImagesGridByRows", 540 | "pos": [ 541 | 740, 542 | 380 543 | ], 544 | "size": { 545 | "0": 315, 546 | "1": 102 547 | }, 548 | "flags": {}, 549 | "order": 12, 550 | "mode": 0, 551 | "inputs": [ 552 | { 553 | "name": "images", 554 | "type": "IMAGE", 555 | "link": 115 556 | }, 557 | { 558 | "name": "annotation", 559 | "type": "GRID_ANNOTATION", 560 | "link": 102 561 | } 562 | ], 563 | "outputs": [ 564 | { 565 | "name": "IMAGE", 566 | "type": "IMAGE", 567 | "links": [ 568 | 126 569 | ], 570 | "slot_index": 0 571 | } 572 | ], 573 | "properties": { 574 | "Node name for S&R": "ImagesGridByRows" 575 | }, 576 | "widgets_values": [ 577 | 30, 578 | 2 579 | ], 580 | "color": "#322", 581 | "bgcolor": "#533" 582 | }, 583 | { 584 | "id": 71, 585 | "type": "ImagesGridByColumns", 586 | "pos": [ 587 | 740, 588 | 233 589 | ], 590 | "size": { 591 | "0": 315, 592 | "1": 102 593 | }, 594 | "flags": {}, 595 | "order": 13, 596 | "mode": 0, 597 | "inputs": [ 598 | { 599 | "name": "images", 600 | "type": "IMAGE", 601 | "link": 122 602 | }, 603 | { 604 | "name": "annotation", 605 | "type": "GRID_ANNOTATION", 606 | "link": null 607 | } 608 | ], 609 | "outputs": [ 610 | { 611 | "name": "IMAGE", 612 | "type": "IMAGE", 613 | "links": [ 614 | 125 615 | ], 616 | "slot_index": 0 617 | } 618 | ], 619 | "properties": { 620 | "Node name for S&R": "ImagesGridByColumns" 621 | }, 622 | "widgets_values": [ 623 | 30, 624 | 2 625 | ], 626 | "color": "#322", 627 | "bgcolor": "#533" 628 | }, 629 | { 630 | "id": 60, 631 | "type": "GridAnnotation", 632 | "pos": [ 633 | 350, 634 | 690 635 | ], 636 | "size": { 637 | "0": 315, 638 | "1": 106 639 | }, 640 | "flags": {}, 641 | "order": 6, 642 | "mode": 0, 643 | "outputs": [ 644 | { 645 | "name": "GRID_ANNOTATION", 646 | "type": "GRID_ANNOTATION", 647 | "links": [ 648 | 102 649 | ], 650 | "slot_index": 0 651 | } 652 | ], 653 | "properties": { 654 | "Node name for S&R": "GridAnnotation" 655 | }, 656 | "widgets_values": [ 657 | "Column 1; Column 2; Column 3", 658 | "My favorite;Others", 659 | 400 660 | ], 661 | "color": "#322", 662 | "bgcolor": "#533" 663 | } 664 | ], 665 | "links": [ 666 | [ 667 | 102, 668 | 60, 669 | 0, 670 | 61, 671 | 1, 672 | "GRID_ANNOTATION" 673 | ], 674 | [ 675 | 104, 676 | 63, 677 | 0, 678 | 54, 679 | 1, 680 | "IMAGE" 681 | ], 682 | [ 683 | 105, 684 | 62, 685 | 0, 686 | 55, 687 | 1, 688 | "IMAGE" 689 | ], 690 | [ 691 | 106, 692 | 43, 693 | 0, 694 | 65, 695 | 1, 696 | "IMAGE" 697 | ], 698 | [ 699 | 110, 700 | 55, 701 | 0, 702 | 54, 703 | 0, 704 | "IMAGE" 705 | ], 706 | [ 707 | 112, 708 | 42, 709 | 0, 710 | 67, 711 | 1, 712 | "IMAGE" 713 | ], 714 | [ 715 | 113, 716 | 67, 717 | 0, 718 | 65, 719 | 0, 720 | "IMAGE" 721 | ], 722 | [ 723 | 114, 724 | 65, 725 | 0, 726 | 55, 727 | 0, 728 | "IMAGE" 729 | ], 730 | [ 731 | 115, 732 | 54, 733 | 0, 734 | 61, 735 | 0, 736 | "IMAGE" 737 | ], 738 | [ 739 | 118, 740 | 69, 741 | 0, 742 | 67, 743 | 0, 744 | "IMAGE" 745 | ], 746 | [ 747 | 119, 748 | 68, 749 | 0, 750 | 69, 751 | 0, 752 | "IMAGE" 753 | ], 754 | [ 755 | 120, 756 | 41, 757 | 0, 758 | 69, 759 | 1, 760 | "IMAGE" 761 | ], 762 | [ 763 | 122, 764 | 54, 765 | 0, 766 | 71, 767 | 0, 768 | "IMAGE" 769 | ], 770 | [ 771 | 124, 772 | 73, 773 | 0, 774 | 72, 775 | 0, 776 | "IMAGE" 777 | ], 778 | [ 779 | 125, 780 | 71, 781 | 0, 782 | 73, 783 | 0, 784 | "*" 785 | ], 786 | [ 787 | 126, 788 | 61, 789 | 0, 790 | 74, 791 | 0, 792 | "IMAGE" 793 | ] 794 | ], 795 | "groups": [], 796 | "config": {}, 797 | "extra": {}, 798 | "version": 0.4 799 | } -------------------------------------------------------------------------------- /workflows/mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LEv145/images-grid-comfy-plugin/852db490ef93702e1c68fe9774bdf65aaa7d3574/workflows/mini.png --------------------------------------------------------------------------------