使用libtorch加载YOLOv8生成的torchscript文件进行目标检测

      在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe  工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容如下:

path: ../datasets/melon # dataset root dir
train: images/train # train images (relative to 'path')
val: images/val  # val images (relative to 'path')
test: # test images (optional)# Classes
names:0: watermelon1: wintermelon

      使用以下python脚本进行训练生成torchscript文件

import argparse
import colorama
from ultralytics import YOLOdef parse_args():parser = argparse.ArgumentParser(description="YOLOv8 object detect")parser.add_argument("--yaml", required=True, type=str, help="yaml file")parser.add_argument("--epochs", required=True, type=int, help="number of training")args = parser.parse_args()return argsdef train(yaml, epochs):model = YOLO("yolov8n.pt") # load a pretrained modelresults = model.train(data=yaml, epochs=epochs, imgsz=640) # train the modelmetrics = model.val() # It'll automatically evaluate the data you trained, no arguments needed, dataset and settings rememberedmodel.export(format="onnx") #, dynamic=True) # export the model, cannot specify dynamic=True, opencv does not support# model.export(format="onnx", opset=12, simplify=True, dynamic=False, imgsz=640)model.export(format="torchscript") # libtorchif __name__ == "__main__":colorama.init()args = parse_args()train(args.yaml, args.epochs)print(colorama.Fore.GREEN + "====== execution completed ======")

      以下是使用libtorch接口加载torchscript文件进行目标检测的实现代码:

