libtorch部署手册

libtorch部署手册

x86架构

libtorch下载

在pytorch网站下载对应cuda版本的libtorch

Start Locally | PyTorch

请添加图片描述

下载后解压

unzip libtorch-shared-with-deps-2.3.1%2Bcu118.zip

示例

需要有cmake编译器大于3.16

进入libtorch目录

cd libtorch

创建example-app目录并进入

sudo mkdir example-app
cd example

创建编译文件CMakeLists.txt和example-app.cpp

// CMakeLists.txt
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(example-app)

# Set CUDA compiler
set(CMAKE_CUDA_COMPILER "/usr/local/cuda/bin/nvcc")  #cuda的nvcc路径,如果系统找不到的情况下需要添加
enable_language(CUDA)

find_package(Torch REQUIRED)
find_package(CUDA REQUIRED)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")

add_executable(example-app example-app.cpp)
target_link_libraries(example-app "${TORCH_LIBRARIES}")

set_property(TARGET example-app PROPERTY CXX_STANDARD 17)

# If using any CUDA functions or libraries, link them here
target_link_libraries(example-app ${CUDA_LIBRARIES})

# Copy Torch DLLs on Windows as needed
if (MSVC)
  file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
  add_custom_command(TARGET example-app
                     POST_BUILD
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                     ${TORCH_DLLS}
                     $<TARGET_FILE_DIR:example-app>)
endif (MSVC)

//example.cpp

#include <torch/torch.h>
#include <iostream>

int main() {
  torch::Tensor tensor = torch::rand({2, 3});
  std::cout << tensor << std::endl;
}

创建build目录并进入进行编译

sudo mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..  //替换为实际libtorch路径

cmake --build . --config Release  

运行example-app

./example-app

得到

(base) ➜  build ./example-app
 0.1078  0.4405  0.5437
 0.1887  0.9248  0.7328
[ CPUFloatType{2,3} ]

opencv3安装

需要先准备opencv3,根据下方链接进行安装

Ubuntu20安装OpenCV3(图解亲测)_ubuntu 20 安装opencv3-CSDN博客

调用模型进行图像分类推理

整体流程与示例类似

//CMakeLists.txt
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(test1-resnet18)

# Set CUDA compiler
set(CMAKE_CUDA_COMPILER "/usr/local/cuda/bin/nvcc")
enable_language(CUDA)

find_package(Torch REQUIRED)
find_package(CUDA REQUIRED)
find_package(OpenCV REQUIRED)  # 查找 OpenCV

add_executable(test1-resnet18 test1-resnet18.cpp)
include_directories(${OpenCV_INCLUDE_DIRS})  # 包含 OpenCV 头文件

target_link_libraries(test1-resnet18 "${TORCH_LIBRARIES}")
target_link_libraries(test1-resnet18 ${CUDA_LIBRARIES})
target_link_libraries(test1-resnet18 ${OpenCV_LIBS})  # 链接 OpenCV 库

set_property(TARGET test1-resnet18 PROPERTY CXX_STANDARD 17)

if(MSVC)
    file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
    add_custom_command(TARGET test1-resnet18
                       POST_BUILD
                       COMMAND ${CMAKE_COMMAND} -E copy_if_different
                       ${TORCH_DLLS}
                       $<TARGET_FILE_DIR:test1-resnet18>)
endif(MSVC)

//test1-resnet18.cpp
#include <torch/torch.h>
#include <torch/script.h>  // For torch::jit::load
#include <opencv2/opencv.hpp>
#include <iostream>
#include <memory>
#include <string>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui.hpp>

