CivArchive
    ← All articles
    Published July 12, 2024by A_Friendly_Spider

    A useful set of scripts: Randomize and more!

    260 views2 reactions0 comments on CivitAI11 collected
    tool guidegeneration data

    Since I've gotten started I've had to learn a few things even though some of it may eventually become useless or need to be repurposed and integrated once I start picking up comfyui, but for my Forge/A1111 WebUi brothers I have a set of scripts that may help you.

    Prompt Generation: Randomize, Fluctuate, and Control Weight and Prompt Tags.

    So this first bit is all about Large Batch prompt tasks. When I have nothing queued up to be produced, it means I can experiment with prompts, Lora's, checkpoints, and break stuff with pure throughput generation looking for interesting image subsets or tag/weights to use for more specific higher level generation.

    This is where my Batch Prompt Generator comes in handy. It takes a simple text file with custom keyword arguments and puts them through a python script to give large batches of prompt outputs.

    As an example:

    $Random_tag() This sets up a set of tags or groups of tags to be chosen at random every time it generates a prompt.
    Further we can augment it's behavior like so

    $random_tag((Hat, Hood),(&(Scarf, Sunglasses, Headband)&), (Facemask))$

    This should output one of the following:

    Hat, Hood

    (Scarf, Sunglasses, Headband)

    Facemask

    You can also do things more advanced than this, like swapping or randomly adding different Lora tags etc.

    Random Numbers and Controlled Numbers

    The next are far more simple and just mathematic functions I initially used to try and manipulate slider Lora for video frame generation.

    &sine_wav, 2, -2, 60, 2& -- This script generates numbers in a wave pattern between 2 and -2 over the period of 60 prompt generations with a percision of 2 decimal places: 0.00. The idea being you can shift between the extremes of weights over a controlled period of generations.

    &rand, 2, -2, 2& -- Much the same, just generates a random number between 2 and -2 up to .00 decimal places.

    Process Control and Large Batch Control Tags

    Another thing I decided I needed, and someone might find useful, is being able to stop adding or start adding certain tags into the prompts they are generating after certain number of prompts generated. The idea being that say if you have a Lora or Prompt meant to start walking, you could drop the walking tag and start a running one at the same time in a large batch of prompts.

    $start 5, $top 10 -- This will start a prompt it is contained in after 5 generated prompts, and stop adding it to further prompts after 10 generations.

    The last bit of this script pulls all of these commands and supportive syntax out of the batch generations so you can just plug and go.

    How to use

    First you need two text files to make a .bat and a .py

    process_prompts.bat

    @echo off
    echo Enter the path to the input file containing prompts:
    set /p input_file=
    echo Enter the name for the new output text file (without extension):
    set /p output_file_name=
    echo Enter the total number of generations to process:
    set /p total_generations=
    python process_prompts.py "%input_file%" "%output_file_name%.txt" "%total_generations%"
    echo Processing complete.
    pause

    process_prompts.py

    import sys
    import re
    import math
    import random
    
    def sine_wave(index, total, min_weight, max_weight, angle, precision):
        amplitude = (max_weight - min_weight) / 2
        midline = (max_weight + min_weight) / 2
        angle_rad = math.radians(angle)
        weight = midline + amplitude * math.sin(angle_rad * (index / total) * 2 * math.pi)
        return round(weight, precision)
    
    def random_weight(min_weight, max_weight, precision):
        weight = random.uniform(min_weight, max_weight)
        return round(weight, precision)
    
    def random_tag(tags):
        processed_tags = []
        buffer = ""
        depth = 0  # Depth of nested parentheses
        special_format = False  # Whether we are in a special format group
    
        i = 0
        while i < len(tags):
            char = tags[i]
    
            # Check for special format toggle
            if char == '&' and (i == 0 or tags[i-1] != '\\') and (i+1 < len(tags) and tags[i+1] == '('):
                special_format = not special_format
                i += 1  # Skip '&' and '('
                continue
            elif char == '&' and (i+1 < len(tags) and tags[i+1] == ')'):
                i += 1  # Skip '&' and ')'
                special_format = not special_format
                continue
    
            if char == '(':
                depth += 1
            elif char == ')':
                depth -= 1
                if depth == 0 and special_format:
                    # Special format: Keep parentheses in output
                    processed_tags.append(buffer + char)
                    buffer = ""
                    continue
    
            if char == ',' and depth == 0:
                if buffer.strip():
                    processed_tags.append(buffer.strip())
                buffer = ""
            else:
                buffer += char
    
            i += 1
    
        if buffer.strip():
            processed_tags.append(buffer.strip())
    
        chosen_tag = random.choice(processed_tags).strip()
        # Post-process to remove outer parentheses if not in special format
        if chosen_tag.startswith('(') and chosen_tag.endswith(')') and not special_format:
            return chosen_tag[1:-1]
        return chosen_tag
    
    
    def process_prompt(prompt, index, total_generations):
        sine_wave_pattern = re.compile(r"&sine_wav, (-?\d+\.?\d*), (-?\d+\.?\d*), (-?\d+\.?\d*), (\d+)&")
        random_weight_pattern = re.compile(r"&Rand, (-?\d+\.?\d*), (-?\d+\.?\d*)&")
        random_tag_pattern = re.compile(r"\$random_tag\((.*?)\)\$")
        directive_pattern = re.compile(r"(<[^<>]*\$(top|tart) \d+[^<>]*>)")
    
        def check_visibility(match):
            content = match.group(0)
            directive, number = re.search(r"\$(top|tart) (\d+)", content).groups()
            threshold = int(number)
            if (directive == "top" and index >= threshold) or (directive == "tart" and index < threshold):
                return ""
            return content
    
        prompt = re.sub(directive_pattern, check_visibility, prompt)
        prompt = re.sub(sine_wave_pattern, lambda m: str(sine_wave(index, total_generations, float(m.group(2)), float(m.group(1)), float(m.group(3)), int(m.group(4)))), prompt)
        prompt = re.sub(random_weight_pattern, lambda m: str(random_weight(float(m.group(1)), float(m.group(2)), 2)), prompt)
        prompt = re.sub(random_tag_pattern, lambda m: random_tag(m.group(1)), prompt)
        prompt = re.sub(r"\$top \d+|\$tart \d+|\$[^$]*\$", "", prompt)
    
        return prompt
    
    def main(input_file, output_file, total_generations):
        with open(input_file, 'r') as file:
            prompts = file.readlines()
    
        processed_prompts = []
    
        for index in range(total_generations):
            for prompt in prompts:
                processed_prompt = process_prompt(prompt.strip(), index, total_generations)
                processed_prompts.append(processed_prompt)
    
        with open(output_file, 'w') as file:
            file.write("\n".join(processed_prompts))
    
    if __name__ == "__main__":
        input_file = sys.argv[1]
        output_file = sys.argv[2]
        total_generations = int(sys.argv[3])
        main(input_file, output_file, total_generations)
    

    Next just make a text file with your guide prompt that you want iterated, copy its path, run the .bat, use the path, name the new text file output in the command window, and how many generations you want it to make and your good to go.
    You can then drag and drop the new text file (assuming you didn't make a syntax error) and you now have hours and hours of generation time when you're otherwise not wanting to either manually or handling more sensitive generation tweaking.

    Attachments