Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What the best way to batch generate multiple images with different parameters? #1330

Open
paintbot opened this issue Aug 25, 2023 · 3 comments

Comments

@paintbot
Copy link

I'm new to ComfyUI, have to say I love the approach! (node based + community ecosystem)

I'm looking for a solution to batch generate images in an automated way with different parameters, prompts or even models? As I understood its not possible to do with native nodes? Are there any tricks or workarounds? I found out that you can do it somehow with 'primitive node' but ist not very efficient.

Is it possible to do this with custom nodes? https://github.com/hnmr293/ComfyUI-nodes-hnmr seems to come closest to what I'm looking for but it seems to be broken at the moment? (nodes don't show up after install) Are there any alternatives? Ideal would be some thing like lists with the different parameters (something like below) but not for rendering grids but individual images. Am grateful for any hint in the direction of automation..

chrome_4NieEPy0cy

@paintbot
Copy link
Author

Anybody any idea?

@tusharbhutt
Copy link

So, basic X/Y Plotting? Maybe this will help:

https://github.com/LucianoCirino/efficiency-nodes-comfyui

It has these X/Y plotters

image

@tjedwards
Copy link

tjedwards commented Sep 12, 2023

I came up with a way using a custom node. (The basic trick could easily be applied to your own node.) I call mine FormattedLineByIndex, and as inputs it takes a fmt, a STRING, and lines, a multiline STRING. It outputs result, a STRING, which is (initially) the first line from lines with the format applied.

To use it, you fill lines with many lines of input and you use the "Extra options->Batch count" setting to ramp the number of batches up to match (or exceed) the line count. Then you press "Queue Prompt" and watch the magic.

The trick is this: There's a class variable (essentially a static variable) called config which holds both index and firstline. Every time the FUNCTION is executed, index is incremented, meaning that the next call will have a new index value. The IS_CHANGED() classmethod is defined to return float("nan"), ensuring the node instance is re-invoked for every prompt.

The firstline config records the first line of the lines input. If that changes, the index config is reset. Also, if the index exceeds available lines it's reset and an exception is thrown. This last part (an exception) is key to stopping an automatic batch run!

The code does need an improvement if you intend to have more than one instance in your setup; the issue is that the static config needs to account for each instance independently. This is easily accomplished using a dictionary where the key is the self of the controlling instance.

Anyway, here's the thing in its current state:

class FormattedLineByIndex:
    class Config:
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
    #TBD multi-instance fix: make this a dict where the caller's self is the key
    config = Config(index=0, firstline=None)

    @classmethod
    def INPUT_TYPES(s):
        return {
            "required": {
                "fmt": ("STRING",{"default":"LINE"}),
                "lines": ("STRING",{"multiline":True}),
            },
        }

    @classmethod
    def IS_CHANGED(s, *args):
        '''forces re-eval'''
        return float("nan")

    RETURN_TYPES = ["STRING","INT","INT"]
    RETURN_NAMES = ["result","next_idx","len"]
    CATEGORY = "utils"
    FUNCTION = "apply_index"
    def apply_index(self, fmt, lines):
        config = FormattedLineByIndex.config
        lines = [e for e in lines.strip().split("\n") if e]
        if not lines:
            raise Exception("NO INPUT")
        firstline = lines[0]
        if firstline != config.firstline:
            print("FormattedLineByIndex: First line changed"
                  f" (from '{config.firstline}' to '{firstline}')"
                  ", resetting index.")
            config.index = 0
            config.firstline = firstline
        lines = [e for e in lines if e.lstrip()[0] != "#"]
        if not lines:
            raise Exception("NO INPUT")
        index = config.index
        config.index += 1 #for next time
        if index >= len(lines):
            #reset for another run, then throw an exception
            config.firstline = None
            raise Exception(f"BAD INDEX: {index}")
        line = lines[index]
        #replace the substring 'LINE' with the contents of line
        return [fmt.replace("LINE",line), config.index, len(lines)]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants