Spaces:
Sleeping
Sleeping
| import cv2 | |
| import numpy as np | |
| import streamlit as st | |
| from PIL import Image | |
| import io | |
| class FaceAnonymizer: | |
| def __init__(self): | |
| # loads harcascade for facial detecition | |
| self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') | |
| def detect_faces(self, image): | |
| """ | |
| input : takes an image | |
| output : returns list of rectangles, each rectangle represent a face | |
| [[(100, 50, 80, 80), (250, 60, 85, 85)] : means two faces were detected. | |
| """ | |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| faces = self.face_cascade.detectMultiScale( | |
| gray, | |
| scaleFactor=1.1, | |
| minNeighbors=5, | |
| minSize=(30, 30) | |
| ) | |
| return faces | |
| def pixelate_area(self, image, x, y, w, h, pixel_size=15): | |
| """ | |
| input : image, | |
| (x,y) is the top-left corner of the rectangle | |
| (w,h) is the width and height of the rectangle | |
| output : returns the image with the selected area pixelated. | |
| """ | |
| reason_of_interest = image[y:y+h, x:x+w] | |
| downscaled_roi = cv2.resize(reason_of_interest, (pixel_size, pixel_size), interpolation=cv2.INTER_LINEAR) | |
| pixelated = cv2.resize(downscaled_roi, (w, h), interpolation=cv2.INTER_NEAREST) | |
| image[y:y+h, x:x+w] = pixelated | |
| return image | |
| def blur_area(self, image, x, y, w, h, blur_strength=25): | |
| """Apply gaussian blur to a specific area""" | |
| roi = image[y:y+h, x:x+w] | |
| # Ensure blur strength is odd | |
| if blur_strength % 2 == 0: | |
| blur_strength += 1 | |
| blurred = cv2.GaussianBlur(roi, (blur_strength, blur_strength), 0) | |
| image[y:y+h, x:x+w] = blurred | |
| return image | |
| def process_image(self, image, method='blur', pixel_size=15, blur_strength=25, padding=10): | |
| """Process an image to anonymize faces""" | |
| result = image.copy() | |
| faces = self.detect_faces(image) | |
| for (x, y, w, h) in faces: | |
| # Add padding around the face | |
| x = max(0, x - padding) | |
| y = max(0, y - padding) | |
| w = min(image.shape[1] - x, w + 2 * padding) | |
| h = min(image.shape[0] - y, h + 2 * padding) | |
| if method == 'pixelate': | |
| result = self.pixelate_area(result, x, y, w, h, pixel_size) | |
| elif method == 'blur': | |
| result = self.blur_area(result, x, y, w, h, blur_strength) | |
| return result, len(faces) | |
| # helper functions to convert PIL to CV2 | |
| def pil_to_cv2(pil_image): | |
| open_cv_image = np.array(pil_image.convert('RGB')) | |
| return cv2.cvtColor(open_cv_image, cv2.COLOR_RGB2BGR) | |
| # helper functions to convert CV2 to PIL | |
| def cv2_to_pil(cv2_image): | |
| """Convert OpenCV image to PIL format""" | |
| rgb_image = cv2.cvtColor(cv2_image, cv2.COLOR_BGR2RGB) | |
| return Image.fromarray(rgb_image) | |
| def main(): | |
| st.set_page_config( | |
| page_title="Face Anonymizer", | |
| page_icon="๐", | |
| layout="wide" | |
| ) | |
| st.title("Face Anonymizer") | |
| st.markdown("Upload an image and automatically blur or pixelate faces for privacy protection") | |
| if 'anonymizer' not in st.session_state: | |
| st.session_state.anonymizer = FaceAnonymizer() | |
| st.sidebar.header("Settings") | |
| method = st.sidebar.selectbox( | |
| "Anonymization Method", | |
| ["blur", "pixelate"], | |
| help="Choose between blur or pixelation effect" | |
| ) | |
| if method == "blur": | |
| blur_strength = st.sidebar.slider( | |
| "Blur Strength", | |
| min_value=5, | |
| max_value=99, | |
| value=25, | |
| step=2, | |
| help="Higher values = more blur (must be odd)" | |
| ) | |
| if blur_strength % 2 == 0: | |
| blur_strength += 1 | |
| else: | |
| pixel_size = st.sidebar.slider( | |
| "Pixel Size", | |
| min_value=5, | |
| max_value=50, | |
| value=15, | |
| help="Lower values = more pixelated" | |
| ) | |
| padding = st.sidebar.slider( | |
| "Face Padding", | |
| min_value=0, | |
| max_value=50, | |
| value=10, | |
| help="Adds an extra padding around detected faces" | |
| ) | |
| # upload a file | |
| uploaded_file = st.file_uploader( | |
| "Choose an image file", | |
| type=['jpg', 'jpeg', 'png'], | |
| help="Upload a JPG, PNG or image" | |
| ) | |
| if uploaded_file is not None: | |
| # if image is uploaded open and display the image | |
| pil_image = Image.open(uploaded_file) | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.subheader("๐ธ Original Image") | |
| st.image(pil_image, use_column_width=True) | |
| # process the image | |
| with st.spinner("detecting and anonymizing faces"): | |
| # convert PIL to cv2 format | |
| cv2_image = pil_to_cv2(pil_image) | |
| # process based on selected method | |
| if method == "blur": | |
| processed_image, face_count = st.session_state.anonymizer.process_image( | |
| cv2_image, method=method, blur_strength=blur_strength, padding=padding | |
| ) | |
| else: | |
| processed_image, face_count = st.session_state.anonymizer.process_image( | |
| cv2_image, method=method, pixel_size=pixel_size, padding=padding | |
| ) | |
| # convert back to PIL for display | |
| result_pil = cv2_to_pil(processed_image) | |
| with col2: | |
| st.subheader("Anonymized Image") | |
| st.image(result_pil, use_column_width=True) | |
| # Show results info | |
| if face_count > 0: | |
| st.success(f"Successfully anonymized {face_count} face(s) using {method}") | |
| else: | |
| st.warning("No faces detected in the image") | |
| img_buffer = io.BytesIO() | |
| result_pil.save(img_buffer, format='PNG') | |
| img_buffer.seek(0) | |
| st.download_button( | |
| label="Download Anonymized Image", | |
| data=img_buffer.getvalue(), | |
| file_name=f"anonymized_{uploaded_file.name}", | |
| mime="image/png", | |
| use_container_width=True | |
| ) | |
| # Settings info | |
| with st.expander("โน๏ธ Processing Details"): | |
| st.write(f"**Method:** {method.title()}") | |
| if method == "blur": | |
| st.write(f"**Blur Strength:** {blur_strength}") | |
| else: | |
| st.write(f"**Pixel Size:** {pixel_size}") | |
| st.write(f"**Face Padding:** {padding}px") | |
| if __name__ == "__main__": | |
| main() |