Spaces:
Running
Running
import gradio as gr | |
import numpy as np | |
import requests | |
from PIL import Image | |
from io import BytesIO | |
import time | |
MAX_SEED = np.iinfo(np.int32).max | |
MAX_IMAGE_SIZE = 1344 | |
title = """ | |
# Stability AI - Developer Platform WebUI | |
### UI for using the stable image api | |
API Key is required to use this service. | |
https://platform.stability.ai/account/keys | |
Contact: D̷ELL@Stability AI - Advocate (https://x.com/xqdior) / Author: umise (https://x.com/UiE029) | |
""" | |
title_jp = """ | |
# Stability AI - Developer Platform WebUI | |
### このSpaceは、Stable Image APIを使用するためのWEB-UIです。 | |
このサービスを利用するにはAPIキーが必要です。以下のリンクから取得してください。 | |
https://platform.stability.ai/account/keys | |
お問い合わせ先: D̷ELL@Stability AI - Advocate (https://x.com/xqdior) / Author: umise (https://x.com/UiE029) | |
""" | |
overview = """ | |
**Overview** | |
Stability AI’s Stable Image services offer a growing set of APIs for developers to build the best in class image applications. | |
- **Disrupting Content Creation:** Stability’s Image APIs are the foundation for applications disrupting publishing, media, gaming, marketing, advertising, design, and more. | |
- **For Developers:** Application developers can build advanced features for designers, photographers, content creators, and a variety of B2C customers. | |
- **Simple APIs:** Stability AI is focused on delivering simple APIs for easy integration into applications with a high bar for quality, alignment, speed, and safety. | |
Get Started Now: https://platform.stability.ai/docs/getting-started/stable-image | |
""" | |
overview_jp = """ | |
各種モードについて | |
- テキストから生成: | |
プロンプトを基に画像を生成します。 | |
- アップスケール: | |
- 通常のアップスケール: | |
できるだけ絵を変更せずにアップスケールします | |
- クリエィティブなアップスケール: | |
絵全体をいい感じになるようにアップスケールします | |
- 画像の編集: | |
- 消去: | |
マスクした要素を削除します | |
- インペイント: | |
マスクされた部分とプロンプトを基に再生成します | |
- アウトペイント: | |
指定した範囲分、画像を拡張します | |
- 検索と置き換え: | |
検索プロンプトに入力されたオブジェクトを探し、プロンプトをもとにオブジェクトを再生成します | |
- コントロール: | |
- 構図: | |
入力画像の構図を基に新しい画像を生成します | |
- スケッチ: | |
ラフなスケッチとプロンプトを基に生成します | |
""" | |
model_url = { | |
"ImageUltra": "https://api.stability.ai/v2beta/stable-image/generate/ultra", | |
"ImageCore": "https://api.stability.ai/v2beta/stable-image/generate/core", | |
"StableDiffusion3": "https://api.stability.ai/v2beta/stable-image/generate/sd3", | |
} | |
service_url = { | |
"Conservative_Upscale": "https://api.stability.ai/v2beta/stable-image/upscale/conservative", | |
"Creative_Upscale": "https://api.stability.ai/v2beta/stable-image/upscale/creative", | |
"Erase": "https://api.stability.ai/v2beta/stable-image/edit/erase", | |
"Inpaint": "https://api.stability.ai/v2beta/stable-image/edit/inpaint", | |
"Outpaint": "https://api.stability.ai/v2beta/stable-image/edit/outpaint", | |
"SR": "https://api.stability.ai/v2beta/stable-image/edit/search-and-replace", | |
"RMBG": "https://api.stability.ai/v2beta/stable-image/edit/remove-background", | |
"Sketch": "https://api.stability.ai/v2beta/stable-image/control/sketch", | |
"Structure": "https://api.stability.ai/v2beta/stable-image/control/structure", | |
} | |
translations = { | |
"en": { | |
"api_key": "API Key", | |
"api_key_placeholder": "Enter your API key", | |
"model_label": "Model", | |
"mode_label": "Mode", | |
"prompt_placeholder": "Enter your prompt", | |
"negative_prompt_placeholder": "Enter a negative prompt", | |
"seed_label": "Seed", | |
"randomize_seed_label": "Randomize seed", | |
"aspect_label": "Aspect ratio", | |
"run_button": "Run", | |
"result_label": "Result", | |
"copy_field_placeholder": "Copy the field", | |
"Negative_prompt": "Negative prompt", | |
"Advanced_Settings": "Advanced Settings", | |
"Example": "Example", | |
"Generate": "Generate", | |
"Upscale": "Upscale", | |
"Edit": "Edit", | |
"Control": "Control", | |
"Submode": "Submode", | |
"Conservative": "Conservative", | |
"Creative": "Creative", | |
"Erase": "Erase", | |
"Inpaint": "Inpaint", | |
"Outpaint": "Outpaint", | |
"Structure": "Structure", | |
"Sketch": "Sketch", | |
"Search_and_Replace": "Search and Replace", | |
"Remove_Background": "Remove Background", | |
"input_image": "Input Image", | |
"style_preset": "Style preset", | |
"preset_description": "This parameter is only available for ImageCore model.", | |
"Search_prompt_placeholder": "Enter a search prompt", | |
"Control_Strength": "Control Strength", | |
"overview": overview, | |
"overview_label": "How to use", | |
"title": title, | |
}, | |
"ja": { | |
"api_key": "APIキー", | |
"api_key_placeholder": "APIキーを入力してください", | |
"model_label": "モデル", | |
"mode_label": "モード", | |
"prompt_placeholder": "プロンプトを入力してください", | |
"negative_prompt_placeholder": "ネガティブプロンプトを入力してください", | |
"seed_label": "シード", | |
"randomize_seed_label": "シードをランダム化", | |
"aspect_label": "アスペクト比", | |
"run_button": "実行", | |
"result_label": "結果", | |
"copy_field_placeholder": "ここに貼り付け用の情報が出てきます", | |
"Negative_prompt": "ネガティブプロンプト", | |
"Advanced_Settings": "追加設定", | |
"Example": "例", | |
"Generate": "テキストから生成", | |
"Upscale": "アップスケール", | |
"Edit": "画像の編集", | |
"Control": "コントロールモード", | |
"Submode": "サブモード", | |
"Conservative": "通常のアップスケール", | |
"Creative": "クリエィティブなアップスケール", | |
"Erase": "消去", | |
"Inpaint": "インペイント", | |
"Outpaint": "アウトペイント(拡張)", | |
"Structure": "構図", | |
"Sketch": "スケッチ", | |
"Search_and_Replace": "検索と置き換え", | |
"Remove_Background": "背景削除", | |
"input_image": "入力画像", | |
"style_preset": "スタイルのプリセット", | |
"preset_description": "このパラメータはimage coreのときにだけ有効になります", | |
"Search_prompt_placeholder": "探したい要素を入力してください", | |
"Control_Strength": "コントロールネットの適用強度", | |
"overview": overview_jp, | |
"overview_label": "使い方", | |
"title": title_jp, | |
}, | |
} | |
lang = "ja" | |
def bytes_to_image(image): | |
image = BytesIO(image) | |
image = Image.open(image).convert("RGB") | |
return image | |
def image_to_bytes(image): | |
byte_io = BytesIO() | |
image.save(byte_io, format="PNG") | |
byte_data = byte_io.getvalue() | |
return byte_data | |
def send_request(url, api_key, file, data): | |
response = requests.post( | |
url, | |
headers={"Authorization": f"Bearer {api_key}", "Accept": "image/*"}, | |
files=file, | |
data=data, | |
) | |
return response | |
def generate( | |
prompt, | |
negative_prompt, | |
seed, | |
mode, | |
submode, | |
input_image, | |
mask, | |
CNstrength, | |
search_prompt, | |
op_left, | |
op_right, | |
op_up, | |
op_down, | |
randomize_seed, | |
aspect, | |
model, | |
preset, | |
api_key, | |
): | |
if randomize_seed: | |
seed = 0 | |
file = {} | |
data = { | |
"prompt": prompt, | |
"negative_prompt": negative_prompt, | |
"output_format": "png", | |
"seed": seed, | |
"aspect_ratio": aspect, | |
} | |
data_rmbg = { | |
"output_format": "png", | |
} | |
if input_image is not None: | |
file["image"] = image_to_bytes(input_image) | |
if mask is not None: | |
file["mask"] = image_to_bytes(mask) | |
if mode == translations[lang]["Generate"]: | |
file["none"] = "" | |
if model == "Stable Image Ultra (8B + workflow)": | |
url = model_url["ImageUltra"] | |
elif model == "Stable Image Core (2B + workflow)": | |
url = model_url["ImageCore"] | |
data["style_preset"] = preset | |
elif model == "Stable Diffusion 3 Medium (2B)": | |
url = model_url["StableDiffusion3"] | |
data["model"] = "sd3-medium" | |
elif model == "Stable Diffusion 3 Large (8B)": | |
url = model_url["StableDiffusion3"] | |
data["model"] = "sd3-large" | |
elif model == "Stable Diffusion 3 Large Turbo (8B Turbo)": | |
url = model_url["StableDiffusion3"] | |
data["model"] = "sd3-large-turbo" | |
else: | |
raise ValueError("Invalid model type") | |
elif mode == translations[lang]["Upscale"]: | |
if submode == translations[lang]["Conservative"]: | |
url = service_url["Conservative_Upscale"] | |
elif submode == translations[lang]["Creative"]: | |
url = service_url["Creative_Upscale"] | |
elif mode == translations[lang]["Edit"]: | |
if submode == translations[lang]["Erase"]: | |
url = service_url["Erase"] | |
elif submode == translations[lang]["Inpaint"]: | |
url = service_url["Inpaint"] | |
elif submode == translations[lang]["Outpaint"]: | |
url = service_url["Outpaint"] | |
data["left"] = op_left | |
data["right"] = op_right | |
data["up"] = op_up | |
data["down"] = op_down | |
elif submode == translations[lang]["Search_and_Replace"]: | |
url = service_url["SR"] | |
data["search_prompt"] = search_prompt | |
elif submode == translations[lang]["Remove_Background"]: | |
data = data_rmbg | |
url = service_url["RMBG"] | |
elif mode == translations[lang]["Control"]: | |
data["control_strength"] = CNstrength | |
if submode == translations[lang]["Sketch"]: | |
url = service_url["Sketch"] | |
elif submode == translations[lang]["Structure"]: | |
url = service_url["Structure"] | |
response = send_request(url, api_key, file, data) | |
if response.status_code == 200: | |
if ( | |
mode == translations[lang]["Upscale"] | |
and submode == translations[lang]["Creative"] | |
): | |
generation_id = response.json().get("id") | |
if not generation_id: | |
raise Exception("No generation ID returned for creative upscale") | |
# Polling for the result | |
result_url = f"https://api.stability.ai/v2beta/stable-image/upscale/creative/result/{generation_id}" | |
while True: | |
result_response = requests.get( | |
result_url, | |
headers={"accept": "image/*", "authorization": f"Bearer {api_key}"}, | |
) | |
if result_response.status_code == 202: | |
print("Generation in-progress, try again in 10 seconds.") | |
time.sleep(10) | |
elif result_response.status_code == 200: | |
print("Generation complete!") | |
image = result_response.content | |
image = bytes_to_image(image) | |
copy_filed_value = f"prompt:{prompt}, negative:{negative_prompt}, mode:{mode}, submode:{submode}" | |
return image, seed, copy_filed_value | |
else: | |
raise Exception(str(result_response.json())) | |
else: | |
image = response.content | |
image = bytes_to_image(image) | |
copy_filed_value = f"prompt:{prompt}, negative:{negative_prompt}, mode:{mode}, submode:{submode}" | |
return image, seed, copy_filed_value | |
else: | |
raise Exception(str(response.json())) | |
examples = [ | |
"Astronaut in a jungle, cold color palette, muted colors, detailed, 8k", | |
"An astronaut riding a green horse", | |
"A delicious ceviche cheesecake slice", | |
] | |
css = """ | |
#col-container { | |
margin: 0 auto; | |
max-width: 50vw; | |
} | |
""" | |
def update_style_visibility(model): | |
if model == "Stable Image Core (2B + workflow)": | |
return gr.update(visible=True) | |
else: | |
return gr.update(visible=False) | |
def update_mode(mode): | |
submode_update = gr.update(choices=["None"], visible=False) | |
image_label_update = gr.update(visible=False) | |
img_input_update = gr.update(visible=False) | |
mask_update = gr.update(visible=False) | |
if mode == translations[lang]["Generate"]: | |
submode_update = gr.update(visible=False) | |
elif mode == translations[lang]["Upscale"]: | |
submode_update = gr.update( | |
choices=[ | |
translations[lang]["Conservative"], | |
translations[lang]["Creative"], | |
], | |
value=translations[lang]["Conservative"], | |
visible=True, | |
) | |
img_input_update = gr.update(visible=True) | |
image_label_update = gr.update(visible=True) | |
elif mode == translations[lang]["Edit"]: | |
submode_update = gr.update( | |
choices=[ | |
translations[lang]["Erase"], | |
translations[lang]["Inpaint"], | |
translations[lang]["Outpaint"], | |
translations[lang]["Search_and_Replace"], | |
translations[lang]["Remove_Background"], | |
], | |
value=translations[lang]["Erase"], | |
visible=True, | |
) | |
img_input_update = gr.update(visible=True) | |
image_label_update = gr.update(visible=True) | |
elif mode == translations[lang]["Control"]: | |
submode_update = gr.update( | |
choices=[ | |
translations[lang]["Structure"], | |
translations[lang]["Sketch"], | |
], | |
value=translations[lang]["Structure"], | |
visible=True, | |
) | |
img_input_update = gr.update(visible=True) | |
image_label_update = gr.update(visible=True) | |
return submode_update, img_input_update, mask_update, image_label_update | |
def update_submode(submode): | |
mask = gr.update(visible=False) | |
outpaint = gr.update(visible=False) | |
cn = gr.update(visible=False) | |
search_prompt = gr.update(visible=False) | |
if submode in [translations[lang]["Erase"], translations[lang]["Inpaint"]]: | |
mask = gr.update(visible=True) | |
else: | |
if submode == translations[lang]["Outpaint"]: | |
outpaint = gr.update(visible=True) | |
elif submode in [translations[lang]["Structure"], translations[lang]["Sketch"]]: | |
cn = gr.update(visible=True) | |
elif submode == translations[lang]["Search_and_Replace"]: | |
search_prompt = gr.update(visible=True) | |
return mask, outpaint, cn, search_prompt | |
with gr.Blocks(css=css) as demo: #, theme="NoCrypt/miku") as demo: | |
with gr.Column(elem_id="col-container"): | |
gr.Markdown( | |
translations[lang]["title"], | |
) | |
with gr.Accordion(translations[lang]["overview_label"], open=False): | |
gr.Markdown( | |
translations[lang]["overview"], | |
) | |
with gr.Row(): | |
api_key = gr.Text( | |
label=translations[lang]["api_key"], | |
type="password", | |
placeholder=translations[lang]["api_key_placeholder"], | |
max_lines=1, | |
container=False, | |
) | |
with gr.Row(): | |
model = gr.Dropdown( | |
label=translations[lang]["model_label"], | |
choices=[ | |
"Stable Image Ultra (8B + workflow)", | |
"Stable Image Core (2B + workflow)", | |
"Stable Diffusion 3 Large Turbo (8B Turbo)", | |
"Stable Diffusion 3 Large (8B)", | |
"Stable Diffusion 3 Medium (2B)", | |
], | |
value="Stable Image Ultra (8B + workflow)", | |
) | |
mode = gr.Dropdown( | |
label=translations[lang]["mode_label"], | |
choices=[ | |
translations[lang]["Generate"], | |
translations[lang]["Upscale"], | |
translations[lang]["Edit"], | |
translations[lang]["Control"], | |
], | |
value=translations[lang]["Generate"], | |
) | |
submode = gr.Dropdown( | |
label=translations[lang]["Submode"], | |
choices=["None"], | |
visible=False, | |
value="None", | |
) | |
with gr.Row(): | |
with gr.Column(): | |
prompt = gr.Text( | |
label="Prompt", | |
show_label=False, | |
max_lines=1, | |
placeholder=translations[lang]["prompt_placeholder"], | |
container=False, | |
) | |
search_prompt = gr.Text( | |
label="search prompt", | |
visible=False, | |
show_label=False, | |
max_lines=1, | |
placeholder=translations[lang]["Search_prompt_placeholder"], | |
) | |
run_button = gr.Button(translations[lang]["run_button"], scale=0) | |
with gr.Row(): | |
gr.Examples( | |
label=translations[lang]["Example"], examples=examples, inputs=[prompt] | |
) | |
with gr.Row(): | |
with gr.Column(): | |
image_label = gr.Markdown( | |
value=translations[lang]["input_image"], visible=False | |
) | |
image = gr.Image( | |
type="pil", | |
label="img input", | |
width="20vw", | |
height="20vw", | |
show_label=True, | |
visible=False, | |
interactive=True, | |
container=False, | |
) | |
with gr.Column(visible=False) as mask: | |
mask_label = gr.Markdown(value="input mask") | |
mask_input = gr.Image( | |
type="pil", | |
label="mask", | |
width="20vw", | |
height="20vw", | |
show_label=True, | |
interactive=True, | |
container=False, | |
) | |
with gr.Row(): | |
result = gr.Image( | |
label=translations[lang]["result_label"], width="20vw", height="20%" | |
) | |
with gr.Accordion(translations[lang]["Advanced_Settings"], open=False): | |
negative_prompt = gr.Text( | |
label=translations[lang]["Negative_prompt"], | |
max_lines=1, | |
placeholder=translations[lang]["negative_prompt_placeholder"], | |
) | |
seed = gr.Slider( | |
label=translations[lang]["seed_label"], | |
minimum=0, | |
maximum=MAX_SEED, | |
step=1, | |
value=0, | |
) | |
CN_strength = gr.Slider( | |
label=translations[lang]["Control_Strength"], | |
minimum=0, | |
maximum=1, | |
step=0.01, | |
value=0.5, | |
visible=False, | |
) | |
randomize_seed = gr.Checkbox( | |
label=translations[lang]["randomize_seed_label"], value=True | |
) | |
aspect = gr.Radio( | |
choices=[ | |
"1:1", | |
"16:9", | |
"21:9", | |
"2:3", | |
"3:2", | |
"4:5", | |
"5:4", | |
"9:16", | |
"9:21", | |
], | |
label=translations[lang]["aspect_label"], | |
value="1:1", | |
) | |
with gr.Row(visible=False) as style: | |
style_preset = gr.Radio( | |
choices=[ | |
"3d-model", | |
"analog-film", | |
"anime", | |
"cinematic", | |
"comic-book", | |
"digital-art", | |
"enhance", | |
"fantasy-art", | |
"isometric", | |
"line-art", | |
"low-poly", | |
"modeling-compound", | |
"neon-punk", | |
"origami", | |
"photographic", | |
"pixel-art", | |
"tile-texture", | |
], | |
label=translations[lang]["style_preset"], | |
value="anime", | |
info=translations[lang]["preset_description"], | |
) | |
with gr.Row(visible=False) as outpaint_scale: | |
paint = gr.Markdown(value="Outpain Scale") | |
op_left = gr.Slider( | |
label="left", minimum=0, maximum=2000, step=4, value=200 | |
) | |
op_right = gr.Slider( | |
label="right", minimum=0, maximum=2000, step=4, value=200 | |
) | |
op_up = gr.Slider( | |
label="up", minimum=0, maximum=2000, step=4, value=200 | |
) | |
op_down = gr.Slider( | |
label="down", minimum=0, maximum=2000, step=4, value=200 | |
) | |
copy_filed = gr.TextArea( | |
value="", | |
label="Copy Field", | |
max_lines=1, | |
placeholder=translations[lang]["copy_field_placeholder"], | |
show_copy_button=True, | |
container=False, | |
) | |
gr.Markdown( | |
f""" | |
## License | |
This work is licensed under a | |
[Creative Commons Attribution-NonCommercial 4.0 International License][cc-by-nc]. | |
[![CC BY-NC 4.0][cc-by-nc-image]][cc-by-nc] | |
[cc-by-nc]: https://creativecommons.org/licenses/by-nc/4.0/ | |
[cc-by-nc-image]: https://licensebuttons.net/l/by-nc/4.0/88x31.png | |
[cc-by-nc-shield]: https://img.shields.io/badge/License-CC%20BY--NC%204.0-lightgrey.svg | |
**MIT Licensed Source Code** | |
Portions of this work are licensed under the MIT License. For more details, please refer to the original source at: [stabilityai/stable-diffusion-3-medium](https://huggingface.co/spaces/stabilityai/stable-diffusion-3-medium) | |
""" | |
) | |
gr.on( | |
triggers=[run_button.click, prompt.submit, negative_prompt.submit], | |
fn=generate, | |
inputs=[ | |
prompt, | |
negative_prompt, | |
seed, | |
mode, | |
submode, | |
image, | |
mask_input, | |
CN_strength, | |
search_prompt, | |
op_left, | |
op_right, | |
op_up, | |
op_down, | |
randomize_seed, | |
aspect, | |
model, | |
style_preset, | |
api_key, | |
], | |
outputs=[result, seed, copy_filed], | |
) | |
mode.change( | |
fn=update_mode, inputs=mode, outputs=[submode, image, mask, image_label] | |
) | |
submode.change( | |
fn=update_submode, | |
inputs=submode, | |
outputs=[mask, outpaint_scale, CN_strength, search_prompt], | |
) | |
model.change(fn=update_style_visibility, inputs=model, outputs=style) | |
demo.launch() | |