diff --git a/README.md b/README.md index e43fba6..33454fd 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ This an extension for [stable-diffusion-webui](https://github.com/AUTOMATIC1111/ Randomly display pictures of the artist's or artistic genre's typical style. More pictures for this artist or genre are shown after selecting, So you don't have to worry about how hard it is to choose the right art style when you create. - + You can filter artists or genre's by keyword, collect the style to favorites, or shield the style you don't like. You can also send the style name to the txt2img or img2img prompt. All the pictures are created by a script named "Create inspiration images" in the txt2img page and the artist's and genre's names come from the following repository: https://github.com/pharmapsychotic/clip-interrogator/tree/main/data @@ -22,3 +22,15 @@ You can also download generated pictures from the following: https://huggingface - Restart webui and experience the joy of creation! [Look here for more install details](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Extensions) + +## Using txt2img inspiration +- Open the txt2img tab +- At the bottom of Script select "Create inspiration images" +- Upload a text file (e.g. the artist.csv from the root directory) + - The header "artist,score,category" is ignored + - If the content is separated by , the first block (everything up to the first ,) is taken + - If the first block is surrounded by ", the enclosed content will be taken +- Choose a placeholder, the default is {inspiration} +- The placeholder can be used at the top of the prompt e.g. "a painting in {inspiration} style" + + diff --git a/etc/inspiration_tab.png b/etc/inspiration_tab.png new file mode 100644 index 0000000..2c46949 Binary files /dev/null and b/etc/inspiration_tab.png differ diff --git a/etc/txt2img_tab.png b/etc/txt2img_tab.png new file mode 100644 index 0000000..e02309e Binary files /dev/null and b/etc/txt2img_tab.png differ diff --git a/scripts/inspiration.py b/scripts/inspiration.py index 518f90f..f985116 100644 --- a/scripts/inspiration.py +++ b/scripts/inspiration.py @@ -1,6 +1,7 @@ import os import random import csv, os, shutil +import re import modules.scripts as scripts from modules import processing, shared, sd_samplers, images from modules.processing import Processed @@ -10,8 +11,11 @@ import modules.ui from modules import shared from modules import script_callbacks import modules.generation_parameters_copypaste + inspiration_dir = os.path.join(scripts.basedir(), "inspiration") inspiration_system_path = os.path.join(inspiration_dir, "system") + + class Script(scripts.Script): def title(self): return "Create inspiration images" @@ -20,57 +24,60 @@ class Script(scripts.Script): return True def ui(self, is_img2img): - file = gr.Files(label="Artist or styles name list. '.txt' files with one name per line",) - with gr.Row(): - prefix = gr.Textbox("a painting in", label="Prompt words before artist or style name", file_count="multiple") - suffix= gr.Textbox("style", label="Prompt words after artist or style name") - negative_prompt = gr.Textbox("picture frame, portrait photo", label="Negative Prompt") - with gr.Row(): - batch_size = gr.Number(1, label="Batch size") - batch_count = gr.Number(2, label="Batch count") - return [batch_size, batch_count, prefix, suffix, negative_prompt, file] + files = gr.Files(label="Artist or styles name list. '.txt' files with one name per line") + prompt_placeholder = gr.Textbox("{inspiration}", label="Prompt Placeholder, which can be used at the top of prompt input") + return [prompt_placeholder, files] - def run(self, p, batch_size, batch_count, prefix, suffix, negative_prompt, files): - p.batch_size = int(batch_size) - p.n_iterint = int(batch_count) - p.negative_prompt = negative_prompt + def run(self, p, prompt_placeholder, files): + if not re.search(prompt_placeholder, p.prompt): + raise Exception("Missing " + prompt_placeholder + " in prompt") + original_prompt = p.prompt p.do_not_save_samples = True - p.do_not_save_grid = True + p.do_not_save_grid = True for file in files: tp = file.orig_name.split(".")[0] - print(tp) + print("Process " + tp + "..") path = os.path.join(inspiration_dir, tp) if not os.path.exists(path): - os.makedirs(path) + os.makedirs(path) f = open(file.name, "r", encoding='utf-8') - line = f.readline() - while len(line) > 0: - name = line.rstrip("\n").split(",")[0] - line = f.readline() - artist_path = os.path.join(path, name) - if not os.path.exists(artist_path): - os.mkdir(artist_path) - if len(os.listdir(artist_path)) >= opts.inspiration_max_samples: + line = f.readline() + while len(line) > 0: + line = line.rstrip("\n") + # Ignore headline of artist.csv from automatic1111 repository + if line == "artist,score,category": + line = f.readline() continue - p.prompt = f"{prefix} {name} {suffix}" + double_quotes_match = re.search('^".+"', line) + if double_quotes_match: + name = double_quotes_match.group()[1:len(double_quotes_match.group()) - 1] + else: + name = line.split(",")[0] + line = f.readline() + artist_path = os.path.join(path, name) + if not os.path.exists(artist_path): + os.mkdir(artist_path) + if len(os.listdir(artist_path)) >= opts.inspiration_max_samples: + print("Limit of " + str(opts.inspiration_max_samples) + " of '" + name + "' in " + tp + " is reached. Limit can be changed in the settings.") + continue + p.prompt = re.sub(prompt_placeholder, name, original_prompt) print(p.prompt) processed = processing.process_images(p) - for img in processed.images: + for img in processed.images: i = 0 - filename = os.path.join(artist_path, format(0, "03d") + ".jpg") + filename = os.path.join(artist_path, format(0, "03d") + ".jpg") while os.path.exists(filename): i += 1 - filename = os.path.join(artist_path, format(i, "03d") + ".jpg") + filename = os.path.join(artist_path, format(i, "03d") + ".jpg") img.save(filename, quality=80) return processed - def read_name_list(file, types=None, keyword=None): if not os.path.exists(file): return [] ret = [] - f = open(file, "r") + f = open(file, "r") line = f.readline() while len(line) > 0: line = line.rstrip("\n") @@ -83,12 +90,14 @@ def read_name_list(file, types=None, keyword=None): line = f.readline() return ret -def save_name_list(file, name): + +def save_name_list(file, name): name_list = read_name_list(file) if name not in name_list: with open(file, "a") as f: f.write(name + "\n") + def get_types_list(): files = os.listdir(inspiration_dir) types = [] @@ -103,6 +112,7 @@ def get_types_list(): types.append(x) return types + def get_inspiration_images(source, types, keyword): keyword = keyword.strip(" ").lower() get_num = int(opts.inspiration_rows_num * opts.inspiration_cols_num) @@ -112,13 +122,13 @@ def get_inspiration_images(source, types, keyword): elif source == "Abandoned": names = read_name_list(os.path.join(inspiration_system_path, "abandoned.txt"), types, keyword) names = random.sample(names, get_num) if len(names) > get_num else names - elif source == "Exclude abandoned": - abandoned = read_name_list(os.path.join(inspiration_system_path, "abandoned.txt"), types, keyword) + elif source == "Exclude abandoned": + abandoned = read_name_list(os.path.join(inspiration_system_path, "abandoned.txt"), types, keyword) all_names = [] for tp in types: name_list = os.listdir(os.path.join(inspiration_dir, tp)) all_names += [os.path.join(tp, x) for x in name_list if keyword in x.lower()] - + if len(all_names) > get_num: names = [] while len(names) < get_num: @@ -137,28 +147,32 @@ def get_inspiration_images(source, types, keyword): for a in names: image_path = os.path.join(inspiration_dir, a) images = os.listdir(image_path) - if len(images) > 0: + if len(images) > 0: image_list.append((os.path.join(image_path, random.choice(images)), a)) else: print(image_path) return image_list, names + def select_click(index, name_list): name = name_list[int(index)] path = os.path.join(inspiration_dir, name) images = os.listdir(path) return name, [os.path.join(path, x) for x in images], "" + def give_up_click(name): file = os.path.join(inspiration_system_path, "abandoned.txt") save_name_list(file, name) return "Added to abandoned list" - + + def collect_click(name): file = os.path.join(inspiration_system_path, "faverites.txt") save_name_list(file, name) return "Added to favorite list" + def moveout_click(name, source): if source == "Abandoned": file = os.path.join(inspiration_system_path, "abandoned.txt") @@ -174,29 +188,34 @@ def moveout_click(name, source): f.write(a + "\n") return f"Moved out {name} from {source} list" + def source_change(source): if source in ["Abandoned", "Favorites"]: return gr.update(visible=True), [] else: return gr.update(visible=False), [] + + def add_to_prompt(name, prompt): name = os.path.basename(name) return prompt + "," + name + def clear_keyword(): return "" -def on_ui_tabs(): + +def on_ui_tabs(): txt2img_prompt = modules.ui.txt2img_paste_fields[0][0] img2img_prompt = modules.ui.img2img_paste_fields[0][0] with gr.Blocks(analytics_enabled=False) as inspiration: - flag = os.path.exists(inspiration_dir) + flag = os.path.exists(inspiration_dir) if flag: types = get_types_list() flag = len(types) > 0 else: os.makedirs(inspiration_dir) - if not flag: + if not flag: gr.HTML("""