eienmojiki commited on
Commit
d85418d
·
1 Parent(s): 76524db

Add vintage, vignette, and HDR effect filters

Browse files

- Introduced three new professional photo filters:
* Vintage: Adds retro color tones and soft blur
* Vignette: Creates darkened image corners
* HDR Effect: Enhances local image details and contrast
- Implemented intensity controls for each filter
- Used advanced image processing techniques with OpenCV
- Registered new filters with default and range parameters
- Improved filter variety and image editing capabilities

Files changed (2) hide show
  1. app.py +79 -47
  2. filters.py +125 -0
app.py CHANGED
@@ -6,67 +6,93 @@ from filters import *
6
  from components import create_filter_controls
7
 
8
  def create_app():
9
- with gr.Blocks(theme="CultriX/gradio-theme") as app:
10
- gr.Markdown("# 📷 Photo Filter App")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  # Khởi tạo components
13
  controls = create_filter_controls()
14
  filter_names = list(registry.filters.keys())
15
-
16
- # Re-initialize controls and filter_names after adding the new filter
17
- controls = create_filter_controls()
18
- filter_names = list(registry.filters.keys())
19
- controls = create_filter_controls()
20
- filter_names = list(registry.filters.keys())
21
-
22
- filter_groups = {} # Store filter groups
23
 
24
- with gr.Row():
25
- with gr.Column():
26
- input_image = gr.Image(label="Input Image", type="numpy")
27
- filter_select = gr.Dropdown(
28
- label="Select Filter",
29
- choices=filter_names,
30
- value="Original"
31
  )
32
 
33
- # Thêm các control vào UI
34
- control_components = []
35
- filter_groups = controls # Assign controls directly to filter_groups
36
- for filter_name, group in filter_groups.items():
37
- for component in group.children: # Iterate over children of the group
38
- control_components.append(component) # Collect sliders for event handling
39
- apply_button = gr.Button("Apply Filter") # Add Apply button
40
-
41
- with gr.Column():
42
- output_image = gr.Image(label="Filtered Image")
43
-
44
- filter_doc = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  # Xử lý cập nhật UI
47
  def update_controls(filter_name):
48
  updates = []
49
  for group_name, group in filter_groups.items():
50
- visibility = (group_name == filter_name)
51
- updates.append(gr.update(visible=visibility)) # Changed from gr.Group.update
52
-
53
- # Get filter documentation
54
- doc = registry.filters[filter_name].__doc__ or "No documentation available."
55
 
 
56
  return updates + [doc]
57
 
58
-
59
- # Xử ảnh khi button click
60
- def process(image, filter_name, *args): # Update process function to take image, filter_name and *args
61
  if image is None:
62
- return None
63
 
64
  try:
65
  image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
66
  params = {}
67
  param_names = list(registry.params_map.get(filter_name, {}).keys())
68
  for i, param_name in enumerate(param_names):
69
- params[param_name] = args[i] # Get parameter values from args
70
 
71
  processed = registry.filters[filter_name](image, **params)
72
 
@@ -75,24 +101,30 @@ def create_app():
75
  else:
76
  processed = cv2.cvtColor(processed, cv2.COLOR_BGR2RGB)
77
 
78
- return processed
79
  except Exception as e:
80
- print(f"Error processing image: {e}")
81
- return image
 
 
 
 
 
 
82
 
83
  # Kết nối sự kiện
84
  filter_select.change(
85
  update_controls,
86
  inputs=filter_select,
87
- outputs=list(filter_groups.values()) + [filter_doc], # Pass list of groups as outputs
88
  api_name=False
89
  )
90
 
91
- input_components = [input_image, filter_select] + control_components # Input components for button click
92
- apply_button.click( # Attach click event to Apply button
93
  process,
94
- inputs=input_components, # Use all input components
95
- outputs=output_image,
96
  )
97
 
98
  return app
 
6
  from components import create_filter_controls
7
 
8
  def create_app():
9
+ # Định nghĩa theme tùy chỉnh
10
+ theme = gr.themes.Soft(
11
+ primary_hue="indigo",
12
+ secondary_hue="slate",
13
+ ).set(
14
+ body_background_fill="*background_fill_secondary",
15
+ block_background_fill="*background_fill_primary",
16
+ block_border_width="0",
17
+ block_shadow="*shadow_drop_lg",
18
+ button_primary_background_fill="*primary_500",
19
+ button_primary_background_fill_hover="*primary_600",
20
+ button_primary_text_color="white",
21
+ )
22
+
23
+ with gr.Blocks(theme=theme) as app:
24
+ gr.Markdown("""
25
+ # 📷 Photo Filter App
26
+ ### Chỉnh sửa ảnh với các bộ lọc chuyên nghiệp
27
+ """)
28
 
