|
|
|
from google.colab import drive |
|
drive.mount('/content/drive') |
|
!pip install deepface==0.0.79 tensorflow==2.10.0 opencv-python-headless==4.7.0.72 |
|
|
|
import gradio as gr |
|
import json |
|
import cv2 |
|
import numpy as np |
|
from deepface import DeepFace |
|
import matplotlib.pyplot as plt |
|
from PIL import Image |
|
import tempfile |
|
import os |
|
import pandas as pd |
|
import shutil |
|
|
|
def verify_faces(img1, img2, threshold=0.70, model="VGG-Face"): |
|
temp_dir = tempfile.mkdtemp() |
|
img1_path = os.path.join(temp_dir, "image1.jpg") |
|
img2_path = os.path.join(temp_dir, "image2.jpg") |
|
|
|
try: |
|
|
|
Image.fromarray(img1).save(img1_path) if isinstance(img1, np.ndarray) else img1.save(img1_path) |
|
Image.fromarray(img2).save(img2_path) if isinstance(img2, np.ndarray) else img2.save(img2_path) |
|
|
|
|
|
result = DeepFace.verify( |
|
img1_path=img1_path, |
|
img2_path=img2_path, |
|
model_name=model, |
|
distance_metric="cosine", |
|
threshold=threshold |
|
) |
|
|
|
|
|
fig, ax = plt.subplots(1, 2, figsize=(10, 5)) |
|
for idx, path in enumerate([img1_path, img2_path]): |
|
img = cv2.cvtColor(cv2.imread(path), cv2.COLOR_BGR2RGB) |
|
ax[idx].imshow(img) |
|
ax[idx].set_title(f"Image {idx+1}") |
|
ax[idx].axis("off") |
|
|
|
confidence = round((1 - result["distance"]) * 100, 2) |
|
plt.suptitle(f"{'β
MATCH' if result['verified'] else 'β NO MATCH'}\nConfidence: {confidence}%", |
|
fontsize=14, y=1.05) |
|
|
|
return fig, result |
|
|
|
except Exception as e: |
|
return None, {"error": str(e)} |
|
|
|
finally: |
|
shutil.rmtree(temp_dir, ignore_errors=True) |
|
|
|
def find_faces(query_img, db_folder, threshold=0.70, model="VGG-Face"): |
|
temp_dir = tempfile.mkdtemp() |
|
query_path = os.path.join(temp_dir, "query.jpg") |
|
|
|
try: |
|
|
|
Image.fromarray(query_img).save(query_path) if isinstance(query_img, np.ndarray) else query_img.save(query_path) |
|
|
|
|
|
if isinstance(db_folder, str): |
|
db_path = db_folder |
|
else: |
|
db_path = os.path.join(temp_dir, "db") |
|
os.makedirs(db_path, exist_ok=True) |
|
for i, file in enumerate(db_folder): |
|
ext = os.path.splitext(file.name)[1] |
|
shutil.copy(file.name, os.path.join(db_path, f"img_{i}{ext}")) |
|
|
|
|
|
dfs = DeepFace.find( |
|
img_path=query_path, |
|
db_path=db_path, |
|
model_name=model, |
|
distance_metric="cosine", |
|
enforce_detection=False, |
|
silent=True |
|
) |
|
|
|
|
|
df = dfs[0] if isinstance(dfs, list) else dfs |
|
df = df[df['distance'] <= threshold].sort_values('distance') |
|
|
|
|
|
fig, axes = plt.subplots(1, min(4, len(df)) if len(df) > 0 else plt.subplots(1, 1)) |
|
axes[0].imshow(cv2.cvtColor(cv2.imread(query_path), cv2.COLOR_BGR2RGB)) |
|
axes[0].set_title("Query Image") |
|
|
|
for idx, (_, row) in enumerate(df.head(3).iterrows()): |
|
if idx >= len(axes)-1: break |
|
match_img = cv2.cvtColor(cv2.imread(row['identity']), cv2.COLOR_BGR2RGB) |
|
axes[idx+1].imshow(match_img) |
|
axes[idx+1].set_title(f"Match {idx+1}\n{row['distance']:.2f}") |
|
|
|
return fig, df[['identity', 'distance']].to_dict('records') |
|
|
|
except Exception as e: |
|
return None, {"error": str(e)} |
|
|
|
finally: |
|
shutil.rmtree(temp_dir, ignore_errors=True) |
|
|
|
def analyze_face(img, actions=['age', 'gender', 'race', 'emotion']): |
|
temp_dir = tempfile.mkdtemp() |
|
img_path = os.path.join(temp_dir, "analyze.jpg") |
|
|
|
try: |
|
|
|
Image.fromarray(img).save(img_path) if isinstance(img, np.ndarray) else img.save(img_path) |
|
|
|
|
|
results = DeepFace.analyze( |
|
img_path=img_path, |
|
actions=actions, |
|
enforce_detection=False, |
|
detector_backend='opencv' |
|
) |
|
|
|
|
|
results = results if isinstance(results, list) else [results] |
|
fig = plt.figure(figsize=(10, 5)) |
|
|
|
|
|
plt.subplot(121) |
|
plt.imshow(cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)) |
|
plt.title("Input Image") |
|
plt.axis('off') |
|
|
|
|
|
plt.subplot(122) |
|
attrs = {k:v for res in results for k,v in res.items() if k != 'region'} |
|
plt.barh(list(attrs.keys()), list(attrs.values())) |
|
plt.title("Analysis Results") |
|
|
|
return fig, results |
|
|
|
except Exception as e: |
|
return None, {"error": str(e)} |
|
|
|
finally: |
|
shutil.rmtree(temp_dir, ignore_errors=True) |
|
|
|
|
|
with gr.Blocks(title="Face Analysis Tool", theme=gr.themes.Soft()) as demo: |
|
gr.Markdown("# π Face Analysis Toolkit") |
|
|
|
with gr.Tabs(): |
|
with gr.Tab("Verify Faces"): |
|
gr.Markdown("## Compare two faces") |
|
with gr.Row(): |
|
img1 = gr.Image(type="pil", label="First Face") |
|
img2 = gr.Image(type="pil", label="Second Face") |
|
thresh = gr.Slider(0.1, 1.0, 0.6, label="Matching Threshold") |
|
model = gr.Dropdown(["VGG-Face", "Facenet", "OpenFace"], value="VGG-Face") |
|
verify_btn = gr.Button("Compare Faces") |
|
result_plot = gr.Plot() |
|
result_json = gr.JSON() |
|
|
|
verify_btn.click( |
|
verify_faces, |
|
[img1, img2, thresh, model], |
|
[result_plot, result_json] |
|
) |
|
|
|
with gr.Tab("Find Faces"): |
|
gr.Markdown("## Find similar faces in database") |
|
query = gr.Image(type="pil", label="Query Image") |
|
db = gr.Textbox("/content/drive/MyDrive/db", label="Database Path") |
|
files = gr.File(file_count="multiple", label="Or upload files") |
|
find_btn = gr.Button("Search Faces") |
|
matches_plot = gr.Plot() |
|
matches_json = gr.JSON() |
|
|
|
find_btn.click( |
|
find_faces, |
|
[query, db, thresh, model], |
|
[matches_plot, matches_json] |
|
) |
|
files.change(lambda x: None, [files], [db]) |
|
|
|
with gr.Tab("Analyze Face"): |
|
gr.Markdown("## Analyze facial attributes") |
|
inp_img = gr.Image(type="pil", label="Input Image") |
|
analyze_btn = gr.Button("Analyze") |
|
analysis_plot = gr.Plot() |
|
analysis_json = gr.JSON() |
|
|
|
analyze_btn.click( |
|
analyze_face, |
|
[inp_img], |
|
[analysis_plot, analysis_json] |
|
) |
|
|
|
demo.launch() |