C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C++使用YOLO模型

在C++中使用YOLO的四种实现方式

作者:LeonDL168

随着计算机视觉技术的不断发展,目标检测在许多应用领域都起到了重要作用,Yolo是一种流行的实时目标检测算法,其以高效的速度和较高的准确率而闻名,本文将介绍在C++中使用YOLO的几种方式,需要的朋友可以参考下

在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中高中等

选择建议

到此这篇关于在C++中使用YOLO的四种实现方式的文章就介绍到这了,更多相关C++使用YOLO模型内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文