Welcome to Inkbunny...
Allowed ratings
To view member-only content, create an account. ( Hide )
MisterFluffums

Tools used for Cubsitting Simulator

Just fair warning: better models than I'm using most probably exist. Development of these tools goes so fast, anything I get used to will be obsolete. I'm mainly listing the tools in case anyone's curious.

And also fair warning: Developing is absolutely tedious and an incredible time sink. I looked it up, and I think I generated close to 10K images for Cubsitting Simulator, of which 1070 ended up actually being used, with about 500 making it into the final game (the other 570 were used as intermediates to generate other pictures).

It's a ton of fun though! I can highly recommend it to give it a go.

To correct minor mistakes, remove background, and other image manipulation, I'm using:
https://krita.org/en/

In the Cubsitting Simulator folder, I have three folders: 'reference images~' for the original generated PNGs (every PNG contains metadata about the prompt used which is seriously convenient), 'krita~' for the edited files with for example their backgrounds removed and  'images' for images that'll ship with the game and that I compressed to .avif at quality 60 from the 'krita~' files. I've added a '~' at the end because Ren'Py will know not to pack those folders ending with ~.

To create visual novels with Ren'Py:
https://www.renpy.org/latest.html

For version control
I can't recommend git more. It's super convenient to have backups, to cooperate with others, and to be able to revert changes if they cause problems.
https://gitforwindows.org/

To run a model locally:
https://github.com/lllyasviel/stable-diffusion-webui-forge

You can download models from CivitAI for free. I'm using the following model as a base model:
https://civitai.com/models/503815?modelVersionId=1760102

For metadata extraction of the images, I just put them all in a directory then run the following Java code:

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class Analyzer {

    public final static String START_DIR = "START_DIR_METADATA_EXTRACTION";
    private final PrintWriter fos;
    private final File metadatadir;

    private final Pattern pattern = Pattern.compile(
            "tEXtparameters\0(.*)\n(?:Negative prompt: (.*)\n)?Steps: (\\d+), Sampler: (.*), CFG scale: (\\d+), Seed: (\\d+), Size: (\\d+)x(\\d+), Model hash: (.*), Model: ([A-Za-z0-9$&+,:;=?@#|'<>.-^*()%!_ \"]*\\d)");
    private final Map<String, Set<Image>> map = new TreeMap<>();

    public Analyzer(String root) throws FileNotFoundException {
        fos = new PrintWriter(new FileOutputStream(new File(root,"generated_prompts.txt")));
        metadatadir = new File("metadata");
        if (!metadatadir.exists()) {
            metadatadir.mkdir();
        }
    }

    public static void main(String[] args) throws IOException {
        Preferences prefs = Preferences.userRoot().node(Analyzer.class.getName());
        JFileChooser chooser = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter(
                "PNG Images", "png");
        chooser.setFileFilter(filter);
        chooser.setAcceptAllFileFilterUsed(true);
        chooser.setCurrentDirectory(new File(prefs.get(START_DIR, ".")));
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        int returnVal = chooser.showOpenDialog(null);
        if (returnVal != JFileChooser.APPROVE_OPTION) {
            System.out.println("You cancelled the metadata extraction.");
            return;
        }
        File selected = chooser.getSelectedFile();
        prefs.put(START_DIR, selected.isFile() ? selected.getParent() : selected.getAbsolutePath());
        Analyzer analyzer = new Analyzer(selected.isFile() ? selected.getParent() : selected.getAbsolutePath());
        analyzer.analyze(selected);
        analyzer.printAll();
    }

    public void analyze(File start) {
        if (start.isDirectory()) {
            for (File f : start.listFiles()) {
                analyze(f);
            }
            return;
        }
        if (start.getName().toLowerCase().endsWith(".png")) {
            analyzePNG(start);
        }
    }

    private void analyzePNG(File f) {
        try {
            String readString = new String(Files.readAllBytes(f.toPath()));
            Matcher matches = pattern.matcher(readString);
            if (!matches.find()) {
                return;
            }
            Image img = new Image(f.getAbsolutePath(),
                    f.getName(),
                    matches.group(1),
                    matches.group(2),
                    matches.group(3),
                    matches.group(4),
                    matches.group(5),
                    matches.group(6),
                    matches.group(7),
                    matches.group(8),
                    matches.group(9),
                    matches.group(10));
            String key = img.model() + img.sampler() + img.steps() + img.cfgScale();
            Set<Image> groupedByModelHash = map.get(key);
            if (groupedByModelHash == null) {
                groupedByModelHash = new TreeSet<>();
                map.put(key, groupedByModelHash);
            }
            groupedByModelHash.add(img);
        } catch (IOException ex) {
            Logger.getLogger(Analyzer.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void printAll() throws FileNotFoundException {
        map.values().forEach(this::printSet);
        fos.close();
    }

    private void printSet(Set<Image> images) {
        Image first = images.stream().findAny().get();
        fos.print("\n----");
        fos.print(first.model());
        fos.print(", hash: ");
        fos.print(first.modelHash());
        fos.print(", sampler: ");
        fos.print(first.sampler());
        fos.print(", steps: ");
        fos.print(first.steps());
        fos.print(", cfgscale: ");
        fos.print(first.cfgScale());
        fos.println("----");
        images.forEach(this::printImage);
    }

    private void printImage(Image img) {
        String metadatatext = String.format("%s: %sx%s, seed %s, positive prompt '%s",
                img.imageName(), img.width(),
                img.height(), img.seed(),
                img.positivePrompt());
        if (img.negativePrompt() != null) {
            metadatatext += ", negative prompt '" + img.negativePrompt();
        }
        metadatatext += '\'';
        fos.print(metadatatext);
        fos.println();
        fos.flush();
        File metadata = new File(metadatadir, img.imageName + ".txt");
        if (metadata.exists()) {
            return;
        }
        try ( PrintWriter metadataWriter = new PrintWriter(metadata)) {
            metadataWriter.print(String.format("%s: model %s, hash %s, sampler %s, steps %s, cfgscale %s, %sx%s, seed %s, positive prompt '%s",
                    img.imageName(), img.model(), img.modelHash,
                    img.sampler(), img.steps(),
                    img.cfgScale(), img.width(),
                    img.height(), img.seed(),
                    img.positivePrompt()));
            metadataWriter.flush();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Analyzer.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private static record Image(String imageFullPath, String imageName, String positivePrompt, String negativePrompt, String steps,
            String sampler, String cfgScale, String seed, String width, String height,
            String modelHash, String model) implements Comparable {

        [at_iconname]Override[/at_iconname]
        public int compareTo(Object o) {
            if (!(o instanceof Image img)) {
                return -1;
            }
            return imageName.compareTo(img.imageName);
        }

    }

}
Viewed: 161 times
Added: 1 week, 3 days ago
 
New Comment:
Move reply box to top
Log in or create an account to comment.