2020年TI杯赛后总结

欢迎关注我的微信公众号【万能的小江江】

Github项目链接:https://github.com/InImpasse/openmv_identify

2020年TI杯大学生电子设计竞赛

简易无接触温度测量与身份识别装置(F题)识别部分

本方案是在星瞳科技所提供的OPENMV例程基础上进行的修改
分辨不同人脸

主要修改部分:

1、将每次识别人脸时只采集1次样本数据修改为采集3次样本数据,每次分别与图像库中图片进行比对。每次比对后都会输出1个结果,循环3次后,输出出现次数最多的那个结果,减小识别误差率。
2、原例程是比对样本与图像库整张的图片中黑白像素数量,取最相近的结果进行输出。本方案将识别范围向中间缩小了1/8,可以一定程度上避免因为背景因素对结果造成的影响。
3、新定义了一些输入IO口和输出IO口,实现通过按键切换模式(身份识别、口罩判别、学习模式选择、学习后身份识别)、通过高低电平输出表示结果的功能(见附表)。
4、修改了图像库保存的位置,身份识别模式的图像库保存在”face/f%d”中,口罩判别模式的图像库保存在”mask/m%d”中,学习模式的图像库保存在”identify/i%s”中。因此,在使用此方案时,您需要先将github上的几个文件夹提前下载至您的OPENMV的根目录当中。
5、添加了相机参数自动调节的功能(自动增益、自动白平衡、自动曝光),一定程度上避免光线原因造成的识别结果误差。(该功能不确定一定有效)识别结果对应

比赛结果:

除题目第3项身份不符报警功能没有加入之外,题目其余要求均实现。识别部分得分为45分(总分50分)

仍需改进部分:

1、未加入身份不符报警功能,原因是因为没有找到合适的报警阈值。且阈值可能因为环境因素发生较大变化,造成队员身份识别错误,风险较大,故不加入。
2、为方便用LED灯表示结果,最后的输出结果没有将各模式编号后按照二进制结果输出高低电平,造成了一些结果的重复。
3、最后的学习后身份识别功能本来想添加两个模式,一个模式是针对学习模式的图像库单独进行识别,另一个模式是对学习模式和身份识别库的图像库一起进行识别,这两个模式之间通过按键进行切换。但最后因为时间问题,及按键数量问题,没有加入该模式,只选择了第一种模式作为默认功能。
4、因为OPENMV的精度问题,在识别过程中出现了几次的识别错误(包括穿黑色衣服无法识别是否戴口罩、因为角度问题识别错人等),因此该方案的识别功能对各方面的要求较高,某些情况下识别难度较大。
5、比赛准备过程中,有尝试过用haar算子追踪人脸(参考人眼追踪方案),再在此基础上圈定人脸范围进行身份识别,但是由于两种模式相机参数不同,故无法实现。
6、比赛准备过程中,也有尝试过用OPENCV官方库中的haar算子,进行人鼻子和嘴巴的追踪,以判断是否佩戴口罩,但在转制xml文件至cascade文件时报错,后判断是xml文件的问题。(OPENMV官方调用的haarcascades_cuda库,该库内的文件可转制成功。但haarcascades库转制失败,cuda库中不包括鼻子和嘴巴的文件。)
7、因为是第一次接触OPENMV,所以文件中较多方案的代码是重复的,一方面是为了方便修改,另外也是由于时间有限。欢迎大家对该方案进行修改,有什么问题可在issues中提出。

代码:

import sensor, time, image, pyb
from pyb import Pin

# 相机参数设置
sensor.reset() # 初始化相机
sensor.set_pixformat(sensor.GRAYSCALE) # 设置灰度像素模式,每个像素8bit
sensor.set_framesize(sensor.B128X128) # 设置图像大小,用于帧差异
sensor.set_windowing((92,112)) # 设置窗口ROI
sensor.skip_frames(10) # 跳过一些帧,等待感光元件变稳定

# 降低环境因素的影响
sensor.set_auto_gain(True) # 开启自动增益
sensor.set_auto_whitebal(True) # 开启自动白平衡
sensor.set_auto_exposure(True) # 开启自动曝光


