使用 Python、OpenCV、Transformer 和 Qdrant 构建面部识别应用程序

作者:微信公众号:【架构师老卢】
5-4 14:24
97

概述:人脸注册应用程序工作流方法 1.使用 Python、OpenCV 和 Qdrant 进行面部识别面部识别技术已成为一股无处不在的力量,重塑了安全、社交媒体和智能手机身份验证等行业。在这篇博客中,我们深入探讨了面部识别的迷人领域,并配备了强大的 Python、OpenCV、图像嵌入和 Qdrant 三重奏。加入我们的旅程,我们将解开创建强大的面部识别系统的复杂性。第 1 部分:面部识别简介在第 1 部分中,我们通过深入研究面部识别技术的基础知识来奠定基础。了解基本原理,探索其应用,并掌握 Python 和 OpenCV 在我们的开发堆栈中的重要性。第 2 部分:设置环境任何项目的关键步骤都是准备

人脸注册应用程序工作流

方法 1.使用 Python、OpenCV 和 Qdrant 进行面部识别

面部识别技术已成为一股无处不在的力量,重塑了安全、社交媒体和智能手机身份验证等行业。在这篇博客中,我们深入探讨了面部识别的迷人领域,并配备了强大的 Python、OpenCV、图像嵌入和 Qdrant 三重奏。加入我们的旅程,我们将解开创建强大的面部识别系统的复杂性。

第 1 部分:面部识别简介

在第 1 部分中,我们通过深入研究面部识别技术的基础知识来奠定基础。了解基本原理,探索其应用,并掌握 Python 和 OpenCV 在我们的开发堆栈中的重要性。

第 2 部分:设置环境

任何项目的关键步骤都是准备开发环境。了解如何无缝集成 Python、OpenCV 和 Qdrant,为我们的面部识别系统创建一个和谐的生态系统。我们提供分步说明,确保您在继续前进之前有坚实的基础。

第 3 部分:面部识别算法的实现

随着基础工作到位,我们深入研究项目的核心。探索面部识别算法的复杂性,并见证我们使用 Python 和 OpenCV 实现它们时的魔力。揭示人脸检测、特征提取和模型训练的内部工作原理。

第 4 部分:与 Qdrant 的数据库集成

如果没有强大的数据库来有效地存储和管理面部数据,任何面部识别系统都是不完整的。在最后一期中,我们将指导您完成Qdrant的集成,以增强我们系统的存储和检索功能。见证 Python、OpenCV 和 Qdrant 之间的协同作用,我们将项目推向高潮。

在本博客结束时,您将全面了解面部识别技术以及开发自己的系统的实用技能。

分步实施

  1. 将所有感兴趣的图片下载到本地文件夹中。
  2. 从图片中识别和提取人脸。
  3. 从提取的人脸计算人脸嵌入。
  4. 将这些面部嵌入存储在 Qdrant 数据库中。
  5. 获取同事的照片以进行识别。
  6. 将脸部与提供的图片相匹配。
  7. 计算所提供图片中已识别人脸的嵌入。
  8. 利用 Qdrant 距离功能从数据库中检索最接近的匹配面孔和相应的照片。

该实验展示了 Python OpenCV 和高级 AI 技术在创建复杂的面部识别/搜索应用程序方面的实际实施,展示了增强用户交互和认知响应的潜力。由于图像是敏感数据,我们不想依赖任何在线服务或将它们上传到互联网上。上面定义的整个管道被开发为100%在本地工作。

技术栈

  • Qdrant:用于存储图像嵌入的矢量存储。
  • OpenCV:从图像中检测人脸。为了从图片中“提取”人脸,我们使用了 Python、OpenCV、计算机视觉工具和预训练的 Haar Cascade 模型
  • imgbeddings:一个 Python 包,用于使用 OpenAI 强大的 CLIP 模型通过 Hugging Face 转换器从图像生成嵌入向量_。_

OpenCV 概述

OpenCV 或开源计算机视觉库是一个开源计算机视觉和机器学习软件库。OpenCV 最初由英特尔开发,现在由开发人员社区维护。它为图像和视频分析提供了广泛的工具和功能,包括用于图像处理、计算机视觉和机器学习的各种算法。