torch::Device device(torch::kCUDA);
auto image_preprocess(cv::Mat image){
    cv::cvtColor(image, image, CV_BGR2RGB);
    cv::Mat img_float;
    image.convertTo(img_float, CV_32F, 1.0/255);
    cv::resize(img_float, img_float, cv::Size(224, 224), cv::INTER_AREA);
    auto img_tensor = torch::from_blob(img_float.data, {1, 224, 224, 3}, torch::kFloat32);
    img_tensor = img_tensor.permute({0, 3, 1, 2});
    img_tensor[0][0] = img_tensor[0][0].sub_(0.485).div_(0.229);
    img_tensor[0][1] = img_tensor[0][1].sub_(0.456).div_(0.224);
    img_tensor[0][2] = img_tensor[0][2].sub_(0.406).div_(0.225);
    img_tensor = img_tensor.to(device);
    return img_tensor;
}
void infer_and_postprocess(torch::Tensor img_tensor,torch::jit::Module model){

    torch::NoGradGuard no_grad;
    auto output = model.forward({img_tensor}).toTensor();
    std::cout << output.sizes() << std::endl;
    std::cout << output.slice(/*dim=*/1,/*start=*/0,/*end=*/3) << '\n';

    // 1. Load labels
    std::string label_file = "/home/zhouhaojie/wxl/libtorch/test1-resnet18/synset_words.txt";
    std::ifstream rf(label_file.c_str());
    CHECK(rf) << "Unable to open labels file" << label_file;
    std::string line;
    std::vector<std::string> labels;
    while(std::getline(rf, line)){labels.push_back(line);}


    // 2. print predicted top-3 labels
    std::tuple<torch::Tensor, torch::Tensor> result = output.sort(-1, true);
    torch::Tensor top_scores = std::get<0>(result)[0];
    torch::Tensor top_idxs = std::get<1>(result)[0].toType(torch::kInt32);
    std::cout << top_scores[0].item<float>() << std::endl;

    for(int i=0; i<3;i++){
        int idx = top_idxs[i].item<int>();
        std::cout << "top-" << i+1 << " label: ";
        std::cout << labels[idx] << ",score: " << top_scores[0].item<float>() << std::endl;
    }
}

//摄像头输入
void camera_stream(const char* url,torch::jit::Module model){
    cv::VideoCapture cap(url); // 0 代表默认摄像头
    if (!cap.isOpened()) {
        std::cerr << "Error: Unable to open the camera" << std::endl;
        return;
    }

    cv::Mat frame;
    cv::namedWindow("Webcam Feed", cv::WINDOW_NORMAL); // 创建一个可调整大小的窗口
    cv::resizeWindow("Webcam Feed", 640, 480); // 设置窗口大小
    while (true) {
        cap >> frame; // 读取新的帧
        if (frame.empty()) {
            std::cerr << "Error: No frame captured from the camera" << std::endl;
            break;
        }

        auto img_tensor = image_preprocess(frame);
        infer_and_postprocess(img_tensor,model);
        cv::cvtColor(frame, frame, cv::COLOR_RGB2BGR);
        // 显示图像
        cv::imshow("Webcam Feed", frame);
        if (cv::waitKey(10) == 27) { // 按 'ESC' 键退出
            break;
        }
    }
    cap.release();  // 释放 VideoCapture 对象
    cv::destroyAllWindows();  // 关闭所有 OpenCV 窗口
}
void vedio_stream(const char* video_path, torch::jit::Module model) {
    cv::VideoCapture cap(video_path);  // 使用视频文件路径而非摄像头

    if (!cap.isOpened()) {
        std::cerr << "Error: Unable to open the video file" << std::endl;
        return;  // 无法打开视频文件时退出
    }

    cv::Mat frame;
    cv::namedWindow("Video Feed", cv::WINDOW_NORMAL);  // 更改窗口名称为"Video Feed"
    cv::resizeWindow("Video Feed", 640, 480);  // 设置窗口大小

    while (true) {
        cap >> frame;  // 从视频文件读取新的帧
        if (frame.empty()) {
            std::cerr << "Error: No frame captured from the video" << std::endl;
            break;  // 如果没有帧被捕获,退出循环
        }

        auto img_tensor = image_preprocess(frame);  // 假设已有的图像预处理函数
        infer_and_postprocess(img_tensor, model);  // 假设已有的模型推理和后处理函数

        cv::cvtColor(frame, frame, cv::COLOR_RGB2BGR);  // 确保显示的颜色格式正确
        cv::imshow("Video Feed", frame);  // 在窗口中显示处理后的帧

        if (cv::waitKey(10) == 27) {  // 按 'ESC' 键退出
            break;
        }
    }

    cap.release();  // 释放 VideoCapture 对象
    cv::destroyAllWindows();  // 关闭所有 OpenCV 窗口
}
// 图片输入
void image_stream(cv::Mat image,torch::jit::Module model){
    auto img_tensor = image_preprocess(image);
    infer_and_postprocess(img_tensor,model);

}

