@嵌入式人工智能应用- EAIDK610-part three
这里承接上文的讲的基础知识,这里继续介绍EAIDK-610的人工智能应用。
7 基于Tengine模型框架人工智能的部署
一般人工智能开发包含了六个步骤:
- 确定需求
- 准备数据
- 确定框架
- 训练模型
- 模型评估
- 模型部署。
本人作者在这里只谈第6部分,模型部署。 关于1~5部分的知识,读者可以自行搜索相关知识:譬如去tensorflow官网上面学习和进行模型训练。
端侧的人工智能应用开源框架有很多,本文这里用到开源框架是OPEN AI LAB公司的Tengine。Tengine 是OPEN AI LAB 为嵌入式设备开发的一个轻量级、高性能并且模块化的引擎。基于ARM平台高效的计算库实现,针对特定硬件平台的性能优化,吸取已有AI计算框架的优点,设计全新的计算图表示。
EAIDK-610系统已经继承tengine的库,所以在部署的时候十分方便。这里就两种常见的物体分类和人脸检测两种模型的操作步骤进行介绍,后续更新更多的模型应用方案。
Tengine的通用的基本操作流程如下:
- 初始化 Tengine - init_tengine
- 创建Tengine图- create_graph
- 获取输入节点 get_graph_input_tensor
- 申请内容空间 pre_graph
- 运行tengine run_graph
- 获取输出tensor get_graph_output_tensor
- 获取数据 get_tensor_buff
- 释放程序内容
release_graph_tensor
postrun_graph
destroy_graph
release_graph
所以对于利用Tengine开发,开发的流程是一样。只是导入模型不一样,后期对输出的数据处理不一样。其他基本开发流程是一样的。
7.1 Tengine部署
- 7.1.1 src、main 和 object 三个文件中的makefile 添加pkg-config 查找tenginge的库,EAIDK-610已经默安装了库。
CXX=g++
CXXFLAGS= -I $(INC_PATH)
SRC=$(shell ls *.cpp)
OBJS=$(patsubst %.cpp,%.o,$(SRC))
LDDFLAGS= `pkg-config --libs fastcv` `pkg-config --libs tengine`
INCFLAGS= `pkg-config --cflags fastcv` `pkg-config --cflags tengine`
all: $(OBJS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $(OBJ_PATH)/$@ $< $(LDDFLAGS) $(INCFLAGS)
- 7.1.2 识别的图片前期处理
(1) 打开需要识别的图像
cv:: Mat img=cv::imread(image_file);
if(img.empty()==true)
{
std::cout<<"input image isn't exit"<<std::endl;
return 1;
}
else
{
std::cout<<"input image is open"<<std::endl;
}
(2) 减少图像尺寸,譬如100100,300300都可以。
cv::Mat img_resize;
cv::resize(img,img_resize,cv::Size(img_w,img_h));
(3)获取一张彩色图片的计算空间
float *input_data=(float *)malloc(img_w*img_h*3*sizeof(float));
(4)对数据进行归一化处理
get_input_data(img_resize, input_data, img_h, img_w);
- 7.1.3 初始化tengine
init_tengine();
if(request_tengine_version("0.9")<0)
{
std::cout<<"tengine version low"<<std::endl;
return 1;
}
else
{
std::cout<<"tengine initial succuss"<<std::endl;
}
- 7.1.4 创建Tengine图
graph_t graph=create_graph(nullptr,"caffe",proto_name.c_str(),mode_name.c_str());
if(graph==nullptr)
{
std::cout<<"create graph failed"<<std::endl;
std::cout<<"tengine error "<<get_tengine_errno()<<std::endl;
return 1;
}
else
{
std::cout<<"create graph success"<<std::endl;
}
- 7.1.5 获取输入节点
tensor_t input_tensor= get_graph_input_tensor(graph,0,0);
int dims[]={1,3,img_w,img_h}; // 模型要求输入是4维数据,所以在原来图像增加一维
if(set_tensor_shape(input_tensor,dims,4)<0) //设置输入的数据尺寸
{
std::cout<<"tensor_shape fail"<<std::endl;
return 1;
}
else
{
std::cout<<"tensor shape success"<<std::endl;
}
- 7.1.6 申请内容空间
prerun_graph(graph);
if(set_tensor_buffer(input_tensor,input_data,3*img_h*img_w*sizeof(float))<0)
{
std::cout<<"set tensor buff fail"<<std::endl;
return 1;
}
else
{
std::cout<<"set tensor buff success"<<std::endl;
}
- 7.1.7 运行tengine
struct timeval t1,t2;
float avg_time=0;
gettimeofday(&t1,NULL);
for(int i=0;i<repeat_count;i++)
run_graph(graph,1);
gettimeofday(&t2,NULL);
float cmp_time=(float)(t2.tv_sec*1000000+t2.tv_usec - t1.tv_sec*1000000-t1.tv_usec) /1000;
// 计算平均值
avg_time= cmp_time/repeat_count; std::cout<<"......................................................"<<std::endl;
std::cout<<"repeat time "<<repeat_count<<" average time is "<<avg_time<<std::endl;
- 7.1.8 获取输出tensor
tensor_t output_tensor=get_graph_output_tensor(graph,0,0);
int out_dims[4];
get_tensor_shape(output_tensor,out_dims,4);
- 7.1.9 获取数据
float *output_data=(float *)(get_tensor_buffer(output_tensor));
int num=out_dims[1];
- 7.1.10 对输出处理,不同的模型,就是处理这块有点差别`
// 对获取的图像进行标注
post_process_ssd(img,0.5,output_data,num,save_file.c_str());
- 7.1.11 释放内存
release_graph_tensor(output_tensor);
release_graph_tensor(input_tensor);
postrun_graph(graph);
destroy_graph(graph);
free(input_data);
release_tengine();
7.2 物体检测模型载入
- 7.2.1 模型介绍
本次使用的模型是 MobileNet-SSD,SSD算是一种one-stage的目标检测框架或者算法。而MobileNet是这种算法所使用的具体的网络结构,用来提取特征。
- 7.2.2 模型载入
物体检测的算法可以识别21种物体种类。把识别出来的物体用不同框标注出来,同时标注出来识别的种类和概率。然后把识别结果存在一个jpg的图片。
void post_process_ssd(const char* image_file, float threshold, const float* outdata, int num)
{
const char* class_names[] = {"background", "aeroplane", "bicycle", "bird", "boat", "bottle",
"bus", "car", "cat", "chair", "cow", "diningtable",
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
"sofa", "train", "tvmonitor"};
image im = imread(image_file);
int raw_h = im.h;
int raw_w = im.w;
Box_t* boxes = malloc(sizeof(Box_t) * DEFAULT_MAX_BOX_COUNT);
int box_count = 0;
fprintf(stderr, "detect result num: %d \n", num);
for (int i = 0; i < num; i++)
{
if (outdata[1] >= threshold)
{
Box_t box;
box.class_idx = outdata[0];
box.score = outdata[1];
box.x0 = outdata[2] * raw_w;
box.y0 = outdata[3] * raw_h;
box.x1 = outdata[4] * raw_w;
box.y1 = outdata[5] * raw_h;
boxes = realloc(boxes, sizeof(Box_t) * (box_count + 1));
boxes[box_count] = box;
box_count++;
fprintf(stderr, "%s\t:%.1f%%\n", class_names[box.class_idx], box.score * 100);
fprintf(stderr, "BOX:( %d , %d ),( %d , %d )\n", box.x0, box.y0, box.x1, box.y1);
}
outdata += 6;
}
for (int i = 0; i < box_count; i++)
{
Box_t box = boxes[i];
draw_box(im, box.x0, box.y0, box.x1, box.y1, 2, 125, 0, 125);
}
free(boxes);
save_image(im, "mobilenet_ssd_out");
free_image(im);
fprintf(stderr, "======================================\n");
fprintf(stderr, "[DETECTED IMAGE SAVED]:\n");
fprintf(stderr, "======================================\n");
}
7.3 人脸识别模型载入
人脸识别处理结果,用框标注出来。标注识别的概率。然后把识别结果存在一个jpg的图片。
void Post_Process_ssd(cv::Mat& img, float threshold, float* outdata, int num, const std::string& save_name)
{
std::vector<Box> boxes;
int line_width=img.cols*0.005;
std::cout<<"****************************************"<<std::endl;
int detect_face_num=0;
for(int i=0;i<num;i++)
{
if(outdata[1] >= threshold)
{
detect_face_num++;
Box box;
box.class_idx=outdata[0];
box.score=outdata[1];
box.x0 = outdata[2] * img.cols;
box.y0 = outdata[3] * img.rows;
box.x1 = outdata[4] * img.cols;
box.y1 = outdata[5] * img.rows;
boxes.push_back(box);
std::cout<<"face num is "<<detect_face_num<<"score is "<< box.score*100<<std::endl;
}
outdata++;
}
std::cout<<" whole face number is "<<detect_face_num<<std::endl;
for(int i=0;i<boxes.size();i++)
{
Box box=boxes[i];
cv::rectangle(img,cv::Rect(box.x0,box.y0,(box.x1-box.x0),(box.y1-box.y0)),cv::Scalar(255,255,0),cv::LINE_8);
std::ostringstream score_str;
score_str.precision(3);
score_str<<box.score;
std::string label=score_str.str();
int baseline=0;
cv::Size label_size=cv::getTextSize(label,cv::FONT_HERSHEY_SIMPLEX,0.3,1,&baseline);
cv::rectangle(img,
cv::Rect(cv::Point(box.x0, box.y0 - label_size.height),cv::Size(label_size.width, label_size.height + baseline)),
cv::Scalar(255, 255, 0), CV_FILLED);
cv::putText(img, label, cv::Point(box.x0, box.y0), cv::FONT_HERSHEY_SIMPLEX, 0.3, cv::Scalar(0, 0, 0));
}
cv::imwrite(save_name, img);
std::cout << "======================================\n";
std::cout << "[DETECTED IMAGE SAVED]:\t" << save_name << "\n";
std::cout << "======================================\n";
}