Code-Cooker / app.py
Severian's picture
Update app.py
8e871a8 verified
raw
history blame
18.6 kB
import torch
import gradio as gr
from PIL import Image
import qrcode
from pathlib import Path
from multiprocessing import cpu_count
import requests
import io
import os
from PIL import Image
#import spaces
import numpy as np
import cv2
from pyzxing import BarCodeReader
from PIL import ImageOps
from huggingface_hub import hf_hub_download, snapshot_download
from PIL import ImageEnhance
from diffusers import (
StableDiffusionPipeline,
StableDiffusionControlNetImg2ImgPipeline,
StableDiffusionControlNetPipeline,
ControlNetModel,
DDIMScheduler,
DPMSolverMultistepScheduler,
DEISMultistepScheduler,
HeunDiscreteScheduler,
EulerDiscreteScheduler,
)
qrcode_generator = qrcode.QRCode(
version=1,
error_correction=qrcode.ERROR_CORRECT_H,
box_size=10,
border=4,
)
# Define available models
CONTROLNET_MODELS = {
"QR Code Monster": "monster-labs/control_v1p_sd15_qrcode_monster",
"QR Code": "DionTimmer/controlnet_qrcode-control_v1p_sd15",
# Add more ControlNet models here
}
DIFFUSION_MODELS = {
"GhostMix": "sinkinai/GhostMix-V2-BakedVae",
# Add more diffusion models here
}
# Global variables to store loaded models
loaded_controlnet = None
loaded_pipe = None
def load_models_on_launch():
global loaded_controlnet, loaded_pipe
print("Loading models on launch...")
controlnet_path = snapshot_download(CONTROLNET_MODELS["QR Code Monster"])
loaded_controlnet = ControlNetModel.from_pretrained(
controlnet_path,
torch_dtype=torch.float16
).to("mps")
diffusion_path = snapshot_download(DIFFUSION_MODELS["GhostMix"])
loaded_pipe = StableDiffusionControlNetImg2ImgPipeline.from_pretrained(
diffusion_path,
controlnet=loaded_controlnet,
torch_dtype=torch.float16,
safety_checker=None,
).to("mps")
print("Models loaded successfully!")
# Modify the load_models function to use global variables
def load_models(controlnet_model, diffusion_model):
global loaded_controlnet, loaded_pipe
if loaded_controlnet is None or loaded_pipe is None:
load_models_on_launch()
return loaded_pipe
# Add new functions for image adjustments
def adjust_image(image, brightness, contrast, saturation):
if image is None:
return None
img = Image.fromarray(image) if isinstance(image, np.ndarray) else image
if brightness != 1:
img = ImageEnhance.Brightness(img).enhance(brightness)
if contrast != 1:
img = ImageEnhance.Contrast(img).enhance(contrast)
if saturation != 1:
img = ImageEnhance.Color(img).enhance(saturation)
return np.array(img)
def resize_for_condition_image(input_image: Image.Image, resolution: int):
input_image = input_image.convert("RGB")
W, H = input_image.size
k = float(resolution) / min(H, W)
H *= k
W *= k
H = int(round(H / 64.0)) * 64
W = int(round(W / 64.0)) * 64
img = input_image.resize((W, H), resample=Image.LANCZOS)
return img
SAMPLER_MAP = {
"DPM++ Karras SDE": lambda config: DPMSolverMultistepScheduler.from_config(config, use_karras=True, algorithm_type="sde-dpmsolver++"),
"DPM++ Karras": lambda config: DPMSolverMultistepScheduler.from_config(config, use_karras=True),
"Heun": lambda config: HeunDiscreteScheduler.from_config(config),
"Euler": lambda config: EulerDiscreteScheduler.from_config(config),
"DDIM": lambda config: DDIMScheduler.from_config(config),
"DEIS": lambda config: DEISMultistepScheduler.from_config(config),
}
def scan_qr_code(image):
# Convert gradio image to PIL Image if necessary
if isinstance(image, np.ndarray):
image = Image.fromarray(image)
# Convert to grayscale
gray_image = image.convert('L')
# Convert to numpy array
np_image = np.array(gray_image)
# Method 1: Using qrcode library
try:
qr = qrcode.QRCode()
qr.add_data('')
qr.decode(gray_image)
return qr.data.decode('utf-8')
except Exception:
pass
# Method 2: Using OpenCV
try:
qr_detector = cv2.QRCodeDetector()
retval, decoded_info, points, straight_qrcode = qr_detector.detectAndDecodeMulti(np_image)
if retval:
return decoded_info[0]
except Exception:
pass
# Method 3: Fallback to zxing-cpp
try:
reader = BarCodeReader()
results = reader.decode(np_image)
if results:
return results[0].parsed
except Exception:
pass
return None
def invert_image(image):
if image is None:
return None
if isinstance(image, np.ndarray):
return 255 - image
elif isinstance(image, Image.Image):
return ImageOps.invert(image.convert('RGB'))
else:
raise ValueError("Unsupported image type")
def invert_displayed_image(image):
if image is None:
return None
inverted = invert_image(image)
if isinstance(inverted, np.ndarray):
return Image.fromarray(inverted)
return inverted
#@spaces.GPU()
def inference(
qr_code_content: str,
prompt: str,
negative_prompt: str,
guidance_scale: float = 10.0,
controlnet_conditioning_scale: float = 2.0,
strength: float = 0.8,
seed: int = -1,
init_image: Image.Image | None = None,
use_qr_code_as_init_image = True,
sampler = "DPM++ Karras SDE",
bg_color: str = "white",
qr_color: str = "black",
invert_final_image: bool = False,
invert_init_image: bool = False,
controlnet_model: str = "QR Code Monster",
diffusion_model: str = "GhostMix",
):
try:
progress = gr.Progress()
# Load models based on user selection
progress(0, desc="Downloading models...")
pipe = load_models(controlnet_model, diffusion_model)
progress(0.5, desc="Models downloaded, preparing for inference...")
if prompt is None or prompt == "":
raise gr.Error("Prompt is required")
if qr_code_content == "":
raise gr.Error("QR Code Content is required")
pipe.scheduler = SAMPLER_MAP[sampler](pipe.scheduler.config)
if seed == -1:
seed = torch.randint(0, 2**32 - 1, (1,)).item()
generator = torch.manual_seed(seed)
print("Generating QR Code from content")
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(qr_code_content)
qr.make(fit=True)
qrcode_image = qr.make_image(fill_color=qr_color, back_color=bg_color)
qrcode_image = resize_for_condition_image(qrcode_image, 768)
# Determine which image to use as init_image and control_image
if use_qr_code_as_init_image:
init_image = qrcode_image
control_image = qrcode_image
else:
control_image = qrcode_image
if init_image is None:
# If no init_image provided, set strength to 1.0 to generate a new image
strength = 1.0
# Adjust strength if using an init_image
if init_image is not None:
strength = min(strength, 0.8) # Cap strength at 0.8 when using init_image
# Invert init_image if requested
if invert_init_image and init_image is not None:
init_image = invert_image(init_image)
out = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=init_image,
control_image=control_image,
width=1024,
height=1024,
guidance_scale=float(guidance_scale),
controlnet_conditioning_scale=float(controlnet_conditioning_scale),
generator=generator,
strength=float(strength),
num_inference_steps=100,
)
final_image = out.images[0]
if invert_final_image:
final_image = invert_image(final_image)
return final_image, seed
except Exception as e:
print(f"Error in inference: {str(e)}")
return Image.new('RGB', (1024, 1024), color='white'), -1
def invert_init_image_display(image):
if image is None:
return None
inverted = invert_image(image)
if isinstance(inverted, np.ndarray):
return Image.fromarray(inverted)
return inverted
with gr.Blocks(theme='Hev832/Applio') as blocks:
gr.Markdown(
"""
![Yamamoto Logo](https://cdn-uploads.huggingface.co/production/uploads/64740cf7485a7c8e1bd51ac9/_VyYxp5qE_nRZ_LJqBxmL.webp)
# 🎨 Yamamoto QR Code Art Generator
Transform Your QR Codes into Brand Masterpieces
"""
)
with gr.Tabs():
with gr.TabItem("1. Input & Design"):
with gr.Row():
with gr.Column(scale=1):
qr_code_content = gr.Textbox(
label="QR Code Content",
placeholder="Enter URL or text for your QR code",
info="This is what your QR code will link to or display when scanned.",
)
prompt = gr.Textbox(
label="Artistic Prompt",
placeholder="Describe the style or theme for your QR code art",
value="A high-resolution, photo-realistic minimalist rendering of Mount Fuji, depicted as a sharp, semi-realistic silhouette of a mountain range on the horizon. The mountain evokes strength and motion with clean, crisp lines and a sense of natural flow. The scene should feature detailed snow textures, subtle highlights on the mountain ridges, and a powerful yet serene atmosphere. The rendering should emphasize the strength of the mountain with a focus on clarity and precision in both texture and light. (Sharp outlines:1.5), (Photo-realistic:1.4), (Detailed textures:1.3), (Minimalist:1.3), (Semi-realistic:1.3), (Monochrome contrast:1.2), (Crisp detail:1.2), (Evoking strength:1.2), inspired by traditional Japanese woodblock prints, nature photography, and minimalist design principles.",
info="Be specific and creative! This guides the AI in creating your unique QR code art.",
)
negative_prompt = gr.Textbox(
label="Elements to Avoid",
placeholder="Describe what you don't want in the image",
value="ugly, disfigured, low quality, blurry, nsfw, bad_pictures, (bad_prompt_version2:0.8), EasyNegative, 3d, cartoon, anime, sketches, (worst quality:2), (low quality:2), (normal quality:2), lowres, normal quality, ((monochrome)), ((grayscale)), poorly drawn, distorted, overexposed, flat shading, bad proportions, deformed, pixelated, messy details, lack of contrast, unrealistic textures, bad anatomy, rough edges, low resolution, text artifacts.",
info="List elements or styles you want to avoid in your QR code art.",
)
with gr.Column(scale=1):
with gr.Accordion("QR Code Customization", open=True):
bg_color = gr.ColorPicker(
label="Background Color",
value="#FFFFFF",
info="Choose the background color for the QR code"
)
qr_color = gr.ColorPicker(
label="QR Code Color",
value="#000000",
info="Choose the color for the QR code pattern"
)
with gr.Accordion("Use Your Own Image as a Reference", open=False):
init_image = gr.Image(label="Reference Image", type="pil")
use_qr_code_as_init_image = gr.Checkbox(
label="Use QR code as base image",
value=True,
interactive=True,
info="Uncheck to use your own image for generation"
)
invert_init_image_button = gr.Button("Invert Init Image")
with gr.TabItem("2. Advanced Settings"):
with gr.Row():
with gr.Column(scale=1):
with gr.Accordion("Art Generation Controls", open=True):
controlnet_conditioning_scale = gr.Slider(
minimum=0.0, maximum=5.0, step=0.01, value=2,
label="QR Code Visibility in Image",
)
strength = gr.Slider(
minimum=0.0, maximum=1.0, step=0.01, value=0.9,
label="Artistic Freedom for the AI",
)
guidance_scale = gr.Slider(
minimum=0.0, maximum=50.0, step=0.25, value=7.5,
label="How closely the AI follows the Prompt",
)
with gr.Accordion("Model Selection", open=True):
controlnet_model_dropdown = gr.Dropdown(
choices=list(CONTROLNET_MODELS.keys()),
value="QR Code Monster",
label="ControlNet Model",
info="Select the ControlNet model for QR code generation"
)
diffusion_model_dropdown = gr.Dropdown(
choices=list(DIFFUSION_MODELS.keys()),
value="GhostMix",
label="Diffusion Model",
info="Select the main diffusion model for image generation"
)
with gr.Column(scale=1):
with gr.Accordion("Generation Settings", open=True):
sampler = gr.Dropdown(
choices=list(SAMPLER_MAP.keys()),
value="DPM++ Karras SDE",
label="Art Style the AI uses to create the image",
)
seed = gr.Slider(
minimum=-1, maximum=9999999999, step=1, value=-1,
label="Creative Seed for the Image Generation",
randomize=False,
)
with gr.Accordion("Additional Options", open=False):
invert_final_image = gr.Checkbox(
label="Invert Final Image",
value=False,
info="Check this to invert the colors of the final image",
)
with gr.TabItem("3. Generate & Refine"):
with gr.Row():
with gr.Column(scale=1):
run_btn = gr.Button("🎨 Create Your QR Art", variant="primary")
result_image = gr.Image(label="Your Artistic QR Code")
used_seed = gr.Number(label="Seed Used", interactive=False)
with gr.Column(scale=1):
with gr.Accordion("Image Adjustment", open=True):
brightness = gr.Slider(minimum=0.1, maximum=2.0, step=0.1, value=1.0, label="Brightness")
contrast = gr.Slider(minimum=0.1, maximum=2.0, step=0.1, value=1.0, label="Contrast")
saturation = gr.Slider(minimum=0.1, maximum=2.0, step=0.1, value=1.0, label="Saturation")
invert_button = gr.Button("Invert Image")
with gr.Accordion("QR Code Verification", open=True):
scan_button = gr.Button("Verify QR Code Works")
scan_result = gr.Textbox(label="Validation Result of QR Code", interactive=False)
gr.Markdown(
"""
### 🔍 Tips for Optimizing Your QR Art
- If the QR code isn't scannable, try adjusting Brightness, Contrast, and Saturation.
- For more artistic flair, increase 'Artistic Freedom' in Advanced Settings.
- To make the QR code clearer, raise 'QR Code Visibility' in Advanced Settings.
- Experiment with different prompts and settings to find your perfect style!
"""
)
def scan_and_display(image):
if image is None:
return "No image to scan"
scanned_text = scan_qr_code(image)
if scanned_text:
return f"Scanned successfully: {scanned_text}"
else:
return "Failed to scan QR code. Try adjusting the settings for better visibility."
def invert_displayed_image(image):
if image is None:
return None
return invert_image(image)
scan_button.click(
scan_and_display,
inputs=[result_image],
outputs=[scan_result]
)
invert_button.click(
invert_displayed_image,
inputs=[result_image],
outputs=[result_image]
)
invert_init_image_button.click(
invert_init_image_display,
inputs=[init_image],
outputs=[init_image]
)
brightness.change(
adjust_image,
inputs=[result_image, brightness, contrast, saturation],
outputs=[result_image]
)
contrast.change(
adjust_image,
inputs=[result_image, brightness, contrast, saturation],
outputs=[result_image]
)
saturation.change(
adjust_image,
inputs=[result_image, brightness, contrast, saturation],
outputs=[result_image]
)
run_btn.click(
inference,
inputs=[
qr_code_content,
prompt,
negative_prompt,
guidance_scale,
controlnet_conditioning_scale,
strength,
seed,
init_image,
use_qr_code_as_init_image,
sampler,
bg_color,
qr_color,
invert_final_image,
controlnet_model_dropdown,
diffusion_model_dropdown,
],
outputs=[result_image, used_seed],
concurrency_limit=20
)
# Load models on launch
load_models_on_launch()
blocks.queue(max_size=20)
blocks.launch(share=False, show_api=True)