29
  # Khởi tạo components
30
  controls = create_filter_controls()
31
  filter_names = list(registry.filters.keys())
32
+ filter_groups = controls
 
 
 
 
 
 
 
33
 
34
+ with gr.Row(equal_height=True):
35
+ with gr.Column(scale=1):
36
+ input_image = gr.Image(
37
+ label="Ảnh gốc",
38
+ type="numpy",
39
+ tool="select",
40
+ height=400
41
  )
42
 
43
+ with gr.Row():
44
+ filter_select = gr.Dropdown(
45
+ label="Chọn bộ lọc",
46
+ choices=filter_names,
47
+ value="Original",
48
+ container=False
49
+ )
50
+ apply_button = gr.Button(
51
+ "Áp dụng",
52
+ variant="primary",
53
+ size="sm"
54
+ )
55
+
56
+ # Tạo container cho các điều khiển
57
+ with gr.Box():
58
+ gr.Markdown("### Tùy chỉnh bộ lọc")
59
+ control_components = []
60
+ for filter_name, group in filter_groups.items():
61
+ for component in group.children:
62
+ control_components.append(component)
63
+
64
+ filter_doc = gr.Markdown(
65
+ container=False,
66
+ show_label=False
67
+ )
68
+
69
+ with gr.Column(scale=1):
70
+ output_image = gr.Image(
71
+ label="Ảnh đã chỉnh sửa",
72
+ height=400,
73
+ show_download_button=True
74
+ )
75
 
76
  # Xử lý cập nhật UI
77
  def update_controls(filter_name):
78
  updates = []
79
  for group_name, group in filter_groups.items():
80
+ updates.append(gr.update(visible=group_name == filter_name))
 
 
 
 
81
 
82
+ doc = registry.filters[filter_name].__doc__ or "Không có mô tả chi tiết."
83
  return updates + [doc]
84
 
85
+ # Xử lý ảnh
86
+ def process(image, filter_name, *args):
 
87
  if image is None:
88
+ return None, gr.update(visible=True, value="⚠️ Vui lòng chọn ảnh trước khi áp dụng bộ lọc")
89
 
90
  try:
91
  image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
92
  params = {}
93
  param_names = list(registry.params_map.get(filter_name, {}).keys())
94
  for i, param_name in enumerate(param_names):
95
+ params[param_name] = args[i]
96
 
97
  processed = registry.filters[filter_name](image, **params)
98
 
 
101
  else:
102
  processed = cv2.cvtColor(processed, cv2.COLOR_BGR2RGB)
103
 
104
+ return processed, gr.update(visible=False)
105
  except Exception as e:
106
+ return None, gr.update(visible=True, value=f" Lỗi xử lý ảnh: {str(e)}")
107
+
108
+ # Thêm thông báo lỗi
109
+ error_message = gr.Markdown(
110
+ visible=False,
111
+ value="",
112
+ container=False
113
+ )
114
 
115
  # Kết nối sự kiện
116
  filter_select.change(
117
  update_controls,
118
  inputs=filter_select,
119
+ outputs=list(filter_groups.values()) + [filter_doc],
120
  api_name=False
121
  )
122
 
123
+ input_components = [input_image, filter_select] + control_components
124
+ apply_button.click(
125
  process,
126
+ inputs=input_components,
127
+ outputs=[output_image, error_message],
128
  )
129
 
130
  return app
filters.py CHANGED
@@ -245,3 +245,128 @@ def adjust_saturation(image, factor: int = 50):
245
  # Convert back to BGR
246
  return cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  # Convert back to BGR
246
  return cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)
247
 
