• [问题求助] FRS的动作活体检测怎么看失败视频的置信度?
    动作活体检测的置信度阈值是多少呢?只能看到通过检测的视频的置信度,但是看不到失败的视频的置信度,如何查看呢?
  • [问题求助] 动作活体检测,检测失败如何查看置信度
    FRS的动作活体检测,检测为活体的话,会针对每一个action输出对应的confidence,但如果检测失败,只会返回false。该怎么查看false时对应action的confidence呢?或者如何设定confidence的分界值,确定怎样才检测为活体呢?
  • [技术干货] 人脸识别知多少?【转】
    Part 01 脸识别技术概述人脸识别技术属于生物特征识别技术,是一种依据人人脸的若干特征(如眼睛、鼻子、嘴巴、眉毛等)自动进行身份识别的技术,又被称为面像识别、人像识别、相貌识别、面孔识别、面部识别等。其主要利用摄像机或摄像头采集含有人脸的图像或视频流,通过人脸检测技术分析其是否存在人脸,存在则给出人脸所在位置、大小和面部关键器官的位置信息;再根据这些信息提取所蕴涵的身份特征,并将其与已知的人脸特征进行对比,从而识别每个人脸的身份。 Part 02 人脸检测人脸检测是人脸识别和人脸分析系统的关键第一步,主要用于解决“人脸在哪里”的问题,在图像中准确标定出人脸的位置和大小,并提供给后续的人脸特征分析和识别。早期的人脸检测工作主要基于人工精心设计的局部描述子进行特征提取,主要可分为4类基于知识的人脸检测方法、基于模型的人脸检测方法、基于特征的人脸检测方法、基于外观的人脸检测方法,其中比较比较经典的作品有haar cascades分类器、HOG(Histogram of Oriented Gridients)特征检测算法。但传统的检测对于多种变异因素的具有挑战性的图像,人脸检测精度有限。 随着深度学习的蓬勃发展,逐渐演变出许多基于不同深度学习架构的人脸检测方法,主要有基于级联cnn模型、基于R-CNN模型、单发探测器模型、基于特征金字塔网络模型、基于变形金刚模型等,极大的改善了传统人脸检测、识别在特征提取、精确度、可扩展性方面均有诸多不足。MTCNN是其中一个比较优秀的人脸检测模型,该模型通过三个阶段的深度卷积网络,以粗到细的方式预测人脸和地标位置,具体步骤为:第一阶段:通过浅层CNN快速生成候选窗口;第二阶段:通过更复杂的CNN拒绝大量非面部窗口来细化窗口;第三阶段:使用更强大的CNN再次细化结果并输出五个面部标志位置。Part 03 人脸特征人脸特征也称人脸表征,是人脸的某些特征,比如脸的长度、脸的宽度、唇部宽度、鼻子长度等,人脸特征提取就是对人脸进行特征建模得到向量化人脸特征的过程。人脸特征提取按照技术特点大致可分为三类:基于全局信息的Holistic方法、基于局部信息的Local特征方法、基于深度学习的方法。基于深度学习的人脸特征可以从数据集中自动学习特征,如果数据集能够覆盖足够的鲁棒性数据(如光照、姿态、表情等),则算法能适应各种挑战,也是当前的主流人脸特征提取方法。 Part 04 人脸识别人脸识别是人脸比对的过程,通过比对提取的人脸特征获两个人脸的相似度,判断方法为比较两个特征间的欧式距离(L2距离)或者余弦距离(cosine距离):L2距离越小,相似度越高;cos距离夹角越小,cos距离越大相似度越高。根据比对的数量不同,又可分为1:1和1:N。1:1最常见的场景就是人证比对,比如我们在乘高铁时所遇到的这种设备。1:N是1张人脸和底库中的N张人脸进行比对,比如在考勤机中,我们的人脸底库中包含全公司的所有人脸照片。转载自:cid:link_0
  • [技术干货] 实时“人脸”模糊!实战教程【转】
    大家好,今天跟大家分享一个实战的教程。老规矩,先看效果(明确一下目标): 随着人脸识别技术的发展,给我们的日常生活带来了许多的便利,但是同样的也存在隐私的问题。以及可能被不法分子用于做一些违法事情。所以很多视频博主,都会给路人打码。但是手动打码是一件非常繁琐的事情,对于单帧图片还算简单,但是假设视频的帧率是 25FPS,即一秒中有25帧图片,那么一个几分钟的视频,其工作量也非常的可怕。因此我们尝试使用程序自动去执行这样子的操作!我们可以使用Opencv、Mediapipe和Python,实现实时模糊人脸。我们可以分两步完成:在打码之前,首先确定人脸位置取出脸,模糊它,然后将处理后的人脸放回到视频帧中(视频处理类似)1、在打码之前,首先确定人脸位置首先配置一下环境,安装必要的库(OpenCV 和 MediaPipe)pip install opencv-python pip install mediapipe在 MediaPipe 库中提供了人脸关键点检测的模块。详细的内容可以参考:cid:link_0当然在该项目的代码中,也提供人脸关键点检测的代码。“facial_landmarks.py”的文件: 下面我们就一起来写一下这部分的代码:首先导入必要的库以及用于人脸关键点检测的模块:import cv2 import mediapipe as mp import numpy as np from facial_landmarks import FaceLandmarks # Load face landmarks fl = FaceLandmarks()然后使用检测出来的人脸关键点最外围的一圈关键点绘制一个多边形(脸部轮廓)。这里使用opencv 中的convxhull() 函数可以实现:# 1. Face landmarks detection landmarks = fl.get_facial_landmarks(frame) convexhull = cv2.convexHull(landmarks)绘制完成后的结果如下所示: 之后使用上面所提取到的人脸关键点坐标创建mask,用提取我们在视频帧中感兴趣的区域: # 2. Face blurrying mask = np.zeros((height, width), np.uint8) # cv2.polylines(mask, [convexhull], True, 255, 3) cv2.fillConvexPoly(mask, convexhull, 255)结果如下所示: 得到这个mask,我们就可以进一步对人脸进行模糊(打码)处理。 打码的操作,这里使用的是OpenCV 中的cv2.blur() 函数:# Extract the face frame_copy = cv2.blur(frame_copy, (27, 27)) face_extracted = cv2.bitwise_and(frame_copy, frame_copy, mask=mask)结果: 目前,我们已经实现对人脸进行打码操作,剩下的就是对人脸以外的区域进行提取,并合并成最终的结果即可! 对人脸以外的区域进行提取(背景),实际上对上面的mask 进行取反即可。 背景提取:# Extract background background_mask = cv2.bitwise_not(mask) background = cv2.bitwise_and(frame, frame, mask=background_mask)从图像的细节可以看出,背景是完全可见的,但面部区域已经变成黑色了。这是我们将在下一步中应用模糊人脸的空白区域。 最后一步,将上面两步获取的人脸mask 和背景进行相加即可,这里使用cv2.add() 即可实现我们的目标: # Final result result = cv2.add(background, face_extracted)结果: 这是对一帧图片进行处理。2、取出脸,模糊它,然后将处理后的人脸放回到视频帧中上面的操作都是在单帧图片上进行处理的,如果我们需要出来的是视频的话,其实原理是完全一样的,只不过是将一个视频拆成一系列的图片即可。稍微做一些修改:(1)输入文件 (图片 ---> 视频)cap = cv2.VideoCapture("person_walking.mp4")(2)对输入的视频帧,做一个循环遍历:while True: ret, frame = cap.read() frame = cv2.resize(frame, None, fx=0.5, fy=0.5) frame_copy = frame.copy() height, width, _ = frame.shape ...转载自:cid:link_1
  • [其他] 浅谈人脸识别技术原理
     人脸检测面貌检测是指在动态的场景与复杂的背景中判断是否存在面像,并分离出这种面像。 1参考模板法 首先设计一个或数个标准人脸的模板,然后计算测试采集的样品与标准模板之间的匹配程度,并通过阈值来判断是否存在人脸。 2人脸规则法 由于人脸具有一定的结构分布特征,所谓人脸规则的方法即提取这些特征生成相应的规则以判断测试样品是否包含人脸。 3样品学习法 这种方法即采用模式识别中人工神经网络的方法,即通过对面像样品集和非面像样品集的学习产生分类器。 4肤色模型法 这种方法是依据面貌肤色在色彩空间中分布相对集中的规律来进行检测。 5特征子脸法 这种方法是将所有面像集合视为一个面像子空间,并基于检测样品与其在子空间的投影之间的距离判断是否存在面像。  人脸跟踪 面貌跟踪是指对被检测到的面貌进行动态目标跟踪。具体采用基于模型的方法或基于运动与模型相结合的方法。此外,利用肤色模型跟踪也不失为一种简单而有效的手段。  人脸比对 面貌比对是对被检测到的面貌像进行身份确认或在面像库中进行目标搜索。这实际上就是说,将采样到的面像与库存的面像依次进行比对,并找出最佳的匹配对象。所以,面像的描述决定了面像识别的具体方法与性能。 1、特征向量法 该方法是先确定眼虹膜、鼻翼、嘴角等面像五官轮廓的大小、位置、距离等属性,然后再计算出它们的几何特征量,而这些特征量形成一描述该面像的特征向量。 2、面纹模板法 该方法是在库中存贮若干标准面像模板或面像器官模板,在进行比对时,将采样面像所有像素与库中所有模板采用归一化相关量度量进行匹配。此外,还有采用模式识别的自相关网络或特征与模板相结合的方法。  识别过程 (1)首先建立人脸的面像档案。即用摄像机采集单位人员的人脸的面像文件或取他们的照片形成面像文件,并将这些面像文件生成面纹(Faceprint)编码贮存起来。 (2)获取当前的人体面像。即用摄像机捕捉的当前出入人员的面像,或取照片输入,并将当前的面像文件生成面纹编码。 (3)用当前的面纹编码与档案库存的比对。即将当前的面像的面纹编码与档案库存中的面纹编码进行检索比对。上述的“面纹编码”方式是根据人脸脸部的本质特征和开头来工作的。这种面纹编码可以抵抗光线、皮肤色调、面部毛发、发型、眼镜、表情和姿态的变化,具有强大的可靠性,从而使它可以从百万人中精确地辨认出某个人。人脸的识别过程,利用普通的图像处理设备就能自动、连续、实时地完成。 
  • [其他] 浅谈人脸识别优劣
     优势 人脸识别的优势在于其自然性和不被被测个体察觉的特点。 所谓自然性,是指该识别方式同人类(甚至其他生物)进行个体识别时所利用的生物特征相同。例如人脸识别,人类也是通过观察比较人脸区分和确认身份的,另外具有自然性的识别还有语音识别、体形识别等,而指纹识别、虹膜识别等都不具有自然性,因为人类或者其他生物并不通过此类生物特征区别个体。 不被察觉的特点对于一种识别方法也很重要,这会使该识别方法不令人反感,并且因为不容易引起人的注意而不容易被欺骗。人脸识别具有这方面的特点,它完全利用可见光获取人脸图像信息,而不同于指纹识别或者虹膜识别,需要利用电子压力传感器采集指纹,或者利用红外线采集虹膜图像,这些特殊的采集方式很容易被人察觉,从而更有可能被伪装欺骗。  劣势 人脸识别被认为是生物特征识别领域甚至人工智能领域最困难的研究课题之一。人脸识别的困难主要是人脸作为生物特征的特点所带来的。 相似性 不同个体之间的区别不大,所有的人脸的结构都相似,甚至人脸器官的结构外形都很相似。这样的特点对于利用人脸进行定位是有利的,但是对于利用人脸区分人类个体是不利的。 易变性 人脸的外形很不稳定,人可以通过脸部的变化产生很多表情,而在不同观察角度,人脸的视觉图像也相差很大,另外,人脸识别还受光照条件(例如白天和夜晚,室内和室外等)、人脸的很多遮盖物(例如口罩、墨镜、头发、胡须等)、年龄等多方面因素的影响。 在人脸识别中,第一类的变化是应该放大而作为区分个体的标准的,而第二类的变化应该消除,因为它们可以代表同一个个体。通常称第一类变化为类间变化(inter-class difference),而称第二类变化为类内变化(intra-class difference)。对于人脸,类内变化往往大于类间变化,从而使在受类内变化干扰的情况下利用类间变化区分个体变得异常困难。 
  • [其他] 浅谈人脸识别之法律法规
    随着信息技术飞速发展,人脸识别逐步渗透到人们生活的方方面面。人脸识别技术在诸多领域发挥着巨大作用的同时,也存在被滥用的情况,最高人民法院发布司法解释,对人脸识别进行规范。 2021年7月28日,《最高人民法院关于审理使用人脸识别技术处理个人信息相关民事案件适用法律若干问题的规定》正式对外发布。《规定》明确:“物业服务企业或者其他建筑物管理人以人脸识别作为业主或者物业使用人出入物业服务区域的唯一验证方式,不同意的业主或者物业使用人请求其提供其他合理验证方式的,人民法院依法予以支持。”根据这一规定,小区物业在使用人脸识别门禁系统录入人脸信息时,应当征得业主或者物业使用人的同意,对于不同意的业主或者物业使用人,小区物业应当提供替代性验证方式,不得侵害业主或物业使用人的人格权益和其他合法权益。 2021年8月,随着人脸识别技术广泛用于现实生活,民众对其被滥用的担心也不断增加,强化人脸信息保护的呼声日益高涨。最高法出台《关于审理使用人脸识别技术处理个人信息相关民事案件适用法律若干问题的规定》,把未经自然人或其监护人单独同意的人脸信息采集行为明确界定为“侵权”,这不仅回应了民众呼唤保护人脸信息的正当诉求,也为司法部门处理此类纠纷提供了法律依据,更增强了公民对其人格权益保护的法律底气。  2021年8月20日,十三届全国人大常委会第三十次会议表决通过《中华人民共和国个人信息保护法》,自2021年11月1日起施行。针对滥用人脸识别技术问题,本法要求,在公共场所安装图像采集、个人身份识别设备,应设置显著的提示标识;所收集的个人图像、身份识别信息只能用于维护公共安全的目的。 2021年11月14日,国家网信办公布《网络数据安全管理条例(征求意见稿)》,并向社会公开征求意见。征求意见稿提出,数据处理者利用生物特征进行个人身份认证的,应当对必要性、安全性进行风险评估,不得将人脸、步态、指纹、虹膜、声纹等生物特征作为唯一的个人身份认证方式,以强制个人同意收集其个人生物特征信息。 2023年8月8日,为规范人脸识别技术应用,保护个人信息权益及其他人身和财产权益,维护社会秩序和公共安全,国家网信办发布《人脸识别技术应用安全管理规定(试行)(征求意见稿)》,向社会公开征求意见。
  • [其他] 浅谈人脸识别的应用前景
    生物识别技术已广泛用于政府、军队、银行、社会福利保障、电子商务、安全防务等领域。例如,一位储户走进了银行,他既没带银行卡,也没有回忆密码就径直提款,当他在提款机上提款时,一台摄像机对该用户的眼睛扫描,然后迅速而准确地完成了用户身份鉴定,办理完业务。这是美国德克萨斯州联合银行的一个营业部中发生的一个真实的镜头。而该营业部所使用的正是现代生物识别技术中的“虹膜识别系统”。此外,美国“9.11”事件后,反恐怖活动已成为各国政府的共识,加强机场的安全防务十分重要。美国维萨格公司的脸像识别技术在美国的两家机场大显神通,它能在拥挤的人群中挑出某一张面孔,判断他是不是通缉犯。 当前社会上频繁出现的入室偷盗、抢劫、伤人等案件的不断发生,鉴于此种原因,防盗门开始走进千家万户,给家庭带来安宁;然而,随着社会的发展,技术的进步,生活节奏的加速,消费水平的提高,人们对于家居的期望也越来越高,对便捷的要求也越来越迫切,基于传统的纯粹机械设计的防盗门,除了坚固耐用外,很难快速满足这些新兴的需求:便捷,开门记录等功能。人脸识别技术已经得到广泛的认同,但其应用门槛仍然很高:技术门槛高(开发周期长),经济门槛高(价格高)。 人脸识别产品已广泛应用于金融、司法、军队、公安、边检、政府、航天、电力、工厂、教育、医疗及众多企事业单位等领域。随着技术的进一步成熟和社会认同度的提高,人脸识别技术将应用在更多的领域。 1、企业、住宅安全和管理。如人脸识别门禁考勤系统,人脸识别防盗门等。 2、电子护照及身份证。中国的电子护照计划公安部一所正在加紧规划和实施。 3、公安、司法和刑侦。如利用人脸识别系统和网络,在全国范围内搜捕逃犯。 4、自助服务。 5、信息安全。如计算机登录、电子政务和电子商务。在电子商务中交易全部在网上完成,电子政务中的很多审批流程也都搬到了网上。而当前,交易或者审批的授权都是靠密码来实现,如果密码被盗,就无法保证安全。但是使用生物特征,就可以做到当事人在网上的数字身份和真实身份统一,从而大大增加电子商务和电子政务系统的可靠性。
  • [通用服务] 【AI使能】人脸识别服务 FRS
    人脸识别服务(Face Recognition Service,简称FRS),是基于人的脸部特征信息,利用计算机对人脸图像进行处理、分析和理解,进行身份识别的一种智能服务。分类文档链接备注最新动态cid:link_4 应用场景cid:link_1 API参考cid:link_2 FAQcid:link_3 华为云在线课程实战篇:刷脸时代已经到来,你准备好了吗?cid:link_0手把手教你玩转人脸识别,初探深度学习。华为云开发者网人脸识别服务 FRS开放能力cid:link_5 
  • [技术干货] FunctionGraph+OBS+FRS+IVS实现人证核身
    FunctionGraph+OBS+FRS+IVS实现人证核身目录场景设计方案架构图方案设计云服务介绍环境准备开发流程1.客户端开发——上传图片/视频到OBS2.FunctionGraph函数代码开发3.下载OBS对象+Base64编码4.FRS活体检测5.IVS人证核身6.SMN发布结果消息7.客户端开发——SMN之HTTP(S)终端接口开发8.FunctionGraph函数整合实现效果部署测试示例工程一、场景设计客户通过页面上传人物照片、视频到OBS并触发FunctionGraph函数,函数调用FRS人脸识别服务判断是否为真人,如果是真人继续调用IVS人证核身服务校验身份真实性,最后通过SMN将结果消息发布出去。方案架构图![方案架构图]二、方案设计创建FunctionGraph函数,并为该函数配置OBS触发器,上传图片或视频到OBS后触发FunctionGraph函数,函数下载OBS对象并调用FRS进行活体检测,活体检测通过后函数再调用IVS进行人证核身,最后函数通过SMN发布人证核身结果消息。云产品提供服务作用OBS对象操作实现上传/下载 图片/视频FunctionGraph配置OBS触发器实现OBS上传时触发函数FunctionGraph创建Java函数创建Java语言的函数FRS活体检测对图片或视频进行动作/静默活体检测IVS认证核身标准版实现对上传的人脸图片进行身份验证SMN发送HTTP(S)消息实现将结果发送到HTTP(S)终端三、云服务介绍华为云OBS: 对象存储服务(Object Storage Service,OBS)是一个基于对象的海量存储服务,为客户提供海量、安全、高可靠、低成本的数据存储能力,使用时无需考虑容量限制,并且提供多种存储类型供选择,满足客户各类业务场景诉求。产品页面华为云FunctionGraph:函数工作流(FunctionGraph)是一项基于事件驱动的函数托管计算服务。通过函数工作流,只需编写业务函数代码并设置运行的条件,无需配置和管理服务器等基础设施,函数以弹性、免运维、高可靠的方式运行。产品页面华为云FRS:人脸识别服务(Face Recognition Service),能够在图像中快速检测人脸、分析人脸关键点信息、获取人脸属性、实现人脸的精确比对和检索。该服务可应用于身份验证、电子考勤、客流分析等场景。产品页面华为云IVS:人证核身服务(Identity Verification Solution)将用户本人与身份证信息关联起来,应用人脸识别与文字识别等技术,对接权威数据库,支持基于二要素(姓名、身份证)认证或三要素(人脸、姓名、身份证)认证,实现对身份真实性的精准核验。产品页面华为云SMN:消息通知服务(Simple Message Notification)为用户提供快速简便、稳定可靠、简化运维、高可扩展、安全可信的消息通知能力。最终用户可以通过HTTP、HTTPS、邮件、短信、触发函数执行、即时通讯工具等方式接收通知信息。华为云用户也可以在应用之间通过消息通知服务实现应用的功能集成,降低系统的复杂性。产品页面四、环境准备| 开发语言:Java| 开发环境:jdk1.8 maven3.6.3| 华为云环境:注册云账号,并完成实名认证五、开发流程| 分为客户端开发和FunctionGraph的Java函数开发。1、客户端开发--上传图片/视频到OBS华为云OBS服务控制台创建桶用来存储人脸图片或视频 如果涉及视频需要压缩上传,需要配置在线解压策略(具体桶中->数据处理>在线解压) 拿到OBS的maven依赖坐标,使用SDK开发上传OBS对象客户端,这里文件传到固定的文件夹下,并使用固定对象名命名,比如face。帮助文档上传对象方法代码参考public void uploadFile(MultipartFile multipartFile){ String endPoint = "https://终端节点"; String ak = "ak"; String sk = "sk"; //1.创建OBS客户端对象 ObsClient obsClient = new ObsClient(ak, sk, endPoint); //2.获取文件输入流 InputStream in = null; try { in = multipartFile.getInputStream(); //3.上传对象,后面根据该对象名下载对象,保持上传下载一致 PutObjectResult putObjectResult = obsClient.putObject("桶名", "对象名", in); System.out.println(putObjectResult); } catch (IOException e) { throw new RuntimeException(e); }finally { if (in!=null){ try { in.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }2、FunctionGraph函数代码开发(1)FunctionGraph函数时序图(2)Java函数环境搭建创建maven项目,导入FunctionGraph提供的Runtime依赖 操作指导创建FunctionGraph Java函数,并配置OBS触发器。 3、下载OBS对象+Base64编码| 终端节点查询 | ak、sk获取从OBS下载对象并进行Base64编码,后面使用该Base64编码结果进行活体检测。导入OBS的Maven依赖。可以从SDK中心查询依赖坐标下载对象+Base64编码示例代码:public static String download() { //1.相关配置数据 String endPoint = "https://终端节点"; String ak = "ak"; String sk = "sk"; //2.创建ObsClient实例 final ObsClient obsClient = new ObsClient(ak, sk, endPoint); //对象名保持和上传对象名一致 ObsObject obsObject = obsClient.getObject("桶名", "对象名"); //3.读取对象内容 ByteArrayOutputStream bos = null; InputStream input = null; String imageStr = null; try { System.out.println("Object content:"); input = obsObject.getObjectContent(); byte[] b = new byte[1024]; bos = new ByteArrayOutputStream(); int len; while ((len = input.read(b)) != -1) { bos.write(b, 0, len); } //4.Base64编码 byte[] data = new byte[bos.size()]; data = bos.toByteArray(); imageStr = new String(Base64.encodeBase64(data)); System.out.println(imageStr); } catch (Exception e) { e.printStackTrace(); } finally { try { bos.close(); } catch (IOException e) { throw new RuntimeException(e); } try { input.close(); } catch (IOException e) { throw new RuntimeException(e); } } return imageStr; }4、FRS活体检测活体检测,判断图片或视频中的人物是否为真人活体。对Base64编码后的结果进行活体检测,图片使用静默活体检测,视频使用动作活体检测。以图片对应的静默活体检测为例,使用SDK开发。开通活体检测服务控制台 导入FRS的Maven依赖。可以从SDK中心查询依赖坐标静默活体检测示例代码://方法参数imageStr:人像图片Base64编码 public static DetectLiveFaceByBase64Response detectLive(String imageStr) { //1.配置基本信息 ICredential credential = new BasicCredentials().withAk("ak").withSk("sk"); FrsClient client = FrsClient.newBuilder() .withCredential(credential) .withRegion(FrsRegion.valueOf("区域")) // 选择服务所在区域,如华北-北京四: FrsRegion.valueOf("cn-north-4") .build(); //2.通过base64编码进行活体检测 DetectLiveFaceByBase64Request request = new DetectLiveFaceByBase64Request(); LiveDetectFaceBase64Req body = new LiveDetectFaceBase64Req(); body.withImageBase64(imageStr); request.withBody(body); DetectLiveFaceByBase64Response response = null; try { response = client.detectLiveFaceByBase64(request); System.out.println(response.toString()); } catch (Exception e) { e.printStackTrace(); } return response; }5、IVS人证核身这里用到IVS标准版(三要素)对图片中人物进行身份认证,这里用到的图片Base64是FRS活体检测返回的结果中的图片Base64编码。开通人证核身服务控制台 导入IVS的Maven依赖。可以从SDK中心查询依赖坐标人证核身标准版:使用身份证图片Base64、人像图片Base64进行认证,示例代码://方法参数imageStr:人像图片Base64编码 public static String ivsVerify(String imageStr) { //1.配置基本信息 ICredential credential = new BasicCredentials().withAk("ak").withSk("sk"); IvsClient client = IvsClient.newBuilder() .withCredential(credential) .withRegion(IvsRegion.valueOf("区域"))// 选择服务所在区域,如华北-北京四就是 FrsRegion.valueOf("cn-north-4") .build(); DetectStandardByIdCardImageRequest request = new DetectStandardByIdCardImageRequest(); IvsStandardByIdCardImageRequestBody body = new IvsStandardByIdCardImageRequestBody(); List listIvsStandardByIdCardImageRequestBodyDataReqData = new ArrayList<>(); listIvsStandardByIdCardImageRequestBodyDataReqData.add( new ReqDataByIdCardImage() .withIdcardImage1("身份证人像面图片Base64") .withFaceImage(imageStr) ); IvsStandardByIdCardImageRequestBodyData databody = new IvsStandardByIdCardImageRequestBodyData(); databody.withReqData(listIvsStandardByIdCardImageRequestBodyDataReqData); Meta metabody = new Meta(); //唯一标识此次请求的ID,用户自定义,不超过64位 metabody.withUuid(UUID.randomUUID().toString()); body.withData(databody); body.withMeta(metabody); request.withBody(body); String result = null; try { //2.调用服务 DetectStandardByIdCardImageResponse response = client.detectStandardByIdCardImage(request); System.out.println(response.toString()); //result取值:valid-成功;invalid-失败 result = response.getResult().getRespData().get(0).getVerificationResult(); } catch (Exception e) { e.printStackTrace(); } //3.返回人证核身结果 return result; }6、SMN发布结果消息这里用SMN向HTTP(S)终端发送消息,将人证核身结果发布到指定的URL地址。开通服务(创建主题+创建订阅)控制台 导入IVS的Maven依赖。可以从SDK中心查询依赖坐标SMN发布消息示例代码://方法请求参数msgContent:消息内容 public static void sendMsg(String msgContent){ String ak = "ak"; String sk = "sk"; //创建SMN客户端 ICredential credential = new BasicCredentials().withAk(ak).withSk(sk); SmnClient client = SmnClient.newBuilder() .withCredential(credential) .withRegion(区域)//如华北-北京四:SmnRegion.CN_NORTH_4 .build(); //创建请求 PublishMessageRequest publishMessageRequest = new PublishMessageRequest(); //创建请求体 PublishMessageRequestBody body = new PublishMessageRequestBody(); body.withMessage(msgContent); body.withSubject("主题"); publishMessageRequest.withTopicUrn("主题URN,可在控制台获取"); publishMessageRequest.withBody(body); //执行请求 PublishMessageResponse response = client.publishMessage(publishMessageRequest); System.out.println(response); }7、客户端开发--SMN之HTTP(S)终端接口开发HTTP(S)终端对应的URL接口需要根据type参数判断是确定订阅、消息或是取消订阅。接口示例代码:@PostMapping("/smn") //该接口完整地址就是创建时配置的终端地址 public void subscriptionConfirmation2(@RequestBody SubscriptionReq subscriptionReq,HttpServletRequest request,HttpServletResponse response){ System.out.println(subscriptionReq); try{ //1.获取参数,封装到MAP中 Map parameterMap = BeanUtils.describe(subscriptionReq); String signing_cert_url = parameterMap.get("signing_cert_url"); String signature = parameterMap.get("signature"); //2.检验消息签名 boolean flag = SmnUtil.isMessageValid(signing_cert_url, signature, parameterMap); //3.判断是订阅还是发布消息 String type = parameterMap.get("type"); if ("SubscriptionConfirmation".equals(type)){ //4.访问订阅确认页面 String map = restTemplate.getForObject(parameterMap.get("subscribe_url"), String.class); System.out.println(map); }else if ("Notification".equals(type)){ //5.消息,打印消息 System.out.println(parameterMap); }else{ //6.取消订阅 } }catch (Exception e){ e.printStackTrace(); } }接口接收请求参数实体类可参考:@Data //Lombok注解 public class SubscriptionReq { private String type; private String signature; private String subject; private String topic_urn; private String message_id; private String signature_version; private String message; private String subscribe_url; private String signing_cert_url; private String timestamp; }8、FunctionGraph函数整合在触发函数对应的方法中调用上面服务方法,实现认证核身认证流程。即在搭建的FunctionGraph Java函数项目中的TriggerTests.java中的apiTest方法中编写人证核身业务逻辑。TriggerTests代码:import java.util.HashMap; import java.util.Map; import com.huawei.services.runtime.Context; import com.huawei.services.runtime.entity.apig.APIGTriggerEvent; import com.huawei.services.runtime.entity.apig.APIGTriggerResponse; import com.huawei.utils.FrsUtils; import com.huawei.utils.IvsUtils; import com.huawei.utils.ObsUtils; import com.huawei.utils.SmnUtils; import com.huaweicloud.sdk.frs.v1.model.DetectLiveFaceByBase64Response; public class TriggerTests { //functionGraph函数触发方法 public APIGTriggerResponse apigTest(APIGTriggerEvent event, Context context) { //1.下载对象,并得到base64编码后结果 String imageBase64 = ObsUtils.download(); //2.活体检测 DetectLiveFaceByBase64Response response = FrsUtils.detectLive(imageBase64); Boolean alive = response.getResult().getAlive(); //3.人证核身 String msg = null; if (alive) { //活体检测成功 String result = IvsUtils.ivsVerify(response.getResult().getPicture()); //result取值:valid-成功;invalid-失败 msg = "valid".equals(result) ? "人证核身成功!是XXX本人。" : "人证核身失败!"; System.out.println(result+"--"+msg); //4.发送结果信息 SmnUtils.sendMsg(msg); } else { msg = "活体检测失败!"; System.out.println(msg); //发送结果消息 SmnUtils.sendMsg(msg); } Map headers = new HashMap(); headers.put("Content-Type", "application/json"); return new APIGTriggerResponse(200, headers, msg); } }六、实现效果1、部署测试将FunctionGraph Java函数对应项目打成jar包。参照帮助文档FunctionGraph创建Java函数,并配置OBS触发器(对应:开发流程->二、FunctionGraph函数代码开发->1、Java函数环境搭建)。FunctionGraph上传项目jar包到函数。通过开发的客户端上传图片或视频到OBS。在客户端刷新接收SMN消息的HTTP(S)终端接口,查看结果信息;查看FunctionGraph日志。 2、示例工程示例代码附件,待补充……
  • [问题求助] 静默活体检测多人脸问题
    针对照片中存在多人脸的情况,能否取最大的人脸区域进行检测。很多情况下手持证件的照片均无法检测成功。建议改进以适用于更多的场景!
  • [技术干货] openCV实战项目--人脸考勤-转载
    人脸任务在计算机视觉领域中十分重要,本项目主要使用了两类技术:人脸检测+人脸识别。代码分为两部分内容:人脸注册 和 人脸识别人脸注册:将人脸特征存储进数据库,这里用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
  • [问题求助] 人脸比对前端请求error_code\":\"FRS.0016\",\"error_msg\":\"The request file type is not supported.
    一直返回这个但APIPost可以正常获取
  • [技术干货] 【YOLOv5-6.x】设置可学习权重结合BiFPN(Add操作)-转载
     前言 在之前的这篇博客中,简要介绍了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】 人脸识别-转载
     一:前言 本次人脸识别技术使用到的是级联分类器  对于级联分类器,如果想要自己训练模型可以参考这篇文章  【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