|
|
| import gradio as gr |
| import torch |
| from PIL import Image, ImageFilter, ImageDraw |
| from ultralytics import YOLO |
| import spaces |
|
|
| MODEL_PATH = "./models/face_yolov8n_v2.pt" |
| MODEL = YOLO(MODEL_PATH) |
|
|
| def create_rounded_rectangle_mask(image:Image.Image, radius, alpha=255): |
| size = image.size |
| factor = 5 |
| radius = radius * factor |
| image = Image.new('RGBA', (size[0] * factor, size[1] * factor), (0, 0, 0, 0)) |
|
|
| |
| corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0)) |
| draw = ImageDraw.Draw(corner) |
| |
| draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill=(255, 255, 255, alpha)) |
|
|
| |
| mx, my = (size[0] * factor, size[1] * factor) |
|
|
| |
| |
| image.paste(corner, (0, 0), corner) |
| image.paste(corner.rotate(90), (0, my - radius), corner.rotate(90)) |
| image.paste(corner.rotate(180), (mx - radius, my - radius), corner.rotate(180)) |
| image.paste(corner.rotate(270), (mx - radius, 0), corner.rotate(270)) |
|
|
| |
| draw = ImageDraw.Draw(image) |
| draw.rectangle([(radius, 0), (mx - radius, my)], fill=(255, 255, 255, alpha)) |
| draw.rectangle([(0, radius), (mx, my - radius)], fill=(255, 255, 255, alpha)) |
| return image.resize(size, Image.LANCZOS) |
|
|
| def max_rounding_radius(rect_coords): |
| |
| rect_coords = [coord |
| -1 for coord in rect_coords] |
| x0, y0, x1, y1 = rect_coords |
|
|
| |
| width = x1 - x0 |
| height = y1 - y0 |
|
|
| |
| min_dimension = min(width, height) |
|
|
| |
| return min_dimension // 2 |
|
|
| @spaces.GPU(enable_queue=True) |
| def generate_image(source_image:Image.Image, confidence=0.3, radius=50, blur_amount=10, margin=0): |
| if source_image is None: |
| return source_image |
|
|
| device = "cuda" if torch.cuda.is_available() else "cpu" |
| pred = MODEL(source_image, conf=confidence, device=device) |
| bboxes = pred[0].boxes.xyxy.cpu().numpy() |
| if bboxes.size == 0: |
| result = None |
| else: |
| bboxes = bboxes.tolist() |
| result = source_image.copy() |
|
|
| for bboxe in bboxes: |
| bboxe = [round(coord) for coord in bboxe] |
|
|
| mean_margin = margin // 2 |
| new_bboxe = bboxe[0]-mean_margin, bboxe[1]-mean_margin, bboxe[2]+mean_margin, bboxe[3]+mean_margin |
|
|
| new_x0, new_y0, new_x1, new_y1 = new_bboxe |
| new_width = new_x1 - new_x0 |
| new_height = new_y1 - new_y0 |
|
|
| if not (new_width > source_image.width or new_height > source_image.height): |
| bboxe = new_bboxe |
|
|
| |
| region = result.crop(bboxe) |
|
|
| final_radius = round(max_rounding_radius(bboxe)* (radius/100)) |
| mask = create_rounded_rectangle_mask(region, final_radius) |
|
|
| |
| blurred_region = region.filter(ImageFilter.GaussianBlur(radius=blur_amount)) |
|
|
| |
| result.paste(blurred_region, bboxe, mask) |
|
|
| return result |
|
|
| css = """ |
| img { |
| max-height: 500px; |
| object-fit: contain; |
| } |
| """ |
|
|
| with gr.Blocks(css=css) as FACE_2_BLUR: |
| with gr.Row(): |
| with gr.Column(): |
| image = gr.Image(label="Upload your image", type="pil") |
| generate_btn = gr.Button("Generate") |
| confidence = gr.Slider(label="Detection model confidence threshold.", value=0.3, minimum=0.0, maximum=1.0, step=0.01) |
| radius = gr.Slider(label="Edges radius in percentage.", value=50, minimum=0, maximum=100, step=1) |
| blur_amount = gr.Number(label="Controls the strength of the blur effect.", value=10, minimum=1, maximum=20, step=1) |
| margin = gr.Slider(label="Margin to add to the blurred box.", value=0, minimum=0, maximum=200) |
|
|
| with gr.Column(): |
| image_out = gr.Image(label="Blurred face image", type="pil") |
|
|
| generate_btn.click( |
| fn=generate_image, |
| inputs=[image, confidence, radius, blur_amount, margin], |
| outputs=image_out, |
| api_name="generate_blurred" |
| ) |
|
|
| FACE_2_BLUR.launch() |