在C++中使用YOLO的四種實(shí)現(xiàn)方式
在C++中使用YOLO進(jìn)行目標(biāo)檢測(cè)主要有以下幾種方式,每種方式都有其特點(diǎn)和適用場(chǎng)景:
方式一:使用OpenCV DNN模塊(最簡(jiǎn)單)
特點(diǎn):無(wú)需依賴Darknet,僅需OpenCV庫(kù),支持ONNX模型,跨平臺(tái)兼容。
適用場(chǎng)景:快速原型開(kāi)發(fā)、CPU/GPU通用部署。
步驟:
轉(zhuǎn)換模型:將YOLOv5/YOLOv8導(dǎo)出為ONNX格式。
# YOLOv5 python export.py --weights yolov5s.pt --include onnx # YOLOv8 yolo export model=yolov8n.pt format=onnx
C++代碼實(shí)現(xiàn):
#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);
// 繪制結(jié)果
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框架(原生支持)
特點(diǎn):YOLO官方框架,支持C/C++接口,性能優(yōu)化好,但依賴復(fù)雜。
適用場(chǎng)景:需要完整復(fù)現(xiàn)YOLO訓(xùn)練和推理流程。
步驟:
編譯Darknet:
git clone https://github.com/AlexeyAB/darknet cd darknet make # 修改Makefile以啟用CUDA/CUDNN
C++代碼實(shí)現(xiàn):
#include "darknet.h"
#include <iostream>
using namespace std;
int main() {
// 加載網(wǎng)絡(luò)
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);
// 解析結(jié)果
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);
// 繪制結(jié)果
// ...
// 釋放資源
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加速(高性能)
特點(diǎn):NVIDIA官方推理優(yōu)化工具,專為GPU設(shè)計(jì),速度最快。
適用場(chǎng)景:嵌入式設(shè)備(Jetson)或GPU服務(wù)器上的高性能部署。
步驟:
轉(zhuǎn)換模型:將ONNX轉(zhuǎn)換為T(mén)ensorRT引擎:
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++代碼實(shí)現(xiàn):
#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();
// 準(zhǔn)備輸入數(shù)據(jù)
cv::Mat image = cv::imread("test.jpg");
cv::Mat input = preprocess(image, 640, 640); // 自定義預(yù)處理函數(shù)
// 分配GPU內(nèi)存
void* buffers[2];
cudaMalloc(&buffers[0], 3 * 640 * 640 * sizeof(float));
cudaMalloc(&buffers[1], 25200 * 85 * sizeof(float));
// 拷貝數(shù)據(jù)到GPU
cudaMemcpy(buffers[0], input.data, 3 * 640 * 640 * sizeof(float), cudaMemcpyHostToDevice);
// 執(zhí)行推理
context->executeV2(buffers);
// 拷貝結(jié)果回CPU
std::vector<float> output(25200 * 85);
cudaMemcpy(output.data(), buffers[1], 25200 * 85 * sizeof(float), cudaMemcpyDeviceToHost);
// 解析結(jié)果
// ...
// 釋放資源
cudaFree(buffers[0]);
cudaFree(buffers[1]);
context->destroy();
engine->destroy();
runtime->destroy();
return 0;
}
方式四:使用libtorch(PyTorch C++前端)
特點(diǎn):直接加載PyTorch模型,無(wú)需轉(zhuǎn)換,支持動(dòng)態(tài)圖。
適用場(chǎng)景:需要與PyTorch訓(xùn)練代碼無(wú)縫銜接的場(chǎng)景。
步驟:
導(dǎo)出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++代碼實(shí)現(xiàn):
#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
// 準(zhǔn)備輸入
cv::Mat image = cv::imread("test.jpg");
cv::Mat input = preprocess(image, 640, 640); // 轉(zhuǎn)換為[0,1]的浮點(diǎn)數(shù)
// 轉(zhuǎn)換為T(mén)ensor
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();
// 解析結(jié)果
// ...
return 0;
}
性能對(duì)比
| 方式 | 速度(FPS) | 依賴復(fù)雜度 | 部署難度 | 靈活性 |
|---|---|---|---|---|
| OpenCV DNN | 中 | 低 | 簡(jiǎn)單 | 中 |
| Darknet | 高 | 高 | 中等 | 高 |
| TensorRT | 極高 | 高 | 復(fù)雜 | 低 |
| libtorch | 中高 | 中 | 中等 | 高 |
選擇建議
- 快速開(kāi)發(fā):選OpenCV DNN。
- 極致性能:選TensorRT。
- 完整功能:選Darknet。
- PyTorch生態(tài):選libtorch。
到此這篇關(guān)于在C++中使用YOLO的四種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)C++使用YOLO模型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C# networkcomms 3.0實(shí)現(xiàn)模擬登陸總結(jié)
這篇文章主要介紹了C# networkcomms 3.0實(shí)現(xiàn)模擬登陸總結(jié),需要的朋友可以參考下2017-06-06
C#中的Task.WaitAll和Task.WaitAny方法介紹
這篇文章介紹了C#中的Task.WaitAll和Task.WaitAny方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
winform實(shí)現(xiàn)拖動(dòng)文件到窗體上的方法
這篇文章主要介紹了winform實(shí)現(xiàn)拖動(dòng)文件到窗體上的方法,以實(shí)例分析了C#中WinForm操作窗體及文件的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09
C# 通過(guò)同步和異步實(shí)現(xiàn)優(yōu)化做早餐的時(shí)間
本文以一個(gè)簡(jiǎn)單的小例子—如何做一頓早餐及如何優(yōu)化做早餐的時(shí)間來(lái)讓大家具體了解一下同步和異步方法的區(qū)別,需要的朋友可以參考一下2021-12-12
Entity Framework主從表數(shù)據(jù)加載方式
這篇文章介紹了Entity Framework主從表數(shù)據(jù)加載方式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06

