CharlieAmalet's picture
Update app.py
8f50378 verified
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 # Factor to increase the image size that I can later antialiaze the corners
radius = radius * factor
image = Image.new('RGBA', (size[0] * factor, size[1] * factor), (0, 0, 0, 0))
# create corner
corner = Image.new('RGBA', (radius, radius), (0, 0, 0, 0))
draw = ImageDraw.Draw(corner)
# added the fill = .. you only drew a line, no fill
draw.pieslice((0, 0, radius * 2, radius * 2), 180, 270, fill=(255, 255, 255, alpha))
# max_x, max_y
mx, my = (size[0] * factor, size[1] * factor)
# paste corner rotated as needed
# use corners alpha channel as mask
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 both inner rects
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) # Smooth the corners
def max_rounding_radius(rect_coords):
# Extract the coordinates of the rectangle [x0, y0, x1, y1]
rect_coords = [coord
-1 for coord in rect_coords]
x0, y0, x1, y1 = rect_coords
# Calculate the width and height of the rectangle
width = x1 - x0
height = y1 - y0
# Determine the smallest dimension (width or height)
min_dimension = min(width, height)
# Calculate the maximum radius as half of the smallest dimension
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
# Crop the region of interest
region = result.crop(bboxe)
final_radius = round(max_rounding_radius(bboxe)* (radius/100))
mask = create_rounded_rectangle_mask(region, final_radius)
# Apply blur filter to the cropped region
blurred_region = region.filter(ImageFilter.GaussianBlur(radius=blur_amount))
# Paste the blurred region back onto the original image
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()