File size: 2,961 Bytes
4af0ad7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import gradio as gr
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import cv2
import torch
import facer
from typing import Tuple

def process_image(input_image: np.ndarray) -> np.ndarray:
    """
    Process the input image to apply face smoothing effect.
    
    Args:
        input_image (np.ndarray): Input image in numpy array format
        
    Returns:
        np.ndarray: Processed image with smoothing effect applied to face
    """
    device = 'cpu'
    
    # Convert numpy array to PIL Image and back to ensure correct format
    input_pil = Image.fromarray(input_image)
    
    # Convert image to format expected by facer
    image = facer.hwc2bchw(np.array(input_pil)).to(device=device)
    
    # Initialize face detector
    face_detector = facer.face_detector('retinaface/mobilenet', device=device)
    
    # Detect faces
    with torch.inference_mode():
        faces = face_detector(image)
        
    if len(faces['bbox']) == 0:
        raise ValueError("No faces detected in the image!")
    
    # Initialize face parser
    face_parser = facer.face_parser('farl/lapa/448', device=device)
    
    # Parse face features
    with torch.inference_mode():
        faces = face_parser(image, faces)
    
    # Process nose segment
    nose_array = np.array(faces['seg']['logits'][0][6])
    nose_array = np.where(nose_array > 0, 1, 0)
    
    # Process face segment
    face_array = np.array(faces['seg']['logits'][0][1])
    face_array = np.where(face_array > 0, 1, 0)
    
    # Combine face and nose arrays
    face_array = np.clip(face_array + nose_array, 0, 1)
    
    # Apply bilateral filter for smoothing
    smooth_img = cv2.bilateralFilter(input_image, 30, 75, 75)
    
    # Apply smoothing only to face region
    smooth_img[face_array == 0] = input_image[face_array == 0]
    
    return smooth_img

def smooth_face(input_img) -> Tuple[np.ndarray, str]:
    """
    Gradio interface function to process the image and handle errors.
    
    Args:
        input_img: Input image from Gradio interface
        
    Returns:
        Tuple[np.ndarray, str]: Processed image and status message
    """
    try:
        processed_img = process_image(input_img)
        return processed_img, "Face smoothing applied successfully!"
    except ValueError as e:
        return input_img, str(e)
    except Exception as e:
        return input_img, f"Error processing image: {str(e)}"

# Create Gradio interface
iface = gr.Interface(
    fn=smooth_face,
    inputs=gr.Image(type="numpy"),
    outputs=[
        gr.Image(type="numpy", label="Processed Image"),
        gr.Textbox(label="Status")
    ],
    title="Face Smoothing App",
    description="Upload an image to apply face smoothing effect. The app will detect faces and apply smoothing only to the face region.",
    examples=["face-4.jpg"]  # Add example images here if you have any
)

# Launch the app
if __name__ == "__main__":
    iface.launch()