int main() {
    // 设置设备
    torch::Device device(torch::kCUDA);  // 使用 CUDA,如果不可用则使用 CPU: torch::kCPU

    // 加载预训练的模型
    auto model = torch::jit::load("/home/zhouhaojie/wxl/libtorch/test1-resnet18/traced_resnet18_model.pt");
    model.to(device);
    model.eval();
    // 摄像头输入
    // auto url = "rtsp://admin:vision123456@192.168.0.202/Streaming/Channels/1";
    // camera_stream(url,model);

    //图片输入
    // cv::Mat image = cv::imread("/home/zhouhaojie/wxl/libtorch/test1-resnet18/dog2.JPEG", cv::IMREAD_COLOR);
    // image_stream(image,model);

	//视频输入
    const char* video_path = "/home/zhouhaojie/wxl/libtorch/test1-resnet18/edge_box.mp4";
    vedio_stream(video_path,model);

    return 0;
}

调用模型进行图像识别推理(yolov8)

在官方中有写到如何使用ultralytics/examples/YOLOv8-LibTorch-CPP-Inference at main · ultralytics/ultralytics (github.com)

但存在问题:

RuntimeError: Expected all tensors to be on the same device, but found at least two devices, cuda:0 and cpu!

解决方案:需要在加载模型的时候直接加载在GPU上,而不能加载好后移动到GPU

原:
        torch::jit::script::Module yolo_model;
        yolo_model = torch::jit::load(model_path);
        yolo_model.eval();
        yolo_model.to(device, torch::kFloat32);
改进后:
        torch::jit::script::Module yolo_model;
        yolo_model = torch::jit::load(model_path, device);
        yolo_model.eval();
//CMakeLists.txt
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
project(test-yolo)

# Set CUDA compiler
set(CMAKE_CUDA_COMPILER "/usr/local/cuda/bin/nvcc")
enable_language(CUDA)

find_package(Torch REQUIRED)
find_package(CUDA REQUIRED)
find_package(OpenCV REQUIRED)  # 查找 OpenCV

add_executable(test-yolo main.cpp)
include_directories(${OpenCV_INCLUDE_DIRS})  # 包含 OpenCV 头文件

target_link_libraries(test-yolo "${TORCH_LIBRARIES}")
target_link_libraries(test-yolo ${CUDA_LIBRARIES})
target_link_libraries(test-yolo ${OpenCV_LIBS})  # 链接 OpenCV 库
target_link_libraries(test-yolo pthread)
set_property(TARGET test-yolo PROPERTY CXX_STANDARD 17)

if(MSVC)
    file(GLOB TORCH_DLLS "${TORCH_INSTALL_PREFIX}/lib/*.dll")
    add_custom_command(TARGET test-yolo
                       POST_BUILD
                       COMMAND ${CMAKE_COMMAND} -E copy_if_different
                       ${TORCH_DLLS}
                       $<TARGET_FILE_DIR:test-yolo>)
endif(MSVC)

//multi_thread_yolo.cpp
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <torch/torch.h>
#include <torch/script.h>
#include <string>
#include <thread>
#include <mutex>
#include <vector>

std::mutex mtx;
using torch::indexing::Slice;
using torch::indexing::None;

float generate_scale(cv::Mat& image, const std::vector<int>& target_size) {
    int origin_w = image.cols;
    int origin_h = image.rows;

    int target_h = target_size[0];
    int target_w = target_size[1];

    float ratio_h = static_cast<float>(target_h) / static_cast<float>(origin_h);
    float ratio_w = static_cast<float>(target_w) / static_cast<float>(origin_w);
    float resize_scale = std::min(ratio_h, ratio_w);
    return resize_scale;
}

