random2222 commited on
Commit
08237b0
·
verified ·
1 Parent(s): 0fa40c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +483 -510
app.py CHANGED
@@ -7,551 +7,524 @@ import tempfile
7
  from typing import Union, Tuple
8
 
9
  # Custom CSS for styling the interface
10
-
11
  custom_css = """
12
  .container {
13
- max-width: 1200px;
14
- margin: 0 auto;
15
  }
16
- /\* Main styling */
17
  .gradio-container {
18
- font-family: 'Roboto', 'Segoe UI', sans-serif;
19
- color: white;
20
  }
21
  /* Card styling */
22
  .app-card {
23
- border-radius: 12px;
24
- box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
25
- padding: 20px;
26
- background: linear-gradient(135deg, #2a3a4a 0%, #1e2a3a 100%);
27
- margin-bottom: 20px;
28
  }
29
  /* Header styling */
30
  h1, h2, h3 {
31
- font-weight: 700 !important;
32
- color: white !important;
33
  }
34
  /* Labels styling */
35
  label, .label {
36
- font-size: 1rem !important;
37
- font-weight: 600 !important;
38
- color: white !important;
39
- margin-bottom: 6px !important;
40
  }
41
  /* Input and slider styling */
42
  .slider-label {
43
- font-weight: 600 !important;
44
- color: white !important;
45
- font-size: 0.95rem !important;
46
  }
47
  /* Button styling */
48
  button.primary {
49
- background: linear-gradient(135deg, #3498db, #2980b9) !important;
50
- color: white !important;
51
- font-weight: 600 !important;
52
- border-radius: 8px !important;
53
- padding: 12px 24px !important;
54
- font-size: 1.1rem !important;
55
- transition: all 0.3s ease !important;
56
- border: none !important;
57
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 0 10px rgba(52, 152, 219, 0.4) !important;
58
  }
59
- button.primary\:hover {
60
- background: linear-gradient(135deg, #2980b9, #2573a7) !important;
61
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 0 15px rgba(52, 152, 219, 0.6) !important;
62
- transform: translateY(-2px) !important;
63
  }
64
  /* Radio buttons */
65
  .radio-group label {
66
- font-weight: 600 !important;
67
- color: white !important;
68
  }
69
  /* Tab styling */
70
  .tab-nav {
71
- font-weight: 600 !important;
72
- font-size: 1.05rem !important;
73
  }
74
- /* Responsive adjustments \*/
75
  @media (max-width: 768px) {
76
- .gradio-container {
77
- padding: 10px !important;
78
- }
79
-
80
- ```
81
- label, .label {
82
- font-size: 0.95rem !important;
83
- }
84
-
85
- button.primary {
86
- padding: 10px 18px !important;
87
- font-size: 1rem !important;
88
- }
89
- ```
90
-
91
  }
92
  """
93
 
94
  # Enable OpenCL for better performance
95
-
96
  cv2.ocl.setUseOpenCL(True)
97
 
