Spaces:
Runtime error
Runtime error
| #version 2.1 | |
| import os | |
| import math | |
| import gradio as gr | |
| import numpy as np | |
| import requests | |
| import json | |
| import base64 | |
| from PIL import Image | |
| from io import BytesIO | |
| import runpod | |
| from enum import Enum | |
| api_key = os.getenv("FAI_API_KEY") | |
| api = os.getenv("FAI_API_5") | |
| api_old = os.getenv("FAI_API_old") | |
| api_anime = os.getenv("FAI_anime") #api_anime = os.getenv("ANIME_API") | |
| rmbgkey = os.getenv("RMBGKEY") | |
| #fore, prompt, intensity, mode, refprompt, isrmbg | |
| #erased this prompt | |
| #"A car, on a high-tech city street at night, surrounded by neon lights and holographic billboards, with sleek skyscrapers and flying cars in the background, a glowing cyber-road beneath", | |
| outputs_list = [["examples/out1.png"],["examples/out3.png"], ["examples/out4.png"],["examples/out5.png"],["examples/out6.png"],["examples/out7.png"],["examples/out2.png"]] | |
| prompt_list = ["A Perfume Bottle, resting on a wooden table, surrounded by lavender leaves with droplets of morning dew glistening, in a serene garden, under the soft glow of a twilight sky, encircled by delicate purple petals", | |
| "A PAir of Shoes, resting on a sleek white platform under a bright spotlight, with subtle pastel-colored reflections and delicate flower petals scattered around, creating a cheerful and elegant setting", | |
| "A Perfume bottle, perched delicately on a rock by the ocean, with a breathtaking sunset casting a golden glow over the waves", | |
| "A Bottle, placed on a rustic wooden table, viewed from a slight above angle, overlooking a serene beach, with seashells and driftwood artfully scattered around, bathed in the warm glow of the setting sun", | |
| "A Sofa, In a snug, inviting room, a large sofa accompanied by plush cushions, a thick rug, book-filled shelves, potted plants, framed art, a coffee table with candles, a floor lamp, and gentle, warm lighting", | |
| "Headphones, hovering gracefully against a swirling pastel galaxy, accented by glimmers of stardust, ethereal light beams, and floating musical notes, under a dreamy aurora borealis, with no visible wires" | |
| ] | |
| example_list = [ | |
| [ | |
| "examples/in5.png", | |
| "A Perfume bottle, perched delicately on a rock βby the ocean, with a breathtaking sunset casting a golden glow over the waves", | |
| 3.5, | |
| "full", | |
| "(( A transparent perfume Bottle with a black cap )), and a yellow liquide, sunset, transparent", | |
| False, | |
| "examples/out5.png" | |
| ], | |
| [ | |
| "examples/in6.png", | |
| "A Sofa, In a snug, inviting room, a large sofa accompanied by plush cushions, a thick rug, book-filled shelves, potted plants, framed art, a coffee table with candles, a floor lamp, and gentle, warm lighting", | |
| 3.5, | |
| "full", | |
| "A light grey Sofa, studio light, shadows", | |
| False, | |
| "examples/out6.png" | |
| ], | |
| #[ | |
| # "examples/in7.png", | |
| # "A car, on a high-tech city street at night, surrounded by neon lights and holographic billboards, with sleek skyscrapers and flying cars in the background, a glowing cyber-road beneath", | |
| # 3.0, | |
| # "full", | |
| # "(( A light grey Car )), vibrant, reflections", | |
| # False, | |
| # "examples/out7.png" | |
| #], | |
| [ | |
| "examples/in1.png", | |
| "A Perfume Bottle, resting on a wooden table, surrounded by lavender leaves with droplets of morning dew glistening, in a serene garden, under the soft glow of a twilight sky, encircled by delicate purple petals", | |
| 3.0, | |
| "full", | |
| "A perfume Bottle with a purple liquide, studio light", | |
| False, | |
| "examples/out1.png" | |
| ], | |
| [ | |
| "examples/in2.png", | |
| "Headphones, hovering gracefully against a swirling pastel galaxy, accented by glimmers of stardust, ethereal light beams, and floating musical notes, under a dreamy aurora borealis, with no visible wires", | |
| 3.50, | |
| "full", | |
| "headphones, vibrant, colorful, ", | |
| False, | |
| "examples/out2.png" | |
| ], | |
| [ | |
| "examples/in3.png", | |
| "A PAir of Shoes, resting on a sleek white platform under a bright spotlight, with subtle pastel-colored reflections and delicate flower petals scattered around, creating a cheerful and elegant setting", | |
| 3.5, | |
| "full", | |
| "(( A Pair of Brown Shoes )), vibrant, Shadow", | |
| False, | |
| "examples/out3.png" | |
| ], | |
| [ | |
| "examples/in4.png", | |
| "A Bottle, placed on a rustic wooden table, viewed from a slight above angle, overlooking a serene beach, with seashells and driftwood artfully scattered around, bathed in the warm glow of the setting sun", | |
| 3.50, | |
| "full", | |
| "A transparent Bottle with a black cap and a semi transparent liquide, Sunset", | |
| False, | |
| "examples/out4.png" | |
| ], | |
| [ | |
| "examples/anime_1in_1.png", | |
| "A man with spiky standing on a cliff overlooking a dystopian city at dusk", | |
| 2.5, | |
| "full", | |
| "Anime style, intense expression, neon lights", | |
| False, | |
| "examples/anime_1out.png" | |
| ], | |
| [ | |
| "examples/anime_2in_1.png", | |
| "A young man standing in front of a park, on a sunny day", | |
| 2.5, | |
| "full", | |
| "((a man with a green shirt)), Anime style, stoic, serene atmosphere, bright sky, sunny ", | |
| False, | |
| "examples/anime_5out.png" | |
| ], | |
| [ | |
| "examples/anime_3in.png", | |
| "A woman , sitting in an outdoor restaurant, beautiful cityscape", | |
| 2.5, | |
| "full", | |
| "Anime style, joyful expression", | |
| False, | |
| "examples/anime_3out.webp" | |
| ], | |
| [ | |
| "examples/anime_4in.png", | |
| "A woman , standing in front of mountains and sea view", | |
| 2.5, | |
| "full", | |
| "Anime style, detailed background", | |
| False, | |
| "examples/anime_4out.webp" | |
| ] | |
| ] | |
| def rmbg(pil_image): | |
| # Convert PIL image to bytes | |
| image_bytes = BytesIO() | |
| pil_image.save(image_bytes, format='PNG') | |
| image_bytes.seek(0) | |
| # Send the image to the remove.bg API | |
| response = requests.post( | |
| 'https://api.remove.bg/v1.0/removebg', | |
| files={'image_file': ('filename.png', image_bytes, 'image/png')}, | |
| data={'size': 'auto'}, | |
| headers={'X-Api-Key': rmbgkey} | |
| ) | |
| if response.status_code == 200: | |
| # Convert the bytes response to a PIL image | |
| result_image = Image.open(BytesIO(response.content)) | |
| return result_image | |
| else: | |
| return None | |
| def image_to_base64(image): | |
| # Open the image file | |
| with image: | |
| # Create a buffer to hold the binary data | |
| buffered = BytesIO() | |
| # Save the image in its original format to the buffer | |
| #print(image.format) | |
| image.save(buffered, format="PNG") | |
| # Get the byte data from the buffer | |
| binary_image_data = buffered.getvalue() | |
| # Encode the binary data to a base64 string | |
| base64_image = base64.b64encode(binary_image_data).decode("utf-8") | |
| return base64_image | |
| def create_square_image(image): | |
| """ | |
| Create a new square image with the side length equal to the largest dimension | |
| of the original image and paste the original image at the center on a transparent canvas. | |
| :param image: A PIL image. | |
| :return: A new square PIL image. | |
| """ | |
| original_width, original_height = image.size | |
| new_side_length = max(original_width, original_height) | |
| # Create a new square image with a transparent background | |
| new_image = Image.new("RGBA", (new_side_length, new_side_length), (255, 255, 255, 0)) | |
| # Calculate the position to paste the original image on the new square canvas | |
| paste_x = (new_side_length - original_width) // 2 | |
| paste_y = (new_side_length - original_height) // 2 | |
| # Paste the original image onto the new square canvas using the alpha channel as a mask | |
| new_image.paste(image, (paste_x, paste_y), image) | |
| return new_image | |
| def process(data, api, api_key): | |
| runpod.api_key = api_key | |
| input_payload = {"input": data } | |
| try: | |
| endpoint = runpod.Endpoint(api) | |
| run_request = endpoint.run(input_payload) | |
| # Initial check without blocking, useful for quick tasks | |
| status = run_request.status() | |
| print(f"Initial job status: {status}") | |
| if status=="IN_QUEUE": | |
| gr.Info("Queued πΆπΆπΆπΆ!", duration=35) | |
| if status != "COMPLETED": | |
| # Polling with timeout for long-running tasks | |
| output = run_request.output(timeout=120) | |
| else: | |
| output = run_request.output() | |
| print(f"Job output: {output}") | |
| except Exception as e: | |
| print(f"An error occurred: {e}") | |
| status = run_request.status() | |
| if status=="FAILED": | |
| raise gr.Error(f"An error occured π₯! {e}", duration=5) | |
| if status=="TIMED_OUT": | |
| raise gr.Error("Sorry we could not secure a worker for you β³! Try again", duration=5) | |
| image_data = output['image'] | |
| # Decode the Base64 string | |
| image_bytes = base64.b64decode(image_data) | |
| # Convert binary data to image | |
| image = Image.open(BytesIO(image_bytes)) | |
| return image | |
| def resize_to_fit(max_size, original_size): | |
| """ | |
| Calculate the new size for an image to fit within max_size while maintaining the aspect ratio. | |
| :param max_size: Maximum allowed size as a tuple (width, height). | |
| :param original_size: Original size of the image as a tuple (width, height). | |
| :return: New size as a tuple (new_width, new_height) that fits within max_size while maintaining the aspect ratio. | |
| """ | |
| original_width, original_height = original_size | |
| max_width, max_height = max_size | |
| # Calculate the scaling factor to maintain aspect ratio | |
| width_ratio = max_width / original_width | |
| height_ratio = max_height / original_height | |
| scaling_factor = min(width_ratio, height_ratio) | |
| # Calculate the new size while maintaining the aspect ratio | |
| new_width = int(original_width * scaling_factor) | |
| new_height = int(original_height * scaling_factor) | |
| return new_width, new_height | |
| def process_generate(fore, prompt, intensity, mode, refprompt, isrmbg, model_type, steps, control_strength): | |
| max_size = (1500,1500) | |
| if prompt in prompt_list: | |
| max_size = (1500,1500) | |
| if isrmbg: | |
| try: | |
| rmbgfore = rmbg(fore) | |
| if rmbgfore is not None: | |
| fore = rmbgfore.convert("RGBA") | |
| print(f"Background removed!") | |
| except: | |
| pass | |
| # | |
| # Select the endpoint based on the model type | |
| if model_type == "anime": | |
| api_endpoint = api_anime #to change to api_anime | |
| elif model_type == "realistic": | |
| fore = create_square_image(fore) | |
| api_endpoint = api_old | |
| else: | |
| raise ValueError("Invalid model type selected.") | |
| size = fore.size | |
| image_width = size[0] | |
| image_height = size[1] | |
| gr.Warning(f"βΉοΈ The input image size is: {size} ") | |
| if size[0]*size[1]<=(512*512): | |
| gr.Warning("βΉοΈ The input image resolution is low, it might lead to some deformation!") | |
| if size[0]*size[1]>(max_size[0]*max_size[1]): | |
| gr.Warning("βΉοΈ The input image size is too big, I will lower it!") | |
| image_width, image_height = resize_to_fit((768,768), (image_width, image_height)) | |
| fore.resize(max_size) | |
| forestr = image_to_base64(fore.convert("RGBA")) | |
| data = { | |
| "foreground_image64": forestr, | |
| "prompt" : prompt, | |
| "mode" : mode, | |
| "intensity" : float(intensity), | |
| "width" : 1000, | |
| "height" : 1000, | |
| "refprompt" : refprompt, | |
| "first_stage_steps" : int(steps), | |
| "first_stage_strength" : float(control_strength), | |
| "second_stage_steps" : 20 | |
| } | |
| #print(f"DATA: {data}") | |
| ''' | |
| data = { | |
| "foreground_image64": forestr, | |
| "prompt" : "There is Perfume, nestled on a crystalline cliff of glistening snow, under a celestial night sky adorned with constellations and swirling galaxies, framed by ethereal, blue flames that dance gracefully in the icy air", | |
| "mode" : "full", #refiner, full | |
| "intensity" : 3.0, | |
| "width" : 1000, | |
| "height" : 1000, | |
| "refprompt" : " transparent glass " | |
| } | |
| ''' | |
| image = process(data, api_endpoint, api_key) | |
| return image | |
| def update_value(val): | |
| return val | |
| class Stage(Enum): | |
| FIRST_STAGE = "first-stage" | |
| SECOND_STAGE = "refiner" | |
| FULL = "full" | |
| css="""#disp_image { | |
| text-align: center; | |
| } | |
| #share-btn-container { | |
| padding-left: 0.5rem !important; | |
| padding-right: 0.5rem !important; | |
| background-color: #000000; | |
| justify-content: center; | |
| align-items: center; | |
| border-radius: 9999px !important; | |
| max-width: 13rem; | |
| margin-left: auto; | |
| } | |
| #share-btn-container > div { | |
| flex-direction: row; | |
| background: black; | |
| align-items: center; | |
| } | |
| #share-btn-container:hover { | |
| background-color: #060606; | |
| } | |
| #share-btn { | |
| all: initial; | |
| color: #ffffff; | |
| font-weight: 600; | |
| cursor: pointer; | |
| font-family: 'IBM Plex Sans', sans-serif; | |
| margin-left: 0.5rem !important; | |
| padding-top: 0.5rem !important; | |
| padding-bottom: 0.5rem !important; | |
| right: 0; | |
| } | |
| #share-btn * { | |
| all: unset; | |
| } | |
| #share-btn-container div:nth-child(-n+2) { | |
| width: auto !important; | |
| min-height: 0px !important; | |
| } | |
| #share-btn-container .wrap { | |
| display: none !important; | |
| } | |
| #share-btn-container.hidden { | |
| display: none !important; | |
| } | |
| #duplicate-button { | |
| margin-left: auto; | |
| color: #fff; | |
| background: #1565c0; | |
| } | |
| body { | |
| font-family: Arial, sans-serif; | |
| background-color: #f4f4f9; | |
| margin: 0; | |
| padding: 0; | |
| min-height: 100vh; | |
| color: #333; | |
| } | |
| .custom-button { | |
| background: linear-gradient(rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.8) 100%); | |
| color: transparent; | |
| font-size: 30px; | |
| color: white; | |
| padding: 10px 20px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: opacity 0.3s ease; | |
| position: relative; | |
| } | |
| .custom-button:hover { | |
| opacity: 0.8; | |
| } | |
| .custom-button::before { | |
| content: 'Generate'; | |
| background: linear-gradient(90deg, rgb(29, 161, 255) 0%, rgb(163, 44, 255) 56%, rgb(251, 156, 255) 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| z-index: 1; | |
| font-weight: bold; | |
| } | |
| .custom-title { | |
| font-size: 36px; | |
| background: #1E2125 text; | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| font-weight: bold; | |
| text-align: center; | |
| margin-bottom: 20px; | |
| } | |
| h1 { | |
| color: #222222; | |
| } | |
| a { | |
| color: #4a90e2; | |
| text-decoration: none; | |
| font-weight: bold; | |
| } | |
| a:hover { | |
| text-decoration: underline; | |
| } | |
| .emoji { | |
| font-size: 1.5em; | |
| } | |
| @media (max-width: 768px) { | |
| .custom-button { | |
| font-size: 20px; | |
| padding: 8px 16px; | |
| } | |
| .custom-title { | |
| font-size: 28px; | |
| } | |
| #disp_image, .container { | |
| padding: 0 1rem; | |
| } | |
| h1 { | |
| font-size: 24px; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .custom-button { | |
| font-size: 16px; | |
| padding: 6px 12px; | |
| } | |
| .custom-title { | |
| font-size: 22px; | |
| } | |
| .container { | |
| padding: 0 0.5rem; | |
| } | |
| h1 { | |
| font-size: 20px; | |
| } | |
| #share-btn-container { | |
| max-width: 100%; | |
| padding: 0.5rem; | |
| } | |
| } | |
| """ | |
| block = gr.Blocks(css=css, title="## FAI Fuzer").queue(default_concurrency_limit=12) | |
| with block: | |
| gr.HTML(''' | |
| <div class="container"> | |
| <center> | |
| <h1 class="custom-title"> | |
| FAI Fuzer medium v0.3: Empower your AI Image Generation with Full Control | |
| <span class="emoji"></span> | |
| </h1> | |
| </center> | |
| <h2>Welcome to the Updated Version of FAI Fuzer!</h2> | |
| <p>This is FAI Fuzer medium v0.3, an updated version of our previously released Fuzer v0.1. In this version, we've introduced several enhancements, including the addition of a new anime model to broaden your creative possibilities.</p> | |
| <p>We appreciate the feedback and support from our community and are excited to bring these new features to you.</p> | |
| <p><strong>What's New in v0.3:</strong></p> | |
| <ul> | |
| <li>π Introduction of an Anime Model: Now you can choose between "realistic" and "anime" styles for your images.</li> | |
| <li>π¨ Enhanced Control: Improved background blending and consistency features.</li> | |
| <li>π Performance Improvements: Faster processing and improved UI responsiveness.</li> | |
| </ul> | |
| <p>Please check us out on <strong>Twitter</strong>: | |
| <a href="https://x.com/FotographerAI">Fotographer AI</a> and | |
| <strong>Discord</strong>: <a href="https://discord.gg/7bKaeXfH">https://discord.gg/7bKaeXfH</a></p> | |
| <h2>Step-by-Step Instructions</h2> | |
| <p>Follow these instructions to control the generation of backgrounds while keeping the foreground's shape and style consistent:</p> | |
| <ul> | |
| <li> | |
| <span class="emoji">π</span> | |
| <strong>Step 1: Describe the Background</strong> | |
| <p>Start by providing a detailed description of the background you want to create.</p> | |
| <div class="example"> | |
| <p><strong>Example:</strong> "A Perfume Bottle nestled on a crystalline cliff of glistening snow, overlooking a serene, moonlit valley."</p> | |
| </div> | |
| </li> | |
| <li> | |
| <span class="emoji">π‘</span> | |
| <strong>Step 2: Describe the Foreground</strong> | |
| <p>Next, describe the texture, lighting, and style of the foreground element.</p> | |
| <div class="example"> | |
| <p><strong>Example:</strong> "A transparent glass perfume bottle, vibrant, sunset lighting reflecting off its surface."</p> | |
| </div> | |
| </li> | |
| <li> | |
| <span class="emoji">ποΈ</span> | |
| <strong>Step 3: Adjust the Intensity</strong> | |
| <p>Decide how much change you want to apply to the image. Adjust the intensity to balance between keeping consistency and introducing new elements.</p> | |
| </li> | |
| </ul> | |
| <p><strong>Note: It usually takes around 20 seconds to generate an image. However, the time can extend up to 40-50 seconds when there is a queue.</strong></p> | |
| </div> | |
| ''') | |
| gr.HTML(""" | |
| <center><h2><a href="https://fotographer.ai/api/top">π Check out our API!</a></h2></center>""") | |
| with gr.Row(): | |
| gr.Markdown("### F.ai Fuzer: Real Composite Photography in 2 minutes!") | |
| with gr.Row(): | |
| fore = gr.Image(image_mode='RGBA', type="pil", label="Foreground Image", height=400, width=400, min_width=400) | |
| # with gr.Column(): | |
| result_gallery = gr.Image(label='Output', min_width=400) #gr.Gallery(height=400, object_fit='contain', label='Outputs') | |
| with gr.Row(): | |
| prompt = gr.Textbox(label="Prompt", min_width=400) | |
| # with gr.Column(): | |
| refprompt = gr.Textbox(label="Refiner Prompt", min_width=400) | |
| with gr.Row(): | |
| with gr.Column(min_width=400): | |
| mode = gr.Radio(choices=[e.value for e in Stage], | |
| value=Stage.FULL.value, | |
| label="Generation Mode", type='value', min_width=400) | |
| mode.change(fn=update_value, inputs=mode, outputs=mode) | |
| model_type = gr.Dropdown(choices=["realistic","anime"], label="Model Type", value="realistic", min_width=400) # Adding the Dropdown for model selection | |
| with gr.Column(min_width=400): | |
| # Adding Advanced Settings for the number of steps and control strength | |
| with gr.Accordion("Advanced Settings", open=True, ): | |
| steps = gr.Slider(label="Number of Steps", minimum=10, maximum=100, value=30, step=1) | |
| control_strength = gr.Slider(label="Control Strength", minimum=0.1, maximum=1.0, value=0.5, step=0.1) | |
| with gr.Column(min_width=400): | |
| gr.HTML(''' | |
| <div class="container"> | |
| <h1>π For more freedom of usage, check out our API</h1> | |
| <p>π€ You can test with free credits:</p> | |
| <h2><a href="https://fotographer.ai/api/top">π API Dashboard</a></h2> | |
| </div> | |
| ''') | |
| with gr.Row(): | |
| intensity = gr.Slider(label="Refiner Strength", minimum=1.0, maximum=7.0, value=3.0, step=0.5) | |
| intensity.change(fn=update_value, inputs=intensity, outputs=intensity) | |
| isrmbg = gr.Checkbox(label="Remove Background") | |
| isrmbg.change(fn=update_value, inputs=isrmbg, outputs=isrmbg) | |
| generate_button = gr.Button(value="Generate", elem_classes="custom-button") | |
| gr.HTML(''' | |
| <div class="container"> | |
| <h2>Features:</h2> | |
| <ul> | |
| <li>πΌοΈ <strong>Consistent Foreground Style:</strong> Keep the foreground's shape and style intact across various backgrounds.</li> | |
| <li>π <strong>Seamless Blending:</strong> Effortlessly blend foreground elements with any background for a natural look.</li> | |
| <li>π¨ <strong>Custom Background Control:</strong> Choose or generate backgrounds that perfectly match your creative vision.</li> | |
| <li>βοΈ <strong>Easy Integration:</strong> Integrates smoothly with your existing workflow and tools.</li> | |
| </ul> | |
| </div> | |
| ''') | |
| gr.HTML(""" | |
| <center><h2><a href="https://fotographer.ai/">π Check Out our other Projects Here!</a></h2></center>""") | |
| with gr.Row(): | |
| dummy_image_for_outputs = gr.Image(visible=False, label='Result') | |
| gr.Examples( | |
| fn=lambda *args: [args[-1]], | |
| examples=example_list, | |
| inputs=[ | |
| fore, prompt, intensity, mode, refprompt, isrmbg, dummy_image_for_outputs | |
| ] | |
| ) | |
| ''' | |
| with gr.Column(): | |
| dummy_image_for_outputs = gr.Image(visible=False, label='Result') | |
| gr.Examples( | |
| examples=outputs_list, | |
| inputs=[dummy_image_for_outputs], | |
| ) | |
| ''' | |
| ins = [fore, prompt, intensity, mode, refprompt, isrmbg, model_type, steps, control_strength] | |
| generate_button.click(fn=process_generate, inputs=ins, outputs=[result_gallery]) | |
| block.launch() | |