文章目录
- 一、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文件在同一个文件夹下,这十分重要。
-
C#与C++的类型对应
//C# //sbyte , short, int, long //float, double //C/C++ //char, short, int, long long (int64_t) //float, double
-
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;
}
- 一般来说,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静态链接库的调用,呜呜。同样也是对视频进行复现。
- 这里也有一个重要的地方;没有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即可,十分方便。