namespace {constexpr bool cuda_enabled{ false };
constexpr int image_size[2]{ 640, 640 }; // {height,width}, input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 6, 8400)
constexpr float model_score_threshold{ 0.45 }; // confidence threshold
constexpr float model_nms_threshold{ 0.50 }; // iou threshold#ifdef _MSC_VER
constexpr char* onnx_file{ "../../../data/best.onnx" };
constexpr char* torchscript_file{ "../../../data/best.torchscript" };
constexpr char* images_dir{ "../../../data/images/predict" };
constexpr char* result_dir{ "../../../data/result" };
constexpr char* classes_file{ "../../../data/images/labels.txt" };
#else
constexpr char* onnx_file{ "data/best.onnx" };
constexpr char* torchscript_file{ "data/best.torchscript" };
constexpr char* images_dir{ "data/images/predict" };
constexpr char* result_dir{ "data/result" };
constexpr char* classes_file{ "data/images/labels.txt" };
#endifstd::vector<std::string> parse_classes_file(const char* name)
{std::vector<std::string> classes;std::ifstream file(name);if (!file.is_open()) {std::cerr << "Error: fail to open classes file: " << name << std::endl;return classes;}std::string line;while (std::getline(file, line)) {auto pos = line.find_first_of(" ");classes.emplace_back(line.substr(0, pos));}file.close();return classes;
}auto get_dir_images(const char* name)
{std::map<std::string, std::string> images; // image name, image path + image namefor (auto const& dir_entry : std::filesystem::directory_iterator(name)) {if (dir_entry.is_regular_file())images[dir_entry.path().filename().string()] = dir_entry.path().string();}return images;
}void draw_boxes(const std::vector<std::string>& classes, const std::vector<int>& ids, const std::vector<float>& confidences,const std::vector<cv::Rect>& boxes, const std::string& name, cv::Mat& frame)
{if (ids.size() != confidences.size() || ids.size() != boxes.size() || confidences.size() != boxes.size()) {std::cerr << "Error: their lengths are inconsistent: " << ids.size() << ", " << confidences.size() << ", " << boxes.size() << std::endl;return;}std::cout << "image name: " << name << ", number of detections: " << ids.size() << std::endl;std::random_device rd;std::mt19937 gen(rd());std::uniform_int_distribution<int> dis(100, 255);for (auto i = 0; i < ids.size(); ++i) {auto color = cv::Scalar(dis(gen), dis(gen), dis(gen));cv::rectangle(frame, boxes[i], color, 2);std::string class_string = classes[ids[i]] + ' ' + std::to_string(confidences[i]).substr(0, 4);cv::Size text_size = cv::getTextSize(class_string, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect text_box(boxes[i].x, boxes[i].y - 40, text_size.width + 10, text_size.height + 20);cv::rectangle(frame, text_box, color, cv::FILLED);cv::putText(frame, class_string, cv::Point(boxes[i].x + 5, boxes[i].y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}cv::imshow("Inference", frame);cv::waitKey(-1);std::string path(result_dir);path += "/" + name;cv::imwrite(path, frame);
}float letter_box(const cv::Mat& src, cv::Mat& dst, const std::vector<int>& imgsz)
{if (src.cols == imgsz[1] && src.rows == imgsz[0]) {if (src.data == dst.data) {return 1.;} else {dst = src.clone();return 1.;}}auto resize_scale = std::min(imgsz[0] * 1. / src.rows, imgsz[1] * 1. / src.cols);int new_shape_w = std::round(src.cols * resize_scale);int new_shape_h = std::round(src.rows * resize_scale);float padw = (imgsz[1] - new_shape_w) / 2.;float padh = (imgsz[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(src, dst, cv::Size(new_shape_w, new_shape_h), 0, 0, cv::INTER_AREA);cv::copyMakeBorder(dst, dst, top, bottom, left, right, cv::BORDER_CONSTANT, cv::Scalar(114.));return resize_scale;
}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;
}// reference: https://github.com/pytorch/vision/blob/main/torchvision/csrc/ops/cpu/nms_kernel.cpp
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)
{using torch::indexing::Slice;using torch::indexing::None;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; }// NMSauto 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);
}} // namespaceint test_yolov8_detect_libtorch()
{// reference: ultralytics/examples/YOLOv8-LibTorch-CPP-Inferenceif (auto flag = torch::cuda::is_available(); flag == true)std::cout << "cuda is available" << std::endl;elsestd::cout << "cuda is not available" << std::endl;torch::Device device(torch::cuda::is_available() ? torch::kCUDA : torch::kCPU);auto classes = parse_classes_file(classes_file);if (classes.size() == 0) {std::cerr << "Error: fail to parse classes file: " << classes_file << std::endl;return -1;}std::cout << "classes: ";for (const auto& val : classes) {std::cout << val << " ";}std::cout << std::endl;try {// load modeltorch::jit::script::Module model;if (torch::cuda::is_available() == true)model = torch::jit::load(torchscript_file, torch::kCUDA);elsemodel = torch::jit::load(torchscript_file, torch::kCPU);model.eval();// note: cpu is normal; gpu is abnormal: the model may not be fully placed on the gpu // model = torch::jit::load(file); model.to(torch::kCUDA) ==> model = torch::jit::load(file, torch::kCUDA)// model.to(device, torch::kFloat32);for (const auto& [key, val] : get_dir_images(images_dir)) {// load image and preprocesscv::Mat frame = cv::imread(val, cv::IMREAD_COLOR);if (frame.empty()) {std::cerr << "Warning: unable to load image: " << val << std::endl;continue;}cv::Mat bgr;letter_box(frame, bgr, {image_size[0], image_size[1]});torch::Tensor tensor = torch::from_blob(bgr.data, { bgr.rows, bgr.cols, 3 }, torch::kByte).to(device);tensor = tensor.toType(torch::kFloat32).div(255);tensor = tensor.permute({ 2, 0, 1 });tensor = tensor.unsqueeze(0);std::vector<torch::jit::IValue> inputs{ tensor };// inferencetorch::Tensor output = model.forward(inputs).toTensor().cpu();// NMSauto keep = non_max_suppression(output, 0.1f, 0.1f, 300)[0];std::vector<int> ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;for (auto 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();boxes.emplace_back(cv::Rect(x1, y1, x2 - x1, y2 - y1));confidences.emplace_back(keep[i][4].item().toFloat());ids.emplace_back(keep[i][5].item().toInt());}draw_boxes(classes, ids, confidences, boxes, key, bgr);}} catch (const c10::Error& e) {std::cerr << "Error: " << e.msg() << std::endl;}return 0;
}

      labels.txt文件内容如下:仅2类

watermelon 0
wintermelon 1

      说明

      1.这里使用的libtorch版本为2.2.2;

      2.通过函数torch::cuda::is_available()判断执行cpu还是gpu

      3.通过非cmake构建项目时,调用torch::cuda::is_available()时即使在gpu下也会返回false,解决方法:项目属性:链接器 --> 命令行:其他选项中添加如下语句:

/INCLUDE:?warp_size@cuda@at@@YAHXZ

      4.gpu下,语句model.to(torch::kCUDA)有问题,好像并不能将模型全部放置在gpu上,应调整为如下语句:

model = torch::jit::load(torchscript_file, torch::kCUDA);

      执行结果如下图所示:同样的预测图像集,结果不如使用 opencv dnn 方法好,它们的前处理和后处理方式不同

      其中一幅图像的检测结果如下图所示:

      GitHub:https://github.com/fengbingchun/NN_Test

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332776.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】自定义类型:联合与枚举的简明概述

&#x1f525;引言 关于自定义类型除了我们常用的结构体&#xff0c;还有联合与枚举也是属于自定义类型。本篇将简单介绍联合与枚举基本概念和使用方法 &#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&…

etcd 和 MongoDB 的混沌(故障注入)测试方法

最近在对一些自建的数据库 driver/client 基础库的健壮性做混沌&#xff08;故障&#xff09;测试, 去验证了解业务的故障处理机制和恢复时长. 主要涉及到了 MongoDB 和 etcd 这两个基础组件. 本文会介绍下相关的测试方法. MongoDB 中的故障测试 MongoDB 是比较世界上热门的文…

php部分特性漏洞学习

php部分函数漏洞学习 简单总结一些我遇到的ctf中的php的一些函数或特性的漏洞&#xff0c;我刷题还是太少了&#xff0c;所以很多例子来自ctfshow&#xff0c;以后遇到相关赛题再更新 1.MD5和其他hash 弱类型比较 php中&#xff0c;有两中判断相等的符号&#xff0c;和&…

c# 贪心算法(Greedy Algo)

贪婪是一种算法范式&#xff0c;它逐步构建解决方案&#xff0c;始终选择提供最明显和直接收益的下一个部分。贪婪算法用于解决优化问题。 如果问题具有以下属性&#xff0c;则可以使用贪心法解决优化问题&#xff1a; 每一步&#xff0c;我们都可以做出当前看来最好的选择&…

HTML-JavaWeb

目录 1.标题排版 2.标题样式 ​编辑 ​编辑 小结 3.超链接 4.正文排版 ​编辑​编辑​编辑5.正文布局 6.表格标签 7.表单标签 8.表单项标签 1.标题排版 ● 图片标签 :< img> src:指定图像的ur1(绝对路径/相对路径) width:图像的宽度(像素/相对于父元素的百…

月薪5万是怎样谈的?

知识星球&#xff08;星球名&#xff1a;芯片制造与封测技术社区&#xff0c;星球号&#xff1a;63559049&#xff09;里的学员问&#xff1a;目前是晶圆厂的PE&#xff0c;但是想跳槽谈了几次薪水&#xff0c;都没法有大幅度的增长&#xff0c;该怎么办&#xff1f;“学得文武…

感知觉训练:解锁独立生活的钥匙

在日新月异的科技时代&#xff0c;一款名为“蝙蝠避障”的辅助软件以其独到之处&#xff0c;为盲人朋友的日常生活平添了诸多便利&#xff0c;不仅实现了实时避障&#xff0c;还通过拍照识别功能扩展了信息获取的边界。然而&#xff0c;科技辅助之外&#xff0c;提升盲人朋友的…

低代码的原理、发展历史、使用场景和优势。

在数字化转型的浪潮中&#xff0c;低代码开发平台&#xff08;YDUIbuilder&#xff09;以其独特的优势迅速崛起&#xff0c;为各行各业带来了创新的解决方案。本文将深入探讨低代码的原理、发展历史、使用场景以及它所带来的优势。 gitee下载&#xff1a;yduibuilder: 快速开发…

MySQL从入门到高级 --- 10.索引

文章目录 第十章&#xff1a;10.索引10.1 分类10.2 创建索引10.2.1 单列索引 - 普通索引10.2.2 查看索引10.2.3 删除索引10.2.4 单列索引 - 唯一索引10.2.5 单列索引 - 主键索引10.2.6 组合索引 10.3 全文索引10.3.1 概述10.3.2 使用 10.4 空间索引10.4.1 操作 10.5 原理10.5.1…

PyTorch学习笔记:新冠肺炎X光分类

前言 目的是要了解pytorch如何完成模型训练 https://github.com/TingsongYu/PyTorch-Tutorial-2nd参考的学习笔记 数据准备 由于本案例目的是pytorch流程学习&#xff0c;为了简化学习过程&#xff0c;数据仅选择了4张图片&#xff0c;分为2类&#xff0c;正常与新冠&#xf…

Python爬虫实战:利用代理IP获取电商数据

文章目录 1.电商数据介绍2.爬取目标3.代理IP推荐4.准备工作4.1 模块安装4.2 代理IP获取 5.爬虫代码实战5.1分析网页5.1.1 获取cookie5.1.2 关键词分析5.1.3 翻页分析5.1.4 数据获取分析 5.2 发送请求5.3 提取数据5.4 保存数据5.5 完整源码5.6 数据分析六、总结 1.电商数据介绍 …

回收站清空的文件怎么恢复?8个方法公开(2024更新版)

“我太粗心了&#xff0c;刚想恢复部分回收站中误删的重要文件&#xff0c;一不小心把回收站清空了&#xff0c;现在还有什么方法可以恢复它们吗&#xff1f;” 在数字时代&#xff0c;电脑已经成为我们日常生活和工作中不可或缺的工具。然而&#xff0c;随着我们对电脑的依赖加…

linux经典定时任务

在使用时记得替换为自己的脚本路径。请在相应的脚本第一行加上#!/bin/bash&#xff0c;否则脚本在定时任务中无法执行。 1、在每天凌晨2点执行 0 2 * * * /bin/sh bashup.sh 2、每天执行两次 下面的示例命令将在每天上午5点和下午5点执行。您可以通过逗号分隔指定多个时间戳…

LeetCode:279.完全平方数

class Solution:def numSquares(self, n: int) -> int:dp[i for i in range(n1)]for i in range(2,n1):for j in range(1,int(i**(0.5))1):dp[i]min(dp[i],dp[i-j*j]1)return dp[-1]代码解释 初始化 DP 数组&#xff1a; dp [i for i in range(n1)] 这里&#xff0c;dp[i]…

【云原生】Kubernetes-----POD资源限制与探针机制

目录 引言 一、资源限制 &#xff08;一&#xff09;基本定义 &#xff08;二&#xff09;资源单位 1.CPU资源 2.内存资源 &#xff08;三&#xff09;请求与限制 &#xff08;四&#xff09;定义方式 1.编写yaml文件 2.查看资源情况 &#xff08;五&#xff09;资源…

构建智能化商场存包柜平台的数据结构设计

随着城市生活节奏的加快&#xff0c;人们对于便利的需求也越来越迫切。在城市中&#xff0c;商场存包柜平台成为了解决人们日常出行中行李存放问题的重要设施。为了更好地管理和运营这些存包柜&#xff0c;智能化商场存包柜平台的数据结构设计显得尤为关键。 一、需求分析与功能…

迷你手持小风扇哪个牌子质量好又实惠?这五款不踩雷推荐!

每年夏天&#xff0c;迷你手持小风扇作为消暑神器都会成为市场上的热销产品。然而&#xff0c;由于选购经验有限&#xff0c;许多消费者在面对众多品牌和型号时&#xff0c;往往难以判断哪个牌子的迷你小风扇既质量好又价格实惠。在追求性价比的同时&#xff0c;我们也不应忽视…

比例溢流阀的放大器找BEUEC

液压比例放大器的使用范围广泛&#xff0c;包括工业生产线、船舶液压系统等多个领域。BEUEC比例放大器是一种重要的液压系统组件&#xff0c;其作用是将微弱的液压信号放大&#xff0c;以实现对液压系统的精确控制。这种设备在多个行业中都有广泛的应用&#xff0c;特别是在需要…

Jenkins安装 :Aws EC2下Docker镜像安装

1 安装docker # 安装docker $ sudo yum install -y docker# 启动docker daemon $ sudo systemctl start docker# 用户加入docker组 $ sudo usermod -aG docker username 2 docker安装jenkins $ docker pull jenkins/jenkins:lts# 安装成功 $ docker images REPOSITORY …

[Algorihm][简单多状态DP问题][买卖股票的最佳时机含冷冻期][买卖股票的最佳时机含手续费]详细讲解

目录 1.买卖股票的最佳时机含冷冻期1.题目链接买卖股票的最佳时机含冷冻期2.算法原理详解3.代码实现 2.买卖股票的最佳时机含手续费1.题目链接2.算法原理详解3.代码实现 1.买卖股票的最佳时机含冷冻期 1.题目链接 买卖股票的最佳时机含冷冻期 2.算法原理详解 思路&#xff…