使用OpenCV dnn c++加载YOLOv8生成的onnx文件进行目标检测

      在网上下载了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: watermelon
  1: wintermelon

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

import argparse
import colorama
from ultralytics import YOLO

def 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 args

def train(yaml, epochs):
	model = YOLO("yolov8n.pt") # load a pretrained model
	results = model.train(data=yaml, epochs=epochs, imgsz=640) # train the model

	metrics = model.val() # It'll automatically evaluate the data you trained, no arguments needed, dataset and settings remembered

	model.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") # libtorch

if __name__ == "__main__":
	colorama.init()
	args = parse_args()

	train(args.yaml, args.epochs)

	print(colorama.Fore.GREEN + "====== execution completed ======")

      使用OpenCV 4.9.0库,生成库的shell脚本build.sh如下:windows上包括cpu和gpu,cuda版本为11.8,cudnn版本为对应的v8.9.7版本,将cudnn的bin, include, lib/x64三个目录内容分别拷贝对应cuda 11.8相应目录下

#! /bin/bash
 
if [ $# != 2 ]; then
    echo "Error: requires two parameters: 1: windows windows_cuda or linux; 2: relese or debug"
    echo "For example: $0 windows debug"
    exit -1
fi
 
if [ $1 != "windows" ] && [ $1 != "windows_cuda" ] && [ $1 != "linux" ]; then
    echo "Error: the first parameter can only be windows or linux"
    exit -1
fi
 
if [ $2 != "release"  ] && [ $2 != "debug" ]; then
    echo "Error: the second parameter can only be release or debug"
    exit -1
fi
 
if [[ ! -d "build" ]]; then
    mkdir build
    cd build
else
    cd build
fi

if [ $2 == "release" ]; then
    build_type="Release"
else
    build_type="Debug"
fi

# copy the contents of the bin,include,lib/x64 cudnn directories to the corresponding CUDA directories
if [ $1 == "windows_cuda" ]; then
    cuda_options="-DWITH_CUDA=ON \
        -DWITH_CUDNN=ON \
        -DCUDA_FAST_MATH=ON \
        -DWITH_CUBLAS=ON"
else
    cuda_options=""
fi

if [ $1 == "windows" ] || [ $1 == "windows_cuda" ]; then
    cmake \
        -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
fi
 
if [ $1 == "linux" ]; then
    cmake \
        -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 -j2
    make install
fi

rc=$?
if [[ ${rc} != 0 ]]; then
    echo -e "\033[0;31mError: there are some errors in the above operation, please check: ${rc}\033[0m"
	exit ${rc}
fi

      以下是使用opencv dnn接口加载onnx文件进行目标检测的实现代码:由变量cuda_enabled指定是执行cpu还是gpu

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" };
#endif

cv::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 name

	for (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);
}

} // namespace

int test_yolov8_detect_opencv()
{
	// reference: ultralytics/examples/YOLOv8-CPP-Inference
	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;
		}

		cv::Mat bgr = modify_image_size(frame);

		cv::Mat blob;
		cv::dnn::blobFromImage(bgr, blob, 1.0 / 255.0, cv::Size(image_size[1], image_size[0]), cv::Scalar(), true, false);
		net.setInput(blob);

		std::vector<cv::Mat> outputs;
		net.forward(outputs, net.getUnconnectedOutLayersNames());

		int rows = outputs[0].size[1];
		int dimensions = outputs[0].size[2];

		// yolov5 has an output of shape (batchSize, 25200, num classes+4+1) (Num classes + box[x,y,w,h] + confidence[c])
		// yolov8 has an output of shape (batchSize, num classes + 4,  8400) (Num classes + box[x,y,w,h])
		if (dimensions > rows) { // Check if the shape[2] is more than shape[1] (yolov8)
			rows = outputs[0].size[2];
			dimensions = outputs[0].size[1];

			outputs[0] = outputs[0].reshape(1, dimensions);
			cv::transpose(outputs[0], outputs[0]);
		}

		float* data = (float*)outputs[0].data;
		float x_factor = bgr.cols * 1.f / image_size[1];
		float y_factor = bgr.rows * 1.f / image_size[0];

		std::vector<int> class_ids;
		std::vector<float> confidences;
		std::vector<cv::Rect> boxes;

		for (auto i = 0; i < rows; ++i) {
			float* classes_scores = data + 4;

			cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);
			cv::Point class_id;
			double max_class_score;

			cv::minMaxLoc(scores, 0, &max_class_score, 0, &class_id);

			if (max_class_score > model_score_threshold) {
				confidences.push_back(max_class_score);
				class_ids.push_back(class_id.x);

				float x = data[0];
				float y = data[1];
				float w = data[2];
				float h = data[3];

				int left = int((x - 0.5 * w) * x_factor);
				int top = int((y - 0.5 * h) * y_factor);

				int width = int(w * x_factor);
				int height = int(h * y_factor);

				boxes.push_back(cv::Rect(left, top, width, height));
			}

			data += dimensions;
		}

		std::vector<int> nms_result;
		cv::dnn::NMSBoxes(boxes, confidences, model_score_threshold, model_nms_threshold, nms_result);

		std::vector<int> ids;
		std::vector<float> confs;
		std::vector<cv::Rect> rects;
		for (size_t i = 0; i < nms_result.size(); ++i) {
			ids.emplace_back(class_ids[nms_result[i]]);
			confs.emplace_back(confidences[nms_result[i]]);
			rects.emplace_back(boxes[nms_result[i]]);
		}
		draw_boxes(classes, ids, confs, rects, key, frame);
	}

	return 0;
}

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

