python3.8+pymilvus

app.py

# coding=UTF-8
from flask import Flask, request, jsonify
import facedata
from flask_cors import CORS

app = Flask(__name__)

@app.route("/insert_data/", methods=['GET', 'POST'])
def insert_data():
    file_id = request.form.get('file_id')
    user_id = request.form.get('user_id')
    file_path = request.form.get('file_path')
    name = request.form.get('name')
    facedata.create_data_table(name)

    if file_path:
        data = facedata.insert_one_data(file_path, int(user_id), int(file_id),name)
        return jsonify(data)
    return jsonify({'code': 0, 'msg': '图片未找到'})


@app.route("/select_data/", methods=['GET', 'POST'])
def select_data():
    file_id = request.form.get('file_id')
    user_id = request.form.get('user_id')
    file_path = request.form.get('file_path')
    name = request.form.get('name')
    facedata.create_data_table(name)

    if file_path:
        data = facedata.select_data(file_path, user_id,name)
        return jsonify(data)
    return jsonify({'code': 0, 'msg': '图片未找到'})


@app.route("/delete_data/", methods=['GET', 'POST'])
def delete_data():
    file_id = request.form.get('file_id')
    name = request.form.get('name')
    facedata.create_data_table(name)
    if file_id:
        expr = "face_id in [{}]".format(file_id)
        facedata.delete_data(expr,name)
        return jsonify({'code': 1, 'msg': '删除成功'})
    return jsonify({'code': 0, 'msg': '图片未找到'})


@app.errorhandler(404)
def handle_404_error(error):
    print('error')
    """自定义错误的方法"""
    # 前端用户最终看到的结果,也可以根据错误进行调整
    return jsonify({'code': 0, 'msg': 'Error'})


CORS(app, resources=r'/*')
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)

main.py

import time
from io import BytesIO

import PIL
import face_recognition
import os
from numpy import array  # 若不加这一行,对从文件中读取已保存的128维人脸编码执行eval()时,报错name 'array' is not defined
import cv2
import facedata
import requests
from PIL import Image
import numpy as np
import base64
import re
t1 = int(time.time())


# http://wotui2020.zhiqingco.com/202304241505568951a8189.png
# 获取列表的第二个元素
def takeSecond(elem):
    return elem[1]


# 判断图片是否清晰,参数是图片的绝对路径
def getImageVar(imgPath: str) -> bool:
    image = cv2.imread(imgPath)
    img2gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    imageVar = cv2.Laplacian(img2gray, cv2.CV_64F).var()  # 一般大于100时认为图片是清晰的,实际需根据情况调节
    if imageVar <= 100:
        return False
    elif imageVar > 100:
        return True


# 检测图片中是否有人脸。能接受不完整的人脸,如缺少鼻子以下,此时是能否检测到人脸的阈值之一,人脸中其他部位的缺少情况未测试
def check_face_img(img_path: str) -> bool:
    img = face_recognition.load_image_file(img_path)  # 加载图像。会将图像加载到 numpy 数组中。
    result = face_recognition.face_locations(img)  # 返回图像中每张人脸的人脸特征位置列表。图像中没有人脸时,返回空列表
    if len(result) > 0:
        return True
    else:
        return False


# 获取图片库中每张图片的128维人脸编码,用于传给face_recognition.face_distance()的face_encodings参数
def get_face_encodings(img_folder_path):
    img_face_encoding_list = []  # 每个元素是文件夹中所有图片的128维人脸编码
    img_path_list = []  # 每个元素是文件夹中图片的绝对路径
    img_list = os.listdir(img_folder_path)

    for img in img_list:
        img_path = os.path.join(img_folder_path, img)
        # if getImageVar(img_path) and check_face_img(img_path):
        if check_face_img(img_path):
            img_path_list.append(img_path)
            img = face_recognition.load_image_file(img_path)  # 加载图像
            img_face_encoding = face_recognition.face_encodings(img)[
                0]  # 返回图像中每张人脸的 128 维人脸编码。后面使用face_recognition.face_distance()时,face_recognition.face_distance()的face_encodings参数中的值不能是由face_recognition.face_encodings(img)组成的,而应由face_recognition.face_encodings(img)[0]组成。
            img_face_encoding_list.append(img_face_encoding)
        elif not getImageVar(img_path):
            print(f'{img_path}图片太模糊,请重新拍摄,入库时已忽略此图片')
        elif not check_face_img(img_path):
            print(f'{img_path}没有检测到完整人脸,请重新拍摄,入库时已忽略此图片')
    return img_face_encoding_list, img_path_list


