Spaces:
Sleeping
Sleeping
import cv2 | |
import numpy as np | |
import os | |
import gradio as gr | |
from PIL import Image | |
import tempfile | |
# Enable OpenCL for better performance if available | |
try: | |
cv2.ocl.setUseOpenCL(True) | |
except: | |
pass # OpenCL might not be available in all environments | |
# ------------------- Black & White Converter Functions ------------------- # | |
def convert_to_black_white(image, threshold_value=127, method="otsu"): | |
"""Convert image to black and white using specified thresholding method""" | |
if isinstance(image, str): | |
image = cv2.imread(image) | |
# Convert to grayscale if not already | |
if len(image.shape) == 3: | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
else: | |
gray = image | |
if method == "adaptive": | |
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, | |
cv2.THRESH_BINARY, 11, 2) | |
elif method == "otsu": | |
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | |
else: | |
_, binary = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY) | |
return binary | |
def process_image_bw(image, threshold_method, threshold_value): | |
"""Process image with black and white thresholding for Gradio""" | |
if image is None: | |
raise gr.Error("No image provided") | |
if threshold_method != "manual": | |
threshold_value = 0 # Not used for adaptive or Otsu | |
# Convert to numpy array if PIL Image | |
if isinstance(image, Image.Image): | |
image_np = np.array(image) | |
# Convert RGB to BGR for OpenCV | |
if len(image_np.shape) == 3: | |
image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR) | |
else: | |
image_np = image | |
result = convert_to_black_white(image_np, threshold_value, threshold_method) | |
return Image.fromarray(result) | |
def process_video_bw(video_path, threshold_method, threshold_value): | |
"""Process video with black and white filter for Gradio""" | |
if video_path is None: | |
raise gr.Error("No video provided") | |
if threshold_method != "manual": | |
threshold_value = 0 # Not used for adaptive or Otsu | |
try: | |
cap = cv2.VideoCapture(video_path) | |
if not cap.isOpened(): | |
raise gr.Error("Could not open video file") | |
# Get video properties | |
fourcc = cv2.VideoWriter_fourcc(*'mp4v') | |
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
fps = int(cap.get(cv2.CAP_PROP_FPS)) | |
# Create temporary output file | |
temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) | |
output_path = temp_output.name | |
temp_output.close() | |
# Create video writer | |
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=False) | |
# Process each frame | |
while cap.isOpened(): | |
ret, frame = cap.read() | |
if not ret: | |
break | |
bw_frame = convert_to_black_white(frame, threshold_value, threshold_method) | |
out.write(bw_frame) | |
cap.release() | |
out.release() | |
return output_path | |
except Exception as e: | |
raise gr.Error(f"Error processing video: {str(e)}") | |
# ------------------- Pencil Sketch Converter Functions ------------------- # | |
def process_image_sketch(image, intensity, blur_ksize, sigma): | |
"""Process image with pencil sketch effect for Gradio""" | |
if image is None: | |
raise gr.Error("No image provided") | |
# Convert to numpy array if PIL Image | |
if isinstance(image, Image.Image): | |
image_np = np.array(image) | |
# Convert RGB to BGR for OpenCV | |
if len(image_np.shape) == 3: | |
image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR) | |
else: | |
image_np = image | |
# Convert to grayscale | |
gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY) if len(image_np.shape) == 3 else image_np | |
# Create sketch effect | |
inverted = cv2.bitwise_not(gray) | |
blur_ksize = blur_ksize if blur_ksize % 2 == 1 else blur_ksize + 1 # Ensure kernel size is odd | |
blurred = cv2.GaussianBlur(inverted, (blur_ksize, blur_ksize), sigma) | |
sketch = cv2.divide(gray, cv2.bitwise_not(blurred), scale=intensity) | |
return Image.fromarray(sketch) | |
def process_video_sketch(video_path, intensity, blur_ksize, sigma): | |
"""Process video with pencil sketch effect for Gradio""" | |
if video_path is None: | |
raise gr.Error("No video provided") | |
try: | |
cap = cv2.VideoCapture(video_path) | |
if not cap.isOpened(): | |
raise gr.Error("Could not open video file") | |
# Get video properties | |
fourcc = cv2.VideoWriter_fourcc(*'mp4v') | |
frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) | |
frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) | |
fps = int(cap.get(cv2.CAP_PROP_FPS)) | |
# Create temporary output file | |
temp_output = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False) | |
output_path = temp_output.name | |
temp_output.close() | |
# Create video writer | |
out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height), isColor=True) | |
# Process each frame | |
while cap.isOpened(): | |
ret, frame = cap.read() | |
if not ret: | |
break | |
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) | |
inverted = cv2.bitwise_not(gray) | |
blur_ksize_adj = blur_ksize if blur_ksize % 2 == 1 else blur_ksize + 1 | |
blurred = cv2.GaussianBlur(inverted, (blur_ksize_adj, blur_ksize_adj), sigma) | |
sketch = cv2.divide(gray, cv2.bitwise_not(blurred), scale=intensity) | |
sketch_bgr = cv2.cvtColor(sketch, cv2.COLOR_GRAY2BGR) | |
out.write(sketch_bgr) | |
cap.release() | |
out.release() | |
return output_path | |
except Exception as e: | |
raise gr.Error(f"Error processing video: {str(e)}") | |
# ------------------- Create Gradio Interface ------------------- # | |
def update_blur(value): | |
"""Ensure blur kernel size is always odd""" | |
return value if value % 2 == 1 else value + 1 | |
def create_interface(): | |
# App title and description | |
title = "Image & Video Processor" | |
description = """ | |
# Image and Video Processing App | |
This app provides tools to convert images and videos to black & white or pencil sketch styles. | |
## Features: | |
- **Black & White Conversion**: Apply different thresholding methods | |
- **Pencil Sketch Effect**: Create artistic pencil drawings with customizable parameters | |
- **Support for both images and videos** | |
Made with ❤️ using Gradio and OpenCV | |
""" | |
# Black and White Image Interface | |
with gr.Blocks(title=title) as app: | |
gr.Markdown(description) | |
with gr.Tab("Black & White Converter"): | |
with gr.Tab("Image"): | |
with gr.Row(): | |
with gr.Column(): | |
bw_image_input = gr.Image(label="Input Image", type="numpy") | |
bw_method = gr.Radio( | |
choices=["otsu", "adaptive", "manual"], | |
value="otsu", | |
label="Thresholding Method" | |
) | |
bw_threshold = gr.Slider( | |
minimum=0, | |
maximum=255, | |
value=127, | |
step=1, | |
label="Manual Threshold (0-255)", | |
interactive=True | |
) | |
bw_image_btn = gr.Button("Convert to Black & White") | |
with gr.Column(): | |
bw_image_output = gr.Image(label="Processed Image") | |
# Show/hide threshold slider based on method | |
def update_threshold_visibility(method): | |
return gr.update(visible=(method == "manual")) | |
bw_method.change(fn=update_threshold_visibility, inputs=bw_method, outputs=bw_threshold) | |
with gr.Tab("Video"): | |
with gr.Row(): | |
with gr.Column(): | |
bw_video_input = gr.Video(label="Input Video") | |
bw_video_method = gr.Radio( | |
choices=["otsu", "adaptive", "manual"], | |
value="otsu", | |
label="Thresholding Method" | |
) | |
bw_video_threshold = gr.Slider( | |
minimum=0, | |
maximum=255, | |
value=127, | |
step=1, | |
label="Manual Threshold (0-255)", | |
interactive=True | |
) | |
bw_video_btn = gr.Button("Convert to Black & White") | |
with gr.Column(): | |
bw_video_output = gr.Video(label="Processed Video") | |
# Show/hide threshold slider based on method | |
bw_video_method.change(fn=update_threshold_visibility, inputs=bw_video_method, outputs=bw_video_threshold) | |
with gr.Tab("Pencil Sketch Converter"): | |
with gr.Tab("Image"): | |
with gr.Row(): | |
with gr.Column(): | |
sketch_image_input = gr.Image(label="Input Image", type="numpy") | |
sketch_intensity = gr.Slider( | |
minimum=1, | |
maximum=255, | |
value=255, | |
step=1, | |
label="Intensity (1-255)" | |
) | |
sketch_blur = gr.Slider( | |
minimum=1, | |
maximum=99, | |
value=21, | |
step=2, | |
label="Blur Kernel Size (odd, 1-99)" | |
) | |
sketch_sigma = gr.Slider( | |
minimum=0, | |
maximum=50, | |
value=0, | |
step=0.1, | |
label="Standard Deviation (0-50)" | |
) | |
sketch_image_btn = gr.Button("Convert to Pencil Sketch") | |
with gr.Column(): | |
sketch_image_output = gr.Image(label="Processed Image") | |
with gr.Tab("Video"): | |
with gr.Row(): | |
with gr.Column(): | |
sketch_video_input = gr.Video(label="Input Video") | |
sketch_video_intensity = gr.Slider( | |
minimum=1, | |
maximum=255, | |
value=255, | |
step=1, | |
label="Intensity (1-255)" | |
) | |
sketch_video_blur = gr.Slider( | |
minimum=1, | |
maximum=99, | |
value=21, | |
step=2, | |
label="Blur Kernel Size (odd, 1-99)" | |
) | |
sketch_video_sigma = gr.Slider( | |
minimum=0, | |
maximum=50, | |
value=0, | |
step=0.1, | |
label="Standard Deviation (0-50)" | |
) | |
sketch_video_btn = gr.Button("Convert to Pencil Sketch") | |
with gr.Column(): | |
sketch_video_output = gr.Video(label="Processed Video") | |
# Examples section | |
with gr.Accordion("Examples", open=False): | |
gr.Markdown(""" | |
## Example Usage: | |
1. **Black & White Conversion**: Great for document scanning, text enhancement, or artistic effects | |
2. **Pencil Sketch**: Perfect for creating artistic renderings from photos | |
Try uploading your own images or videos! | |
""") | |
# Set up event listeners | |
bw_image_btn.click( | |
fn=process_image_bw, | |
inputs=[bw_image_input, bw_method, bw_threshold], | |
outputs=bw_image_output | |
) | |
bw_video_btn.click( | |
fn=process_video_bw, | |
inputs=[bw_video_input, bw_video_method, bw_video_threshold], | |
outputs=bw_video_output | |
) | |
sketch_image_btn.click( | |
fn=process_image_sketch, | |
inputs=[sketch_image_input, sketch_intensity, sketch_blur, sketch_sigma], | |
outputs=sketch_image_output | |
) | |
sketch_video_btn.click( | |
fn=process_video_sketch, | |
inputs=[sketch_video_input, sketch_video_intensity, sketch_video_blur, sketch_video_sigma], | |
outputs=sketch_video_output | |
) | |
# Make blur slider always odd | |
sketch_blur.change(update_blur, sketch_blur, sketch_blur) | |
sketch_video_blur.change(update_blur, sketch_video_blur, sketch_video_blur) | |
return app | |
# Create and launch the app | |
app = create_interface() | |
# This is needed for Hugging Face Spaces | |
if __name__ == "__main__": | |
app.launch() |