kidgen-faceid / app.py
Alshargi's picture
Update app.py
71293fd verified
import os
import gradio as gr
import numpy as np
from PIL import Image, ImageOps
import torch
from diffusers import StableDiffusionImg2ImgPipeline, EulerAncestralDiscreteScheduler
# -----------------------------
# CPU performance knobs
# -----------------------------
os.environ["OMP_NUM_THREADS"] = "2"
os.environ["MKL_NUM_THREADS"] = "2"
try:
torch.set_num_threads(2)
except Exception:
pass
torch.set_grad_enabled(False)
device = "cpu"
dtype = torch.float32
MODEL_ID = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
MODEL_ID,
torch_dtype=dtype,
safety_checker=None,
requires_safety_checker=False,
)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
pipe = pipe.to(device)
# Make diffusers quieter / slightly faster
pipe.set_progress_bar_config(disable=True)
# Helpful on CPU
try:
pipe.enable_attention_slicing()
except Exception:
pass
# VAE optimizations (often helps)
try:
pipe.enable_vae_slicing()
except Exception:
pass
try:
pipe.enable_vae_tiling()
except Exception:
pass
# -----------------------------
# Image utils
# -----------------------------
def _to_pil(img_np):
if img_np is None:
return None
if isinstance(img_np, Image.Image):
return img_np.convert("RGB")
arr = np.asarray(img_np)
if arr.dtype != np.uint8:
arr = np.clip(arr, 0, 255).astype(np.uint8)
if arr.ndim == 3 and arr.shape[2] == 4:
arr = arr[:, :, :3]
return Image.fromarray(arr).convert("RGB")
def _center_square(pil_img: Image.Image, out_size=512):
w, h = pil_img.size
s = min(w, h)
left = (w - s) // 2
top = (h - s) // 2
pil_img = pil_img.crop((left, top, left + s, top + s))
pil_img = pil_img.resize((out_size, out_size), Image.LANCZOS)
return pil_img
def _blend_parents(dad_pil, mom_pil, out_size=512):
dad = _center_square(dad_pil, out_size)
mom = _center_square(mom_pil, out_size)
blended = Image.blend(dad, mom, alpha=0.5)
blended = ImageOps.autocontrast(blended, cutoff=1)
return blended
# -----------------------------
# Generation
# -----------------------------
def generate_cartoon_kid(
dad_np,
mom_np,
age,
style_strength,
steps,
guidance_scale,
seed,
extra_prompt,
negative_prompt
):
dad = _to_pil(dad_np)
mom = _to_pil(mom_np)
if dad is None or mom is None:
raise gr.Error("ارفع صورتين: الأب + الأم.")
init = _blend_parents(dad, mom, out_size=512)
# ✅ Faster on CPU: 256 instead of 384
init_small = init.resize((256, 256), Image.LANCZOS)
base_prompt = (
"cute 3d animated child portrait, pixar-like style, "
"soft studio lighting, smooth skin, big friendly eyes, "
"high quality, centered face, clean background"
)
age = int(age)
if age <= 7:
age_prompt = f"a {age}-year-old kid, very cute, youthful"
elif age <= 12:
age_prompt = f"a {age}-year-old pre-teen kid, youthful"
else:
age_prompt = f"a {age}-year-old teen, youthful"
prompt = f"{base_prompt}, {age_prompt}"
if extra_prompt and extra_prompt.strip():
prompt = f"{prompt}, {extra_prompt.strip()}"
if seed is None or int(seed) < 0:
seed = np.random.randint(0, 2**31 - 1)
gen = torch.Generator(device=device).manual_seed(int(seed))
strength = float(style_strength)
# ✅ Inference mode saves overhead
with torch.inference_mode():
out = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
image=init_small,
strength=strength,
num_inference_steps=int(steps),
guidance_scale=float(guidance_scale),
generator=gen,
).images[0]
out = out.resize((512, 512), Image.LANCZOS)
return init, out, int(seed), prompt
# -----------------------------
# UI (same)
# -----------------------------
with gr.Blocks() as demo:
gr.Markdown("# 👶 Kid Generator — Cartoon (CPU)\nFaster CPU settings (256px + fewer steps).")
with gr.Row():
dad_in = gr.Image(type="numpy", label="Father")
mom_in = gr.Image(type="numpy", label="Mother")
with gr.Row():
age = gr.Slider(4, 15, value=11, step=1, label="Age")
style_strength = gr.Slider(0.45, 0.85, value=0.65, step=0.01, label="Cartoon Strength")
with gr.Row():
# ✅ recommend smaller steps default
steps = gr.Slider(6, 20, value=10, step=1, label="Steps (CPU faster)")
guidance_scale = gr.Slider(3, 8, value=5.5, step=0.1, label="Guidance Scale")
seed = gr.Number(value=-1, precision=0, label="Seed (-1 random)")
extra_prompt = gr.Textbox(label="Extra Prompt (optional)", placeholder="smiling, curly hair, freckles")
negative_prompt = gr.Textbox(
label="Negative Prompt",
value="realistic photo, ugly, deformed, blurry, low quality, watermark, text, scary, old, wrinkles"
)
btn = gr.Button("Generate Cartoon Kid", variant="primary")
with gr.Row():
init_out = gr.Image(label="Blended Parents (Init)")
out_img = gr.Image(label="Cartoon Kid Result")
used_seed = gr.Number(label="Used Seed", precision=0)
used_prompt = gr.Textbox(label="Used Prompt")
btn.click(
fn=generate_cartoon_kid,
inputs=[dad_in, mom_in, age, style_strength, steps, guidance_scale, seed, extra_prompt, negative_prompt],
outputs=[init_out, out_img, used_seed, used_prompt],
)
demo.launch()