# 定义IO口
# 输入引脚
p_in0 = Pin('P0', Pin.IN, Pin.PULL_UP) # 设置P0为“按键1”输入引脚,并开启上拉电阻
p_in1 = Pin('P1', Pin.IN, Pin.PULL_UP) # 设置P1为“按键2”输入引脚,并开启上拉电阻
p_in2 = Pin('P2', Pin.IN, Pin.PULL_UP) # 设置P2为“按键3”输入引脚,并开启上拉电阻
p_in3 = Pin('P3', Pin.IN, Pin.PULL_UP) # 设置P3为“按键4”输入引脚,并开启上拉电阻
# 输出引脚
RED_LED_PIN = 1 # 红色LED输出引脚
GREEN_LED_PIN = 2 # 绿色LED输出引脚
BLUE_LED_PIN = 3 # 蓝色LED输出引脚
p_out0 = Pin('P4', Pin.OUT_PP) # 设置p_out为输出引脚
p_out1 = Pin('P5', Pin.OUT_PP) # 设置p_out为输出引脚
p_out2 = Pin('P6', Pin.OUT_PP) # 设置p_out为输出引脚
p_out3 = Pin('P7', Pin.OUT_PP) # 设置p_out为输出引脚

# 定义全局变量
face_result = 0 #身份识别结果
mask_result = 0 #口罩判别结果
iden_out_result = 0 #学习后识别结果