def get_on_face_encodings(img_path):
    img_face_encoding_list = []  # 每个元素是文件夹中所有图片的128维人脸编码
    img_path_list = []  # 每个元素是文件夹中图片的绝对路径
    if img_path.startswith("http"):
        img_data = requests.get(img_path, stream=True)
        img_path = BytesIO(img_data.content)
    else:
        img_path = os.path.join(img_path)
    if check_face_img(img_path):
        img_path_list.append(img_path)
        img = face_recognition.load_image_file(img_path)  # 加载图像
        img_face_encoding = face_recognition.face_encodings(img)[
            0]  # 返回图像中每张人脸的 128 维人脸编码。后面使用face_recognition.face_distance()时,face_recognition.face_distance()的face_encodings参数中的值不能是由face_recognition.face_encodings(img)组成的,而应由face_recognition.face_encodings(img)[0]组成。
        img_face_encoding_list.append(img_face_encoding)
    elif not check_face_img(img_path):
        print(f'{img_path}没有检测到完整人脸,请重新拍摄,入库时已忽略此图片')
    return img_face_encoding_list


# 从本地文件读取已保存的图片库中每张图片的128维人脸编码,节省计算时间
def get_face_encodings_from_file(img_folder_path):
    with open("face_encod_list.txt", 'r', encoding='utf-8') as f:
        img_face_encoding_list = eval(f.read())  # 需from numpy import array,否则eval()报错name 'array' is not defined
    img_path_list = []
    img_list = os.listdir(img_folder_path)
    for img in img_list:
        img_path = os.path.join(img_folder_path, img)
        img_path_list.append(img_path)
    return img_face_encoding_list, img_path_list


# 匹配
def pipei_image(image, image_path):
    if check_face_img(images):
        img_face_encoding_list, img_path_list = get_face_encodings_from_file(image_path)

        img = face_recognition.load_image_file(images)
        source_img_face_encoding = face_recognition.face_encodings(img)[0]

        result = face_recognition.face_distance(face_encodings=img_face_encoding_list,
                                                face_to_compare=source_img_face_encoding)  # 给定人脸编码列表,将它们与已知的人脸编码进行比较,并得到每个比较人脸的欧氏距离。距离大小为面孔的相似程度。欧氏距离越小相似度越大。欧氏距离的典型阈值是0.6,即小于0.6的可认为匹配成功。face_encodings是要比较的人脸编码列表,face_to_compare是要与之进行比较的人脸编码。
        temp_list = list(zip(img_path_list, result))
        temp_list.sort(key=takeSecond)  # 将原列表按列表中每个子元素中的第二个元素的值进行升序排列
        if temp_list[0][-1] < 0.6:  # 升序排列后第一个元素的相似度最高,如果它的欧氏距离小于0.6,认为匹配成功
            print(temp_list[0])
        else:
            print('匹配失败')  # 有可能不是本人,也有可能是本次拍摄时不清晰
    elif not check_face_img(images):
        print('没有检测到完整人脸,请重新拍摄')


images = os.getcwd() + "/image/9.png"
image_path = os.getcwd() + "/image"
if __name__ == "__main__":
    img_b64encode = "base64"
    base64_data = re.sub('^data:image/.+;base64,', '', img_b64encode)
    byte_data = base64.b64decode(base64_data)
    image_data = BytesIO(byte_data)
    print(check_face_img(image_data))
    # while True:
    #     user_id = 0
    #     name = "ATTUG4VKQ032cd"
    #     file_path = ''
    #     d = facedata.select_data(file_path, user_id, name)
    #     print(d)
    #     time.sleep(1)
    pass
    # print(get_on_face_encodings(images))
    # print(check_face_img(images))
    # get_face_encodings(image_path)
    # img_face_encoding_list = get_on_face_encodings(images)
    # print(img_face_encoding_list)
    # get_face_encodings_from_file(image_path)
    # pipei_image(images,image_path)
    # t3 = int(time.time())
    # print(t3 - t1)
    # facedata.drop_data_tabel('face_feature_vector_admin')
    # facedata.create_data_table()
    # facedata.insert_one_data(images,102,100)
    # if check_face_img(images):
    #     facedata.select_data(images)
    # else:
    #     print("不是人脸")
    # wl_img = ""
    # facedata.insert_one_data(wl_img, 102, 100)
    # facedata.select_data(wl_img)