OpenCV 的主要功能包括:

  • **图像处理:**OpenCV 为基本和高级图像处理任务提供了大量功能,例如过滤、变换和颜色处理。
  • **计算机视觉算法:**该库包括各种计算机视觉算法的实现,包括特征检测、对象识别和图像拼接。
  • **机器学习:**OpenCV 与机器学习框架集成,并提供用于训练和部署机器学习模型的工具。这对于对象检测和面部识别等任务特别有用。
  • 相机校准:OpenCV 包括相机校准功能,这在计算机视觉应用中至关重要,用于校正相机镜头引起的失真。
  • **实时计算机视觉:**它支持实时计算机视觉应用程序,使其适用于视频分析、运动跟踪和增强现实等任务。
  • **跨平台支持:**OpenCV 兼容各种操作系统,包括 Windows、Linux、macOS、Android 和 iOS。这使得它适用于广泛的应用。
  • 社区支持:OpenCV 拥有庞大而活跃的社区,在全球研究人员、开发人员和工程师的贡献下不断发展。

OpenCV 广泛用于学术界、工业界和研究领域,用于从简单的图像处理到复杂的计算机视觉和机器学习应用程序的任务。它的多功能性和全面的工具集使其成为在计算机视觉领域工作的开发人员的首选库。

imgbeddings 概述

这是一个 Python 包,用于使用 OpenAI 强大的 CLIP 模型通过 Hugging Face 转换器从图像生成嵌入向量。这些图像嵌入源自一个图像模型,该模型在2020年年中之前已经看到了整个互联网,可用于许多方面:无监督聚类(例如通过umap),嵌入搜索(例如通过faiss),以及将下游用于其他与框架无关的ML / AI任务,例如构建分类器或计算图像相似性。

  • 嵌入生成模型是 ONNX INT8 量化的,这意味着它们在 CPU 上的速度提高了 20-30%,在磁盘上的速度要小得多,并且不需要 PyTorch 或 TensorFlow 作为依赖项!
  • 由于 CLIP 的零拍摄性能,适用于许多不同的图像域。
  • 包括用于使用主成分分析 (PCA) 来降低生成的嵌入的维数而不会丢失太多信息的实用程序。

Vector Store 解释

定义

向量存储是专门用于高效存储和检索向量嵌入的数据库。这种专业化至关重要,因为像 SQL 这样的传统数据库没有针对处理大量向量数据进行微调。

嵌入的作用

嵌入在高维空间中以数值向量格式表示数据,通常是非结构化数据,如文本或图像。传统的关系数据库不适合存储和检索这些向量表示。

矢量存储的主要特性

  • 高效索引:向量存储可以使用相似性算法对相似向量进行索引和快速搜索。
  • 增强检索:此功能允许应用程序根据提供的目标向量查询识别相关向量。

Qdrant 概述

https://qdrant.tech/documentation

Qdrant 是一个专门的矢量相似性搜索引擎,旨在通过用户友好的 API 提供生产就绪服务。它有助于点(向量)以及其他有效载荷的存储、搜索和管理。这些有效载荷作为补充信息,提高搜索的精度,并为用户提供有价值的数据。

Qdrant 的入门是无缝的。利用 Python qdrant-client,访问 Qdrant 的最新 Docker 映像并建立本地连接,或探索 Qdrant 的 Cloud 免费套餐选项,直到您准备好进行全面过渡。

高级 Qdrant 架构

了解语义相似性

在一组文档或术语的上下文中,语义相似性是一种指标,它根据项目的含义或语义内容的相似性来衡量项目之间的距离,而不是依赖于词典相似性。这涉及使用数学工具来评估语言单元、概念或实例之间语义关系的强度。通过此过程获得的数字描述是通过比较支持其含义或描述其性质的信息而得出的。

区分语义相似性和语义相关性至关重要。语义关联性包括两个术语之间的任何关系,而语义相似性具体涉及“是”关系。这种区别阐明了语义比较的细微差别及其在各种语言和概念环境中的应用。

代码实现

安装所需的依赖项

pip install qdrant-client imgbeddings pillow opencv-python

