umerfarooq29 commited on
Commit
5a67aab
·
verified ·
1 Parent(s): 02df7bf

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -0
app.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
+ import io
4
+ import random
5
+ from PIL import Image, ImageOps
6
+ import numpy as np
7
+ import streamlit as st
8
+
9
+ # ---- ML libs ----
10
+ import torch
11
+ from diffusers import StableDiffusionControlNetImg2ImgPipeline, ControlNetModel, UniPCMultistepScheduler
12
+ from huggingface_hub import login
13
+
14
+ # ---- OpenCV for simple preproc (Canny) ----
15
+ import cv2
16
+
17
+ st.set_page_config(page_title="Sketch2Face (Streamlit + ControlNet)", layout="centered")
18
+
19
+ st.title("Sketch2Face — turn your face sketches into stylized images")
20
+ st.write("Upload a face sketch (line drawing). Use the prompt to guide style, pose & mood.")
21
+
22
+ # Get HF token (recommended to set as secret on HF Spaces or as env var locally)
23
+ HF_TOKEN = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_HUB_TOKEN")
24
+ if HF_TOKEN:
25
+ try:
26
+ login(token=HF_TOKEN)
27
+ except Exception:
28
+ pass
29
+ else:
30
+ st.warning("No Hugging Face token found. On Spaces, add HF_TOKEN in Settings → Secrets for model download. Locally use 'huggingface-cli login'.")
31
+
32
+ # Sidebar controls
33
+ with st.sidebar:
34
+ st.header("Generation settings")
35
+ model_id = st.text_input("Stable Diffusion model (hf repo)", value="runwayml/stable-diffusion-v1-5")
36
+ controlnet_id = st.text_input("ControlNet (canny) repo", value="lllyasviel/sd-controlnet-canny")
37
+ prompt = st.text_area("Prompt", value="A realistic portrait of a young man, soft lighting, cinematic")
38
+ negative_prompt = st.text_area("Negative prompt (optional)", value="lowres, deformed, extra fingers, watermark")
39
+ guidance_scale = st.slider("Guidance scale", 1.0, 20.0, 7.5)
40
+ strength = st.slider("Strength (how much to change sketch)", 0.1, 1.0, 0.7)
41
+ num_inference_steps = st.slider("Steps", 10, 60, 28)
42
+ seed = st.number_input("Seed (0 for random)", min_value=0, max_value=999999999, value=0, step=1)
43
+ use_gpu = st.checkbox("Use GPU (if available)", value=True)
44
+ run_btn = st.button("Generate")
45
+
46
+ # Upload sketch
47
+ uploaded = st.file_uploader("Upload your sketch (png/jpg). Prefer simple line art.", type=["png","jpg","jpeg"])
48
+ example_col1, example_col2 = st.columns(2)
49
+ with example_col1:
50
+ st.markdown("**Tip:**** clear black lines on white background work best.")
51
+ with example_col2:
52
+ st.markdown("**Tip:** crop to face / 1:1 or 3:4 ratio.")
53
+
54
+ @st.cache_resource(show_spinner=False)
55
+ def load_models(sd_model_id, cn_model_id, device):
56
+ # Load ControlNet then the combined pipeline
57
+ controlnet = ControlNetModel.from_pretrained(
58
+ cn_model_id, torch_dtype=torch.float16 if device=="cuda" else torch.float32
59
+ )
60
+ pipe = StableDiffusionControlNetImg2ImgPipeline.from_pretrained(
61
+ sd_model_id,
62
+ controlnet=controlnet,
63
+ safety_checker=None,
64
+ torch_dtype=torch.float16 if device=="cuda" else torch.float32,
65
+ )
66
+ # Scheduler & device
67
+ pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
68
+ if device == "cuda":
69
+ pipe.enable_xformers_memory_efficient_attention()
70
+ pipe.to("cuda")
71
+ else:
72
+ pipe.to("cpu")
73
+ return pipe
74
+
75
+ def prepare_control_image_pil(pil_img, target_size=512):
76
+ # Ensure grayscale -> convert to single-channel edge map using Canny
77
+ img = pil_img.convert("RGB")
78
+ open_cv_image = np.array(img)[:, :, ::-1] # RGB->BGR
79
+ gray = cv2.cvtColor(open_cv_image, cv2.COLOR_BGR2GRAY)
80
+ # Auto-threshold can be useful; here we use fixed, but you can expose sliders
81
+ edges = cv2.Canny(gray, 100, 200)
82
+ edges = cv2.resize(edges, (target_size, target_size))
83
+ # convert single channel to 3-channel PIL
84
+ edges_rgb = cv2.cvtColor(edges, cv2.COLOR_GRAY2RGB)
85
+ return Image.fromarray(edges_rgb)
86
+
87
+ def prepare_init_image(pil_img, target_size=512):
88
+ img = pil_img.convert("RGB")
89
+ img = ImageOps.fit(img, (target_size, target_size), Image.LANCZOS)
90
+ return img
91
+
92
+ if run_btn:
93
+ if not uploaded:
94
+ st.error("Please upload a sketch first.")
95
+ else:
96
+ device = "cuda" if (torch.cuda.is_available() and use_gpu) else "cpu"
97
+ with st.spinner("Loading models (first run may take ~1-2 minutes)..."):
98
+ pipe = load_models(model_id, controlnet_id, device)
99
+ # load user image
100
+ img = Image.open(uploaded)
101
+ control_image = prepare_control_image_pil(img, target_size=512)
102
+ init_image = prepare_init_image(img, target_size=512)
103
+
104
+ # seed
105
+ gen_seed = None if seed == 0 else int(seed)
106
+ generator = torch.Generator(device=device)
107
+ if gen_seed is not None:
108
+ generator = generator.manual_seed(gen_seed)
109
+ else:
110
+ generator = None
111
+
112
+ with st.spinner("Generating..."):
113
+ try:
114
+ output = pipe.img2img(
115
+ prompt=prompt,
116
+ image=init_image,
117
+ control_image=control_image,
118
+ negative_prompt=negative_prompt or None,
119
+ strength=float(strength),
120
+ guidance_scale=float(guidance_scale),
121
+ num_inference_steps=int(num_inference_steps),
122
+ generator=generator,
123
+ )
124
+ except Exception as e:
125
+ st.exception(f"Generation failed: {e}")
126
+ raise
127
+
128
+ result = output.images[0]
129
+ st.image(result, caption="Generated image", use_column_width=True)
130
+ # offer download
131
+ buf = io.BytesIO()
132
+ result.save(buf, format="PNG")
133
+ buf.seek(0)
134
+ st.download_button("Download image (PNG)", data=buf, file_name="sketch2face.png", mime="image/png")
135
+
136
+ # Show sample control image / debug
137
+ if uploaded:
138
+ try:
139
+ img = Image.open(uploaded)
140
+ control_img = prepare_control_image_pil(img, target_size=256)
141
+ st.caption("Preview: internal Canny/control image (what ControlNet sees)")
142
+ st.image(control_img)
143
+ except Exception:
144
+ pass
145
+
146
+ st.markdown("---")
147
+ st.markdown("Made for sketch-to-face. Adjust prompt & strength. For best results, upload clear line sketches and try style prompts like 'photorealistic', 'studio lighting', or artists' names (check licenses).")