98
- # ------------------- Theme Setup -------------------
99
-
100
- def create\_custom\_theme():
101
- """Create a custom dark theme for the interface"""
102
- return gr.themes.Base().set(
103
- body\_background\_fill="linear-gradient(to bottom right, #1a1f2c, #121620)",
104
- body\_background\_fill\_dark="linear-gradient(to bottom right, #1a1f2c, #121620)",
105
- body\_text\_color="white",
106
- body\_text\_color\_dark="white",
107
- button\_primary\_background\_fill="linear-gradient(135deg, #3498db, #2980b9)",
108
- button\_primary\_background\_fill\_hover="linear-gradient(135deg, #2980b9, #2573a7)",
109
- button\_primary\_text\_color="white",
110
- button\_primary\_text\_color\_dark="white",
111
- button\_primary\_border\_color="transparent",
112
- button\_primary\_border\_color\_dark="transparent",
113
- button\_secondary\_background\_fill="#34495e",
114
- button\_secondary\_background\_fill\_hover="#2c3e50",
115
- button\_secondary\_text\_color="white",
116
- button\_secondary\_text\_color\_dark="white",
117
- block\_title\_text\_color="white",
118
- block\_title\_text\_color\_dark="white",
119
- block\_label\_text\_color="white",
120
- block\_label\_text\_color\_dark="white",
121
- slider\_color="#3498db",
122
- slider\_color\_dark="#3498db",
123
- border\_color\_primary="#3498db",
124
- border\_color\_primary\_dark="#3498db",
125
- background\_fill\_primary="#2a3a4a",
126
- background\_fill\_primary\_dark="#2a3a4a",
127
- background\_fill\_secondary="#1e2a3a",
128
- background\_fill\_secondary\_dark="#1e2a3a",
129
- border\_radius\_size="12px",
130
- spacing\_md="12px",
131
- spacing\_lg="16px",
132
- text\_size="16px",
133
- text\_md="18px",
134
- text\_lg="20px",
135
- text\_xl="24px",
136
- font=\["Roboto", "ui-sans-serif", "system-ui", "sans-serif"],
137
- )
138
-
139
- # ------------------- Black & White Converter Functions -------------------
140
-
141
- def convert\_to\_black\_white(image, threshold\_value=127, method="otsu"):
142
- """Convert image to black and white using specified thresholding method"""
143
- if isinstance(image, str):
144
- image = cv2.imread(image)
145
-
146
- ```
147
- # Convert to grayscale if not already
148
- if len(image.shape) == 3:
149
- gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
150
- else:
151
- gray = image
152
-
153
- if method == "adaptive":
154
- binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
155
- cv2.THRESH_BINARY, 11, 2)
156
- elif method == "otsu":
157
- _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
158
- else:
159
- _, binary = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY)
160
-
161
- return binary
162
- ```
163
-
164
- def process\_image\_bw(image, threshold\_value, method):
165
- """Process image with black and white thresholding"""
166
- if image is None:
167
- return None
168
-
169
- ```
170
- # Convert to numpy array if PIL Image
171
- if isinstance(image, Image.Image):
172
- image_np = np.array(image)
173
- # Convert RGB to BGR for OpenCV
174
- if len(image_np.shape) == 3:
175
- image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
176
- else:
177
- image_np = image
178
-
179
- result = convert_to_black_white(image_np, threshold_value, method)
180
- return result
181
- ```
182
-
183
- def process\_video\_bw(video\_path, threshold\_value, method):
184
- """Process video with black and white thresholding"""
185
- if not os.path.exists(video\_path):
186
- return "Video file not found", None
187
-
188
- ```
189
- try:
190
- cap = cv2.VideoCapture(video_path)
191
- if not cap.isOpened():
192
- return "Could not open video file", None
193
-
194
- # Get video properties
195
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
196
- frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
197
- frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
198
- fps = int(cap.get(cv2.CAP_PROP_FPS))
199
-
200
- # Create temporary output file
201
- temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
202
- output_path = temp_output.name
203
- temp_output.close()
204
-
205
- # Create video writer
206
- out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=False)
207
-
208
- # Process each frame
209
- while cap.isOpened():
210
- ret, frame = cap.read()
211
- if not ret:
212
- break
213
-
214
- bw_frame = convert_to_black_white(frame, threshold_value, method)
215
- out.write(bw_frame)
216
-
217
- cap.release()
218
- out.release()
219
-
220
- return "Video processed successfully", output_path
221
- except Exception as e:
222
- return f"Error processing video: {str(e)}", None
223
- ```
224
-
225
- # ------------------- Pencil Sketch Converter Functions -------------------
226
-
227
- def process\_image\_sketch(image, intensity=255, blur\_ksize=21, sigma=0):
228
- """Convert image to pencil sketch effect"""
229
- if image is None:
230
- return None
231
-
232
- ```
233
- # Convert to numpy array if PIL Image
234
- if isinstance(image, Image.Image):
235
- image_np = np.array(image)
236
- # Convert RGB to BGR for OpenCV
237
- if len(image_np.shape) == 3:
238
- image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
239
- else:
240
- image_np = image
241
-
242
- # Convert to grayscale
243
- gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) if len(image_np.shape) == 3 else image_np
244
-
245
- # Create sketch effect
246
- inverted = cv2.bitwise_not(gray)
247
- blur_ksize = blur_ksize if blur_ksize % 2 == 1 else blur_ksize + 1 # Ensure kernel size is odd
248
- blurred = cv2.GaussianBlur(inverted, (blur_ksize, blur_ksize), sigma)
249
- sketch = cv2.divide(gray, cv2.bitwise_not(blurred), scale=intensity)
250
-
251
- return sketch
252
- ```
253
-
254
- def process\_video\_sketch(video\_path, intensity=255, blur\_ksize=21, sigma=0):
255
- """Process video with pencil sketch effect"""
256
- if not os.path.exists(video\_path):
257
- return "Video file not found", None
258
-
259
- ```
260
- try:
261
- cap = cv2.VideoCapture(video_path)
262
- if not cap.isOpened():
263
- return "Could not open video file", None
264
-
265
- # Get video properties
266
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
267
- frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
268
- frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
269
- fps = int(cap.get(cv2.CAP_PROP_FPS))
270
-
271
- # Create temporary output file
272
- temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
273
- output_path = temp_output.name
274
- temp_output.close()
275
-
276
- # Create video writer
277
- out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=True)
278
-
279
- # Process each frame
280
- while cap.isOpened():
281
- ret, frame = cap.read()
282
- if not ret:
283
- break
284
-
285
- sketch_frame = process_image_sketch(frame, intensity, blur_ksize, sigma)
286
- # Convert grayscale to BGR for video output
287
- sketch_bgr = cv2.cvtColor(sketch_frame, cv2.COLOR_GRAY2BGR)
288
- out.write(sketch_bgr)
289
-
290
- cap.release()
291
- out.release()
292
-
293
- return "Video processed successfully", output_path
294
- except Exception as e:
295
- return f"Error processing video: {str(e)}", None
296
- ```
297
-
298
- # ------------------- Gradio Interface Functions -------------------
299
-
300
- def black\_white\_image(image, threshold\_method, threshold\_value):
301
- """Process image with black and white filter for Gradio"""
302
- if threshold\_method != "manual":
303
- threshold\_value = 0 # Not used for adaptive or Otsu
304
-
305
- ```
306
- result = process_image_bw(image, threshold_value, threshold_method)
307
- return Image.fromarray(result)
308
- ```
309
-
310
- def black\_white\_video(video, threshold\_method, threshold\_value):
311
- """Process video with black and white filter for Gradio"""
312
- if threshold\_method != "manual":
313
- threshold\_value = 0 # Not used for adaptive or Otsu
314
-
315
- ```
316
- message, output_path = process_video_bw(video, threshold_value, threshold_method)
317
- if output_path:
318
- return output_path
319
- else:
320
- raise gr.Error(message)
321
- ```
322
-
323
- def sketch\_image(image, intensity, blur\_ksize, sigma):
324
- """Process image with pencil sketch filter for Gradio"""
325
- result = process\_image\_sketch(image, intensity, blur\_ksize, sigma)
326
- return Image.fromarray(result)
327
-
328
- def sketch\_video(video, intensity, blur\_ksize, sigma):
329
- """Process video with pencil sketch filter for Gradio"""
330
- message, output\_path = process\_video\_sketch(video, intensity, blur\_ksize, sigma)
331
- if output\_path:
332
- return output\_path
333
- else:
334
- raise gr.Error(message)
335
-
336
- # ------------------- Create Gradio Interface -------------------
337
-
338
- def create\_interface():
339
- \# Tooltip content
340
- otsu\_tooltip = "Otsu automatically determines the optimal threshold value by analyzing the image histogram."
341
- adaptive\_tooltip = "Adaptive thresholding calculates different thresholds for different areas of the image, useful for images with varying lighting conditions."
342
- manual\_tooltip = "Manual threshold lets you set a specific brightness cutoff point between black and white pixels."
343
- intensity\_tooltip = "Controls the strength of the pencil sketch effect. Higher values create more contrast."
344
- blur\_tooltip = "Controls how much the image is blurred. Higher values create a softer sketch effect."
345
- sigma\_tooltip = "Controls the standard deviation of the Gaussian blur. Higher values increase the blurring effect."
346
-
347
- ```
348
- # Black and White Image Interface
349
- with gr.Blocks(title="Image Processor", css=custom_css, theme=gr.themes.Base()) as app:
350
- with gr.Row(elem_classes="container"):
351
- gr.Markdown("""
352
- # Image and Video Processor
353
- Transform your media with professional black & white conversion and pencil sketch effects
354
- """)
355
-
356
- with gr.Tabs(value="Pencil Sketch Converter") as tabs:
357
- with gr.TabItem("Black & White Converter", elem_classes="app-card"):
358
- with gr.Tabs() as bw_tabs:
359
- with gr.TabItem("Image Processing"):
360
- with gr.Row(equal_height=True):
361
- with gr.Column(scale=1):
362
- bw_image_input = gr.Image(label="Input Image", elem_classes="input-image")
363
-
364
- with gr.Group():
365
- bw_method = gr.Radio(
366
- choices=["otsu", "adaptive", "manual"],
367
- value="otsu",
368
- label="Thresholding Method",
369
- info=otsu_tooltip,
370
- elem_classes="radio-group"
371
- )
372
-
373
- bw_threshold = gr.Slider(
374
- minimum=0,
375
- maximum=255,
376
- value=127,
377
- step=1,
378
- label="Manual Threshold Value",
379
- info=manual_tooltip,
380
- interactive=True,
381
- elem_classes="slider-label"
382
- )
383
-
384
- bw_image_btn = gr.Button("Convert", elem_classes="primary")
385
-
386
- with gr.Column(scale=1):
387
- bw_image_output = gr.Image(label="Processed Image")
388
-
389
- with gr.TabItem("Video Processing"):
390
- with gr.Row(equal_height=True):
391
- with gr.Column(scale=1):
392
- bw_video_input = gr.Video(label="Input Video")
393
-
394
- with gr.Group():
395
- bw_video_method = gr.Radio(
396
- choices=["otsu", "adaptive", "manual"],
397
- value="otsu",
398
- label="Thresholding Method",
399
- info=otsu_tooltip,
400
- elem_classes="radio-group"
401
- )
402
-
403
- bw_video_threshold = gr.Slider(
404
- minimum=0,
405
- maximum=255,
406
- value=127,
407
- step=1,
408
- label="Manual Threshold Value",
409
- info=manual_tooltip,
410
- interactive=True,
411
- elem_classes="slider-label"
412
- )
413
-
414
- bw_video_btn = gr.Button("Convert", elem_classes="primary")
415
-
416
- with gr.Column(scale=1):
417
- bw_video_output = gr.Video(label="Processed Video")
418
-
419
- with gr.TabItem("Pencil Sketch Converter", elem_classes="app-card"):
420
- with gr.Tabs() as sketch_tabs:
421
- with gr.TabItem("Image Processing"):
422
- with gr.Row(equal_height=True):
423
- with gr.Column(scale=1):
424
- sketch_image_input = gr.Image(label="Input Image")
425
-
426
- with gr.Group():
427
- sketch_intensity = gr.Slider(
428
- minimum=1,
429
- maximum=255,
430
- value=255,
431
- step=1,
432
- label="Intensity",
433
- info=intensity_tooltip,
434
- elem_classes="slider-label"
435
- )
436
-
437
- sketch_blur = gr.Slider(
438
- minimum=1,
439
- maximum=99,
440
- value=21,
441
- step=2,
442
- label="Blur Kernel Size",
443
- info=blur_tooltip,
444
- elem_classes="slider-label"
445
- )
446
-
447
- sketch_sigma = gr.Slider(
448
- minimum=0,
449
- maximum=50,
450
- value=0,
451
- step=0.1,
452
- label="Standard Deviation",
453
- info=sigma_tooltip,
454
- elem_classes="slider-label"
455
- )
456
 
457
- sketch_image_btn = gr.Button("Convert", elem_classes="primary")
458
-
459
- with gr.Column(scale=1):
460
- sketch_image_output = gr.Image(label="Processed Image")
461
-
462
- with gr.TabItem("Video Processing"):
463
- with gr.Row(equal_height=True):
464
- with gr.Column(scale=1):
465
- sketch_video_input = gr.Video(label="Input Video")
466
-
467
- with gr.Group():
468
- sketch_video_intensity = gr.Slider(
469
- minimum=1,
470
- maximum=255,
471
- value=255,
472
- step=1,
473
- label="Intensity",
474
- info=intensity_tooltip,
475
- elem_classes="slider-label"
476
- )
 
 
 
 
 
 
 
 
 
477
 
478
- sketch_video_blur = gr.Slider(
479
- minimum=1,
480
- maximum=99,
481
- value=21,
482
- step=2,
483
- label="Blur Kernel Size",
484
- info=blur_tooltip,
485
- elem_classes="slider-label"
486
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
 
488
- sketch_video_sigma = gr.Slider(
489
- minimum=0,
490
- maximum=50,
491
- value=0,
492
- step=0.1,
493
- label="Standard Deviation",
494
- info=sigma_tooltip,
495
- elem_classes="slider-label"
496
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
 
498
- sketch_video_btn = gr.Button("Convert", elem_classes="primary")
499
-
500
- with gr.Column(scale=1):
501
- sketch_video_output = gr.Video(label="Processed Video")
502
-
503
- with gr.Row(elem_classes="container"):
504
- gr.Markdown("""
505
- ### How to use:
506
- 1. Upload an image or video
507
- 2. Adjust the settings as needed
508
- 3. Click the Convert button to process your media
509
- """)
510
-
511
- # Set up event listeners
512
- bw_image_btn.click(
513
- fn=black_white_image,
514
- inputs=[bw_image_input, bw_method, bw_threshold],
515
- outputs=bw_image_output
516
- )
517
-
518
- bw_video_btn.click(
519
- fn=black_white_video,
520
- inputs=[bw_video_input, bw_video_method, bw_video_threshold],
521
- outputs=bw_video_output
522
- )
523
-
524
- sketch_image_btn.click(
525
- fn=sketch_image,
526
- inputs=[sketch_image_input, sketch_intensity, sketch_blur, sketch_sigma],
527
- outputs=sketch_image_output
528
- )
529
-
530
- sketch_video_btn.click(
531
- fn=sketch_video,
532
- inputs=[sketch_video_input, sketch_video_intensity, sketch_video_blur, sketch_video_sigma],
533
- outputs=sketch_video_output
534
- )
535
-
536
- # Make blur slider always odd
537
- def update_blur(value):
538
- return value if value % 2 == 1 else value + 1
539
-
540
- sketch_blur.change(update_blur, sketch_blur, sketch_blur)
541
- sketch_video_blur.change(update_blur, sketch_video_blur, sketch_video_blur)
542
-
543
- # Add visibility toggle based on method selection
544
- def update_threshold_visibility(method):
545
- return gr.update(visible=(method == "manual"))
546
-
547
- bw_method.change(update_threshold_visibility, bw_method, bw_threshold)
548
- bw_video_method.change(update_threshold_visibility, bw_video_method, bw_video_threshold)
549
-
550
- return app
551
- ```
552
-
553
- # ------------------- Launch App -------------------
554
-
555
- if **name** == "**main**":
556
- app = create\_interface()
557
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  from typing import Union, Tuple
8
 
