dibend commited on
Commit
fb2df62
·
verified ·
1 Parent(s): 1f87340

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -58
app.py CHANGED
@@ -5,38 +5,36 @@ import cv2 # Using OpenCV for fast resizing and filtering
5
  import requests
6
  import os
7
 
8
- # --- Face Detection Setup ---
9
- # Download the Haar Cascade model for face detection if it doesn't exist
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  haar_cascade_url = "https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml"
11
  haar_cascade_filename = "haarcascade_frontalface_default.xml"
12
- if not os.path.exists(haar_cascade_filename):
13
- print(f"Downloading {haar_cascade_filename}...")
14
- try:
15
- r = requests.get(haar_cascade_url, timeout=10)
16
- r.raise_for_status() # Raise an exception for bad status codes
17
- with open(haar_cascade_filename, 'wb') as f:
18
- f.write(r.content)
19
- print("Download complete.")
20
- face_cascade = cv2.CascadeClassifier(haar_cascade_filename)
21
- except requests.exceptions.RequestException as e:
22
- print(f"Error downloading Haar Cascade model: {e}")
23
- face_cascade = None
24
- else:
25
  face_cascade = cv2.CascadeClassifier(haar_cascade_filename)
26
 
27
-
28
- # Create a sunglasses image with transparency programmatically
29
- def create_sunglasses_mask():
30
- sunglasses = Image.new('RGBA', (300, 100), (0, 0, 0, 0))
31
- draw = ImageDraw.Draw(sunglasses)
32
- # Lenses
33
- draw.ellipse((20, 20, 130, 80), fill='black')
34
- draw.ellipse((170, 20, 280, 80), fill='black')
35
- # Bridge
36
- draw.line((130, 50, 170, 50), fill='black', width=10)
37
- return sunglasses
38
-
39
- sunglasses_img = create_sunglasses_mask()
40
 
41
 
42
  # --- Optimized Filter Functions ---
@@ -91,13 +89,11 @@ def apply_sharpen(img_np):
91
  return cv2.filter2D(img_np, -1, kernel)
92
 
93
  def apply_sunglasses(img_np):
94
- """Detects faces and overlays sunglasses."""
95
  if img_np is None or face_cascade is None: return img_np
96
-
97
  pil_image = Image.fromarray(img_np)
98
  gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
99
  faces = face_cascade.detectMultiScale(gray, 1.3, 5)
100
-
101
  for (x, y, w, h) in faces:
102
  sunglasses_width = int(w * 0.9)
103
  sunglasses_height = int(sunglasses_width * sunglasses_img.height / sunglasses_img.width)
@@ -105,73 +101,115 @@ def apply_sunglasses(img_np):
105
  pos_y = y + int(h * 0.2)
106
  resized_sunglasses = sunglasses_img.resize((sunglasses_width, sunglasses_height))
107
  pil_image.paste(resized_sunglasses, (pos_x, pos_y), resized_sunglasses)
108
-
109
  return np.array(pil_image)
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
  # --- Main Processing Functions ---
113
- def process_static_image(image, filter_name):
114
  if image is None: return None
115
- img_np = np.array(image.convert("RGB"))
116
 
 
 
 
 
 
 
117
  filter_map = {
118
  "Grayscale": apply_grayscale, "Sepia": apply_sepia, "Invert": apply_invert,
119
  "Posterize": apply_posterize, "Solarize": apply_solarize, "Vignette": apply_vignette,
120
  "Contour": apply_contour, "Sharpen": apply_sharpen, "Sunglasses": apply_sunglasses,
121
- "None": lambda img: img
122
  }
123
 
124
  filter_function = filter_map.get(filter_name, lambda img: img)
125
- processed_np = filter_function(img_np)
126
- return processed_np
127
 
128
  def process_live_frame(frame, filter_name):
129
  if frame is None: return None
130
  resized_frame = cv2.resize(frame, (640, 480), interpolation=cv2.INTER_AREA)
131
- return process_static_image(Image.fromarray(resized_frame), filter_name)
132
-
133
 
134
  # --- Gradio UI ---
