Files changed (1) hide show
  1. app.py +129 -32
app.py CHANGED
@@ -60,7 +60,7 @@ def is_valid_prompt(prompt):
60
 
61
  return True
62
 
63
- # ── Generation function ───────────────────────────────────────────────────────
64
  def generate_image(prompt, num_inference_steps=500, guidance_scale=7.5):
65
  if not is_valid_prompt(prompt):
66
  raise gr.Error("Please enter a valid prompt (not empty or special characters only).")
@@ -144,38 +144,135 @@ def generate_image(prompt, num_inference_steps=500, guidance_scale=7.5):
144
 
145
  return image
146
 
147
- # ── Gradio UI ─────────────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  with gr.Blocks() as demo:
149
- gr.Markdown("# πŸ–ΌοΈ Latent Diffusion Model β€” Text to Image")
150
-
151
- with gr.Row():
152
- with gr.Column():
153
- prompt = gr.Text(label="Prompt", placeholder="e.g. people walking on street")
154
- steps = gr.Slider(label="Inference Steps", minimum=100, maximum=1000, step=50, value=500)
155
- guidance = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=15.0, step=0.5, value=7.5)
156
- generate_button = gr.Button("Generate", variant="primary")
157
-
158
- with gr.Column():
159
- output_image = gr.Image(label="Generated Image")
160
- # clip_score = gr.Text(label="CLIP Score")
161
-
162
- generate_button.click(
163
- fn=generate_image,
164
- inputs=[prompt, steps, guidance],
165
- # outputs=[output_image, clip_score]
166
- outputs=[output_image]
167
-
168
- )
169
-
170
- gr.Examples(
171
- examples=[
172
- ["people walking on street", 500, 7.5],
173
- ["a dog playing in the park", 500, 7.5],
174
- ["sunset over mountains", 500, 7.5],
175
- ],
176
-
177
- inputs=[prompt, steps, guidance]
178
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
 
180
  if __name__ == "__main__":
181
  demo.launch()
 
60
 
61
  return True
62
 
63
+ # ── Generation function (Text-to-Image) ──────────────────────────────────────
64
  def generate_image(prompt, num_inference_steps=500, guidance_scale=7.5):
65
  if not is_valid_prompt(prompt):
66
  raise gr.Error("Please enter a valid prompt (not empty or special characters only).")
 
144
 
145
  return image
146
 
147
+
148
+ # ── NEW: Image-to-Image (Img2Img) Function ───────────────────────────────────
149
+ def generate_img2img(prompt, init_image, strength=0.75, num_inference_steps=500, guidance_scale=7.5):
150
+ if init_image is None:
151
+ raise gr.Error("Please upload an image first.")
152
+ if not is_valid_prompt(prompt):
153
+ raise gr.Error("Please enter a valid prompt (not empty or special characters only).")
154
+
155
+ # 1. Preprocess the input image
156
+ init_image = init_image.convert("RGB").resize((128, 128))
157
+ init_tensor = torch.from_numpy(np.array(init_image)).permute(2, 0, 1).unsqueeze(0).float() / 255.0
158
+ init_tensor = (init_tensor * 2 - 1).to(device)
159
+ if torch_dtype == torch.float16:
160
+ init_tensor = init_tensor.half()
161
+
162
+ # 2. Encode image to latent space
163
+ with torch.no_grad():
164
+ latents = vae.encode(init_tensor).latent_dist.sample()
165
+ latents = 0.18215 * latents
166
+
167
+ # 3. Add noise according to strength
168
+ noise = torch.randn_like(latents)
169
+ start_step = int(strength * num_inference_steps)
170
+ scheduler.set_timesteps(num_inference_steps)
171
+ timesteps = scheduler.timesteps
172
+
173
+ if start_step < len(timesteps):
174
+ t_start = timesteps[start_step]
175
+ latents = scheduler.add_noise(latents, noise, t_start)
176
+ else:
177
+ latents = scheduler.add_noise(latents, noise, timesteps[-1])
178
+
179
+ # 4. Prepare text embeddings (same as txt2img)
180
+ text_input = tokenizer(
181
+ prompt, padding="max_length", max_length=77,
182
+ truncation=True, return_tensors="pt"
183
+ ).to(device)
184
+
185
+ uncond_input = tokenizer(
186
+ [""], padding="max_length", max_length=77,
187
+ truncation=True, return_tensors="pt"
188
+ ).to(device)
189
+
190
+ with torch.no_grad():
191
+ text_embeddings = text_encoder(text_input.input_ids)[0]
192
+ uncond_embeddings = text_encoder(uncond_input.input_ids)[0]
193
+ text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
194
+
195
+ # 5. Denoising loop (starting from noisy latent)
196
+ for i, t in enumerate(timesteps):
197
+ if i < start_step:
198
+ continue # skip steps we already noised
199
+
200
+ latent_model_input = torch.cat([latents] * 2)
201
+ latent_model_input = scheduler.scale_model_input(latent_model_input, t)
202
+
203
+ with torch.no_grad():
204
+ noise_pred = unet(
205
+ latent_model_input, t,
206
+ encoder_hidden_states=text_embeddings
207
+ ).sample
208
+
209
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
210
+ noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
211
+ latents = scheduler.step(noise_pred, t, latents).prev_sample
212
+
213
+ # 6. Decode
214
+ latents = 1 / 0.18215 * latents
215
+ with torch.no_grad():
216
+ image = vae.decode(latents).sample
217
+
218
+ image = (image / 2 + 0.5).clamp(0, 1)
219
+ image = image.detach().cpu().float()
220
+ image = image.permute(0, 2, 3, 1).squeeze(0).numpy()
221
+ image = (image * 255).round().astype("uint8")
222
+ return Image.fromarray(image)
223
+
224
+
225
+ # ── Gradio UI (Ω…ΨΉ Ψ§Ω„ΨͺبويباΨͺ) ────────────────────────────────────────────────
226
  with gr.Blocks() as demo:
227
+ gr.Markdown("# πŸ–ΌοΈ Latent Diffusion Model β€” Text to Image + Img2Img")
228
+
229
+ with gr.Tabs():
230
+ # Ψͺبويب Text-to-Image (Ψ§Ω„ΩƒΩˆΨ― Ψ§Ω„Ψ£Ψ΅Ω„ΩŠ Ψ¨Ψ―ΩˆΩ† أي Ψͺغيير)
231
+ with gr.TabItem("Text to Image"):
232
+ with gr.Row():
233
+ with gr.Column():
234
+ prompt = gr.Text(label="Prompt", placeholder="e.g. people walking on street")
235
+ steps = gr.Slider(label="Inference Steps", minimum=100, maximum=1000, step=50, value=500)
236
+ guidance = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=15.0, step=0.5, value=7.5)
237
+ generate_button = gr.Button("Generate", variant="primary")
238
+
239
+ with gr.Column():
240
+ output_image = gr.Image(label="Generated Image")
241
+
242
+ generate_button.click(
243
+ fn=generate_image,
244
+ inputs=[prompt, steps, guidance],
245
+ outputs=[output_image]
246
+ )
247
+
248
+ gr.Examples(
249
+ examples=[
250
+ ["people walking on street", 500, 7.5],
251
+ ["a dog playing in the park", 500, 7.5],
252
+ ["sunset over mountains", 500, 7.5],
253
+ ],
254
+ inputs=[prompt, steps, guidance]
255
+ )
256
+
257
+ # Ψͺبويب Image-to-Image (Ψ§Ω„Ψ¬Ψ―ΩŠΨ―)
258
+ with gr.TabItem("Image to Image (Img2Img)"):
259
+ with gr.Row():
260
+ with gr.Column():
261
+ init_image = gr.Image(label="ارفع Ψ§Ω„Ψ΅ΩˆΨ±Ψ©", type="pil")
262
+ prompt_img = gr.Text(label="Prompt", placeholder="Ψ§ΩƒΨͺΨ¨ وءف Ψ§Ω„ΨͺΨΉΨ―ΩŠΩ„ Ψ§Ω„Ω…Ψ·Ω„ΩˆΨ¨")
263
+ strength = gr.Slider(label="Strength (Ω‚ΩˆΨ© Ψ§Ω„ΨͺΨΉΨ―ΩŠΩ„)", minimum=0.1, maximum=1.0, step=0.05, value=0.75)
264
+ steps_img = gr.Slider(label="Inference Steps", minimum=100, maximum=1000, step=50, value=500)
265
+ guidance_img = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=15.0, step=0.5, value=7.5)
266
+ generate_img_btn = gr.Button("Generate from Image", variant="primary")
267
+
268
+ with gr.Column():
269
+ output_img2img = gr.Image(label="Ψ§Ω„Ψ΅ΩˆΨ±Ψ© Ψ§Ω„Ω…ΨΉΨ―Ω„Ψ©")
270
+
271
+ generate_img_btn.click(
272
+ fn=generate_img2img,
273
+ inputs=[prompt_img, init_image, strength, steps_img, guidance_img],
274
+ outputs=[output_img2img]
275
+ )
276
 
277
  if __name__ == "__main__":
278
  demo.launch()