| 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 |
|
|
| |
| if not os.path.exists("novelai_api"): |
| with zipfile.ZipFile("novelai_api.zip", "r") as zip_ref: |
| zip_ref.extractall(".") |
|
|
| |
| sys.path.append(os.path.abspath("novelai_api")) |
|
|
| |
| from novelai_api import NovelAIAPI |
| from novelai_api.ImagePreset import ImageModel, ImageGenerationType |
|
|
| |
| 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.") |
|
|
| |
| ARTISTS_PATH = 'artists.json' |
| CHARACTERS_PATH = 'characters.json' |
|
|
| |
| 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 [] |
|
|
| |
| 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)) |
| 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 |
|
|
| |
| def extract_character_from_prompt(prompt): |
| return prompt.split(",")[0].strip() if prompt else "" |
|
|
| |
| def get_random_characters(count): |
| characters = load_json_file(CHARACTERS_PATH) |
| return ', '.join(random.sample([char['character_name'] for char in characters], count)) |
|
|
| |
| def get_random_artists(count): |
| artists = load_json_file(ARTISTS_PATH) |
| return ', '.join(random.sample([artist['artist_name'] for artist in artists], count)) |
|
|
| |
| def parse_prompt(prompt): |
| |
| prompt = re.sub(r'randChar\((\d+)\)', lambda m: get_random_characters(int(m.group(1))), prompt) |
| |
| prompt = re.sub(r'randArtist\((\d+)\)', lambda m: get_random_artists(int(m.group(1))), prompt) |
| return prompt |
|
|
| |
| 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" |
| prompt = parse_prompt(prompt) |
|
|
| try: |
| async for _, img in api.low_level.generate_image(prompt, ImageModel.Anime_v3, ImageGenerationType.NORMAL, parameters): |
| return img |
| except Exception as e: |
| print(f"An error occurred during image generation: {e}") |
| return None |
|
|
| |
| 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") |
|
|
| |
| def update_parameters_with_metadata(image_path): |
| parsed_params = parse_image_metadata(image_path) |
| character_name = parsed_params.get('character_name', 'random') |
| 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() |