04.25.25

Bas relief style CNC milling with an STL

So, we have these pieces that need a certain texture placed on them. Well, less a texture and more a 3D scanned surface of some sort of machine block or something. I’m not entirely sure of the application. All I know is that it requires a very fine surface to be milled so that it matches up with some sort of casting in order to grab it.

Anyways, were supplied with something like this, some much larger (this one’s 15″x5.5″):

That’s the trimmed up version before I export it. So what I wanted to do was have the surface be milled in a square spiral, basically so that it is always climb milling but that the output is linear, not some weird arc. MasterCAM has a variety of options for surfacing but nothing quite like that. I could have it zig-zag but I didn’t want that.

So, I just programmed my own generator in python. It worked out OK. These are the steps:

  • Load the STL file and find its X,Y and Z bounds
  • Generate a grid based on the bounds at a resolution of the supplied stepover.
  • Add a given number of grid lines to the outside, to give a bit of overlap
  • Generate a spiral path with the points
  • Scan each point on the grid with the Moller-Trumbore intersection aglorithm, a way to check the intersection of a ray to a triangle in 3D space. Perfect for STL since it’s all triangles.
  • Any point that does not have a collision, mark as a non cut
  • While scanning, use multiple ray intersections along the ball in order to account for the shape of the ball-nose end mill. (more resolution on the ball==slower)
  • Output G-code based on these intersections

That is the DXF of the output. Works well.

If anyone wants it, they can have it. Enjoy. Also, in case you’re curous to see what it turned out like, here it is. I had to finish it with Mastercam surface routines though but the roughing (done with my program) was pretty good especially after filtering.

Tags: , ,
| Posted in Design, Machining, Programming | Comments Off on Bas relief style CNC milling with an STL
04.10.25

Spline Cutting Macro (HAAS Macro)

Wrote a quick spline cutting routine for cutting splines for the side. Supports starting at a given tooth, roughing depth, taper adjustment and finish depths. Can come from -/+Y and go any direction on X. Nothing too spectacular but I leave it here for fun.

%
O2023 (SPLINE CUTTING PROGRAM)
(STEVEN MACKAAY 2025)
(!!!USE AT YOUR OWN RISK!!!)

(SETUP VARIABLES)
#100 = 38.   (TOTAL NUMBER OF TEETH/SPLINES)
#101 = -.5   (START X POSITION)
#102 = 4.13   (END X POSITION)
#103 = -2.55   (CLEARANCE Y POSITION - ABSOLUTE DISTANCE FROM Y0)
#104 = -2.35  (START Y POSITION - ABSOLUTE DISTANCE FROM Y0)
#105 = -2.2323   (FINISH Y POSITION - ABSOLUTE DISTANCE FROM Y0)
#106 = 0.02  (Y STEPDOWN AMOUNT - POSITIVE VALUE)
#107 = 0.001  (Y FINISH PASS AMOUNT - POSITIVE VALUE)
#108 = 1.25 (CUTTER RADIUS - POSITIVE VALUE)
#109 = 0.002 (TAPER AMOUNT Y PER UNIT X - POSITIVE TAPER WIDENS AWAY FROM X AXIS AT ENDX)
#110 = 15.  (CUTTING FEEDRATE F)
#111 = 5.0   (PLUNGE FEEDRATE F)
#112 = 50.0  (CLEARANCE FEEDRATE F)
#113 = 460  (SPINDLE SPEED S)
#114 = 1.0   (START TOOTH NUMBER 1 TO #100)
#115 = 4.0   (SPINDLE DIRECTION 3 FOR M03 CW, 4 FOR M04 CCW)

(CALCULATED VARIABLES)
#120 = 0 (CURRENT TOOTH NUMBER)
#121 = 0 (ANGLE FOR CURRENT TOOTH)
#122 = 0 (CURRENT Y POSITION FOR STEPDOWN)
#123 = 0 (CUTTER COMPENSATION - WILL BE POSITIVE OR NEGATIVE BASED ON Y SIGN)
#124 = 0 (DIRECTION FLAG - 1 FOR POSITIVE Y, -1 FOR NEGATIVE Y)
#125 = 0 (TOTAL NUMBER OF STEPDOWNS)
#126 = 0 (CURRENT STEPDOWN NUMBER)
#127 = 0 (STEP DIRECTION - SIGN OF STEP TOWARDS Y0)
#128 = 0 (FINISH PASS Y POSITION)
#130 = 0 (REMAINING DISTANCE AFTER FULL STEPDOWNS)

