Artificial Intelligence

Biometric Face Analysis with DeepFace 2

• Bookmarks: 2


in this second part we will go into the deepface embeddings inside the json file and we will see how deepface works in comparison to a landmark technique.

The image is fed into a deep convolutional neural network (CNN)—for example, a network like FaceNet. The network applies several layers of convolution, non-linear activation (ReLU or similar), and pooling. Each convolution layer extracts local features (edges, textures, patterns) by applying learned filters over the image. The output from these layers is a set of feature maps that capture various aspects of the face.

Each individual number (like the first number 0.0761 or the second number 1.3274 in your example) represents one dimension of the face’s embedding space. While each dimension is not directly interpretable (e.g., “this number means the shape of the nose”), collectively they form a unique signature for the face. The value in each dimension is an abstract representation learned from data. It encodes complex facial features that help the network distinguish between different faces.

one embedding number is the result of a neuron’s weighted sum of inputs (from the deep network’s preceding layers), plus a bias, potentially transformed by a non-linear function, and then normalized as part of the overall face representation. This process is optimized during training to ensure that the entire embedding vector effectively captures the uniqueness of each face for tasks such as recognition or verification.

what is an neural network embedding compared to landmarks as points on the face?
while landmarks give you direct geometric positions on the face, embeddings provide a rich, learned representation of the overall facial appearance that can capture more complex details useful for identity matching and deep learning applications.

The comparison works by first computing the Euclidean distance between each pair of face embeddings. Each embedding is a high-dimensional vector that represents the unique features of a face. In the script, we build a distance matrix where each element represents the Euclidean distance between two embeddings; a lower distance means the two faces are more similar, while a higher distance means they are more different. Then, for each face, the script finds the smallest nonzero distance (indicating the closest match) and the largest distance (indicating the most different face). This method allows you to quantitatively compare how similar or different each face is based solely on its numerical representation.

DeepFace Example Script 1 shown in the Video (Analyse each Face)

import os
import json
import shutil
import numpy as np
import cv2
from deepface import DeepFace

# Set input, output, and log folders
input_folder = "inputs"
output_folder = "outputs"
log_file = "debug_log.txt"

os.makedirs(input_folder, exist_ok=True)
os.makedirs(output_folder, exist_ok=True)

with open(log_file, "w") as log:
    log.write("Debug Log - DeepFace Processing\n\n")

def resize_image(image_path, max_width=800, max_height=800):
    img = cv2.imread(image_path)
    if img is None:
        return None
    height, width = img.shape[:2]
    if width > max_width or height > max_height:
        scale = min(max_width / width, max_height / height)
        new_size = (int(width * scale), int(height * scale))
        img = cv2.resize(img, new_size, interpolation=cv2.INTER_AREA)
    return img

# Convert analysis data to plain dictionary by converting through JSON
def convert_analysis(data):
    return json.loads(json.dumps(data, default=lambda x: float(x) if isinstance(x, np.float32) else repr(x)))

for filename in os.listdir(input_folder):
    if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
        input_path = os.path.join(input_folder, filename)
        output_image_path = os.path.join(output_folder, filename)
        json_output_path = os.path.join(output_folder, os.path.splitext(filename)[0] + ".json")
        txt_summary_path = os.path.join(output_folder, os.path.splitext(filename)[0] + "_dominant.txt")
        try:
            print("Processing:", filename)
            img = resize_image(input_path)
            if img is None:
                print("Skipping", filename, "(Invalid image)")
                continue
            analysis = DeepFace.analyze(input_path, actions=['age', 'gender', 'race', 'emotion'],
                                        enforce_detection=False, detector_backend='mtcnn')
            if isinstance(analysis, list) and len(analysis) > 0:
                analysis = max(analysis, key=lambda x: x.get("face_confidence", 0))
            if analysis.get("face_confidence", 0) < 0.9:
                print("Skipping", filename, "(Low confidence face detected)")
                continue

            plain_analysis = convert_analysis(analysis)

            try:
                embeddings = DeepFace.represent(input_path, model_name="Facenet", enforce_detection=False)
            except Exception as e:
                embeddings = None
                with open(log_file, "a") as log:
                    log.write("Error generating embeddings for " + filename + ": " + str(e) + "\n")

            full_data = {"analysis": plain_analysis, "embeddings": embeddings}

            with open(json_output_path, "w", encoding="utf-8") as f:
                json.dump(full_data, f, indent=4, ensure_ascii=False,
                          default=lambda x: float(x) if isinstance(x, np.float32) else repr(x))
                f.flush()
                os.fsync(f.fileno())
            with open(json_output_path, "r", encoding="utf-8") as f:
                content = f.read().strip()
                if not content.endswith("}"):
                    raise ValueError("JSON file was not fully written")
            shutil.copy(input_path, output_image_path)
            print("Processed:", filename, "->", json_output_path)

            # Create a text file with dominant outputs
            dominant_summary = {
                "age": plain_analysis.get("age"),
                "face_confidence": plain_analysis.get("face_confidence"),
                "dominant_gender": plain_analysis.get("dominant_gender"),
                "dominant_race": plain_analysis.get("dominant_race"),
                "dominant_emotion": plain_analysis.get("dominant_emotion")
            }
            with open(txt_summary_path, "w", encoding="utf-8") as f:
                for key, value in dominant_summary.items():
                    f.write(str(key) + ": " + str(value) + "\n")
            print("Dominant summary saved to:", txt_summary_path)

        except Exception as e:
            print("Error with", filename, ":", e)
            with open(log_file, "a") as log:
                log.write("Error with " + filename + ": " + str(e) + "\n")