watermelon 0
wintermelon 1

      执行结果如下图所示:

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题---2418. 按身高排序【简单】

题目描述 给你一个字符串 数组 names &#xff0c;和一个由 互不相同 的正整数组成的数组 heights 。两个数组的长度均为 n 。 对于每个下标 i&#xff0c;names[i] 和 heights[i] 表示第 i 个人的名字和身高。 请按身高 降序 顺序返回对应的名字数组 names 。 示例 1&…

力扣刷题---961. 在长度 2N 的数组中找出重复 N 次的元素【简单】

题目描述&#x1f357; 给你一个整数数组 nums &#xff0c;该数组具有以下属性&#xff1a; nums.length 2 * n. nums 包含 n 1 个 不同的 元素 nums 中恰有一个元素重复 n 次 找出并返回重复了 n 次的那个元素。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,3] 输…

微服务中使用Maven BOM来管理你的版本依赖

摘要: 原创出处 sf.gg/a/1190000021198564 「飘渺Jam」欢迎转载&#xff0c;保留摘要&#xff0c;谢谢&#xff01; 为什么要使用BOM? 如何定义BOM? 项目使用方法? BOM&#xff08;Bill of Materials&#xff09;是由Maven提供的功能,它通过定义一整套相互兼容的jar包版…

93.网络游戏逆向分析与漏洞攻防-游戏技能系统分析-增强技能信息显示后进行分析

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果&#xff0c;代码看不懂是正常的&#xff0c;只要会抄就行&#xff0c;抄着抄着就能懂了 内容…

STM32中断编程入门

文章目录 一、 理论部分1.中断系统2.中断执行流程3.NVIC的基本结构4.EXTI介绍5.AFIO复用IO口 二、实验目的&#xff1a;学习stm32中断原理和开发编程方法。使用标准完成以下任务&#xff1a;&#xff08;一&#xff09;实验一 开关控制LED的亮灭1.代码部分2.运行结果 &#xff…

网络空间安全数学基础·整除与同余

主要内容&#xff1a; 整除的基本概念&#xff08;掌握&#xff09; 素数&#xff08;掌握&#xff09; 同余的概念&#xff08;掌握&#xff09; 1.1整除 定义&#xff1a;设a&#xff0c;b是任意两个整数&#xff0c;其中b≠0&#xff0c;如果存在一个整数q&#xff0c;使 …

如何网页在线编辑 Office word 文档,并支域功能:创建域/插入域/替换域等

在日常在线办公场景中&#xff0c;我们经常会遇到一些复杂的文档编辑需求&#xff0c;特别是我们经常会遇到一些复杂的数学公式&#xff0c;会用到“域”功能&#xff0c;“域”功能便是一个高级且实用的工具。通过设置域&#xff0c;用户可以实现文档的自动化处理&#xff0c;…

卷积神经网络CNN动态演示和输出特征图计算公式

目录 一、卷积运算 1、卷积&#xff08;Convolution&#xff09; 2、填充&#xff08;Padding&#xff09; &#xff08;1&#xff09;Valid Padding &#xff08;2&#xff09;Same Padding 3、步长 4、卷积核大小为什么一般为奇数奇数&#xff1f; 5、卷积核kernel和…

【C++】哈希和unordered系列容器

