Danbinabo / insighrface

基于InsightFace的人脸识别
77 stars 24 forks source link

'FaceRecognizer' object has no attribute 'database_2d_array'. get_affine_image_3d_array函数一直返回0的 #6

Closed gclsoft closed 4 years ago

gclsoft commented 4 years ago

我的FaceRecognizer 复制了https://github.com/StevenLei2017/AIprojects/blob/7cabc6f3773e87f46d54c6d5afa37313de075eb1/007%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB%E5%AE%9E%E8%B7%B5_mxnet%E7%89%88insightFace/code/FaceRecognizer_2.py

  1. 但是在# 获取仿射变换后的新图像 def get_affine_image_3d_array这个函数里, return affine_image_3d_array一直返回0
  2. 'FaceRecognizer' object has no attribute 'database_2d_array' 这个self.database_2d_array一直没有赋值,一访问就报错
# 加载常用的库
import numpy as np
import os
import cv2
from PIL import Image
import time
# 加载mxnet库
import mxnet as mx
import mxnet.ndarray as nd
# 加载用于标准化的sklearn.preprocessing
from sklearn import preprocessing
from sklearn.model_selection import KFold

# 加载人脸向量化模型
def load_model(prefix='C:\\ai\\new\\insighrface_camera_gcl\\test_run\\resources\\insightFace_model\\model', epoch=0, batch_size=10):
    symbol, arg_params, auxiliary_params = mx.model.load_checkpoint(prefix, epoch)
    all_layers = symbol.get_internals()
    output_layer = all_layers['fc1_output']
    context = mx.cpu()#gpu(0)
    model = mx.mod.Module(symbol=output_layer, context=context, label_names=None)
    model.bind(data_shapes=[('data', (batch_size, 3, 112, 112))])
    model.set_params(arg_params, auxiliary_params)
    return model

# 定义类 人脸向量化器FaceVectorizer
class FaceVectorizer(object):
    # 实例化对象后的初始化方法
    def __init__(self):
        self.batch_size = 1
        self.model = None
        self.database_2d_array= None

    # 获取输入数据
    def get_feedData(self, image_4d_array):
        image_list = []
        for image_3d_array in image_4d_array:
            height, width, _ = image_3d_array.shape
            if height != 112 or width != 112:
                image_3d_array = cv2.resize(image_3d_array, (112, 112))
            image_list.append(image_3d_array)
        image_4d_array_1 = np.array(image_list)
        image_4d_array_2 = np.transpose(image_4d_array_1, [0, 3, 1, 2])
        image_4D_Array = nd.array(image_4d_array_2)
        image_quantity = len(image_list)
        label_1D_Array = nd.ones((image_quantity,))
        feed_data = mx.io.DataBatch(data=(image_4D_Array,), label=(label_1D_Array,))
        return feed_data

    # 使用insightFace模型,把多张人脸的图像数据转换多张人脸的特征向量
    # 返回结果为2维数组
    def get_feature_2d_array(self, image_4d_array):
        if len(image_4d_array.shape) == 3:
            image_4d_array = np.expand_dims(image_4d_array, 0)
        assert len(image_4d_array.shape) == 4, 'image_ndarray shape length is not 4'
        image_quantity = len(image_4d_array)
        if image_quantity != self.batch_size or not self.model:
            self.batch_size = image_quantity
            self.model = load_model(batch_size=self.batch_size)
        feed_data = self.get_feedData(image_4d_array)
        self.model.forward(feed_data, is_train=False)
        outputs = self.model.get_outputs()
        output_2D_Array = outputs[0]
        output_2d_array = output_2D_Array.asnumpy()
        feature_2d_array = preprocessing.normalize(output_2d_array)
        return feature_2d_array

# 计算准确率
def get_accuracy(distance_1d_array, actual_isSame_1d_array, threshold):
    predict_isSame_1d_ndarray = np.less(distance_1d_array, threshold)
    true_positive_quantity = np.sum(np.logical_and(
        predict_isSame_1d_ndarray, actual_isSame_1d_array))
    true_negetive_quantity = np.sum(np.logical_and(
        np.logical_not(predict_isSame_1d_ndarray), np.logical_not(actual_isSame_1d_array)))
    accuracy = float(true_positive_quantity + true_negetive_quantity) / len(distance_1d_array)
    return accuracy

# 获取2个特征2维数组间的距离
import math

