在C++中使用YOLO的四種實現方式
在C++中使用YOLO進行目標檢測主要有以下幾種方式,每種方式都有其特點和適用場景:
方式一:使用OpenCV DNN模塊(最簡單)
特點:無需依賴Darknet,僅需OpenCV庫,支持ONNX模型,跨平臺兼容。
適用場景:快速原型開發(fā)、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++接口,性能優(yōu)化好,但依賴復雜。
適用場景:需要完整復現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官方推理優(yōu)化工具,專為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); // 執(zhí)行推理 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模型,無需轉換,支持動態(tài)圖。
適用場景:需要與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 | 中高 | 中 | 中等 | 高 |
選擇建議
- 快速開發(fā):選OpenCV DNN。
- 極致性能:選TensorRT。
- 完整功能:選Darknet。
- PyTorch生態(tài):選libtorch。
到此這篇關于在C++中使用YOLO的四種實現方式的文章就介紹到這了,更多相關C++使用YOLO模型內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#中的Task.WaitAll和Task.WaitAny方法介紹
這篇文章介紹了C#中的Task.WaitAll和Task.WaitAny方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04