注 ---------------------- このブログでは主にコードについて話します ---------------
1. 基本的な準備
1. 既存の環境:
サードパーティのライブラリ
------ YAML-CPP
------ パドル推論
ジェットソン
----ジェットパック 4.6
自己訓練されたモデル
モデル、パラメータ、yml ファイル
2. CMake_Lists (理解できない場合は、私の他のブログを参照してください)
入り口
cmake_minimum_required(VERSION 3.10.2) #camke版本
project(paddle_test) #设置项目名字
find_package(OpenCV) #CMake自带了FindOpenCV.cmake文件,通过调用find_package命令来搜索OpenCV库
include_directories(OpenCV_INCLUDE_DIRS)#将OpenCV库的头文件路径添加到编译选项中
include_directories(/home/hty/C++_PACK/paddle_inference)#将paddle的头文件路径添加到编译选项中
include_directories(/home/hty/C++_PACK/yaml-cpp/include)
set(PADDLE_LIBRARIES /home/hty/C++_PACK/paddle_inference/paddle/lib/libpaddle_inference.so)
set(YAML_LIBRARIES /home/hty/C++_PACK/yaml-cpp/build/libyaml-cpp.so)
message(${OpenCV_INCLUDE_DIRS}) #输出OpenCV库的头文件路径 可删
message(${OpenCV_LIBRARIES}) #输出OpenCV库的链接路径和库名称 可删
add_definitions("-Wall -std=c++11") #添加C++编译选项
add_executable(paddle_test paddle_test.cpp) #可执行文件名字 编译文件
target_link_libraries(paddle_test ${OpenCV_LIBRARIES}) #将OpenCV库链接到可执行文件中
target_link_libraries(paddle_test ${PADDLE_LIBRARIES}) #将paddle库链接到可执行文件中
target_link_libraries(paddle_test ${YAML_LIBRARIES})
2. コード部分(後日掲載)
1.ymlデータの読み込み(ラベルタグの読み込み)
YAML::Node yaml_reader = YAML::LoadFile("../SSD.yml");
float threshold = 0.5f; //阈值
//读取yaml中的标签
std::vector<std::string> label_list = yaml_reader["label_list"].as<std::vector<std::string>>();
//获取标准化参数
std::vector<float> means = {0.485,0.456,0.406};
std::vector<float> stds = {0.229,0.224,0.225};
std::cout << "label_list is ------";
for (const auto& label : label_list) { //const auto& 只读label的值 遍历label
std::cout << label << " ";
}
2. モデルファイルの構成と予測 Config の初期化
//配置模型参数
std::string model_file = "../SSD_model"; //模型文件
std::string params_file = "../SSD_pa"; //参数文件
paddle_infer::Config config;
config.SetModel(model_file, params_file); // 加载模型文件
config.EnableUseGpu(1000, 0); //使用gpu
config.SwitchIrOptim(true); //IR加速
config.EnableMemoryOptim(); //内存优化 显存内存复用
config.SetOptimCacheDir("../trt_cache"); // 设置缓存路径
config.EnableTensorRtEngine(1 << 30, 1, 3, paddle_infer::PrecisionType::kHalf, true, false); //TensorRT调用 采用float16
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config); //创建Predictor
3. 入出力データの設定
//输入设置 300*300
int im_size = 300;
cv::Mat im_shape = cv::Mat(1, 2, CV_32FC1);
im_shape.at<float>(0, 0) = im_size;
im_shape.at<float>(0, 1) = im_size;
std::vector<int> input_shape = {1, 3, im_size, im_size}; //1*3*300*300
struct DATA{ //定义数据
float id;
float score;
float x_min;
float y_min;
float x_max;
float y_max;
}data;
4. 画像の前処理
入力 Mat をベクトルに変換すると、形状が hwc から chw、BGR から RGB に変わります。
/* 图片预处理
img: 图片
img_size:图片大小
mean: 输入均值
std: 输入标准差
return chw储存方式的vector
*/
std::vector<float> preprocess(cv::Mat img, cv::Size img_size,std::vector<float> mean,std::vector<float> std){
cv::Mat result;
//放缩到目标大小
cv::resize(img, result, img_size);
cv::cvtColor(result, result, cv::COLOR_BGR2RGB); //BGR to RGB
result.convertTo(result, CV_32FC3, 1.0 / 255.0); //转为float32类型 并且归一化
//标准化
for (int c = 0; c < 3; ++c) { //遍历所有像素 三个通道 进行标准化
result.forEach<cv::Vec3f>( //使用opencv中forEach对像素进行函数操作 cv::Vec3f定义为float32
[&](cv::Vec3f &pixel, const int *position) -> void { //[&]lambal表达式引用捕获,直接访问外部mean,std变量
pixel[c] -= mean[c];
pixel[c] /= std[c];
}
);
}
//将图像转为vector类型,并且从hwc转为chw
std::vector<cv::Mat> mv;
cv::split(result, mv); //分离三通道
std::vector<float> R = mv[0].reshape(1, 1);
std::vector<float> G = mv[1].reshape(1, 1);
std::vector<float> B = mv[2].reshape(1, 1);
//RGB数据合并
std::vector<float> input_data;
input_data.insert(input_data.end(), R.begin(), R.end());
input_data.insert(input_data.end(), G.begin(), G.end());
input_data.insert(input_data.end(), B.begin(), B.end());
return input_data;
}
5. 予報
/* 预测
input_data:输入预处理后的vector数据
input_shape:输入的图片大小 格式为NCHW
predictor :初始化好的predictor
return : 预测的输出
*/
std::vector<float> predict(std::vector<float> input_data,std::vector<int> input_shape,std::shared_ptr<paddle_infer::Predictor> predictor){
// 准备输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape(input_shape);
input_tensor->CopyFromCpu(input_data.data()); //复制一份图片,拷贝数据输入
//运行
predictor->Run();
//输出
auto output_names = predictor->GetOutputNames();//获取输出名字
auto output_tensor = predictor->GetOutputHandle(output_names[0]);//获取输出句柄
std::vector<int> output_shape = output_tensor->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(),
1, std::multiplies<int>());
std::vector<float> out_datas;
out_datas.resize(out_num);
output_tensor->CopyToCpu(out_datas.data()); //将输出拷贝到out_data
return out_datas;
}
完全なコード
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <chrono>
#include <numeric>
#include <cmath>
#include "paddle/include/paddle_inference_api.h"
#include <opencv2/opencv.hpp>
#include <yaml-cpp/yaml.h>
/* 图片预处理
img: 图片
img_size:图片大小
mean: 输入均值
std: 输入标准差
return chw储存方式的vector
*/
std::vector<float> preprocess(cv::Mat img, cv::Size img_size,std::vector<float> mean,std::vector<float> std){
cv::Mat result;
//放缩到目标大小
cv::resize(img, result, img_size);
cv::cvtColor(result, result, cv::COLOR_BGR2RGB); //BGR to RGB
result.convertTo(result, CV_32FC3, 1.0 / 255.0); //转为float32类型 并且归一化
//标准化
for (int c = 0; c < 3; ++c) { //遍历所有像素 三个通道 进行标准化
result.forEach<cv::Vec3f>( //使用opencv中forEach对像素进行函数操作 cv::Vec3f定义为float32
[&](cv::Vec3f &pixel, const int *position) -> void { //[&]lambal表达式引用捕获,直接访问外部mean,std变量
pixel[c] -= mean[c];
pixel[c] /= std[c];
}
);
}
//将图像转为vector类型,并且从hwc转为chw
std::vector<cv::Mat> mv;
cv::split(result, mv); //分离三通道
std::vector<float> R = mv[0].reshape(1, 1);
std::vector<float> G = mv[1].reshape(1, 1);
std::vector<float> B = mv[2].reshape(1, 1);
//RGB数据合并
std::vector<float> input_data;
input_data.insert(input_data.end(), R.begin(), R.end());
input_data.insert(input_data.end(), G.begin(), G.end());
input_data.insert(input_data.end(), B.begin(), B.end());
return input_data;
}
/* 预测
input_data:输入预处理后的vector数据
input_shape:输入的图片大小 格式为NCHW
predictor :初始化好的predictor
return : 预测的输出
*/
std::vector<float> predict(std::vector<float> input_data,std::vector<int> input_shape,std::shared_ptr<paddle_infer::Predictor> predictor){
// 准备输入 Tensor
auto input_names = predictor->GetInputNames();
auto input_tensor = predictor->GetInputHandle(input_names[0]);
input_tensor->Reshape(input_shape);
input_tensor->CopyFromCpu(input_data.data()); //复制一份图片,拷贝数据输入
//运行
predictor->Run();
//输出
auto output_names = predictor->GetOutputNames();//获取输出名字
auto output_tensor = predictor->GetOutputHandle(output_names[0]);//获取输出句柄
std::vector<int> output_shape = output_tensor->shape();
int out_num = std::accumulate(output_shape.begin(), output_shape.end(),
1, std::multiplies<int>());
std::vector<float> out_datas;
out_datas.resize(out_num);
output_tensor->CopyToCpu(out_datas.data()); //将输出拷贝到out_data
return out_datas;
}
int main(void)
{
YAML::Node yaml_reader = YAML::LoadFile("../SSD.yml");
float threshold = 0.5f; //阈值
//读取yaml中的标签
std::vector<std::string> label_list = yaml_reader["label_list"].as<std::vector<std::string>>();
//获取标准化参数
std::vector<float> means = {0.485,0.456,0.406};
std::vector<float> stds = {0.229,0.224,0.225};
std::cout << "label_list is ------";
for (const auto& label : label_list) { //const auto& 只读label的值 遍历label
std::cout << label << " ";
}
//配置模型参数
std::string model_file = "../SSD_model"; //模型文件
std::string params_file = "../SSD_pa"; //参数文件
paddle_infer::Config config;
config.SetModel(model_file, params_file); // 加载模型文件
config.EnableUseGpu(1000, 0); //使用gpu
config.SwitchIrOptim(true); //IR加速
config.EnableMemoryOptim(); //内存优化 显存内存复用
config.SetOptimCacheDir("../trt_cache"); // 设置缓存路径
config.EnableTensorRtEngine(1 << 30, 1, 3, paddle_infer::PrecisionType::kHalf, true, false); //TensorRT调用 采用float16
std::shared_ptr<paddle_infer::Predictor> predictor = paddle_infer::CreatePredictor(config); //创建Predictor
//输入设置 300*300
int im_size = 300;
cv::Mat im_shape = cv::Mat(1, 2, CV_32FC1);
im_shape.at<float>(0, 0) = im_size;
im_shape.at<float>(0, 1) = im_size;
std::vector<int> input_shape = {1, 3, im_size, im_size}; //1*3*300*300
struct DATA{ //定义数据
float id;
float score;
float x_min;
float y_min;
float x_max;
float y_max;
}data;
//配置摄像头
cv::VideoCapture cap(0);
if (!cap.isOpened()){
std::cout << "error to open capture" << std::endl;
return -1;
}
while (true){
auto start_time = std::chrono::high_resolution_clock::now();
cv::Mat frame;
bool ret = cap.read(frame);
if (!ret)
{
break;
}
//图像预处理
std::vector<float> input_data = preprocess(frame,cv::Size(im_size,im_size),means,stds);
//预测
std::vector<float> out_datas = predict(input_data,input_shape,predictor);
// 后处理
int out_data_len = out_datas.size()/6;
std::cout<<"----------------data_start---------------------------"<<std::endl;
for(int i =0;i<out_data_len;i++){ //遍历所有数据
float temp_score = out_datas[6*i+1];
if(temp_score>threshold){ //准确度大于threshold
//获取位置等数据
data.id = out_datas[6*i];
data.score = out_datas[6*i+1];
data.x_min = out_datas[6*i+2]*frame.rows;
data.y_min = out_datas[6*i+3]*frame.cols;
data.x_max = out_datas[6*i+4]*frame.rows;
data.y_max = out_datas[6*i+5]*frame.cols;
std::string label_id = label_list[(int)data.id-1];
std::stringstream string_ss;
string_ss << data.score;
std::string string_score = string_ss.str();
std::cout<<"label is "<<label_id<<" score is "<<string_score<<std::endl;
//画图
cv::rectangle(frame, cv::Point((int)data.x_min, (int)data.y_min),
cv::Point((int)data.x_max, (int)data.y_max), cv::Scalar(255, 0, 255), 2);
cv::putText(frame, label_id, cv::Point((int)data.x_min, (int)data.y_min - 2), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(255, 0, 0), 2);
cv::putText(frame, string_score, cv::Point((int)data.x_min - 35, (int)data.y_min - 2), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 2);
}
}
//图片显示
//cv::imshow("img",frame);
cv::waitKey(1);
// 计算帧率
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
double frame_time = duration.count() / 1000000.0; // 将微秒转换为秒
double fps = 1.0f/frame_time;
std::cout<<"FPS is ----- "<<fps<<std::endl;
std::cout<<"----------------data_end---------------------------"<<std::endl;
}
return 0;
}