b-aryan commited on
Commit
446b3da
·
verified ·
1 Parent(s): 28bca13

Updated app.py

Browse files
Files changed (1) hide show
  1. app.py +132 -62
app.py CHANGED
@@ -67,7 +67,9 @@ def download_image_for_gradio(url: str, timeout: int = 20) -> Image.Image | None
67
  """Downloads an image from a URL for Gradio, returns PIL Image or raises gr.Error."""
68
  print(f"Attempting to download image from: {url}")
69
  if not url or not url.startswith(('http://', 'https://')):
70
- raise gr.Error("Please enter a valid HTTP or HTTPS image URL.")
 
 
71
 
72
  try:
73
  headers = {'User-Agent': 'Gradio-Image-Processor/1.1'}
@@ -108,19 +110,27 @@ def download_image_for_gradio(url: str, timeout: int = 20) -> Image.Image | None
108
 
109
  # --- Processing Function (Handles the ML part with progress) ---
110
  def run_processing(input_pil_image: Image.Image, progress=gr.Progress(track_tqdm=True)):
111
- """Processes the already downloaded PIL image and returns the result."""
112
  if MODEL is None:
113
  # Include the more specific error message if loading failed
114
  error_msg = f"Model is not loaded. Cannot process image. Load error: {MODEL_LOAD_ERROR}" if MODEL_LOAD_ERROR else "Model is not loaded. Cannot process image."
115
  raise gr.Error(error_msg)
116
  if input_pil_image is None:
117
- return None, None
 
 
 
118
 
119
  start_time = time.time()
120
  print("Starting image processing...")
121
  progress(0, desc="Preparing for processing...")
122
 
123
  try:
 
 
 
 
 
