1.平台简介
客户行为数字化平台(Gamma Insight Platform)是一个面向开发者的平台,使用的开发语言是C++。该平台旨在帮助开发者解决AI算法落地门槛高,成本高,实现周期长,高度定制化造成的重复开发等问题,建立健全一个有用、高效、支持完备的平台。在我们的平台上集成和开放了很多计算机视觉方向的AI能力,在网页上就可以轻松地实现对这些能力的调用,再加上平台本身提供的软件工具,可以很方便地打造面向自己的业务、解决自己需求的软件哦。
- 利用此平台网页开发你需要:
- c++
- 能联网的浏览器(xjk-dev or pinganNet)
- 利用此平台部署在本地你需要:
- 有GPU的主机
- RTSP/USB摄像头(非必须,可以有待处理的视频、图片文件等)
- 主机安装Ubuntu 16.04
- 利用此平台进行后端开发(to 平台维护者),除上述以外,你需要:
- 代码网址
- 安装依赖
2.平台初印象
step1. 登录地址:http://192.168.137.83:8081(内网,xjk-dev)
或者http://192.168.35.192:8081(内网,pinganNet)
因网络速度与稳定性原因,目前阶段会在网络之间切换,两个都试试。
登录账户密码为:admin:123456
step2. 点击新建技能,直接感受保存、编译、运行、查看预览效果
可以看到,当前这个功能的结果,不仅用人脸框追踪人脸的动向,而且识别出来这张人脸是谁。
为了体验平台上目前已经支持的功能,请查看 网址https://gitlab.com/GammaLab_AI/freeforward_build/tree/master/etc
下的文件并将其内容替换网页端已有的json文件即可。目前可运行的包括:
1) ff.gpu.face.json ——人脸检测
2)ff.facefeature.json ——人脸识别
3)ff.objectDetect.json ——物体识别
4)ff.person.interest.json ——人脸识别 + 微表情识别
5)ff.face.count.json ——人脸检测 + tracking工具
6)ff.attention.identify.json ——人脸识别 + 头部姿态
step 3. 当你发现有一个功能适用场景与你想要的刚好匹配,想要下载这个执行体并部署于本地,处理你本地的视频文件、图片或者视频流的话,你需要:
第一步:布置好本地环境(详见Chap 4-1)
第二步:下载这个执行体(下载地址:方法详见Chap 4-2)
第三步:解压并修改配置文件 (配置文件详细解析见Chap 4-3)
第四步:用终端打开当前路径,执行指令:./freeforward -c freeforward.json(文件名可自定义)
比较理想的结果如下图所示,能够处理你本地的视频流输入啦
至此为止,你已经获得一个完整的能运行在你本地的技能啦!
(或许并不想要下载到本地,而是直接在线上进行分析拿到日志、表格等统计信息即可呢?使用者上传视频,获取结果,我们获得视频,涉及付费模式)
3.平台初体验
现在你已经看到平台上能做到的功能啦,利用这些功能的组合或者部分中间结果可以实现你想要的需求,那么可以开始动手打造你自己的一个技能啦!下面就以“数人头”这个需求为例展示给各位如何实现吧。
(以后打开每一个算法模块都要呈现完整的算法的类名,算法技能对外提供的技能和接口,可以查看但不能修改源文件,修改源文件需要权限设置)
1)首先查看能用的算法和工具吧。数人头的需求本身并没有针对“这个人是谁”提出需求,也就是说我们只要用到“人脸检测”模型,而不需要用到“人脸识别”模型。“人脸检测”推荐使用已有的封装类:CFaceWrapperSkillGpu。而除此之外,如果你有相关的工程经验,那么就会知道,要对同一个人脸进行不重复的计数,并能够排除“掉帧”或“个别帧检测人脸失败”的情况,那么“人脸追踪”功能就是必不可少的。平台上已经提供了IOU_Tracking模块,类名为IOU_Tracking,能帮助我们的技能实现有效的平滑效果;
(头文件批量包含的问题需要修改)
2)知道了平台能提供的功能,就要定制我们自己的需求啦。假设我们希望这个技能的需求如下:
需求明细项 | 呈现方式/功能 | 需实现方式 |
---|---|---|
处理材料 | 视频文件 | 通过json文件配置 |
视觉要求 | 在视频处理中实时显示已经出现的人头数目 | 定义user_metadata内容 |
算法模型 | 实现对图像的“人脸检测”功能 | 利用类:CFaceWrapperSkillGpu |
工具类 | 实现人脸框追踪 | 利用类:IOU_Tracking |
按照这个需求我们可以确定实现的逻辑:设立计数器初始值为0,当检测到人脸时,将人脸框(坐标、大小)送到IOU_Tracking模块进行判断,是不是可以认为是一个全新的人脸框。如果是,计数器加1,否则不变。
3)进行网页端开发。在我们的设计中,开发者核心需要实现的函数是infer,以及postInfer两个。大部分已经填写好,skill.h文件修改内容如下所示:
插入代码
skill.h
#ifndef CUSTOM_SKILL_H_
#define CUSTOM_SKILL_H_
#pragma once
#include <vector>
#include <string>
#include <unordered_map>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include "../skill.h"
#include "./vip_skill/faceWrapper/face_wrapper_skill_gpu.h"
#include "../../utilities/faceRelated/IOU_Tracking.h"
struct FFRuntime;
class IInferer;
namespace cv {
class Mat;
}
class CCustomSkill : public ISkill {
private:
FFRuntime * ffrt_;
std::vector<std::string> user_metadata_;
std::string interface_path_;
bool isLocalMode_;
public:
explicit CCustomSkill(const Json::Value & params, FFRuntime * ffrt);
~CCustomSkill();
public:
virtual int PreInfer(int pipeid, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs);
virtual int Infer(int pipeid, std::vector<IInferer *> & inferers, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs);
virtual int PostInfer(int pipeid, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs);
public:
virtual int NotifiedEnded(int pipeid, const std::vector<int>& camids, bool ended=false);
//==================below is new added=============================================
private:
CFaceWrapperSkillGpu* face_detector_ = NULL;
int face_count_ ;
IOU_Tracking* iou_tracker_ = NULL;
//==================above is new added=============================================
};
#endif
由代码中可见,实际新增内容是在末尾处定义的三个private变量,CFaceWrapperSkillGpu型指针用以实现人脸检测,而face_count_用以实现持续计数充当计数器,IOU_Tracking用以实现人脸框追踪。
skill.cpp文件修改内容则如下所示:
#pragma GCC diagnostic ignored "-Wsign-compare"
#include <vector>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include "../../runtime/inferer/inferer.h"
#include "../../runtime/view/view.h"
#include "../../runtime/db/db.h"
#include "../../runtime/ffrt.h"
#include "../../runtime/view/guiview/guiview.h"
#include <json/json.h>
#include <sys/stat.h>
#include "custom_skill.h"
#include "./vip_skill/faceWrapper/face_wrapper_utilities.h"
using namespace FACEWRAPPER2;
CCustomSkill::CCustomSkill(const Json::Value & params, FFRuntime * ffrt)
: ffrt_(ffrt)
{
std::string file_path = params["user_path"].asString();
interface_path_ = params["interface_data_path"].asString();
isLocalMode_ = (params["isLocalMode"].asInt()==1)?true:false;
int ret = mkdir(interface_path_.c_str(), 0777);
if(ret){
std::cout << "[face_count_skill] CCustomSkill: mkdir fail" << ret <<std::endl;
}
//==================below is new added=============================================
face_detector_ = new CFaceWrapperSkillGpu(params, ffrt);
iou_tracker_ = new IOU_Tracking();
face_count_ = 0;
//==================above is new added=============================================
}
CCustomSkill::~CCustomSkill()
{
}
int CCustomSkill::PreInfer(int pipeid, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs)
{
return 0;
}
int CCustomSkill::Infer(int pipeid, std::vector<IInferer *> & inferers, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs)
{
//==================below is new added=============================================
if(!face_detector_ && isLocalMode_)
{
std::cout<<"face_detector_ has not be constructed! Attention!"<<std::endl;
return -1;
}
if(!iou_tracker_ && isLocalMode_)
{
std::cout<<"iou_tracker_ has not be constructed! Attention!"<<std::endl;
return -1;
}
int ret = face_detector_->Infer(pipeid, inferers, camids, frameids, {imgs});
if(ret)
{
return -2;
}
std::vector < cv :: Rect > faceBoxes;
face_detector_->getFaceBoxes( faceBoxes);
std::vector < std :: vector < cv :: Point >> landmarks;
face_detector_->getLandmarks(landmarks);
std::vector<std::string> box_ids;
std::vector<bool> newid_state;
int ret2 = iou_tracker_->tracking(faceBoxes, box_ids, newid_state);
if(ret2 == false){
std::cout << "[face_count_skill] [infer] iou_tracker_ : mkdir fail" << ret <<std::endl;
}
int i = 0;
for (std::vector<std::string> :: iterator it = box_ids.begin(); it != box_ids.end(); it++)
{
if (newid_state[i]) {
face_count_ ++;
}
}
//==================above is new added=============================================
return 0;
}
int CCustomSkill::PostInfer(int pipeid, const std::vector<int>& camids, const std::vector<int> & frameids, const std::vector<cv::Mat> & imgs)
{
cv::Mat img_out;
if(ffrt_->view){
CGuiView* viewer = (CGuiView*)ffrt_->view;
user_metadata_.clear();
//==================below is new added=============================================
user_metadata_.push_back("ENTER NUM:"+std::to_string(face_count_));
viewer->displayG(imgs.at(0),user_metadata_,img_out, isLocalMode_);
//==================above is new added=============================================
if(!isLocalMode_){
std::string file_name = interface_path_ +"/" + std::to_string(frameids.at(0)) + ".jpg";
cv::imwrite(file_name, img_out);
}
}
return 0;
}
int CCustomSkill::NotifiedEnded(int pipeid, const std::vector<int>& camids, bool ended)
{
return 0;
}
由文件中修改可见,实际需要修改三个函数:构造函数、Infer也就是调用前向的函数、以及后处理函数PostInfer。
修改json文件(配置文件详细解析见Chap 4-3):
{
"skill":{
"type": "custom",
"param": {
"user_path":"/media/zhangjing/data/Project/freeforward_build/models/face_one2N_model/",
"interface_data_path":"shared",
"isLocalMode": 1
}
},
"pipes": [
{
"inputtest": {
"type": "usb",
"param": {
"mrl": "v4l2:///dev/video0",
"cords": [ 1.0, 1.0, 1.0, 2.0, 3.0 ]
}
},
"input": {
"type": "rtsp",
"param": {
"mrl": "file:///var/gammalab/freeforward_build/test_video/count.mp4",
"cords": [ 1.0, 1.0, 1.0, 2.0, 3.0 ]
}
},
"inferers": [
{
"type": "caffewrapper",
"param": {
"cpu": false,
"id" : "pNet",
"weight": "./models/face_one2N_model/det1.caffemodel",
"net": "./models/face_one2N_model/det1-memory_gpu.prototxt"
}
},
{
"type": "caffewrapper",
"param": {
"cpu": false,
"id" : "RNet",
"weight": "./models/face_one2N_model/det2.caffemodel",
"net": "./models/face_one2N_model/det2-gpu.prototxt"
}
},
{
"type": "caffewrapper",
"param": {
"cpu": false,
"id" : "ONet",
"weight": "./models/face_one2N_model/det3.caffemodel",
"net": "./models/face_one2N_model/det3-gpu.prototxt"
}
}
],
"DUMMY": { }
}
],
"view": {
"type": "guiview",
"param": { }
},
"db": {
"type": "redis",
"param": { }
},
"logger": {
"type": "consolelogger",
"param": { }
},
"DUMMY": { }
}
看起来很繁琐但其实其中满满的都是套路哦,先直接使用,下一章再详解。
4)好啦,核心的部分已经搞定啦,快来看看我们第一个技能的能不能运行,以及效果如何吧。一路点击保存、编译、运行、查看预览效果。
须知(for now):
a.编译以后得到确切的编译成功信息后再点运行这一步。这个信息可以点击查看编译信息得到
b.运行以后大致等2min左右的时间后再查看预览效果,否则结果未完全生成;
看,按照我们的预期运行起来了~
5)然后就可以下载到本地实际试试啦。假设作为开发者的你已经布置好本地环境了(详见Chap 4-1),那么剩下的操作步骤与Chap2的step3没有什么区别啦。我们拿本地的一个视频测试一下(示例文件地址:https://gitlab.com/GammaLab_AI/freeforward/tree/master/shared/testvideos ,也可以自己准备示例视频)。
注意左上角有对人数实现计数。
也就是说,我们部署在本地,实现了在商店里计算客流量的的功能。是不是可以由此扩展到更多的业务场景呢?
4.平台配置导读
以下内容是你想深入了解平台、更好地利用平台开发所需的information:
1)布置能运行这个执行体的本地环境
a. 必不可少的是Ubuntu16.04系统安装好,网上有很多参考教程(Lab内开发有统一的版本);
b. 你需要安装Linux的基本依赖,这些依赖如下:
- PACKAGES="vlc libvlc-dev liblog4cxx-dev libhiredis-dev redis-server python3-zmq qtbase5-dev qttools5-dev-tools
libdbus-1-dev libharfbuzz-dev libgflags-dev libgoogle-glog-dev libblas-dev libhdf5-serial-dev hdf5-tools
libleveldb-dev liblmdb-dev libboost-all-dev libopenblas-dev libsnappy-dev libeigen3-dev openexr build-essential
cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev python-dev python-numpy libtbb2
libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev protobuf-compiler libprotobuf-dev
curl libssl-dev"
以上依赖的安装方法:
- sudo apt-get install -q -y XXX;
e.g. sudo apt-get install -q -y libvlc_dev;
建议以上安装包以几个一组安装,不宜一条指令全部安装,否则有个别失败的安装项容易被忽略掉。
c.安装OpenCV3.1
curl -kLO https://github.com/Itseez/opencv/archive/3.1.0.zip && unzip 3.1.0.zip && cd opencv-3.1.0
&& mkdir -p build && cd build && cmake -D CMAKE_BUILD_TYPE=Release .. && make && sudo make install
在安装OpenCV3.1的过程中可能会遇到下载ippicv_linux_20151201.taz失败的情况,参考解决办法见:
https://blog.csdn.net/shangpapa3/article/details/79481883
或者直接使用网址:
c. 还需要安装nvidia的cuda工具包及其类库cudnn,版本已放在XXX网址 在安装的时候注意有显卡驱动的选择可能会引起安装失败,需要使用指令:apt-get install nvidia-384,同时在安装cuda的时候当问到是否安装适配驱动时选择否。剩余的安装步骤可参考:
https://blog.csdn.net/xunan003/article/details/82145596
2)下载执行体
(未来会在技能开发页面上直接呈现下载地址,当前需要找到Jenkins对应地址,将其下载下来)
a. 打开网页地址:192.168.137.70:8000(under xjk-dev)或者192.168.30.171:8000(under pinganNet).登录账户密码:gammalab:gammalab
b. 按照编号找到freeforward_build任务,按照触发时间点可以找到对应的任务编号,等待任务顺利完成之后即可以得到生成结果的下载链接。
3)配置文件解析
下载后解压,得到的文件内容如下所示:
修改配置文件freeforward.json文件,该文件分段解析如下:
(user_path的使用之后会是个问题)
skill一项是可以由用户在编写skill.cpp中可以直接获取的一项,默认通过网页开发的用户skill[type]都是custom。
"skill":{
"type": "custom",
"param": {//用户自定义。但不建议修改,除非在代码中需要用到user_path来读取文件,适用于本地调试
"user_path":"/media/zhangjing/data/Project/freeforward_build/models/face_one2N_model/",
"interface_data_path":"shared",
"isLocalMode": 1//1-本地模式,适用于本地调试;0-服务器模式,网页端开发使用
}
},
"pipes": [先把pipe内部结构简化以明确pipes的范畴,一个pipe对应一项输入(一个视频流或者一个视频文件),pipes本身是数组
{
"input": {此项定义输入类型
...
},
"inferers": [此项列出该技能对应本输入需要依次调用的算法模型,也是以数组的方式
{
...
},
{
...
},
],
"DUMMY": { }//暂时无用
}
],
以下分析pipes中的单项:
以下各个类型,在本地调试时都需要适当修改mrl
//USB型input
"input": {
"type": "usb",
"param": {
"mrl": "v4l2:///dev/video0",
"cords": [ 1.0, 1.0, 1.0, 2.0, 3.0 ]
}
},
//RTSP型input,亦可以处理视频文件
"input": {
"type": "rtsp",
"param": {
"mrl": "file:///var/gammalab/freeforward_build/test_video/count.mp4",
"cords": [ 1.0, 1.0, 1.0, 2.0, 3.0 ]
}
},
//图片型input,目前图片命名规则为自然数,如1.jpg,2.jpg等
"input": {
"type": "imagefiles",
"param": {
"mrl": "/home/yons/workingCopys/freeforward_build/user_data/pics",
"cords": [ 1.0, 1.0, 1.0, 2.0, 3.0 ]
}
},
注意:以上是不同的示例,在有效的json文件中只能保留一种input
以下是inferers,也就是控制调用模型的配置项。模型的具体地址参见(https://gitlab.com/GammaLab_AI/freeforward/tree/master/models)
"inferers": [//这里的顺序对应代码中的调用顺序,需要一一对应
{
"type": "caffewrapper",//推荐使用,对应caffe-binding模式
"param": {
"cpu": false,//false则对应使用GPU,true则对应CPU
"id" : "pNet",
"weight": "./models/face_one2N_model/det1.caffemodel",
"net": "./models/face_one2N_model/det1-memory_gpu.prototxt"
}
},
{
"type": "caffewrapper",
"param": {
"cpu": false,
"id" : "RNet",
"weight": "./models/face_one2N_model/det2.caffemodel",
"net": "./models/face_one2N_model/det2-gpu.prototxt"
}
},
{
"type": "caffewrapper",
"param": {
"cpu": false,
"id" : "ONet",
"weight": "./models/face_one2N_model/det3.caffemodel",
"net": "./models/face_one2N_model/det3-gpu.prototxt"
}
}
],
本例是人脸检测所使用到的模型。
5.平台后台开发
如果你想要深入本平台的后台开发和维护工作,或者你是一位希望将此平台打造成更具适配性、兼容性的开发者,请阅读此章节。如果你想做的只是利用平台实现你的需求,本章相当于试卷上的附加题,做不做随你。
平台整个后端的工程地址:https://gitlab.com/GammaLab_AI/freeforward_build
1)工程的base类以及继承关系
写不动了。。。
2)技能的概念
继承自skill,有三个需要实现的函数。。。。
3)平台上当前支持的两种infer方式。caffe-binding以及原生caffe。分别有示例。
4)平台AI能力详情列表
5)平台工具类开发建议
faceCommonTools为例
6)平台开发调试指南
step 1.
step 2.
step 3.