135
  css = """
136
  #title { text-align: center; color: #1d1e22; font-size: 2.8em; font-weight: 700; }
137
  #subtitle { text-align: center; color: #57606a; font-size: 1.2em; }
138
- .gradio-container { max-width: 980px !important; margin: auto !important; }
139
  """
140
 
141
  with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
142
  gr.Markdown("# Image Filters: Old, New & Face Effects", elem_id="title")
143
  gr.Markdown("Apply classic and modern filters to your images or live webcam feed.", elem_id="subtitle")
144
 
145
- filter_choices = [
146
- "None",
147
- "--- Old School ---", "Grayscale", "Sepia", "Invert", "Posterize", "Solarize",
148
- "--- New School ---", "Vignette", "Contour", "Sharpen",
149
- "--- Face Effects ---", "Sunglasses"
150
- ]
151
-
152
  with gr.Tabs():
153
  with gr.TabItem("Live Webcam"):
154
  with gr.Row(equal_height=True):
155
- with gr.Column():
156
  webcam_input = gr.Image(sources=["webcam"], streaming=True, label="Webcam Input")
157
- with gr.Column():
 
 
 
 
 
 
 
 
158
  webcam_output = gr.Image(label="Filtered Output")
159
- webcam_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
160
 
161
- webcam_input.stream(process_live_frame, [webcam_input, webcam_filter], webcam_output)
162
- webcam_filter.change(process_live_frame, [webcam_input, webcam_filter], webcam_output)
 
 
 
163
 
164
  with gr.TabItem("Image File"):
165
  with gr.Row(equal_height=True):
166
- with gr.Column():
167
  upload_input = gr.Image(type="pil", sources=["upload"], label="Upload an Image")
168
- with gr.Column():
 
 
 
 
 
 
 
 
169
  upload_output = gr.Image(label="Filtered Output")
170
- upload_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
171
-
172
- upload_input.change(process_static_image, [upload_input, upload_filter], upload_output)
173
- upload_filter.change(process_static_image, [upload_input, upload_filter], upload_output)
174
 
 
 
 
 
 
 
 
 
 
175
 
176
  if __name__ == "__main__":
177
  demo.launch(debug=True)
 
5
  import requests
6
  import os
7
 
8
+ # --- Asset Setup ---
9
+ # Function to download a file from a URL
10
+ def download_file(url, filename):
11
+ if not os.path.exists(filename):
12
+ print(f"Downloading {filename}...")
13
+ try:
14
+ r = requests.get(url, timeout=10)
15
+ r.raise_for_status()
16
+ with open(filename, 'wb') as f:
17
+ f.write(r.content)
18
+ print("Download complete.")
19
+ return True
20
+ except requests.exceptions.RequestException as e:
21
+ print(f"Error downloading {filename}: {e}")
22
+ return False
23
+ return True
24
+
25
+ # Download Haar Cascade for face detection
26
  haar_cascade_url = "https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml"
27
  haar_cascade_filename = "haarcascade_frontalface_default.xml"
28
+ face_cascade = None
29
+ if download_file(haar_cascade_url, haar_cascade_filename):
 
 
 
 
 
 
 
 
 
 
 
30
  face_cascade = cv2.CascadeClassifier(haar_cascade_filename)
31
 
32
+ # Download Metamask logo for face effect
33
+ metamask_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/MetaMask_Fox.svg/1024px-MetaMask_Fox.svg.png"
34
+ metamask_filename = "metamask_logo.png"
35
+ metamask_img = None
36
+ if download_file(metamask_url, metamask_filename):
37
+ metamask_img = Image.open(metamask_filename).convert("RGBA")
 
 
 
 
 
 
 
38
 
39
 
40
  # --- Optimized Filter Functions ---
 
89
  return cv2.filter2D(img_np, -1, kernel)
90
 
91
  def apply_sunglasses(img_np):
 
92
  if img_np is None or face_cascade is None: return img_np
93
+ sunglasses_img = create_sunglasses_mask()
94
  pil_image = Image.fromarray(img_np)
95
  gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