# 功能部分
while(True):
    # 读取输入引脚数值
    value0 = p_in0.value() # 读入p_in0引脚的值
    value1 = p_in1.value() # 读入p_in1引脚的值
    value2 = p_in2.value() # 读入p_in2引脚的值
    value3 = p_in3.value() # 读入p_in3引脚的值

    # 定义变量
    face_num0 = 0
    face_num1 = 0
    face_num2 = 0

    mask_num0 = 0
    mask_num1 = 0
    mask_num2 = 0

    iden_in_num0 = 0
    iden_in_num1 = 0
    iden_in_num2 = 0

    iden_out_num0 = 0
    iden_out_num1 = 0
    iden_out_num2 = 0

    # 判断输入引脚数值
    if value2 == 0: # 第3个按键按下时,开启学习模式
        print("开启学习模式")
        p_out0.low() # 0010 第3个灯亮 表示第3个按键被按下
        p_out1.low()
        p_out2.high()
        p_out3.low()

        sensor.skip_frames(time = 500) # 延时0.5s

        p_out0.low() # 将4个输出IO口全部置低电平(不然灯会一直亮着)
        p_out1.low()
        p_out2.low()
        p_out3.low()

        if value0 == 0: # 第1个按键按下时,学习第1个人
            print("正在学习第1个人脸")

            #设置识别数量
            iden_in_num0 = 1 # 设置被拍摄者序号,第一个人的图片保存到i1文件夹,第二个人的图片保存到i2文件夹,以此类推。每次更换拍摄者时,修改num值
            n = 5 # 拍摄照片数量

            while(n):
                pyb.LED(RED_LED_PIN).on() # 红灯亮

                sensor.skip_frames(time = 3000) # 延时3s

                pyb.LED(RED_LED_PIN).off() # 红灯灭
                pyb.LED(BLUE_LED_PIN).on() # 蓝灯亮

                # 保存截取到的图片到SD卡
                print(n)
                sensor.snapshot().save("identify/i%s/%s.pgm" % (iden_in_num0, n) ) # 此处会被保存于"identify/i1/"

                n -= 1 

                pyb.LED(BLUE_LED_PIN).off() # 蓝灯灭
            break
        if value1 == 0: # 第2个按键按下时,学习第2个人
            print("正在学习第2个人脸")
            iden_in_num1 = 2 # 设置被拍摄者序号,第一个人的图片保存到i1文件夹,第二个人的图片保存到i2文件夹,以此类推。每次更换拍摄者时,修改num值
            n = 5 # 拍摄照片数量
            while(n):
                pyb.LED(RED_LED_PIN).on() # 红灯亮

                sensor.skip_frames(time = 3000) # 延时3s

                pyb.LED(RED_LED_PIN).off() # 红灯灭
                pyb.LED(BLUE_LED_PIN).on() # 蓝灯亮

                # 保存截取到的图片到SD卡
                print(n)
                sensor.snapshot().save("identify/i%s/%s.pgm" % (iden_in_num1, n) ) # 此处会被保存于"identify/i2/"

                n -= 1

                pyb.LED(BLUE_LED_PIN).off() # 蓝灯灭
            break
        if value3 == 0: # 第4个按键按下时,学习第3个人(因为第3个按键需长按,保持P6在低电平状态,才能执行之后的程序)
            print("正在学习第3个人脸")
            iden_in_num2 = 3 # 设置被拍摄者序号,第一个人的图片保存到i1文件夹,第二个人的图片保存到i2文件夹,以此类推。每次更换拍摄者时,修改num值
            n = 5 # 拍摄照片数量
            while(n):
                pyb.LED(RED_LED_PIN).on()  # 红灯亮

                sensor.skip_frames(time = 3000) # 延时3s

                pyb.LED(RED_LED_PIN).off() # 红灯灭
                pyb.LED(BLUE_LED_PIN).on() # 蓝灯亮

                # 保存截取到的图片到SD卡
                print(n)
                sensor.snapshot().save("identify/i%s/%s.pgm" % (iden_in_num2, n) ) # 此处会被保存于"identify/i3/"
                n -= 1

                pyb.LED(BLUE_LED_PIN).off() # 蓝灯灭
            break

    if value3 == 0: # 第4个按键单独按下时,开启学习后识别模式
        print("正在开启学习后识别模式")
        p_out0.low() # 0001 第4个灯亮 表示第4个按键被按下
        p_out1.low()
        p_out2.low()
        p_out3.high()

        sensor.skip_frames(time = 500) #延时0.5s

        p_out0.low() # 将4个输出IO口全部置低电平
        p_out1.low()
        p_out2.low()
        p_out3.low()

        #设置样本数量
        NUM_SUBJECTS = 3 # 图像库总人数
        NUM_SUBJECTS_IMGS = 5 # 样本图片数量

        for iden in range(3): # 循环拍摄3次样本图片,进行比对,输出出现次数最多的结果
            # 拍摄当前人脸
            sensor.skip_frames(time = 3000) # 等待3s

            img = sensor.snapshot() # 拍照

            d0 = img.find_lbp((11,14,70,84)) # 当前拍摄人脸的lbp特征,将识别ROI区域往内缩小1/8,避免一定程度上背景带来的影响
            img = None
            pmin = 999999 # pmin为最小特征差异度
            iden_out_num=0

            def min(pmin, a, s): # 确保几次循环后pmin还是最小特征差异度,a代表dist/NUM_SUBJECTS_IMGS
                global iden_out_num
                if a<pmin:
                    pmin=a
                    iden_out_num=s
                return pmin

            for s in range(1, NUM_SUBJECTS+1):
                dist = 0
                for i in range(2, NUM_SUBJECTS_IMGS+1):
                    img = image.Image("identify/i%s/%s.pgm"%(s, i))
                    d1 = img.find_lbp((11,14,70,84)) # d1为第s文件夹中的第i张图片的lbp特征,识别ROI区域往内缩小1/8
                    dist += image.match_descriptor(d0, d1) # 计算d0 d1即样本图像与被检测人脸的特征差异度。
                print("第%d个的特征差异度是: %d"%(s, dist/NUM_SUBJECTS_IMGS)) # 输出当前特征差异度,方便在串口终端显示数据
                pmin = min(pmin, dist/NUM_SUBJECTS_IMGS, s) # 特征差异度越小,被检测人脸与此样本更相似更匹配。
                print(pmin)

            # 按照循环次数,将每次识别后的结果存入,共3次
            if iden == 0:
                iden_out_num0 = iden_out_num
                print("第%d次:%d"%(iden+1,iden_out_num0))
            if iden == 1:
                iden_out_num1 = iden_out_num
                print("第%d次:%d"%(iden+1,iden_out_num1))
            if iden == 2:
                iden_out_num2 = iden_out_num
                print("第%d次:%d"%(iden+1,iden_out_num2))

            pyb.LED(BLUE_LED_PIN).on() # 蓝灯亮
            sensor.skip_frames(time = 50) # 等待0.05s
            pyb.LED(BLUE_LED_PIN).off() # 蓝灯灭

        # 在此处对3次输出结果进行比较,输出出现次数最多的那个结果。如果三个分别为不同结果,则报错。
        if iden_out_num0 == iden_out_num1 == iden_out_num2:
            print("结果是:",iden_out_num0)
            iden_out_result = iden_out_num0
        else:
            if iden_out_num0 == iden_out_num1:
                print("结果是:",iden_out_num0)
                iden_out_result = iden_out_num0
            if iden_out_num0 == iden_out_num2:
                print("结果是:",iden_out_num0)
                iden_out_result = iden_out_num0
            if iden_out_num1 == iden_out_num2:
                print("结果是:",iden_out_num1)
                iden_out_result = iden_out_num1
            #else: #不知道为什么有时候有相同结果时也会报错,故此处屏蔽该段代码,下同
             #   print("error")
              #  iden_out_result = 15



    if value0 == 0: # 第1个按键单独按下时,开启身份识别模式
        print("正在开启身份识别模式")
        p_out0.high() # 0001 第1个灯亮 表示第1个按键被按下
        p_out1.low()
        p_out2.low()
        p_out3.low()

        sensor.skip_frames(time = 500) # 延时0.5s

        p_out0.low() # 将4个输出IO口全部置低电平
        p_out1.low()
        p_out2.low()
        p_out3.low()

        #设置样本数量
        NUM_SUBJECTS = 3 # 图像库总人数
        NUM_SUBJECTS_IMGS = 5 # 样本图片数量

        for face in range(3):
            # 拍摄当前人脸
            sensor.skip_frames(time = 3000) # 等待3s

            img = sensor.snapshot() # 拍照

            d0 = img.find_lbp((11,14,70,84)) # 当前拍摄人脸的lbp特征,将识别ROI区域往内缩小1/8

            img = None
            pmin = 999999 # pmin为最小特征差异度
            face_num=0

            def min(pmin, a, s): # 确保几次循环后pmin还是最小特征差异度,a代表dist/NUM_SUBJECTS_IMGS
                global face_num
                if a<pmin:
                    pmin=a
                    face_num=s
                return pmin

            for s in range(1, NUM_SUBJECTS+1):
                dist = 0
                for i in range(2, NUM_SUBJECTS_IMGS+1):
                    img = image.Image("face/f%d/%d.pgm"%(s, i))
                    d1 = img.find_lbp((11,14,70,84)) # d1为第s文件夹中的第i张图片的lbp特征,识别ROI区域往内缩小1/8
                    dist += image.match_descriptor(d0, d1) # 计算d0 d1即样本图像与被检测人脸的特征差异度。
                print("Average dist for subject %d: %d"%(s, dist/NUM_SUBJECTS_IMGS)) # 输出当前特征差异度,方便在串口终端显示数据
                pmin = min(pmin, dist/NUM_SUBJECTS_IMGS, s) # 特征差异度越小,被检测人脸与此样本更相似更匹配。
                print(pmin)

            # 按照循环次数,将每次识别后的结果存入,共3次               
            if face == 0:
                face_num0 = face_num
                print("第%d次:%d"%(face+1,face_num0))
            if face == 1:
                face_num1 = face_num
                print("第%d次:%d"%(face+1,face_num1))
            if face == 2:
                face_num2 = face_num
                print("第%d次:%d"%(face+1,face_num2))

            pyb.LED(RED_LED_PIN).on() # 红灯亮
            sensor.skip_frames(time = 50) # 等待0.05s
            pyb.LED(RED_LED_PIN).off() # 红灯灭

        # 在此处对3次输出结果进行比较,输出出现次数最多的那个结果。如果三个分别为不同结果,则报错。
        if face_num0 == face_num1 == face_num2:
            print("结果是:",face_num0)
            face_result = face_num0
        else:
            if face_num0 == face_num1:
                print("结果是:",face_num0)
                face_result = face_num0
            if face_num0 == face_num2:
                print("结果是:",face_num0)
                face_result = face_num0
            if face_num1 == face_num2:
                print("结果是:",face_num1)
                face_result = face_num1
            #else:
             #   print("error")
              #  face_result = 15

    if value1 == 0: # 第2个按键单独按下时,开启口罩判别模式
        print("正在开启口罩判别模式")

        p_out0.low() # 0100 第2个灯亮 表示第2个按键被按下
        p_out1.high()
        p_out2.low()
        p_out3.low()

        sensor.skip_frames(time = 500) # 延时0.5s

        p_out0.low() # 将4个输出IO口全部置低电平
        p_out1.low()
        p_out2.low()
        p_out3.low()

        #设置样本数量
        NUM_SUBJECTS = 3 # 图像库总人数
        NUM_SUBJECTS_IMGS = 5 # 样本图片数量

        for mask in range(3):
            # 拍摄当前人脸。
            sensor.skip_frames(time = 3000) # 等待3s
            img = sensor.snapshot() # 拍照
            d0 = img.find_lbp((11,14,70,84)) # 当前拍摄人脸的lbp特征,将识别ROI区域往内缩小1/8

            img = None
            pmin = 999999 # pmin为最小特征差异度
            mask_num=0

            def min(pmin, a, s): # 确保几次循环后pmin还是最小特征差异度,a代表dist/NUM_SUBJECTS_IMGS
                global mask_num
                if a<pmin:
                    pmin=a
                    mask_num=s
                return pmin

            for s in range(1, NUM_SUBJECTS+1):
                dist = 0
                for i in range(2, NUM_SUBJECTS_IMGS+1):
                    img = image.Image("mask/m%d/%d.pgm"%(s, i))
                    d1 = img.find_lbp((11,14,70,84)) # d1为第s文件夹中的第i张图片的lbp特征,识别ROI区域往内缩小1/8
                    dist += image.match_descriptor(d0, d1) # 计算d0 d1即样本图像与被检测人脸的特征差异度。
                print("Average dist for subject %d: %d"%(s, dist/NUM_SUBJECTS_IMGS)) # 输出当前特征差异度,方便在串口终端显示数据
                pmin = min(pmin, dist/NUM_SUBJECTS_IMGS, s) # 特征差异度越小,被检测人脸与此样本更相似更匹配。
                print(pmin)

            # 按照循环次数,将每次识别后的结果存入,共3次            
            if mask == 0:
                mask_num0 = mask_num
                print("第%d次:%d"%(mask+1,mask_num0))
            if mask == 1:
                mask_num1 = mask_num
                print("第%d次:%d"%(mask+1,mask_num1))
            if mask == 2:
                mask_num2 = mask_num
                print("第%d次:%d"%(mask+1,mask_num2))

            pyb.LED(GREEN_LED_PIN).on() # 绿灯亮
            sensor.skip_frames(time = 50) # 等待0.05s
            pyb.LED(GREEN_LED_PIN).off() #绿灯灭

        # 在此处对3次输出结果进行比较,输出出现次数最多的那个结果。如果三个分别为不同结果,则报错。
        if mask_num0 == mask_num1 == mask_num2:
            print("结果是:",mask_num0)
            mask_result = mask_num0
        else:
            if mask_num0 == mask_num1:
                print("结果是:",mask_num0)
                mask_result = mask_num0
            if mask_num0 == mask_num2:
                print("结果是:",mask_num0)
                mask_result = mask_num0
            if mask_num1 == mask_num2:
                print("结果是:",mask_num1)
                mask_result = mask_num1
            #else:
                #print("error")

    # 此处为输出IO口高低电平定义,具体请参照附表
    # 身份识别结果输出
    if face_result == 1:#1000
        print("face_num1",face_result)
        p_out0.high()
        p_out1.low()
        p_out2.low()
        p_out3.low()

    if face_result == 2:#0100
        print("face_num2",face_result)
        p_out0.low()
        p_out1.high()
        p_out2.low()
        p_out3.low()

    if face_result == 3:#0010
        print("face_num3",face_result)
        p_out0.low()
        p_out1.low()
        p_out2.high()
        p_out3.low()

    if face_result == 15:#1111
        print("error",face_result)
        p_out0.high()
        p_out1.high()
        p_out2.high()
        p_out3.high()

    # 口罩判别结果输出
    if mask_result == 1:#0011
        print("mask_num1",mask_result)
        p_out0.low()
        p_out1.low()
        p_out2.high()
        p_out3.high()

    if mask_result == 2:#1100
        print("mask_num2",mask_result)
        p_out0.high()
        p_out1.high()
        p_out2.low()
        p_out3.low()

    if mask_result == 15:#1111
        print("error",mask_result)
        p_out0.high()
        p_out1.high()
        p_out2.high()
        p_out3.high()

    # 学习后识别结果输出
    if iden_out_result == 1:#0100
        print("iden_out_num1",iden_out_result)
        p_out0.low()
        p_out1.high()
        p_out2.low()
        p_out3.low()

    if iden_out_result == 2:#0010
        print("iden_out_num2",iden_out_result)
        p_out0.low()
        p_out1.low()
        p_out2.high()
        p_out3.low()

    if iden_out_result == 3:#0001
        print("iden_out_num3",iden_out_result)
        p_out0.low()
        p_out1.low()
        p_out2.low()
        p_out3.high()

    if iden_out_result == 15:#1111
        print("error",iden_out_result)
        p_out0.high()
        p_out1.high()
        p_out2.high()
        p_out3.high()

   转载规则


《2020年TI杯赛后总结》 InImpasse 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录