float letterbox(cv::Mat &input_image, cv::Mat &output_image, const std::vector<int> &target_size) {
    if (input_image.cols == target_size[1] && input_image.rows == target_size[0]) {
        if (input_image.data == output_image.data) {
            return 1.;
        } else {
            output_image = input_image.clone();
            return 1.;
        }
    }

    float resize_scale = generate_scale(input_image, target_size);
    int new_shape_w = std::round(input_image.cols * resize_scale);
    int new_shape_h = std::round(input_image.rows * resize_scale);
    float padw = (target_size[1] - new_shape_w) / 2.;
    float padh = (target_size[0] - new_shape_h) / 2.;

    int top = std::round(padh - 0.1);
    int bottom = std::round(padh + 0.1);
    int left = std::round(padw - 0.1);
    int right = std::round(padw + 0.1);

    cv::resize(input_image, output_image,
               cv::Size(new_shape_w, new_shape_h),
               0, 0, cv::INTER_AREA);

    cv::copyMakeBorder(output_image, output_image, top, bottom, left, right,
                       cv::BORDER_CONSTANT, cv::Scalar(114, 114, 114));
    return resize_scale;
}

torch::Tensor xyxy2xywh(const torch::Tensor& x) {
    auto y = torch::empty_like(x);
    y.index_put_({"...", 0}, (x.index({"...", 0}) + x.index({"...", 2})).div(2));
    y.index_put_({"...", 1}, (x.index({"...", 1}) + x.index({"...", 3})).div(2));
    y.index_put_({"...", 2}, x.index({"...", 2}) - x.index({"...", 0}));
    y.index_put_({"...", 3}, x.index({"...", 3}) - x.index({"...", 1}));
    return y;
}

torch::Tensor xywh2xyxy(const torch::Tensor& x) {
    auto y = torch::empty_like(x);
    auto dw = x.index({"...", 2}).div(2);
    auto dh = x.index({"...", 3}).div(2);
    y.index_put_({"...", 0}, x.index({"...", 0}) - dw);
    y.index_put_({"...", 1}, x.index({"...", 1}) - dh);
    y.index_put_({"...", 2}, x.index({"...", 0}) + dw);
    y.index_put_({"...", 3}, x.index({"...", 1}) + dh);
    return y;
}

torch::Tensor nms(const torch::Tensor& bboxes, const torch::Tensor& scores, float iou_threshold) {
    if (bboxes.numel() == 0)
        return torch::empty({0}, bboxes.options().dtype(torch::kLong));
    auto x1_t = bboxes.select(1, 0).contiguous();
    auto y1_t = bboxes.select(1, 1).contiguous();
    auto x2_t = bboxes.select(1, 2).contiguous();
    auto y2_t = bboxes.select(1, 3).contiguous();

    torch::Tensor areas_t = (x2_t - x1_t) * (y2_t - y1_t);
    auto order_t = std::get<1>(
        scores.sort(/*stable=*/true, /*dim=*/0, /* descending=*/true));
    auto ndets = bboxes.size(0);
    torch::Tensor suppressed_t = torch::zeros({ndets}, bboxes.options().dtype(torch::kByte));
    torch::Tensor keep_t = torch::zeros({ndets}, bboxes.options().dtype(torch::kLong));
    auto suppressed = suppressed_t.data_ptr<uint8_t>();
    auto keep = keep_t.data_ptr<int64_t>();
    auto order = order_t.data_ptr<int64_t>();
    auto x1 = x1_t.data_ptr<float>();
    auto y1 = y1_t.data_ptr<float>();
    auto x2 = x2_t.data_ptr<float>();
    auto y2 = y2_t.data_ptr<float>();
    auto areas = areas_t.data_ptr<float>();
    int64_t num_to_keep = 0;
    for (int64_t _i = 0; _i < ndets; _i++) {
        auto i = order[_i];   //这里有问题
        if (suppressed[i] == 1)
            continue;
        keep[num_to_keep++] = i;
        auto ix1 = x1[i];
        auto iy1 = y1[i];
        auto ix2 = x2[i];
        auto iy2 = y2[i];
        auto iarea = areas[i];
        for (int64_t _j = _i + 1; _j < ndets; _j++) {
            auto j = order[_j];
            if (suppressed[j] == 1)
                continue;
            auto xx1 = std::max(ix1, x1[j]);
            auto yy1 = std::max(iy1, y1[j]);
            auto xx2 = std::min(ix2, x2[j]);
            auto yy2 = std::min(iy2, y2[j]);

            auto w = std::max(static_cast<float>(0), xx2 - xx1);
            auto h = std::max(static_cast<float>(0), yy2 - yy1);
            auto inter = w * h;
            auto ovr = inter / (iarea + areas[j] - inter);
            if (ovr > iou_threshold)
                suppressed[j] = 1;
        }
    }
    return keep_t.narrow(0, 0, num_to_keep);
}


