import gradio as gr import os import cv2 import numpy as np import asyncio from utils import detect_faces_frame, apply_blur, load_caffe_models from ultralight import UltraLightDetector import tempfile import json # Create output directories os.makedirs("output/image", exist_ok=True) os.makedirs("output/video", exist_ok=True) os.makedirs("temp", exist_ok=True) # Initialize detector once detector = UltraLightDetector() # Age and gender options for filters AGE_OPTIONS = ['0-2', '4-6', '8-12', '15-20', '25-32', '38-43', '48-53', '60+'] GENDER_OPTIONS = ['Male', 'Female'] # Operation options OPERATION_OPTIONS = { "Gaussian Blur": 0, "Black Patch": 1, "Pixelation": 2 } def convert_for_json(obj): """Convert NumPy arrays to lists for JSON serialization""" if isinstance(obj, np.ndarray): return obj.tolist() elif isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj) elif isinstance(obj, np.int32) or isinstance(obj, np.int64): return int(obj) elif isinstance(obj, dict): return {k: convert_for_json(v) for k, v in obj.items()} elif isinstance(obj, list): return [convert_for_json(item) for item in obj] else: return obj def process_image(image, operation_name, age_filters=[], gender_filters=[], selected_face_indices=[]): """Process an image with face blurring""" # Convert from PIL to cv2 format if image is None: return None, "Please upload an image" # Convert from RGB (gradio) to BGR (OpenCV) if isinstance(image, str): # If it's a path image_cv = cv2.imread(image) else: # If it's a numpy array image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) # Get operation code operation = OPERATION_OPTIONS.get(operation_name, 0) # Detect faces loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) predictions = loop.run_until_complete(detect_faces_frame(detector=detector, frame=image_cv)) loop.close() # Create a temporary copy for drawing face boxes image_with_boxes = image_cv.copy() face_thumbnails = [] # Draw boxes around all detected faces with indices for i, pred in enumerate(predictions): box = np.array(pred['box']) x1, y1, x2, y2 = box.astype(int) # Draw box cv2.rectangle(image_with_boxes, (x1, y1), (x2, y2), (0, 255, 0), 1) face_img = image_cv[y1:y2, x1:x2] face_rgb = cv2.cvtColor(face_img, cv2.COLOR_BGR2RGB) caption = f"Face #{i} | {pred['gender']} | {pred['age']}" face_thumbnails.append((face_rgb, caption)) # Draw index # cv2.putText(image_with_boxes, f"#{i}: {pred['gender']}, {pred['age']}", # (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) # Convert to RGB for display image_with_boxes_rgb = cv2.cvtColor(image_with_boxes, cv2.COLOR_BGR2RGB) # Create filters dictionary filters = { "gender": gender_filters, "age": age_filters } # Create selected_faces list based on indices selected_faces = [] if selected_face_indices: indices = [int(idx.strip()) for idx in selected_face_indices.split(",") if idx.strip().isdigit()] for i in indices: if i < len(predictions): selected_faces.append({"box": predictions[i]["box"]}) # Apply blur loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) processed_image = loop.run_until_complete( apply_blur( detected_faces=predictions, frame=image_cv.copy(), filters=filters, selected_faces=selected_faces, operation=operation ) ) loop.close() # Convert back to RGB for Gradio processed_image_rgb = cv2.cvtColor(processed_image, cv2.COLOR_BGR2RGB) # Save results as JSON results_data = { "faces_detected": len(predictions), "predictions": convert_for_json(predictions), "operation": operation_name, "filters": { "gender": gender_filters, "age": age_filters }, "selected_faces": [int(idx.strip()) for idx in selected_face_indices.split(",") if idx.strip().isdigit()] if selected_face_indices else [] } return [image_with_boxes_rgb, processed_image_rgb, json.dumps(results_data, indent=2), face_thumbnails] # def process_video(video_path, operation_name, age_filters=[], gender_filters=[], progress=gr.Progress()): # """Process a video with face blurring""" # if video_path is None: # return None, "Please upload a video" # # Get operation code # operation = OPERATION_OPTIONS.get(operation_name, 0) # # Create a temporary file for the output # output_path = tempfile.NamedTemporaryFile(suffix='.mp4', delete=False).name # # Open the video # cap = cv2.VideoCapture(video_path) # if not cap.isOpened(): # return None, "Could not open video file" # # Get video properties # width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # fps = cap.get(cv2.CAP_PROP_FPS) # total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # # Determine frame skipping (process every nth frame for speed) # frame_skip = max(1, round(fps / 15)) # Process at most 15 fps # # Create VideoWriter object # fourcc = cv2.VideoWriter_fourcc(*'mp4v') # out = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) # # Create filters dictionary # filters = { # "gender": gender_filters, # "age": age_filters # } # # Process frames # frame_count = 0 # face_count = 0 # # Process limited frames to prevent timeout (Gradio has a 60s limit by default) # max_frames_to_process = min(300, total_frames) # Limit to 300 frames # for _ in progress.tqdm(range(max_frames_to_process)): # ret, frame = cap.read() # if not ret: # break # # Process every nth frame (for efficiency) # if frame_count % frame_skip == 0: # # Detect faces # loop = asyncio.new_event_loop() # asyncio.set_event_loop(loop) # predictions = loop.run_until_complete(detect_faces_frame(detector=detector, frame=frame)) # loop.close() # face_count += len(predictions) # # Apply blur # loop = asyncio.new_event_loop() # asyncio.set_event_loop(loop) # processed_frame = loop.run_until_complete( # apply_blur( # detected_faces=predictions, # frame=frame, # filters=filters, # operation=operation # ) # ) # loop.close() # # Write processed frame # out.write(processed_frame) # else: # # Write original frame for skipped frames # out.write(frame) # frame_count += 1 # # Release resources # cap.release() # out.release() # # Summary message # summary = f"Processed {frame_count} frames, detected {face_count} faces" # if frame_count < total_frames: # summary += f" (limited to first {frame_count} frames out of {total_frames})" # return output_path, summary # Create Gradio interface with gr.Blocks(title="Face Privacy Protection Tool") as demo: gr.Markdown("# Face Privacy Protection Tool") gr.Markdown("Upload an image or video to detect faces and apply privacy filters") with gr.Tabs(): with gr.TabItem("Image Processing"): with gr.Row(): with gr.Column(): image_input = gr.Image(label="Upload Image", type="pil") operation_dropdown = gr.Dropdown( choices=list(OPERATION_OPTIONS.keys()), value="Gaussian Blur", label="Blur Operation" ) with gr.Accordion("Advanced Filtering", open=False): age_filter = gr.CheckboxGroup( choices=AGE_OPTIONS, label="Filter by Age (select to blur)" ) gender_filter = gr.CheckboxGroup( choices=GENDER_OPTIONS, label="Filter by Gender (select to blur)" ) selected_faces = gr.Textbox( label="Select Specific Faces to Blur (comma-separated indices, e.g., 0,1,3)", placeholder="Enter face indices separated by commas" ) image_button = gr.Button("Process Image") with gr.Column(): output_tabs = gr.Tabs() with output_tabs: with gr.TabItem("Face Detection"): image_with_boxes = gr.Image(label="Detected Faces") with gr.TabItem("Processed Image"): image_output = gr.Image(label="Processed Image") with gr.TabItem("JSON Results"): json_output = gr.JSON(label="Detection Results") with gr.TabItem("Detected Faces (Metadata)"): face_gallery = gr.Gallery( label="Detected Faces", show_label=True, columns=4, height="auto", object_fit="contain" ) image_button.click( process_image, inputs=[image_input, operation_dropdown, age_filter, gender_filter, selected_faces], outputs=[image_with_boxes, image_output, json_output, face_gallery] ) # with gr.TabItem("Video Processing"): # with gr.Row(): # with gr.Column(): # video_input = gr.Video(label="Upload Video") # video_operation = gr.Dropdown( # choices=list(OPERATION_OPTIONS.keys()), # value="Gaussian Blur", # label="Blur Operation" # ) # with gr.Accordion("Advanced Filtering", open=False): # video_age_filter = gr.CheckboxGroup( # choices=AGE_OPTIONS, # label="Filter by Age (select to blur)" # ) # video_gender_filter = gr.CheckboxGroup( # choices=GENDER_OPTIONS, # label="Filter by Gender (select to blur)" # ) # video_button = gr.Button("Process Video") # with gr.Column(): # video_output = gr.Video(label="Processed Video") # video_summary = gr.Textbox(label="Processing Summary") # video_button.click( # process_video, # inputs=[video_input, video_operation, video_age_filter, video_gender_filter], # outputs=[video_output, video_summary] # ) gr.Markdown(""" ## How to Use 1. **Upload** an image or video using the respective tab 2. **Choose** your preferred blur operation: - **Gaussian Blur**: Blurs facial features while maintaining face shape - **Black Patch**: Completely covers faces with black rectangles - **Pixelation**: Creates a mosaic effect over faces 3. **Advanced Filtering**: - Filter by age group (select which age groups to blur) - Filter by gender (select which genders to blur) - For images, you can select specific face indices to blur 4. **Process** the media and view the results Note: Video processing may take some time depending on the file size. """) if __name__ == "__main__": demo.launch()