facedata.py

from io import BytesIO

import requests
from pymilvus import connections, CollectionSchema, FieldSchema, DataType, Collection, utility
import os
import main
import face_recognition
import base64
import re
def init_data():
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
    pass
    name = 1
    try:
        collection = Collection(name)  # Get an existing collection.
        collection.load()
    except Exception as e:
        pass
    connections.disconnect("default")  # 断开 Milvus 连接


def query_data(name):
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
    collection = Collection(name)  # Get an existing collection.
    res = collection.query(
        expr="face_id in [1]",
        output_fields=["face_id", "file_id"],
        limit=100,
        consistency_level="Strong"
    )
    connections.disconnect("default")  # 断开 Milvus 连接
    return res


def delete_data(expr,name):
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
    collection = Collection(name)  # Get an existing collection.
    result = collection.delete(expr)
    connections.disconnect("default")  # 断开 Milvus 连接
    return result


def counts(name):
    collection = Collection(name)
    state = collection.get_compaction_state()
    connections.disconnect("default")  # 断开 Milvus 连接


def select_data(source_img_file_path, user_id,name):
    if source_img_file_path.startswith("http"):
        img_data = requests.get(source_img_file_path, stream=True)
        file_path = BytesIO(img_data.content)
    elif source_img_file_path.startswith("data:image"):
        base64_data = re.sub('^data:image/.+;base64,', '', source_img_file_path)
        byte_data = base64.b64decode(base64_data)
        file_path = BytesIO(byte_data)
    else:
        file_path = os.path.join(source_img_file_path)

    if main.check_face_img(file_path):
        try:
            connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
            if source_img_file_path.startswith("http"):
                img_data = requests.get(source_img_file_path, stream=True)
                source_img_file_path = BytesIO(img_data.content)
            elif source_img_file_path.startswith("data:image"):
                source_img_file_path = file_path
            else:
                source_img_file_path = os.path.join(source_img_file_path)

            collection = Collection(name)
            img = face_recognition.load_image_file(source_img_file_path)  # 加载图像
            img_face_encoding = face_recognition.face_encodings(img)
            img_face_encoding = img_face_encoding[0]
            search_params = {"metric_type": "L2", "params": {
                "nprobe": 10}}  # 准备适合你的搜索场景的参数。下面的示例定义了搜索将使用欧式距离计算,并从 IVF_FLAT 索引构建的十个最近的聚类中检索向量。

            search_param = {
                "data": [img_face_encoding],
                "anns_field": "face_feature_vector",
                "param": search_params,
                "limit": 1,
                "timeout": 10,
                #"expr": "user_id in [" + user_id + "]",
                "output_fields": ['user_id', 'face_id'],
                "consistency_level": "Strong"
            }

            results = collection.search(**search_param)
            # 查看最相似向量的 primary key 及其距离值
            id_list = results[0].ids
            distance_list = results[0].distances
            connections.disconnect("default")  # 断开 Milvus 连接
            if (len(list(id_list)) > 0) and (list(distance_list)[0] <= 0.2):
                file_id = 0
                user_id = 0
                for hit in results[0]:
                    file_id = hit.entity.get("face_id")
                    user_id = hit.entity.get('user_id')
                return {'code': 1, 'msg': "图片已匹配", 'data': {"user_id": user_id, 'file_id': file_id}}
            else:
                return {'code': 0, 'msg': "没有匹配人脸"}

        except Exception as e:
            print(e)
            connections.disconnect("default")  # 断开 Milvus 连接
            return {'code': 0, 'msg': "图片有误"}
    else:
        return {'code': 0, 'msg': "人脸有误"}


def drop_data_tabel(name):
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
    utility.drop_collection(name)