创建一个文件夹来存储所需的图像

mkdir photos

下载模型参数文件

OpenCV GitHub 存储库下载haarcascade_frontalface_default.xml预训练的 Haar 级联模型并将其存储在本地。

此代码是在 Google Colab 上使用 Python 实现的。

示例代码

导入所需的依赖项

#import required libraries
import cv2
import numpy as np
from imgbeddings import imgbeddings
from PIL import Image

从图像中提取人脸的辅助功能

def detect_face(image_path,target_path):
  # loading the haar case algorithm file into alg variable
  alg = "haarcascade_frontalface_default.xml"
  # passing the algorithm to OpenCV
  haar_cascade = cv2.CascadeClassifier(alg)
  # loading the image path into file_name variable
  file_name = image_path
  # reading the image
  img = cv2.imread(file_name, 0)
  # creating a black and white version of the image
  gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
  # detecting the faces
  faces = haar_cascade.detectMultiScale(gray_img, scaleFactor=1.05, minNeighbors=2, minSize=(100, 100))
  # for each face detected
  for x, y, w, h in faces:
      # crop the image to select only the face
      cropped_image = img[y : y + h, x : x + w]
      # loading the target image path into target_file_name variable
      target_file_name = target_path
      cv2.imwrite(
          target_file_name,
          cropped_image,
      )

以下代码负责

faces = haar_cascade.detectMultiScale(gray_img, scaleFactor=1.05, minNeighbors=2, minSize=(100, 100))

哪里:

  • gray_img — 我们需要查找人脸的源图像。
  • scaleFactor — 比例因子;比率越高,压缩越大,图像质量损失越大。
  • minNeighbors — 要收集的邻居面孔数量。越高,同一张脸可以多次出现的次数就越多。
  • minSize — 检测到的人脸的最小大小,在本例中为 100 像素的正方形。

for 循环遍历检测到的所有人脸,并将它们存储在单独的文件中。您可能希望定义一个变量(可能使用 x 和 y 参数)以将各种面孔存储在不同的文件中。

人脸检测阶段的结果并不完美:它可以识别出四张可见的四张脸中的三张,但对于我们的目的来说已经足够好了。您可以微调算法参数,以找到更适合您的用例的参数。

用于计算嵌入的辅助函数

def generate_embeddings(image_path):
  #
  # loading the face image path into file_name variable
  file_name = "/content/target_photo_1.jpg"
  # opening the image
  img = Image.open(file_name)
  # loading the `imgbeddings`
  ibed = imgbeddings()
  # calculating the embeddings
  embedding = ibed.to_embeddings(img)[0]
  emb_array = np.array(embedding).reshape(1,-1)
  return emb_array

从图像中检测人脸并转换为目标文件夹中的灰度图像

os.mkdir("target")
# loop through the images in the photos folder and extract faces
file_path = "/content/photos"
for item in os.listdir(file_path):
    if item.endswith(".jpeg"):
        detect_face(os.path.join(file_path,item),os.path.join("/content/target",item))

遍历从目标文件夹中提取的人脸并生成嵌入

img_embeddings = [generate_embeddings(os.path.join("/content/target",item)) for item in os.listdir("/content/target")]
print(len(img_embeddings))
#
print(img_embeddings[0].shape)
#
#save the vector of embeddings as a NumPy array so that we don't have to run it again later
np.save("vectors_cv2", np.array(img_embeddings), allow_pickle=False)

设置矢量存储以存储图像嵌入

# Create a local Qdrant vector store
client =QdrantClient(path="qdrant_db_cv2")
#
my_collection = "image_collection_cv2"
client.recreate_collection(
    collection_name=my_collection,
    vectors_config=models.VectorParams(size=768, distance=models.Distance.COSINE)
)
# generate metadata
payload = []
files_list= os.listdir("/content/target")
for i in range(len(os.listdir("/content/target"))):
    payload.append({"image_id" :i,
                    "name":files_list[i].split(".")[0]})