124
  output_pil_image = process_image_from_data(
125
  input_pil_image=input_pil_image,
126
  model=MODEL,
@@ -150,26 +160,54 @@ def run_processing(input_pil_image: Image.Image, progress=gr.Progress(track_tqdm
150
 
151
  return output_pil_image, str(save_path)
152
 
153
- # --- Wrapper Function for Button Click ---
154
- def download_and_clear(url):
155
- """Calls download, returns (image, None) on success, raises gr.Error on failure."""
156
- print("Download and clear triggered.") # Debug print
157
- try:
158
- img = download_image_for_gradio(url)
159
- # Return the downloaded image for the original slot, and None for the processed slot
160
- return img, None
161
- except gr.Error as e:
162
- # If download fails, raise the Gradio error again
163
- # Gradio will handle clearing outputs and displaying the error message
164
- print(f"Download failed: {e}") # Debug print
165
- raise e
166
- except Exception as e:
167
- # Catch other unexpected errors during the wrapper logic
168
- print(f"Unexpected error in download_and_clear: {e}") # Debug print
169
- import traceback
170
- traceback.print_exc()
171
- # Raise a Gradio error so the user sees something meaningful
172
- raise gr.Error(f"An unexpected error occurred preparing the image: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
 
175
  def show_image(index):
@@ -192,68 +230,98 @@ with gr.Blocks() as demo:
192
  gr.Markdown(f"""
193
  # Watermark Remover
194
 
195
- **Disclaimer: This project was made to showcase my Deep Learning skills with no intention to cause harm to any business or infringe on any IP and it will be decommissioned as soon as I get a decent job offer.
196
  If you still have any issue, please reach out to me at [[email protected]](mailto:[email protected]).**
197
  """)
198
 
199
- gr.Markdown(f"""
200
- This is a demo of a DL model which takes in URL of an image with watermark and gives out the image with its watermark removed.
201
- The image is broken into overlapping patches of 256x256 pixels with a stride of 64 before feeding them to the model,
202
- the model infers on each patch separately and then they are stitched together to form the whole image again to give the final output.
203
- """)
204
-
205
- if MODEL_LOAD_ERROR:
206
- gr.Markdown(f"<font color='red'>**Model Loading Error:** {MODEL_LOAD_ERROR}</font>")
207
-
208
  with gr.Row():
209
  with gr.Column(scale=1):
 
 
 
 
 
 
 
 
 
 
210
  image_url = gr.Textbox(
211
  label="Image URL",
212
- placeholder="Paste image URL (https://www.shutterstock.com/shutterstock/photos/...)",
213
- info="Enter the direct link to a publicly accessible image file (jpg, png, etc.)."
214
  )
 
 
 
 
 
 
 
 
 
215
  submit_button = gr.Button("Process Image", variant="primary")
216
- gr.Markdown("---") # Separator
217
  gr.Examples(
218
- examples=[
219
- ["https://www.shutterstock.com/shutterstock/photos/2429728793/display_1500/stock-photo-monkey-funny-open-mouth-and-showing-teeth-crazy-nature-angry-short-hair-brown-grand-bassin-2429728793.jpg"],
220
- ["https://www.shutterstock.com/shutterstock/photos/1905929728/display_1500/stock-photo-skeptic-surprised-cat-thinking-dont-know-what-to-do-big-eyes-closeup-tabby-cat-look-side-dont-1905929728.jpg"],
221
- ["https://www.shutterstock.com/shutterstock/photos/2501926843/display_1500/stock-photo-brunette-woman-laying-on-couch-cuddling-light-brown-dog-and-brown-tabby-cat-happy-2501926843.jpg"]
222
- ],
223
- inputs=image_url # Link examples to the URL textbox
 
 
 
224
  )
225
 
226
  with gr.Column(scale=2):
 
 
 
 
 
 
 
 
 
 
 
 
227
  with gr.Row():
 
228
  original_image = gr.Image(label="Original Input Image", type="pil", interactive=False)
229
  processed_image = gr.Image(label="Processed Output Image", type="pil", interactive=False)
230
  download_button = gr.DownloadButton("Download Processed Image", visible=True, variant="secondary")
231
- gr.Markdown("---") # Separator
232
- gr.Markdown(f"""
233
- **A bit about the model:** In this project, I have trained a GAN network,
234
- with the Generator being inspired from Pix2Pix and Pix2PixHD architectures and the Discriminator is very similar to PatchGAN in Pix2Pix and CycleGAN.
235
- For the loss, I have also added Perceptual Loss using VGG like in Pix2PixHD and SRGAN papers apart from the L1 and BCE loss.
236
- """)
237
 
238
- gr.Markdown(f"""If you liked this project, you can find my CV [here]() or reach me out at [[email protected]](mailto:[email protected]).""")
 
 
 
 
 
239
 
 
 
240
 
241
  # --- Event Handling Logic ---
242
 
243
  # 1. When Button is clicked:
244
- # - Input: URL from textbox
245
- # - Action: Call 'download_and_clear' wrapper function
246
- # - Output: Update 'original_image' with downloaded image, clear 'processed_image' by returning None for it.
 
 
 
247
  submit_button.click(
248
- fn=download_and_clear, # <--- CORRECTED: Use the wrapper function
249
- inputs=image_url,
250
- outputs=[original_image, processed_image] # Target both outputs
251
  )
252
 
253
- # 2. When 'original_image' component *changes* (i.e., after successful download):
254
  # - Input: The PIL image data from 'original_image'
255
  # - Action: Call 'run_processing' (this function has the progress bar)
256
- # - Output: Update the 'processed_image' component.
257
  original_image.change(
258
  fn=run_processing,
259
  inputs=original_image,
@@ -261,24 +329,26 @@ with gr.Blocks() as demo:
261
  # concurrency_limit=1 # Optional: Prevent multiple simultaneous processing runs if needed
262
  )
263
 
 
 
264
  processed_image.change(
265
  fn=lambda img: gr.update(variant="primary" if img is not None else "secondary"),
266
  inputs=processed_image,
267
  outputs=download_button
268
  )
269
 
 
 
270
  download_button.click(
271
  fn=lambda path: path if path else None,
272
  inputs=saved_image_path,
273
- outputs=download_button
274
  )
275
 
276
 
277
-
278
  # --- Launch the Application ---
279
  if __name__ == "__main__":
280
  print("Launching Gradio Blocks interface...")
281
  # Set queue=True for better handling under load, especially with long-running processing
282
- # demo.queue() # Consider adding this
283
- demo.launch(share=False, server_name="0.0.0.0", show_api=False)
284
-
 
67
  """Downloads an image from a URL for Gradio, returns PIL Image or raises gr.Error."""
68
  print(f"Attempting to download image from: {url}")
69
  if not url or not url.startswith(('http://', 'https://')):
70
+ # Don't raise error here, let the caller decide if URL is optional
71
+ print("Invalid or empty URL provided.")
72
+ return None # Indicate failure without raising error immediately
73
 
74
  try:
75
  headers = {'User-Agent': 'Gradio-Image-Processor/1.1'}
 
110
 
111
  # --- Processing Function (Handles the ML part with progress) ---
112
  def run_processing(input_pil_image: Image.Image, progress=gr.Progress(track_tqdm=True)):
113
+ """Processes the input PIL image (from upload or download) and returns the result."""
114
  if MODEL is None:
115
  # Include the more specific error message if loading failed
116
  error_msg = f"Model is not loaded. Cannot process image. Load error: {MODEL_LOAD_ERROR}" if MODEL_LOAD_ERROR else "Model is not loaded. Cannot process image."
117
  raise gr.Error(error_msg)
118
  if input_pil_image is None:
119
+ # This case might happen if the input component feeding this function is cleared
120
+ # before processing starts, or if the previous step failed silently.
121
+ print("run_processing called with None input.")
122
+ return None, None # Return None for both outputs
123
 
124
  start_time = time.time()
125
  print("Starting image processing...")
126
  progress(0, desc="Preparing for processing...")
127
 
128
  try:
129
+ # Ensure image is RGB before processing
130
+ if input_pil_image.mode != 'RGB':
131
+ print(f"Converting input image from {input_pil_image.mode} to RGB.")
132
+ input_pil_image = input_pil_image.convert('RGB')
133
+
134
  output_pil_image = process_image_from_data(
135
  input_pil_image=input_pil_image,
136
  model=MODEL,
 
160
 
161
  return output_pil_image, str(save_path)
162
 
163
+ # --- NEW Wrapper Function for Button Click (Handles URL or Upload) ---
164
+ def handle_input(url, uploaded_image):
165
+ """
166
+ Determines the input source (URL or Upload), prepares the image,
167
+ and returns it for the 'original_image' display.
168
+ Clears the other input method and the processed image output.
169
+ Raises gr.Error if input is invalid or download fails.
170
+ """
171
+ print("Handle input triggered.") # Debug print
172
+ input_image = None
173
+ final_url = url # Keep original url unless cleared
174
+ final_uploaded = None # Clear upload by default
175
+
176
+ if uploaded_image is not None:
177
+ print("Processing uploaded image.")
178
+ input_image = uploaded_image
179
+ final_url = "" # Clear URL field if upload is used
180
+
181
+ elif url:
182
+ print("Processing URL.")
183
+ try:
184
+ input_image = download_image_for_gradio(url)
185
+ if input_image is None:
186
+ # download_image_for_gradio returns None for non-http/https URLs
187
+ raise gr.Error("Invalid URL provided. Please enter a valid HTTP or HTTPS image URL.")
188
+ # Keep the url in the box if download succeeds
189
+ except gr.Error as e:
190
+ # If download fails, raise the Gradio error again
191
+ print(f"Download failed: {e}") # Debug print
192
+ raise e
193
+ except Exception as e:
194
+ # Catch other unexpected errors during the wrapper logic
195
+ print(f"Unexpected error in handle_input (download branch): {e}") # Debug print
196
+ import traceback
197
+ traceback.print_exc()
198
+ # Raise a Gradio error so the user sees something meaningful
199
+ raise gr.Error(f"An unexpected error occurred preparing the image from URL: {e}")
200
+ else:
201
+ # Neither URL nor upload provided
202
+ raise gr.Error("Please provide an image URL or upload an image file.")
203
+
204
+ # At this point, input_image should be a valid PIL image or an error was raised.
205
+ # Return:
206
+ # 1. The input PIL image for 'original_image'
207
+ # 2. None to clear 'processed_image'
208
+ # 3. The final URL value (empty if upload was used, original if URL was used)
209
+ # 4. None to clear the file upload input
210
+ return input_image, None, final_url, final_uploaded
211
 
212
 
213
  def show_image(index):
 
230
  gr.Markdown(f"""
231
  # Watermark Remover
232
 
233
+ **Disclaimer: This project was made to showcase my Deep Learning skills with no intention to cause harm to any business or infringe on any IP and it will be decommissioned as soon as I get a decent job offer.
234
  If you still have any issue, please reach out to me at [[email protected]](mailto:[email protected]).**
235
  """)
236
 
 
 
 
 
 
 
 
 
 
237
  with gr.Row():
238
  with gr.Column(scale=1):
239
+ gr.Markdown(f"""
240
+ This is a demo of a DL model which takes in an image with a watermark (either via URL or direct upload) and gives out the image with its watermark removed.
241
+ The image is broken into overlapping patches of {PATCH_KERNEL_SIZE}x{PATCH_KERNEL_SIZE} pixels with a stride of {PATCH_STRIDE} before feeding them to the model,
242
+ the model infers on each patch separately and then they are stitched together to form the whole image again to give the final output.
243
+ """)
244
+
245
+ if MODEL_LOAD_ERROR:
246
+ gr.Markdown(f"<font color='red'>**Model Loading Error:** {MODEL_LOAD_ERROR}</font>")
247
+
248
+ # Input Method 1: URL
249
  image_url = gr.Textbox(
250
  label="Image URL",
251
+ placeholder="Paste image URL (e.g., https://www.shutterstock.com/shutterstock/photos/.../image.jpg)",
252
+ info="Enter the direct link to a publicly accessible image file."
253
  )
254
+
255
+ # Input Method 2: File Upload
256
+ file_upload_input = gr.Image(
257
+ label="Or Upload Image File",
258
+ type="pil", # Keep as PIL for direct use
259
+ sources=["upload"], # Specify only upload source
260
+ height=130
261
+ )
262
+
263
  submit_button = gr.Button("Process Image", variant="primary")
264
+ gr.Markdown("---") # Separator
265
  gr.Examples(
266
+ examples=[
267
+ [
268
+ "https://www.shutterstock.com/shutterstock/photos/2429728793/display_1500/stock-photo-monkey-funny-open-mouth-and-showing-teeth-crazy-nature-angry-short-hair-brown-grand-bassin-2429728793.jpg", None], # Need None for the upload input
269
+ [
270
+ "https://www.shutterstock.com/shutterstock/photos/1905929728/display_1500/stock-photo-skeptic-surprised-cat-thinking-dont-know-what-to-do-big-eyes-closeup-tabby-cat-look-side-dont-1905929728.jpg", None],
271
+ [
272
+ "https://www.shutterstock.com/shutterstock/photos/2501926843/display_1500/stock-photo-brunette-woman-laying-on-couch-cuddling-light-brown-dog-and-brown-tabby-cat-happy-2501926843.jpg", None]
273
+ ],
274
+ inputs=[image_url, file_upload_input] # Link examples to both inputs
275
  )
276
 
277
  with gr.Column(scale=2):
278
+ gr.Markdown(f"""### Some Samples""")
279
+ img_display = gr.Image(type="pil")
280
+ with gr.Row():
281
+ prev_btn = gr.Button("<")
282
+ next_btn = gr.Button(">")
283
+
284
+ prev_btn.click(fn=prev_img, inputs=index_state, outputs=[img_display, index_state])
285
+ next_btn.click(fn=next_img, inputs=index_state, outputs=[img_display, index_state])
286
+
287
+ # Load the first image
288
+ demo.load(fn=show_image, inputs=index_state, outputs=[img_display, index_state])
289
+
290
  with gr.Row():
291
+ # Make original_image interactive=False as it's now purely a display
292
  original_image = gr.Image(label="Original Input Image", type="pil", interactive=False)
293
  processed_image = gr.Image(label="Processed Output Image", type="pil", interactive=False)
294
  download_button = gr.DownloadButton("Download Processed Image", visible=True, variant="secondary")
 
 
 
 
 
 
295
 
296
+ gr.Markdown("---") # Separator
297
+ gr.Markdown(f"""
298
+ **A bit about the model:** In this project, I have trained a GAN network,
299
+ with the Generator being inspired from Pix2Pix and Pix2PixHD architectures and the Discriminator is very similar to PatchGAN in Pix2Pix.
300
+ For the loss, I have also added Perceptual Loss using VGG like in Pix2PixHD and SRGAN papers apart from the L1 and BCE loss.
301
+ """)
302
 
303
+ gr.Markdown(
304
+ f"""If you liked this project, you can find my CV [here]() or reach me out at [[email protected]](mailto:[email protected]).""")
305
 
306
  # --- Event Handling Logic ---
307
 
308
  # 1. When Button is clicked:
309
+ # - Input: URL from textbox AND image from file upload
310
+ # - Action: Call 'handle_input' wrapper function to decide which input to use,
311
+ # download if needed, and handle errors.
312
+ # - Output: Update 'original_image' with the chosen/downloaded image,
313
+ # clear 'processed_image', clear the URL field if upload was used,
314
+ # clear the file upload component.
315
  submit_button.click(
316
+ fn=handle_input,
317
+ inputs=[image_url, file_upload_input],
318
+ outputs=[original_image, processed_image, image_url, file_upload_input] # Target outputs
319
  )
320
 
321
+ # 2. When 'original_image' component *changes* (i.e., after successful input handling):
322
  # - Input: The PIL image data from 'original_image'
323
  # - Action: Call 'run_processing' (this function has the progress bar)
324
+ # - Output: Update the 'processed_image' component and the saved_image_path state.
325
  original_image.change(
326
  fn=run_processing,
327
  inputs=original_image,
 
329
  # concurrency_limit=1 # Optional: Prevent multiple simultaneous processing runs if needed
330
  )
331
 
332
+ # 3. When 'processed_image' changes (after processing finishes or is cleared):
333
+ # - Action: Update the visibility/style of the download button.
334
  processed_image.change(
335
  fn=lambda img: gr.update(variant="primary" if img is not None else "secondary"),
336
  inputs=processed_image,
337
  outputs=download_button
338
  )
339
 
340
+ # 4. When Download Button is clicked:
341
+ # - Action: Provide the file path stored in saved_image_path state to the download component.
342
  download_button.click(
343
  fn=lambda path: path if path else None,
344
  inputs=saved_image_path,
345
+ outputs=download_button # The output of the click event for DownloadButton is the file path itself
346
  )
347
 
348
 
 
349
  # --- Launch the Application ---
350
  if __name__ == "__main__":
351
  print("Launching Gradio Blocks interface...")
352
  # Set queue=True for better handling under load, especially with long-running processing
353
+ demo.queue() # Use queue for better user experience with processing time
354
+ demo.launch(share=False, server_name="0.0.0.0", show_api=False)