96
  faces = face_cascade.detectMultiScale(gray, 1.3, 5)
 
97
  for (x, y, w, h) in faces:
98
  sunglasses_width = int(w * 0.9)
99
  sunglasses_height = int(sunglasses_width * sunglasses_img.height / sunglasses_img.width)
 
101
  pos_y = y + int(h * 0.2)
102
  resized_sunglasses = sunglasses_img.resize((sunglasses_width, sunglasses_height))
103
  pil_image.paste(resized_sunglasses, (pos_x, pos_y), resized_sunglasses)
 
104
  return np.array(pil_image)
105
 
106
+ def apply_metamask_head(img_np):
107
+ """Detects faces and overlays a Metamask logo."""
108
+ if img_np is None or face_cascade is None or metamask_img is None: return img_np
109
+ pil_image = Image.fromarray(img_np)
110
+ gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
111
+ faces = face_cascade.detectMultiScale(gray, 1.3, 5)
112
+ for (x, y, w, h) in faces:
113
+ # Make the logo slightly larger than the face and position it
114
+ logo_width = int(w * 1.4)
115
+ logo_height = int(logo_width * metamask_img.height / metamask_img.width)
116
+ pos_x = x - int((logo_width - w) / 2)
117
+ pos_y = y - int(logo_height * 0.2) # Position it slightly higher
118
+ resized_logo = metamask_img.resize((logo_width, logo_height))
119
+ pil_image.paste(resized_logo, (pos_x, pos_y), resized_logo)
120
+ return np.array(pil_image)
121
+
122
+ # Helper for sunglasses
123
+ def create_sunglasses_mask():
124
+ sunglasses = Image.new('RGBA', (300, 100), (0, 0, 0, 0))
125
+ draw = ImageDraw.Draw(sunglasses)
126
+ draw.ellipse((20, 20, 130, 80), fill='black')
127
+ draw.ellipse((170, 20, 280, 80), fill='black')
128
+ draw.line((130, 50, 170, 50), fill='black', width=10)
129
+ return sunglasses
130
 
131
  # --- Main Processing Functions ---
132
+ def process_image(image, filter_name):
133
  if image is None: return None
 
134
 
135
+ # Convert PIL Image to NumPy array for processing
136
+ if isinstance(image, Image.Image):
137
+ img_np = np.array(image.convert("RGB"))
138
+ else: # It's already a numpy array from the webcam
139
+ img_np = image
140
+
141
  filter_map = {
142
  "Grayscale": apply_grayscale, "Sepia": apply_sepia, "Invert": apply_invert,
143
  "Posterize": apply_posterize, "Solarize": apply_solarize, "Vignette": apply_vignette,
144
  "Contour": apply_contour, "Sharpen": apply_sharpen, "Sunglasses": apply_sunglasses,
145
+ "Metamask Head": apply_metamask_head, "None": lambda img: img
146
  }
147
 
148
  filter_function = filter_map.get(filter_name, lambda img: img)
149
+ return filter_function(img_np)
 
150
 
151
  def process_live_frame(frame, filter_name):
152
  if frame is None: return None
153
  resized_frame = cv2.resize(frame, (640, 480), interpolation=cv2.INTER_AREA)
154
+ return process_image(resized_frame, filter_name)
 
155
 
156
  # --- Gradio UI ---
157
  css = """
158
  #title { text-align: center; color: #1d1e22; font-size: 2.8em; font-weight: 700; }
159
  #subtitle { text-align: center; color: #57606a; font-size: 1.2em; }
160
+ .gradio-container { max-width: 1080px !important; margin: auto !important; }
161
  """
162
 
163
  with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
164
  gr.Markdown("# Image Filters: Old, New & Face Effects", elem_id="title")
165
  gr.Markdown("Apply classic and modern filters to your images or live webcam feed.", elem_id="subtitle")
166
 
 
 
 
 
 
 
 
167
  with gr.Tabs():
168
  with gr.TabItem("Live Webcam"):
169
  with gr.Row(equal_height=True):
170
+ with gr.Column(scale=2):
171
  webcam_input = gr.Image(sources=["webcam"], streaming=True, label="Webcam Input")