9
  # Custom CSS for styling the interface
 
10
  custom_css = """
11
  .container {
12
+ max-width: 1200px;
13
+ margin: 0 auto;
14
  }
15
+ /* Main styling */
16
  .gradio-container {
17
+ font-family: 'Roboto', 'Segoe UI', sans-serif;
18
+ color: white;
19
  }
20
  /* Card styling */
21
  .app-card {
22
+ border-radius: 12px;
23
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
24
+ padding: 20px;
25
+ background: linear-gradient(135deg, #2a3a4a 0%, #1e2a3a 100%);
26
+ margin-bottom: 20px;
27
  }
28
  /* Header styling */
29
  h1, h2, h3 {
30
+ font-weight: 700 !important;
31
+ color: white !important;
32
  }
33
  /* Labels styling */
34
  label, .label {
35
+ font-size: 1rem !important;
36
+ font-weight: 600 !important;
37
+ color: white !important;
38
+ margin-bottom: 6px !important;
39
  }
40
  /* Input and slider styling */
41
  .slider-label {
42
+ font-weight: 600 !important;
43
+ color: white !important;
44
+ font-size: 0.95rem !important;
45
  }
46
  /* Button styling */
47
  button.primary {
48
+ background: linear-gradient(135deg, #3498db, #2980b9) !important;
49
+ color: white !important;
50
+ font-weight: 600 !important;
51
+ border-radius: 8px !important;
52
+ padding: 12px 24px !important;
53
+ font-size: 1.1rem !important;
54
+ transition: all 0.3s ease !important;
55
+ border: none !important;
56
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 0 10px rgba(52, 152, 219, 0.4) !important;
57
  }
58
+ button.primary:hover {
59
+ background: linear-gradient(135deg, #2980b9, #2573a7) !important;
60
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2), 0 0 15px rgba(52, 152, 219, 0.6) !important;
61
+ transform: translateY(-2px) !important;
62
  }
63
  /* Radio buttons */
64
  .radio-group label {
65
+ font-weight: 600 !important;
66
+ color: white !important;
67
  }
68
  /* Tab styling */
69
  .tab-nav {
70
+ font-weight: 600 !important;
71
+ font-size: 1.05rem !important;
72
  }
73
+ /* Responsive adjustments */
74
  @media (max-width: 768px) {
75
+ .gradio-container {
76
+ padding: 10px !important;
77
+ }
78
+
79
+ label, .label {
80
+ font-size: 0.95rem !important;
81
+ }
82
+
83
+ button.primary {
84
+ padding: 10px 18px !important;
85
+ font-size: 1rem !important;
86
+ }
 
 
 
87
  }
88
  """
