关于C++如何导出dll用于C++和C#的研究(内含有YOLOV5调用接口的制作完整代码)

news2024/11/26 8:49:01

文章目录

  • 一、VS2019复现视频的易错点。
  • 二、[补充一个C++接口的知识](https://www.bilibili.com/video/BV1tA411N7HB/?spm_id_from=333.337.search-card.all.click&vd_source=f99e21db912f182fe051b1b6b156e0e3)
  • 三、回归正题如何调用dll?前面第一个视频教会我们导出函数的dll,第二个教会我们静态库的调用,那我们继续研究一下如何将类导出并调用dll。

教学视频
这个我找到的将这方面比较全面的教学视频了。

一、VS2019复现视频的易错点。

1.在创建C#控制台应用的时候跳选择框架平台,但所有框架均不支持,导致无法生成exe,因为无法调用dll。
在这里插入图片描述

** 后面我发现可以有两个控制台应用的选项,选择下面这个.NET Framework框架就可以。**
在这里插入图片描述
2. 在我们第一次生成的时候需要调整我们的输出目录,可以看到我们这里的两个exe文件是不在一个文件夹下面的,所以这里是我们需要自己调整的。直接跟着课程走后面会出错。
在这里插入图片描述
调整后
在这里插入图片描述
你需要确保两个exe文件和dll文件在同一个文件夹下,这十分重要。
在这里插入图片描述

  1. C#与C++的类型对应

        //C#
        //sbyte , short, int, long
        //float, double
        //C/C++
        //char, short, int, long long (int64_t)
        //float, double
    
  2. C++dll中添加cout或者printf,C#调用dll时可以打印出来(之前我一直以为dll里面不可以添加cout语句呢qaq)

HEAD void CallingConvention Test_BasicDataString(char* str)
{
	//printf("%s\n",str);
	std::cout << str << std::endl;
}
  1. 一般来说,C#中需要把变量定义好,传入到dll中。但有时候dll中函数没有参数,但却有返回值,这个时候需要深拷贝这个返回值,才能继续使用。不然就会有访问无效指针错误的可能。
  IntPtr numarr = Test_BasicDataRet();//无参;但有返回,则需要深拷贝
  int[] retArr = new int[5];
  Marshal.Copy(numarr, retArr, 0, 5);//把返回的内存拷贝到当前内存中来使用(深拷贝,如果是浅拷贝该指针可能会被回收,而导致后续计算访问空指针);0,5表示要拷贝数组的其实元组和数组长度。

二、补充一个C++接口的知识

后面生成dll的时候会用到(在C++里面调用肯定还是接口更加合适吧,毕竟接口更加方便,所以想首先尝试)看完才知道这个up只讲了lib静态链接库的调用,呜呜。同样也是对视频进行复现。

  1. 这里也有一个重要的地方;没有main函数只生成.lib和.dll文件的时候,需要在这里选择。
    在这里插入图片描述
    2.这边把所有的文件都放到bin目录下。
    在这里插入图片描述
    3.创建一个CPP_CALL_API用来调用接口。需要把这三个.lib .dll .h文件放到文件夹中,然后放到目录下面。

在这里插入图片描述
dll:二进制机器代码 01010101001
lib:静态链接库
exe:可执行文件
pdb:调试的具体内容

常使用YOLOV5的C++推理来写一下静态编译(静态链接库)
这边刚好有一个yolov5的C++调用,尝试封装一下。代码的全在文件里面,这边不做特别详细的说明。这里先看一下传统推理的效果。然后我们在封装一下试一下。
在这里插入图片描述
问题一:发现一个问题,类里面需要使用结构体。那这个结构体放在类外边还是类里面比较好呢?最后发现放在外面可以生成dll和lib成功,放在里面我不会。

YOLOV5-API文件如下:

  • .h文件如下

这边的static YOLOv5DNNDetector* CreateYOLOv5DNNDetector();这个静态函数可以用于返回一个YOLOv5DNNDetector的子类对象Detector。。当然你也可以不继承YOLOv5DNNDetector,但是继承YOLOv5DNNDetector的好处就是子类内容可以延展和丰满一些。而且你也可以把你的代码隐藏得更好

#pragma once
#include <opencv2/opencv.hpp>
#ifndef INTERFACE_H
#define INTERFACE_H
#define _CRT_SECURE_NO_WARNINGS
#define FENGZHUANGCPP_API __declspec(dllexport)

struct DetectResult {
	int classId;
	float score;
	cv::Rect box;
};

class FENGZHUANGCPP_API YOLOv5DNNDetector 
{
public:
	static YOLOv5DNNDetector* CreateYOLOv5DNNDetector();
	virtual void initConfig(std::string onnxpath, int iw, int ih, float threshold)=0;//虚函数
	virtual void detect(cv::Mat& frame, std::vector<DetectResult>&result)=0;//虚函数
private:

	int input_w = 640;
	int input_h = 640;
	cv::dnn::Net net;
	int threshold_score = 0.45;
};

#endif 
  • .cpp文件如下
#include "Interface.h"

class Detector:public YOLOv5DNNDetector {
public:
	Detector();
	virtual void initConfig(std::string onnxpath, int iw, int ih, float threshold);//虚函数
	virtual void detect(cv::Mat& frame, std::vector<DetectResult>& result);//虚函数
private:
	int input_w = 640;
	int input_h = 640;
	cv::dnn::Net net;
	int threshold_score = 0.45;
};

Detector::Detector()
{
}

void Detector::initConfig(std::string onnxpath, int iw, int ih, float threshold) {
	this->input_w = iw;
	this->input_h = ih;
	this->threshold_score = threshold;
	this->net = cv::dnn::readNetFromONNX(onnxpath);
	this->net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
	this->net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);

	//this->net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
	//this->net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
}