目录 一、unordered系列关联式容器的引入 二、容器使用 2.1 unordered_map的文档说明 2.2 unordered_map的使用 2.3 unordered_set 三、底层结构 3.1 哈希概念 3.2 哈希表 3.3 哈希冲突 3.4 哈希函数 3.5 哈希冲突解决 3.5.1 闭散列 3.5.2 开散列 3.5.3 思考 四…

【微积分】CH16 integrals and vector fields听课笔记

【托马斯微积分学习日记】13.1-线积分_哔哩哔哩_bilibili 概述 16.1line integrals of scalar functions [中英双语]可视化多元微积分 - 线积分介绍_哔哩哔哩_bilibili 16.2vector fields and line integrals&#xff1a; work circulation and flux 向量场差不多也是描述某种…

Vitis HLS 学习笔记--控制驱动任务示例

目录 1. 简介 2. 代码解析 2.1 kernel 代码回顾 2.2 功能分析 2.3 查看综合报告 2.4 查看 Schedule Viewer 2.5 查看 Dataflow Viewer 3. Vitis IDE的关键设置 3.1 加载数据文件 3.2 设置 Flow Target 3.3 配置 fifo 深度 4. 总结 1. 简介 本文对《Vitis HLS 学习…

Android面试题之Kotlin常见集合操作技巧

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 list 创建和修改 不可变list,listOf var list listOf("a","d","f") println(list.getOrElse(3){"Unkn…

《计算机网络微课堂》2-5 信道的极限容量

本节课我们介绍信道极限容量的有关问题。 我们都知道信号在传输过程中会受到各种因素的影响&#xff0c;如图所示&#xff0c;这是一个数字信号&#xff0c;‍‍当它通过实际的信道后&#xff0c;波形会产生失真&#xff0c;当失真不严重时&#xff0c;在输出端‍‍还可根据以失…

Mysql教程(0):学习框架

1、Mysql简介 MySQL 是一个开放源代码的、免费的关系型数据库管理系统。在 Web 开发领域&#xff0c;MySQL 是最流行、使用最广泛的关系数据库。MySql 分为社区版和商业版&#xff0c;社区版完全免费&#xff0c;并且几乎能满足全部的使用场景。由于 MySQL 是开源的&#xff0…

力扣刷题---409. 最长回文串【简单】

题目描述 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回 通过这些字母构造成的 最长的回文串 。 在构造过程中&#xff0c;请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。 示例 1: 输入:s “abccccdd” 输出:7 解释: 我们可以构造的最长的回文串…

Docker部署SpringBoot项目(jar包+Mysql)

部署Java项目 项目准备准备Java项目镜像准备配置网络 部署项目细节展示 项目准备 准备Java项目 hmall项目是一个maven聚合项目&#xff0c;使用IDEA打开hmall项目&#xff0c;查看项目结构如图&#xff1a; 我们要部署的就是其中的hm-service&#xff0c;其中的配置文件采用…

Java网络编程之TCP协议核心机制(三)

题外话 最近学习内容很多嗷 正题 延时应答机制 当客户端发送数据到服务器时,服务器不会立即返回ACK,而是等待一会再返回ACK 这段等待时间应用程序可能会消化掉接收缓冲区中的数据,当服务器返回ACK时,就会携带此时接收缓冲区大小的信息 当客户端下次再发送数据的时候就可以…

WebGL的室内设计软件

WebGL (Web Graphics Library) 是一个JavaScript API&#xff0c;它提供了一种在网页上渲染3D图形的方法&#xff0c;无需使用插件。利用WebGL&#xff0c;开发者可以创建和展示复杂的3D场景&#xff0c;包括室内设计。以下是开发基于WebGL的室内设计软件时可能涉及的一些关键步…

青鸟云报修系统:实现高效、便捷的维修申请处理

在日常生活和工作中&#xff0c;故障报修难免会遇到&#xff0c;售后报修服务则成为了解决问题的关键。纸质化售后报修维修申请单&#xff0c;作为报修流程中的重要一环&#xff0c;在一定程度上能够记录和追踪售后报修维修流程&#xff0c;但在实际操作过程中却存在着诸多弊端…

【MySQL数据库】:MySQL表的操作

目录 创建表 创建表案例 查看表结构 修改表 插入数据 新增列 删除一行数据 修改列类型 修改列名 修改表名 删除列 删除表 表操作至少会涉及如下两类SQL语句&#xff1a; DDL&#xff08;Data Definition Language&#xff09;数据定义语言&#xff1a;比如…