89
 
90
  # Enable OpenCL for better performance
 
91
  cv2.ocl.setUseOpenCL(True)
92
 
93
+ # ------------------- Theme Setup ------------------- #
94
+ def create_custom_theme():
95
+ """Create a custom dark theme for the interface"""
96
+ return gr.themes.Base().set(
97
+ body_background_fill="linear-gradient(to bottom right, #1a1f2c, #121620)",
98
+ body_background_fill_dark="linear-gradient(to bottom right, #1a1f2c, #121620)",
99
+ body_text_color="white",
100
+ body_text_color_dark="white",
101
+ button_primary_background_fill="linear-gradient(135deg, #3498db, #2980b9)",
102
+ button_primary_background_fill_hover="linear-gradient(135deg, #2980b9, #2573a7)",
103
+ button_primary_text_color="white",
104
+ button_primary_text_color_dark="white",
105
+ button_primary_border_color="transparent",
106
+ button_primary_border_color_dark="transparent",
107
+ button_secondary_background_fill="#34495e",
108
+ button_secondary_background_fill_hover="#2c3e50",
109
+ button_secondary_text_color="white",
110
+ button_secondary_text_color_dark="white",
111
+ block_title_text_color="white",
112
+ block_title_text_color_dark="white",
113
+ block_label_text_color="white",
114
+ block_label_text_color_dark="white",
115
+ slider_color="#3498db",
116
+ slider_color_dark="#3498db",
117
+ border_color_primary="#3498db",
118
+ border_color_primary_dark="#3498db",
119
+ background_fill_primary="#2a3a4a",
120
+ background_fill_primary_dark="#2a3a4a",
121
+ background_fill_secondary="#1e2a3a",
122
+ background_fill_secondary_dark="#1e2a3a",
123
+ border_radius_size="12px",
124
+ spacing_md="12px",
125
+ spacing_lg="16px",
126
+ text_size="16px",
127
+ text_md="18px",
128
+ text_lg="20px",
129
+ text_xl="24px",
130
+ font=["Roboto", "ui-sans-serif", "system-ui", "sans-serif"],
131
+ )
132
+
133
+ # ------------------- Black & White Converter Functions ------------------- #
134
+ def convert_to_black_white(image, threshold_value=127, method="otsu"):
135
+ """Convert image to black and white using specified thresholding method"""
136
+ if isinstance(image, str):
137
+ image = cv2.imread(image)
138
+
139
+ # Convert to grayscale if not already
140
+ if len(image.shape) == 3:
141
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
142
+ else:
143
+ gray = image
144
+
145
+ if method == "adaptive":
146
+ binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
147
+ cv2.THRESH_BINARY, 11, 2)
148
+ elif method == "otsu":
149
+ _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
150
+ else:
151
+ _, binary = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY)
152
+
153
+ return binary
154
+
155
+ def process_image_bw(image, threshold_value, method):
156
+ """Process image with black and white thresholding"""
157
+ if image is None:
158
+ return None
159
+
160
+ # Convert to numpy array if PIL Image
161
+ if isinstance(image, Image.Image):
162
+ image_np = np.array(image)
163
+ # Convert RGB to BGR for OpenCV
164
+ if len(image_np.shape) == 3:
165
+ image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
166
+ else:
167
+ image_np = image
168
+
169
+ result = convert_to_black_white(image_np, threshold_value, method)
170
+ return result
171
+
172
+ def process_video_bw(video_path, threshold_value, method):
173
+ """Process video with black and white thresholding"""
174
+ if not os.path.exists(video_path):
175
+ return "Video file not found", None
176
+
177
+ try:
178
+ cap = cv2.VideoCapture(video_path)
179
+ if not cap.isOpened():
180
+ return "Could not open video file", None
181
+
182
+ # Get video properties
183
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
184
+ frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
185
+ frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
186
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
187
+
188
+ # Create temporary output file
189
+ temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
190
+ output_path = temp_output.name
191
+ temp_output.close()
192
+
193
+ # Create video writer
194
+ out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=False)
195
+
196
+ # Process each frame
197
+ while cap.isOpened():
198
+ ret, frame = cap.read()
199
+ if not ret:
200
+ break
201
+
202
+ bw_frame = convert_to_black_white(frame, threshold_value, method)
203
+ out.write(bw_frame)
204
+
205
+ cap.release()
206
+ out.release()
207
+
208
+ return "Video processed successfully", output_path
209
+ except Exception as e:
210
+ return f"Error processing video: {str(e)}", None
211
+
212
+ # ------------------- Pencil Sketch Converter Functions ------------------- #
213
+ def process_image_sketch(image, intensity=255, blur_ksize=21, sigma=0):
214
+ """Convert image to pencil sketch effect"""
215
+ if image is None:
216
+ return None
217
+
218
+ # Convert to numpy array if PIL Image
219
+ if isinstance(image, Image.Image):
220
+ image_np = np.array(image)
221
+ # Convert RGB to BGR for OpenCV
222
+ if len(image_np.shape) == 3:
223
+ image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
224
+ else:
225
+ image_np = image
226
+
227
+ # Convert to grayscale
228
+ gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) if len(image_np.shape) == 3 else image_np
229
+
230
+ # Create sketch effect
231
+ inverted = cv2.bitwise_not(gray)
232
+ blur_ksize = blur_ksize if blur_ksize % 2 == 1 else blur_ksize + 1 # Ensure kernel size is odd
233
+ blurred = cv2.GaussianBlur(inverted, (blur_ksize, blur_ksize), sigma)
234
+ sketch = cv2.divide(gray, cv2.bitwise_not(blurred), scale=intensity)
235
+
236
+ return sketch
237
+
238
+ def process_video_sketch(video_path, intensity=255, blur_ksize=21, sigma=0):
239
+ """Process video with pencil sketch effect"""
240
+ if not os.path.exists(video_path):
241
+ return "Video file not found", None
242
+
243
+ try:
244
+ cap = cv2.VideoCapture(video_path)
245
+ if not cap.isOpened():
246
+ return "Could not open video file", None
247
+
248
+ # Get video properties
249
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
250
+ frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
251
+ frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
252
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
253
+
254
+ # Create temporary output file
255
+ temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False)
256
+ output_path = temp_output.name
257
+ temp_output.close()
258
+
259
+ # Create video writer
260
+ out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=True)
261
+
262
+ # Process each frame
263
+ while cap.isOpened():
264
+ ret, frame = cap.read()
265
+ if not ret:
266
+ break
267
+
268
+ sketch_frame = process_image_sketch(frame, intensity, blur_ksize, sigma)
269
+ # Convert grayscale to BGR for video output
270
+ sketch_bgr = cv2.cvtColor(sketch_frame, cv2.COLOR_GRAY2BGR)
271
+ out.write(sketch_bgr)
272
+
273
+ cap.release()
274
+ out.release()
275
+
276
+ return "Video processed successfully", output_path
277
+ except Exception as e:
278
+ return f"Error processing video: {str(e)}", None
279
+
280
+ # ------------------- Gradio Interface Functions ------------------- #
281
+ def black_white_image(image, threshold_method, threshold_value):
282
+ """Process image with black and white filter for Gradio"""
283
+ if threshold_method != "manual":
284
+ threshold_value = 0 # Not used for adaptive or Otsu
285
+
286
+ result = process_image_bw(image, threshold_value, threshold_method)
287
+ return Image.fromarray(result)
288
+
289
+ def black_white_video(video, threshold_method, threshold_value):
290
+ """Process video with black and white filter for Gradio"""
291
+ if threshold_method != "manual":
292
+ threshold_value = 0 # Not used for adaptive or Otsu
293
+
294
+ message, output_path = process_video_bw(video, threshold_value, threshold_method)
295
+ if output_path:
296
+ return output_path
297
+ else:
298
+ raise gr.Error(message)
299
+
300
+ def sketch_image(image, intensity, blur_ksize, sigma):
301
+ """Process image with pencil sketch filter for Gradio"""
302
+ result = process_image_sketch(image, intensity, blur_ksize, sigma)
303
+ return Image.fromarray(result)
304
+
305
+ def sketch_video(video, intensity, blur_ksize, sigma):
306
+ """Process video with pencil sketch filter for Gradio"""
307
+ message, output_path = process_video_sketch(video, intensity, blur_ksize, sigma)
308
+ if output_path:
309
+ return output_path
310
+ else:
311
+ raise gr.Error(message)
312
+
313
+ # ------------------- Create Gradio Interface ------------------- #
314
+ def create_interface():
315
+ # Tooltip content
316
+ otsu_tooltip = "Otsu automatically determines the optimal threshold value by analyzing the image histogram."
317
+ adaptive_tooltip = "Adaptive thresholding calculates different thresholds for different areas of the image, useful for images with varying lighting conditions."
318
+ manual_tooltip = "Manual threshold lets you set a specific brightness cutoff point between black and white pixels."
319
+ intensity_tooltip = "Controls the strength of the pencil sketch effect. Higher values create more contrast."
320
+ blur_tooltip = "Controls how much the image is blurred. Higher values create a softer sketch effect."
321
+ sigma_tooltip = "Controls the standard deviation of the Gaussian blur. Higher values increase the blurring effect."
322
+
323
+ # Black and White Image Interface
324
+ with gr.Blocks(title="Image Processor", css=custom_css, theme=gr.themes.Base()) as app:
325
+ with gr.Row(elem_classes="container"):
326
+ gr.Markdown("""
327
+ # Image and Video Processor
328
+ Transform your media with professional black & white conversion and pencil sketch effects
329
+ """)
330
+
331
+ with gr.Tabs(value="Pencil Sketch Converter") as tabs:
332
+ with gr.TabItem("Black & White Converter", elem_classes="app-card"):
333
+ with gr.Tabs() as bw_tabs:
334
+ with gr.TabItem("Image Processing"):
335
+ with gr.Row(equal_height=True):
336
+ with gr.Column(scale=1):
337
+ bw_image_input = gr.Image(label="Input Image", elem_classes="input-image")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
 