void Detector::detect(cv::Mat& frame, std::vector<DetectResult>&results) {
	// 图象预处理 - 格式化操作
	int w = frame.cols;
	int h = frame.rows;
	//int _max = std::max(h, w);
	//cv::Mat image = cv::Mat::zeros(cv::Size(_max, _max), CV_8UC3);
	//cv::Rect roi(0, 0, w, h);
	//frame.copyTo(image(roi));

	float x_factor = frame.cols / 640.0f;
	float y_factor = frame.rows / 640.0f;

	// 推理
	cv::Mat blob = cv::dnn::blobFromImage(frame, 1 / 255.0, cv::Size(this->input_w, this->input_h), cv::Scalar(0, 0, 0), true, false);
	this->net.setInput(blob);
	cv::Mat preds = this->net.forward();

	// 后处理, 1x25200x85//共计25200个检验框
	// std::cout << "rows: "<< preds.size[1]<< " data: " << preds.size[2] << std::endl;
	cv::Mat det_output(preds.size[1], preds.size[2], CV_32F, preds.ptr<float>());
	float confidence_threshold = 0.5;
	std::vector<cv::Rect> boxes;//四个参数
	std::vector<int> classIds;
	std::vector<float> confidences;//第五个参数
	for (int i = 0; i < det_output.rows; i++) {
		float confidence = det_output.at<float>(i, 4);
		if (confidence < 0.45) {
			continue;
		}
		cv::Mat classes_scores = det_output.row(i).colRange(5, preds.size[2]);
		cv::Point classIdPoint;
		double score;
		minMaxLoc(classes_scores, 0, &score, 0, &classIdPoint);

		// 置信度 0~1之间
		if (score > this->threshold_score)
		{
			float cx = det_output.at<float>(i, 0);
			float cy = det_output.at<float>(i, 1);
			float ow = det_output.at<float>(i, 2);
			float oh = det_output.at<float>(i, 3);
			int x = static_cast<int>((cx - 0.5 * ow) * x_factor);
			int y = static_cast<int>((cy - 0.5 * oh) * y_factor);
			int width = static_cast<int>(ow * x_factor);
			int height = static_cast<int>(oh * y_factor);
			// printf("cx:%.2f, cy:%.2f, ow:%.2f, oh:%.2f, x_factor:%.2f, y_factor:%.2f \n", cx, cy, ow, oh, x_factor, y_factor);
			cv::Rect box;
			box.x = x;
			box.y = y;
			box.width = width;
			box.height = height;

			boxes.push_back(box);
			classIds.push_back(classIdPoint.x);
			confidences.push_back(score);
		}
	}

	// NMS
	std::vector<int> indexes;
	cv::dnn::NMSBoxes(boxes, confidences, 0.25, 0.45, indexes);
	for (size_t i = 0; i < indexes.size(); i++) {
		DetectResult dr;
		int index = indexes[i];
		int idx = classIds[index];
		dr.box = boxes[index];
		dr.classId = idx;
		dr.score = confidences[index];
		cv::rectangle(frame, boxes[index], cv::Scalar(0, 0, 255), 2, 8);
		//cv::rectangle(frame, cv::Point(boxes[index].tl().x, boxes[index].tl().y - 20),
		//cv::Point(boxes[index].br().x, boxes[index].tl().y), cv::Scalar(0, 255, 255), -1);
		results.push_back(dr);
	}

	std::ostringstream ss;
	std::vector<double> layersTimings;
	double freq = cv::getTickFrequency() / 1000.0;
	double time = net.getPerfProfile(layersTimings) / freq;
	ss << "FPS: " << 1000 / time << " ; time : " << time << " ms";
	putText(frame, ss.str(), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);
}