print("All images processed and saved.")
print("Debug log saved:", log_file)

DeepFace Example Script 2 shown in the Video (compare two Faces)

import os
import json
import shutil
import numpy as np

# Define the outputs directory
outputs_dir = "outputs"

# Dictionary to hold embedding vectors for each file
embeddings_dict = {}

# Loop through all files in the outputs directory and load embeddings
for filename in os.listdir(outputs_dir):
    if filename.endswith(".json"):
        filepath = os.path.join(outputs_dir, filename)
        with open(filepath, "r", encoding="utf-8") as f:
            data = json.load(f)
            # Assume each JSON has an "embeddings" list with at least one object.
            if "embeddings" in data and len(data["embeddings"]) > 0:
                emb = data["embeddings"][0].get("embedding")
                if emb is not None:
                    embeddings_dict[filename] = np.array(emb, dtype=float)

# Get a sorted list of filenames (for consistency)
files = sorted(embeddings_dict.keys())
n = len(files)

# Compute pairwise Euclidean distances
distance_matrix = np.zeros((n, n))
for i in range(n):
    for j in range(i + 1, n):
        dist = np.linalg.norm(embeddings_dict[files[i]] - embeddings_dict[files[j]])
        distance_matrix[i, j] = dist
        distance_matrix[j, i] = dist

# Print out the list of filenames
print("List of JSON files:")
for f in files:
    print(f)

print("\nPairwise Euclidean Distances between embeddings:")
for i in range(n):
    for j in range(i + 1, n):
        print(f"Distance between {files[i]} and {files[j]}: {distance_matrix[i, j]:.4f}")

# For each file, find the closest and the most different image based solely on embeddings
print("\nClosest and most different pairs based on embeddings:")
for i in range(n):
    # Get distances for current file, ignoring itself (distance 0)
    distances = distance_matrix[i].copy()
    distances[i] = np.inf  # Exclude self-distance from minimum search
    closest_index = np.argmin(distances)
    farthest_index = np.argmax(distance_matrix[i])
    print(f"For {files[i]}:")
    print(f"   Closest match: {files[closest_index]} with a distance of {distance_matrix[i, closest_index]:.4f}")
    print(f"   Most different: {files[farthest_index]} with a distance of {distance_matrix[i, farthest_index]:.4f}")

Sample Output

Closest and most different pairs based on embeddings:
For ComfyUI_1100463988676150__00001_.json:
   Closest match: ComfyUI_438562564760470__00001_.json with a distance of 10.5618
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 14.7956
For ComfyUI_1103713160253224__00001_.json:
   Closest match: ComfyUI_808248245634676__00001_.json with a distance of 9.1734
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 15.1873
For ComfyUI_409685121763906__00001_.json:
   Closest match: ComfyUI_438562564760470__00001_.json with a distance of 10.5327
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 15.8965
For ComfyUI_429550121028527__00001_.json:
   Closest match: ComfyUI_1103713160253224__00001_.json with a distance of 10.5238
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 14.7593
For ComfyUI_438562564760470__00001_.json:
   Closest match: ComfyUI_1103713160253224__00001_.json with a distance of 10.3624
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 13.7726
For ComfyUI_566139754687745__00001_.json:
   Closest match: ComfyUI_757238577944334__00001_.json with a distance of 13.1367
   Most different: ComfyUI_409685121763906__00001_.json with a distance of 15.8965
For ComfyUI_620597068933962__00001_.json:
   Closest match: ComfyUI_757238577944334__00001_.json with a distance of 2.7232
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 13.7686
For ComfyUI_757238577944334__00001_.json:
   Closest match: ComfyUI_620597068933962__00001_.json with a distance of 2.7232
   Most different: ComfyUI_438562564760470__00001_.json with a distance of 13.1565
For ComfyUI_808248245634676__00001_.json:
   Closest match: ComfyUI_1103713160253224__00001_.json with a distance of 9.1734
   Most different: ComfyUI_566139754687745__00001_.json with a distance of 14.7380
2 recommended
comments icon0 comments
0 notes
27 views
bookmark icon

Write a comment...

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.