• [技术干货] 基于STM32单片机设计的红外测温仪(带人脸检测)
    # 基于STM32单片机设计的红外测温仪(带人脸检测)由于医学发展的需要,在很多情况下,一般的温度计己经满足不了快速而又准确的测温要求,例如:车站、地铁、机场等人口密度较大的地方进行人体温度测量。 当前设计的这款红外测温仪由测温硬件+上位机软件组合而成,主要用在地铁、车站入口等地方,可以准确识别人脸进行测温,如果有人温度超标会进行语音提示并且保存当前人脸照片。## 1、 硬件选型与设计思路### (1). 设备端主控单片机采用STM32F103C8T6,人体测温功能采用非接触式红外测温模块。!(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) 上位机运行效果!(https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/6/1657088458540921923.png)!(https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20227/6/1657088467253561881.png)上位机需要连接STM32设备之后才可以获取温度数据,点击软件上的打开摄像头按钮,开启摄像头,让检测到人脸时,下面会显示当前测量的温度。如果没有连接STM32设备,那么默认会显示一个正常的固定温度值。界面上右边红色的字,表示当前处理一帧图像的耗时时间,电脑性能越好,检测速度越快。### (4) 拿到可执行文件之后如何运行?先解压压缩包,进入“测温仪上位机-可执行文件”目录,将“haarcascade_frontalface_alt2.xml”拷贝到C盘根目录。!(https://bbs-img.huaweicloud.com/blogs/img/16e6b1.png)!(https://bbs-img.huaweicloud.com/blogs/img/17a28c.png)然后双击“FaceTemperatureCheck.exe”运行程序。!(https://bbs-img.huaweicloud.com/blogs/img/18c1fe.png)未连接设备,也可以打开摄像头检测人脸,只不过温度值是一个固定的正常温度值范围。## 二、上位机设计## 2.1 安装编译环境如果需要自己编译运行源代码,需要先安装Qt5开发环境。下载地址: https://download.qt.io/official_releases/qt/5.12/5.12.0/!(https://bbs-img.huaweicloud.com/blogs/img/197e29.png)下载之后,先将电脑网络断掉(不然会提示要求输入QT的账号),然后双击安装包进行安装。安装可以只需要选择一个MinGW 32位编译器就够用了,详情看下面截图,选择“MinGW 7.3.0 32-bit”后,就点击下一步,然后等待安装完成即可。!(https://bbs-img.huaweicloud.com/blogs/img/1b27e0.png)## 2.2 软件代码整体效果如果需要完整的工程,可以在这里去下载:https://download.csdn.net/download/xiaolong1126626497/85892490!(https://bbs-img.huaweicloud.com/blogs/img/1d3671.png)打开工程后(工程文件的后缀是xxx.pro),点击左下角的绿色三角形按钮就可以编译运行程序。!(https://bbs-img.huaweicloud.com/blogs/img/206514.png)## 2.3 UI设计界面!(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()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()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 配置文件(修改参数-很重要)!(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”目录下。!(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() 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;icomboBox_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()readAll(); //转换温度 current_temp=rx_data.toDouble();}```
  • [问题求助] 能不能将华为云的人脸识别功能做成插件
    像华为云客服,华为云OBS等都以插件形式方便用户,可以不可以将华为云的人脸识别功能和文字识别功能做成插件,比如会员实名认证,刷脸登录都会用到。
  • [问题求助] 如何在微信支付或支付宝支付中增加指纹支付
    在后台支付设置里能不能增加支付方式种类,除了扫码方式外,可不可以增加人脸支付和指纹支付?还有就是在第三方登录方式中除了QQ,微博,微信三种外可不可以增加人脸识别和指纹登录?
  • [问题求助] 如何在微信支付或者支付宝支付中增加指纹支付
    在后台支付设置里能不能增加支付方式种类,除了扫码方式外,可不可以增加人脸支付和指纹支付?还有就是在第三方登录方式中除了QQ,微博,微信三种外可不可以增加人脸识别和指纹登录?
  • [问题求助] 人脸识别编译警告,求助!!!!
    又找不到包的警告,求助!!!
  • [MindX SDK] CenterFace动态分辨率模型推理案例分享
    # MindX SDK+CenterFace动态分辨率模型推理## 1 介绍[MindX SDK](https://gitee.com/link?target=https%3A%2F%2Fwww.hiascend.com%2Fsoftware%2Fmindx-sdk) 是华为推出的软件开发套件(SDK),提供极简易用、高性能的API和工具,助力昇腾AI处理器赋能各种应用场景。对于一个训练好的模型,在Mind SDK上仅需在stream流的pipeline配置文件中简单配置几行,便可使用内置的插件实现模型的前处理和后处理,完成常见的推理任务。本文将介绍如何在MindX SDK框架上实现动态分辨率模型的推理。(代码地址:https://gitee.com/ascend/mindxsdk-referenceapps/tree/master/contrib/CenterFace)**动态分辨率:**指模型转化为om格式在MindX SDK上进行推理时,通常模型输入的图片大小一般固定为ATC工具转化时所设定的值。如果所有尺寸的图片都缩放至同一大小,对于尺寸与模型输入差距过大的图片,模型推理的效果难以符合预期。因此在使用ATC工具转化模型时,开启动态分辨率的功能,配置多个档位以适配常见的图片尺寸,模型推理时根据图片大小选用合适的档位。本案例推理采用[CenterFace](https://github.com/Star-Clouds/CenterFace)(一个轻量化的人脸检测模型,同时实现了人脸检测+关键点检测),考虑到API中已有的后处理插件基类,以及输出结果的特殊性,开发了两个后处理插件,其余均使用框架自带插件。本文将涵盖以下内容:*模型推理流程*、*动态分辨率模型转换*、*模型后处理开发介绍*。## 2 开发工具准备> MindX SDK模型开发须使用本地IDE工具连接昇腾服务器,使用本教程前,请确保以下环境正确安装。- 在昇腾机器上安装MindX SDK,MindX SDK软件包安装[参考链接](https://support.huawei.com/enterprise/zh/doc/EDOC1100209684?section=j00h) - Cmake- 本地C++开发环境搭建,以下演示使用[CLion](https://www.jetbrains.com/clion/download/download-thanks.html?platform=windows)- 远程SSH工具,如MobaXterm,xshell,final shell(以下演示使用MobaXterm)## 3 开发环境配置请参考官方技术文档:(https://gitee.com/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/quick_start/1-2IDE开发环境搭建.md#/ascend/docs-openmind/blob/master/guide/mindx/sdk/tutorials/quick_start/Cmake介绍.md)## 4 开发流程介绍在MinX SDK上实现模型推理,通常包含以下几个步骤:- 准备好用于推理的模型。- 使用ATC工具转换为om模型(转换过程根据模型的要求配置aipp做前处理)。- 根据业务需求,编排steam流,编写主函数。- 根据需要开发插件或后处理插件。## 5 开发### 5.1 准备模型从CenterFace官方仓下载onnx模型([github下载链接](https://gitee.com/link?target=https%3A%2F%2Fgithub.com%2FStar-Clouds%2FCenterFace%2Fblob%2Fmaster%2Fmodels%2Fonnx%2Fcenterface.onnx)),无法访问github请使用[备用链接](https://mindx.sdk.obs.cn-north-4.myhuaweicloud.com/mindxsdk-referenceapps%20/contrib/CenterFaceWithDynamicResolution/centerface_offical.onnx)。如果你的模型是其它格式,同样可以通过以下工具转化为MindX SDK使用的模型格式。### 5.2 动态分辨率模型转换转换模型之前需要在昇腾服务器上安装和配置好ATC模型转换工具(模型转换工具相关介绍参考[链接](https://gitee.com/link?target=https%3A%2F%2Fsupport.huaweicloud.com%2Ftg-cannApplicationDev330%2Fatlasatc_16_0005.html))。如果服务器上已经具备ATC工具,但未配置环境变量,可参考如下配置:```bashexport install_path=/usr/local/Ascend/ascend-toolkit/latestexport PATH=/usr/local/python3/bin:${install_path}/atc/ccec_compiler/bin:${install_path}/atc/bin:$PATHexport 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.eggexport LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATHexport ASCEND_OPP_PATH=${install_path}/opp```接着获取模型输入节点的名称,下载Netron,安装后使用Netron打开onnx模型![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843579757448142.png)可以看到模型的输入节点名称为:`input.1`使用SSH工具附带的ftp功能把下载好的onnx模型上传至昇腾服务器,cd进入模型存放的目录,执行如下命令:```bashatc --model=centerface_offical.onnx --output=centerface_offical --dynamic_image_size="768,1024;800,800;1024,768;864,1120;1120,864;960,1216;1216,960;1056,1312;1312,1056;1152,1408;1408,1152;1248,1504;1504,1248;1344,1600;1600,1344;1440,1696;1696,1440;1536,1792;1792,1536;1632,1888;1888,1632;1728,1984;1984,1728;1824,2080;2080,1824" --soc_version=Ascend310 --input_shape="input.1:1,3,-1,-1" --input_format=NCHW --framework=5 --insert_op_conf=centerface_aipp.cfg```以上命令转成具有多档位的动态分辨率模型,转成单档位可使用如下命令:```bashatc --model=centerface_offical.onnx --output=centerface_offical --soc_version=Ascend310 --input_shape="input.1:1,3,800,800" --input_format=NCHW --framework=5 --insert_op_conf=centerface_aipp.cfg```命令参数的解释如下:- --model 待转换模型存放的路径- --output 模型输出路径及输出模型命名- --soc_version 芯片版本- --input_shape 模型输入节点名称和tensor形状,当配置动态分辨率属性时,图片宽高应设为-1- --input_format 输入图片的数据格式- --dynamic_image_size 动态分辨率属性,配置后模型可接受不同分辨率的输入- --framework 原模型使用的框架- --insert_op_conf aipp配置文件> aipp配置文件为可选,作用在于对模型输入数据进行前处理,是根据模型的输入要求进行具体配置的。>> 此处因MindX SDK图片解码器仅支持JPG图像,并且解码成YUV420sp,而CenterFace模型的输入格式为RGB888,因此配置了色域转换的相关属性。具体配置文件如下```yamlaipp_op{aipp_mode: staticcrop: falseinput_format : YUV420SP_U8#非动态分辨率请设置具体的宽高src_image_size_h : 0src_image_size_w : 0csc_switch : truerbuv_swap_switch : falsematrix_r0c0 : 256matrix_r0c1 : 0matrix_r0c2 : 359matrix_r1c0 : 256matrix_r1c1 : -88matrix_r1c2 : -183matrix_r2c0 : 256matrix_r2c1 : 454matrix_r2c2 : 0input_bias_0 : 0input_bias_1 : 128input_bias_2 : 128mean_chn_0 : 0mean_chn_1 : 0mean_chn_2 : 0min_chn_0 : 0min_chn_1 : 0min_chn_2 : 0var_reci_chn_0: 1.0var_reci_chn_1: 1.0var_reci_chn_2: 1.0}```开启动态分辨率属性后,经过ATC工具转化的模型,已具备接受多个分辨率输入的功能,通过修改pipeline中缩放插件的属性,即可改变模型输入的大小。### 5.3 项目创建与pipeline流程编排#### 新建CLION项目![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843536158771876.png)#### CmakeLists文件```cmakecmake_minimum_required(VERSION 3.10)project(CenterFace)add_compile_options(-fPIC -fstack-protector-all -g -Wl,-z,relro,-z,now,-z -pie -Wall)add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0 -Dgoogle=mindxsdk_private)set(CMAKE_BUILD_TYPE Debug)set(MX_SDK_HOME $ENV{MX_SDK_HOME})#使用本地IDE运行请配置MX_SDK_HOME的绝对路径if (NOT DEFINED ENV{MX_SDK_HOME}) string(REGEX REPLACE "(.*)/(.*)/(.*)/(.*)" "\\1" MX_SDK_HOME ${CMAKE_CURRENT_SOURCE_DIR}) message(STATUS "set default MX_SDK_HOME: ${MX_SDK_HOME}")else () message(STATUS "env MX_SDK_HOME: ${MX_SDK_HOME}")endif()set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/")#需要用到的头文件include_directories( ${MX_SDK_HOME}/include ${MX_SDK_HOME}/opensource/include ${MX_SDK_HOME}/opensource/include/opencv4)#链接的动态库link_directories( ${MX_SDK_HOME}/lib ${MX_SDK_HOME}/opensource/lib)add_executable(Main main.cpp )target_link_libraries(Main glog mxbase mxpidatatype plugintoolkit streammanager cpprest mindxsdk_protobuf opencv_world )```#### 主函数编写```C++#include "MxBase/Log/Log.h"#include "MxBase/MemoryHelper/MemoryHelper.h"#include "MxStream/StreamManager/MxStreamManager.h"#include "opencv2/opencv.hpp"#include #include #include int main(int argc, char *argv[]) { // 读取pipeline文件信息 std::string pipelineConfigPath = "pipeline配置文件路径"; std::string pipelineConfig = ReadPipelineConfig(pipelineConfigPath); if (pipelineConfig == "") { return APP_ERR_COMM_INIT_FAIL; } std::string streamName = "center_face"; // 新建一个流管理MxStreamManager对象并初始化 auto mxStreamManager = std::make_shared(); APP_ERROR ret = mxStreamManager->InitManager(); // 加载pipeline得到的信息,创建一个新的stream业务流 ret = mxStreamManager->CreateMultipleStreams(pipelineConfig); MxStream::MxstDataInput dataBuffer; // 将图片的信息读取到dataBuffer中 ret = readfile(fileName, dataBuffer); // 通过SendData函数传递输入信息到指定的工作元件模块 // streamName是pipeline文件中业务流名称;inPluginId为输入端口编号,对应输入元件的编号 ret = mxStreamManager->SendData(streamName, 0, dataBuffer); delete dataBuffer.dataPtr; // 获取Stream上后处理插件的处理结果 std::vector keyVec = {"mxpi_imagedecoder0", "mxpi_objectpostprocessor0", "mxpi_objectpostprocessor1"}; std::vector output =mxStreamManager->GetProtobuf(streamName, 0, keyVec); // mxpi_imagedecoder0图片解码插件输出信息 auto mxpiVision = std::static_pointer_cast(output[0].messagePtr); // mxpi_objectpostprocessor0 后处理插件1输出信息 auto objectList = std::static_pointer_cast(output[1].messagePtr); // mxpi_objectpostprocessor1 后处理插件2输出信息 auto keypointList = std::static_pointer_cast(output[2].messagePtr); // 把经过后处理之后的推理结果写入到图片中 SaveResult(mxpiVision, objectList, keypointList); mxStreamManager->DestroyAllStreams(); return 0;}```> 以上代码仅作主函数的大体逻辑展示#### pipeline流程编排模型的推理业务流程如下:1. 使用StreamManager的api从外部把图片二进制数据送入appsrc插件。2. 使用图像解码mxpi_imagedecoder对图片进行解码。3. 通过图像缩放插件mxpi_imageresize将图形缩放至指定的分辨率档位。4. 缩放后的图形输入模型推理插件mxpi_tensorinfer得到模型输出。5. 把模型得到的四个输出送入两个后处理插件。6. 人脸检测插件用来得到人脸检测框,关键点检测插件得到五个关键点。7. 人脸检测框送入OSD可视化插件编码到原图中。8. 最后两个后处理插件分支汇聚到appsink结束整个推理流程。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843473547740298.png)在pipeline的编排中,对于上图分叉的部分,使用了内置的tee插件接queue插件处理,使得两个后处理插件都能获得模型推理的输出数据。此外OSD可视化插件的输入用到了图像解码挂载的metadata,同样使用了tee插件接queue,图上并未标明。对应的pipeline文件:```json{ "center_face": { "stream_config": { "deviceId": "0" }, "appsrc0": { "props": { "blocksize": "409600" }, "factory": "appsrc", "next": "mxpi_imagedecoder0" }, "mxpi_imagedecoder0": { "props": { "deviceId": "0" }, "factory": "mxpi_imagedecoder", "next": "mxpi_imageresizer0" }, "mxpi_imageresizer0": { "props": { "dataSource": "mxpi_imagedecoder0", "resizeHeight": "768", "resizeWidth": "1024" }, "factory": "mxpi_imageresize", "next": "tee0" }, "tee0": { "factory": "tee", "next": [ "queue0", "queue1" ] }, "queue0": { "props": { "max-size-buffers": "50" }, "factory": "queue", "next": "mxpi_tensorinfer0" }, "queue1": { "props": { "max-size-buffers": "50" }, "factory": "queue", "next": "mxpi_opencvosd0:0" }, "tee1": { "factory": "tee", "next": [ "queue2", "queue3" ] }, "queue2": { "props": { "max-size-buffers": "50" }, "factory": "queue", "next": "mxpi_objectpostprocessor0" }, "queue3": { "props": { "max-size-buffers": "50" }, "factory": "queue", "next": "mxpi_objectpostprocessor1" }, "mxpi_tensorinfer0": { "props": { "dataSource": "mxpi_imageresizer0", "modelPath": "../model/centerface_offical.om" }, "factory": "mxpi_tensorinfer", "next": "tee1" }, "mxpi_objectpostprocessor0": { "props": { "dataSource": "mxpi_tensorinfer0", "postProcessConfigPath": "../model/centerface.cfg", "postProcessLibPath": "../plugins/lib/plugins/libmxpi_centerfacepostprocessor.so" }, "factory": "mxpi_objectpostprocessor", "next": "mxpi_object2osdinstances0" }, "mxpi_objectpostprocessor1": { "props": { "dataSource": "mxpi_tensorinfer0", "postProcessConfigPath": "../model/centerface.cfg", "postProcessLibPath": "../plugins/lib/plugins/libmxpi_centerfacekeypointpostprocessor.so" }, "factory": "mxpi_keypointpostprocessor", "next": "mxpi_synchronize0:1" }, "mxpi_object2osdinstances0": { "props": { "colorMap": "128, 128, 255|200,200,200|0,128,255|255,128,0", "fontFace": "16", "fontScale": "0.5", "fontThickness": "1", "fontLineType": "16", "rectThickness": "1", "rectLineType": "16" }, "factory": "mxpi_object2osdinstances", "next": "mxpi_opencvosd0:1" }, "mxpi_opencvosd0": { "props": { "dataSourceImage": "mxpi_imagedecoder0", "dataSourceOsd": "mxpi_object2osdinstances0" }, "factory": "mxpi_opencvosd", "next": "mxpi_synchronize0:0" }, "mxpi_synchronize0": { "factory": "mxpi_synchronize", "next": "appsink0" }, "appsink0": { "props": { "blocksize": "4096000" }, "factory": "appsink" } }}```> 上述涉及的路径均使用相对路径,相对于main函数文件所在位置。### 5.4 后处理插件开发后处理插件主要包含两个,一个对应人脸检测的功能,另一个对应人脸关键点。后处理插件需要对这模型的四个输出进行处理(包含热图、中心点偏移、目标框宽高和人脸五个关键点的相对目标框大小归一化后的偏移)得到各自的结果,以完成推理。而后处理插件的开发一般根据自己的任务需求,先在api中寻找是否有已经实现好的基类,继承合适的基类可减少开发的任务量,如果没有便只能继承最基础的基类。通常包含以下几个步骤(下面以人脸检测后处理插件作为演示):#### 编写CMakeLists.txt编写CMakeLists.txt,主要用于设置后处理动态库的目标文件以及链接相关的第三方库。每一个后处理插件都有一个CMakeLists.txt文件与之对应。编写后处理的CMakeLists.txt文件时,用户只需修改生成的后处理名和生成动态库的目标文件即可,延用原有的配置。人脸检测后处理插件的CMakeList.txt如下:```cmakecmake_minimum_required(VERSION 3.10)project(mxpi_centerfacepostprocessor)add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0 -Dgoogle=mindxsdk_private)set(PLUGIN_NAME "mxpi_centerfacepostprocessor")set(TARGET_LIBRARY ${PLUGIN_NAME})# 插件生成的位置set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/../lib/plugins)#在本地IDE上运行时请替换为MX_SDK_HOME的绝对路径,如set(MX_SDK_HOME /home/MindX_SDK/mxvision)set(MX_SDK_HOME $ENV{MX_SDK_HOME})if (NOT DEFINED ENV{MX_SDK_HOME}) string(REGEX REPLACE "(.*)/(.*)/(.*)/(.*)" "\\1" MX_SDK_HOME ${CMAKE_CURRENT_SOURCE_DIR}) message(STATUS "set default MX_SDK_HOME: ${MX_SDK_HOME}")else () message(STATUS "env MX_SDK_HOME: ${MX_SDK_HOME}")endif()#包含的头文件include_directories(${CMAKE_CURRENT_BINARY_DIR})include_directories(${MX_SDK_HOME}/include)include_directories(${MX_SDK_HOME}/opensource/include)include_directories(${MX_SDK_HOME}/opensource/include/gstreamer-1.0)include_directories(${MX_SDK_HOME}/opensource/include/glib-2.0)include_directories(${MX_SDK_HOME}/opensource/lib/glib-2.0/include)#链接动态库link_directories(${MX_SDK_HOME}/lib)link_directories(${MX_SDK_HOME}/opensource/lib)add_compile_options(-std=c++11 -fPIC -fstack-protector-all -pie -Wno-deprecated-declarations)add_compile_options("-DPLUGIN_NAME=${PLUGIN_NAME}")add_definitions(-DENABLE_DVPP_INTERFACE)add_library(${TARGET_LIBRARY} SHARED MxCenterfacePostProcessor.cpp MxCenterfacePostProcessor.h)target_link_libraries(${TARGET_LIBRARY} glib-2.0 gstreamer-1.0 gobject-2.0 gstbase-1.0 gmodule-2.0 glog)target_link_libraries(${TARGET_LIBRARY} mxpidatatype plugintoolkit mxbase mindxsdk_protobuf )#target_link_libraries(${TARGET_LIBRARY} -Wl,-z,relro,-z,now,-z,noexecstack -s)```#### 实现人脸框检测的cpp文件首先,根据任务类型,选择SDK已经支持的后处理基类去派生一个新的子类,这些后处理基类分别为目标检测,分类任务,语义分割,文本生成。这些基类都继承自相同的父类——PostProcessBase。人脸检测框后处理插件继承的类为ObjectPostProcessBase;```c++class MxCenterfacePostProcessor : public MxBase::ObjectPostProcessBase```**实现基类方法:**inti函数:![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654844885252208174.png)Process函数:![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654844937949430601.png)| 输入参数 | 解释 || ----------------- | ------------------------------------------------------------ || tensors | 上游插件的输出,当把后处理插件编排在模型推理插件之后,这里tensors便是模型的输出。 || objectInfos | MxBase::ObjectInfo是api内置的目标框数据结构,后处理的结果需要存放在里面。 || resizedImageInfos | 图片size信息,包含原图的宽高和resize之后的宽高,用于后处理逻辑的计算。 || configParamMap | 从配置文件读取得到的map,在init函数读入。 |在Process函数中,首先调用父类的CheckAndMoveTensors()接口,对tensors的形状进行校验并搬运内存至Host侧。然后再调用后处理业务函数,这里featLayerData包含单张图片四个输出对应void*指针,数据都是以一维数组形式存储,具体的数据size、type及数据含义需要参考论文原作者的实现。在CenterFace模型中,MxCenterfacePostProcessor后处理插件逻辑包括根据置信度筛选数据、计算出人脸框、使用nms算法对人脸框去重这一系列操作,最终将结果放入objInfo中。后处理开发完成后,增加一个对外的接口如GetObjectInstance(),以便于让业务流中后处理插件的factory能加载生成的so文件。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654845275412513003.png)> 这里对外接口根据继承的基类有不同名称,通常为Get+基类名称+Instance#### 生成插件在Clion中点击运行按钮,即可生成对应so文件:![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843383135724620.png)生成插件后即可在pipeline配置文件中使用,如下:```json"mxpi_objectpostprocessor1": { "props": { "dataSource": "mxpi_tensorinfer0", "postProcessConfigPath": "../model/centerface.cfg", "postProcessLibPath": "../plugins/lib/plugins/libmxpi_centerfacekeypointpostprocessor.so" }, "factory": "mxpi_keypointpostprocessor", "next": "XXXX"}```factory为后处理插件基类对应插件的名字,后处理插件实际也是用的内置的插件,开发后处理插件相当于在插件的基础上编写,仅需关注业务的具体逻辑代码,如databuff的传递等其他操作由插件完成。如果不知道factory该填什么,可以在MX_SDK_HOME/lib/plugins下查找。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843337671691251.png)## 6 运行过程展示### 6.1 本地IDE运行第一步:配置FaceDetectPostProcessor、KeyPointPostProcessor、C++中CMakeLists文件的SDK路径,![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843287648131732.png)第二步:设置cmake编译的环境变量,编译插件。在设置>构建、执行、部署>cmake界面,找到环境,配置如下:```bashLD_LIBRARY_PATH=/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:/usr/local/Ascend/driver/lib64/;```![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843212875646606.png)加载FaceDetectPostProcessor中的CMakeLists文件,第一次加载CmakeLists文件时,选择Load Cmake Project,之后重新加载选择Reload Cmake Project。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843151269379700.png)加载CmakeLists文件之后,首先在①号位置选择对应的插件,这里选择的是FaceDetectPostProcessor插件,点击②编译![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843096561305478.png)点击之后,程序运行,运行完成会显示Finished,得到一个cmake-build-debug文件夹,并且远程会有编译生成的文件夹。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654843018575639101.png)按以上方法将另一个插件KeyPointPostProcessor中CMakeLists文件进行编译,生成的插件都会存放在plugins目录下。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842947888770306.png)第三步:编译、运行main文件接着加载main.cpp对应的CmakeLists,加载成功会自动生成Main的运行配置,准备图片test.jpg放置在C++目录下,编辑Main运行配置,添加程序实参,并配置环境变量,点击确定。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842914614432881.png)![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842871463441960.png)环境变量如下,其中的MX_SDK_HOME需要替换为昇腾服务器上的绝对路径。```bashMX_SDK_HOME=${SDK安装路径}LD_LIBRARY_PATH=${MX_SDK_HOME}/lib:${MX_SDK_HOME}/opensource/lib:${MX_SDK_HOME}/opensource/lib64:/usr/local/Ascend/ascend-toolkit/latest/acllib/lib64:/usr/local/Ascend/driver/lib64/GST_PLUGIN_SCANNER=${MX_SDK_HOME}/opensource/libexec/gstreamer-1.0/gst-plugin-scannerGST_PLUGIN_PATH=${MX_SDK_HOME}/opensource/lib/gstreamer-1.0:${MX_SDK_HOME}/lib/plugins```然后点击运行。成功运行后,可在远程的C++/result文件夹中查看结果。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842804364324769.png)### 6.2 远程命令行运行相对于本地运行,远程运行方式会更加简洁方便,具体参考以下操作第一步:进入到远程文件夹![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842773435869432.png)第二步:输入命令:```bashbash build.sh```此时它会自动将三个CMakeLists全部编译。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842685849257801.png)第三步:进入C++文件夹,输入命令运行run.sh文件,假如测试图片在run.sh同级目录,名字为test.jpg,测试命令如下:```bashcd C++bash run.sh test.jpg #这里相对路径是相对C++/Main可执行文件的。#或者准备图片放置在文件夹下,以下命令会依次对文件夹下的图片进行检测。bash run.sh images```![1653201293644](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842550524626553.png)第四步:在result文件中查看运行结果,刷新可以看到是否是刚刚运行生成的结果。![1653201442455](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842482875232996.png)## 7 常见问题- 报错:$'\r': command not found![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842427382418968.png) 解决方案:原因是windows和linux换行符不一致,在CLion右下角将CRLF改成LF,然后上传到远程服务器。![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842131426441396.png)
  • [问题求助] 复现摄像头人脸检测例程报错
    【功能模块】复现人脸检测例程报错,数据由摄像头输入,今天早上可以跑通,但是晚上再测试时,就出现了报错【截图信息】设置的ip如下:尝试更改过以上的两个ip,但是没有效果,请问要如何解决呢?早上才跑通的,晚上测试就不行了,也不大清楚是不是误改了哪里。十分感谢!【日志信息】(可选,上传日志内容或者附件)
  • [行业资讯] 可怕!一张照片就能破解人脸识别
    根据技术人员演示,只要拿到一张人脸图片,运用深度合成的办法,就能让人脸动起来,以假乱真,轻松攻破一些人脸识别系统。在专业技术人员的眼中,目前大部分人脸识别技术的算法并不是牢不可破。(央视新闻)转载于CSDN
  • [技术干货] 人脸识别:使用Scikit-Learn构建人脸识别系统
    什么是人脸识别人脸识别是将未知个体的人脸与存储记录数据库中的图像进行比较的任务。映射可以是一对一或一对多,这取决于我们是在运行人脸验证还是人脸识别。在本教程中,我们感兴趣的是构建一个面部识别系统,该系统将验证图像(通常称为探测图像)是否存在于预先存在的面部数据库(通常称为评估集)中。直觉建立这样一个系统涉及四个主要步骤:1.检测图像中的人脸可用的人脸检测模型包括MTCNN、FaceNet、Dlib等。2.裁剪和对齐人面OpenCV库提供了此步骤所需的所有工具。3.查找每个面的向量表示由于程序不能直接处理jpg或png文件,我们需要某种方法将图像转换为数字。在本教程中,我们将使用Insightface模型为人脸创建多维(512-d)嵌入,从而封装与人脸相关的有用语义信息。要使用单个库处理所有三个步骤,我们将使用insightface。特别是,我们将使用Insightface的ArcFace模型。InsightFace是一个开源的深度人脸分析模型,用于人脸识别、人脸检测和人脸对齐任务。4.比较嵌入一旦我们将每个唯一的人脸转换成一个向量,比较特征就归结为比较相应的嵌入。我们将利用这些嵌入来训练scikit-learn模型。另外,如果你想继续,代码可以在Github上找到:https://github.com/V-Sher/Face-Search。安装程序创建虚拟环境(可选):python3 -m venv face_search_env激活此环境:source face_search_env/bin/activate此环境中的必要安装:pip install mxnet==1.8.0.post0pip install -U insightface==0.2.1pip install onnx==1.10.1pip install onnxruntime==1.8.1更重要的是,完成pip安装insightface后:从onedrive下载antelope模型版本。(它包含两个预训练的检测和识别模型)。把它放在*~/.insightface/models/下,所以在~/.insightface/models/antelope.onnx*上有onnx模型。这是正确完成设置后的外观:如果你查看antelope目录,你会发现用于人脸检测和识别的两个onnx模型:注意:自从上周insightface 0.4.1的最新版本发布以来,安装并不像我希望的那样简单(至少对我来说)。因此,我将在本教程中使用0.2.1。将来,我将相应地更新Github上的代码。如果你被卡住了,请看这里的说明。数据集我们将使用Kaggle上提供的Yale人脸数据集,该数据集包含15个人的大约165张灰度图像(即每个人大概11张唯一图像)。这些图像由各种表情、姿势和照明组成。获得数据集后,继续将其解压缩到项目中新创建的数据目录中(请参阅Github上的项目目录结构)开始如果你想继续,可以在Github上找到Jupyter笔记本:https://github.com/V-Sher/Face-Search/blob/main/notebooks/face-search-yale.ipynb。导入import osimport pickleimport numpy as npfrom PIL import Imagefrom typing import Listfrom tqdm import tqdmfrom insightface.app import FaceAnalysisfrom sklearn.neighbors import NearestNeighbors加载Insightface模型安装insightface后,我们必须调用app=FaceAnalysis(name="model_name")来加载模型。由于我们将onnx模型存储在antelope目录中:app = FaceAnalysis(name="antelope")app.prepare(ctx_id=0, det_size=(640, 640))生成Insightface嵌入使用insightface模型为图像生成嵌入非常简单。例如:# 为图像生成嵌入img_emb_results = app.get(np.asarray(img))img_emb = img_emb_results[0].embeddingimg_emb.shape------------OUTPUT---------------(512,)数据集在使用此数据集之前,我们必须修复目录中文件的扩展名,使文件名以.gif结尾。(或.jpg、.png等)。例如,以下代码段将文件名subject01.glasses更改为subject01_glasses.gif。# 修复扩展名YALE_DIR = "../data/yalefaces"files = os.listdir(YALE_DIR)[1:]for i, img in enumerate(files):# print("original name: ", img)new_ext_name = "_".join(img.split(".")) + ".gif"# print("new name: ",  new_ext_name)os.rename(os.path.join(YALE_DIR, img), os.path.join(YALE_DIR, new_ext_name))接下来,我们将数据分为评估集和探测集:每个受试者90%或10张图像将成为评估集的一部分,每个受试者剩余的10%或1张图像将用于探测集中。为了避免采样偏差,将使用名为create_probe_eval_set的辅助函数随机选择每个对象的探测图像。它将包含属于特定主题的11个图像(文件名)的列表作为输入,并返回长度为1和10的两个列表。前者包含用于探测集的文件名,而后者包含用于评估集的文件名。def create_probe_eval_set(files: List):# 选择0和len(files)-1之间的随机索引random_idx = np.random.randint(0,len(files))probe_img_fpaths = [files[random_idx]]eval_img_fpaths = [files[idx] for idx in range(len(files)) if idx != random_idx]return probe_img_fpaths, eval_img_fpaths生成嵌入create_probe_eval_set返回的两个列表都按顺序送到名为generate_embs的助手函数。对于列表中的每个文件名,它读取灰度图像,将其转换为RGB,计算相应的嵌入,最后返回嵌入以及图像标签。def generate_embs(img_fpaths: List[str])embs_set = list()embs_label = list()for img_fpath in img_fpaths:  # 读取灰度图        img = Image.open(os.path.join(YALE_DIR, img_fpath))        img_arr = np.asarray(img)          # 将灰度转换为RGB        im = Image.fromarray((img_arr * 255).astype(np.uint8))        rgb_arr = np.asarray(im.convert('RGB'))             # 生成Insightface嵌入        res = app.get(rgb_arr)                  # 将emb添加到eval set        embs_set.append(res)                  # 添加标签到eval_label set        embs_label.append(img_fpath.split("_")[0])          return embs_set, embs_label现在我们有了一个生成嵌入的框架,让我们继续使用generate_embs()为探测和评估集创建嵌入。# 排序文件files = os.listdir(YALE_DIR)files.sort()eval_set = list()eval_labels = list()probe_set = list()probe_labels = list()IMAGES_PER_IDENTITY = 11for i in tqdm(range(1, len(files), IMAGES_PER_IDENTITY), unit_divisor=True): # 忽略在files[0]的README.txt文件# print(i)probe, eval = create_probe_eval_set(files[i:i+IMAGES_PER_IDENTITY])# 存储eval embs和标签eval_set_t, eval_labels_t = generate_embs(eval)eval_set.extend(eval_set_t)eval_labels.extend(eval_labels_t)# 存储探测embs和标签probe_set_t, probe_labels_t = generate_embs(probe)probe_set.extend(probe_set_t)probe_labels.extend(probe_labels_t)需要考虑的几件事:os.listdir返回的文件是完全随机的,因此第3行的排序很重要。不带排序和带排序的os.listdir输出:[可选]如果我们使用sklearn提供的分层训练测试功能,我们本可以替换create_probe_eval_set函数,去掉forloop,并简化上述代码段中的几行。然而,在本教程中,我将清晰性置于代码简单性之上。通常情况下,insightface无法检测到人脸,并随后为其生成空嵌入。这解释了为什么probe_setor eval_set列表中的某些条目可能为空。重要的是我们要过滤掉它们,只保留非空值。为此,我们创建了另一个名为filter_empty_embs的助手函数:def filter_empty_embs(img_set: List, img_labels: List[str]):# 在insightface无法生成嵌入的地方过滤filtering where insightface could not generate an embeddinggood_idx = [i for i,x in enumerate(img_set) if x]if len(good_idx) == len(img_set):        clean_embs = [e[0].embedding for e in img_set]        clean_labels = img_labels        else:        # 保留good_idx        clean_labels = np.array(img_labels)[good_idx]        clean_set = np.array(img_set, dtype=object)[good_idx]        # 生成embs        clean_embs = [e[0].embedding for e in clean_set]return clean_embs, clean_labels它将图像集(probe_set或eval_set)作为输入,并删除insightface无法生成嵌入的元素(参见第6行)。随后,它还会更新标签(probe_labels或eval_labels)(请参见第7行),以使集合和标签具有相同的长度。最后,对于评估集和探测集中,我们可以获得512维嵌入:evaluation_embs, evaluation_labels = filter_empty_embs(eval_set, eval_labels)probe_embs, probe_labels = filter_empty_embs(probe_set, probe_labels)assert len(evaluation_embs) == len(evaluation_labels)assert len(probe_embs) == len(probe_labels)有了这两套设备,我们现在可以使用Sklearn库中实现的一种流行的无监督学习方法来构建人脸识别系统。创建人脸识别系统我们使用.fit训练最近邻模型,评估嵌入为X。这是一种用于无监督最近邻学习的简洁技术。注:一般来说,距离可以是任何度量单位,如欧几里德、曼哈顿、余弦、闵可夫斯基等。# 最近邻学习方法nn = NearestNeighbors(n_neighbors=3, metric="cosine")nn.fit(X=evaluation_embs)# 保存模型到磁盘filename = 'faceID_model.pkl'with open(filename, 'wb') as file:pickle.dump(nn, file)# 过了一段时间…# 从磁盘加载模型# with open(filename, 'rb') as file:#     pickle_model = pickle.load(file)因为我们正在实施一种无监督的学习方法,请注意,我们没有将任何标签传递给fit方法,即评估标签。我们在这里所做的就是将评估集中的人脸嵌入映射到一个潜在空间中。为什么??简单回答:通过提前将训练集存储在内存中,我们可以在推理过程中加快搜索最近邻的速度。它是如何做到这一点的?简单回答:在内存中以优化的方式存储树是非常有用的,尤其是当训练集很大并且搜索新点的邻居时,计算成本会很高。基于邻域的方法被称为非泛化机器学习方法,因为它们只是“记住”其所有训练数据推理对于每个新的探测图像,我们可以通过使用nn.neights方法搜索其前k个邻域来确定它是否存在于评估集中。例如,# 测试图像的实例推理dists, inds = nn.kneighbors(X = probe_img_emb.reshape(1,-1),                            n_neighbors = 3,                            return_distances = True                            )如果评估集中返回索引(IND)处的标签与图像的原始/真实标签完全匹配,则我们知道我们在验证系统中找到了自己的脸。我们已经将上述逻辑包装到print_ID_results方法中。它将探测图像路径、评估集标签和详细标志作为输入,以指定是否应显示详细结果。def print_ID_results(img_fpath: str, evaluation_labels: np.ndarray, verbose: bool = False):   img = Image.open(img_fpath)img_emb = app.get(np.asarray(img))[0].embedding# 从KNN获取预测dists, inds = nn.kneighbors(X=img_emb.reshape(1,-1), n_neighbors=3, return_distance=True)# 获取邻居的标签pred_labels = [evaluation_labels[i] for i in inds[0]]# 检查dist是否大于0.5,如果是,打印结果no_of_matching_faces = np.sum([1 if d <=0.6 else 0 for d in dists[0]])if no_of_matching_faces > 0:        print("Matching face(s) found in database! ")        verbose = Trueelse:        print("No matching face(s) not found in database!")        # 打印标签和相应的距离 if verbose:        for label, dist in zip(pred_labels, dists[0]):print(f"Nearest neighbours found in the database have labels {label} and is at a distance of {dist}")这里需要注意的几个重要事项:IND包含评估标签集中最近邻的索引(第6行)。例如,inds=[[2,0,11]]意味着评估中索引=2处的标签被发现最靠近探测图像,然后是索引=0处的标签。因为对于任何图像,nn.neighbors都会返回非空响应。我们要过滤一些,如果返回的距离小于或等于0.6(行12),我们只考虑这些结果。(请注意,0.6的选择完全是任意的)。例如,继续上面的例子,其中Inds= [[2,0,11 ] ]和例子= [[ 0.4,0.6,0.9 ] ],我们将只考虑在索引=2和索引=0,因为最后一个邻居的距离太大。作为一个快速的健康检查,让我们看看当我们输入婴儿的脸作为探测图像时系统的响应。正如所料,它显示没有找到匹配的脸!但是,我们将verbose设置为True,因此我们可以在数据库中看到其伪近邻的标签和距离,所有这些都非常大(>0.8)。人脸识别系统的评价测试此系统是否良好的方法之一是查看前k个邻居中存在多少相关结果。相关结果是真实标签与预测标签匹配的结果。该度量通常称为k处的精确度,其中k是预先确定的。例如,从探测集中选择一个图像(或者更确切地说是一个嵌入),其真实标签为“subject01”。如果nn.Neighers为该图像返回的前两个pred_labels为['subject01','subject01'],则表示k处的精度(p@k)k=2时为100%。类似地,如果pred_labels中只有一个值等于“subject05”,p@k将是50%,依此类推…dists, inds = nn.kneighbors(X=probe_embs_example.reshape(1, -1),n_neighbors=2, return_distance=True)pred_labels = [evaluation_labels[i] for i in inds[0] ]pred_labels----- OUTPUT ------['002', '002']让我们继续计算整个探测集上p@k的平均值:# 探测集上的推理dists, inds = nn.kneighbors(X=probe_embs, n_neighbors=2, return_distance=True)# 计算平均p@kp_at_k = np.zeros(len(probe_embs))for i in range(len(probe_embs)):true_label = probe_labels[i]pred_neighbr_idx = inds[i]pred_labels = [evaluation_labels[id] for id in pred_neighbr_idx]pred_is_labels = [1 if label == true_label else 0 for label in pred_labels]p_at_k[i] = np.mean(pred_is_labels)p_at_k.mean()------ OUTPUT --------0.990%!还可以,但肯定可以继续改进。
  • [问题求助] 【Atlas 200】【手势识别】如何将人脸识别例程的功能更改为手势识别?
    人脸识别Demo已经可以跑通,现在想根据这个例程,仍然使用Presenterserver,只是把功能从人脸识别更改为手势识别,应该更改那些地方呢?人脸识别用的是这个cplusplus/level2_simple_inference/n_performance/1_multi_process_thread/multi_channels_rtsp · Ascend/samples - 码云 - 开源中国 (gitee.com)
  • [问题求助] 【Atlas200DK产品】【人脸检测样例运行功能】摄像头调用权限使用不了
    【功能模块】【操作步骤&问题现象】1、执行bash sample_run.sh2、显示 No module named 'cameracapture'【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [行业资讯] 苹果人脸识别无效?多多用自己的脸打开了孙莉手机
    近日,中国内地女演员孙莉的手机在识别其女儿时,成功解锁。通过孙莉微博了解,其使用的手机为iPhone 12 Pro Max。(图片来源于“多妈7788”微博)据了解,iPhone 12系列手机依旧延用颇受好评的Face ID功能,用户只需将前置摄像头对准面部,就能轻松实现刷脸解锁、支付等操作,而Face ID主要采用了3D结构光技术。3D结构光是将激光散斑图像投射到物体表面,由摄像头接收采集物体表面反射的信息,根据物体造成的光信号变化计算出物体位置和深度信息,识别精度能达到0.5mm,甚至更高的精度。简单来说,这项技术通过发射具有一定结构特征的光线,投射到用户面部。再通过摄像头感知面部的散斑,从而描绘出你面部的结构。这些散斑主要反馈的是面部特征的深度信息,所以相当于给用户的脸建了一个3D模型。基于这一特征,在金融领域中,3D结构光也得到进一步的应用:支付宝率先与3D结构光企业达成合作,并研发出基于3D结构光的刷脸支付。但对于苹果而言,其在不断强调个人隐私的情况下,号称Face ID是最安全的系统,但在本次事件中,却依旧出现被解锁等问题。而相类似的问题并不少——在iPhone X刚使用Face ID时,就已经出现母子之间可以相互识别并解锁的问题。
  • [问题求助] 【200DK】【利用网络摄像头运行人脸识别案例】protobuf报错
    【功能模块】在运行时protobuf报错检查依赖项发现没问题【操作步骤&问题现象】1、报错信息。protobuf依赖项信息2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] 【200DK】【利用网络摄像头运行人脸识别案例】显示不出图像
    【功能模块】最后的调试了结果又报错了。。。运行多路人脸识别案例报错信息如下:【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] 【200DK】【利用网络摄像头运行人脸识别案例】显示不出图像
    【功能模块】最后的最后还出错了........【操作步骤&问题现象】【截图信息】【日志信息】(可选,上传日志内容或者附件)
总条数:280 到第
上滑加载中