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

news2024/11/25 15:49:20

      在网上下载了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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HACL-Net:基于MRI的胎盘植入谱诊断的分层注意力和对比学习网络

文章目录 HACL-Net: Hierarchical Attention and Contrastive Learning Network for MRI-Based Placenta Accreta Spectrum Diagnosis摘要方法实验结果 HACL-Net: Hierarchical Attention and Contrastive Learning Network for MRI-Based Placenta Accreta Spectrum Diagnosis…

世界上首位AI程序员诞生,AI将成为人类的对手吗?

3月13日&#xff0c;世界上第一位AI程序员Devin诞生&#xff0c;不仅能自主学习新技术&#xff0c;自己改Bug&#xff0c;甚至还能训练和微调自己的AI模型&#xff0c;表现已然远超GPT-4等“顶流选手”。 AI的学习速度如此之快&#xff0c;人类的教育能否跟上“机器学习”的速…

2、xss-labs之level2

1、打开页面 2、传入xss代码 payload&#xff1a;<script>alert(xss)</script>&#xff0c;发现返回<script>alert(xss)</script> 3、分析原因 打开f12&#xff0c;没什么发现 看后端源码&#xff0c;在这form表单通过get获取keyword的值赋给$str&am…

《拯救大学生课设不挂科第三期之Windows下安装Dev C++(VC 6.0上位替代)与跑通Hello World程序C/C++版教程》【官方笔记】

背景&#xff1a; 大学老师使用的VC6.0(VC 6.0)太老了&#xff0c;老师为什么用VC6.0&#xff0c;象漂亮认为是高校缺乏鲶鱼刺激。很大一部分高校已经与市场脱节&#xff0c;这也是为什么很多同学毕业后想要入行计算机基本都得自己重新回炉打造一次&#xff0c;这和高校的老旧…

OpenStack平台Keystone组件的使用

1. 规划节点 安装基础服务的服务器规划 IP地址 主机名 节点 192.168.100.10 controller Openstack控制节点 2. 基础准备 使用机电云共享的单节点的openstack系统&#xff0c;自行修改虚拟网络编辑器、网络适配器&#xff0c;系统用户名&#xff1a;root&#xff0c;密…

在Github上寻找安装ROS软件包

1、创建一个功能包 并下载git sudo apt install git 2、找到自己想在github上要克隆的包 复制此链接 3、克隆到本地 git clone 链接 4.scripts目录用于放置脚本文件和python程序 使用脚本安装编译需要的依赖库 5、下载完成后&#xff0c;在~catkin_ws目录下运行catkin_make进…

电脑键盘如何练习盲打?

电脑键盘如何练习盲打&#xff1f;盲打很简单&#xff0c;跟着我做&#xff0c;今天教会你。 请看【图1】&#xff1a; 【图1】中&#xff0c;红色方框就是8个基准键位&#xff0c;打字时我们左右手的8个手指就是放在这8个基准键位上&#xff0c;F键和J键上各有一个小突起&…

FTP协议——BFTPD安装(Linux)

1、简介 BFTPD&#xff0c;全称为 Brutal File Transfer Protocol Daemon&#xff0c;是一个用于Unix和类Unix系统的轻量级FTP服务器软件。它的设计理念是提供一个简单、快速、安全的FTP服务器解决方案&#xff0c;特别适用于需要低资源占用的环境。 2、步骤 环境&#xff1…

网络爬虫原理及其应用

你是否想知道Google 和 Bing 等搜索引擎如何收集搜索结果中显示的所有数据。这是因为搜索引擎对其档案中的所有页面建立索引&#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和…

蓝桥杯杨辉三角

PREV-282 杨辉三角形【第十二届】【蓝桥杯省赛】【B组】 &#xff08;二分查找 递推&#xff09;&#xff1a; 解析&#xff1a; 1.杨辉三角具有对称性&#xff1a; 2.杨辉三角具有一定规律 通过观察发现&#xff0c;第一次出现的地方一定在左部靠右的位置&#xff0c;所以从…

