Featured image of post 2020年TI杯赛后总结

2020年TI杯赛后总结

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中提出。

代码:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
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()