def create_data_table(name):
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名

    if utility.has_collection(name)==True:
        return True

    # 准备架构,包括字段架构、集合架构和集合名称。
    face_id = FieldSchema(
        name="face_id",
        dtype=DataType.INT64,
        is_primary=True,
        auto_id=False  # 关闭自动赋值自增id,实际人员的id由前端传入
    )

    user_id = FieldSchema(
        name="user_id",
        dtype=DataType.INT64,
        auto_id=False
    )

    face_feature_vector = FieldSchema(
        name='face_feature_vector',
        dtype=DataType.FLOAT_VECTOR,
        # 128维人脸编码是浮点数。一个集合中必须有一个字段是DataType.FLOAT_VECTOR或DataType.BINARY_VECTOR类型,文档原话The collection to create must contain a primary key field and a vector field. INT64 is the only supported data type for the primary key field in current release of Milvus. from https://milvus.io/cn/docs/v2.0.0/create_collection.md#Prepare-Schema
        dim=128  # face_recognition.face_encodings()返回的列表再取第一个元素,值是128维人脸编码。向量列数,一个向量有多少个元素就有多少列。
    )

    schema = CollectionSchema(
        fields=[face_id, user_id, face_feature_vector],
        description=name
    )

    collection_name = name

    # 使用架构创建集合
    Collection(
        name=collection_name,
        schema=schema,
        using='default',  # 服务器别名,要在哪个服务器创建集合
        shards_num=2,
        consistency_level="Strong"
    )

    collection = Collection(name)
    collection.load()
    pass


def insert_one_data(img_path, user_id, file_id,name):
    connections.connect(alias="default", host='127.0.0.1', port='19530')  # 构建一个 Milvus 连接,alias是创建的Milvus连接的别名
    if img_path.startswith("http"):
        img_data = requests.get(img_path, stream=True)
        img_path = BytesIO(img_data.content)
    else:
        img_path = os.path.join(img_path)

    if main.check_face_img(img_path):
        img = face_recognition.load_image_file(img_path)  # 加载图像
        img_face_encoding = face_recognition.face_encodings(img)[
            0]  # 返回图像中每张人脸的 128 维人脸编码。后面使用face_recognition.face_distance()时,face_recognition.face_distance()的face_encodings参数中的值不能是由face_recognition.face_encodings(img)组成的,而应由face_recognition.face_encodings(img)[0]组成。
        collection = Collection(name)  # Get an existing collection.

        collection.insert(
            [[file_id], [user_id], [img_face_encoding]])

        connections.disconnect("default")  # 断开 Milvus 连接
        return {'code': 1, 'msg': '入库成功'}
    elif not main.check_face_img(img_path):
        connections.disconnect("default")  # 断开 Milvus 连接
        return {'code': 0, 'msg': '没有检测到完整人脸,请重新拍摄'}


def create_index():
    index_params = {  # 准备索引参数
        "metric_type": "L2",
        "index_type": "IVF_FLAT",
        "params": {"nlist": 1024}
    }
    collection = Collection("face_feature_vector_admin")
    collection.create_index(
        field_name="face_feature_vector",
        index_params=index_params
    )


def insert_data(name):
    # 准备要插入的数据。要插入的数据的数据类型必须与集合的架构匹配,否则 Milvus 将引发异常。
    img_folder_path = os.getcwd() + "/image"

    img_list = os.listdir(img_folder_path)
    for img in img_list:
        img_path = os.path.join(img_folder_path, img)
        if main.check_face_img(img_path):
            img = face_recognition.load_image_file(img_path)  # 加载图像
            img_face_encoding = face_recognition.face_encodings(img)[
                0]  # 返回图像中每张人脸的 128 维人脸编码。后面使用face_recognition.face_distance()时,face_recognition.face_distance()的face_encodings参数中的值不能是由face_recognition.face_encodings(img)组成的,而应由face_recognition.face_encodings(img)[0]组成。
            temp_id = 101
            file_id = 100
            collection = Collection(name)  # Get an existing collection.
            mr = collection.insert(
                [[temp_id], [file_id], [img_face_encoding]])
            print(mr)  # 插入操作的执行结果
        elif not main.check_face_img(img_path):
            print(f'{img_path}没有检测到完整人脸,请重新拍摄,入库时已忽略此图片')

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值