dibend commited on
Commit
cd0403b
·
verified ·
1 Parent(s): a8c7c16

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -58
app.py CHANGED
@@ -1,82 +1,101 @@
1
  import gradio as gr
2
  from PIL import Image, ImageOps
3
  import numpy as np
 
4
 
5
- # --- Filter Functions ---
6
- # Each function takes a PIL Image and returns a processed PIL Image.
7
 
8
- def apply_grayscale(image):
9
- """Converts the image to grayscale."""
10
- if image is None:
11
  return None
12
- return ImageOps.grayscale(image.convert("RGB"))
 
13
 
14
- def apply_sepia(image):
15
- """Applies a sepia tone filter to the image."""
16
- if image is None:
17
  return None
18
- img = image.convert("RGB")
19
- width, height = img.size
20
- pixels = img.load()
21
-
22
- for py in range(height):
23
- for px in range(width):
24
- r, g, b = img.getpixel((px, py))
25
- tr = int(0.393 * r + 0.769 * g + 0.189 * b)
26
- tg = int(0.349 * r + 0.686 * g + 0.168 * b)
27
- tb = int(0.272 * r + 0.534 * g + 0.131 * b)
28
- if tr > 255: tr = 255
29
- if tg > 255: tg = 255
30
- if tb > 255: tb = 255
31
- pixels[px, py] = (tr, tg, tb)
32
- return img
33
-
34
- def apply_invert(image):
35
- """Inverts the colors of the image."""
36
- if image is None:
37
- return None
38
- return ImageOps.invert(image.convert("RGB"))
39
 
40
- def apply_posterize(image):
41
- """Reduces the number of bits for each color channel."""
42
- if image is None:
43
  return None
44
- # Posterize to 4 bits per channel
45
- return ImageOps.posterize(image.convert("RGB"), 4)
46
 
47
- def apply_solarize(image):
48
- """Inverts pixel values above a threshold."""
49
- if image is None:
50
  return None
51
- return ImageOps.solarize(image.convert("RGB"), threshold=128)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # --- Main Processing Function ---
54
- def apply_filter(image, filter_name):
55
  """
56
- Dispatcher function that calls the appropriate filter
57
- based on the user's selection.
58
  """
59
  if image is None:
60
  return None
61
 
62
- # Convert Gradio's numpy array to a PIL Image if necessary
63
- if isinstance(image, np.ndarray):
64
- pil_image = Image.fromarray(image)
65
- else:
66
- pil_image = image
67
-
68
  filter_map = {
69
  "Grayscale": apply_grayscale,
70
  "Sepia": apply_sepia,
71
  "Invert": apply_invert,
72
  "Posterize": apply_posterize,
73
  "Solarize": apply_solarize,
74
- "None": lambda img: img # Return original if no filter
75
  }
76
 
77
- # Get the function from the map and apply it
78
  filter_function = filter_map.get(filter_name, lambda img: img)