torch::Tensor non_max_suppression(torch::Tensor& prediction, float conf_thres = 0.25, float iou_thres = 0.45, int max_det = 300) {
    auto bs = prediction.size(0);
    auto nc = prediction.size(1) - 4;
    auto nm = prediction.size(1) - nc - 4;
    auto mi = 4 + nc;
    auto xc = prediction.index({Slice(), Slice(4, mi)}).amax(1) > conf_thres;

    prediction = prediction.transpose(-1, -2);
    prediction.index_put_({"...", Slice({None, 4})}, xywh2xyxy(prediction.index({"...", Slice(None, 4)})));

    std::vector<torch::Tensor> output;
    for (int i = 0; i < bs; i++) {
        output.push_back(torch::zeros({0, 6 + nm}, prediction.device()));
    }
    
    for (int xi = 0; xi < prediction.size(0); xi++) {
        auto x = prediction[xi];
        x = x.index({xc[xi]});
        auto x_split = x.split({4, nc, nm}, 1);
        auto box = x_split[0], cls = x_split[1], mask = x_split[2];
        auto [conf, j] = cls.max(1, true);
        x = torch::cat({box, conf, j.toType(torch::kFloat), mask}, 1);
        x = x.index({conf.view(-1) > conf_thres});
        int n = x.size(0);
        if (!n) { continue; }
        // NMS
        auto c = x.index({Slice(), Slice{5, 6}}) * 7680;
        auto boxes = x.index({Slice(), Slice(None, 4)}) + c;
        auto scores = x.index({Slice(), 4});
        auto i = nms(boxes, scores, iou_thres);
        i = i.index({Slice(None, max_det)});
        output[xi] = x.index({i});
    }

    return torch::stack(output);
}

torch::Tensor clip_boxes(torch::Tensor& boxes, const std::vector<int>& shape) {
    boxes.index_put_({"...", 0}, boxes.index({"...", 0}).clamp(0, shape[1]));
    boxes.index_put_({"...", 1}, boxes.index({"...", 1}).clamp(0, shape[0]));
    boxes.index_put_({"...", 2}, boxes.index({"...", 2}).clamp(0, shape[1]));
    boxes.index_put_({"...", 3}, boxes.index({"...", 3}).clamp(0, shape[0]));
    return boxes;
}

