在c++中使用yolo进行目标检测主要有以下几种方式,每种方式都有其特点和适用场景:
方式一:使用opencv dnn模块(最简单)
特点:无需依赖darknet,仅需opencv库,支持onnx模型,跨平台兼容。
适用场景:快速原型开发、cpu/gpu通用部署。
步骤:
转换模型:将yolov5/yolov8导出为onnx格式。
# yolov5 python export.py --weights yolov5s.pt --include onnx # yolov8 yolo export model=yolov8n.pt format=onnx
c++代码实现:
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace dnn;
using namespace std;
struct detection {
int classid;
float confidence;
rect box;
};
int main() {
// 加载模型
net net = readnet("yolov5s.onnx");
net.setpreferablebackend(dnn_backend_opencv);
net.setpreferabletarget(dnn_target_cpu); // 或 dnn_target_cuda
// 读取图像
mat image = imread("test.jpg");
mat blob;
blobfromimage(image, blob, 1/255.0, size(640, 640), scalar(), true, false);
net.setinput(blob);
// 前向传播
vector<mat> outputs;
net.forward(outputs, net.getunconnectedoutlayersnames());
// 解析输出
vector<int> classids;
vector<float> confidences;
vector<rect> boxes;
float* data = (float*)outputs[0].data;
for (int i = 0; i < outputs[0].rows; ++i) {
mat scores = outputs[0].row(i).colrange(5, outputs[0].cols);
point classidpoint;
double confidence;
minmaxloc(scores, 0, &confidence, 0, &classidpoint);
if (confidence > 0.4) {
int centerx = (int)(data[i * 85 + 0] * image.cols);
int centery = (int)(data[i * 85 + 1] * image.rows);
int width = (int)(data[i * 85 + 2] * image.cols);
int height = (int)(data[i * 85 + 3] * image.rows);
int left = centerx - width / 2;
int top = centery - height / 2;
classids.push_back(classidpoint.x);
confidences.push_back((float)confidence);
boxes.push_back(rect(left, top, width, height));
}
}
// 非极大值抑制
vector<int> indices;
nmsboxes(boxes, confidences, 0.4, 0.5, indices);
// 绘制结果
for (int idx : indices) {
rectangle(image, boxes[idx], scalar(0, 255, 0), 2);
string label = format("class: %d, conf: %.2f", classids[idx], confidences[idx]);
puttext(image, label, point(boxes[idx].x, boxes[idx].y - 10),
font_hershey_simplex, 0.5, scalar(0, 255, 0), 2);
}
imshow("detection", image);
waitkey(0);
return 0;
}
编译命令:
g++ yolo_opencv.cpp -o yolo `pkg-config --cflags --libs opencv4`
方式二:使用darknet框架(原生支持)
特点:yolo官方框架,支持c/c++接口,性能优化好,但依赖复杂。
适用场景:需要完整复现yolo训练和推理流程。
步骤:
编译darknet:
git clone https://github.com/alexeyab/darknet cd darknet make # 修改makefile以启用cuda/cudnn
c++代码实现:
#include "darknet.h"
#include <iostream>
using namespace std;
int main() {
// 加载网络
network* net = load_network("cfg/yolov4.cfg", "yolov4.weights", 0);
set_batch_network(net, 1);
// 加载图像
image im = load_image_color("test.jpg", 0, 0);
image sized = letterbox_image(im, net->w, net->h);
// 前向传播
layer l = net->layers[net->n - 1];
float* x = sized.data;
network_predict(net, x);
// 解析结果
int nboxes = 0;
detection* dets = get_network_boxes(net, im.w, im.h, 0.5, 0.5, 0, 1, &nboxes);
do_nms_sort(dets, nboxes, l.classes, 0.45);
// 绘制结果
// ...
// 释放资源
free_detections(dets, nboxes);
free_image(im);
free_image(sized);
free_network(net);
return 0;
}
编译命令:
g++ yolo_darknet.cpp -o yolo -i/path/to/darknet/include -l/path/to/darknet/lib -ldarknet -lpthread -lcuda -lcudnn -lopencv_core -lopencv_imgproc -lopencv_imgcodecs
方式三:使用tensorrt加速(高性能)
特点:nvidia官方推理优化工具,专为gpu设计,速度最快。
适用场景:嵌入式设备(jetson)或gpu服务器上的高性能部署。
步骤:
转换模型:将onnx转换为tensorrt引擎:
import tensorrt as trt
def build_engine(onnx_path, engine_path):
logger = trt.logger(trt.logger.warning)
builder = trt.builder(logger)
network = builder.create_network(1 << int(trt.networkdefinitioncreationflag.explicit_batch))
parser = trt.onnxparser(network, logger)
with open(onnx_path, 'rb') as model:
parser.parse(model.read())
config = builder.create_builder_config()
config.max_workspace_size = 1 << 30 # 1gb
config.set_flag(trt.builderflag.fp16) # 启用fp16
engine = builder.build_engine(network, config)
with open(engine_path, 'wb') as f:
f.write(engine.serialize())
return engine
build_engine("yolov5s.onnx", "yolov5s.engine")
c++代码实现:
#include <nvinfer.h>
#include <nvonnxparser.h>
#include <opencv2/opencv.hpp>
#include <cuda_runtime_api.h>
// tensorrt logger
class logger : public nvinfer1::ilogger {
void log(severity severity, const char* msg) noexcept override {
if (severity != severity::kinfo) std::cerr << "tensorrt: " << msg << std::endl;
}
};
int main() {
// 加载引擎
logger logger;
std::ifstream file("yolov5s.engine", std::ios::binary);
std::vector<char> enginedata((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
nvinfer1::iruntime* runtime = nvinfer1::createinferruntime(logger);
nvinfer1::icudaengine* engine = runtime->deserializecudaengine(enginedata.data(), enginedata.size());
nvinfer1::iexecutioncontext* context = engine->createexecutioncontext();
// 准备输入数据
cv::mat image = cv::imread("test.jpg");
cv::mat input = preprocess(image, 640, 640); // 自定义预处理函数
// 分配gpu内存
void* buffers[2];
cudamalloc(&buffers[0], 3 * 640 * 640 * sizeof(float));
cudamalloc(&buffers[1], 25200 * 85 * sizeof(float));
// 拷贝数据到gpu
cudamemcpy(buffers[0], input.data, 3 * 640 * 640 * sizeof(float), cudamemcpyhosttodevice);
// 执行推理
context->executev2(buffers);
// 拷贝结果回cpu
std::vector<float> output(25200 * 85);
cudamemcpy(output.data(), buffers[1], 25200 * 85 * sizeof(float), cudamemcpydevicetohost);
// 解析结果
// ...
// 释放资源
cudafree(buffers[0]);
cudafree(buffers[1]);
context->destroy();
engine->destroy();
runtime->destroy();
return 0;
}
方式四:使用libtorch(pytorch c++前端)
特点:直接加载pytorch模型,无需转换,支持动态图。
适用场景:需要与pytorch训练代码无缝衔接的场景。
步骤:
导出torchscript模型:
import torch
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=true)
model.eval()
traced_script_module = torch.jit.trace(model, torch.rand(1, 3, 640, 640))
traced_script_module.save("yolov5s_scripted.pt")
c++代码实现:
#include <torch/script.h>
#include <opencv2/opencv.hpp>
int main() {
// 加载模型
torch::jit::script::module module = torch::jit::load("yolov5s_scripted.pt");
module.to(at::kcuda); // 若有gpu
// 准备输入
cv::mat image = cv::imread("test.jpg");
cv::mat input = preprocess(image, 640, 640); // 转换为[0,1]的浮点数
// 转换为tensor
torch::tensor tensor = torch::from_blob(input.data, {1, 3, 640, 640}, torch::kfloat32);
tensor = tensor.to(at::kcuda); // 若有gpu
// 前向传播
std::vector<torch::jit::ivalue> inputs;
inputs.push_back(tensor);
torch::tensor output = module.forward(inputs).totensor();
// 解析结果
// ...
return 0;
}
性能对比
| 方式 | 速度(fps) | 依赖复杂度 | 部署难度 | 灵活性 |
|---|---|---|---|---|
| opencv dnn | 中 | 低 | 简单 | 中 |
| darknet | 高 | 高 | 中等 | 高 |
| tensorrt | 极高 | 高 | 复杂 | 低 |
| libtorch | 中高 | 中 | 中等 | 高 |
选择建议
- 快速开发:选opencv dnn。
- 极致性能:选tensorrt。
- 完整功能:选darknet。
- pytorch生态:选libtorch。
到此这篇关于在c++中使用yolo的四种实现方式的文章就介绍到这了,更多相关c++使用yolo模型内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论