79
- return filter_function(pil_image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  # --- Gradio UI ---
82
  css = """
@@ -112,9 +131,9 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
112
  webcam_output = gr.Image(label="Filtered Output")
113
  webcam_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
114
 
115
- # When the webcam input or filter changes, apply the filter
116
- webcam_input.stream(apply_filter, [webcam_input, webcam_filter], webcam_output)
117
- webcam_filter.change(apply_filter, [webcam_input, webcam_filter], webcam_output)
118
 
119
  with gr.TabItem("Image File"):
120
  with gr.Row(equal_height=True):
@@ -124,9 +143,9 @@ with gr.Blocks(css=css, theme=gr.themes.Soft()) as demo:
124
  upload_output = gr.Image(label="Filtered Output")
125
  upload_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
126
 
127
- # When the uploaded image or filter changes, apply the filter
128
- upload_input.change(apply_filter, [upload_input, upload_filter], upload_output)
129
- upload_filter.change(apply_filter, [upload_input, upload_filter], upload_output)
130
 
131
 
132
  if __name__ == "__main__":
 
1
  import gradio as gr
2
  from PIL import Image, ImageOps
3
  import numpy as np
4
+ import cv2 # Using OpenCV for fast resizing
5
 
6
+ # --- Optimized Filter Functions ---
7
+ # Each function now takes a NumPy array and returns a processed NumPy array for speed.
8
 
9
+ def apply_grayscale(img_np):
10
+ """Converts the image to grayscale using the luminosity method."""
11
+ if img_np is None:
12
  return None
13
+ # Using dot product for weighted sum is very fast
14
+ return np.dot(img_np[...,:3], [0.2989, 0.5870, 0.1140]).astype(np.uint8)
15
 
16
+ def apply_sepia(img_np):
17
+ """Applies a sepia tone filter using a fast NumPy matrix operation."""
18
+ if img_np is None:
19
  return None
20
+
21
+ # Sepia transformation matrix
22
+ sepia_matrix = np.array([
23
+ [0.393, 0.769, 0.189],
24
+ [0.349, 0.686, 0.168],
25
+ [0.272, 0.534, 0.131]
26
+ ]).T # Transpose for correct dot product with RGB vectors
27
+
28
+ # Apply the matrix transformation and clip to valid 0-255 range
29
+ sepia_img = img_np.dot(sepia_matrix)
30
+ return np.clip(sepia_img, 0, 255).astype(np.uint8)
 
 
 
 
 
 
 
 
 
 
31
 
32
+ def apply_invert(img_np):
33
+ """Inverts the colors of the image using NumPy."""
34
+ if img_np is None:
35
  return None
36
+ return 255 - img_np
 
37
 
38
+ def apply_posterize(img_np):
39
+ """Reduces the number of bits for each color channel using NumPy."""
40
+ if img_np is None:
41
  return None
42
+ # This is a fast way to posterize using bitwise operations
43
+ bits = 4
44
+ shift = 8 - bits
45
+ return ((img_np >> shift) << shift).astype(np.uint8)
46
+
47
+ def apply_solarize(img_np):
48
+ """Inverts pixel values above a threshold using NumPy."""
49
+ if img_np is None:
50
+ return None
51
+ threshold = 128
52
+ # Create a boolean mask for pixels above the threshold
53
+ hot_pixels = img_np > threshold
54
+ # Invert only those pixels
55
+ img_np[hot_pixels] = 255 - img_np[hot_pixels]
56
+ return img_np
57
+
58
+ # --- Main Processing Functions ---
59
 
60
+ def process_static_image(image, filter_name):
 
61
  """
62
+ Processes an uploaded PIL image. No resizing is done.
 
63
  """
64
  if image is None:
65
  return None
66
 
67
+ # Convert PIL Image to NumPy array for processing
68
+ img_np = np.array(image)
69
+
 
 
 
70
  filter_map = {
71
  "Grayscale": apply_grayscale,
72
  "Sepia": apply_sepia,
73
  "Invert": apply_invert,
74
  "Posterize": apply_posterize,
75
  "Solarize": apply_solarize,
76
+ "None": lambda img: img
77
  }
78
 
 
79
  filter_function = filter_map.get(filter_name, lambda img: img)
80
+ processed_np = filter_function(img_np)
81
+
82
+ # Return NumPy array, Gradio will handle conversion to displayable image
83
+ return processed_np
84
+
85
+ def process_live_frame(frame, filter_name):
86
+ """
87
+ Processes a live webcam frame. Includes a resizing step for performance.
88
+ """
89
+ if frame is None:
90
+ return None
91
+
92
+ # --- OPTIMIZATION: Reduce resolution of the live feed ---
93
+ # Resize the frame to 640x480 for faster processing
94
+ resized_frame = cv2.resize(frame, (640, 480), interpolation=cv2.INTER_AREA)
95
+
96
+ # The rest of the logic is the same as the static image processing
97
+ return process_static_image(Image.fromarray(resized_frame), filter_name)
98
+
99
 
100
  # --- Gradio UI ---
101
  css = """
 
131
  webcam_output = gr.Image(label="Filtered Output")
132
  webcam_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
133
 
134
+ # Use the optimized function for live frames
135
+ webcam_input.stream(process_live_frame, [webcam_input, webcam_filter], webcam_output)
136
+ webcam_filter.change(process_live_frame, [webcam_input, webcam_filter], webcam_output)
137
 
138
  with gr.TabItem("Image File"):
139
  with gr.Row(equal_height=True):
 
143
  upload_output = gr.Image(label="Filtered Output")
144
  upload_filter = gr.Radio(filter_choices, value="None", label="Select Filter")
145
 
146
+ # Use the standard function for static images
147
+ upload_input.change(process_static_image, [upload_input, upload_filter], upload_output)
148
+ upload_filter.change(process_static_image, [upload_input, upload_filter], upload_output)
149
 
150
 
151
  if __name__ == "__main__":