File size: 6,124 Bytes
87f88b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80c3ca8
 
 
 
 
 
87f88b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556a859
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import gradio as gr
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from sklearn.metrics.pairwise import cosine_similarity

import matplotlib.patches as patches
from facenet_pytorch import InceptionResnetV1, MTCNN
import mtcnn
import torch
import shutil
from PIL import Image
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

# Current directory
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

def save_uploaded_files(uploaded_file_paths, folder):
    if not os.path.exists(folder):
        os.makedirs(folder)
    else:
        shutil.rmtree(folder, ignore_errors=True)
        os.makedirs(folder)
    
    for uploaded_file_path in uploaded_file_paths:
        shutil.move(uploaded_file_path, os.path.join(folder, os.path.basename(uploaded_file_path)))

def face_detection(img, threshold=0.9, return_coords=True):
    # Detection
    detector = mtcnn.MTCNN()
    faces = detector.detect_faces(img)

    # Create a list to store face coordinates and cropped faces
    face_list = []
    face_coords = []

    # Draw bounding boxes and save each face as a separate image
    for i, face in enumerate(faces):
        if face['confidence']>= threshold:
            x, y, width, height = face['box']
            # Append face coordinates to the list
            face_coords.append((x, y, width, height))

            # Create a rectangle patch
            rect = patches.Rectangle((x, y), width, height, linewidth=2, edgecolor='orange', facecolor='none')

            # Add the rectangle to the plot
            plt.gca().add_patch(rect)

            # Crop the face and append to the list
            face_img = img[y:y+height, x:x+width]
            face_list.append(face_img)
        else:
            continue
    
    if return_coords:
        return face_list, face_coords
    else:
        return face_list

def generate_combined_reference():
    image_paths = []
    for image in os.listdir("reference"):
        image_paths.append("reference/" + image)
    
    # Open each image and resize or pad to the dimensions of the largest image
    max_width, max_height = 0, 0
    images_resized = []

    for path in image_paths:
        image = Image.open(path)
        width, height = image.size
        max_width = max(max_width, width)
        max_height = max(max_height, height)
        images_resized.append(image.resize((max_width, max_height)))
    
    # Create a new blank image with the combined width and the total height
    combined_image = Image.new("RGB", (max_width * len(images_resized), max_height))

    # Paste each image into the combined image
    for i, image in enumerate(images_resized):
        combined_image.paste(image, (i * max_width, 0))

    # Save the combined image
    combined_image.save("combined_reference.jpg")

    return "Created combined reference image."

def img_to_encoding(img):
    model = InceptionResnetV1(pretrained='vggface2').eval()
    img = cv2.resize(img, (160, 160))
    img = np.expand_dims(img, axis = 0)
    img = img / 255.0  # Normalize pixel values
    img = torch.tensor(img.transpose(0, 3, 1, 2), dtype = torch.float32)  # Adjust image format for PyTorch
    encoding = model(img)

    return encoding.flatten().detach().numpy()

def process_image():
    # Load group photo in "testing" folder
    group_photo_path = "testing/"
    group_photo = None

    for image in os.listdir(group_photo_path):
        group_photo = cv2.imread(group_photo_path + image)
        break

    if group_photo is None:
        return "No image found in testing folder."
    elif len(os.listdir(group_photo_path)) > 1:
        return "Can only process one image at a time."

    # Face detection
    group_photo_faces, group_photo_face_coords = face_detection(group_photo)

    # Generate reference image & do face detection
    generate_combined_reference()
    reference_photo = plt.imread("combined_reference.jpg")
    reference_faces = face_detection(reference_photo, threshold=0.9, return_coords=False)

    # Convert the reference faces & group photo into 128 dimensional vector
    ref_encodings = [img_to_encoding(face) for face in reference_faces]
    face_encodings = [img_to_encoding(face) for face in group_photo_faces]

    # Calculate cosine similarity between each face in the group photo and each reference face
    similarities = cosine_similarity(ref_encodings, face_encodings)

    # compute the average similarity for each face in face_list across all reference faces
    average_similarities = np.mean(similarities, axis=0)

    # Blur the face with the highest average similarity in group photo
    max_avg_similarity_idx = np.argmax(average_similarities)

    # Coordinates of the face with the highest average similarity
    (x, y, w, h) = group_photo_face_coords[max_avg_similarity_idx]

    # Blur the corresponding region in group photo
    img_obstruct = cv2.imread(group_photo_path + image)
    
    if max_avg_similarity_idx >= 0.3:
        img_obstruct[y:y+h, x:x+w] = cv2.blur(img_obstruct[y:y+h, x:x+w], (50, 50))
        img_obstruct = cv2.cvtColor(img_obstruct, cv2.COLOR_BGR2RGB)
    else:
        img_obstruct = cv2.cvtColor(img_obstruct, cv2.COLOR_BGR2RGB)

    # Delete all photos in reference and testing folder after processing
    shutil.rmtree('reference', ignore_errors=True)
    shutil.rmtree('testing', ignore_errors=True)
    
    return img_obstruct

# Setting up Gradio Interface
def interface_fn(reference_images, group_photo):
    save_uploaded_files(reference_images, 'reference')
    save_uploaded_files(group_photo, 'testing')
    return process_image()

iface = gr.Interface(
    fn=interface_fn,
    inputs=[
        gr.File(file_types=["image"], file_count="multiple"),
        gr.File(file_types=["image"], file_count="multiple")
    ],
    outputs=gr.Image(),
    title="Face Detection and Blurring",
    description="Upload multiple reference images and a group photo. The app will process the images and return the group photo with blurred faces."
)

if __name__ == "__main__":
    iface.launch()