Spaces:
Paused
Paused
import gradio as gr | |
import json | |
import os | |
import random | |
from PIL import Image | |
import base64 | |
import io | |
# Define categories at the top so they are accessible throughout the code | |
categories = [ | |
('Sex?', 'sex_tags'), | |
('Setting', 'scene_tags'), | |
('Position (sex)', 'position_tags'), | |
('Position (no sex)', 'positionNo_tags'), | |
('Outfit', 'outfit_tags'), | |
('Accessories', 'accessory_tags'), | |
('Camera View/Angle', 'camera_tags'), | |
('Concept', 'concept_tags'), | |
('Facial Expression', 'facial_expression_tags'), | |
('Action (sex)', 'pose_tags'), | |
('Action (no sex)', 'action_tags'), | |
('Additional', 'additional_tags'), | |
('LORA', 'lora_tags') | |
] | |
class DataManager: | |
def __init__(self, base_dir='/data'): | |
self.base_dir = base_dir | |
# Ensure the base directory exists | |
if not os.path.exists(self.base_dir): | |
os.makedirs(self.base_dir) | |
self.characters_file = os.path.join(self.base_dir, 'characters.json') | |
self.persistent_tags_file = os.path.join(self.base_dir, 'persistent_tags.json') | |
self.category_tags_file = os.path.join(self.base_dir, 'category_tags.json') | |
self.images_folder = os.path.join(self.base_dir, 'character_images') | |
# Ensure the images folder exists | |
if not os.path.exists(self.images_folder): | |
os.makedirs(self.images_folder) | |
self.load_data() | |
def load_data(self): | |
# Load characters | |
if os.path.exists(self.characters_file): | |
with open(self.characters_file, 'r') as f: | |
self.characters = json.load(f) | |
else: | |
self.characters = [] | |
# Load persistent tags | |
if os.path.exists(self.persistent_tags_file): | |
with open(self.persistent_tags_file, 'r') as f: | |
self.persistent_tags = json.load(f) | |
else: | |
self.persistent_tags = [] | |
# Load category tags | |
self.load_category_tags() | |
def save_characters(self): | |
with open(self.characters_file, 'w') as f: | |
json.dump(self.characters, f) | |
def save_persistent_tags(self): | |
with open(self.persistent_tags_file, 'w') as f: | |
json.dump(self.persistent_tags, f) | |
def load_category_tags(self): | |
if os.path.exists(self.category_tags_file): | |
with open(self.category_tags_file, 'r') as f: | |
self.category_tags = json.load(f) | |
else: | |
self.category_tags = {} | |
def save_category_tags(self): | |
with open(self.category_tags_file, 'w') as f: | |
json.dump(self.category_tags, f) | |
def get_category_tags(self, category_var_name): | |
# Return the tags list for the given category variable name | |
return self.category_tags.get(category_var_name, []) | |
def set_category_tags(self, category_var_name, tags_list): | |
self.category_tags[category_var_name] = tags_list | |
self.save_category_tags() | |
def get_characters(self): | |
# Load character images | |
for char in self.characters: | |
image_path = char.get('image_path') | |
if image_path and os.path.exists(image_path): | |
char['image'] = image_path | |
else: | |
char['image'] = None | |
return self.characters | |
def set_persistent_tags(self, tags_list): | |
self.persistent_tags = tags_list | |
self.save_persistent_tags() | |
def get_persistent_tags(self): | |
return self.persistent_tags | |
def add_character(self, character): | |
# Save image to disk and store the filename | |
image_data = character['image'] # This is base64 encoded string | |
safe_name = "".join(c for c in character['name'] if c.isalnum() or c in (' ', '_', '-')).rstrip() | |
image_filename = f"{safe_name}.png" | |
image_path = os.path.join(self.images_folder, image_filename) | |
# Decode the base64 image data and save it | |
if image_data: | |
image = Image.open(io.BytesIO(base64.b64decode(image_data.split(",")[1]))) | |
image.save(image_path) | |
character['image_path'] = image_path | |
else: | |
character['image_path'] = None | |
character.pop('image', None) | |
# Assuming traits is a string, split into list if necessary | |
if isinstance(character['traits'], str): | |
character['traits'] = character['traits'].split(',') | |
character['traits'] = [t.strip() for t in character['traits']] | |
self.characters.append(character) | |
self.save_characters() | |
def character_creation_app(data_manager): | |
with gr.Tab("Character Creation"): | |
with gr.Row(): | |
name_input = gr.Textbox(label="Character Name") | |
traits_input = gr.Textbox(label="Traits/Appearance Tags (comma separated)") | |
image_input = gr.Image(label="Upload Character Image", type="filepath") | |
# New gender selection input | |
gender_input = gr.Radio(choices=["Boy", "Girl"], label="Gender") | |
save_button = gr.Button("Save Character") | |
output = gr.Textbox(label="Status", interactive=False) | |
def save_character(name, traits, image_path, gender): | |
if not name or not traits or not gender: | |
return "Please enter all fields." | |
character = {'name': name, 'traits': traits, 'gender': gender, 'image': None} | |
# Read and encode image if provided | |
if image_path: | |
with open(image_path, "rb") as img_file: | |
image_data = base64.b64encode(img_file.read()).decode('utf-8') | |
character['image'] = f"data:image/png;base64,{image_data}" | |
else: | |
character['image'] = None | |
data_manager.add_character(character) | |
return f"Character '{name}' saved successfully." | |
save_button.click(save_character, inputs=[name_input, traits_input, image_input, gender_input], outputs=output) | |
def prompt_generator_app(data_manager): | |
with gr.Tab("Prompt Generator"): | |
gr.Markdown("## Prompt Generator") | |
# Add a refresh tags button | |
refresh_tags_button = gr.Button("Refresh Tags") | |
inputs = {} | |
tag_displays = {} | |
for category_name, var_name in categories: | |
tags_list = data_manager.get_category_tags(var_name) | |
tags_string = ', '.join(tags_list) | |
max_tags = len(tags_list) | |
if max_tags == 0: | |
default_value = 0 | |
else: | |
default_value = min(1, max_tags) | |
with gr.Group(): | |
gr.Markdown(f"### {category_name}") | |
tag_display = gr.Markdown(f"**Tags:** {tags_string}") | |
tag_num = gr.Slider(minimum=0, maximum=max_tags, step=1, value=default_value, label=f"Number of {category_name} Tags to Select") | |
inputs[f"{var_name}_num"] = tag_num | |
tag_displays[var_name] = (tag_display, tag_num) | |
# For Character Selection | |
with gr.Group(): | |
gr.Markdown("### Character Selection") | |
# Get the list of characters | |
def get_character_options(): | |
characters = data_manager.get_characters() | |
character_options = [] | |
for char in characters: | |
option_label = f"{char['name']} ({char['gender']})" | |
character_options.append(option_label) | |
return character_options | |
character_options = get_character_options() | |
character_select = gr.CheckboxGroup(choices=character_options, label="Select Characters", interactive=True) | |
refresh_characters_button = gr.Button("Refresh Character List") | |
def refresh_characters(): | |
new_options = get_character_options() | |
return gr.update(choices=new_options) | |
refresh_characters_button.click(refresh_characters, outputs=character_select) | |
random_characters = gr.Checkbox(label="Select Random Characters") | |
num_characters = gr.Slider(minimum=1, maximum=10, step=1, value=1, label="Number of Characters (if random)") | |
generate_button = gr.Button("Generate Prompt") | |
prompt_output = gr.Textbox(label="Generated Prompt", lines=5) | |
def generate_prompt(*args): | |
arg_idx = 0 | |
prompt_tags = [] | |
for category_name, var_name in categories: | |
tags_list = data_manager.get_category_tags(var_name) | |
tags_num = args[arg_idx] | |
arg_idx += 1 | |
if tags_list and tags_num > 0: | |
selected_tags = random.sample(tags_list, min(len(tags_list), int(tags_num))) | |
prompt_tags.extend(selected_tags) | |
# Handle Characters | |
selected_character_options = args[arg_idx] | |
random_chars = args[arg_idx + 1] | |
num_random_chars = args[arg_idx + 2] | |
arg_idx += 3 | |
characters = data_manager.get_characters() | |
if random_chars: | |
num = min(len(characters), int(num_random_chars)) | |
selected_chars = random.sample(characters, num) | |
else: | |
# Extract selected character names from options | |
selected_chars = [] | |
for option in selected_character_options: | |
name = option.split(' (')[0] | |
for char in characters: | |
if char['name'] == name: | |
selected_chars.append(char) | |
break | |
# Determine the number of boys and girls | |
num_girls = sum(1 for char in selected_chars if char.get('gender') == 'Girl') | |
num_boys = sum(1 for char in selected_chars if char.get('gender') == 'Boy') | |
# Build the initial character count tags | |
character_count_tags = [] | |
if num_girls > 0: | |
character_count_tags.append(f"{num_girls}girl" if num_girls == 1 else f"{num_girls}girls") | |
if num_boys > 0: | |
character_count_tags.append(f"{num_boys}boy" if num_boys == 1 else f"{num_boys}boys") | |
prompt_parts = [] | |
if character_count_tags: | |
prompt_parts.append(', '.join(character_count_tags)) | |
# Build character descriptions | |
character_descriptions = [] | |
for idx, char in enumerate(selected_chars): | |
# Get traits for the character | |
traits = ', '.join(char['traits']) | |
# Create a description for each character | |
# For SDXL models, use the format "[char1 description] AND [char2 description]" | |
# Each character's description is enclosed in parentheses | |
character_description = f"({traits})" | |
character_descriptions.append(character_description) | |
# Join character descriptions appropriately for SDXL models | |
if character_descriptions: | |
character_descriptions_str = ' AND '.join(character_descriptions) | |
prompt_parts.append(character_descriptions_str) | |
# Append selected prompt tags from categories | |
if prompt_tags: | |
prompt_tags_str = ', '.join(prompt_tags) | |
prompt_parts.append(prompt_tags_str) | |
# Load persistent tags | |
persistent_tags = data_manager.get_persistent_tags() | |
if persistent_tags: | |
persistent_tags_str = ', '.join(persistent_tags) | |
prompt_parts.append(persistent_tags_str) | |
# Add ending tags | |
ending_tags = "source_anime, score_9, score_8_up, score_7_up, masterpiece, best quality, very aesthetic, absurdres, anime artwork, anime style, vibrant, studio anime, highly detailed" | |
prompt_parts.append(ending_tags) | |
prompt_string = ', '.join(prompt_parts) | |
return prompt_string | |
# Prepare the list of inputs for the generate_prompt function | |
inputs_list = [] | |
for category_name, var_name in categories: | |
inputs_list.append(inputs[f"{var_name}_num"]) | |
# Add character_select directly to inputs | |
inputs_list.extend([character_select, random_characters, num_characters]) | |
generate_button.click(generate_prompt, inputs=inputs_list, outputs=prompt_output) | |
# Function to refresh tags display and sliders | |
def refresh_tags(): | |
updates = [] | |
for category_name, var_name in categories: | |
# Reload tags from data_manager | |
tags_list = data_manager.get_category_tags(var_name) | |
tags_string = ', '.join(tags_list) | |
max_tags = len(tags_list) | |
if max_tags == 0: | |
slider_value = 0 | |
else: | |
slider_value = min(1, max_tags) | |
# Update the tag display and slider | |
tag_display, tag_num = tag_displays[var_name] | |
updates.append(gr.update(value=f"**Tags:** {tags_string}")) | |
updates.append(gr.update(maximum=max_tags, value=slider_value)) | |
return updates | |
# Prepare the outputs list | |
outputs = [component for pair in tag_displays.values() for component in pair] | |
# Connect the refresh_tags function to the refresh_tags_button | |
refresh_tags_button.click(refresh_tags, outputs=outputs) | |
def tags_app(data_manager): | |
with gr.Tab("Tags"): | |
gr.Markdown("## Edit Tags for Each Category") | |
for category_name, var_name in categories: | |
gr.Markdown(f"### {category_name} Tags") | |
tags_list = data_manager.get_category_tags(var_name) | |
tags_string = ', '.join(tags_list) | |
tag_input = gr.Textbox(label=f"{category_name} Tags (comma separated)", value=tags_string) | |
save_button = gr.Button(f"Save {category_name} Tags") | |
status_output = gr.Textbox(label="", interactive=False) | |
# Function to save tags | |
def make_save_category_tags_fn(var_name, category_name): | |
def fn(tags_string): | |
tags_list = [t.strip() for t in tags_string.split(',') if t.strip()] | |
data_manager.set_category_tags(var_name, tags_list) | |
return f"{category_name} tags saved successfully." | |
return fn | |
save_fn = make_save_category_tags_fn(var_name, category_name) | |
save_button.click(save_fn, inputs=tag_input, outputs=status_output) | |
# Persistent Tags | |
gr.Markdown(f"### Persistent Tags") | |
persistent_tags_string = ', '.join(data_manager.get_persistent_tags()) | |
persistent_tags_input = gr.Textbox(label="Persistent Tags (comma separated)", value=persistent_tags_string) | |
save_persistent_tags_button = gr.Button("Save Persistent Tags") | |
persistent_status_output = gr.Textbox(label="", interactive=False) | |
def save_persistent_tags(tags_string): | |
tags_list = [t.strip() for t in tags_string.split(',') if t.strip()] | |
data_manager.set_persistent_tags(tags_list) | |
return "Persistent tags saved successfully." | |
save_persistent_tags_button.click(save_persistent_tags, inputs=persistent_tags_input, outputs=persistent_status_output) | |
def main(): | |
data_manager = DataManager(base_dir='/data') | |
with gr.Blocks() as demo: | |
prompt_generator_app(data_manager) | |
character_creation_app(data_manager) | |
tags_app(data_manager) | |
demo.launch() | |
if __name__ == "__main__": | |
main() |