339
+ with gr.Group():
340
+ bw_method = gr.Radio(
341
+ choices=["otsu", "adaptive", "manual"],
342
+ value="otsu",
343
+ label="Thresholding Method",
344
+ info=otsu_tooltip,
345
+ elem_classes="radio-group"
346
+ )
347
+
348
+ bw_threshold = gr.Slider(
349
+ minimum=0,
350
+ maximum=255,
351
+ value=127,
352
+ step=1,
353
+ label="Manual Threshold Value",
354
+ info=manual_tooltip,
355
+ interactive=True,
356
+ elem_classes="slider-label"
357
+ )
358
+
359
+ bw_image_btn = gr.Button("Convert", elem_classes="primary")
360
+
361
+ with gr.Column(scale=1):
362
+ bw_image_output = gr.Image(label="Processed Image")
363
+
364
+ with gr.TabItem("Video Processing"):
365
+ with gr.Row(equal_height=True):
366
+ with gr.Column(scale=1):
367
+ bw_video_input = gr.Video(label="Input Video")
368
 
369
+ with gr.Group():
370
+ bw_video_method = gr.Radio(
371
+ choices=["otsu", "adaptive", "manual"],
372
+ value="otsu",
373
+ label="Thresholding Method",
374
+ info=otsu_tooltip,
375
+ elem_classes="radio-group"
376
+ )
377
+
378
+ bw_video_threshold = gr.Slider(
379
+ minimum=0,
380
+ maximum=255,
381
+ value=127,
382
+ step=1,
383
+ label="Manual Threshold Value",
384
+ info=manual_tooltip,
385
+ interactive=True,
386
+ elem_classes="slider-label"
387
+ )
388
+
389
+ bw_video_btn = gr.Button("Convert", elem_classes="primary")
390
+
391
+ with gr.Column(scale=1):
392
+ bw_video_output = gr.Video(label="Processed Video")
393
+
394
+ with gr.TabItem("Pencil Sketch Converter", elem_classes="app-card"):
395
+ with gr.Tabs() as sketch_tabs:
396
+ with gr.TabItem("Image Processing"):
397
+ with gr.Row(equal_height=True):
398
+ with gr.Column(scale=1):
399
+ sketch_image_input = gr.Image(label="Input Image")
400
 