def get_distance_1d_array(feature_2d_array_1, feature_2d_array_2, distance_method='euclidean'):
    # 计算欧几里得距离
    if distance_method == 'euclidean':
        diffValue_2d_array = np.subtract(feature_2d_array_1, feature_2d_array_2)
        distance_1d_array = np.sum(np.square(diffValue_2d_array), 1)
    # 计算余弦相似度
    elif distance_method == 'cosine':
        numerator_1d_array = np.sum(np.multiply(feature_2d_array_1, feature_2d_array_2), axis=1)
        denominator_1d_array = np.linalg.norm(feature_2d_array_1, axis=1) * np.linalg.norm(feature_2d_array_2, axis=1)
        similarity_1d_array = numerator_1d_array / denominator_1d_array
        distance_1d_array = np.arccos(similarity_1d_array) / math.pi
    return distance_1d_array

# 定义类 人脸识别器FaceRecognizer
class FaceRecognizer(object):
    # 实例化对象后的初始化方法
    def __init__(self, face_dirPath='../resources/face_database', distance_method='euclidean'):
        self.feature_dimension = 512
        self.distance_method = distance_method
        self.face_vectorizer = FaceVectorizer()
        self.fileSuffix_set = set(['jpg', 'bmp', 'png'])
        self.load_database(face_dirPath)

    # 加载人脸数据库
    def load_database(self, face_dirPath='./face_database'):
        # 统计人脸数据库的人数,人脸图像数量
        try:
            self.personName_list = next(os.walk(face_dirPath))[1]
            personId_list = []
            for i, personName in enumerate(self.personName_list):
                dirPath = os.path.join(face_dirPath, personName)
                fileName_list = next(os.walk(dirPath))[2]
                for fileName in fileName_list:
                    fileSuffix = os.path.splitext(fileName)[1][1:]
                    if fileSuffix in self.fileSuffix_set:
                        personId_list.append(i)
            self.personId_1d_array = np.array(personId_list)
            self.bincount_1d_array = np.bincount(self.personId_1d_array)
            self.person_quantity = len(self.personName_list)
            self.image_quantity = len(personId_list)
            print('人脸数据库中总共有%d个人, %d个人脸图像' % (self.person_quantity, self.image_quantity))
            # 加载人脸图像数据,转换为向量
            startTime = time.time()
            batch_size = 30
            imageData_list = []
            count = 0
            self.database_2d_array = np.empty((self.image_quantity, self.feature_dimension))
            # 遍历每个人的人脸图像文件夹
            for personName in self.personName_list:
                dirPath = os.path.join(face_dirPath, personName)
                fileName_list = next(os.walk(dirPath))[2]
                for fileName in fileName_list:
                    fileSuffix = os.path.splitext(fileName)[1][1:]
                    # 文件名后缀需要符合要求
                    if fileSuffix in self.fileSuffix_set:
                        filePath = os.path.join(dirPath, fileName)
                        image_3d_array = np.array(Image.open(filePath))
                        image_3d_array = cv2.resize(image_3d_array, (112, 112))
                        imageData_list.append(image_3d_array)
                        count += 1
                        if count % batch_size == 0:
                            image_4d_array = np.array(imageData_list)
                            self.database_2d_array[count - batch_size: count] = self.face_vectorizer.get_feature_2d_array(
                                image_4d_array)
                            imageData_list.clear()
            if count % batch_size != 0:
                image_4d_array = np.array(imageData_list)
                remainder = count % batch_size
                self.database_2d_array[count - remainder: count] = self.face_vectorizer.get_feature_2d_array(image_4d_array)
            # 打印加载人脸图像数据花费的时间
            usedTime = time.time() - startTime
            print('加载%d张人脸图像,总共用时 %.4f秒' % (self.image_quantity, usedTime))
            # 计算得出最佳阈值
            self.make_bestThreshold()
        except Exception as e:
            print('load_database error:', e)

    # 通过生成随机的数据集,使用10折交叉验证得出最佳阈值
    def make_bestThreshold(self):
        self.make_randomDataSet()
        startTime = time.time()
        k_fold = KFold(n_splits=10, shuffle=False)
        sample_quantity = len(self.distance_1d_array)
        index_1d_array = np.arange(sample_quantity)
        # 在200个阈值中找出最佳阈值
        bestThreshold_list = []
        for fold_index, (train_1d_array, test_1d_array) in enumerate(k_fold.split(index_1d_array)):
            train_distance_1d_array = self.distance_1d_array[train_1d_array]
            train_isSame_1d_array = self.isSame_1d_array[train_1d_array]
            test_distance_1d_array = self.distance_1d_array[test_1d_array]
            test_isSame_1d_array = self.isSame_1d_array[test_1d_array]
            accuracy_list = []
            threshold_1d_array = np.arange(0, 2.0, 0.01)
            for threshold in threshold_1d_array:
                train_accuracy = get_accuracy(train_distance_1d_array, train_isSame_1d_array, threshold)
                test_accuracy = get_accuracy(test_distance_1d_array, test_isSame_1d_array, threshold)
                # 训练集权重0.4,测试集权重0.6
                accuracy = 0.4 * train_accuracy + 0.6 * test_accuracy
                accuracy_list.append(accuracy)
            bestThreshold_index = np.argmax(accuracy_list)
            bestThreshold = threshold_1d_array[bestThreshold_index]
            max_accuracy = np.max(accuracy_list)
            print('第%d次计算,使用判断阈值%.2f获得最大准确率%.4f' % (fold_index + 1, bestThreshold, max_accuracy))
            bestThreshold_list.append(bestThreshold)
        self.bestThreshold = np.mean(bestThreshold_list)
        print('经过10折交叉验证,获得最佳判断阈值%.4f' % self.bestThreshold)
        usedTime = time.time() - startTime
        print('获取最佳判断阈值,用时%.2f秒' % (usedTime))

    # 生成随机的数据集
    def make_randomDataSet(self):
        startTime = time.time()
        sample_quantity = 32 * int(self.image_quantity ** 0.58)
        feature_2d_array_1 = np.empty((sample_quantity, self.feature_dimension))
        feature_2d_array_2 = np.empty((sample_quantity, self.feature_dimension))
        self.isSame_1d_array = np.empty((sample_quantity))
        # 数据集前半部分为相同人脸
        same_sample_quantity = int(sample_quantity / 2)
        selected_personId_1d_array = np.where(self.bincount_1d_array >= 2)[0]
        same_personId_1d_array = np.random.choice(selected_personId_1d_array, same_sample_quantity)
        for i, personId in enumerate(same_personId_1d_array):
            selected_index = np.where(self.personId_1d_array == personId)[0]
            index_1, index_2 = np.random.choice(selected_index, 2, replace=False)
            feature_2d_array_1[i] = self.database_2d_array[index_1]
            feature_2d_array_2[i] = self.database_2d_array[index_2]
        self.isSame_1d_array[:same_sample_quantity] = True
        # 数据集后半部分为不同人脸
        difference_sample_quantity = int(sample_quantity / 2)
        index_1d_array = np.arange(self.image_quantity)
        for i in range(same_sample_quantity, same_sample_quantity + difference_sample_quantity):
            index_1, index_2 = np.random.choice(index_1d_array, 2)
            personId_1, personId_2 = self.personId_1d_array[[index_1, index_2]]
            # 通过循环判断,使personId_1不等于personId_2
            while personId_1 == personId_2:
                index_1, index_2 = np.random.choice(index_1d_array, 2)
                personId_1, personId_2 = self.personId_1d_array[[index_1, index_2]]
            feature_2d_array_1[i] = self.database_2d_array[index_1]
            feature_2d_array_2[i] = self.database_2d_array[index_2]
        self.isSame_1d_array[same_sample_quantity:] = False
        # 打印随机生成数据集花费的时间
        usedTime = time.time() - startTime
        print('随机生成数据集,总共%d组人脸,用时%.2f秒' % (sample_quantity, usedTime))
        # 得出2个特征2维数组间的距离
        self.distance_1d_array = get_distance_1d_array(feature_2d_array_1, feature_2d_array_2, self.distance_method)

    # 获取人脸对应的人名
    def get_personName(self, imageFilePath):
        image_3d_array = np.array(Image.open(imageFilePath))
        personName = self.get_personName_1(image_3d_array)
        return personName

    # 获取人脸对应的人名
    def get_personName_1(self, image_3d_array):
        try:
            if len(image_3d_array.shape) == 3:
                image_4d_array = np.expand_dims(image_3d_array, 0)
            elif len(image_3d_array.shape) == 4:
                image_4d_array = image_3d_array
            else:
                raise ValueError('传入图像数据的维度既不是3维也不是4维')
            feature_2d_array = self.face_vectorizer.get_feature_2d_array(image_4d_array)
            if not self.database_2d_array:
                return '找不到'

            distance_1d_array = get_distance_1d_array(self.database_2d_array, feature_2d_array, self.distance_method)
            isSame_1d_array = np.less(distance_1d_array, self.bestThreshold)
            predict_personId_1d_array = self.personId_1d_array[isSame_1d_array]
            if len(predict_personId_1d_array) == 0:
                personName = '无效人脸'
            else:
                min_distance_index = np.argmin(distance_1d_array)
                similar_personId = self.personId_1d_array[min_distance_index]
                predict_bincount_1d_array = np.bincount(predict_personId_1d_array)
                similar_percent = predict_bincount_1d_array[similar_personId] / self.bincount_1d_array[similar_personId]
                if similar_percent >= 0.7:
                    personName = self.personName_list[similar_personId]
                else:
                    personName = '无效人脸'
            return personName
        except Exception as ex:
            print(ex)
            return 'Unkown'