• [问题求助] 人脸比对前端请求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 
  • [技术干货] 智慧园区集成资产说明——添加人脸图片接口说明
    功能描述该接口用于向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" } }
  • [问题求助] 第三方登录可否增加人脸识别登录配置
    在云速建站后台管理的第三方帐号登录中,能不能增加人脸识别登录配置?以解决会员实名认证的问题,
  • [其他] ModelArts奉上的青春云毕业礼 ——用AI为毕业生们“拍”一张专属的毕业照
    去年同醉,酴花下,健笔赋新词。今年君去,酴欲破,谁与醉为期。六月初夏,又是一年毕业季,喜悦总是与感伤并存:我们为迈入新的人生阶段而喜悦,也为与朝夕相处之人分别而不舍。一张毕业照,寄托着多少人的思念。对于很多人来说,有一张身穿学士服的毕业照,学生生活才算完整,它给三年、四年甚至更长时间的高校生活画上了一个圆满的句号。有创意的毕业照也常常能吸引不少人的注意,几乎每年,创意毕业照都能登上全网热搜。可是今年的毕业季却成了许多人的遗憾,出于疫情防控的需要,很多学校取消了毕业典礼,甚至封闭校园,不允许学生返校。对于毕业生来说,寒假时轻轻松松的"下个月见",却可能成为对彼此说的最后一句话。很多人说,他们可能失去了唯一一次穿学士服、拍毕业照的机会。为了弥补这样的遗憾,不少人"大显神通":有的人用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 用树莓派摄像头进行人脸识别的案例
    【功能模块】Atlas 200 DK外接树莓派摄像头采集人脸后,进行人脸识别的案例,请问哪里有,在gitee/samples里没有找到,麻烦您可以发下具体的链接么,不胜感激【操作步骤&问题现象】1、2、【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [问题求助] 【Atlas 200 DK】【目标识别】如何将multi_channels_rtsp中的人脸识别换成自己的识别物?
    如何将multi_channels_rtsp中的人脸识别换成自己的识别物(cup,pencil,telephone等)?具体是在哪里改
  • [MindX SDK] 实时人脸检测案例分享
    # 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单片机设计的红外测温仪(带人脸检测)
    # 基于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,微博,微信三种外可不可以增加人脸识别和指纹登录?
  • [问题求助] 人脸识别编译警告,求助!!!!
    又找不到包的警告,求助!!!
  • [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 开发环境配置 请参考官方技术文档:[IDE开发环境搭建](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工具,但未配置环境变量,可参考如下配置: ```bash 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 export LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATH export 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进入模型存放的目录,执行如下命令: ```bash atc --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 ``` 以上命令转成具有多档位的动态分辨率模型,转成单档位可使用如下命令: ```bash atc --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,因此配置了色域转换的相关属性。具体配置文件如下 ```yaml aipp_op{ aipp_mode: static crop: false input_format : YUV420SP_U8 #非动态分辨率请设置具体的宽高 src_image_size_h : 0 src_image_size_w : 0 csc_switch : true rbuv_swap_switch : false matrix_r0c0 : 256 matrix_r0c1 : 0 matrix_r0c2 : 359 matrix_r1c0 : 256 matrix_r1c1 : -88 matrix_r1c2 : -183 matrix_r2c0 : 256 matrix_r2c1 : 454 matrix_r2c2 : 0 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 min_chn_1 : 0 min_chn_2 : 0 var_reci_chn_0: 1.0 var_reci_chn_1: 1.0 var_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文件 ```cmake cmake_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 PostProcessBases/PostProcessDataType.h> #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如下: ```cmake cmake_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界面,找到环境,配置如下: ```bash LD_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需要替换为昇腾服务器上的绝对路径。 ```bash MX_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-scanner GST_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) 第二步:输入命令: ```bash bash build.sh ``` 此时它会自动将三个CMakeLists全部编译。 ![](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20226/10/1654842685849257801.png) 第三步:进入C++文件夹,输入命令运行run.sh文件,假如测试图片在run.sh同级目录,名字为test.jpg,测试命令如下: ```bash cd 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)
总条数:372 到第
上滑加载中