401
+ with gr.Group():
402
+ sketch_intensity = gr.Slider(
403
+ minimum=1,
404
+ maximum=255,
405
+ value=255,
406
+ step=1,
407
+ label="Intensity",
408
+ info=intensity_tooltip,
409
+ elem_classes="slider-label"
410
+ )
411
+
412
+ sketch_blur = gr.Slider(
413
+ minimum=1,
414
+ maximum=99,
415
+ value=21,
416
+ step=2,
417
+ label="Blur Kernel Size",
418
+ info=blur_tooltip,
419
+ elem_classes="slider-label"
420
+ )
421
+
422
+ sketch_sigma = gr.Slider(
423
+ minimum=0,
424
+ maximum=50,
425
+ value=0,
426
+ step=0.1,
427
+ label="Standard Deviation",
428
+ info=sigma_tooltip,
429
+ elem_classes="slider-label"
430
+ )
431
+
432
+ sketch_image_btn = gr.Button("Convert", elem_classes="primary")
433
+
434
+ with gr.Column(scale=1):
435
+ sketch_image_output = gr.Image(label="Processed Image")
436
+
437
+ with gr.TabItem("Video Processing"):
438
+ with gr.Row(equal_height=True):
439
+ with gr.Column(scale=1):
440
+ sketch_video_input = gr.Video(label="Input Video")
441
 