(DETERMINE APPROACH DIRECTION)
IF [[#103 LT 0]] THEN #124 = -1 (APPROACHING FROM NEGATIVE Y)
IF [[#103 GT 0]] THEN #124 = 1 (APPROACHING FROM POSITIVE Y)

(DETERMINE STEP DIRECTION - TOWARDS Y0)
#127 = -#124 (STEP DIRECTION TOWARDS Y0)

(CALCULATE CUTTER COMPENSATION)
IF [[#124 LT 0]] THEN #123 = -#108 (NEGATIVE Y - SUBTRACT CUTTER RADIUS)
IF [[#124 GT 0]] THEN #123 = #108 (POSITIVE Y - ADD CUTTER RADIUS)

(CALCULATE FINISH PASS Y POSITION)
#128 = #105 - #127 * #107 (POSITION BEFORE FINISH PASS)

(CALCULATE NUMBER OF STEPDOWNS)
#125 = FIX[ABS[#104 - #128] / #106] (NUMBER OF FULL STEPDOWNS)
#130 = ABS[#104 - #128] - [#125 * #106] (REMAINING DISTANCE AFTER FULL STEPDOWNS)
IF [[#130 GT 0.0001]] THEN #125 = #125 + 1 (ADD ONE MORE STEPDOWN IF NEEDED)

G90 (ABSOLUTE POSITIONING)
G17 (XY PLANE SELECTION)
G20 (INCH PROGRAMMING)
G40 (CANCEL CUTTER COMPENSATION)
G49 (CANCEL TOOL LENGTH COMPENSATION)
G80 (CANCEL CANNED CYCLES)
T15 M6 (SELECT TOOL 15)
G43 H15 (APPLY TOOL LENGTH COMPENSATION)
S#113 (SET SPINDLE SPEED)

(MAIN LOOP FOR EACH TOOTH)
#120 = #114 (START AT SPECIFIED TOOTH)

M08 (COOLANT ON)

WHILE [[#120 LE #100]] DO1
  (CALCULATE TOOTH ANGLE)
  #121 = 360.0 * [#120 - 1] / #100 (ANGLE FOR CURRENT TOOTH)
  
  (POSITION TO TOOTH START)
  G0 A#121 (ROTATE TO TOOTH ANGLE)
  G0 X#101 Y[#103 + #123] (RAPID TO START POSITION WITH CLEARANCE Y AND CUTTER COMPENSATION)
  
  (START SPINDLE AND COOLANT)
  IF [[#115 EQ 3]] THEN M03
  IF [[#115 EQ 4]] THEN M04

  
  (RAPID TO Z0)
  G0 Z0
  
  (MOVE TO START Y POSITION)
  G1 Y[#104 + #123] F#112 (FEED TO START Y WITH CLEARANCE FEED AND CUTTER COMPENSATION)
  
  (STEPDOWN LOOP)
  #126 = 1 (INITIALIZE STEPDOWN COUNTER)
  
  WHILE [[#126 LE #125]] DO2
    (CALCULATE CURRENT Y STEPDOWN)
    IF [[#126 LT #125]] THEN #122 = #104 + #127 * #106 * #126 (INCREMENTAL STEPDOWNS TOWARDS Y0)
    IF [[#126 EQ #125]] THEN #122 = #128 (LAST STEPDOWN BEFORE FINISH PASS)
    
    (PLUNGE TO STEPDOWN)
    G1 Y[#122 + #123] F#111 (PLUNGE TO CURRENT DEPTH WITH PLUNGE FEED AND CUTTER COMPENSATION)
    
    (CUT TO END X WITH TAPER)
    G1 X#102 Y[#122 + #123 + [#109 * #124]] F#110 (CUT TO END X WITH TAPER ADJUSTMENT)
    
    (RETRACT TO CLEARANCE)
    G0 Y[#103 + #123] (RAPID BACK TO CLEARANCE Y WITH CUTTER COMPENSATION)
    
    (RETURN TO START X)
    G0 X#101 (RAPID BACK TO START X)
    
    (INCREMENT STEPDOWN COUNTER)
    #126 = #126 + 1
  END2
  
  (FINISH PASS)
  G0 Y[#103 + #123] (RAPID TO CLEARANCE Y)
  G0 X#101 (RAPID TO START X)
  G1 Y[#105 + #123] F#111 (PLUNGE TO FINISH Y WITH PLUNGE FEED)
  G1 X#102 Y[#105 + #123 + [#109 * #124]] F#110 (CUT FINISH PASS TO END X WITH TAPER)
  G0 Y[#103 + #123] (RAPID BACK TO CLEARANCE Y)
  
  (INCREMENT TOOTH COUNTER)
  #120 = #120 + 1
END1

(PROGRAM END)
M5 (SPINDLE STOP)
M9 (COOLANT OFF)
G28 G91 Z0 (RETURN TO Z HOME)
G28 G91 Y0 (RETURN TO Y HOME)
M30 (END PROGRAM)
%

Tags: , , , ,
| Posted in Machining, Programming | Comments Off on Spline Cutting Macro (HAAS Macro)
04.1.25

NCComm – An RS232 device for CNC communication

So, I wanted to try my hand at making a communication device between a standalone device and the CNC, something I wanted. I achieved that. I wanted it made out of the most common parts an average person can easily buy off Aliexpress. You can buy one for like 200 bucks but it was fun to make nonetheless. Things you need for this build:

  • Arduino Mega 2560 or 1280 (I used this because it had enough IO, you could use a nano or a standard arduino but you’d wanna use a different keypad)
  • A membrane keypad (This one is a 4×5 keypad, so 9 IO are needed)
  • An SD card module that accepts 5v. (I used the large style, it is more hardy in an industrial environment.)
  • A 2004 LCD module with an I2C backpack (You can use a standard LCD without a backpack but you’ll want to change the way the output is used, I made a wrapper for that.)
  • An RS232 module. (This one should be with RTS and CTS, most CNC’s I know of need handshaking or just a CTS signal unless you tie the RTS CTS lines together on the cable)
  • Not necessary but I used a hat for the 2560 just to simplify the connections to the board to use standard headers.

Simple, straightforward. Here is the code I have thus far. It’s not too solid and handshaking isn;t working perfect but it works thus far.

Here: https://www.smackaay.com/wp-content/uploads/2025/04/NCComm.zip

Enjoy

Tags: , , , ,
| Posted in Electronics, Machining, Programming | Comments Off on NCComm – An RS232 device for CNC communication
03.16.25

DooM WAD Level Selector

I was going through old CDs and came across some doom wads, levels for the old DooM. So I fired up GZdoom and tried a few but it got kind of tiring going through command line so I had AI (mostly) write a WAD file loader. The result is the aptly named Doom PWAD Manager. It can load pwads and display a preview and give you info.

You can define multiple engines and multiple directories. You can have directory specific command line terms or based on the engine. The WAD files MUST be unzipped into subdirectories in whatever directories you choose, it will not read from a zip.

Below is the download, I took the liberty of making it an EXE if you like, source is there too though.

https://www.smackaay.com/wp-content/uploads/2025/03/PWADManager.zip

enjoy.

Tags: , , ,
| Posted in Miscellaneous stuff, Programming | Comments Off on DooM WAD Level Selector
09.15.24

Ripping an image based PDF to text (and old TurboBASIC commands)

Recently, I wanted to pull text from a PDF that was scanned in from an old manual, namely the Borland Turbo Basic manual. The text that was in the document was garbage, nothing there to be done, so I decided to write something that would allow me to:

1. Load a PDF
2. Rip the images from the PDF
3. OCR the images to generate the text

So, here it is.

import fitz  # PyMuPDF
from PIL import Image
import pytesseract
import io

# Set the path to the Tesseract executable
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

def pdf_to_text(pdf_path: str):
    # Open the PDF file
    pdf_document = fitz.open(pdf_path)
    text_output = []

    # Iterate through each page in the PDF
    for page_num in range(pdf_document.page_count):
        print(f"Processing page {page_num + 1} of {pdf_document.page_count}...")
        
        # Get the page object
        page = pdf_document.load_page(page_num)
        
        # Convert the page to a pixmap (image)
        pix = page.get_pixmap()
        
        # Convert the pixmap into a PIL image
        img = Image.open(io.BytesIO(pix.tobytes("png")))
        
        # Run OCR on the image using pytesseract
        page_text = pytesseract.image_to_string(img)

        # Append the extracted text to the list
        text_output.append(f"Page {page_num + 1}:\n{page_text}\n")

    # Close the document after processing
    pdf_document.close()

    # Join all pages' text into a single string
    return "\n".join(text_output)

if __name__ == "__main__":
    pdf_path = "BorTurboBasic.pdf"  # Replace with the path to your PDF file
    extracted_text = pdf_to_text(pdf_path)

    # Save the text to a file
    with open("output_text.txt", "w", encoding="utf-8") as f:
        f.write(extracted_text)

    print("OCR complete. Text extracted and saved to 'output_text.txt'.")

You need to install Tesseract in order to rip the text and put the location into the pytesseract.pytesseract.tesseract_cmd variable. Also, it’s not perfect and it’s not great but it’s better than nothing. If the text is super garbled then it works to simply dump the text into something like Google Gemini and have it rip info you need from it.

Speaking of which, here is a cheat-sheet formatted and parsed from an old Turbo Basic manual.

Turbo Basic Commands (smackaay.com)

Enjoy!

Tags: , , , , , , , , , , , , , ,
| Posted in Programming | Comments Off on Ripping an image based PDF to text (and old TurboBASIC commands)
08.25.24

New prompt permutation script

A while back I made a prompt permutation script for generating large numbers of image prompts for use in automatic1111. I updated it with a new operator, the incremental operator ‘&’ so it will cycle through the list items instead of choosing random ones. Here is a sample prompt and output. Basically a fancy search and replace but I use it quite often.

photo, a %SIZE brutalist &BUILDING on a sunny day (this is the base prompt)
photo, painting
brutalist, post-modern, deconstructivism
sunny day, night time
%SIZE, small, medium, large, huge
&BUILDING, house, tower, factory, school
Output:
photo, a small brutalist house on a sunny day
photo, a huge brutalist tower on a night time
photo, a medium post-modern factory on a sunny day
photo, a medium post-modern school on a night time
photo, a medium deconstructivism house on a sunny day
photo, a large deconstructivism tower on a night time
painting, a medium brutalist factory on a sunny day
painting, a small brutalist school on a night time
painting, a huge post-modern house on a sunny day
painting, a small post-modern tower on a night time
painting, a large deconstructivism factory on a sunny day
painting, a medium deconstructivism school on a night time

Anyways, here is the python script along with an html version so you can use it with an interface of sorts.

http://smackaay.com/files/ppermute/ppermute.html The little webpage for it.

Here is the python script.

import itertools
import random

# File path assignments
INPUT_FILE_PATH = 'img5.txt'  # Change this to the path of your input file
OUTPUT_FILE_PATH = 'output.txt'  # Change this to the desired path for the output file

def load_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    return [line.strip() for line in lines]

def generate_permutations(prompt, modifiers, random_modifiers, increment_modifiers):
    all_combinations = list(itertools.product(*modifiers))
    
    permutations = []
    increment_counters = {key: 0 for key in increment_modifiers.keys()}
    
    for combination in all_combinations:
        new_prompt = prompt
        for original, replacement in zip(modifiers, combination):
            new_prompt = replace_first(new_prompt, original[0], replacement)
        
        # Handle random modifiers
        for placeholder, values in random_modifiers.items():
            if placeholder in new_prompt:
                replacement = random.choice(values)
                new_prompt = new_prompt.replace(placeholder, replacement, 1)
        
        # Handle increment modifiers
        for placeholder, values in increment_modifiers.items():
            if placeholder in new_prompt:
                replacement = values[increment_counters[placeholder] % len(values)]
                new_prompt = new_prompt.replace(placeholder, replacement, 1)
                increment_counters[placeholder] += 1
        
        # Remove placeholders from the final prompt
        new_prompt = remove_placeholders(new_prompt, random_modifiers.keys() | increment_modifiers.keys())
        
        permutations.append(new_prompt)
    
    return permutations

def replace_first(text, search, replacement):
    if search not in text:
        raise ValueError(f"Term '{search}' not found in the prompt.")
    return text.replace(search, replacement, 1)

def remove_placeholders(text, placeholders):
    for placeholder in placeholders:
        text = text.replace(placeholder, "")
    return text

def save_to_file(output_path, permutations):
    with open(output_path, 'w') as file:
        for permutation in permutations:
            file.write(permutation + '\n')

def main():
    lines = load_file(INPUT_FILE_PATH)
    if not lines:
        print("The input file is empty.")
        return

    prompt = lines[0]
    modifiers = [line.split(', ') for line in lines[1:] if not line.startswith('%') and not line.startswith('&')]
    random_modifiers = {}
    increment_modifiers = {}
    
    for line in lines[1:]:
        if line.startswith('%'):
            parts = line.split(', ')
            key = parts[0]
            values = parts[1:]
            random_modifiers[key] = values
        elif line.startswith('&'):
            parts = line.split(', ')
            key = parts[0]
            values = parts[1:]
            increment_modifiers[key] = values

    try:
        all_permutations = generate_permutations(prompt, modifiers, random_modifiers, increment_modifiers)
        save_to_file(OUTPUT_FILE_PATH, all_permutations)
        print(f"Generated prompts have been saved to {OUTPUT_FILE_PATH}")
        print(f"Total number of permutations: {len(all_permutations)}")
    except ValueError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()

Tags: , , , ,
| Posted in Personal stuff, Programming | Comments Off on New prompt permutation script
08.14.24

Gage Block Buildup Calculator

So, I have that Python source code on the side of my site there for calculating gage block buildups. I figured it was time to turn it into a JS program so that people can just access it from the web. Not super complicated but useful nonetheless.

http://smackaay.com/files/gbcalc/gbcalc.html

Features as follows:

  • Imperial 81, 28, 34, 36 and 92 pc sets
  • Metric 88, 47 and 112 pc sets
  • Multiple ( as many as you want) results
  • The ability to remove blocks from the list if they are either missing or used in a previous buildup. This is handy.

Anyways, hope somebody out there enjoys this!

Tags: , ,
| Posted in Machining, Programming | Comments Off on Gage Block Buildup Calculator
08.5.24

The YouTube Recycle Bin

I was watching a video from a youtuber KVN AUST. The video: https://youtu.be/8uHFm6LK6PE?si=SLIaCEzNBx_iL97V It featured a map for looking at and searching for odd videos across YouTube. It’s pretty fun just to see little slices of life or weird things people would bother uploading so I made a little JS proggy to generate the most common search terms.

Select the prefix and the type of random term you want to find, click on Generate Search Term and then click Search on YouTube. You can select No Spaces or With Quotes if certain things don’t work. The random date is anything in the last 20 years. Enjoy!

YouTube Recycle Bin Search Generator




Tags: , ,
| Posted in Personal stuff, Programming | Comments Off on The YouTube Recycle Bin
07.10.24

A visit from an old friend, the boreGauge

A few years back we made a gauge for measuring large bores in hydraulic cylinders. Seems the company that bought it from us needed the software for it again. I had to dig through my old source code and see if I had a recent version, turns out I did. On this project I did the electronics, software and commissioned it.

An image of the BoreGauge

What the device does is, you place it in the bore, set your zeros and then measure the bore all the way down. This way you can see if there are any high spots, low spots or waviness. The software keeps track of the position as well and provides a csv file of the data and plots it on the screen.

This was a pretty fun project, I might redesign it and make a more substantial attempt at monetizing it later.

| Posted in Design, Electronics, Programming | Comments Off on A visit from an old friend, the boreGauge
05.28.24

StableDiffusion Permutation Script Update

So, like a week ago I wrote a script to make permutations for SD prompts. I’ve updated the script to allow for random terms as well. This allows one to add variance in the prompt but to not add to the number of permutations. Everything is explained in the code block comment. just change the filenames near the end of the script and run.

"""
Script: prompt_permutator.py

Description:
This script generates permutations of a given prompt with various modifiers.
The script reads an input file that contains a base prompt and lists of modifiers.
It creates all possible combinations of the modifiers and generates new prompts
based on these combinations. Additionally, it handles placeholders that are randomly
replaced with specified values and ensures these placeholders are not included in the final output.

Input File Format:
- The first line contains the base prompt.
- Subsequent lines contain comma-separated lists of modifiers.
- Lines starting with a placeholder (e.g., %1) are treated as random modifiers and are replaced
  with random values from the list provided.

Example Input File (test2.txt):

A %1 flower on a hill, photorealistic
on a hill, in a vase, on a bed
photorealistic, manga
%1, Red, Green, Blue

In this example:
- The base prompt is: "A %1 flower on a hill, photorealistic"
- The modifiers are: ["on a hill", "in a vase", "on a bed"] and ["photorealistic", "manga"]
- The placeholder %1 will be replaced with a random choice from ["Red", "Green", "Blue"]

Output:
The script generates all permutations of the prompt with the modifiers and replaces
the placeholder with a random value. The results are saved to an output file.

Usage:
1. Prepare an input file (e.g., 'test2.txt') following the described format.
2. Specify the input and output file paths in the script or pass them as arguments.
3. Run the script to generate the permutations and save them to the output file.

Example Execution:
$ python prompt_permutator.py

Dependencies:
- itertools
- random

Author:
Steven M

Date:
May 28, 2024

"""

import itertools
import random

def load_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    return [line.strip() for line in lines]

def generate_permutations(prompt, modifiers, random_modifiers):
    # Create all combinations of modifiers
    all_combinations = list(itertools.product(*modifiers))
    
    permutations = []
    for combination in all_combinations:
        new_prompt = prompt
        for original, replacement in zip(modifiers, combination):
            new_prompt = replace_first(new_prompt, original[0], replacement)
        
        # Handle random modifiers
        for placeholder, values in random_modifiers.items():
            if placeholder in new_prompt:
                replacement = random.choice(values)
                new_prompt = new_prompt.replace(placeholder, replacement, 1)
        
        # Remove placeholders from the final prompt
        new_prompt = remove_placeholders(new_prompt, random_modifiers.keys())
        
        permutations.append(new_prompt)
    
    return permutations

def replace_first(text, search, replacement):
    # Helper function to replace only the first occurrence of a term
    if search not in text:
        raise ValueError(f"Term '{search}' not found in the prompt.")
    return text.replace(search, replacement, 1)

def remove_placeholders(text, placeholders):
    for placeholder in placeholders:
        text = text.replace(placeholder, "")
    return text

def save_to_file(output_path, permutations):
    with open(output_path, 'w') as file:
        for permutation in permutations:
            file.write(permutation + '\n')

def main(input_file_path, output_file_path):
    lines = load_file(input_file_path)
    if not lines:
        print("The input file is empty.")
        return

    prompt = lines[0]
    modifiers = [line.split(', ') for line in lines[1:] if not line.startswith('%')]
    random_modifiers = {}
    
    for line in lines[1:]:
        if line.startswith('%'):
            parts = line.split(', ')
            key = parts[0]
            values = parts[1:]
            random_modifiers[key] = values

    try:
        all_permutations = generate_permutations(prompt, modifiers, random_modifiers)
        save_to_file(output_file_path, all_permutations)
        print(f"Generated prompts have been saved to {output_file_path}")
        print(f"Total number of permutations: {len(all_permutations)}")
    except ValueError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    input_file_path = 'test2.txt'  # Change this to the path of your input file
    output_file_path = 'output.txt'  # Change this to the desired path for the output file
    main(input_file_path, output_file_path)

So, in essence, anything with a % at the beginning of the line will be processed differently and the term will be matched up. As always, I guarantee nothing.

Tags: , , ,
| Posted in Programming | Comments Off on StableDiffusion Permutation Script Update
05.28.24

Pong-2024

I was bored and made a quick Pong game. It’s not great, not terribly well finished but I wanted to see how good the tools are these days. It’s been a while since I wrote a game. It was fun to make. Give it a shot.

https://smackaay.com/webgames/pong2024/index.html

It’s output in HTML5 so no installation is required.

Tags: , , ,
| Posted in Programming | Comments Off on Pong-2024
05.23.24

Resolutions for SD image generation

When making images for StableDiffusion it’s best to take the aspect ratio in mind and make it fit into the total number of pixels that the model was trained on. This results in the best images for that given model. So, for SDXL it’s 1024×1024, others it may be 768×768 or even 512×512. Here is a list of effective X and Y values to total up to the most common aspect ratios for various training sizes. Obviously you would reverse the values if you go y/x.

1024x1024

Aspect Ratio 4:3 - Resolution: 1182x886
Aspect Ratio 16:9 - Resolution: 1365x768
Aspect Ratio 21:9 - Resolution: 1564x670
Aspect Ratio 1:1 - Resolution: 1024x1024
Aspect Ratio 3:2 - Resolution: 1254x836
Aspect Ratio 5:4 - Resolution: 1144x915
Aspect Ratio 16:10 - Resolution: 1295x809
Aspect Ratio 2:1 - Resolution: 1448x724
Aspect Ratio 18:9 - Resolution: 1448x724
Aspect Ratio 32:9 - Resolution: 1930x543
Aspect Ratio 3:1 - Resolution: 1773x591
Aspect Ratio 4:1 - Resolution: 2048x512
Aspect Ratio 5:3 - Resolution: 1321x793

768x768

Aspect Ratio 4:3 - Resolution: 886x665
Aspect Ratio 16:9 - Resolution: 1024x576
Aspect Ratio 21:9 - Resolution: 1173x502
Aspect Ratio 1:1 - Resolution: 768x768
Aspect Ratio 3:2 - Resolution: 940x627
Aspect Ratio 5:4 - Resolution: 858x686
Aspect Ratio 16:10 - Resolution: 971x607
Aspect Ratio 2:1 - Resolution: 1086x543
Aspect Ratio 18:9 - Resolution: 1086x543
Aspect Ratio 32:9 - Resolution: 1448x407
Aspect Ratio 3:1 - Resolution: 1330x443
Aspect Ratio 4:1 - Resolution: 1536x384
Aspect Ratio 5:3 - Resolution: 991x594

512x512

Aspect Ratio 4:3 - Resolution: 591x443
Aspect Ratio 16:9 - Resolution: 682x384
Aspect Ratio 21:9 - Resolution: 782x335
Aspect Ratio 1:1 - Resolution: 512x512
Aspect Ratio 3:2 - Resolution: 627x418
Aspect Ratio 5:4 - Resolution: 572x457
Aspect Ratio 16:10 - Resolution: 647x404
Aspect Ratio 2:1 - Resolution: 724x362
Aspect Ratio 18:9 - Resolution: 724x362
Aspect Ratio 32:9 - Resolution: 965x271
Aspect Ratio 3:1 - Resolution: 886x295
Aspect Ratio 4:1 - Resolution: 1024x256
Aspect Ratio 5:3 - Resolution: 660x396

So, if for some reason you need to calculate this on your own for some future or past resolution, here is the Python.

from sympy import symbols, Eq, solve

# Define symbols
x, y = symbols('x y')

# Equation 1: Total pixel count remains constant
total_pixels = 512*512

# List of common aspect ratios as tuples (width, height)
aspect_ratios = [
    (4, 3), (16, 9), (21, 9), (1, 1), (3, 2),
    (5, 4), (16, 10), (2, 1), (18, 9), (32, 9),
    (3, 1), (4, 1), (5, 3)
]

# Iterate over the aspect ratios and solve the equations
resolutions = []
for width_ratio, height_ratio in aspect_ratios:
    # Equation 2: Aspect ratio
    eq1 = Eq(x * y, total_pixels)
    eq2 = Eq(x / y, width_ratio / height_ratio)
    
    # Solve the equations
    solution = solve((eq1, eq2), (x, y))
    
    # Extract the resolution and convert to positive integers
    resolution = (abs(int(solution[0][0])), abs(int(solution[0][1])))
    resolutions.append((width_ratio, height_ratio, resolution))

# Print the results
for width_ratio, height_ratio, resolution in resolutions:
    print(f"Aspect Ratio {width_ratio}:{height_ratio} - Resolution: {resolution[0]}x{resolution[1]}")

As always, I guarantee nothing. enjoy.

Tags: , , ,
| Posted in Miscellaneous stuff, Programming | Comments Off on Resolutions for SD image generation
05.20.24

A quick script for creating prompt permutations for StableDiffusion

So, I enjoy making stuff in StableDiffusion and in the WebUI interface is an option for a prompt list. I like using the Prompt S/R in the scripts but it tends to top out after about 1500 permutations. Here is a script for generating those in as many dimensions as you want.

import itertools

def load_file(file_path):
    with open(file_path, 'r') as file:
        lines = file.readlines()
    return [line.strip() for line in lines]

def generate_permutations(prompt, modifiers):
    # Create all combinations of modifiers
    all_combinations = list(itertools.product(*modifiers))
    
    permutations = []
    for combination in all_combinations:
        new_prompt = prompt
        for original, replacement in zip(modifiers, combination):
            new_prompt = replace_first(new_prompt, original[0], replacement)
        permutations.append(new_prompt)
    
    return permutations

def replace_first(text, search, replacement):
    # Helper function to replace only the first occurrence of a term
    if search not in text:
        raise ValueError(f"Term '{search}' not found in the prompt.")
    return text.replace(search, replacement, 1)

def save_to_file(output_path, permutations):
    with open(output_path, 'w') as file:
        for permutation in permutations:
            file.write(permutation + '\n')

def main(input_file_path, output_file_path):
    lines = load_file(input_file_path)
    if not lines:
        print("The input file is empty.")
        return

    prompt = lines[0]
    modifiers = [line.split(', ') for line in lines[1:]]

    try:
        all_permutations = generate_permutations(prompt, modifiers)
        save_to_file(output_file_path, all_permutations)
        print(f"Generated prompts have been saved to {output_file_path}")
        print(f"Total number of permutations: {len(all_permutations)}")
    except ValueError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    input_file_path = 'testprompt.txt'  # Change this to the path of your input file
    output_file_path = 'output.txt'  # Change this to the desired path for the output file
    main(input_file_path, output_file_path)

This Python script will allow you to generate a permutation of a prompt based on the original words that are in the prompt. So, for example, here is the prompt file with the first line being the raw prompt and the following lines being the search and replace terms:

A beautiful flower sitting on a wooden table. in a kitchen
beautiful flower, tall woman, eager beaver, soup can
sitting, jumping, glowering

This will generate the following in output.txt

A beautiful flower sitting on a wooden table. in a kitchen
A beautiful flower jumping on a wooden table. in a kitchen
A beautiful flower glowering on a wooden table. in a kitchen
A tall woman sitting on a wooden table. in a kitchen
A tall woman jumping on a wooden table. in a kitchen
A tall woman glowering on a wooden table. in a kitchen
A eager beaver sitting on a wooden table. in a kitchen
A eager beaver jumping on a wooden table. in a kitchen
A eager beaver glowering on a wooden table. in a kitchen
A soup can sitting on a wooden table. in a kitchen
A soup can jumping on a wooden table. in a kitchen
A soup can glowering on a wooden table. in a kitchen

So, the strings “Beautiful flower” and “sitting” and the following terms are replaced in the original prompt. So, in this case you have 4 terms in the first term line (line 2) and 3 terms in the second term line so, 4 time 3 is 12. All 12 permutations of that prompt are outputted to a file. It will tell you the total number of permutations and throw an error when a term isn’t found.

I make no guarantees about this script, enjoy anyways.

| Posted in Programming | Comments Off on A quick script for creating prompt permutations for StableDiffusion