使用OpenCV dnn c++加载YOLOv8生成的onnx文件进行实例分割

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

path: ../datasets/melon_seg # 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

      对melon数据集进行训练的Python实现如下:最终生成的模型文件有best.pt、best.onnx、best.torchscript

import argparse
import colorama
from ultralytics import YOLOdef parse_args():parser = argparse.ArgumentParser(description="YOLOv8 train")parser.add_argument("--yaml", required=True, type=str, help="yaml file")parser.add_argument("--epochs", required=True, type=int, help="number of training")parser.add_argument("--task", required=True, type=str, choices=["detect", "segment"], help="specify what kind of task")args = parser.parse_args()return argsdef train(task, yaml, epochs):if task == "detect":model = YOLO("yolov8n.pt") # load a pretrained modelelif task == "segment":model = YOLO("yolov8n-seg.pt") # load a pretrained modelelse:print(colorama.Fore.RED + "Error: unsupported task:", task)raiseresults = 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.task, args.yaml, args.epochs)print(colorama.Fore.GREEN + "====== execution completed ======")

      以下是使用opencv dnn接口加载onnx文件进行实例分割的C++实现代码:

namespace {constexpr bool cuda_enabled{ false };
constexpr int input_size[2]{ 640, 640 }; // {height,width}, input shape (1, 3, 640, 640) BCHW and output shape(s): detect:(1,6,8400); segment:(1,38,8400),(1,32,160,160)
constexpr float confidence_threshold{ 0.45 }; // confidence threshold
constexpr float iou_threshold{ 0.50 }; // iou threshold
constexpr float mask_threshold{ 0.50 }; // segment mask 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" };
#endifcv::Mat modify_image_size(const cv::Mat& img)
{auto max = std::max(img.rows, img.cols);cv::Mat ret = cv::Mat::zeros(max, max, CV_8UC3);img.copyTo(ret(cv::Rect(0, 0, img.cols, img.rows)));return ret;
}std::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;
}float image_preprocess(const cv::Mat& src, cv::Mat& dst)
{cv::cvtColor(src, dst, cv::COLOR_BGR2RGB);float scalex = src.cols * 1.f / input_size[1];float scaley = src.rows * 1.f / input_size[0];if (scalex > scaley)cv::resize(dst, dst, cv::Size(input_size[1], static_cast<int>(src.rows / scalex)));elsecv::resize(dst, dst, cv::Size(static_cast<int>(src.cols / scaley), input_size[0]));cv::Mat tmp = cv::Mat::zeros(input_size[0], input_size[1], CV_8UC3);dst.copyTo(tmp(cv::Rect(0, 0, dst.cols, dst.rows)));dst = tmp;return (scalex > scaley) ? scalex : scaley;
}void get_masks(const cv::Mat& features, const cv::Mat& proto, const std::vector<int>& output1_sizes, const cv::Mat& frame, const cv::Rect box, cv::Mat& mk)
{const cv::Size shape_src(frame.cols, frame.rows), shape_input(input_size[1], input_size[0]), shape_mask(output1_sizes[3], output1_sizes[2]);cv::Mat res = (features * proto).t();res = res.reshape(1, { shape_mask.height, shape_mask.width });// apply sigmoid to the maskcv::exp(-res, res);res = 1.0 / (1.0 + res);cv::resize(res, res, shape_input);float scalex = shape_src.width * 1.0 / shape_input.width;float scaley = shape_src.height * 1.0 / shape_input.height;cv::Mat tmp;if (scalex > scaley)cv::resize(res, tmp, cv::Size(shape_src.width, static_cast<int>(shape_input.height * scalex)));elsecv::resize(res, tmp, cv::Size(static_cast<int>(shape_input.width * scaley), shape_src.height));cv::Mat dst = tmp(cv::Rect(0, 0, shape_src.width, shape_src.height));mk = dst(box) > mask_threshold;
}void draw_boxes_mask(const std::vector<std::string>& classes, const std::vector<int>& ids, const std::vector<float>& confidences,const std::vector<cv::Rect>& boxes, const std::vector<cv::Mat>& masks, const std::string& name, cv::Mat& frame)
{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);cv::Mat mk = frame.clone();std::vector<cv::Scalar> colors;for (auto i = 0; i < classes.size(); ++i)colors.emplace_back(cv::Scalar(dis(gen), dis(gen), dis(gen)));for (auto i = 0; i < ids.size(); ++i) {cv::rectangle(frame, boxes[i], colors[ids[i]], 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, colors[ids[i]], 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);mk(boxes[i]).setTo(colors[ids[i]], masks[i]);}cv::addWeighted(frame, 0.5, mk, 0.5, 0, frame);//cv::imshow("Inference", frame);//cv::waitKey(-1);std::string path(result_dir);cv::imwrite(path + "/" + name, frame);
}void post_process_mask(const cv::Mat& output0, const cv::Mat& output1, const std::vector<int>& output1_sizes, const std::vector<std::string>& classes, const std::string& name, cv::Mat& frame)
{std::vector<int> class_ids;std::vector<float> confidences;std::vector<cv::Rect> boxes;std::vector<std::vector<float>> masks;float scalex = frame.cols * 1.f / input_size[1]; // note: image_preprocess functionfloat scaley = frame.rows * 1.f / input_size[0];auto scale = (scalex > scaley) ? scalex : scaley;const float* data = (float*)output0.data;for (auto i = 0; i < output0.rows; ++i) {cv::Mat scores(1, classes.size(), CV_32FC1, (float*)data + 4);cv::Point class_id;double max_class_score;cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);if (max_class_score > confidence_threshold) {confidences.emplace_back(max_class_score);class_ids.emplace_back(class_id.x);masks.emplace_back(std::vector<float>(data + 4 + classes.size(), data + output0.cols)); // 32float x = data[0];float y = data[1];float w = data[2];float h = data[3];int left = std::max(0, std::min(int((x - 0.5 * w) * scale), frame.cols));int top = std::max(0, std::min(int((y - 0.5 * h) * scale), frame.rows));int width = std::max(0, std::min(int(w * scale), frame.cols - left));int height = std::max(0, std::min(int(h * scale), frame.rows - top));boxes.emplace_back(cv::Rect(left, top, width, height));}data += output0.cols;}std::vector<int> nms_result;cv::dnn::NMSBoxes(boxes, confidences, confidence_threshold, iou_threshold, nms_result);cv::Mat proto = output1.reshape(0, { output1_sizes[1], output1_sizes[2] * output1_sizes[3] });std::vector<int> ids;std::vector<float> confs;std::vector<cv::Rect> rects;std::vector<cv::Mat> mks;for (size_t i = 0; i < nms_result.size(); ++i) {auto index = nms_result[i];ids.emplace_back(class_ids[index]);confs.emplace_back(confidences[index]);boxes[index] = boxes[index] & cv::Rect(0, 0, frame.cols, frame.rows);cv::Mat mk;get_masks(cv::Mat(masks[index]).t(), proto, output1_sizes, frame, boxes[index], mk);mks.emplace_back(mk);rects.emplace_back(boxes[index]);}draw_boxes_mask(classes, ids, confs, rects, mks, name, frame);
}} // namespaceint test_yolov8_segment_opencv()
{namespace fs = std::filesystem;auto net = cv::dnn::readNetFromONNX(onnx_file);if (net.empty()) {std::cerr << "Error: there are no layers in the network: " << onnx_file << std::endl;return -1;}if (cuda_enabled) {net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);} else {net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);}if (!fs::exists(result_dir)) {fs::create_directories(result_dir);}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;for (const auto& [key, val] : get_dir_images(images_dir)) {cv::Mat frame = cv::imread(val, cv::IMREAD_COLOR);if (frame.empty()) {std::cerr << "Warning: unable to load image: " << val << std::endl;continue;}auto tstart = std::chrono::high_resolution_clock::now();cv::Mat bgr = modify_image_size(frame);cv::Mat blob;cv::dnn::blobFromImage(bgr, blob, 1.0 / 255.0, cv::Size(input_size[1], input_size[0]), cv::Scalar(), true, false);net.setInput(blob);std::vector<cv::Mat> outputs;net.forward(outputs, net.getUnconnectedOutLayersNames());if (outputs.size() != 2) {std::cerr << "Error: output must have 2 layers: " << outputs.size() << std::endl;return -1;}// output0cv::Mat data0 = cv::Mat(outputs[0].size[1], outputs[0].size[2], CV_32FC1, outputs[0].data).t();// output1std::vector<int> sizes;for (int i = 0; i < 4; ++i)sizes.emplace_back(outputs[1].size[i]);cv::Mat data1 = cv::Mat(sizes, CV_32F, outputs[1].data);auto tend = std::chrono::high_resolution_clock::now();std::cout << "elapsed millisenconds: " << std::chrono::duration_cast<std::chrono::milliseconds>(tend - tstart).count() << " ms" << std::endl;post_process_mask(data0, data1, sizes, classes, key, frame);}return 0;
}

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