torch::Tensor scale_boxes(const std::vector<int>& img1_shape, torch::Tensor& boxes, const std::vector<int>& img0_shape) {
    auto gain = (std::min)((float)img1_shape[0] / img0_shape[0], (float)img1_shape[1] / img0_shape[1]);
    auto pad0 = std::round((float)(img1_shape[1] - img0_shape[1] * gain) / 2. - 0.1);
    auto pad1 = std::round((float)(img1_shape[0] - img0_shape[0] * gain) / 2. - 0.1);

    boxes.index_put_({"...", 0}, boxes.index({"...", 0}) - pad0);
    boxes.index_put_({"...", 2}, boxes.index({"...", 2}) - pad0);
    boxes.index_put_({"...", 1}, boxes.index({"...", 1}) - pad1);
    boxes.index_put_({"...", 3}, boxes.index({"...", 3}) - pad1);
    boxes.index_put_({"...", Slice(None, 4)}, boxes.index({"...", Slice(None, 4)}).div(gain));
    return boxes;
}
void process_video(const std::string& camera_url, torch::jit::script::Module& yolo_model, const torch::Device& device, const std::vector<std::string>& classes) {
    cv::VideoCapture cap(camera_url); // 打开摄像头
    if (!cap.isOpened()) {
        std::cerr << "无法打开视频捕捉设备: " << camera_url << std::endl;
        return;
    }

    cv::Mat frame;
    cv::namedWindow("Webcam Feed - " + camera_url, cv::WINDOW_NORMAL); // 创建一个可调整大小的窗口
    cv::resizeWindow("Webcam Feed - " + camera_url, 640, 480); // 设置窗口大小
    while (cap.read(frame)) {
        cv::Mat input_image;
        letterbox(frame, input_image, {640, 640});

        torch::Tensor image_tensor = torch::from_blob(input_image.data, {input_image.rows, input_image.cols, 3}, torch::kByte).to(device);
        image_tensor = image_tensor.toType(torch::kFloat32).div(255);
        image_tensor = image_tensor.permute({2, 0, 1});
        image_tensor = image_tensor.unsqueeze(0);
        std::vector<torch::jit::IValue> inputs {image_tensor};

        // Inference
        torch::Tensor output = yolo_model.forward(inputs).toTensor().cpu();
        auto keep = non_max_suppression(output)[0];
        auto boxes = keep.index({Slice(), Slice(None, 4)});
        keep.index_put_({Slice(), Slice(None, 4)}, scale_boxes({input_image.rows, input_image.cols}, boxes, {frame.rows, frame.cols}));

        // Draw bounding boxes and labels
        std::lock_guard<std::mutex> lock(mtx);
        std::cout << camera_url << "检测结果:" << std::endl;
        for (int i = 0; i < keep.size(0); i++) {
            int x1 = keep[i][0].item().toFloat();
            int y1 = keep[i][1].item().toFloat();
            int x2 = keep[i][2].item().toFloat();
            int y2 = keep[i][3].item().toFloat();
            float conf = keep[i][4].item().toFloat();
            int cls = keep[i][5].item().toInt();
            std::cout << "Rect: [" << x1 << "," << y1 << "," << x2 << "," << y2 << "]  Conf: " << conf << "  Class: " << classes[cls] << std::endl;
            cv::rectangle(frame, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(255, 0, 0), 2);
            std::string label = classes[cls] + " " + std::to_string(conf);
            cv::putText(frame, label, cv::Point(x1, y1 - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1);
        }

        // Display the resulting frame
        cv::imshow("Webcam Feed - " + camera_url, frame);
        if (cv::waitKey(1) == 27) { // 按 'ESC' 键退出
            break;
        }
    }
    cap.release();
    cv::destroyAllWindows();
}
int main() {
    // Device
    torch::Device device(torch::cuda::is_available() ? torch::kCUDA :torch::kCPU);
    // Note that in this example the classes are hard-coded
    std::vector<std::string> classes {"person", "bicycle", "car", "motorcycle", "airplane", "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",
                                      "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
                                      "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"};


    //图片输入
    // try {
    //     // Load the model (e.g. yolov8s.torchscript)
    //     std::string model_path = "/home/zhouhaojie/wxl/libtorch/test-yolo/ultralytics/yolov8s.torchscript";
    //     torch::jit::script::Module yolo_model;
    //     yolo_model = torch::jit::load(model_path,device);
    //     yolo_model.eval();
    //     // yolo_model.to(device, torch::kFloat32);
    //     std::cout << "模型加载成功" << std::endl;

    //     // Load image and preprocess
    //     cv::Mat image = cv::imread("/home/zhouhaojie/wxl/libtorch/test1-resnet18/dog2.JPEG");
    //     cv::Mat input_image;
    //     letterbox(image, input_image, {640, 640});

    //     torch::Tensor image_tensor = torch::from_blob(input_image.data, {input_image.rows, input_image.cols, 3}, torch::kByte).to(device);
    //     image_tensor = image_tensor.toType(torch::kFloat32).div(255);
    //     image_tensor = image_tensor.permute({2, 0, 1});
    //     image_tensor = image_tensor.unsqueeze(0);
    //     std::vector<torch::jit::IValue> inputs {image_tensor};

    //     // Inference
    //     torch::Tensor output = yolo_model.forward(inputs).toTensor().cpu();
    //     // NMS
    //     auto keep = non_max_suppression(output)[0];
    //     auto boxes = keep.index({Slice(), Slice(None, 4)});
    //     keep.index_put_({Slice(), Slice(None, 4)}, scale_boxes({input_image.rows, input_image.cols}, boxes, {image.rows, image.cols}));

    //     // Show the results
    //     for (int i = 0; i < keep.size(0); i++) {
    //         int x1 = keep[i][0].item().toFloat();
    //         int y1 = keep[i][1].item().toFloat();
    //         int x2 = keep[i][2].item().toFloat();
    //         int y2 = keep[i][3].item().toFloat();
    //         float conf = keep[i][4].item().toFloat();
    //         int cls = keep[i][5].item().toInt();
    //         std::cout << "Rect: [" << x1 << "," << y1 << "," << x2 << "," << y2 << "]  Conf: " << conf << "  Class: " << classes[cls] << std::endl;
    //         cv::rectangle(image, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(255, 0, 0), 2);
    //         std::string label = classes[cls] + " " + std::to_string(conf);
    //         cv::putText(image, label, cv::Point(x1, y1 - 5), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1);
    //     }
    //     cv::imwrite("/home/zhouhaojie/wxl/libtorch/test-yolo/dog_detected.jpg", image);
    //     std::cout << "检测结果已保存到 bus_detected.jpg" << std::endl;
    // } catch (const c10::Error& e) {
    //     std::cout << e.msg() << std::endl;
    // }


    //摄像头输入
    try {
        // Load the model (e.g. yolov8s.torchscript)
        std::string model_path = "/home/zhouhaojie/wxl/libtorch/test-yolo/ultralytics/yolov8s.torchscript";
        torch::jit::script::Module yolo_model;
        yolo_model = torch::jit::load(model_path, device);
        yolo_model.eval();
        std::cout << "模型加载成功" << std::endl;

        // List of camera URLs
        std::vector<std::string> camera_urls = {
            "rtsp://admin:vision123456@192.168.0.201/Streaming/Channels/1",
            "rtsp://admin:vision123456@192.168.0.202/Streaming/Channels/1"
            // Add more camera URLs here
        };

        // Start threads for each camera
        std::vector<std::thread> threads;
        for (const auto& camera_url : camera_urls) {
            threads.emplace_back(process_video, camera_url, std::ref(yolo_model), std::ref(device), std::ref(classes));
        }

        // Wait for all threads to finish
        for (auto& t : threads) {
            t.join();
        }
    } catch (const c10::Error& e) {
        std::cout << e.msg() << std::endl;
    }
    return 0;
}

arm架构(jetson)

pytorch下载

在nvidia官网下载cuda对应版本的pytorch,并按照官方指令安装

CUDA Toolkit 12.4 Downloads | NVIDIA Developer

请添加图片描述

安装好后提取出其中的share、include、bin、lib即可。其他与x86架构教程相似。

libtorch 是一个用于C++的PyTorch C++前端库,它提供了一个用于构建、训练和部署深度学习模型的高级API。而YOLO(You Only Look Once)是一种流行的目标检测算法,能够在图像中实时地检测和定位多个目标。部署 YOLO 模型需要以下步骤: 1. 安装 PyTorchlibtorch:首先,需要安装 PyTorchlibtorchPyTorch 用于训练模型,而 libtorch 用于将训练好的模型部署到 C++ 环境中。 2. 训练 YOLO 模型:在 PyTorch 中,可以使用现成的 YOLO 模型实现,也可以自定义模型。通过加载训练数据集,定义模型结构并训练模型,可以得到一个经过训练的 YOLO 模型。 3. 导出模型:在训练完成后,可以通过 PyTorch 提供的导出功能将模型导出为 ONNX 格式或 TorchScript 格式,以便在 C++ 环境中使用。 4. 使用 libtorch 进行部署:在 C++ 程序中,使用 libtorch 库加载导出的模型,并编写代码进行图像的预处理和后处理。通过调用模型的前向传播函数,可以输入图像并获取模型的输出,即目标检测的结果。 5. 图像的预处理和后处理:在图像的预处理过程中,需要将图像转换为模型可接受的输入格式,例如将图像进行缩放、归一化和通道转换等操作。在模型的输出结果中,会包含被检测到的目标的位置和类别信息,可以通过解析输出结果并进行后处理,如非最大值抑制和边界框绘制等,来得到最终的目标检测结果。 综上所述,部署 YOLO 模型需要使用 libtorch 进行模型的加载和前向传播,同时编写相关的预处理和后处理代码,以实现目标检测任务的部署
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值