print(payload[:3])
ids = list(range(len(os.listdir("/content/target"))))
#Load the embeddings from the save pickle file
embeddings = np.load("vectors_cv2.npy").tolist()
#
# Load the image embeddings
for i in range(0, len(os.listdir("/content/target"))):
    client.upsert(
        collection_name=my_collection,
        points=models.Batch(
            ids=[ids[i]],
            vectors=embeddings[i],
            payloads=[payload[i]]
        )
    )

通过计数向量来确保成功上传载量

client.count(
    collection_name=my_collection,
    exact=True,
)
##Response
CountResult(count=6)

目视检查创建的集合

client.scroll(
    collection_name=my_collection,
    limit=10
)

图像搜索

加载新图像并提取人脸

load_image_path = '/content/target/Aishw.jpeg'
target_image_path = 'black.jpeg'
detect_face(load_image_path,target_path)

检查保存的图像

Image.open("/content/black.jpeg")

灰度裁剪的人脸图像已保存

生成图像嵌入

query_embedding = generate_embeddings("/content/black.jpeg")
print(type(query_embedding))
#
print(query_embedding.shape)

##Response
numpy.ndarray
(1, 768)

搜索图像以识别提供的输入图像

results = client.search(
    collection_name=my_collection,
    query_vector=query_embedding[0],
    limit=5,
    with_payload=True
)
print(results)
files_list= [ os.path.join("/content/target",f) for f in os.listdir("/content/target")]
print(files_list)

##Response

[ScoredPoint(id=3, version=0, score=0.9999998807907104, payload={'image_id': 3, 'name': 'Aishw'}, vector=None, shard_key=None),
 ScoredPoint(id=2, version=0, score=0.9999998807907104, payload={'image_id': 2, 'name': 'deepika'}, vector=None, shard_key=None),
 ScoredPoint(id=1, version=0, score=0.9999998807907104, payload={'image_id': 1, 'name': 'nohra'}, vector=None, shard_key=None),
 ScoredPoint(id=0, version=0, score=0.9999998807907104, payload={'image_id': 0, 'name': 'kajol'}, vector=None, shard_key=None),
 ScoredPoint(id=5, version=0, score=0.9999998211860657, payload={'image_id': 5, 'name': 'kareena'}, vector=None, shard_key=None)]


['/content/target/kajol.jpeg',
 '/content/target/nohra.jpeg',
 '/content/target/deepika.jpeg',
 '/content/target/Aishw.jpeg',
 '/content/target/aish.jpeg',
 '/content/target/kareena.jpeg']

Helper 函数来显示结果

def see_images(results, top_k=2):
    for i in range(top_k):
        image_id = results[i].payload['image_id']
        name    = results[i].payload['name']
        score = results[i].score
        image = Image.open(files_list[image_id])

        print(f"Result #{i+1}: {name} was diagnosed with {score * 100} confidence")
        print(f"This image score was {score}")
        display(image)
        print("-" * 50)
        print()

显示搜索结果 - 显示前 5 张匹配图片

see_images(results, top_k=5)

图像搜索的结果

结果 #1:Aishw 被诊断为 99.99998807907104 置信度。

该图像得分为 0.9999998807907104。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #2:deepika 被诊断为 99.99998807907104 置信度。

该图像得分为 0.9999998807907104。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #3:nohra 被诊断为 99.99998807907104 置信度。

该图像得分为 0.9999998807907104。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #4:kajol 被诊断为 99.99998807907104 置信度。

该图像得分为 0.9999998807907104。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #5:kareena 被诊断为 99.99998211860657 置信度。

该图像得分为 0.9999998211860657。

正如你所看到的,我们使用了现有的图像,并取回了其他图像以及原始图像。相似性分数还为我们提供了查询图像与数据库中图像相似性的良好指示。

方法 2.使用 Transformer 和 Qdrant 进行图像识别

除了 OpenCV,我们还可以使用 Vision Transformer 来执行相同的任务。请在下面找到示例代码:

代码实现

安装所需的依赖项

pip install -qU qdrant-client transformers datasets  

导入所需的库

from transformers import ViTImageProcessor, ViTModel
from qdrant_client import QdrantClient
from qdrant_client.http import models
from datasets import load_dataset
import numpy as np
import torch

设置矢量存储