YOLOv5DNNDetector* YOLOv5DNNDetector::CreateYOLOv5DNNDetector()
{
	return new Detector();
}

CALL_YOLOV5-API文件如下:
.h文件

#pragma once
#include <opencv2/opencv.hpp>
#ifndef INTERFACE_H
#define INTERFACE_H
#define _CRT_SECURE_NO_WARNINGS
#define FENGZHUANGCPP_API __declspec(dllexport)

struct DetectResult {
	int classId;
	float score;
	cv::Rect box;
};

class FENGZHUANGCPP_API YOLOv5DNNDetector 
{
public:
	static YOLOv5DNNDetector* CreateYOLOv5DNNDetector();
	virtual void initConfig(std::string onnxpath, int iw, int ih, float threshold)=0;//虚函数
	virtual void detect(cv::Mat& frame, std::vector<DetectResult>&result)=0;//虚函数
};

#endif 

.cpp文件

#include "dlllib/Interface.h"
#include <iostream>
#include <fstream>
#pragma comment(lib,"YOLOV5_API.lib")
std::string label_map = "E:/Template Matching/code.txt";
int main(int argc, char** argv) {
	std::vector<std::string> classNames;
	std::ifstream fp(label_map);
	std::string name;
	while (!fp.eof()) {
		getline(fp, name);
		if (name.length())
		{
			classNames.push_back(name);
		}
	}
	fp.close();
	YOLOv5DNNDetector* IF = YOLOv5DNNDetector::CreateYOLOv5DNNDetector();//声明一个对象
	std::shared_ptr<YOLOv5DNNDetector> detector(IF);
	detector->initConfig("E:/Template Matching/bestocr.onnx", 640, 640, 0.25f);
	std::vector<DetectResult> results;
	cv::Mat frame = cv::imread("E:/Template Matching/234.png");
	detector->detect(frame, results);
	for (DetectResult dr : results) {
		cv::Rect box = dr.box;
		cv::putText(frame, classNames[dr.classId], cv::Point(box.tl().x, box.tl().y - 10), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
	}
	cv::imshow("YOLOv5-6.1 + OpenCV DNN - by gloomyfish", frame);
	cv::waitKey(0);
	cv::destroyAllWindows();
	return 0;
}

报错如下:一般有两种解决办法
在这里插入图片描述
第一种办法:修改输出目录是的lib和exe在一个目录下。
在这里插入图片描述

在这里插入图片描述
成功了。
在这里插入图片描述

第二种办法:将lib文件名添加到附加依赖项目中。
在这里插入图片描述

在这里插入图片描述
但是很明显放到动态链接库里面使得推理时间提高了将近一倍。

三、回归正题如何调用dll?前面第一个视频教会我们导出函数的dll,第二个教会我们静态库的调用,那我们继续研究一下如何将类导出并调用dll。

  • 常规思路,直接把dll放到release下。我尝试了一下,没用。
  • 我觉得应该dll和lib深入了解一番!!!
  • 静态库lib,会在生成exe的时候把所有的代码拷贝到exe文件中,导致输出的exe文件过大,并且每一次小的更新,都需要重新编译所有的lib文件,把新的内容写入到exe中。另一方面静态库被运用时,有多少次调用lib库就有多少次复制,因此十分占用内存。
  • 动态库dll弥补静态库的缺陷,首先动态库是机器代码01010,运行快速。不会多次复制占用内存。动态库更新只需要更换dll即可,十分方便。

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

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

相关文章

07 流量回放实现自动化回归测试

在本模块的前四讲里&#xff0c;我向你介绍了可以直接落地的、能够支撑百万并发的读服务的系统架构&#xff0c;包含懒加载缓存、全量缓存&#xff0c;以及数据同步等方案的技术细节。 基于上述方案及细节&#xff0c;你可以直接对你所负责的读服务进行架构升级&#xff0c;将…

SpringBoot引入Layui样式总是出现404

一般出现Layui样式文件如css&#xff0c;js404的错误 解决方案 &#xff08;1&#xff09;首先将其中的静态资源下载resources/static中 &#xff08;2&#xff09;在启动类中重写方法 package com.gq.booksystem;import org.mybatis.spring.annotation.MapperScan; import …

Java本地缓存技术选型(Guava Cache、Caffeine、EhCache)

前言 对一个java开发者而言&#xff0c;提到缓存&#xff0c;第一反应就是Redis。利用这类缓存足以解决大多数的性能问题了&#xff0c;我们也要知道&#xff0c;这种属于remote cache&#xff08;分布式缓存&#xff09;&#xff0c;应用的进程和缓存的进程通常分布在不同的服…

nvm下载的node没有npm

nvm下载的node没有npm 相信大家最近可能发现自己使用的nvm下载nodejs没有npm了。 会出现这种情况&#xff1a; C:\Users\89121>nvm install 15 Downloading node.js version 15.14.0 (64-bit)... Complete Downloading npm version 7.7.6... Download failed. Rolling Bac…

编写一个函数,该函数可以统计一个长度为2的字符串在另一个字符串中出现的次数。

本文收录于专栏:算法之翼 https://blog.csdn.net/weixin_52908342/category_10943144.html 订阅后本专栏全部文章可见。 本文含有题目的题干、解题思路、解题思路、解题代码、代码解析。本文分别包含C语言、C++、Java、Python四种语言的解法完整代码和详细的解析。 题干 编写…

SpringBoot Aop使用篇

Getting Started SpringBoot AOP的实践 AOP相关的概念&#xff1a; Aspect&#xff08;切面&#xff09;&#xff1a; Aspect 声明类似于 Java 中的类声明&#xff0c;在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。就是抽离出来的逻辑类&#xff0c;比如日志、权限…

【Redis 开发】全局ID生成器

全局ID生成器 为了增加ID的安全性&#xff0c;我们可以不直接使用Redis自增的数值&#xff0c;而是拼接一些其他信息&#xff1a; ID的组成部分&#xff1a; 符号位:1bit,一直为0 时间戳:31bit&#xff0c;一秒为单位&#xff0c;可以使用69年 序列号:32bit&#xff0c;秒内…

深度学习系列64:数字人wav2lip详解

1. 整体流程 第一步&#xff0c;加载视频/图片和音频/tts。用melspectrogram将wav文件拆分成mel_chunks。 第二步&#xff0c;调用face_detect模型&#xff0c;给出人脸检测结果&#xff08;可以改造成从文件中读取&#xff09;&#xff0c;包装成4个数组batch&#xff1a;img…

一次违法网站的渗透经历

0x01 前言 在一次攻防演练中&#xff0c;我发现了一个有趣的渗透路径。在信息收集阶段&#xff0c;我注意到目标网站和用户资产网站共享相同的IP网段。这意味着它们可能在同一台服务器上托管&#xff0c;或者至少由同一家互联网服务提供商管理。这种情况为我们的渗透测试提供了…

MySQL数据库运维:运行监控及解决sql执行死锁问题

前言 在现代数据密集型应用程序的开发和部署中&#xff0c;MySQL数据库的运维是至关重要的环节之一。一个良好设计和维护的MySQL数据库系统可以确保数据的准确性、可靠性和高效的访问&#xff0c;从而支持业务的顺利运行。然而&#xff0c;随着业务规模的增长和复杂性增加&…

【threejs教程7】threejs聚光灯、摄影机灯和汽车运动效果

【图片完整效果代码位于文章末】 在上一篇文章中我们实现了汽车模型的加载&#xff0c;这篇文章主要讲如何让汽车看起来像在运动。同时列出聚光灯和摄像机灯光的加载方法。 查看上一篇&#x1f449;【threejs教程6】threejs加载glb模型文件&#xff08;小米su7&#xff09;&…

5分钟——测试搭建的springboot接口(二)

5分钟——测试搭建的springboot接口&#xff08;二&#xff09; 1. 查看数据库字段2. 测试getAll接口3. 测试add接口4. 测试update接口5. 测试deleteById接口 1. 查看数据库字段 2. 测试getAll接口 3. 测试add接口 4. 测试update接口 5. 测试deleteById接口

RocketMQ 消息重复消费

现象 触发消息后&#xff0c;在1s内收到了两次消息消费的日志。 消息消费日志重复&#xff0c;reconsumeTimes0&#xff0c;主机实例也不同&#xff0c;说明是同一条消息被消费了两次 分析 生产者发送消息的时候使用了重试机制&#xff0c;发送消息后由于网络原因没有收到MQ…

【vue,unapi】UniApp引入全局js实现全局方法,全局变量

创建一个全局文件utils.js export const baseUrl "https://www.baidu.com/"export const fn () > {console.log("demo"); } export const obj {baseUrl : "https://www.baidu.com/",demo(){console.log("demo2");} }第一种&#…

2024 OceanBase 开发者大会:OceanBase 4.3正式发布,打造近PB级实时分析数据库

4月20日&#xff0c;2024 OceanBase开发者大会盛大召开&#xff0c;吸引了50余位业界知名的数据库专家和爱好者&#xff0c;以及来自全国各地的近600名开发者齐聚一堂。他们围绕一体化、多模、TP与AP融合等前沿技术趋势展开深入讨论&#xff0c;分享场景探索的经验和最佳实践&a…

人事管理软件全面盘点:2024年十款热门推荐!

随着企业规模的扩大和业务的多元化&#xff0c;人事管理变得越来越复杂。为了帮助企业更有效地管理人力资源&#xff0c;市场上出现了一系列主流的人事管理软件。 本篇文章为您盘点十款2024年值得关注的人事管理软件&#xff1a;Zoho People、北森、i人事、薪人薪事、肯耐珂萨…

鸿蒙开发使用云数据库的坑

在使用华为官网提供的CloudDBZoneWrapper.js有个坑&#xff0c; CloudDBZoneWrapper.js需要使用两个包 const clouddb require(hw-agconnect/database-server/dist/index.js); const agconnect require(agconnect/common-server); const path require(path);/*配置区域 */…

Java后台开发的前置说明

1.知识点逻辑 一个部分 都是先挑重点知识点讲解 然后根据这些重点知识点去完成一个项目的开发 然后在到返回来解决这个部分其他细枝末节的知识点 2.软件开发的分工 我们大致可以将软件开发分成四块&#xff1a; 1.前端开发(比如开发电脑中的京东 htmlcssjavascript) 2.移动开…

HarmonyOS开发实战(黑马健康系列一:欢迎页)

系列文章目录 &#xff08;零&#xff09;鸿蒙HarmonyOS入门&#xff1a;如何配置环境&#xff0c;输出“Hello World“ &#xff08;一&#xff09;鸿蒙HarmonyOS开发基础 &#xff08;二&#xff09;鸿蒙HarmonyOS主力开发语言ArkTS-基本语法 &#xff08;三&#xff09;鸿蒙…

Windows 10 使用 Vagrant 快速创建虚拟机

一、下载 Vagrant 官网地址&#xff1a;Oracle VM VirtualBox 阿里云盘&#xff1a;阿里云盘分享 二、安装 Vagrant 安装软件前请先确认 CPU 是否开启了虚拟化&#xff0c;要求开启 2.1、双击运行可执行文件后点击下一步 2.2、选择安装路径&#xff0c;为了避免中文乱码产生的…