驱动编译报error: negative width in bit-field ‘<anonymous>’错误

错误如下图所示&#xff1a; 代码如下&#xff1a; 问题点&#xff1a;module_param的其他用户的权限参数上。 在Linux中&#xff0c;文件权限由读(r)、写(w)、执行(x)权限组成&#xff0c;分别对应数值4、2、1。 第一位0是占位符&#xff0c;在这里没有意义&#xff0c;因为…

[emailprotected](2)核心概念-JSX

目录 1&#xff0c;什么是 jsx2&#xff0c;空标签3&#xff0c;通过大括号使用 js4&#xff0c;防止注入攻击5&#xff0c;元素的不可变性 官方文档 1&#xff0c;什么是 jsx Facebook 起草的 js 扩展语法。本质上是 js 对象&#xff0c;会被 babel 编译&#xff0c;最终转换…

Halcon 极坐标转换图像

一、概述 先看效果 将圆形的用极坐标转换成矩性然后再进行识别或者其他缺陷检测&#xff0c;最后在还圆到原图中 二、原理&#xff1a; halcon 圆环类缺陷检测的一种方法&#xff08;极坐标变换法&#xff09;_halcon缺口检测-CSDN博客 图像极坐标变换与反变换&#xff08;…

docker实战之搭建MYSQL8.0主从同步

目录 环境配置容器创建主服务器创建MYSQL容器新增my.cnf文件创建用户并授权 从服务器创建MYSQL容器新增my.cnf文件重启MYSQL容器配置主从同步 验证主从同步彩蛋 MySQL 主从同步&#xff08;Master-Slave Replication&#xff09;是一种常用的解决方案&#xff0c;它允许一个主服…

2024年蓝桥杯Web开发【大赛大纲】15届

一、 组别 Web应用开发分为&#xff1a;大学组和职业院校组。 每位选手只能申请参加其中一个组别的竞赛。各个组别单独评奖。 研究生和本科生只能报大学组。 其它高职高专院校可自行选择报任意组别。 二. 竞赛赛程 省赛时长&#xff1a;4小时。 决赛时长&#xff1a;4小…

【STM32CubeIDE】软件硬件SPI+六针OLED使用

前言 本文将介绍STM32 6针OLED的使用&#xff0c;分别使用软件和硬件两种SPI驱动方式&#xff0c;最终实现OLED显示TEST-ok字符和数字累加刷新显示 软件平台&#xff1a;STM32CubeIDEHAL库 硬件&#xff1a;STM32F103ZET6(正点原子战舰V3)六针OLED 题外话&#xff1a; 最…

【Apache Doris】BE宕机问题排查指南

【Apache Doris】BE宕机问题排查指南 背景BE宕机分类如何判断是BE进程是Crash还是OOMBE Crash 后如何排查BE OOM 后如何分析Cache 没及时释放导致BE OOM&#xff08;2.0.3-rc04&#xff09; 关于社区 作者&#xff5c;李渊渊 背景 在实际线上生产环境中&#xff0c;大家可能遇…

深入探索:移动云服务器的强大之处

文章目录 一 什么是移动云二 移动云服务器的使用三 移动云服务器的优点四 在移动云上部署node.js项目五 移动云服务器的应用场景六 移动云服务器的使用体验总结 一 什么是移动云 移动云是指用户可以通过移动设备访问云端的数据和应用&#xff0c;无需在本地设备上进行存储和处…

Notes for video: EDC-Con 2022/01 - EDC Conceptual Overview and Architecture

Eclipse Dataspace Connector 中文概念 Eclipse Dataspace Connector (EDC) 是一个开源项目&#xff0c;旨在提供一种标准化的方法来连接和共享数据空间中的数据。它是 Eclipse Foundation 下的一个项目&#xff0c;目标是促进数据共享和数据交换的互操作性。以下是 EDC 的一些…