172
+ with gr.Column(scale=1):
173
+ gr.Markdown("### Filter Controls")
174
+ with gr.Accordion("Old School", open=True):
175
+ webcam_radio_old = gr.Radio(["None", "Grayscale", "Sepia", "Invert", "Posterize", "Solarize"], label="Filter", value="None")
176
+ with gr.Accordion("New School", open=True):
177
+ webcam_radio_new = gr.Radio(["None", "Vignette", "Contour", "Sharpen"], label="Filter", value="None")
178
+ with gr.Accordion("Face Effects", open=True):
179
+ webcam_radio_face = gr.Radio(["None", "Sunglasses", "Metamask Head"], label="Filter", value="None")
180
+ with gr.Column(scale=2):
181
  webcam_output = gr.Image(label="Filtered Output")
 
182
 
183
+ # Link radio buttons to the stream
184
+ webcam_radio_old.change(process_live_frame, [webcam_input, webcam_radio_old], webcam_output).then(lambda: "None", None, webcam_radio_new).then(lambda: "None", None, webcam_radio_face)
185
+ webcam_radio_new.change(process_live_frame, [webcam_input, webcam_radio_new], webcam_output).then(lambda: "None", None, webcam_radio_old).then(lambda: "None", None, webcam_radio_face)
186
+ webcam_radio_face.change(process_live_frame, [webcam_input, webcam_radio_face], webcam_output).then(lambda: "None", None, webcam_radio_old).then(lambda: "None", None, webcam_radio_new)
187
+ webcam_input.stream(lambda img, r1, r2, r3: process_live_frame(img, next(f for f in (r1,r2,r3) if f != "None")), [webcam_input, webcam_radio_old, webcam_radio_new, webcam_radio_face], webcam_output)
188
 
189
  with gr.TabItem("Image File"):
190
  with gr.Row(equal_height=True):
191
+ with gr.Column(scale=2):
192
  upload_input = gr.Image(type="pil", sources=["upload"], label="Upload an Image")
193
+ with gr.Column(scale=1):
194
+ gr.Markdown("### Filter Controls")
195
+ with gr.Accordion("Old School", open=True):
196
+ upload_radio_old = gr.Radio(["None", "Grayscale", "Sepia", "Invert", "Posterize", "Solarize"], label="Filter", value="None")
197
+ with gr.Accordion("New School", open=True):
198
+ upload_radio_new = gr.Radio(["None", "Vignette", "Contour", "Sharpen"], label="Filter", value="None")
199
+ with gr.Accordion("Face Effects", open=True):
200
+ upload_radio_face = gr.Radio(["None", "Sunglasses", "Metamask Head"], label="Filter", value="None")
201
+ with gr.Column(scale=2):
202
  upload_output = gr.Image(label="Filtered Output")
 
 
 
 
203
 
204
+ # Link radio buttons to the static image processor
205
+ def upload_filter_change(img, r1, r2, r3):
206
+ active_filter = next((f for f in (r1, r2, r3) if f != "None"), "None")
207
+ return process_image(img, active_filter)
208
+
209
+ upload_radio_old.change(upload_filter_change, [upload_input, upload_radio_old, upload_radio_new, upload_radio_face], upload_output).then(lambda: "None", None, upload_radio_new).then(lambda: "None", None, upload_radio_face)
210
+ upload_radio_new.change(upload_filter_change, [upload_input, upload_radio_old, upload_radio_new, upload_radio_face], upload_output).then(lambda: "None", None, upload_radio_old).then(lambda: "None", None, upload_radio_face)
211
+ upload_radio_face.change(upload_filter_change, [upload_input, upload_radio_old, upload_radio_new, upload_radio_face], upload_output).then(lambda: "None", None, upload_radio_old).then(lambda: "None", None, upload_radio_new)
212
+ upload_input.change(upload_filter_change, [upload_input, upload_radio_old, upload_radio_new, upload_radio_face], upload_output)
213
 
214
  if __name__ == "__main__":
215
  demo.launch(debug=True)