248
+
249
+ @registry.register("Vintage", defaults={
250
+ "intensity": 50,
251
+ }, min_vals={
252
+ "intensity": 0,
253
+ }, max_vals={
254
+ "intensity": 100,
255
+ }, step_vals={
256
+ "intensity": 1,
257
+ })
258
+ def vintage_filter(image, intensity: int = 50):
259
+ """
260
+ ## Adds a vintage/retro effect to the image.
261
+
262
+ **Args:**
263
+ * `image` (numpy.ndarray): Input image (BGR)
264
+ * `intensity` (int): Intensity of the vintage effect (0-100)
265
+
266
+ **Returns:**
267
+ * `numpy.ndarray`: Image with vintage effect
268
+ """
269
+ intensity_scale = intensity / 100.0
270
+
271
+ # Split channels
272
+ b, g, r = cv2.split(image.astype(np.float32))
273
+
274
+ # Adjust colors for vintage look
275
+ r = np.clip(r * (1 + 0.3 * intensity_scale), 0, 255)
276
+ g = np.clip(g * (1 - 0.1 * intensity_scale), 0, 255)
277
+ b = np.clip(b * (1 - 0.2 * intensity_scale), 0, 255)
278
+
279
+ # Create sepia-like effect
280
+ result = cv2.merge([b, g, r]).astype(np.uint8)
281
+
282
+ # Add slight blur for softness
283
+ if intensity > 0:
284
+ blur_amount = int(3 * intensity_scale) * 2 + 1
285
+ result = cv2.GaussianBlur(result, (blur_amount, blur_amount), 0)
286
+
287
+ return result
288
+
289
+
290
+ @registry.register("Vignette", defaults={
291
+ "intensity": 50,
292
+ }, min_vals={
293
+ "intensity": 0,
294
+ }, max_vals={
295
+ "intensity": 100,
296
+ }, step_vals={
297
+ "intensity": 1,
298
+ })
299
+ def vignette_effect(image, intensity: int = 50):
300
+ """
301
+ ## Adds a vignette effect (darker corners) to the image.
302
+
303
+ **Args:**
304
+ * `image` (numpy.ndarray): Input image (BGR)
305
+ * `intensity` (int): Intensity of the vignette (0-100)
306
+
307
+ **Returns:**
308
+ * `numpy.ndarray`: Image with vignette effect
309
+ """
310
+ height, width = image.shape[:2]
311
+
312
+ # Create a vignette mask
313
+ X_resultant = np.abs(np.linspace(-1, 1, width)[None, :])
314
+ Y_resultant = np.abs(np.linspace(-1, 1, height)[:, None])
315
+ mask = np.sqrt(X_resultant**2 + Y_resultant**2)
316
+ mask = 1 - np.clip(mask, 0, 1)
317
+
318
+ # Adjust mask based on intensity
319
+ mask = (mask - mask.min()) / (mask.max() - mask.min())
320
+ mask = mask ** (1 + intensity/50)
321
+
322
+ # Apply mask to image
323
+ mask = mask[:, :, None]
324
+ result = image.astype(np.float32) * mask
325
+
326
+ return np.clip(result, 0, 255).astype(np.uint8)
327
+
328
+
329
+ @registry.register("HDR Effect", defaults={
330
+ "strength": 50,
331
+ }, min_vals={
332
+ "strength": 0,
333
+ }, max_vals={
334
+ "strength": 100,
335
+ }, step_vals={
336
+ "strength": 1,
337
+ })
338
+ def hdr_effect(image, strength: int = 50):
339
+ """
340
+ ## Applies an HDR-like effect to enhance image details.
341
+
342
+ **Args:**
343
+ * `image` (numpy.ndarray): Input image (BGR)
344
+ * `strength` (int): Strength of the HDR effect (0-100)
345
+
346
+ **Returns:**
347
+ * `numpy.ndarray`: Image with HDR-like effect
348
+ """
349
+ strength_scale = strength / 100.0
350
+
351
+ # Convert to LAB color space
352
+ lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB).astype(np.float32)
353
+
354
+ # Split channels
355
+ l, a, b = cv2.split(lab)
356
+
357
+ # Apply CLAHE to L channel
358
+ clahe = cv2.createCLAHE(clipLimit=3.0 * strength_scale, tileGridSize=(8, 8))
359
+ l = clahe.apply(l.astype(np.uint8)).astype(np.float32)
360
+
361
+ # Enhance local contrast
362
+ if strength > 0:
363
+ blur = cv2.GaussianBlur(l, (0, 0), 3)
364
+ detail = cv2.addWeighted(l, 1 + strength_scale, blur, -strength_scale, 0)
365
+ l = cv2.addWeighted(l, 1 - strength_scale/2, detail, strength_scale/2, 0)
366
+
367
+ # Merge channels and convert back
368
+ enhanced_lab = cv2.merge([l, a, b])
369
+ result = cv2.cvtColor(enhanced_lab.astype(np.uint8), cv2.COLOR_LAB2BGR)
370
+
371
+ return result
372
+