watermelon 0
wintermelon 1

      说明:

      1.通过指定变量cuda_enabled判断走cpu还是gpu流程 ;

      2.opencv使用4.9.0版本,编译opencv使用的shell脚本如下:执行gpu时结果总不对,yolov8 issues中说因有不支持的layer导致

#! /bin/bashif [ $# != 2 ]; thenecho "Error: requires two parameters: 1: windows windows_cuda or linux; 2: relese or debug"echo "For example: $0 windows debug"exit -1
fiif [ $1 != "windows" ] && [ $1 != "windows_cuda" ] && [ $1 != "linux" ]; thenecho "Error: the first parameter can only be windows or linux"exit -1
fiif [ $2 != "release"  ] && [ $2 != "debug" ]; thenecho "Error: the second parameter can only be release or debug"exit -1
fiif [[ ! -d "build" ]]; thenmkdir buildcd build
elsecd build
fiif [ $2 == "release" ]; thenbuild_type="Release"
elsebuild_type="Debug"
fi# copy the contents of the bin,include,lib/x64 cudnn directories to the corresponding CUDA directories: cuda 11.8+cudnn8.7.x
# cudnn8.9.x: init.hpp:32 cv::dnn::cuda4dnn::checkVersions cuDNN reports version 8.7 which is not compatible with the version 8.9 with which OpenCV was built
# net_impl.cpp:178 cv::dnn::dnn4_v20231225::Net::Impl::setUpNet DNN module was not built with CUDA backend; switching to CPU: SET: CUDA_ARCH_BIN, OPENCV_DNN_CUDA
if [ $1 == "windows_cuda" ]; thencuda_options="-DWITH_CUDA=ON \-DWITH_CUDNN=ON \-DCUDA_FAST_MATH=ON \-DWITH_CUBLAS=ON \-DOPENCV_DNN_CUDA=ON \-DCUDA_ARCH_BIN=5.0;5.2;6.0;6.1;7.0;7.5;8.0;8.6;8.9;9.0"
elsecuda_options=""
fiif [ $1 == "windows" ] || [ $1 == "windows_cuda" ]; thencmake \-G"Visual Studio 17 2022" -A x64 \${cuda_options} \-DCMAKE_BUILD_TYPE=${build_type} \-DCMAKE_CONFIGURATION_TYPES=${build_type} \-DBUILD_SHARED_LIBS=ON \-DBUILD_opencv_world=ON \-DBUILD_PERF_TESTS=OFF \-DBUILD_TESTS=OFF \-DCMAKE_INSTALL_PREFIX=../install \-DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \..cmake --build . --target install --config $2
fiif [ $1 == "linux" ]; thencmake \-DCMAKE_C_COMPILER=/usr/bin/gcc \-DCMAKE_CXX_COMPILER=/usr/bin/g++ \-DCMAKE_BUILD_TYPE=${build_type} \-DBUILD_SHARED_LIBS=ON \-DBUILD_opencv_world=ON \-DBUILD_PERF_TESTS=OFF \-DBUILD_TESTS=OFF \-DCMAKE_INSTALL_PREFIX=../install \-DOPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \..make -j2make install
firc=$?
if [[ ${rc} != 0 ]]; thenecho -e "\033[0;31mError: there are some errors in the above operation, please check: ${rc}\033[0m"exit ${rc}
fi

      执行结果如下图所示:同样的预测图像集,与onnxruntime结果相似,但并不完全相同,它们具有相同的后处理流程;下面显示的耗时是在cpu下,gpu下仅20毫秒左右

      其中一幅图像的分割结果如下图所示:

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

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

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

