Commit 0b55404d authored by lijian6's avatar lijian6
Browse files

first init

parent e012f86c
cmake_minimum_required(VERSION 3.0.0)
project(yolov8_ort CXX C)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -pthread -g")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(ORT_DIR "/opt/dtk/onnxruntime")
message(STATUS "ONNXRUNTIME_DIR: ${ORT_DIR}")
find_package(OpenCV REQUIRED)
message(StATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
include_directories("include/")
add_executable(main
src/utils.cpp
src/yolov8Predictor.cpp
src/main.cpp)
target_include_directories(main PRIVATE "${ORT_DIR}/include")
target_compile_features(main PRIVATE cxx_std_17)
target_link_libraries(main ${OpenCV_LIBS})
target_link_libraries(main "${ORT_DIR}/lib/libonnxruntime.so")
\ No newline at end of file
# yolov8_onnxruntime # yolov8
## 论文
## 模型结构
yolov8是一种单阶段目标检测算法,该算法在YOLOV5的基础上添加了一些新的改进思路,使其速度与精度都得到了极大的性能提升。<br>
![block.jpg](./block.jpg)
## 算法原理
YOLOv8算法通过将图像划分为不同大小的网格,预测每个网格中的目标类别和边界框,利用特征金字塔结构和自适应的模型缩放来实现高效准确的实时目标检测。
- 骨干网络和 Neck 部分将 YOLOv5 的 C3 结构换成了梯度流更丰富的 C2f 结构,并对不同尺度模型调整了不同的通道数,大幅提升了模型性能。
- Head 部分相比 YOLOv5 改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,同时也从 Anchor-Based 换成了 Anchor-Free.
- Loss 计算方面采用了 TaskAlignedAssigner 正样本分配策略,并引入了 Distribution Focal Loss.
- 训练的数据增强部分引入了 YOLOX 中的最后 10 epoch 关闭 Mosiac 增强的操作,可以有效地提升精度
![model_framework.png](./model_framework.png)
## 环境配置
### Docker
拉取镜像
```
docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:opencv49_ffmpeg4.2.1_ubuntu20.04-dtk24.04.2
```
创建并启动容器
```
docker run -it --network=host --ipc=host --name=yolov8_ort --privileged --hostname localhost --shm-size=16G --device=/dev/kfd --device=/dev/mkfd --device=/dev/dri -v /opt/hyhal:/opt/hyhal:ro -v /your_code_path:/your_code_path --group-add video --cap-add=SYS_PTRACE --security-opt seccomp=unconfined 99d476c02fdd /bin/bash
```
## 数据集
无,使用项目中的案例图像进行推理测试
## 推理
### 编译工程
```
git clone http://developer.sourcefind.cn/codes/modelzoo/yolov5s_onnxruntime.git
mkdir build && cd build
cmake ../
make
```
### 运行案例
```
./main
```
### result
更多推理结果结果请见img_out文件夹
![result.png](./img_out/bus.jpg)
### 精度
## 应用场景
### 算法类别
`目标检测`
### 热点应用行业
`金融、交通、教育`
## 源码仓库及问题反馈
- https://developer.sourcefind.cn/codes/modelzoo/yolov8_onnxruntime
## 参考资料
- https://github.com/ultralytics/ultralytics/tree/main/examples/YOLOv8-ONNXRuntime-CPP
- https://github.com/Li-99/yolov8_onnxruntime
\ No newline at end of file
block.jpg

1.35 MB

from ultralytics import YOLO
import torch
import onnx
# 加载预训练模型
model = YOLO('./model/yolov8n.pt')
# 设置导出参数
batch_size = 4 # 批量大小
input_shape = (batch_size, 3, 640, 640) # 输入图像尺寸 (C, H, W),这里以 640x640 为例
opset_version = 16 # ONNX 操作集版本
output_file = './model/yolov8n.pt' # 输出 ONNX 文件路径
# dynamic_axes = {'images': {0: 'batch'}, 'output': {0: 'batch'}} # 动态批次大小
# 创建随机输入张量
dummy_input = torch.randn(input_shape)
# 导出模型为 ONNX 格式
torch.onnx.export(
model.model, # 要导出的模型
dummy_input, # 模型的输入
output_file, # 输出文件路径
verbose=False,
export_params=True, # 存储已训练参数的值
opset_version=11, # ONNX版本,YOLO通常使用11或更高
do_constant_folding=True, # 是否执行常量折叠优化
input_names=['input'], # 输入节点名称
output_names=['output'], # 输出节点名称
# dynamic_axes={'input': {0: 'batch_size'}, # 批次大小可变
# 'output': {0: 'batch_size'}}
)
model = onnx.load(output_file)
graph = model.graph
output_to_keep = 'output'
outputs_to_remove = [output for output in graph.output if output.name != output_to_keep]
for output in outputs_to_remove:
graph.output.remove(output)
onnx.save(model, output_file)
print(f"Model has been successfully exported to {output_file}")
\ No newline at end of file
icon.png

77.3 KB

/*
Copyright (c) 2009, Hideyuki Tanaka
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
//#define USE_DEMANGLING
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include <stdexcept>
#include <typeinfo>
#include <cstring>
#include <algorithm>
#ifdef USE_DEMANGLING
#include <cxxabi.h>
#endif
#include <cstdlib>
namespace cmdline{
namespace detail{
template <typename Target, typename Source, bool Same>
class lexical_cast_t{
public:
static Target cast(const Source &arg){
Target ret;
std::stringstream ss;
if (!(ss<<arg && ss>>ret && ss.eof()))
throw std::bad_cast();
return ret;
}
};
template <typename Target, typename Source>
class lexical_cast_t<Target, Source, true>{
public:
static Target cast(const Source &arg){
return arg;
}
};
template <typename Source>
class lexical_cast_t<std::string, Source, false>{
public:
static std::string cast(const Source &arg){
std::ostringstream ss;
ss<<arg;
return ss.str();
}
};
template <typename Target>
class lexical_cast_t<Target, std::string, false>{
public:
static Target cast(const std::string &arg){
Target ret;
std::istringstream ss(arg);
if (!(ss>>ret && ss.eof()))
throw std::bad_cast();
return ret;
}
};
template <typename T1, typename T2>
struct is_same {
static const bool value = false;
};
template <typename T>
struct is_same<T, T>{
static const bool value = true;
};
template<typename Target, typename Source>
Target lexical_cast(const Source &arg)
{
return lexical_cast_t<Target, Source, detail::is_same<Target, Source>::value>::cast(arg);
}
static inline std::string demangle(const std::string &name)
{
#ifdef USE_DEMANGLING
int status=0;
const char *p=::__cxa_demangle(name.c_str(), 0, 0, &status);
std::string ret(p);
free(p);
return ret;
#else
return name;
#endif
}
template <class T>
std::string readable_typename()
{
return demangle(typeid(T).name());
}
template <class T>
std::string default_value(T def)
{
return detail::lexical_cast<std::string>(def);
}
template <>
inline std::string readable_typename<std::string>()
{
return "string";
}
} // detail
//-----
class cmdline_error : public std::exception {
public:
cmdline_error(const std::string &msg): msg(msg){}
~cmdline_error() throw() {}
const char *what() const throw() { return msg.c_str(); }
private:
std::string msg;
};
template <class T>
struct default_reader{
T operator()(const std::string &str){
return detail::lexical_cast<T>(str);
}
};
template <class T>
struct range_reader{
range_reader(const T &low, const T &high): low(low), high(high) {}
T operator()(const std::string &s) const {
T ret=default_reader<T>()(s);
if (!(ret>=low && ret<=high)) throw cmdline::cmdline_error("range_error");
return ret;
}
private:
T low, high;
};
template <class T>
range_reader<T> range(const T &low, const T &high)
{
return range_reader<T>(low, high);
}
template <class T>
struct oneof_reader{
T operator()(const std::string &s){
T ret=default_reader<T>()(s);
if (std::find(alt.begin(), alt.end(), ret)==alt.end())
throw cmdline_error("");
return ret;
}
void add(const T &v){ alt.push_back(v); }
private:
std::vector<T> alt;
};
template <class T>
oneof_reader<T> oneof(T a1)
{
oneof_reader<T> ret;
ret.add(a1);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
ret.add(a9);
return ret;
}
template <class T>
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10)
{
oneof_reader<T> ret;
ret.add(a1);
ret.add(a2);
ret.add(a3);
ret.add(a4);
ret.add(a5);
ret.add(a6);
ret.add(a7);
ret.add(a8);
ret.add(a9);
ret.add(a10);
return ret;
}
//-----
class parser{
public:
parser(){
}
~parser(){
for (std::map<std::string, option_base*>::iterator p=options.begin();
p!=options.end(); p++)
delete p->second;
}
void add(const std::string &name,
char short_name=0,
const std::string &desc=""){
if (options.count(name)) throw cmdline_error("multiple definition: "+name);
options[name]=new option_without_value(name, short_name, desc);
ordered.push_back(options[name]);
}
template <class T>
void add(const std::string &name,
char short_name=0,
const std::string &desc="",
bool need=true,
const T def=T()){
add(name, short_name, desc, need, def, default_reader<T>());
}
template <class T, class F>
void add(const std::string &name,
char short_name=0,
const std::string &desc="",
bool need=true,
const T def=T(),
F reader=F()){
if (options.count(name)) throw cmdline_error("multiple definition: "+name);
options[name]=new option_with_value_with_reader<T, F>(name, short_name, need, def, desc, reader);
ordered.push_back(options[name]);
}
void footer(const std::string &f){
ftr=f;
}
void set_program_name(const std::string &name){
prog_name=name;
}
bool exist(const std::string &name) const {
if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
return options.find(name)->second->has_set();
}
template <class T>
const T &get(const std::string &name) const {
if (options.count(name)==0) throw cmdline_error("there is no flag: --"+name);
const option_with_value<T> *p=dynamic_cast<const option_with_value<T>*>(options.find(name)->second);
if (p==NULL) throw cmdline_error("type mismatch flag '"+name+"'");
return p->get();
}
const std::vector<std::string> &rest() const {
return others;
}
bool parse(const std::string &arg){
std::vector<std::string> args;
std::string buf;
bool in_quote=false;
for (std::string::size_type i=0; i<arg.length(); i++){
if (arg[i]=='\"'){
in_quote=!in_quote;
continue;
}
if (arg[i]==' ' && !in_quote){
args.push_back(buf);
buf="";
continue;
}
if (arg[i]=='\\'){
//i++;
if (i>=arg.length()){
errors.push_back("unexpected occurrence of '\\' at end of string");
return false;
}
}
buf+=arg[i];
}
if (in_quote){
errors.push_back("quote is not closed");
return false;
}
if (buf.length()>0)
args.push_back(buf);
//for (size_t i=0; i<args.size(); i++)
// std::cout<<"\""<<args[i]<<"\""<<std::endl;
return parse(args);
}
bool parse(const std::vector<std::string> &args){
int argc=static_cast<int>(args.size());
std::vector<const char*> argv(argc);
for (int i=0; i<argc; i++)
argv[i]=args[i].c_str();
return parse(argc, &argv[0]);
}
bool parse(int argc, const char * const argv[]){
errors.clear();
others.clear();
if (argc<1){
errors.push_back("argument number must be longer than 0");
return false;
}
if (prog_name=="")
prog_name=argv[0];
std::map<char, std::string> lookup;
for (std::map<std::string, option_base*>::iterator p=options.begin();
p!=options.end(); p++){
if (p->first.length()==0) continue;
char initial=p->second->short_name();
if (initial){
if (lookup.count(initial)>0){
lookup[initial]="";
errors.push_back(std::string("short option '")+initial+"' is ambiguous");
return false;
}
else lookup[initial]=p->first;
}
}
for (int i=1; i<argc; i++){
if (strncmp(argv[i], "--", 2)==0){
const char *p=strchr(argv[i]+2, '=');
if (p){
std::string name(argv[i]+2, p);
std::string val(p+1);
set_option(name, val);
}
else{
std::string name(argv[i]+2);
if (options.count(name)==0){
errors.push_back("undefined option: --"+name);
continue;
}
if (options[name]->has_value()){
if (i+1>=argc){
errors.push_back("option needs value: --"+name);
continue;
}
else{
i++;
set_option(name, argv[i]);
}
}
else{
set_option(name);
}
}
}
else if (strncmp(argv[i], "-", 1)==0){
if (!argv[i][1]) continue;
char last=argv[i][1];
for (int j=2; argv[i][j]; j++){
last=argv[i][j];
if (lookup.count(argv[i][j-1])==0){
errors.push_back(std::string("undefined short option: -")+argv[i][j-1]);
continue;
}
if (lookup[argv[i][j-1]]==""){
errors.push_back(std::string("ambiguous short option: -")+argv[i][j-1]);
continue;
}
set_option(lookup[argv[i][j-1]]);
}
if (lookup.count(last)==0){
errors.push_back(std::string("undefined short option: -")+last);
continue;
}
if (lookup[last]==""){
errors.push_back(std::string("ambiguous short option: -")+last);
continue;
}
if (i+1<argc && options[lookup[last]]->has_value()){
set_option(lookup[last], argv[i+1]);
i++;
}
else{
set_option(lookup[last]);
}
}
else{
others.push_back(argv[i]);
}
}
for (std::map<std::string, option_base*>::iterator p=options.begin();
p!=options.end(); p++)
if (!p->second->valid())
errors.push_back("need option: --"+std::string(p->first));
return errors.size()==0;
}
void parse_check(const std::string &arg){
if (!options.count("help"))
add("help", '?', "print this message");
check(0, parse(arg));
}
void parse_check(const std::vector<std::string> &args){
if (!options.count("help"))
add("help", '?', "print this message");
check(args.size(), parse(args));
}
void parse_check(int argc, char *argv[]){
if (!options.count("help"))
add("help", '?', "print this message");
check(argc, parse(argc, argv));
}
std::string error() const{
return errors.size()>0?errors[0]:"";
}
std::string error_full() const{
std::ostringstream oss;
for (size_t i=0; i<errors.size(); i++)
oss<<errors[i]<<std::endl;
return oss.str();
}
std::string usage() const {
std::ostringstream oss;
oss<<"usage: "<<prog_name<<" ";
for (size_t i=0; i<ordered.size(); i++){
if (ordered[i]->must())
oss<<ordered[i]->short_description()<<" ";
}
oss<<"[options] ... "<<ftr<<std::endl;
oss<<"options:"<<std::endl;
size_t max_width=0;
for (size_t i=0; i<ordered.size(); i++){
max_width=std::max(max_width, ordered[i]->name().length());
}
for (size_t i=0; i<ordered.size(); i++){
if (ordered[i]->short_name()){
oss<<" -"<<ordered[i]->short_name()<<", ";
}
else{
oss<<" ";
}
oss<<"--"<<ordered[i]->name();
for (size_t j=ordered[i]->name().length(); j<max_width+4; j++)
oss<<' ';
oss<<ordered[i]->description()<<std::endl;
}
return oss.str();
}
private:
void check(int argc, bool ok){
if ((argc==1 && !ok) || exist("help")){
std::cerr<<usage();
exit(0);
}
if (!ok){
std::cerr<<error()<<std::endl<<usage();
exit(1);
}
}
void set_option(const std::string &name){
if (options.count(name)==0){
errors.push_back("undefined option: --"+name);
return;
}
if (!options[name]->set()){
errors.push_back("option needs value: --"+name);
return;
}
}
void set_option(const std::string &name, const std::string &value){
if (options.count(name)==0){
errors.push_back("undefined option: --"+name);
return;
}
if (!options[name]->set(value)){
errors.push_back("option value is invalid: --"+name+"="+value);
return;
}
}
class option_base{
public:
virtual ~option_base(){}
virtual bool has_value() const=0;
virtual bool set()=0;
virtual bool set(const std::string &value)=0;
virtual bool has_set() const=0;
virtual bool valid() const=0;
virtual bool must() const=0;
virtual const std::string &name() const=0;
virtual char short_name() const=0;
virtual const std::string &description() const=0;
virtual std::string short_description() const=0;
};
class option_without_value : public option_base {
public:
option_without_value(const std::string &name,
char short_name,
const std::string &desc)
:nam(name), snam(short_name), desc(desc), has(false){
}
~option_without_value(){}
bool has_value() const { return false; }
bool set(){
has=true;
return true;
}
bool set(const std::string &){
return false;
}
bool has_set() const {
return has;
}
bool valid() const{
return true;
}
bool must() const{
return false;
}
const std::string &name() const{
return nam;
}
char short_name() const{
return snam;
}
const std::string &description() const {
return desc;
}
std::string short_description() const{
return "--"+nam;
}
private:
std::string nam;
char snam;
std::string desc;
bool has;
};
template <class T>
class option_with_value : public option_base {
public:
option_with_value(const std::string &name,
char short_name,
bool need,
const T &def,
const std::string &desc)
: nam(name), snam(short_name), need(need), has(false)
, def(def), actual(def) {
this->desc=full_description(desc);
}
~option_with_value(){}
const T &get() const {
return actual;
}
bool has_value() const { return true; }
bool set(){
return false;
}
bool set(const std::string &value){
try{
actual=read(value);
has=true;
}
catch(const std::exception &e){
return false;
}
return true;
}
bool has_set() const{
return has;
}
bool valid() const{
if (need && !has) return false;
return true;
}
bool must() const{
return need;
}
const std::string &name() const{
return nam;
}
char short_name() const{
return snam;
}
const std::string &description() const {
return desc;
}
std::string short_description() const{
return "--"+nam+"="+detail::readable_typename<T>();
}
protected:
std::string full_description(const std::string &desc){
return
desc+" ("+detail::readable_typename<T>()+
(need?"":" [="+detail::default_value<T>(def)+"]")
+")";
}
virtual T read(const std::string &s)=0;
std::string nam;
char snam;
bool need;
std::string desc;
bool has;
T def;
T actual;
};
template <class T, class F>
class option_with_value_with_reader : public option_with_value<T> {
public:
option_with_value_with_reader(const std::string &name,
char short_name,
bool need,
const T def,
const std::string &desc,
F reader)
: option_with_value<T>(name, short_name, need, def, desc), reader(reader){
}
private:
T read(const std::string &s){
return reader(s);
}
F reader;
};
std::map<std::string, option_base*> options;
std::vector<option_base*> ordered;
std::string ftr;
std::string prog_name;
std::vector<std::string> others;
std::vector<std::string> errors;
};
} // cmdline
\ No newline at end of file
#pragma once
#include <codecvt>
#include <fstream>
#include <opencv2/opencv.hpp>
struct Yolov8Result
{
cv::Rect box;
cv::Mat boxMask; // mask in box
float conf{};
int classId{};
};
namespace utils
{
static std::vector<cv::Scalar> colors;
size_t vectorProduct(const std::vector<int64_t> &vector);
std::wstring charToWstring(const char *str);
std::vector<std::string> loadNames(const std::string &path);
void visualizeDetection(cv::Mat &image, std::vector<Yolov8Result> &results,
const std::vector<std::string> &classNames);
void letterbox(const cv::Mat &image, cv::Mat &outImage,
const cv::Size &newShape,
const cv::Scalar &color,
bool auto_,
bool scaleFill,
bool scaleUp,
int stride);
void scaleCoords(cv::Rect &coords, cv::Mat &mask,
const float maskThreshold,
const cv::Size &imageShape, const cv::Size &imageOriginalShape);
template <typename T>
T clip(const T &n, const T &lower, const T &upper);
}
#pragma once
#include <iostream>
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <utility>
#include "utils.h"
class YOLOPredictor
{
public:
explicit YOLOPredictor(std::nullptr_t){};
YOLOPredictor(const std::string &modelPath,
const bool &isGPU,
float confThreshold,
float iouThreshold,
float maskThreshold);
// ~YOLOPredictor();
std::vector<std::vector<Yolov8Result>> predict(std::vector<cv::Mat> &images);
int classNums = 80;
private:
Ort::Env env{nullptr};
Ort::SessionOptions sessionOptions{nullptr};
Ort::Session session{nullptr};
void preprocessing(std::vector<cv::Mat> &image, float *&blob, std::vector<int64_t> &inputTensorShape);
std::vector<std::vector<Yolov8Result>> postprocessing(const cv::Size &resizedImageShape,
const std::vector<cv::Size> &originalImageShapes,
std::vector<Ort::Value> &outputTensors);
static void getBestClassInfo(std::vector<float>::iterator it,
float &bestConf,
int &bestClassId,
const int _classNums);
cv::Mat getMask(const cv::Mat &maskProposals, const cv::Mat &maskProtos);
bool isDynamicInputShape{};
std::vector<const char *> inputNames;
std::vector<Ort::AllocatedStringPtr> input_names_ptr;
std::vector<const char *> outputNames;
std::vector<Ort::AllocatedStringPtr> output_names_ptr;
std::vector<std::vector<int64_t>> inputShapes;
std::vector<std::vector<int64_t>> outputShapes;
float confThreshold = 0.3f;
float iouThreshold = 0.4f;
bool hasMask = false;
float maskThreshold = 0.5f;
};
\ No newline at end of file
#pragma once
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <utility>
#include "utils.h"
class YOLOPredictor
{
public:
explicit YOLOPredictor(std::nullptr_t){};
YOLOPredictor(const std::string &modelPath,
const bool &isGPU,
float confThreshold,
float iouThreshold,
float maskThreshold);
// ~YOLOPredictor();
std::vector<Yolov8Result> predict(cv::Mat &image);
int classNums = 80;
private:
Ort::Env env{nullptr};
Ort::SessionOptions sessionOptions{nullptr};
Ort::Session session{nullptr};
void preprocessing(cv::Mat &image, float *&blob, std::vector<int64_t> &inputTensorShape);
std::vector<Yolov8Result> postprocessing(const cv::Size &resizedImageShape,
const cv::Size &originalImageShape,
std::vector<Ort::Value> &outputTensors);
static void getBestClassInfo(std::vector<float>::iterator it,
float &bestConf,
int &bestClassId,
const int _classNums);
cv::Mat getMask(const cv::Mat &maskProposals, const cv::Mat &maskProtos);
bool isDynamicInputShape{};
std::vector<const char *> inputNames;
std::vector<Ort::AllocatedStringPtr> input_names_ptr;
std::vector<const char *> outputNames;
std::vector<Ort::AllocatedStringPtr> output_names_ptr;
std::vector<std::vector<int64_t>> inputShapes;
std::vector<std::vector<int64_t>> outputShapes;
float confThreshold = 0.3f;
float iouThreshold = 0.4f;
bool hasMask = false;
float maskThreshold = 0.5f;
};
\ No newline at end of file
# 模型唯一标识
modelCode=1207
# 模型名称
modelName=yolov8_onnxruntime
# 模型描述
modelDescription=基于onnxruntime推理框架的cpp接口实现的yolov8多batch推理
# 应用场景
appScenario=推理,目标检测,金融,交通,教育
# 框架类型
frameType=onnxruntime
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
\ No newline at end of file
#include <regex>
#include <iostream>
#include <filesystem>
#include <ctime>
#include "utils.h"
#include "yolov8Predictor.h"
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
// yolov8参数
float confThreshold = 0.4f;
float iouThreshold = 0.4f;
float maskThreshold = 0.5f;
bool isGPU = true;
// 模型路径
const std::string classNamePath = "../model/coco.names";
const std::vector<std::string> classNames = utils::loadNames(classNamePath);
const std::string modelPath = "/public/home/changhl/shihua/yolov8n.onnx";
//输入图像路径
const std::string imagePath = "../img_in/";
const std::string _strPattern = imagePath + "*.jpg";
std::vector<cv::String> ImageNames;
cv::glob(_strPattern, ImageNames);
//输出图像路径
const std::string savePath = "../img_out";
const std::string suffixName = "png";
if (classNames.empty())
{
std::cerr << "Error: Empty class names file." << std::endl;
return -1;
}
if (!std::filesystem::exists(modelPath))
{
std::cerr << "Error: There is no model." << std::endl;
return -1;
}
if (!std::filesystem::is_directory(imagePath))
{
std::cerr << "Error: There is no img." << std::endl;
return -1;
}
if (!std::filesystem::is_directory(savePath))
{
std::filesystem::create_directory(savePath);
}
std::cout << "Model from :::" << modelPath << std::endl;
std::cout << "Images from :::" << imagePath << std::endl;
std::cout << "Resluts will be saved :::" << savePath << std::endl;
YOLOPredictor predictor{nullptr};
try
{
predictor = YOLOPredictor(modelPath, isGPU,
confThreshold,
iouThreshold,
maskThreshold);
std::cout << "Model was initialized." << std::endl;
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;
return -1;
}
assert(classNames.size() == predictor.classNums);
std::regex pattern(".+\\.(jpg|jpeg|png|gif)$");
std::cout << "Start predicting..." << std::endl;
clock_t startTime, endTime;
startTime = clock();
int picNums = 0;
int batch_size = 4;
std::vector<cv::Mat> imagebatch;
for(int i = 0; i < batch_size; i++)
{
std::cout << "image name:" << ImageNames[i] << std::endl;
cv::Mat image = cv::imread(ImageNames[i]);
if (image.empty())
{
std::cerr << "Error: Empty image." << std::endl;
return -1;
}
imagebatch.push_back(image);
}
std::vector<std::vector<Yolov8Result>> results = predictor.predict(imagebatch);
for(int i = 0; i < results.size(); i++)
{
std::vector<Yolov8Result> result = results[i];
cv::Mat image = imagebatch[i];
utils::visualizeDetection(image, result, classNames);
std::string newFilename = std::filesystem::path(ImageNames[i]).filename().string();
std::string outputFilename = savePath + "/" + newFilename;
cv::imwrite(outputFilename, image);
std::cout << "Saved !!!" << std::endl;
}
endTime = clock();
std::cout << "The total run time is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "seconds" << std::endl;
std::cout << "The average run time is: " << (double)(endTime - startTime) / batch_size / CLOCKS_PER_SEC << "seconds" << std::endl;
std::cout << "##########DONE################" << std::endl;
return 0;
}
#include "utils.h"
size_t utils::vectorProduct(const std::vector<int64_t> &vector)
{
if (vector.empty())
return 0;
size_t product = 1;
for (const auto &element : vector)
product *= element;
return product;
}
std::wstring utils::charToWstring(const char *str)
{
typedef std::codecvt_utf8<wchar_t> convert_type;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.from_bytes(str);
}
std::vector<std::string> utils::loadNames(const std::string &path)
{
// load class names
std::vector<std::string> classNames;
std::ifstream infile(path);
if (infile.good())
{
std::string line;
while (getline(infile, line))
{
if (line.back() == '\r')
line.pop_back();
classNames.emplace_back(line);
}
infile.close();
}
else
{
std::cerr << "ERROR: Failed to access class name path: " << path << std::endl;
}
// set color
srand(time(0));
for (int i = 0; i < 2 * classNames.size(); i++)
{
int b = rand() % 256;
int g = rand() % 256;
int r = rand() % 256;
colors.push_back(cv::Scalar(b, g, r));
}
return classNames;
}
void utils::visualizeDetection(cv::Mat &im, std::vector<Yolov8Result> &results,
const std::vector<std::string> &classNames)
{
cv::Mat image = im.clone();
for (const Yolov8Result &result : results)
{
int x = result.box.x;
int y = result.box.y;
int conf = (int)std::round(result.conf * 100);
int classId = result.classId;
std::string label = classNames[classId] + " 0." + std::to_string(conf);
int baseline = 0;
cv::Size size = cv::getTextSize(label, cv::FONT_ITALIC, 0.4, 1, &baseline);
image(result.box).setTo(colors[classId + classNames.size()], result.boxMask);
cv::rectangle(image, result.box, colors[classId], 2);
cv::rectangle(image,
cv::Point(x, y), cv::Point(x + size.width, y + 12),
colors[classId], -1);
cv::putText(image, label,
cv::Point(x, y - 3 + 12), cv::FONT_ITALIC,
0.4, cv::Scalar(0, 0, 0), 1);
}
cv::addWeighted(im, 0.4, image, 0.6, 0, im);
}
void utils::letterbox(const cv::Mat &image, cv::Mat &outImage,
const cv::Size &newShape = cv::Size(640, 640),
const cv::Scalar &color = cv::Scalar(114, 114, 114),
bool auto_ = true,
bool scaleFill = false,
bool scaleUp = true,
int stride = 32)
{
cv::Size shape = image.size();
float r = std::min((float)newShape.height / (float)shape.height,
(float)newShape.width / (float)shape.width);
if (!scaleUp)
r = std::min(r, 1.0f);
float ratio[2]{r, r};
int newUnpad[2]{(int)std::round((float)shape.width * r),
(int)std::round((float)shape.height * r)};
auto dw = (float)(newShape.width - newUnpad[0]);
auto dh = (float)(newShape.height - newUnpad[1]);
if (auto_)
{
dw = (float)((int)dw % stride);
dh = (float)((int)dh % stride);
}
else if (scaleFill)
{
dw = 0.0f;
dh = 0.0f;
newUnpad[0] = newShape.width;
newUnpad[1] = newShape.height;
ratio[0] = (float)newShape.width / (float)shape.width;
ratio[1] = (float)newShape.height / (float)shape.height;
}
dw /= 2.0f;
dh /= 2.0f;
if (shape.width != newUnpad[0] && shape.height != newUnpad[1])
{
cv::resize(image, outImage, cv::Size(newUnpad[0], newUnpad[1]));
}
int top = int(std::round(dh - 0.1f));
int bottom = int(std::round(dh + 0.1f));
int left = int(std::round(dw - 0.1f));
int right = int(std::round(dw + 0.1f));
cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
}
void utils::scaleCoords(cv::Rect &coords,
cv::Mat &mask,
const float maskThreshold,
const cv::Size &imageShape,
const cv::Size &imageOriginalShape)
{
float gain = std::min((float)imageShape.height / (float)imageOriginalShape.height,
(float)imageShape.width / (float)imageOriginalShape.width);
int pad[2] = {(int)(((float)imageShape.width - (float)imageOriginalShape.width * gain) / 2.0f),
(int)(((float)imageShape.height - (float)imageOriginalShape.height * gain) / 2.0f)};
coords.x = (int)std::round(((float)(coords.x - pad[0]) / gain));
coords.x = std::max(0, coords.x);
coords.y = (int)std::round(((float)(coords.y - pad[1]) / gain));
coords.y = std::max(0, coords.y);
coords.width = (int)std::round(((float)coords.width / gain));
coords.width = std::min(coords.width, imageOriginalShape.width - coords.x);
coords.height = (int)std::round(((float)coords.height / gain));
coords.height = std::min(coords.height, imageOriginalShape.height - coords.y);
mask = mask(cv::Rect(pad[0], pad[1], imageShape.width - 2 * pad[0], imageShape.height - 2 * pad[1]));
cv::resize(mask, mask, imageOriginalShape, cv::INTER_LINEAR);
mask = mask(coords) > maskThreshold;
}
template <typename T>
T utils::clip(const T &n, const T &lower, const T &upper)
{
return std::max(lower, std::min(n, upper));
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment