From 0b78b050b6d2d0075c34be19c07e01ebb746cfc2 Mon Sep 17 00:00:00 2001 From: Tropfchen <6117944+Tropfchen@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:55:03 +0200 Subject: [PATCH] init --- .gitignore | 160 +++++++++++++++++++++++++++++++ README.md | 35 +++++++ __init__.py | 25 +++++ js/quickNodes.js | 161 +++++++++++++++++++++++++++++++ nodes.py | 241 +++++++++++++++++++++++++++++++++++++++++++++++ yeetctor.PNG | Bin 0 -> 22641 bytes 6 files changed, 622 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100644 js/quickNodes.js create mode 100644 nodes.py create mode 100644 yeetctor.PNG diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..60dde52 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Yet Another Resolution Selector (YARS) + +A slightly different Resolution Selector node, allowing to freely change base resolution and aspect ratio, with options to maintain the pixel count or use the base resolution as the highest or lowest dimension. + +![Example](yeetctor.png) + +## Installation + +Exactly the same as with other simple custom nodes. + +- Click the green **Code** button, select **Download Zip**, and unpack it in your ComfyUI `custom_nodes` directory + +or + +- Clone this repository by running `git clone https://github.com/Tropfchen/ComfyUI-yaResolutionSelector.git` in your ComfyUI `custom_nodes` directory + +To uninstall: + +- Delete the `ComfyUI-yaResolutionSelector` in your ComfyUI custom_nodes directory and `yarsQuickNodes.js` file in `ComfyUI\web\extensions\tropf` directory + +## Use + +Simply right click on `Empty Latent Image` and choose one of `Prepend yaResolution Selector`, you can also find new nodes in `utils` menu. + +### yaResolution Selector (Advanced) + +Recommend node with flexible ratio choice + +- base_resolution: in theory, this should match the base resolution of the model you're using (512 for SD1.5, 1024 for SDXL). +- Overextend: By default, with a 2:1 ratio and a base resolution of 512, the node will output 256x512. However, when 'overextend' is set to true, the output will be 512x1024. This is beneficial when using models that can still generate good images at resolutions larger than the base resolution. +- constant_resolution: The node will attempt to output dimensions with the same pixel count as a 1:1 image generated with the base resolution. This outputs resolutions similar to those recommended for use with SDXL. It's also useful with node connected to an image rescale node, especially when you know the maximum resolution that your GPU VRAM can safely handle. + +### yaResolution Selector + +Basic version for users that prefer aspect ratio presets. New presets can be added by modifying nodes.py file, remember to always include values in format width:height diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..07ea6b2 --- /dev/null +++ b/__init__.py @@ -0,0 +1,25 @@ +from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS + +__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] + +# --------------------------- Install web extension ---------------------------- +import shutil +from pathlib import Path + +# Get base paths +node_dir = Path(__file__).resolve().parent +comfy_dir = node_dir.parent.parent + +script_path = node_dir / "js" / "quickNodes.js" +destination_dir = comfy_dir / "web" / "extensions" / "tropfchen" +destination_path = destination_dir / "yarsQuickNodes.js" + +ER = "during installing Embedding Picker web extension" +try: + destination_dir.mkdir(parents=True, exist_ok=True) + + shutil.copy2(script_path, destination_path) +except PermissionError as permission_error: + print(f"Permission error {ER}: {permission_error}") +except Exception as e: + print(f"An unexpected error occurred {ER}: {e}") diff --git a/js/quickNodes.js b/js/quickNodes.js new file mode 100644 index 0000000..d1ee0ab --- /dev/null +++ b/js/quickNodes.js @@ -0,0 +1,161 @@ +import { app } from "../../scripts/app.js"; + +// ----------- ComfyUI\web\extensions\core\widgetInputs.js copypaste ----------- +const CONVERTED_TYPE = "converted-widget"; +function hideWidget(node, widget, suffix = "") { + widget.origType = widget.type; + widget.origComputeSize = widget.computeSize; + widget.origSerializeValue = widget.serializeValue; + widget.computeSize = () => [0, -4]; // -4 is due to the gap litegraph adds between widgets automatically + widget.type = CONVERTED_TYPE + suffix; + widget.serializeValue = () => { + // Prevent serializing the widget if we have no input linked + if (!node.inputs) { + return undefined; + } + let node_input = node.inputs.find((i) => i.widget?.name === widget.name); + + if (!node_input || !node_input.link) { + return undefined; + } + return widget.origSerializeValue + ? widget.origSerializeValue() + : widget.value; + }; + + // Hide any linked widgets, e.g. seed+seedControl + if (widget.linkedWidgets) { + for (const w of widget.linkedWidgets) { + hideWidget(node, w, ":" + widget.name); + } + } +} + +function getWidgetType(config) { + // Special handling for COMBO so we restrict links based on the entries + let type = config[0]; + let linkType = type; + if (type instanceof Array) { + type = "COMBO"; + linkType = linkType.join(","); + } + return { type, linkType }; +} + +function convertToInput(node, widget, config) { + hideWidget(node, widget); + + const { linkType } = getWidgetType(config); + + // Add input and store widget config for creating on primitive node + const sz = node.size; + node.addInput(widget.name, linkType, { + widget: { name: widget.name, config }, + }); + + for (const widget of node.widgets) { + widget.last_y += LiteGraph.NODE_SLOT_HEIGHT; + } + + // Restore original size but grow if needed + node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]); +} +//------------------------------------------------------------------------------ + +// ------ section inspired by pythongosssss 'ComfyUI-Custom-Scripts' code ------ +function addMenuHandler(nodeType, callback) { + const oldMenuOptions = nodeType.prototype.getExtraMenuOptions; + + nodeType.prototype.getExtraMenuOptions = function () { + const menuOptions = oldMenuOptions.apply(this, arguments); + callback.apply(this, arguments); + + return menuOptions; + }; +} + +/** + * Creates a new node in the graph and adjusts its position based on the passed options. + * @param {string} name - The name of the new node. + * @param {object} nextTo - The reference node to position the new node next to. + * @param {object} [options={}] - Optional parameters to adjust the new node's behavior. + * @param {boolean} [options.select=true] - If set to true, the new node will be selected. + * @param {number} [options.shiftY=0] - The vertical shift from the reference node's position. + * @param {boolean} [options.before=false] - If true, the new node will be positioned + * to the left of the reference node; otherwise, to the right. + * @param {array} [options.size] - The size of the new node. + * + * @returns {object} The newly created node. + */ +function placeNewNode(name, nextTo, options = {}) { + const nodeSeparation = 30; + const { select = true, shiftY = 0, before = false, size } = options; + + const node = LiteGraph.createNode(name); + + if (size) { + node.size = size; + } + + app.graph.add(node); + + const [nextToX, nextToY] = nextTo.pos; + const [nextToWidth] = nextTo.size; + + const offsetX = before + ? -node.size[0] - nodeSeparation + : nextToWidth + nodeSeparation; + node.pos = [nextToX + offsetX, nextToY + shiftY]; + + if (select) { + app.canvas.selectNode(node, false); + } + + return node; +} + +function prependNewNode(node, nodeData, newNodeName, newNodewidgetNames) { + const newNode = placeNewNode(newNodeName, node, { + before: true, + shiftY: 0, + }); + + for (const widget_name of newNodewidgetNames) { + let slot = node.findInputSlot(widget_name); + if (slot === -1) { + //Convert widget into input + const w = node.widgets.find((obj) => obj.name === widget_name); + const { required, optional } = nodeData?.input; + const config = required[w.name] || + optional?.[w.name] || [w.type, w.options || {}]; + convertToInput(node, w, config); + + slot = node.findInputSlot(widget_name); + } + newNode.connect(newNode.findOutputSlot(widget_name), node, slot); + } +} + +app.registerExtension({ + name: "trop.YARS", + async beforeRegisterNodeDef(nodeType, nodeData, app) { + if ( + nodeData.name === "EmptyLatentImage" || + nodeData.name === "ImageScale" + ) { + addMenuHandler(nodeType, function (_, options) { + const entries = [ + { name: "yaResolution Selector", value: "YARS" }, + { name: "Advanced yaResolution Selector", value: "YARSAdv" }, + ]; + for (const entry of entries) { + options.unshift({ + content: `Prepend ${entry.name}`, + callback: () => + prependNewNode(this, nodeData, entry.value, ["width", "height"]), + }); + } + }); + } + }, +}); diff --git a/nodes.py b/nodes.py new file mode 100644 index 0000000..44b73a5 --- /dev/null +++ b/nodes.py @@ -0,0 +1,241 @@ +import re + + +class Dimensions: + def __init__(self, width: int, height: int): + self.width = width + self.height = height + + @property + def width(self): + return self._width + + @width.setter + def width(self, value: int): + if value < 64: + raise ValueError("width of less than 64 pixel") + self._width = int(value / 2) * 2 if value % 2 else value + + @property + def height(self): + return self._height + + @height.setter + def height(self, value: int): + if value < 64: + raise ValueError("height of less than 64 pixel") + self._height = int(value / 2) * 2 if value % 2 else value + + +def calculate_aspect_ratio( + base_resolution: int, ratio: float, overextend: bool +) -> Dimensions: + width = base_resolution + height = base_resolution + + if overextend: + if ratio > 1: + height *= ratio + else: + width /= ratio + else: + if ratio > 1: + width /= ratio + else: + height *= ratio + + return Dimensions(int(width), int(height)) + + +def calculate_constant_constant_resolution( + base_resolution: int, ratio: float +) -> Dimensions: + pixel_count = base_resolution ** 2 + new_height = (pixel_count * ratio) ** 0.5 + new_width = new_height / ratio + return Dimensions(new_width, new_height) + + +class YARS: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(self): + ratios = [ + "1:1", + "landscape (4:3)", + "landscape (3:2)", + "landscape (16:9)", + "landscape (16:10)", + "landscape (21:9)", + "portrait (3:4)", + "portrait (2:3)", + "portrait (9:16)", + "portrait (9:10)", + "portrait (9:21)", + ] + + return { + "required": { + "base_resolution": ( + "INT", + { + "default": 512, + "min": 512, + "max": 8192, + "step": 128, + }, + ), + "aspect_ratio": ((ratios),), + "overextend": ( + "BOOLEAN", + {"default": False, "label_on": "yes ", "label_off": "no "}, + ), + }, + } + + RETURN_TYPES = ("INT", "INT") + RETURN_NAMES = ("width", "height") + FUNCTION = "calculate" + OUTPUT_NODE = False + CATEGORY = "utils" + + def calculate( + self, + base_resolution: int, + aspect_ratio: str, + overextend: bool, + ) -> tuple[int, int]: + if m := re.search(r"(\d+):(\d+)", aspect_ratio): + ratio: float = int(m.group(2)) / int(m.group(1)) + d = calculate_aspect_ratio(base_resolution, ratio, overextend) + + return (d.width, d.height) + + raise ValueError(f"Could't find aspect ratio in string `{aspect_ratio}`") + + +class YARSAdv: + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(self): + return { + "required": { + "base_resolution": ( + "INT", + { + "default": 512, + "min": 512, + "max": 8192, + "step": 128, + }, + ), + "width_ratio": ( + "INT", + { + "default": 1, + "min": 1, + "max": 1024, + "step": 1, + }, + ), + "height_ratio": ( + "INT", + { + "default": 1, + "min": 1, + "max": 1024, + "step": 1, + }, + ), + "overextend": ( + "BOOLEAN", + {"default": False, "label_on": "yes ", "label_off": "no "}, + ), + "constant_resolution": ( + "BOOLEAN", + {"default": False, "label_on": "yes ", "label_off": "no "}, + ), + }, + } + + RETURN_TYPES = ("INT", "INT") + RETURN_NAMES = ("width", "height") + FUNCTION = "calculate" + OUTPUT_NODE = False + CATEGORY = "utils" + + def calculate( + self, + base_resolution: int, + width_ratio: int, + height_ratio: int, + overextend: bool, + constant_resolution: bool, + ) -> tuple[int, int]: + ratio: float = height_ratio / width_ratio + + if constant_resolution: + d = calculate_constant_constant_resolution(base_resolution, ratio) + else: + d = calculate_aspect_ratio(base_resolution, ratio, overextend) + + return (d.width, d.height) + + +NODE_CLASS_MAPPINGS = {"YARS": YARS, "YARSAdv": YARSAdv} +NODE_DISPLAY_NAME_MAPPINGS = { + "YARS": "yaResolution Selector", + "YARSAdv": "yaResolution Selector (Advanced)", +} + +if __name__ == "__main__": + import unittest + + class TestDimensions(unittest.TestCase): + def test_constant_resolution_calculates_correct_dimensions(self): + d = calculate_constant_constant_resolution(1024, 1) + self.assertEqual((d.width, d.height), (1024, 1024), "Incorrect dimensions") + + def test_constant_resolution_calculates_correct_aspect_ratio(self): + d = calculate_constant_constant_resolution(1024, 4 / 3) + self.assertAlmostEqual( + d.height / d.width, 4 / 3, 2, "Incorrect aspect ratio" + ) + + def test_constant_resolution_calculates_correct_pixel_count(self): + d = calculate_constant_constant_resolution(1024, 4 / 3) + self.assertEqual(d.width * d.height, 1047252, "Incorrect pixel count") + + def test_calculate_dimensions_with_false_overextend(self): + d = calculate_aspect_ratio(1024, 4 / 3, False) + self.assertEqual( + (d.width, d.height), + (768, 1024), + "Incorrect dimensions without overextending", + ) + + def test_calculate_dimensions_with_true_overextend(self): + d = calculate_aspect_ratio(512, 4 / 3, True) + self.assertEqual( + (d.width, d.height), + (512, 682), + "Incorrect dimensions with overextending", + ) + + def test_cleanup_dimensions_raises_error_with_low_height(self): + with self.assertRaises( + ValueError, msg="No error raised with height less than 64 pixels" + ): + Dimensions(512, 60) + + def test_cleanup_dimensions_raises_error_with_low_width(self): + with self.assertRaises( + ValueError, msg="No error raised with width less than 64 pixels" + ): + Dimensions(55, 512) + + unittest.main() diff --git a/yeetctor.PNG b/yeetctor.PNG new file mode 100644 index 0000000000000000000000000000000000000000..cd0b82b1e4c1e55470896e3a5cae81fdd8fd750e GIT binary patch literal 22641 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 literal 0 HcmV?d00001