相关文章

【UML用户指南】-05-对基本结构建模-类

目录 1、名称&#xff08;name&#xff09; 2、属性 &#xff08;attribute&#xff09; 3、操作&#xff08;operation&#xff09; 4、对属性和操作的组织 4.1、衍型 4.2、职责 &#xff08;responsibility&#xff09; 4.3、其他特征 4.4、对简单类型建模 5、结构良…

【Mtk Camera开发学习】06 MTK 和 Qcom 平台支持通过 Camera 标准API 打开 USBCamera

本专栏内容针对 “知识星球”成员免费&#xff0c;欢迎关注公众号&#xff1a;小驰行动派&#xff0c;加入知识星球。 #MTK Camera开发学习系列 #小驰私房菜 Google 官方介绍文档&#xff1a; https://source.android.google.cn/docs/core/camera/external-usb-cameras?hlzh-…

【传知代码】DETR[端到端目标检测](论文复现)

前言&#xff1a;想象一下&#xff0c;当自动驾驶汽车行驶在繁忙的街道上&#xff0c;DETR能够实时识别出道路上的行人、车辆、交通标志等目标&#xff0c;并准确预测出它们的位置和轨迹。这对于提高自动驾驶的安全性、减少交通事故具有重要意义。同样&#xff0c;在安防监控、…