# Create a local Qdrant vector store
client =QdrantClient(path="qdrant_db")
my_collection = "image_collection"
client.recreate_collection(
    collection_name=my_collection,
    vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE)
)

加载模型

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
processor = ViTImageProcessor.from_pretrained('facebook/dino-vits16')
model = ViTModel.from_pretrained('facebook/dino-vits16').to(device)

预处理照片文件夹中的图像并加载到数据帧中

import pandas as pd
import os
image_file = []
image_name =[]
#
for file in os.listdir("/content/photos"):
    if file.endswith(".jpeg"):
        image_name.append(file.split(".")[0])
        image_file.append(Image.open(os.path.join("/content/photos",file)))
#
df = pd.DataFrame({"Image":image_file,"Name":image_name})
descriptions = df['Name'].tolist()
print(descriptions)

数据帧快照

使用 ViT 生成嵌入

在计算机视觉系统中,矢量数据库用于存储图像特征。这些图像特征是捕获其视觉内容的图像的矢量表示,它们用于提高计算机视觉任务(如对象检测、图像分类和图像检索)的性能。

为了从图像中提取这些有用的特征表示,我们将使用视觉转换器 (ViT)。ViT是先进的算法,使计算机能够以与人类类似的方式“看到”和理解视觉信息。他们使用 transformer 架构来处理图像并从中提取有意义的特征。

要了解 ViT 的工作原理,请想象您有一个包含许多不同部分的大型拼图游戏。为了解决这个难题,你通常会看看各个部分,它们的形状,以及它们如何组合在一起形成完整的画面。ViT的工作方式类似,这意味着视觉转换器不是一次查看整个图像,而是将其分解成称为“补丁”的更小部分。这些补丁中的每一个都像是一块拼图,捕获图像的特定部分,然后由ViT分析和处理这些碎片。

通过分析这些斑块,ViT可以识别重要的图案,如边缘、颜色和纹理,并将它们组合起来,形成对给定图像的连贯理解。

final_embeddings = []
for item in df['Image'].values.tolist():
    inputs = processor(images=item, return_tensors="pt").to(device)
    with torch.no_grad():
        outputs = model(**inputs).last_hidden_state.mean(dim=1).cpu().numpy()
    final_embeddings.append(outputs)

保存嵌入

np.save("vectors", np.array(final_embeddings), allow_pickle=False)

生成元数据

payload = []
for i in range(df.shape[0]):
    payload.append({"image_id" :i,
                    "name":df.iloc[i]['Name']})

ids = list(range(df.shape[0]))
embeddings = np.load("vectors.npy").tolist()

将嵌入加载到矢量存储中

for i in range(0, df.shape[0]):
    client.upsert(
        collection_name=my_collection,
        points=models.Batch(
            ids=[ids[i]],
            vectors=embeddings[i],
            payloads=[payload[i]]
        )
    )

#check if the update is successful
client.count(
    collection_name=my_collection,
    exact=True,
)
#To visually inspect the collection we just created, we can scroll through our vectors with the client.scroll() method.
client.scroll(
    collection_name=my_collection,
    limit=10
)

从数据存储中搜索图像/照片

img = Image.open("YOUR IMAGE PATH")
inputs = processor(images=img, return_tensors="pt").to(device)
one_embedding = model(**inputs).last_hidden_state
#
results = client.search(
    collection_name=my_collection,
    query_vector=one_embedding.mean(dim=1)[0].tolist(),
    limit=5,
    with_payload=True
)
see_images(results, top_k=2)

搜索结果

要搜索的原始图像

搜索结果

结果 #1:Aishw 被诊断为 100.00000144622251 置信度。

图像得分为 1.0000000144622252。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #2:deepika 被诊断为 90.48531271076924 置信度。

图像得分为 0.9048531271076924。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #3:nohra 被诊断为 88.62201422801974 置信度。

图像得分为 0.8862201422801974。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #4:aish 被诊断为 87.71421890846095 置信度。

该图像得分为 0.8771421890846095。

— — — — — — — — — — — — — — — — — — — — — — — — —

结果 #5:kareena 被诊断为 86.80090570447916 置信度。

该图像得分为 0.8680090570447916。

— — — — — — — — — — — — — — — — — — — — — — — — —