442
+ with gr.Group():
443
+ sketch_video_intensity = gr.Slider(
444
+ minimum=1,
445
+ maximum=255,
446
+ value=255,
447
+ step=1,
448
+ label="Intensity",
449
+ info=intensity_tooltip,
450
+ elem_classes="slider-label"
451
+ )
452
+
453
+ sketch_video_blur = gr.Slider(
454
+ minimum=1,
455
+ maximum=99,
456
+ value=21,
457
+ step=2,
458
+ label="Blur Kernel Size",
459
+ info=blur_tooltip,
460
+ elem_classes="slider-label"
461
+ )
462
+
463
+ sketch_video_sigma = gr.Slider(
464
+ minimum=0,
465
+ maximum=50,
466
+ value=0,
467
+ step=0.1,
468
+ label="Standard Deviation",
469
+ info=sigma_tooltip,
470
+ elem_classes="slider-label"
471
+ )
472
+
473
+ sketch_video_btn = gr.Button("Convert", elem_classes="primary")
474
+
475
+ with gr.Column(scale=1):
476
+ sketch_video_output = gr.Video(label="Processed Video")
477
+
478
+ with gr.Row(elem_classes="container"):
479
+ gr.Markdown("""
480
+ ### How to use:
481
+ 1. Upload an image or video
482
+ 2. Adjust the settings as needed
483
+ 3. Click the Convert button to process your media
484
+ """)
485
+
486
+ # Set up event listeners
487
+ bw_image_btn.click(
488
+ fn=black_white_image,
489
+ inputs=[bw_image_input, bw_method, bw_threshold],
490
+ outputs=bw_image_output
491
+ )
492
+
493
+ bw_video_btn.click(
494
+ fn=black_white_video,
495
+ inputs=[bw_video_input, bw_video_method, bw_video_threshold],
496
+ outputs=bw_video_output
497
+ )
498
+
499
+ sketch_image_btn.click(
500
+ fn=sketch_image,
501
+ inputs=[sketch_image_input, sketch_intensity, sketch_blur, sketch_sigma],
502
+ outputs=sketch_image_output
503
+ )
504
+
505
+ sketch_video_btn.click(
506
+ fn=sketch_video,
507
+ inputs=[sketch_video_input, sketch_video_intensity, sketch_video_blur, sketch_video_sigma],
508
+ outputs=sketch_video_output
509
+ )
510
+
511
+ # Make blur slider always odd
512
+ def update_blur(value):
513
+ return value if value % 2 == 1 else value + 1
514
+
515
+ sketch_blur.change(update_blur, sketch_blur, sketch_blur)
516
+ sketch_video_blur.change(update_blur, sketch_video_blur, sketch_video_blur)
517
+
518
+ # Add visibility toggle based on method selection
519
+ def update_threshold_visibility(method):
520
+ return gr.update(visible=(method == "manual"))
521
+
522
+ bw_method.change(update_threshold_visibility, bw_method, bw_threshold)
523
+ bw_video_method.change(update_threshold_visibility, bw_video_method, bw_video_threshold)
524
+
525
+ return app
526
+
527
+ # ------------------- Launch App ------------------- #
528
+ if __name__ == "__main__":
529
+ app = create_interface()
530
+ app.launch()