Proxyman 现代直观的 HTTP 调试代理应用程序

Proxyman 是一款现代而直观的 HTTP 调试代理应用程序&#xff0c;它的功能强大&#xff0c;使您可以轻松捕获、检查和操作 HTTP(s) 流量。不再让繁杂的网络调试工具阻碍您的工作&#xff0c;使用 Proxyman&#xff0c;您将轻松应对网络调试的挑战。 下载地址&#xff1a;https…

BeagleBone Black入门总结

文章目录 参考连接重要路径系统镜像下载访问 BeagleBone 参考连接 镜像下载启动系统制作&#xff1a;SD卡烧录工具入门书籍推荐&#xff1a;BeagleBone cookbookBeagleBon cookbook 例程BeagleBone概况&#xff1f;BeagleBone 官方管理仓库(原理图&#xff0c;官方例程。。。)…

IP纯净度是什么,对用户有多么重要?

在网络应用和数据采集等领域&#xff0c;代理IP被广泛使用&#xff0c;而代理IP的纯净度则直接影响其性能和可用性。代理IP的纯净度主要涉及到代理IP在网络传输过程中的稳定性、匿名性和安全性。今天就带大家一起了解代理IP纯净度对用户的重要性。 第一&#xff0c;保护用户的隐…

http和https数据传输与协议区分

目录 1. 数据传输安全性2. 端口号3. URL 前缀4. SSL/TLS 证书5. 性能6. SEO 和用户信任7. 应用场景总结 HTTP&#xff08;HyperText Transfer Protocol&#xff09;和 HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是用于在客户端&#xff08;如浏览器&…

PyCharm中 Fitten Code插件的使用说明一

一. 简介 Fitten Code插件是是一款由非十大模型驱动的 AI 编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;帮您调试 Bug&#xff0c;节省您的时间&#xff0c;另外还可以对话聊天&#xff0c;解决您编程碰到的问题。 前一篇文章学习了 PyCharm…

项目管理--领导者vs管理者

项目管理领导者和管理者&#xff0c;虽然这两个角色在项目管理中都非常重要&#xff0c;但它们之间还是存在一些区别。首先&#xff0c;让我们来了解一下这两个角色的定义和职责。项目管理领导者是指那些能够激励团队成员&#xff0c;带领他们朝着共同目标前进的人。他们具备良…

高防CDN是如何应对DDoS和CC攻击的

高防CDN&#xff08;内容分发网络&#xff09;主要通过分布式的网络架构来帮助网站抵御DDoS&#xff08;分布式拒绝服务&#xff09;和CC&#xff08;挑战碰撞&#xff09;攻击。 下面是高防CDN如何应对这些攻击的详细描述&#xff1a; 1. DDoS攻击防护 DDoS攻击通过大量的恶…

