File size: 8,789 Bytes
bb85dc7 3ee4725 04317d3 fee61cb 17d34fe 04317d3 fee61cb 17d34fe fee61cb 3ee4725 fee61cb 212866f bb85dc7 04317d3 fee61cb 04317d3 17d34fe 2fdefdd 17d34fe 2fdefdd 17d34fe fee61cb 17d34fe fee61cb 17d34fe fee61cb 2fdefdd 17d34fe 2fdefdd 17d34fe 2fdefdd 17d34fe 2fdefdd 10ed116 04317d3 2fdefdd fee61cb 04317d3 fee61cb 04317d3 17d34fe fee61cb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | import os
import sys
import zipfile
import gradio as gr
import numpy as np
from PIL import Image, PngImagePlugin
import asyncio
import io
import json
import re
import random
import base64
# Unzip the novelai_api package if it's not already extracted
if not os.path.exists("novelai_api"):
with zipfile.ZipFile("novelai_api.zip", "r") as zip_ref:
zip_ref.extractall(".")
# Add the extracted directory to Python path
sys.path.append(os.path.abspath("novelai_api"))
# Import NovelAIAPI
from novelai_api import NovelAIAPI
from novelai_api.ImagePreset import ImageModel, ImageGenerationType
# Retrieve the NovelAI API token from environment variables
ACCESS_TOKEN = os.getenv("NOVELAI_ACCESS_TOKEN")
if ACCESS_TOKEN is None:
raise ValueError("API token not found. Please set it as a secret in Hugging Face settings.")
# Paths to JSON data files
ARTISTS_PATH = 'artists.json'
CHARACTERS_PATH = 'characters.json'
# Load data from a JSON file
def load_json_file(file_path):
try:
with open(file_path, 'r') as file:
return json.load(file)
except Exception as e:
print(f"Error reading {file_path}: {e}")
return []
# Parse metadata from an image file
def parse_image_metadata(image_path):
params = {}
try:
with Image.open(image_path) as img:
if isinstance(img.info, PngImagePlugin.PngInfo):
metadata = img.info.get("parameters")
if metadata:
metadata_json = json.loads(metadata)
params['prompt'] = metadata_json.get("prompt", "")
params['steps'] = metadata_json.get("steps", 28)
params['height'] = metadata_json.get("height", 832)
params['width'] = metadata_json.get("width", 1216)
params['scale'] = metadata_json.get("scale", 6.5)
params['uc'] = metadata_json.get("uc", "")
params['sampler'] = metadata_json.get("sampler", "k_euler_ancestral")
params['noise_schedule'] = metadata_json.get("noise_schedule", "karras")
params['controlnet_strength'] = metadata_json.get("controlnet_strength", 1) == 1
params['prefer_brownian'] = metadata_json.get("prefer_brownian", True)
params['cfg_rescale'] = metadata_json.get("cfg_rescale", 0.2)
params['seed'] = metadata_json.get("seed", random.randint(0, 2**32 - 1)) # Default to a random seed if not found
params['character_name'] = extract_character_from_prompt(params['prompt'])
except (json.JSONDecodeError, KeyError):
print("Error parsing metadata JSON.")
except Exception as e:
print(f"Error reading image metadata: {e}")
return params
# Extract character from the beginning of the positive prompt
def extract_character_from_prompt(prompt):
return prompt.split(",")[0].strip() if prompt else ""
# Function to get random characters
def get_random_characters(count):
characters = load_json_file(CHARACTERS_PATH)
return ', '.join(random.sample([char['character_name'] for char in characters], count))
# Function to get random artists
def get_random_artists(count):
artists = load_json_file(ARTISTS_PATH)
return ', '.join(random.sample([artist['artist_name'] for artist in artists], count))
# Function to parse and replace placeholders in the prompt
def parse_prompt(prompt):
# Replace randChar(n) with n random characters
prompt = re.sub(r'randChar\((\d+)\)', lambda m: get_random_characters(int(m.group(1))), prompt)
# Replace randArtist(n) with n random artists
prompt = re.sub(r'randArtist\((\d+)\)', lambda m: get_random_artists(int(m.group(1))), prompt)
return prompt
# Generate image based on prompt and metadata parameters
async def generate_image(api, metadata_params):
parameters = {
"negative_prompt": metadata_params.get("uc", ""),
"height": metadata_params.get("height", 832),
"width": metadata_params.get("width", 1216),
"scale": metadata_params.get("scale", 6.5),
"steps": metadata_params.get("steps", 28),
"sampler": metadata_params.get("sampler", "k_euler_ancestral"),
"noise_schedule": metadata_params.get("noise_schedule", "karras"),
"controlnet_strength": 1.0 if metadata_params.get("controlnet_strength", True) else 0.0,
"prefer_brownian": metadata_params.get("prefer_brownian", True),
"cfg_rescale": metadata_params.get("cfg_rescale", 0.2),
"seed": metadata_params.get("seed", random.randint(0, 2**32 - 1)),
}
prompt = metadata_params.get("prompt") or "default prompt" # Set a default prompt if not in metadata
prompt = parse_prompt(prompt) # Parse the prompt to replace placeholders
try:
async for _, img in api.low_level.generate_image(prompt, ImageModel.Anime_v3, ImageGenerationType.NORMAL, parameters):
return img # Returning raw bytes
except Exception as e:
print(f"An error occurred during image generation: {e}")
return None
# Gradio Interface with Blocks
with gr.Blocks(css="#col-container { max-width: 800px; }") as demo:
characters = load_json_file(CHARACTERS_PATH)
character_options = ['random'] + [char['character_name'] for char in characters]
with gr.Column(elem_id="col-container"):
gr.Markdown("<div id='title'>NovelAI Image Generator</div>")
character_selection = gr.Dropdown(character_options, label="Enter or Select Character", value="random")
additional_tags = gr.Textbox(label="Additional Tags (comma-separated)", placeholder="e.g., nsfw, vibrant colors")
artists = gr.Textbox(label="Artists (comma-separated) or leave empty for random", placeholder="e.g., artist1, artist2")
quality_tags = gr.Checkbox(label="Include Quality Tags", value=True)
negative_prompt = gr.Textbox(label="Negative Prompt", value="{{worst quality, bad quality, censored}}, amputee, deformed")
with gr.Accordion("Advanced Options", open=False):
width = gr.Slider(512, 1216, step=64, label="Width", value=832)
height = gr.Slider(512, 1216, step=64, label="Height", value=1216)
scale = gr.Slider(1, 100, step=0.01, label="Scale", value=6.5)
steps = gr.Slider(1, 50, step=1, label="Steps", value=28)
sampler = gr.Dropdown(["k_euler", "k_euler_ancestral", "ddim", "k_dpm_2"], label="Sampler", value="k_euler_ancestral")
noise_schedule = gr.Dropdown(["karras", "polyexponential"], label="Noise Schedule", value="karras")
controlnet_strength = gr.Checkbox(label="ControlNet Strength", value=True)
prefer_brownian = gr.Checkbox(label="Prefer Brownian Noise", value=True)
cfg_rescale = gr.Slider(0, 1, step=0.01, label="CFG Rescale", value=0.2)
metadata_upload = gr.Image(label="Upload Image with Metadata", type="filepath")
# Function to handle image upload and extract metadata
def update_parameters_with_metadata(image_path):
parsed_params = parse_image_metadata(image_path)
character_name = parsed_params.get('character_name', 'random') # Extracted character
return (
character_name,
parsed_params.get('uc', ''),
parsed_params.get('width', 832),
parsed_params.get('height', 1216),
parsed_params.get('scale', 6.5),
parsed_params.get('steps', 28),
parsed_params.get('sampler', 'k_euler_ancestral'),
parsed_params.get('noise_schedule', 'karras'),
parsed_params.get('controlnet_strength', True),
parsed_params.get('prefer_brownian', True),
parsed_params.get('cfg_rescale', 0.2)
)
metadata_upload.change(
fn=update_parameters_with_metadata,
inputs=metadata_upload,
outputs=[character_selection, negative_prompt, width, height, scale, steps, sampler, noise_schedule, controlnet_strength, prefer_brownian, cfg_rescale]
)
generate_button = gr.Button("Generate Image")
result_image = gr.Image(type="numpy", label="Generated Image")
result_message = gr.Textbox(label="Status")
generate_button.click(
fn=lambda *args: asyncio.run(generate_image(*args)),
inputs=[
character_selection, additional_tags, artists, quality_tags, negative_prompt,
width, height, scale, steps, sampler, noise_schedule, controlnet_strength,
prefer_brownian, cfg_rescale
],
outputs=[result_image, result_message],
)
if __name__ == "__main__":
demo.launch() |