from maix import nn, camera, image, display
from maix.nn.app.face import FaceRecognize
import time
from evdev import InputDevice
from select import select


score_threshold = 70                            #识别分数阈值
input_size = (224, 224, 3)                      #输入图片尺寸
input_size_fe = (128, 128, 3)                   #输入人脸数据
feature_len = 256                               #人脸数据宽度
steps = [8, 16, 32]                             #
channel_num = 0                                 #通道数量
users = []                                      #初始化用户列表
names = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]  #人脸标签定义
model = {
    "param": "/home/model/face_recognize/model_int8.param",
    "bin": "/home/model/face_recognize/model_int8.bin"
}
model_fe = {
    "param": "/home/model/face_recognize/fe_res18_117.param",
    "bin": "/home/model/face_recognize/fe_res18_117.bin"
}


for i in range(len(steps)):
    channel_num += input_size[1] / steps[i] * (input_size[0] / steps[i]) * 2
channel_num = int(channel_num)     #统计通道数量
options = {                             #准备人脸输出参数
    "model_type":  "awnn",
    "inputs": {
        "input0": input_size
    },
    "outputs": {
        "output0": (1, 4, channel_num) ,
        "431": (1, 2, channel_num) ,
        "output2": (1, 10, channel_num)
    },
    "mean": [127.5, 127.5, 127.5],
    "norm": [0.0078125, 0.0078125, 0.0078125],
}
options_fe = {                             #准备特征提取参数
    "model_type":  "awnn",
    "inputs": {
        "inputs_blob": input_size_fe
    },
    "outputs": {
        "FC_blob": (1, 1, feature_len)
    },
    "mean": [127.5, 127.5, 127.5],
    "norm": [0.0078125, 0.0078125, 0.0078125],
}
keys = InputDevice('/dev/input/event0')

threshold = 0.5                                         #人脸阈值
nms = 0.3
max_face_num = 1                                        #输出的画面中的人脸的最大个数
print("-- load model:", model)
m = nn.load(model, opt=options)
print("-- load ok")
print("-- load model:", model_fe)
m_fe = nn.load(model_fe, opt=options_fe)
print("-- load ok")
face_recognizer = FaceRecognize(m, m_fe, feature_len, input_size, threshold, nms, max_face_num)

def get_key():                                      #按键检测函数
    r,w,x = select([keys], [], [],0)
    if r:
        for event in keys.read():
            if event.value == 1 and event.code == 0x02:     # 右键
                return 1
            elif event.value == 1 and event.code == 0x03:   # 左键
                return 2
            elif event.value == 2 and event.code == 0x03:   # 左键连按
                return 3
    return 0

def map_face(box,points):                           #将224*224空间的位置转换到240*240或320*240空间内
    # print(box,points)
    if display.width() == display.height():
        def tran(x):
            return int(x/224*display.width())
        box = list(map(tran, box))
        def tran_p(p):
            return list(map(tran, p))
        points = list(map(tran_p, points))
    else:
        # 168x224(320x240) > 224x224(240x240) > 320x240
        s = (224*display.height()/display.width()) # 168x224
        w, h, c = display.width()/224, display.height()/224, 224/s
        t, d = c*h, (224 - s) // 2 # d = 224 - s // 2 == 28
        box[0], box[1], box[2], box[3] = int(box[0]*w), int((box[1]-28)*t), int(box[2]*w), int((box[3])*t)
        def tran_p(p):
            return [int(p[0]*w), int((p[1]-d)*t)] # 224 - 168 / 2 = 28 so 168 / (old_h - 28) = 240 / new_h
        points = list(map(tran_p, points))
    # print(box,points)
    return box,points

def darw_info(draw, box, points, disp_str, bg_color=(255, 0, 0), font_color=(255, 255, 255)):    #画框函数
    box,points = map_face(box,points)
    font_wh = image.get_string_size(disp_str)
    for p in points:
        draw.draw_rectangle(p[0] - 1, p[1] -1, p[0] + 1, p[1] + 1, color=bg_color)
    draw.draw_rectangle(box[0], box[1], box[0] + box[2], box[1] + box[3], color=bg_color, thickness=2)
    draw.draw_rectangle(box[0], box[1] - font_wh[1], box[0] + font_wh[0], box[1], color=bg_color, thickness = -1)
    draw.draw_string(box[0], box[1] - font_wh[1], disp_str, color=font_color)
def recognize(feature):                                                                   #进行人脸匹配
    def _compare(user):                                                         #定义映射函数
        return face_recognizer.compare(user, feature)                      #推测匹配分数 score相关分数
    face_score_l = list(map(_compare,users))                               #映射特征数据在记录中的比对分数
    return max(enumerate(face_score_l), key=lambda x: x[-1])                #提取出人脸分数最大值和最大值所在的位置

def run():
    img = camera.capture()                       #获取224*224*3的图像数据
    AI_img = img.copy().resize(224, 224)
    if not img:
        time.sleep(0.02)
        return
    faces = face_recognizer.get_faces(AI_img.tobytes(),False)           #提取人脸特征信息
    if faces:
        for prob, box, landmarks, feature in faces:
            key_val = get_key()
            if key_val == 1:                                # 右键添加人脸记录
                if len(users) < len(names):
                    print("add user:", len(users))
                    users.append(feature)
                else:
                    print("user full")
            elif key_val == 2:                              # 左键删除人脸记录
                if len(users) > 0:
                    print("remove user:", names[len(users) - 1])
                    users.pop()
                else:
                    print("user empty")

            if len(users):                             #判断是否记录人脸
                maxIndex = recognize(feature)

                if maxIndex[1] > score_threshold:                                      #判断人脸识别阈值,当分数大于阈值时认为是同一张脸,当分数小于阈值时认为是相似脸
                    darw_info(img, box, landmarks, "{}:{:.2f}".format(names[maxIndex[0]], maxIndex[1]), font_color=(0, 0, 255, 255), bg_color=(0, 255, 0, 255))
                    print("user: {}, score: {:.2f}".format(names[maxIndex[0]], maxIndex[1]))
                else:
                    darw_info(img, box, landmarks, "{}:{:.2f}".format(names[maxIndex[0]], maxIndex[1]), font_color=(255, 255, 255, 255), bg_color=(255, 0, 0, 255))
                    print("maybe user: {}, score: {:.2f}".format(names[maxIndex[0]], maxIndex[1]))
            else:                                           #没有记录脸
                darw_info(img, box, landmarks, "error face", font_color=(255, 255, 255, 255), bg_color=(255, 0, 0, 255))


    display.show(img)



if __name__ == "__main__":
    import signal
    def handle_signal_z(signum,frame):
        print("APP OVER")
        exit(0)
    signal.signal(signal.SIGINT,handle_signal_z)
    while True:
        run()