【ArcGIS微课1000例】0119:TIFF与grid格式互相转换

文章目录 一、任务描述二、tiff转grid三、grid转tif四、注意事项一、任务描述 地理栅格数据常用TIFF格式和GRID格式进行存储。TIFF格式的栅格数据常以单文件形式存储,不仅存储有R、G、B三波段的像素值,还保存有地理坐标信息。GRID格式的栅格数据常以多文件的形式进行存储,且…

PDF编辑与转换的终极工具智能PDF处理Acrobat Pro DC

Acrobat Pro DC 2023是一款功能全面的PDF编辑管理软件&#xff0c;支持创建、编辑、转换、签署和共享PDF文件。它具备OCR技术&#xff0c;可将扫描文档转换为可编辑文本&#xff0c;同时提供智能PDF处理技术&#xff0c;确保文件完整性和可读性。此外&#xff0c;软件还支持电子…

使用 Scapy 库编写 ICMP 不可达攻击脚本

一、介绍 ICMP不可达攻击是一种利用ICMP&#xff08;Internet Control Message Protocol&#xff09;不可达消息来干扰或中断目标系统的网络通信的攻击类型。通过发送伪造的ICMP不可达消息&#xff0c;攻击者可以诱使目标系统认为某些网络路径或主机不可达&#xff0c;从而导致…

k8s小型实验模拟

&#xff08;1&#xff09;Kubernetes 区域可采用 Kubeadm 方式进行安装。&#xff08;5分&#xff09; &#xff08;2&#xff09;要求在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff0c;Pod使用hostPat…

netty+springboot+vue聊天室(需要了解netty)

先看看这个使用websocket实现的聊天室&#xff0c;因为前端是使用websocket&#xff0c;和下面的demo的前端差不多就不解释实现原理&#xff0c;所以建议还是看看(要是会websocket的大佬请忽略) springbootwebsocketvue聊天室 目录 一、实现内容二、代码实现1.后端2.前端源码…

k8s-部署对象存储minio

环境信息 minio版本 :最新 k8s 版本1.22 使用nfs作为共享存储 一.单节点安装包部署 脚本部署&#xff0c;一键部署&#xff0c;单节点应用于数据量小&#xff0c;一些缓存存储&#xff0c;比如gitlab-runner的产物数据&#xff0c;maven的打包依赖数据 #!/bin/bash# 步骤…

【AI基础】第三步:纯天然保姆喂饭级-安装并运行chatglm2-6b

chatglm2构建时使用了RUST&#xff0c;所以在安装chatglm2之前&#xff0c;先安装RUST。 此系列文章列表&#xff1a; 【AI基础】第一步&#xff1a;安装python开发环境-windows篇_下载安装ai环境python-CSDN博客 【AI基础】第一步&#xff1a;安装python开发环境-conda篇_mini…

【云原生】Docker Compose 使用详解

目录 一、前言 二、Docker Compose 介绍 2.1 Docker Compose概述 2.2 Docker Compose特点 2.3 Docker Compose使用场景 三、Docker Compose 搭建 3.1 安装docker环境 3.2 Docker Compose安装方式一 3.2.1 下载最新版/如果不是最新可替换最新版本 3.2.2 设置权限 3.2.…

c++【入门】正多边形每个内角的度数

限制 时间限制 : 1 秒 内存限制 : 128 MB 题目 根据多边形内角和定理&#xff0c;正多边形内角和等于&#xff1a;&#xff08;n &#xff0d; 2&#xff09;180(n大于等于3且n为整数&#xff09;&#xff08;如下图所示是三角形、四边形、五边形、六边形的形状&#xff09…

ROS学习记录:栅格地图格式

一、机器人导航所使用的地图数据&#xff0c;就是ROS导航软件包里的map_server节点在话题 /map 中发布的消息数据&#xff0c;消息类型是nav_msgs消息包中的OccupancyGrid&#xff0c;它的中文意思的占据栅格&#xff0c;是一种正方形小格子组成的地图。 二、对障碍物进行俯视&…