-
针对照片中存在多人脸的情况,能否取最大的人脸区域进行检测。很多情况下手持证件的照片均无法检测成功。建议改进以适用于更多的场景!
-
人脸任务在计算机视觉领域中十分重要,本项目主要使用了两类技术:人脸检测+人脸识别。代码分为两部分内容:人脸注册 和 人脸识别人脸注册:将人脸特征存储进数据库,这里用feature.csv代替人脸识别:将人脸特征与CSV文件中人脸特征进行比较,如果成功匹配则写入考勤文件attendance.csv文章前半部分为一步步实现流程介绍,最后会有整理过后的完整项目代码。一、项目实现A. 注册:导入相关包import cv2import numpy as npimport dlibimport timeimport csv# from argparse import ArgumentParserfrom PIL import Image, ImageDraw, ImageFont设计注册功能注册过程我们需要完成的事:打开摄像头获取画面图片在图片中检测并获取人脸位置根据人脸位置获取68个关键点根据68个关键点生成特征描述符保存(优化)展示界面,加入注册时成功提示等1、基本步骤我们首先进行前三步:# 检测人脸,获取68个关键点,获取特征描述符def faceRegister(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):'''faceId:人脸IDuserName: 人脸姓名faceCount: 采集该人脸图片的数量interval: 采集间隔'''cap = cv2.VideoCapture(0)# 人脸检测模型hog_face_detector = dlib.get_frontal_face_detector()# 关键点 检测模型shape_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')# resnet模型face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')while True:ret, frame = cap.read()# 镜像frame = cv2.flip(frame,1)# 转为灰度图frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 检测人脸detections = hog_face_detector(frame,1)for face in detections:# 人脸框坐标 左上和右下l, t, r, b = face.left(), face.top(), face.right(), face.bottom()# 获取68个关键点points = shape_detector(frame,face)# 绘制关键点for point in points.parts():cv2.circle(frame,(point.x,point.y),2,(0,255,0),1)# 绘制矩形框cv2.rectangle(frame,(l,t),(r,b),(0,255,0),2)cv2.imshow("face",frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakcap.release()cv2.destroyAllWindowsfaceRegister()此时一张帅脸如下:2、描述符的采集之后,我们根据参数,即faceCount 和 Interval 进行描述符的生成和采集。(这里我默认是faceCount=3,Interval=3,即每3秒采集一次,共3次)def faceRegister(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):'''faceId:人脸IDuserName: 人脸姓名faceCount: 采集该人脸图片的数量interval: 采集间隔'''cap = cv2.VideoCapture(0)# 人脸检测模型hog_face_detector = dlib.get_frontal_face_detector()# 关键点 检测模型shape_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')# resnet模型face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')# 开始时间start_time = time.time()# 执行次数collect_times = 0while True:ret, frame = cap.read()# 镜像frame = cv2.flip(frame,1)# 转为灰度图frame_gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)# 检测人脸detections = hog_face_detector(frame,1)for face in detections:# 人脸框坐标 左上和右下l, t, r, b = face.left(), face.top(), face.right(), face.bottom()# 获取68个关键点points = shape_detector(frame,face)# 绘制人脸关键点for point in points.parts():cv2.circle(frame, (point.x, point.y), 2, (0, 255, 0), 1)# 绘制矩形框cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)# 采集:if collect_times < faceCount:# 获取当前时间now = time.time()# 时间限制if now - start_time > interval:# 获取特征描述符face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame,points)# dlib格式转为数组face_descriptor = [f for f in face_descriptor]collect_times += 1start_time = nowprint("成功采集{}次".format(collect_times))else:# 时间间隔不到intervalprint("等待进行下一次采集")passelse:# 已经成功采集完3次了print("采集完毕")cap.release()cv2.destroyAllWindows()returncv2.imshow("face",frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakcap.release()cv2.destroyAllWindows()faceRegister()等待进行下一次采集...成功采集1次等待进行下一次采集...成功采集2次等待进行下一次采集...成功采集3次采集完毕3、完整的注册最后就是写入csv文件这里加入了注册成功等的提示,且把一些变量放到了全局,因为后面人脸识别打卡时也会用到。# 加载人脸检测器hog_face_detector = dlib.get_frontal_face_detector()cnn_detector = dlib.cnn_face_detection_model_v1('./weights/mmod_human_face_detector.dat')haar_face_detector = cv2.CascadeClassifier('./weights/haarcascade_frontalface_default.xml')# 加载关键点检测器points_detector = dlib.shape_predictor('./weights/shape_predictor_68_face_landmarks.dat')# 加载resnet模型face_descriptor_extractor = dlib.face_recognition_model_v1('./weights/dlib_face_recognition_resnet_model_v1.dat')# 绘制中文def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):if (isinstance(img, np.ndarray)): # 判断是否OpenCV图片类型img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))# 创建一个可以在给定图像上绘图的对象draw = ImageDraw.Draw(img)# 字体的格式fontStyle = ImageFont.truetype("./fonts/songti.ttc", textSize, encoding="utf-8")# 绘制文本draw.text(position, text, textColor, font=fontStyle)# 转换回OpenCV格式return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)# 绘制左侧信息def drawLeftInfo(frame, fpsText, mode="Reg", detector='haar', person=1, count=1):# 帧率cv2.putText(frame, "FPS: " + str(round(fpsText, 2)), (30, 50), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 模式:注册、识别cv2.putText(frame, "Mode: " + str(mode), (30, 80), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)if mode == 'Recog':# 检测器cv2.putText(frame, "Detector: " + detector, (30, 110), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 人数cv2.putText(frame, "Person: " + str(person), (30, 140), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 总人数cv2.putText(frame, "Count: " + str(count), (30, 170), cv2.FONT_ITALIC, 0.8, (0, 255, 0), 2)# 注册人脸def faceRegiser(faceId=1, userName='default', interval=3, faceCount=3, resize_w=700, resize_h=400):# 计数count = 0# 开始注册时间startTime = time.time()# 视频时间frameTime = startTime# 控制显示打卡成功的时长show_time = (startTime - 10)# 打开文件f = open('./data/feature.csv', 'a', newline='')csv_writer = csv.writer(f)cap = cv2.VideoCapture(0)while True:ret, frame = cap.read()frame = cv2.resize(frame, (resize_w, resize_h))frame = cv2.flip(frame, 1)# 检测face_detetion = hog_face_detector(frame, 1)for face in face_detetion:# 识别68个关键点points = points_detector(frame, face)# 绘制人脸关键点for point in points.parts():cv2.circle(frame, (point.x, point.y), 2, (0, 255, 0), 1)# 绘制框框l, t, r, b = face.left(), face.top(), face.right(), face.bottom()cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)now = time.time()if (now - show_time) < 0.5:frame = cv2AddChineseText(frame,"注册成功 {count}/{faceCount}".format(count=(count + 1), faceCount=faceCount),(l, b + 30), textColor=(255, 0, 255), textSize=30)# 检查次数if count < faceCount:# 检查时间if now - startTime > interval:# 特征描述符face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame, points)face_descriptor = [f for f in face_descriptor]# 描述符增加进data文件line = [faceId, userName, face_descriptor]# 写入csv_writer.writerow(line)# 保存照片样本print('人脸注册成功 {count}/{faceCount},faceId:{faceId},userName:{userName}'.format(count=(count + 1),faceCount=faceCount,faceId=faceId,userName=userName))frame = cv2AddChineseText(frame,"注册成功 {count}/{faceCount}".format(count=(count + 1), faceCount=faceCount),(l, b + 30), textColor=(255, 0, 255), textSize=30)show_time = time.time()# 时间重置startTime = now# 次数加一count += 1else:print('人脸注册完毕')f.close()cap.release()cv2.destroyAllWindows()returnnow = time.time()fpsText = 1 / (now - frameTime)frameTime = now# 绘制drawLeftInfo(frame, fpsText, 'Register')cv2.imshow('Face Attendance Demo: Register', frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakf.close()cap.release()cv2.destroyAllWindows()此时执行:faceRegiser(3,"用户B")人脸注册成功 1/3,faceId:3,userName:用户B人脸注册成功 2/3,faceId:3,userName:用户B人脸注册成功 3/3,faceId:3,userName:用户B人脸注册完毕其features文件:B. 识别、打卡识别步骤如下:打开摄像头获取画面根据画面中的图片获取里面的人脸特征描述符根据特征描述符将其与feature.csv文件里特征做距离判断获取ID、NAME考勤记录写入attendance.csv里这里与上面流程相似,不过是加了一个对比功能,距离小于阈值,则表示匹配成功。就加快速度不一步步来了,代码如下:# 刷新右侧考勤信息def updateRightInfo(frame, face_info_list, face_img_list):# 重新绘制逻辑:从列表中每隔3个取一批显示,新增人脸放在最前面# 如果有更新,重新绘制# 如果没有,定时往后移动left_x = 30left_y = 20resize_w = 80offset_y = 120index = 0frame_h = frame.shape[0]frame_w = frame.shape[1]for face in face_info_list[:3]:name = face[0]time = face[1]face_img = face_img_list[index]# print(face_img.shape)face_img = cv2.resize(face_img, (resize_w, resize_w))offset_y_value = offset_y * indexframe[(left_y + offset_y_value):(left_y + resize_w + offset_y_value), -(left_x + resize_w):-left_x] = face_imgcv2.putText(frame, name, ((frame_w - (left_x + resize_w)), (left_y + resize_w) + 15 + offset_y_value),cv2.FONT_ITALIC, 0.5, (0, 255, 0), 1)cv2.putText(frame, time, ((frame_w - (left_x + resize_w)), (left_y + resize_w) + 30 + offset_y_value),cv2.FONT_ITALIC, 0.5, (0, 255, 0), 1)index += 1return frame# 返回DLIB格式的facedef getDlibRect(detector='hog', face=None):l, t, r, b = None, None, None, Noneif detector == 'hog':l, t, r, b = face.left(), face.top(), face.right(), face.bottom()if detector == 'cnn':l = face.rect.left()t = face.rect.top()r = face.rect.right()b = face.rect.bottom()if detector == 'haar':l = face[0]t = face[1]r = face[0] + face[2]b = face[1] + face[3]nonnegative = lambda x: x if x >= 0 else 0return map(nonnegative, (l, t, r, b))# 获取CSV中信息def getFeatList():print('加载注册的人脸特征')feature_list = Nonelabel_list = []name_list = []# 加载保存的特征样本with open('./data/feature.csv', 'r') as f:csv_reader = csv.reader(f)for line in csv_reader:# 重新加载数据faceId = line[0]userName = line[1]face_descriptor = eval(line[2])label_list.append(faceId)name_list.append(userName)# 转为numpy格式face_descriptor = np.asarray(face_descriptor, dtype=np.float64)# 转为二维矩阵,拼接face_descriptor = np.reshape(face_descriptor, (1, -1))# 初始化if feature_list is None:feature_list = face_descriptorelse:# 拼接feature_list = np.concatenate((feature_list, face_descriptor), axis=0)print("特征加载完毕")return feature_list, label_list, name_list# 人脸识别def faceRecognize(detector='haar', threshold=0.5, write_video=False, resize_w=700, resize_h=400):# 视频时间frameTime = time.time()# 加载特征feature_list, label_list, name_list = getFeatList()face_time_dict = {}# 保存name,time人脸信息face_info_list = []# numpy格式人脸图像数据face_img_list = []# 侦测人数person_detect = 0# 统计人脸数face_count = 0# 控制显示打卡成功的时长show_time = (frameTime - 10)# 考勤记录f = open('./data/attendance.csv', 'a')csv_writer = csv.writer(f)cap = cv2.VideoCapture(0)# resize_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))//2# resize_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) //2videoWriter = cv2.VideoWriter('./record_video/out' + str(time.time()) + '.mp4', cv2.VideoWriter_fourcc(*'MP4V'), 15,(resize_w, resize_h))while True:ret, frame = cap.read()frame = cv2.resize(frame, (resize_w, resize_h))frame = cv2.flip(frame, 1)# 切换人脸检测器if detector == 'hog':face_detetion = hog_face_detector(frame, 1)if detector == 'cnn':face_detetion = cnn_detector(frame, 1)if detector == 'haar':frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)face_detetion = haar_face_detector.detectMultiScale(frame_gray, minNeighbors=7, minSize=(100, 100))person_detect = len(face_detetion)for face in face_detetion:l, t, r, b = getDlibRect(detector, face)face = dlib.rectangle(l, t, r, b)# 识别68个关键点points = points_detector(frame, face)cv2.rectangle(frame, (l, t), (r, b), (0, 255, 0), 2)# 人脸区域face_crop = frame[t:b, l:r]# 特征face_descriptor = face_descriptor_extractor.compute_face_descriptor(frame, points)face_descriptor = [f for f in face_descriptor]face_descriptor = np.asarray(face_descriptor, dtype=np.float64)# 计算距离distance = np.linalg.norm((face_descriptor - feature_list), axis=1)# 最小距离索引min_index = np.argmin(distance)# 最小距离min_distance = distance[min_index]predict_name = "Not recog"if min_distance < threshold:# 距离小于阈值,表示匹配predict_id = label_list[min_index]predict_name = name_list[min_index]# 判断是否新增记录:如果一个人距上次检测时间>3秒,或者换了一个人,将这条记录插入need_insert = Falsenow = time.time()if predict_name in face_time_dict:if (now - face_time_dict[predict_name]) > 3:# 刷新时间face_time_dict[predict_name] = nowneed_insert = Trueelse:# 还是上次人脸need_insert = Falseelse:# 新增数据记录face_time_dict[predict_name] = nowneed_insert = Trueif (now - show_time) < 1:frame = cv2AddChineseText(frame, "打卡成功", (l, b + 30), textColor=(0, 255, 0), textSize=40)if need_insert:# 连续显示打卡成功1sframe = cv2AddChineseText(frame, "打卡成功", (l, b + 30), textColor=(0, 255, 0), textSize=40)show_time = time.time()time_local = time.localtime(face_time_dict[predict_name])# 转换成新的时间格式(2016-05-05 20:28:54)face_time = time.strftime("%H:%M:%S", time_local)face_time_full = time.strftime("%Y-%m-%d %H:%M:%S", time_local)# 开始位置增加face_info_list.insert(0, [predict_name, face_time])face_img_list.insert(0, face_crop)# 写入考勤表line = [predict_id, predict_name, min_distance, face_time_full]csv_writer.writerow(line)face_count += 1# 绘制人脸点cv2.putText(frame, predict_name + " " + str(round(min_distance, 2)), (l, b + 30), cv2.FONT_ITALIC, 0.8,(0, 255, 0), 2)# 处理下一张脸now = time.time()fpsText = 1 / (now - frameTime)frameTime = now# 绘制drawLeftInfo(frame, fpsText, 'Recog', detector=detector, person=person_detect, count=face_count)# 舍弃face_img_list、face_info_list后部分,节约内存if len(face_info_list) > 10:face_info_list = face_info_list[:9]face_img_list = face_img_list[:9]frame = updateRightInfo(frame, face_info_list, face_img_list)if write_video:videoWriter.write(frame)cv2.imshow('Face Attendance Demo: Recognition', frame)if cv2.waitKey(10) & 0xFF == ord('q'):breakf.close()videoWriter.release()cap.release()cv2.destroyAllWindows()然后效果就和我们宿舍楼下差不多了~我年轻的时候,我大概比现在帅个几百倍吧,哎。二、总代码上文其实把登录和注册最后一部分代码放在一起就是了,这里就不再复制粘贴了,相关权重文件下载链接:opencv/data at master · opencv/opencv · GitHub懒得下载或者懒得找也可以私信我发你,看见或有时间回。当然本项目还有很多需要优化的地方,比如设置用户不能重复、考勤打卡每天只能一次、把csv改为链接成数据库等等,后续代码优化完成后就可以部署然后和室友**了。————————————————版权声明:本文为CSDN博主「老师我作业忘带了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/suic009/article/details/127382811
-
一直返回这个但APIPost可以正常获取
-
前言 在之前的这篇博客中,简要介绍了BiFPN的原理,以及YOLOv5作者如何结合BiFPN:【魔改YOLOv5-6.x(中)】:加入ACON激活函数、CBAM和CA注意力机制、加权双向特征金字塔BiFPN 本文将尝试进一步结合BiFPN,主要参考自:YOLOv5结合BiFPN 修改yaml文件(以yolov5s为例) 只修改一处 本文以yolov5s.yaml为例进行修改,修改模型配置文件时要注意以下几点: 这里的yaml文件只修改了一处,也就是将19层的Concat换成了BiFPN_Add,要想修改其他层的Concat,可以类比进行修改 BiFPN_Add本质是add操作,不是concat操作,因此,BiFPN_Add的各个输入层要求大小完全一致(通道数、feature map大小等),因此,这里要修改之前的参数[-1, 13, 6],来满足这个要求: -1层就是上一层的输出,原来上一层的输出channel数为256,这里改成512 13层就是这里[-1, 3, C3, [512, False]], # 13 这样修改后,BiFPN_Add各个输入大小都是[bs,256,40,40] 最后BiFPN_Add后面的参数层设置为[256, 256]也就是输入输出channel数都是256 # YOLOv5 🚀 by Ultralytics, GPL-3.0 license # Parameters nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 BiFPN head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, Concat, [1]], # cat backbone P4 [-1, 3, C3, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 4], 1, Concat, [1]], # cat backbone P3 [-1, 3, C3, [256, False]], # 17 (P3/8-small) [-1, 1, Conv, [512, 3, 2]], # 为了BiFPN正确add,调整channel数 [[-1, 13, 6], 1, BiFPN_Add3, [256, 256]], # cat P4 <--- BiFPN change 注意v5s通道数是默认参数的一半 [-1, 3, C3, [512, False]], # 20 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, Concat, [1]], # cat head P5 [-1, 3, C3, [1024, False]], # 23 (P5/32-large) [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) ] 将Concat全部换成BiFPN_Add # YOLOv5 🚀 by Ultralytics, GPL-3.0 license # Parameters nc: 80 # number of classes depth_multiple: 0.33 # model depth multiple width_multiple: 0.50 # layer channel multiple anchors: - [10,13, 16,30, 33,23] # P3/8 - [30,61, 62,45, 59,119] # P4/16 - [116,90, 156,198, 373,326] # P5/32 # YOLOv5 v6.0 backbone backbone: # [from, number, module, args] [[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2 [-1, 1, Conv, [128, 3, 2]], # 1-P2/4 [-1, 3, C3, [128]], [-1, 1, Conv, [256, 3, 2]], # 3-P3/8 [-1, 6, C3, [256]], [-1, 1, Conv, [512, 3, 2]], # 5-P4/16 [-1, 9, C3, [512]], [-1, 1, Conv, [1024, 3, 2]], # 7-P5/32 [-1, 3, C3, [1024]], [-1, 1, SPPF, [1024, 5]], # 9 ] # YOLOv5 v6.0 BiFPN head head: [[-1, 1, Conv, [512, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 6], 1, BiFPN_Add2, [256, 256]], # cat backbone P4 [-1, 3, C3, [512, False]], # 13 [-1, 1, Conv, [256, 1, 1]], [-1, 1, nn.Upsample, [None, 2, 'nearest']], [[-1, 4], 1, BiFPN_Add2, [128, 128]], # cat backbone P3 [-1, 3, C3, [256, False]], # 17 (P3/8-small) [-1, 1, Conv, [512, 3, 2]], # 为了BiFPN正确add,调整channel数 [[-1, 13, 6], 1, BiFPN_Add3, [256, 256]], # cat P4 <--- BiFPN change 注意v5s通道数是默认参数的一半 [-1, 3, C3, [512, False]], # 20 (P4/16-medium) [-1, 1, Conv, [512, 3, 2]], [[-1, 10], 1, BiFPN_Add2, [256, 256]], # cat head P5 [-1, 3, C3, [1024, False]], # 23 (P5/32-large) [[17, 20, 23], 1, Detect, [nc, anchors]], # Detect(P3, P4, P5) ] 打印模型参数 可以参考这篇博客:【YOLOv5-6.x】模型参数及detect层输出测试(自用),进行模型配置文件测试并查看输出结果: from n params module arguments 0 -1 1 3520 models.common.Conv [3, 32, 6, 2, 2] 1 -1 1 18560 models.common.Conv [32, 64, 3, 2] 2 -1 1 18816 models.common.C3 [64, 64, 1] 3 -1 1 73984 models.common.Conv [64, 128, 3, 2] 4 -1 2 115712 models.common.C3 [128, 128, 2] 5 -1 1 295424 models.common.Conv [128, 256, 3, 2] 6 -1 3 625152 models.common.C3 [256, 256, 3] 7 -1 1 1180672 models.common.Conv [256, 512, 3, 2] 8 -1 1 1182720 models.common.C3 [512, 512, 1] 9 -1 1 656896 models.common.SPPF [512, 512, 5] 10 -1 1 131584 models.common.Conv [512, 256, 1, 1] 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 12 [-1, 6] 1 65794 models.common.BiFPN_Add2 [256, 256] 13 -1 1 296448 models.common.C3 [256, 256, 1, False] 14 -1 1 33024 models.common.Conv [256, 128, 1, 1] 15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] 16 [-1, 4] 1 16514 models.common.BiFPN_Add2 [128, 128] 17 -1 1 74496 models.common.C3 [128, 128, 1, False] 18 -1 1 295424 models.common.Conv [128, 256, 3, 2] 19 [-1, 13, 6] 1 65795 models.common.BiFPN_Add3 [256, 256] 20 -1 1 296448 models.common.C3 [256, 256, 1, False] 21 -1 1 590336 models.common.Conv [256, 256, 3, 2] 22 [-1, 10] 1 65794 models.common.BiFPN_Add2 [256, 256] 23 -1 1 1051648 models.common.C3 [256, 512, 1, False] 24 [17, 20, 23] 1 229245 models.yolo.Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]] Model Summary: 278 layers, 7384006 parameters, 7384006 gradients, 17.2 GFLOPs 修改common.py 复制粘贴一下代码: # 结合BiFPN 设置可学习参数 学习不同分支的权重 # 两个分支add操作 class BiFPN_Add2(nn.Module): def __init__(self, c1, c2): super(BiFPN_Add2, self).__init__() # 设置可学习参数 nn.Parameter的作用是:将一个不可训练的类型Tensor转换成可以训练的类型parameter # 并且会向宿主模型注册该参数 成为其一部分 即model.parameters()会包含这个parameter # 从而在参数优化的时候可以自动一起优化 self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True) self.epsilon = 0.0001 self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0) self.silu = nn.SiLU() def forward(self, x): w = self.w weight = w / (torch.sum(w, dim=0) + self.epsilon) return self.conv(self.silu(weight[0] * x[0] + weight[1] * x[1])) # 三个分支add操作 class BiFPN_Add3(nn.Module): def __init__(self, c1, c2): super(BiFPN_Add3, self).__init__() self.w = nn.Parameter(torch.ones(3, dtype=torch.float32), requires_grad=True) self.epsilon = 0.0001 self.conv = nn.Conv2d(c1, c2, kernel_size=1, stride=1, padding=0) self.silu = nn.SiLU() def forward(self, x): w = self.w weight = w / (torch.sum(w, dim=0) + self.epsilon) # 将权重进行归一化 # Fast normalized fusion return self.conv(self.silu(weight[0] * x[0] + weight[1] * x[1] + weight[2] * x[2])) 修改yolo.py 在parse_model函数中找到elif m is Concat:语句,在其后面加上BiFPN_Add相关语句: elif m is Concat: c2 = sum(ch[x] for x in f) # 添加bifpn_add结构 elif m in [BiFPN_Add2, BiFPN_Add3]: c2 = max([ch[x] for x in f]) 修改train.py 1. 向优化器中添加BiFPN的权重参数 将BiFPN_Add2和BiFPN_Add3函数中定义的w参数,加入g1 g0, g1, g2 = [], [], [] # optimizer parameter groups for v in model.modules(): # hasattr: 测试指定的对象是否具有给定的属性,返回一个布尔值 if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter): # bias g2.append(v.bias) # biases if isinstance(v, nn.BatchNorm2d): # weight (no decay) g0.append(v.weight) elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter): # weight (with decay) g1.append(v.weight) # BiFPN_Concat elif isinstance(v, BiFPN_Add2) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter): g1.append(v.w) elif isinstance(v, BiFPN_Add3) and hasattr(v, 'w') and isinstance(v.w, nn.Parameter): g1.append(v.w) 2. 查看BiFPN_Add层参数更新情况 想要查看BiFPN_Add层的参数更新情况,可以参考这篇博客【Pytorch】查看模型某一层的参数数值(自用),直接定位到w参数,随着模型训练输出对应的值。 References YOLOv5结合BiFPN 【论文笔记】EfficientDet(BiFPN)(2020) nn.Module、nn.Sequential和torch.nn.parameter学习笔记 ———————————————— 版权声明:本文为CSDN博主「嗜睡的篠龙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_43799388/article/details/124091648
-
一:前言 本次人脸识别技术使用到的是级联分类器 对于级联分类器,如果想要自己训练模型可以参考这篇文章 【OpenCV】 级联分类器训练模型 【友情提示:训练对电脑的配置要求比较高,另外还需要有足够庞大的样本数据,因此,如果是研究生在导师实验室用着3090的,可以自己训练,但也要投入足够的时间进行数据采集。不过,这边为了方便大家学习,博主会在资源中分享 人脸识别训练模型 车辆识别训练模型 ,在读完这篇文章后,感兴趣的,想要学习的,欢迎自取】 二:人脸识别案例 实现步骤及完整代码 步骤1 灰度化处理 //灰度化处理 节省内存 Mat gray; cvtColor(frame,gray,CV_RGB2GRAY); 步骤2 将灰度图再次进行 行列压缩 //级联分类器比帧差法还更慢,因此,需要再将灰度图大小压缩一半左右 行列压缩 Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1); //按存储大小计算 压缩方式采用线性压缩 resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR); 步骤3 直方图均值化 //直方图均值化 让灰度图经过直方图函数处理 黑白分明 equalizeHist(smalling,smalling); //imshow("smalling",smalling); 步骤4 使用模型 对每一个像素点遍历 图像甄别 //调用级联分类器进行模型匹配并进行框选识别 使用模型去进行每一个像素点的遍历 vectorfaces; //使用CV_HAAR_SCALE_IMAGE算法 图像甄别 cascade.detectMultiScale(smalling,faces,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30)); //绘制矩形 vector::const_iterator iter; //使用到容器迭代器进行遍历 for(iter=faces.begin();iter!=faces.end();iter++) { rectangle(frame, cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),//左上 cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),//右下 Scalar(0,255,0),2,8//颜色 像素位 ); } imshow("frame",frame); 人脸识别案例 完整代码如下: #include #include using namespace cv; using namespace std; //人脸识别 void datectFace(Mat &frame,CascadeClassifier cascade,double scale) { //灰度化处理 节省内存 Mat gray; cvtColor(frame,gray,CV_RGB2GRAY); //级联分类器比帧差法还更慢,因此,需要再将灰度图大小压缩一半左右 行列压缩 Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1); //按存储大小计算 压缩方式采用线性压缩 resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR); //直方图均值化 让灰度图经过直方图函数处理 黑白分明 equalizeHist(smalling,smalling); //imshow("smalling",smalling); //调用级联分类器进行模型匹配并进行框选识别 使用模型去进行每一个像素点的遍历 vectorfaces; //使用CV_HAAR_SCALE_IMAGE算法 图像甄别 cascade.detectMultiScale(smalling,faces,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30)); //绘制矩形 vector::const_iterator iter; //使用到容器迭代器进行遍历 for(iter=faces.begin();iter!=faces.end();iter++) { rectangle(frame, cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),//左上 cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),//右下 Scalar(0,255,0),2,8//颜色 像素位 ); } imshow("frame",frame); } int main(int argc, char *argv[]) { //级联分类器对象 CascadeClassifier cascade; //读取级联分类器 cascade.load("D:/00000cars-face/face.xml"); Mat frame; //视频路径的获取 VideoCapture cap(0); while (cap.read(frame)) { //将读到的帧进行显示 imshow("frame",frame); //检测识别 图像 级联分类器 比例 datectFace(frame,cascade,2); waitKey(3); } return 0; } 结果测试:可对人脸框选识别 三:车辆识别案例 级联分类器 具体实现 如果对于上述的人脸识别案例 理解透彻 那么车辆识别也是一样的实现方法 只不过就是换了一个级联分类器 图像数据读取 罢了。 这边就直接给出 车辆识别案例 完整代码 #include #include using namespace cv; using namespace std; //车辆识别案例 void datectCarDaw(Mat &frame,CascadeClassifier cascade,double scale) { //灰度化处理 节省内存 Mat gray; cvtColor(frame,gray,CV_RGB2GRAY); //级联分类器比帧差法还更慢,因此,需要再将灰度图大小压缩一半左右 行列压缩 Mat smalling(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1); //按存储大小计算 压缩方式采用线性压缩 resize(gray,smalling,smalling.size(),0,0,INTER_LINEAR); //直方图均值化 让灰度图经过直方图函数处理 黑白分明 equalizeHist(smalling,smalling); //imshow("smalling",smalling); //调用级联分类器进行模型匹配并进行框选识别 使用模型去进行每一个像素点的遍历 vectorcars; //使用CV_HAAR_SCALE_IMAGE算法 图像甄别 cascade.detectMultiScale(smalling,cars,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30)); //绘制矩形 vector::const_iterator iter; //使用到容器迭代器进行遍历 for(iter=cars.begin();iter!=cars.end();iter++) { rectangle(frame, cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),//左上 cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),//右下 Scalar(0,255,0),2,8//颜色 像素位 ); } imshow("frame",frame); } int main(int argc, char *argv[]) { //级联分类器对象 CascadeClassifier cascade; //读取级联分类器 cascade.load("D:/00000cars-face/cars.xml"); Mat frame; //视频路径的获取 VideoCapture cap("D:/00000000000003jieduanshipincailliao/carMove.mp4"); while (cap.read(frame)) { //将读到的帧进行显示 imshow("frame",frame); //检测识别 图像 级联分类器 比例 datectCarDaw(frame,cascade,2); waitKey(3); } return 0; } 结果测试: 可以看出,图中汽车可以被识别框选,电动车不会被识别框选。 相比博主在上周分享的 帧差法 车辆识别 来看,本次车辆识别的准确度明显提高,因此,这种方法非常值得学习! 想了解 帧差法 车辆识别 可以阅读下面这篇文章 车辆识别 帧差法 具体步骤 手把手教学 以上,就是博主的全部内容啦!欢迎一起交流学习! ———————————————— 版权声明:本文为CSDN博主「我今年十六岁」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_56051805/article/details/126188407
-
功能描述该接口用于向VCM子系统的指定人脸库中添加人脸照片。该照片中应包含指定人员的人脸图片。注意事项同一人员在人脸库的人脸图片不能超过五张。一个人脸库最大支持100万人脸。基本信息接口名称添加人脸图片v1.1接口路径/io-adapter/south/face-recognition/v1.1/face-picture/addHTTP方法POST请求参数请求参数如表1所示。表1 请求消息参数说明参数名称类型必选(M)/可选(O)位置参数含义X-HW-IDStringMHeaderROMA授权的应用ID。X-HW-APPKEYStringOHeaderROMA授权的应用的密钥。说明:如果调用侧在ROMA平台设置了白名单,该参数可不填,如果没有,该参数必填。channelStringMHeader选择的子系统渠道。由上层应用携带,供ROMA平台做路由选择。厂家无需实现。repositoryCodeStringMBody人脸库标识。“增加人脸库”接口返回的人脸库标识。imageUrlStringOBody人脸图片的URL路径与imageBase64二者选一。长度范围:不超过255个字符。imageBase64StringOBody人脸图片内容的BASE64编码的字符串。与imageUrl二者选一。Base64编码后大小不超过5M。图片为JPG/JPEG/BMP/PNG格式。imageIdStringOBody人脸图片的唯一ID,与当前图像绑定。若用户没提供,系统会生成一个。该ID长度范围为1~36位,可以包含字母、数字、中划线或者下划线,不包含其他的特殊字符。VCM不支持personIdStringMBody人脸图片所包含的人员实例的ID。一张人脸照片建议仅包含一个人员的正脸。如果照片中有多个人脸,则表示此人员实例的脸是照片中尺寸最大的那个脸。长度范围:0-9A-Fa-f,长度24个字符extensionobjectOBody扩展对象。用于携带厂家定义的特有字段。表2 extension参数说明参数名称类型必选(M)/可选(O)位置参数含义nameStringOBody人员姓名,支持中文、英文、数字和空格,首位不为特殊字符,长度[1,255]。genderStringOBody人员性别:0:男1:女-1:未知credentialTypeStringOBody证件类型:0:身份证1:护照2:学生证3:军官证4:驾照5:其他credentialNumberStringOBody证件号,支持英文、数字、(),长度[1,255]。请求样例{ "repositoryCode": "5d11c13a066b980884971778", "imageBase64": "/9j/4...", "personId": "5cf8b755066b980884970701" }响应参数响应参数如表3所示。表3 响应消息参数说明参数名称类型参数含义resultoutputs接口响应参数。成功时返回下方结果,失败或者数据为空时,不返回result内部的内容结构,仅返回null。resCodeString返回码。具体请参考“人员匹配错误码”。resMsgString返回消息。originalResInfooriginalResInfo原始响应消息。表4 outputs参数说明参数名称类型参数含义facesFaceStructure[]照片中包含的人脸结构。如果照片中包含多张人脸,则返回一个列表。表5 FaceStructure参数说明参数名称类型参数含义personIdString人脸图片所包含的人员实例的ID固定为24个字符。faceIdString人脸ID。支持1~36个字符。表6 originalResInfo参数说明参数名称类型参数含义originalResCodeString原始返回码。originalResMsgString原始返回消息。响应样例{ "result": { "faces": [ { "faceId": "5d1325f6066b980c1aa4ee6b", "personId": "5cf8b755066b980854970701" } ] }, "resCode": "0", "resMsg": "Success.", "originalResInfo": { "originalResCode": "0", "originalResMsg": "success" } }
-
在云速建站后台管理的第三方帐号登录中,能不能增加人脸识别登录配置?以解决会员实名认证的问题,
-
去年同醉,酴花下,健笔赋新词。今年君去,酴欲破,谁与醉为期。六月初夏,又是一年毕业季,喜悦总是与感伤并存:我们为迈入新的人生阶段而喜悦,也为与朝夕相处之人分别而不舍。一张毕业照,寄托着多少人的思念。对于很多人来说,有一张身穿学士服的毕业照,学生生活才算完整,它给三年、四年甚至更长时间的高校生活画上了一个圆满的句号。有创意的毕业照也常常能吸引不少人的注意,几乎每年,创意毕业照都能登上全网热搜。可是今年的毕业季却成了许多人的遗憾,出于疫情防控的需要,很多学校取消了毕业典礼,甚至封闭校园,不允许学生返校。对于毕业生来说,寒假时轻轻松松的"下个月见",却可能成为对彼此说的最后一句话。很多人说,他们可能失去了唯一一次穿学士服、拍毕业照的机会。为了弥补这样的遗憾,不少人"大显神通":有的人用PS给自己"抠"了一张毕业照,有的同学精通绘画,给自己和朋友们画了一张Q版毕业照,还有的同学把前两者结合,将自己的脸拼到了画中。但总觉得差了点什么,可能大家更想要一张"真实"的毕业照吧...于是今天,我将为大家分享,如何用AI简单而优雅地实现“云毕业照的拍摄”。首先看看效果:上传一个人的证件照,就可以很轻松地实现身穿学士服的毕业照的合成。当然,青春云毕业怎会只有一个人,当上传多张照片时,就可以实现一个多人合照的生成。如果说觉得背景有一些单调,那也没有关系!系统同样支持更换背景~大家可以在下面的网站中尝试生成自己的毕业照~http://119.3.249.156:32123/那么有小伙伴肯定会问了~这样一个毕业照生成器的工作原理是什么样的呢?接下来就跟大家分享我的设计思路和核心代码~1.图片上传在这个网站中,前端通过一个form标签,获取包括照片、性别、学科、学位等信息,传递到后端以进行毕业照的生成。2.特征提取当获得了上传的照片,就可以提取其人脸特征,以实现进一步的换脸操作。这在里我们使用的是python中的dlib库,它拥有一个人脸特征提取器,可以通过人脸上的68个特征点实现对人脸特征的采集。完成人脸特征点的提取后,我们就可以根据特征点构建一个人脸掩模,黑色的部分代表需要换脸的部分,白色的部分代表脸部以外(即不需要换脸的部分)。3.换脸整个换脸的操作是非常简单的,只要定位到学士服模板中人脸的位置,然后根据掩模做一个柏松融合,就能得到换脸之后的学士服照片了。大家可能会问,什么是柏松融合?其实很简单,举个栗子~如上图所示,这里的掩模是一个左半部分白色,右半部分黑色的矩形,这代表着将用图中橘子的右半部分替换苹果的右半部分,得到的便是如图右下角所示的一个“半苹果半橘子”的图片,其过渡部分非常自然,几乎看不出有拼接的痕迹。而实现这样合成效果的方法就是柏松融合。而我们将其用到了人脸上。4.多人合成单人的毕业照显得不那么完整,我们可以通过创建一个大的画布,将通过上述步骤生成的单人毕业照依次拼接在上面,从而实现多人毕业照的合成。5.添加背景背景添加方法和换脸类似,只是这次抠的是整个人的身体,再将纯蓝的背景更换为喜欢的照片,就能实现毕业照背景的添加。至此,整个AI毕业照的生成就彻底完成啦~当然,还有一些细节问题需要注意。比如,在进行多人毕业照合成时,涉及到一个对上传照片中每一张人脸位置的检测,目前主流的方法往往是使用神经网络进行检测,编写难度、硬件要求都比较高,因此我选择在ModelArts上直接实现,它提供了一个自动学习功能,可以一键训练人脸检测模型并在线部署。打开华为云ModelArts控制台,直接点击【自动学习】,就进入了配置界面:在选定数据集输出和输出位置之后,点击创建项目,进入到【数据标注】界面中。在数据标注界面,可以很方便地上传数据、并对通过鼠标框选的方式进行人脸数据的标注,完成数据集的标注后,点击【开始训练】按钮。对训练验证集比例、计算规格等进行配置,最后点击确定,自动人脸检测模型就会开始训练,整个训练过程会持续大约10分钟。 当训练完成时,会自动部署在线预测服务,通过调用在线服务,就可以实现人脸检测功能。 当然,在线服务不仅仅只能在网页中调用,可以通过在python代码中调用在线服务,从而实现与程序的完美结合,从而减少本地机器的负担、提高识别准确率。到这里,整个项目的分享就结束啦,欢迎大家下载附件中的源代码~一起体验“云毕业”叭!(注意哟~源代码中的在线服务service_id需要自己按照上面步骤部署~)【转载】华为云社区 author: Srius 发表于 2020/06/12 16:44:47
-
【功能模块】Atlas 200 DK外接树莓派摄像头采集人脸后,进行人脸识别的案例,请问哪里有,在gitee/samples里没有找到,麻烦您可以发下具体的链接么,不胜感激【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
如何将multi_channels_rtsp中的人脸识别换成自己的识别物(cup,pencil,telephone等)?具体是在哪里改
-
# MindX SDK -- 人脸检测参考设计案例 ## 1 案例概述 ### 1.1 概要描述 在本系统中,目的是基于MindX SDK,在华为云昇腾平台上,开发端到端**实时人脸检测**的参考设计,实现**对三路视频中的人脸以及人脸关键点进行检测**的功能,达到性能要求 ### 1.2 模型介绍 本项目用到了一个模型: 用于人脸检测的Yunet模型 Yunet模型相关文件可以在此处下载:https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/yunet/yunet.onnx ### 1.3 实现流程 1、基础环境: MindX SDK : 版本2.0.4 获取方式:https://www.hiascend.com/software/Mindx-sdk ubuntu : 版本:18.04 获取方式:请上ubuntu官网获取 Ascend-CANN-toolkit: 版本:5.0.4 获取方式:https://www.hiascend.com/software/cann/commercial 2、模型转换:利用atc工具完成模型转化:yunet.onxx --> yunet.om 3、业务流程编排与配置 4、开发检测后处理插件YunetPostProcess 5、python主程序代码开发 技术流程图如下: ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/13/1657676880325366760.png) ### 1.4 代码地址 > 本项目的代码地址为:https://gitee.com/ascend/mindxsdk-referenceapps/tree/master/contrib/yunet ## 2 软件方案介绍 ### 2.1 项目方案架构介绍 表2.1 系统方案中各模块功能: | 序号 | 子系统 | 功能描述 | | ---- | -------------- | ------------------------------------------------------------ | | 1 | 视频输入流 | 接收外部调用接口的输入视频路径,对视频进行拉流,并将拉去的裸流存储到缓冲区(buffer)中,并发送到下游插件。 | | 2 | 视频解码 | 用于视频解码,当前只支持H264格式。 | | 3 | 数据分发 | 对单个输入数据进行2次分发。 | | 4 | 数据缓存 | 输出时为后续处理过程创建一个线程,用于将输入数据与输出数据解耦,并创建缓存队列,存储尚未输入到下流插件的数据。 | | 5 | 图像处理 | 对解码后的YUV格式的图像进行放缩。 | | 6 | 模型推理插件 | 目标检测。 | | 7 | 模型后处理插件 | 对模型输出的张量进行后处理,得到物体类型数据。 | | 8 | 目标框转绘插件 | 物体类型转化为OSD实例 | | 9 | OSD可视化插件 | 实现对视频流的每一帧图像进行绘制。 | | 10 | 视频编码插件 | 用于将OSD可视化插件输出的图片进行视频编码,输出视频。 | ### 2.2 代码目录结构与说明 本工程名称为Yunet实时人脸检测,工程目录如下图所示: ```` ├── build.sh ├── config │ ├── face_yunet.cfg # yunet配置文件 │ └── Yunet.aippconfig # 模型转换aipp配置文件 ├── kpmain.py # 关键点信息输出代码 ├── main.py # 单路视频输出代码 ├── test.py # 三路后处理性能测试代码 ├── models │ └── Yunet.onnx ├── pipeline │ ├── InferTest.pipeline # 三路后处理性能测试pipeline │ ├── PluginTest.pipeline # 原方案插件性能测试pipeline │ ├── KPYunet.pipeline # 关键点信息输出pipeline │ └── Yunet.pipeline # 单路视频输出pipeline ├── plugin │ ├── build.sh │ ├── CMakeLists.txt │ ├── YunetPostProcess.cpp # 人脸检测框后处理代码 │ └── YunetPostProcess.h ├── plugin2 │ ├── build.sh │ ├── CMakeLists.txt │ ├── KPYunetPostProcess.cpp # 人脸关键点后处理代码 │ ├── KPYunetPostProcess.h ├── plugin3 │ ├── build.sh │ ├── CMakeLists.txt │ ├── TotalYunetPostProcess.cpp # 人脸检测框与关键点后处理代码(以供可视化) │ └── TotalYunetPostProcess.h ├── README.md └── run.sh ```` ## 3.1 环境依赖说明 环境依赖软件和版本如下表: | 软件名称 | 版本 | | :-----------: | :---------: | | ubantu | 18.04.1 LTS | | MindX SDK | 2.0.4 | | Python | 3.9.2 | | CANN | 5.0.4 | | numpy | 1.22.3 | | opencv-python | 4.5.5 | | 软件名称 | 版本 | 说明 | 使用教程 | | -------- | ----- | ------------------------------ | ------------------------------------------------------------ | | live555 | 1.09 | 实现视频转rstp进行推流 | [链接](https://gitee.com/ascend/mindxsdk-referenceapps/blob/master/docs/%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99/Live555%E7%A6%BB%E7%BA%BF%E8%A7%86%E9%A2%91%E8%BD%ACRTSP%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3.md) | | ffmpeg | 4.2.1 | 实现mp4格式视频转为264格式视频 | [链接](https://gitee.com/ascend/mindxsdk-referenceapps/blob/master/docs/%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99/pc%E7%AB%AFffmpeg%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B.md#https://ffmpeg.org/download.html) | 本项目适用于单人及多人正脸视频。对于人脸侧面视频,可以将人脸位置正确标出,但关键点信息标注准确率较低。本项目可以适用于仰卧人脸,但不适用于侧卧人脸。 特别地,在无人脸的情况下,我们在视频左上角设置了红色提示点。当左上角像素出现红色时,说明此场景没有检测出人脸。(下面给出该特殊点检测框的数据信息) ```` "MxpiObject":[{"classVec":[{"classId":3,"className":"","confidence":0,"headerVec":[]}],"x0":0,"x1":0,"y0":0,"y1":0}] ```` 另外,本项目要求输入视频为1920*1080 25fps视频,不支持25帧率以上视频。 ### 3.2 环境搭建 在编译运行项目前,需要设置环境变量: ```bash . /usr/local/Ascend/ascend-toolkit/set_env.sh . ${SDK安装路径}/mxVision/set_env.sh export install_path=/usr/local/Ascend/ascend-toolkit/latest export PATH=${install_path}/atc/ccec_compiler/bin:${install_path}/atc/bin:$PATH export PYTHONPATH=${install_path}/atc/python/site-packages:${install_path}/atc/python/site-packages/auto_tune.egg/auto_tune:${install_path}/atc/python/site-packages/schedule_search.egg export LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATH export ASCEND_OPP_PATH=${install_path}/opp ``` 注:其中SDK安装路径${MX_SDK_HOME}替换为用户的SDK安装路径。 ### 3.3 模型转换 本项目中使用的模型是yunet模型,onnx模型可以直接[下载](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/yunet/yunet.onnx)。下载后使用模型转换工具ATC将onnx模型转换为om模型,模型转换工具相关介绍参考[链接](https://support.huaweicloud.com/tg-cannApplicationDev330/atlasatc_16_0005.html) 模型转换步骤如下: 设置ATC env:当前目录下运行 ```` export install_path=/usr/local/Ascend/ascend-toolkit/latest export PATH=/usr/local/python3/bin:${install_path}/atc/ccec_compiler/bin:${install_path}/atc/bin:$PATH export PYTHONPATH=${install_path}/atc/python/site-packages:${install_path}/atc/python/site-packages/auto_tune.egg/auto_tune:${install_path}/atc/python/site-packages/schedule_search.egg:$PYTHONPATH export LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATH export ASCEND_OPP_PATH=${install_path}/opp ```` cd到models文件夹,运行 ```` atc --framework=5 --model=yunet.onnx --output=yunet --input_format=NCHW --input_shape="input:1,3,120,160" --log=debug --soc_version=Ascend310 --insert_op_conf=../config/Yunet.aippconfig ```` 执行该命令后会在指定输出.om模型路径生成项目指定模型文件`yunet.om`。若模型转换成功则输出: ``` ATC start working now, please wait for a moment. ATC run success, welcome to the next use. ``` aipp文件配置如下: ``` aipp_op { related_input_rank : 0 src_image_size_w : 160 src_image_size_h : 120 crop : false aipp_mode: static input_format : YUV420SP_U8 csc_switch : true rbuv_swap_switch : false matrix_r0c0 : 256 matrix_r0c1 : 454 matrix_r0c2 : 0 matrix_r1c0 : 256 matrix_r1c1 : -88 matrix_r1c2 : -183 matrix_r2c0 : 256 matrix_r2c1 : 0 matrix_r2c2 : 359 input_bias_0 : 0 input_bias_1 : 128 input_bias_2 : 128 mean_chn_0 : 0 mean_chn_1 : 0 mean_chn_2 : 0 min_chn_0 : 0.0 min_chn_1 : 0.0 min_chn_2 : 0.0 var_reci_chn_0 : 1.0 var_reci_chn_1 : 1.0 var_reci_chn_2 : 1.0 } ``` ## 4 实时人脸检测流程开发实现 ### 4.1 pipeline编排 ``` mxpi_rtspsrc mxpi_videodecoder # 视频解码 mxpi_imageresize # 图像缩放 tee # 解码视频分发 mxpi_tensorinfer # 模型推理 mxpi_objectpostprocessor # 模型后处理 mxpi_object2osdinstances # osd实例化 mxpi_channelimagesstitcher # 图片合并 mxpi_channelosdcoordsconverter mxpi_opencvosd mxpi_videoencoder appsink ``` ### 4.2 实时人脸检测后处理库开发 参考分类识别模型后处理库开发。 > 参考链接:https://gitee.com/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/quick_start/4-2%E6%A8%A1%E5%9E%8B%E5%90%8E%E5%A4%84%E7%90%86%E5%BA%93(%E5%86%85%E7%BD%AE%E7%B1%BB%E5%9E%8B)%E5%BC%80%E5%8F%91%E8%B0%83%E8%AF%95%E6%8C%87%E5%AF%BC.md ### 4.3 主程序开发 1、初始化流管理。 2、加载视频,进行推理。 3、获取pipeline各插件输出结果,解析输出结果。 4、根据识别结果在视频中标出人脸信息 5、测试性能,获取输出视频帧率 6、销毁流 ## 5 编译与运行 `main.py`:用来生成端对端单路推理的可视化视频,以提供单路推理结果可视化的应用样例 `kpmain.py`:用来生成单路关键点后处理的数据结果(用来确保关键点类型后处理的实现成功,关键点效果看main.py的可视化结果) (`kpmain.py`在此项目中不是必须的,当前没有keypoint类型osd支持下,仅给出单路pipeline输出数据信息供参考) `test.py`:用来输出端对端三路推理的后处理结果,以检测三路推理性能是否达标 需要注意的是,本项目后处理插件支持三路视频推理的后处理,但由于mxVision-2.0.4暂不支持三路后处理输出,所以此处采取单路视频可视化和三路推理性能检测两部分,分别提供可视化应用与性能检测的功能。 1.编译后处理插件 `cd`到`plugin`目录,`mkdir`新建文件夹`build` `cd`到`build`,运行 ```` cmake .. make -j make install ```` 如果权限问题,`cd`到MindSDK安装路径的`lib/modelpostprocessors`目录,将`libyunetpostprocess.so`的权限更改为`640`。 对于`plugin2`、`plugin3`目录也同样处理。 2.`config/face_yunet.cfg` 确认权限`640`。 3.运行`main.py`程序 `cd`到根目录,运行 ```` bash run.sh ```` 最后会得到`result.264`即为输出结果 ## 6 常见问题 ### 权限问题 **问题描述:** ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/13/1657676735740192074.png) **解决方案:** cd到run包的./lib/modelpostprocessors目录,运行 ```` chmod 640 libyunetpostprocess.so ```` 对于plugin2、plugin3目录也同样处理。 ### 负荷问题 若视频解码器负荷过高则会出现以下问题: ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/13/1657676706976134205.png) ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/13/1657676761121560284.png) 导致此问题的可能原因为:视频帧率过高、视频尺寸过大或解码器正在同时解码过多其他视频 解决方案:确保三路视频都为1920*1080 25fps并且减少其它任务的运行 ## 7 参考链接 > Yunet模型,参考链接:https://github.com/ShiqiYu/libfacedetection
-
# 基于STM32单片机设计的红外测温仪(带人脸检测) 由于医学发展的需要,在很多情况下,一般的温度计己经满足不了快速而又准确的测温要求,例如:车站、地铁、机场等人口密度较大的地方进行人体温度测量。 当前设计的这款红外测温仪由测温硬件+上位机软件组合而成,主要用在地铁、车站入口等地方,可以准确识别人脸进行测温,如果有人温度超标会进行语音提示并且保存当前人脸照片。 ## 1、 硬件选型与设计思路 ### (1). 设备端 主控单片机采用STM32F103C8T6,人体测温功能采用非接触式红外测温模块。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/6/1657088443080712875.png) ### (2). 上位机设计思路 上位机采用Qt5设计,Qt5是一套基于C++语言的跨平台软件库,性能非常强大,目前桌面端很多主流的软件都是采用QT开发。比如: 金山办公旗下的-WPS,字节跳动旗下的-剪映,暴雪娱乐公司旗下-多款游戏登录器等等。Qt在车联网领域用的也非常多,比如,哈佛,特斯拉,比亚迪等等很多车的中控屏整个系统都是采用Qt设计。 在测温项目里,上位机与STM32之间采用串口协议进行通信,上位机可以打开笔记本电脑默认的摄像头,进行人脸检测;当检测到人脸时,控制STM32测量当前人体的实时温度实时,再将温度传递到上位机显示;当温度正常时,上位机上显示绿色的提示字样“温度正常”,并有语音播报,语音播报的声音使用笔记本自带的声卡发出。如果温度过高,上位机显示红色提示字样“温度异常,请重新测量”,并有语音播报提示。温度过高时,会自动将当前人脸拍照留存,照片存放在当前软件目录下的“face”目录里,文件的命名规则是“38.8_2022-01-05-22-12-34.jpg”,其中38.8表示温度值,后面是日期(年月日时分秒)。 ### (3) 上位机运行效果 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/6/1657088458540921923.png) ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/6/1657088467253561881.png) 上位机需要连接STM32设备之后才可以获取温度数据,点击软件上的打开摄像头按钮,开启摄像头,让检测到人脸时,下面会显示当前测量的温度。如果没有连接STM32设备,那么默认会显示一个正常的固定温度值。界面上右边红色的字,表示当前处理一帧图像的耗时时间,电脑性能越好,检测速度越快。 ### (4) 拿到可执行文件之后如何运行? 先解压压缩包,进入“测温仪上位机-可执行文件”目录,将“haarcascade_frontalface_alt2.xml”拷贝到C盘根目录。 ![img](https://bbs-img.huaweicloud.com/blogs/img/16e6b1.png) ![img](https://bbs-img.huaweicloud.com/blogs/img/17a28c.png) 然后双击“FaceTemperatureCheck.exe”运行程序。 ![img](https://bbs-img.huaweicloud.com/blogs/img/18c1fe.png) 未连接设备,也可以打开摄像头检测人脸,只不过温度值是一个固定的正常温度值范围。 ## 二、上位机设计 ## 2.1 安装编译环境 如果需要自己编译运行源代码,需要先安装Qt5开发环境。 下载地址: https://download.qt.io/official_releases/qt/5.12/5.12.0/ ![img](https://bbs-img.huaweicloud.com/blogs/img/197e29.png) 下载之后,先将电脑网络断掉(不然会提示要求输入QT的账号),然后双击安装包进行安装。 安装可以只需要选择一个MinGW 32位编译器就够用了,详情看下面截图,选择“MinGW 7.3.0 32-bit”后,就点击下一步,然后等待安装完成即可。 ![img](https://bbs-img.huaweicloud.com/blogs/img/1b27e0.png) ## 2.2 软件代码整体效果 如果需要完整的工程,可以在这里去下载:https://download.csdn.net/download/xiaolong1126626497/85892490 ![img](https://bbs-img.huaweicloud.com/blogs/img/1d3671.png) 打开工程后(工程文件的后缀是xxx.pro),点击左下角的绿色三角形按钮就可以编译运行程序。 ![img](https://bbs-img.huaweicloud.com/blogs/img/206514.png) ## 2.3 UI设计界面 ![img](https://bbs-img.huaweicloud.com/blogs/img/239444.png) ## 2.4 人脸检测核心代码 ```cpp //人脸检测代码 bool ImageHandle::opencv_face(QImage qImage) { bool check_flag=0; QTime time; time.start(); static CvMemStorage* storage = nullptr; static CvHaarClassifierCascade* cascade = nullptr; //模型文件路径 QString face_model_file = QCoreApplication::applicationDirPath()+"/"+FACE_MODEL_FILE; //加载分类器:正面脸检测 cascade = (CvHaarClassifierCascade*)cvLoad(face_model_file.toUtf8().data(), 0, 0, 0 ); if(!cascade) { qDebug()"分类器加载错误.\n"; return check_flag; } //创建内存空间 storage = cvCreateMemStorage(0); //加载需要检测的图片 IplImage* img = QImageToIplImage(&qImage); if(img ==nullptr ) { qDebug()"图片加载错误.\n"; return check_flag; } double scale=1.2; //创建图像首地址,并分配存储空间 IplImage* gray = cvCreateImage(cvSize(img->width,img->height),8,1); //创建图像首地址,并分配存储空间 IplImage* small_img=cvCreateImage(cvSize(cvRound(img->width/scale),cvRound(img->height/scale)),8,1); cvCvtColor(img,gray, CV_BGR2GRAY); cvResize(gray, small_img, CV_INTER_LINEAR); cvEqualizeHist(small_img,small_img); //直方图均衡 /* * 指定相应的人脸特征检测分类器,就可以检测出图片中所有的人脸,并将检测到的人脸通过矩形的方式返回。 * 总共有8个参数,函数说明: 参数1:表示输入图像,尽量使用灰度图以加快检测速度。 参数2:表示Haar特征分类器,可以用cvLoad()函数来从磁盘中加载xml文件作为Haar特征分类器。 参数3:用来存储检测到的候选目标的内存缓存区域。 参数4:表示在前后两次相继的扫描中,搜索窗口的比例系数。默认为1.1即每次搜索窗口依次扩大10% 参数5:表示构成检测目标的相邻矩形的最小个数(默认为3个)。如果组成检测目标的小矩形的个数和小于 min_neighbors - 1 都会被排除。如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。 参数6:要么使用默认值,要么使用CV_HAAR_DO_CANNY_PRUNING,如果设置为CV_HAAR_DO_CANNY_PRUNING,那么函数将会使用Canny边缘检测来排除边缘过多或过少的区域,因此这些区域通常不会是人脸所在区域。 参数7:表示检测窗口的最小值,一般设置为默认即可。 参数8:表示检测窗口的最大值,一般设置为默认即可。 函数返回值:函数将返回CvSeq对象,该对象包含一系列CvRect表示检测到的人脸矩形。 */ CvSeq* objects = cvHaarDetectObjects(small_img, cascade, storage, 1.1, 3, 0/*CV_HAAR_DO_CANNY_PRUNING*/, cvSize(50,50)/*大小决定了检测时消耗的时间多少*/); qDebug()"人脸数量:"total; //遍历找到对象和周围画盒 QPainter painter(&qImage);//构建 QPainter 绘图对象 QPen pen; pen.setColor(Qt::blue); //画笔颜色 pen.setWidth(5); //画笔宽度 painter.setPen(pen); //设置画笔 CvRect *max=nullptr; for(int i=0;i(objects->total);++i) { //得到人脸的坐标位置和宽度高度信息 CvRect* r=(CvRect*)cvGetSeqElem(objects,i); if(max==nullptr)max=r; else { if(r->width > max->width || r->height > max->height) { max=r; } } } //如果找到最大的目标脸 if(max!=nullptr) { check_flag=true; //将人脸区域绘制矩形圈起来 painter.drawRect(max->x*scale,max->y*scale,max->width*scale,max->height*scale); } cvReleaseImage(&gray); //释放图片内存 cvReleaseImage(&small_img); //释放图片内存 cvReleaseHaarClassifierCascade(&cascade); //释放内存-->分类器 cvReleaseMemStorage(&objects->storage); //释放内存-->检测出图片中所有的人脸 //释放图片 cvReleaseImage(&img); qint32 time_ms=0; time_ms=time.elapsed(); //耗时时间 emit ss_log_text(QString("%1").arg(time_ms)); //保存结果 m_image=qImage.copy(); return check_flag; } ``` ## 2.5 配置文件(修改参数-很重要) ![img](https://bbs-img.huaweicloud.com/blogs/img/2fe364.png) 参数说明: 如果电脑上有多个摄像头,可以修改配置文件里的摄像头编号,具体的数量在程序启动时会自动查询,通过打印代码输出到终端。 如果自己第一次编译运行源码,运行之后, (1)需要将软件源码目录下的“haarcascade_frontalface_alt2.xml” 文件拷贝到C盘根目录,或者其他非中文目录下,具体路径可以在配置文件里修改,默认就是C盘根目录。 (2)需要将软件源码目录下的“OpenCV-MinGW-Build-OpenCV-3.4.7\x86\mingw\bin”目录里所有文件拷贝到,生成的程序执行文件同级目录下。 这样才能保证程序可以正常运行。 报警温度的阀值范围,也可以自行更改,在配置文件里有说明。 ## 2.6 语音提示文件与背景图 语音提示文件,背景图是通过资源文件加载的。 源文件存放在,源代码的“FaceTemperatureCheck\res”目录下。 ![img](https://bbs-img.huaweicloud.com/blogs/img/32b8d5.png) 自己也可以自行替换,重新编译程序即可生效。 ## 2.7 语音播报与图像显示处理代码 ```cpp //图像处理的结果 void Widget::slot_HandleImage(bool flag,QImage image) { bool temp_state=0; //检测到人脸 if(flag) { //判断温度是否正常 if(current_tempMIN_TEMP) { temp_state=true; //显示温度正常 ui->label_temp->setStyleSheet("color: rgb(0, 255, 127);font: 20pt \"Arial\";"); ui->label_temp->setText(QString("%1℃").arg(current_temp)); } else //语音播报,温度异常 { temp_state=false; //显示温度异常 ui->label_temp->setStyleSheet("color: rgb(255, 0, 0);font: 20pt \"Arial\";"); ui->label_temp->setText(QString("%1℃").arg(current_temp)); } //获取当前ms时间 long long currentTime = QDateTime::currentDateTime().toMSecsSinceEpoch(); //判断时间间隔是否到达 if(currentTime-old_currentTime>AUDIO_RATE_MS) { //更改当前时间 old_currentTime=currentTime; //温度正常 if(temp_state) { //语音播报,温度正常 QSound::play(":/res/ok.wav"); } //温度异常 else { //语音播报,温度异常 QSound::play(":/res/error.wav"); //拍照留存 QString dir_str = QCoreApplication::applicationDirPath()+"/face"; //检查目录是否存在,若不存在则新建 QDir dir; if (!dir.exists(dir_str)) { bool res = dir.mkpath(dir_str); qDebug() "新建目录状态:" res; } //目录存在就保存图片 QDir dir2; if (dir2.exists(dir_str)) { //拼接名称 QDateTime dateTime(QDateTime::currentDateTime()); //时间效果: 2020-03-05 16:25::04 周一 QString qStr=QString("%1/%2_").arg(dir_str).arg(current_temp); qStr+=dateTime.toString("yyyy-MM-dd-hh-mm-ss-ddd"); image.save(qStr+".jpg"); } } } } else //不显示温度 { ui->label_temp->setStyleSheet("color: rgb(0, 255, 127);font: 20pt \"Arial\";"); ui->label_temp->setText("----"); } //处理图像的结果画面 ui->widget_player->slotGetOneFrame(image); } ``` ## 2.8 STM32的温度接收处理代码 ```cpp //刷新串口端口 void Widget::on_pushButton_flush_uart_clicked() { QList UartInfoList=QSerialPortInfo::availablePorts(); //获取可用串口端口信息 ui->comboBox_ComSelect->clear(); if(UartInfoList.count()>0) { for(int i=0;i/如果当前串口 COM 口忙就返回真,否则返回假 { QString info=UartInfoList.at(i).portName(); info+="(占用)"; ui->comboBox_ComSelect->addItem(info); //添加新的条目选项 } else { ui->comboBox_ComSelect->addItem(UartInfoList.at(i).portName()); //添加新的条目选项 } } } else { ui->comboBox_ComSelect->addItem("无可用COM口"); //添加新的条目选项 } } //连接测温设备 void Widget::on_pushButton_OpenUart_clicked() { if(ui->pushButton_OpenUart->text()=="连接测温设备") //打开串口 { ui->pushButton_OpenUart->setText("断开连接"); /*配置串口的信息*/ UART_Config->setPortName(ui->comboBox_ComSelect->currentText()); //COM的名称 if(!(UART_Config->open(QIODevice::ReadWrite))) //打开的属性权限 { QMessageBox::warning(this, tr("状态提示"), tr("设备连接失败!\n请选择正确的COM口"), QMessageBox::Ok); ui->pushButton_OpenUart->setText("连接测温设备"); return; } } else //关闭串口 { ui->pushButton_OpenUart->setText("连接测温设备"); /*关闭串口-*/ UART_Config->clear(QSerialPort::AllDirections); UART_Config->close(); } } //读信号 void Widget::ReadUasrtData() { /*返回可读的字节数*/ if(UART_Config->bytesAvailable()=0) { return; } /*定义字节数组*/ QByteArray rx_data; /*读取串口缓冲区所有的数据*/ rx_data=UART_Config->readAll(); //转换温度 current_temp=rx_data.toDouble(); } ```
-
像华为云客服,华为云OBS等都以插件形式方便用户,可以不可以将华为云的人脸识别功能和文字识别功能做成插件,比如会员实名认证,刷脸登录都会用到。
-
在后台支付设置里能不能增加支付方式种类,除了扫码方式外,可不可以增加人脸支付和指纹支付?还有就是在第三方登录方式中除了QQ,微博,微信三种外可不可以增加人脸识别和指纹登录?
-
在后台支付设置里能不能增加支付方式种类,除了扫码方式外,可不可以增加人脸支付和指纹支付?还有就是在第三方登录方式中除了QQ,微博,微信三种外可不可以增加人脸识别和指纹登录?
上滑加载中
推荐直播
-
DTT年度收官盛典:华为开发者空间大咖汇,共探云端开发创新
2025/01/08 周三 16:30-18:00
Yawei 华为云开发工具和效率首席专家 Edwin 华为开发者空间产品总监
数字化转型进程持续加速,驱动着技术革新发展,华为开发者空间如何巧妙整合鸿蒙、昇腾、鲲鹏等核心资源,打破平台间的壁垒,实现跨平台协同?在科技迅猛发展的今天,开发者们如何迅速把握机遇,实现高效、创新的技术突破?DTT 年度收官盛典,将与大家共同探索华为开发者空间的创新奥秘。
回顾中 -
GaussDB应用实战:手把手带你写SQL
2025/01/09 周四 16:00-18:00
Steven 华为云学堂技术讲师
本期直播将围绕数据库中常用的数据类型、数据库对象、系统函数及操作符等内容展开介绍,帮助初学者掌握SQL入门级的基础语法。同时在线手把手教你写好SQL。
回顾中 -
算子工具性能优化新特性演示——MatMulLeakyRelu性能调优实操
2025/01/10 周五 15:30-17:30
MindStudio布道师
算子工具性能优化新特性演示——MatMulLeakyRelu性能调优实操
即将直播
热门标签