mirror of
https://github.com/yakhyo/uniface.git
synced 2025-12-30 09:02:25 +00:00
361 lines
2.6 MiB
Plaintext
361 lines
2.6 MiB
Plaintext
|
|
{
|
||
|
|
"cells": [
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"# Face Search: One-to-Many Face Matching\n",
|
||
|
|
"\n",
|
||
|
|
"This notebook demonstrates how to build a face database and search for matching faces - useful for photo organization, security systems, and social media applications."
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": null,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [],
|
||
|
|
"source": [
|
||
|
|
"%pip install -q uniface"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## 2. Import Libraries"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 1,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"1.3.0\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"import cv2\n",
|
||
|
|
"import matplotlib.pyplot as plt\n",
|
||
|
|
"\n",
|
||
|
|
"import uniface\n",
|
||
|
|
"from uniface import FaceAnalyzer\n",
|
||
|
|
"from uniface.detection import RetinaFace\n",
|
||
|
|
"from uniface.recognition import ArcFace\n",
|
||
|
|
"\n",
|
||
|
|
"print(uniface.__version__)"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 2,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"✓ Model loaded (CoreML (Apple Silicon))\n",
|
||
|
|
"✓ Model loaded (CoreML (Apple Silicon))\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"analyzer = FaceAnalyzer(\n",
|
||
|
|
" detector=RetinaFace(conf_thresh=0.5),\n",
|
||
|
|
" recognizer=ArcFace()\n",
|
||
|
|
")"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 3,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Detected 1 face with 512D features\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Load Einstein's photo\n",
|
||
|
|
"einstein_path = '../assets/einstien.png'\n",
|
||
|
|
"einstein_image = cv2.imread(einstein_path)\n",
|
||
|
|
"\n",
|
||
|
|
"# Get Einstein's face features\n",
|
||
|
|
"einstein_faces = analyzer.analyze(einstein_image)\n",
|
||
|
|
"\n",
|
||
|
|
"if einstein_faces:\n",
|
||
|
|
" einstein_face = einstein_faces[0]\n",
|
||
|
|
" print(f'Detected {len(einstein_faces)} face with {einstein_face.embedding.shape[1]}D features')\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 4,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Detected 29 people in the group photo\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Load the group photo\n",
|
||
|
|
"group_photo_path = '../assets/scientists.png'\n",
|
||
|
|
"group_photo = cv2.imread(group_photo_path)\n",
|
||
|
|
"\n",
|
||
|
|
"# Find all faces in the group photo\n",
|
||
|
|
"group_faces = analyzer.analyze(group_photo)\n",
|
||
|
|
"print(f'Detected {len(group_faces)} people in the group photo')"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 5,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABW8AAAJOCAYAAADI/fWYAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvQe4rVtVn7/B3ruCIihVOlwQBREQEVDEglGjxhpbLLGXaCyxJir2FmOJLRp7wwIRFJXeBORS5AIK2BHsgHL+z7se3v1/z7hzrb3POftyL/CN51nPWusrs4455hi/OeaY1zp37ty5o4022mijjTbaaKONNtpoo4022mijjTbaaKONrlF07au7ABtttNFGG2200UYbbbTRRhtttNFGG2200UYbXZk28HajjTbaaKONNtpoo4022mijjTbaaKONNtroGkgbeLvRRhtttNFGG2200UYbbbTRRhtttNFGG210DaQNvN1oo4022mijjTbaaKONNtpoo4022mijjTa6BtIG3m600UYbbbTRRhtttNFGG2200UYbbbTRRhtdA2kDbzfaaKONNtpoo4022mijjTbaaKONNtpoo42ugbSBtxtttNFGG2200UYbbbTRRhtttNFGG2200UbXQNrA24022mijjTbaaKONNtpoo4022mijjTbaaKNrIG3g7UYbbbTRRhtttNFGG2200UYbbbTRRhtttNE1kDbwdqMlvcu7vMvRta51rd3nq7/6q6/u4mx0dHT0CZ/wCcd9co973ONqK4dl4PO///f/PvH55z73uee98zu/8ztH11R6zGMec/T+7//+R2/zNm9zdO1rX/u4zC9+8YuPXpPomsJLVwVtsmujjTbaaKONNtpoo4022mij1yTawNtXY/rVX/3V80AxgKfSs5/97PPuf9zHfdyV0rj//e9/fP893uM9XoWlv+ZTQUd+b/SaTX/+53++A25/8zd/8+hFL3rR0blz545eHQjwteN83+eaxMMA+NfUsm200UYbbbTRRhtttNFGG2200TWJXvfqLsBGF093vetdd96Br3jFK3b/H/7whx/d6U53Or7/e7/3e+c9P//z3u///u8f/7/b3e52lZd5o9c+euu3fuujb/7mbz7+f6Mb3ejomki/9Vu/tQNtIQDFz/zMzzy6wQ1usPv/Rm/0RkevSfTv//2/P7rVrW61+/3O7/zO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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1500x600 with 2 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"fig, axes = plt.subplots(1, 2, figsize=(15, 6))\n",
|
||
|
|
"\n",
|
||
|
|
"# Display Einstein's photo\n",
|
||
|
|
"axes[0].imshow(cv2.cvtColor(einstein_image, cv2.COLOR_BGR2RGB))\n",
|
||
|
|
"axes[0].set_title(\"Who we're looking for: Einstein\", fontsize=14, fontweight='bold')\n",
|
||
|
|
"axes[0].axis('off')\n",
|
||
|
|
"\n",
|
||
|
|
"# Display the group photo\n",
|
||
|
|
"axes[1].imshow(cv2.cvtColor(group_photo, cv2.COLOR_BGR2RGB))\n",
|
||
|
|
"axes[1].set_title(f'Where we search: Group of {len(group_faces)} scientists', fontsize=14, fontweight='bold')\n",
|
||
|
|
"axes[1].axis('off')\n",
|
||
|
|
"\n",
|
||
|
|
"plt.tight_layout()\n",
|
||
|
|
"plt.show()\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 6,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Top 5 most similar people:\n",
|
||
|
|
"1. Person #11: similarity = 0.6035\n",
|
||
|
|
"2. Person #17: similarity = 0.1830\n",
|
||
|
|
"3. Person #15: similarity = 0.1683\n",
|
||
|
|
"4. Person #25: similarity = 0.1680\n",
|
||
|
|
"5. Person #24: similarity = 0.1545\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"if not einstein_faces or not group_faces:\n",
|
||
|
|
" print('Error: Could not detect faces')\n",
|
||
|
|
"else:\n",
|
||
|
|
" # Compare Einstein with each person in the group\n",
|
||
|
|
" matches = []\n",
|
||
|
|
" for i, person in enumerate(group_faces):\n",
|
||
|
|
" similarity = einstein_face.compute_similarity(person)\n",
|
||
|
|
" matches.append((i, similarity))\n",
|
||
|
|
"\n",
|
||
|
|
" # Sort by similarity (best matches first)\n",
|
||
|
|
" matches.sort(key=lambda x: x[1], reverse=True)\n",
|
||
|
|
"\n",
|
||
|
|
" # Show top 5 matches\n",
|
||
|
|
" print('Top 5 most similar people:')\n",
|
||
|
|
" for rank, (person_idx, similarity) in enumerate(matches[:5], 1):\n",
|
||
|
|
" print(f'{rank}. Person #{person_idx + 1}: similarity = {similarity:.4f}')\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 7,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"if einstein_faces and group_faces:\n",
|
||
|
|
" # Get the best match\n",
|
||
|
|
" best_match_idx, best_similarity = matches[0]\n",
|
||
|
|
"\n",
|
||
|
|
" # Draw bounding boxes\n",
|
||
|
|
" result_image = group_photo.copy()\n",
|
||
|
|
"\n",
|
||
|
|
" for i, person in enumerate(group_faces):\n",
|
||
|
|
" bbox = person.bbox.astype(int)\n",
|
||
|
|
"\n",
|
||
|
|
" if i == best_match_idx:\n",
|
||
|
|
" color = (0, 255, 0)\n",
|
||
|
|
" thickness = 3\n",
|
||
|
|
" else:\n",
|
||
|
|
" color = (128, 128, 128)\n",
|
||
|
|
" thickness = 1\n",
|
||
|
|
"\n",
|
||
|
|
" cv2.rectangle(result_image, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, thickness)\n",
|
||
|
|
"\n",
|
||
|
|
" if i == best_match_idx:\n",
|
||
|
|
" label = f'Match: {best_similarity:.3f}'\n",
|
||
|
|
" cv2.putText(result_image, label, (bbox[0], bbox[1] - 10),\n",
|
||
|
|
" cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2)\n",
|
||
|
|
"\n",
|
||
|
|
" plt.figure(figsize=(15, 10))\n",
|
||
|
|
" plt.imshow(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB))\n",
|
||
|
|
" plt.title(f'Best match: Person #{best_match_idx + 1}', fontsize=14)\n",
|
||
|
|
" plt.axis('off')\n",
|
||
|
|
" plt.tight_layout()\n",
|
||
|
|
" plt.show()\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 8,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"data": {
|
||
|
|
"image/png": "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
|
||
|
|
"text/plain": [
|
||
|
|
"<Figure size 1600x400 with 4 Axes>"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"metadata": {},
|
||
|
|
"output_type": "display_data"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"if einstein_faces and group_faces:\n",
|
||
|
|
" # Show top 3 matches\n",
|
||
|
|
" top_k = min(3, len(matches))\n",
|
||
|
|
"\n",
|
||
|
|
" fig, axes = plt.subplots(1, top_k + 1, figsize=(16, 4))\n",
|
||
|
|
"\n",
|
||
|
|
" # Show Einstein's face\n",
|
||
|
|
" einstein_rgb = cv2.cvtColor(einstein_image, cv2.COLOR_BGR2RGB)\n",
|
||
|
|
" axes[0].imshow(einstein_rgb)\n",
|
||
|
|
" axes[0].set_title(\"Query\", fontsize=12)\n",
|
||
|
|
" axes[0].axis('off')\n",
|
||
|
|
"\n",
|
||
|
|
" # Show top 3 matches from the group\n",
|
||
|
|
" for i, (person_idx, similarity) in enumerate(matches[:top_k]):\n",
|
||
|
|
" person = group_faces[person_idx]\n",
|
||
|
|
" bbox = person.bbox.astype(int)\n",
|
||
|
|
"\n",
|
||
|
|
" # Crop this person's face\n",
|
||
|
|
" face_crop = group_photo[bbox[1]:bbox[3], bbox[0]:bbox[2]]\n",
|
||
|
|
"\n",
|
||
|
|
" if face_crop.size > 0:\n",
|
||
|
|
" face_rgb = cv2.cvtColor(face_crop, cv2.COLOR_BGR2RGB)\n",
|
||
|
|
" axes[i + 1].imshow(face_rgb)\n",
|
||
|
|
" axes[i + 1].set_title(f'Match {i + 1}: {similarity:.3f}', fontsize=12)\n",
|
||
|
|
" axes[i + 1].axis('off')\n",
|
||
|
|
"\n",
|
||
|
|
" plt.tight_layout()\n",
|
||
|
|
" plt.show()\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "code",
|
||
|
|
"execution_count": 9,
|
||
|
|
"metadata": {},
|
||
|
|
"outputs": [
|
||
|
|
{
|
||
|
|
"name": "stdout",
|
||
|
|
"output_type": "stream",
|
||
|
|
"text": [
|
||
|
|
"Best match: Person #11\n",
|
||
|
|
"Similarity: 0.6035\n",
|
||
|
|
"Threshold: 0.6\n",
|
||
|
|
"Result: Match found (Einstein is person #11)\n"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"source": [
|
||
|
|
"# Use threshold to determine if it's a match\n",
|
||
|
|
"THRESHOLD = 0.6\n",
|
||
|
|
"\n",
|
||
|
|
"if einstein_faces and group_faces:\n",
|
||
|
|
" best_match_idx, best_similarity = matches[0]\n",
|
||
|
|
"\n",
|
||
|
|
" print(f'Best match: Person #{best_match_idx + 1}')\n",
|
||
|
|
" print(f'Similarity: {best_similarity:.4f}')\n",
|
||
|
|
" print(f'Threshold: {THRESHOLD}')\n",
|
||
|
|
"\n",
|
||
|
|
" if best_similarity > THRESHOLD:\n",
|
||
|
|
" print(f'Result: Match found (Einstein is person #{best_match_idx + 1})')\n",
|
||
|
|
" else:\n",
|
||
|
|
" print(f'Result: No match (similarity below threshold)')\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"metadata": {},
|
||
|
|
"source": [
|
||
|
|
"## Notes\n",
|
||
|
|
"\n",
|
||
|
|
"- Similarity score ranges from -1 to 1 (higher = more similar)\n",
|
||
|
|
"- Threshold of 0.6 is commonly used (above = match, below = no match)\n",
|
||
|
|
"- Adjust threshold based on your use case (higher = stricter matching)\n"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"cell_type": "markdown",
|
||
|
|
"metadata": {},
|
||
|
|
"source": []
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"metadata": {
|
||
|
|
"kernelspec": {
|
||
|
|
"display_name": "base",
|
||
|
|
"language": "python",
|
||
|
|
"name": "python3"
|
||
|
|
},
|
||
|
|
"language_info": {
|
||
|
|
"codemirror_mode": {
|
||
|
|
"name": "ipython",
|
||
|
|
"version": 3
|
||
|
|
},
|
||
|
|
"file_extension": ".py",
|
||
|
|
"mimetype": "text/x-python",
|
||
|
|
"name": "python",
|
||
|
|
"nbconvert_exporter": "python",
|
||
|
|
"pygments_lexer": "ipython3",
|
||
|
|
"version": "3.13.5"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"nbformat": 4,
|
||
|
|
"nbformat_minor": 4
|
||
|
|
}
|