{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Face Verification: One-to-One Face Comparison\n",
    "\n",
    "This notebook demonstrates how to verify if two face images belong to the same person using **UniFace**.\n",
    "\n",
    "## 1. Install UniFace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install -q uniface"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.3.1\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": "markdown",
   "metadata": {},
   "source": [
    "## 3. Initialize Face Analyzer\n",
    "We need detection and recognition models for face verification.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "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": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Detected 1 and 1 faces\n"
     ]
    }
   ],
   "source": [
    "image_path1 = '../assets/test_images/image0.jpg'\n",
    "image_path2 = '../assets/test_images/image1.jpg'\n",
    "\n",
    "image1 = cv2.imread(image_path1)\n",
    "image2 = cv2.imread(image_path2)\n",
    "\n",
    "# Analyze faces\n",
    "faces1 = analyzer.analyze(image1)\n",
    "faces2 = analyzer.analyze(image2)\n",
    "\n",
    "print(f'Detected {len(faces1)} and {len(faces2)} faces')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 381452 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
    "\n",
    "axes[0].imshow(cv2.cvtColor(image1, cv2.COLOR_BGR2RGB))\n",
    "axes[0].set_title('Image 1')\n",
    "axes[0].axis('off')\n",
    "\n",
    "axes[1].imshow(cv2.cvtColor(image2, cv2.COLOR_BGR2RGB))\n",
    "axes[1].set_title('Image 2')\n",
    "axes[1].axis('off')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Similarity: 0.1135\n"
     ]
    }
   ],
   "source": [
    "if faces1 and faces2:\n",
    "    face1 = faces1[0]\n",
    "    face2 = faces2[0]\n",
    "\n",
    "    similarity = face1.compute_similarity(face2)\n",
    "    print(f'Similarity: {similarity:.4f}')\n",
    "else:\n",
    "    print('Error: Could not detect faces')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Similarity: 0.1135\n",
      "Threshold: 0.6\n",
      "Result: Different people\n"
     ]
    }
   ],
   "source": [
    "THRESHOLD = 0.6\n",
    "\n",
    "if faces1 and faces2:\n",
    "    is_same_person = similarity > THRESHOLD\n",
    "\n",
    "    print(f'Similarity: {similarity:.4f}')\n",
    "    print(f'Threshold: {THRESHOLD}')\n",
    "    print(f'Result: {\"Same person\" if is_same_person else \"Different people\"}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Comparing multiple pairs:\n",
      "image0.jpg vs image1.jpg: 0.1135\n",
      "image0.jpg vs image2.jpg: 0.0833\n",
      "image1.jpg vs image2.jpg: -0.0082\n"
     ]
    }
   ],
   "source": [
    "image_pairs = [\n",
    "    ('../assets/test_images/image0.jpg', '../assets/test_images/image1.jpg'),\n",
    "    ('../assets/test_images/image0.jpg', '../assets/test_images/image2.jpg'),\n",
    "    ('../assets/test_images/image1.jpg', '../assets/test_images/image2.jpg'),\n",
    "]\n",
    "\n",
    "print('Comparing multiple pairs:')\n",
    "for img1_path, img2_path in image_pairs:\n",
    "    img1 = cv2.imread(img1_path)\n",
    "    img2 = cv2.imread(img2_path)\n",
    "\n",
    "    faces_a = analyzer.analyze(img1)\n",
    "    faces_b = analyzer.analyze(img2)\n",
    "\n",
    "    if faces_a and faces_b:\n",
    "        sim = faces_a[0].compute_similarity(faces_b[0])\n",
    "\n",
    "        img1_name = img1_path.split('/')[-1]\n",
    "        img2_name = img2_path.split('/')[-1]\n",
    "\n",
    "        print(f'{img1_name} vs {img2_name}: {sim:.4f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Notes\n",
    "\n",
    "- Similarity score ranges from -1 to 1 (higher = more similar)\n",
    "- Threshold of 0.6 is commonly used (above = same person, below = different)\n",
    "- Adjust threshold based on your use case (higher = stricter matching)"
   ]
  },
  {
   "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
}
