基本思想:写了一个简单的c#调用c++的dll库图形化界面程序,完成yolov5检测图片的分类,本项目问的人太多了。。。。附属链接吧

链接:https://pan.baidu.com/s/1RVh1XtqvFuWuHyuZKxuzDw?pwd=vsxz 
提取码:vsxz 
--来自百度网盘超级会员V1的分享

一、创建一个c++工程,详细的构建和配置环境就不详细累述了,贴代码吧,导入opencv和ncnn库即可,因为需要考虑使用C#调用,所以改的代码尽量迎合C#调用的风格

main.cpp


#include "connect.h"int main(int argc, char** argv)
{cv::Mat image = cv::imread("E:\\images\\test\\test1.jpg");unsigned char* src = image.data;cv::Mat result = cv::Mat(image.rows, image.cols, CV_8UC3, src);unsigned char* dest = result.data;const char* model_param = "G:\\ncnn-20210525-windows-vs2019\\ncnn-20210525-windows-vs2019\\x64\\bin\\best_sim211223.param";const char* model_bin = "G:\\ncnn-20210525-windows-vs2019\\ncnn-20210525-windows-vs2019\\x64\\bin\\best_sim211223.bin";ConnectCppWrapper::init_model(model_param, model_bin);ConnectCppWrapper::detect_image(src, dest, image.rows, image.cols);cv::imshow("demo", result);cv::waitKey(0);return 0;
}

connect.h

#pragma once#include "yolov5.h"namespace  ConnectCppWrapper
{extern "C" __declspec(dllexport) int __stdcall init_model(const char* model_param, const char* bin_param);extern "C" __declspec(dllexport) int __stdcall detect_image(unsigned char* ImageBuffer, unsigned char* ImageResult, int height, int width);}

connect.cpp

#include "connect.h"namespace ConnectCppWrapper
{Yolov5* yolov5Item = new Yolov5();int __stdcall init_model(const char* model_param, const char* bin_param){return yolov5Item->init_model(model_param, bin_param);}int __stdcall detect_image(unsigned char* ImageBuffer, unsigned char* ImageResult, int height, int width){cv::Mat result;cv::Mat image = cv::Mat(height, width, CV_8UC3, ImageBuffer);yolov5Item->detect_yolov5(image);int length = (int)(result.total() * result.elemSize());unsigned char* buffer = new unsigned char[length];memcpy(ImageResult, result.data, length * sizeof(unsigned char));return 0;}
}

yolov5.h  这部分代码和模型来自ncnn的example

#pragma once#include "layer.h"
#include "net.h"#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>#define YOLOV5_V60 1 //YOLOv5 v6.0
using namespace std;
using namespace ncnn;
struct Object
{cv::Rect_<float> rect;int label;float prob;
};
class Yolov5 {
public:Yolov5();~Yolov5();
private:float  intersection_area(const Object& a, const Object& b);void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right);void qsort_descent_inplace(std::vector<Object>& faceobjects);void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold);void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector<Object>& objects);float sigmoid(float x);
public:int detect_yolov5(cv::Mat image);void  draw_objects(const cv::Mat& image, const std::vector<Object>& objects);int init_model(const char* param_file, const char* bin_file);private:ncnn::Net m_yolov5;const int m_target_size = 640;const float m_prob_threshold = 0.25f;const float m_nms_threshold = 0.45f;};

yolov5.cpp


#include "yolov5.h"
#if YOLOV5_V60
#define MAX_STRIDE 64
#else
#define MAX_STRIDE 32
class YoloV5Focus : public ncnn::Layer
{
public:YoloV5Focus(){one_blob_only = true;}virtual int forward(const ncnn::Mat& bottom_blob, ncnn::Mat& top_blob, const ncnn::Option& opt) const{int w = bottom_blob.w;int h = bottom_blob.h;int channels = bottom_blob.c;int outw = w / 2;int outh = h / 2;int outc = channels * 4;top_blob.create(outw, outh, outc, 4u, 1, opt.blob_allocator);if (top_blob.empty())return -100;#pragma omp parallel for num_threads(opt.num_threads)for (int p = 0; p < outc; p++){const float* ptr = bottom_blob.channel(p % channels).row((p / channels) % 2) + ((p / channels) / 2);float* outptr = top_blob.channel(p);for (int i = 0; i < outh; i++){for (int j = 0; j < outw; j++){*outptr = *ptr;outptr += 1;ptr += 2;}ptr += w;}}return 0;}
};DEFINE_LAYER_CREATOR(YoloV5Focus)
#endif //YOLOV5_V60Yolov5::Yolov5() {
}
Yolov5::~Yolov5() {
}float Yolov5::intersection_area(const Object& a, const Object& b)
{cv::Rect_<float> inter = a.rect & b.rect;return inter.area();
}void Yolov5::qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{int i = left;int j = right;float p = faceobjects[(left + right) / 2].prob;while (i <= j){while (faceobjects[i].prob > p)i++;while (faceobjects[j].prob < p)j--;if (i <= j){// swapstd::swap(faceobjects[i], faceobjects[j]);i++;j--;}}#pragma omp parallel sections{
#pragma omp section{if (left < j) qsort_descent_inplace(faceobjects, left, j);}
#pragma omp section{if (i < right) qsort_descent_inplace(faceobjects, i, right);}}
}void Yolov5::qsort_descent_inplace(std::vector<Object>& faceobjects)
{if (faceobjects.empty())return;qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}void Yolov5::nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].rect.area();}for (int i = 0; i < n; i++){const Object& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const Object& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold)keep = 0;}if (keep)picked.push_back(i);}
}float Yolov5::sigmoid(float x)
{return static_cast<float>(1.f / (1.f + exp(-x)));
}void Yolov5::generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector<Object>& objects)
{const int num_grid = feat_blob.h;int num_grid_x;int num_grid_y;if (in_pad.w > in_pad.h){num_grid_x = in_pad.w / stride;num_grid_y = num_grid / num_grid_x;}else{num_grid_y = in_pad.h / stride;num_grid_x = num_grid / num_grid_y;}const int num_class = feat_blob.w - 5;const int num_anchors = anchors.w / 2;for (int q = 0; q < num_anchors; q++){const float anchor_w = anchors[q * 2];const float anchor_h = anchors[q * 2 + 1];const ncnn::Mat feat = feat_blob.channel(q);for (int i = 0; i < num_grid_y; i++){for (int j = 0; j < num_grid_x; j++){const float* featptr = feat.row(i * num_grid_x + j);// find class index with max class scoreint class_index = 0;float class_score = -FLT_MAX;for (int k = 0; k < num_class; k++){float score = featptr[5 + k];if (score > class_score){class_index = k;class_score = score;}}float box_score = featptr[4];float confidence = sigmoid(box_score) * sigmoid(class_score);if (confidence >= prob_threshold){// yolov5/models/yolo.py Detect forward// y = x[i].sigmoid()// y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i]  # xy// y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # whfloat dx = sigmoid(featptr[0]);float dy = sigmoid(featptr[1]);float dw = sigmoid(featptr[2]);float dh = sigmoid(featptr[3]);float pb_cx = (dx * 2.f - 0.5f + j) * stride;float pb_cy = (dy * 2.f - 0.5f + i) * stride;float pb_w = pow(dw * 2.f, 2) * anchor_w;float pb_h = pow(dh * 2.f, 2) * anchor_h;float x0 = pb_cx - pb_w * 0.5f;float y0 = pb_cy - pb_h * 0.5f;float x1 = pb_cx + pb_w * 0.5f;float y1 = pb_cy + pb_h * 0.5f;Object obj;obj.rect.x = x0;obj.rect.y = y0;obj.rect.width = x1 - x0;obj.rect.height = y1 - y0;obj.label = class_index;obj.prob = confidence;objects.push_back(obj);}}}}
}int Yolov5::init_model(const char* param_file, const char* bin_file) {m_yolov5.opt.use_vulkan_compute = true;// yolov5.opt.use_bf16_storage = true;// original pretrained model from https://github.com/ultralytics/yolov5// the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
#if YOLOV5_V60int ok0= m_yolov5.load_param(param_file);int ok1 = m_yolov5.load_model(bin_file);
#elseyolov5.register_custom_layer("YoloV5Focus", YoloV5Focus_layer_creator);yolov5.load_param("yolov5s.param");yolov5.load_model("yolov5s.bin");
#endifreturn ok0 + ok1;}int Yolov5::detect_yolov5(cv::Mat image)
{std::vector<Object> objects;int img_w = image.cols;int img_h = image.rows;// letterbox pad to multiple of MAX_STRIDEint w = img_w;int h = img_h;float scale = 1.f;if (w > h){scale = (float)m_target_size / w;w = m_target_size;h = h * scale;}else{scale = (float)m_target_size / h;h = m_target_size;w = w * scale;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);// pad to target_size rectangle// yolov5/utils/datasets.py letterboxint wpad = (w + MAX_STRIDE - 1) / MAX_STRIDE * MAX_STRIDE - w;int hpad = (h + MAX_STRIDE - 1) / MAX_STRIDE * MAX_STRIDE - h;ncnn::Mat in_pad;ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);const float norm_vals[3] = { 1 / 255.f, 1 / 255.f, 1 / 255.f };in_pad.substract_mean_normalize(0, norm_vals);ncnn::Extractor ex = m_yolov5.create_extractor();ex.input("images", in_pad);std::vector<Object> proposals;// anchor setting from yolov5/models/yolov5s.yaml// stride 8{ncnn::Mat out;ex.extract("output", out);ncnn::Mat anchors(6);anchors[0] = 10.f;anchors[1] = 13.f;anchors[2] = 16.f;anchors[3] = 30.f;anchors[4] = 33.f;anchors[5] = 23.f;std::vector<Object> objects8;generate_proposals(anchors, 8, in_pad, out, m_prob_threshold, objects8);proposals.insert(proposals.end(), objects8.begin(), objects8.end());}// stride 16{ncnn::Mat out;
#if YOLOV5_V60ex.extract("376", out);
#elseex.extract("781", out);
#endifncnn::Mat anchors(6);anchors[0] = 30.f;anchors[1] = 61.f;anchors[2] = 62.f;anchors[3] = 45.f;anchors[4] = 59.f;anchors[5] = 119.f;std::vector<Object> objects16;generate_proposals(anchors, 16, in_pad, out, m_prob_threshold, objects16);proposals.insert(proposals.end(), objects16.begin(), objects16.end());}// stride 32{ncnn::Mat out;
#if YOLOV5_V60ex.extract("401", out);
#elseex.extract("801", out);
#endifncnn::Mat anchors(6);anchors[0] = 116.f;anchors[1] = 90.f;anchors[2] = 156.f;anchors[3] = 198.f;anchors[4] = 373.f;anchors[5] = 326.f;std::vector<Object> objects32;generate_proposals(anchors, 32, in_pad, out, m_prob_threshold, objects32);proposals.insert(proposals.end(), objects32.begin(), objects32.end());}// sort all proposals by score from highest to lowestqsort_descent_inplace(proposals);// apply nms with nms_thresholdstd::vector<int> picked;nms_sorted_bboxes(proposals, picked, m_nms_threshold);int count = picked.size();objects.resize(count);for (int i = 0; i < count; i++){objects[i] = proposals[picked[i]];// adjust offset to original unpaddedfloat x0 = (objects[i].rect.x - (wpad / 2)) / scale;float y0 = (objects[i].rect.y - (hpad / 2)) / scale;float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;// clipx0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);objects[i].rect.x = x0;objects[i].rect.y = y0;objects[i].rect.width = x1 - x0;objects[i].rect.height = y1 - y0;}draw_objects(image, objects);return 0;}void Yolov5::draw_objects(const cv::Mat& image, const std::vector<Object>& objects)
{static const char* class_names[] = {"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light","fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow","elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee","skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard","tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple","sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch","potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone","microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear","hair drier", "toothbrush"};for (size_t i = 0; i < objects.size(); i++){const Object& obj = objects[i];fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);cv::rectangle(image, obj.rect, cv::Scalar(255, 0, 0));char text[256];sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);int baseLine = 0;cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);int x = obj.rect.x;int y = obj.rect.y - label_size.height - baseLine;if (y < 0)y = 0;if (x + label_size.width > image.cols)x = image.cols - label_size.width;cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),cv::Scalar(255, 255, 255), -1);cv::putText(image, text, cv::Point(x, y + label_size.height),cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));}// cv::imshow("demo", image);//cv::waitKey(0);//return image;
}

先使用vs测试一下

然后再生dll库

58、Visual studio 2019+C#传递Mat数据给C++动态包处理,并将处理结果Mat返回给C#显示、保存_sxj731533730-CSDN博客

二、然后在创建.NET工程,拖拽三个按钮和两个pictureBox画布

在已经安装的模板中选择编程语言为 --->visualC#,

选择 ---> "windows经典桌面中的Windows窗体应用"

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using OpenCvSharp;
using System.Drawing;
using OpenCvSharp.Extensions;
using System.Text;namespace WindowsFormsApp1
{static class Program{[DllImport(@"F:\sxj\20211108\detectYolov5Ncnn\x64\Release\detectYolov5Ncnn.dll",CharSet = CharSet.Ansi,CallingConvention = CallingConvention.StdCall)]public static extern int init_model(StringBuilder model_param, StringBuilder model_bin);/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){StringBuilder model_param = new StringBuilder("F:\\sxj\\20211201\\yolov5s_6.0.param");StringBuilder model_bin = new StringBuilder("F:\\sxj\\20211201\\yolov5s_6.0.bin");init_model(model_param, model_bin);Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}
}

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;using OpenCvSharp;
using System.Runtime.InteropServices;
using OpenCvSharp.Extensions;
using System.IO.Compression;
using System.Drawing.Imaging;namespace WindowsFormsApp1
{public partial class Form1 : Form{[DllImport(@"F:\sxj\20211108\detectYolov5Ncnn\x64\Release\detectYolov5Ncnn.dll")]public static extern int detect_image(byte[] ImageBuffer, byte[] , int imageHeight, int imageWidth );public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){OpenFileDialog openFileDialog = new OpenFileDialog();openFileDialog.Filter = @"jpeg|*.jpg|bmp|*.bmp|gif|*.gif";if (openFileDialog.ShowDialog() == DialogResult.OK){string fullpath = openFileDialog.FileName;FileStream fs = new FileStream(fullpath, FileMode.Open);byte[] picturebytes = new byte[fs.Length];BinaryReader br = new BinaryReader(fs);picturebytes = br.ReadBytes(Convert.ToInt32(fs.Length));MemoryStream ms = new MemoryStream(picturebytes);Bitmap bmpt = new Bitmap(ms);pictureBox1.Image = bmpt;pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;}else{MessageBox.Show("图片打开失败");}}private void button2_Click(object sender, EventArgs e){SaveFileDialog saveImageDialog = new SaveFileDialog();saveImageDialog.Title = "图片保存";saveImageDialog.Filter = @"jpeg|*.jpg|bmp|*.bmp";saveImageDialog.FileName = System.DateTime.Now.ToString("yyyyMMddHHmmss");//设置默认文件名if (saveImageDialog.ShowDialog() == DialogResult.OK){string fileName = saveImageDialog.FileName.ToString();//Console.WriteLine("fileName" + fileName);if (fileName != "" && fileName != null){string fileExtName = fileName.Substring(fileName.LastIndexOf(".") + 1).ToString();//Console.WriteLine("fileExtName" + fileExtName);System.Drawing.Imaging.ImageFormat imgformat = null;if (fileExtName != ""){switch (fileExtName){case "jpg":imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;break;case "bmp":imgformat = System.Drawing.Imaging.ImageFormat.Bmp;break;default:imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;break;}try{Bitmap bit = new Bitmap(pictureBox2.Image);MessageBox.Show(fileName);pictureBox2.Image.Save(fileName, imgformat);}catch{}}}}}private void button3_Click(object sender, EventArgs e){Bitmap bmp = (Bitmap)pictureBox1.Image.Clone();byte[] source = GetBGRValues(bmp);byte[] dest = source;detect_image(source, dest, bmp.Height, bmp.Width);Bitmap bmpConvert = Byte2Bitmap(dest, bmp.Width, bmp.Height);Image images = bmpConvert;pictureBox2.Image = images;pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;}public static byte[] GetBGRValues(Bitmap bmp){var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;var imgBytes = bmp.Height * rowBytes;byte[] rgbValues = new byte[imgBytes];IntPtr ptr = bmpData.Scan0;for (var i = 0; i < bmp.Height; i++){Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);ptr += bmpData.Stride;}bmp.UnlockBits(bmpData);return rgbValues;}public static Bitmap Byte2Bitmap(Byte[] data, int width, int height){Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);BitmapData bmData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);IntPtr ptr = bmData.Scan0;for (int i = 0; i < image.Height; i++){Marshal.Copy(data, i * image.Width * 3, ptr, image.Width * 3);ptr = (IntPtr)(ptr.ToInt64() + bmData.Stride);}image.UnlockBits(bmData);return image;}private void pictureBox1_Click(object sender, EventArgs e){}}
}

测试效果图

(1)初始化界面

(2)选择一张图

(3)检测出结果

三、如果想把检测的目标坐标和类别置信度返回给c# 可以这样写(目前只写了一个支持单类检测的,多类别信息需要变成数组即可)

main.cpp函数


#include "connect.h"int main(int argc, char** argv)
{ConnectCppWrapper::DefectResult* data = new  ConnectCppWrapper::DefectResult;printf("data.prob= %f\n", data->prob);printf("data.label= %d\n", data->label);printf("data.x= %d\n", data->x);printf("data.y= %d\n", data->y);printf("data.width= %d\n", data->width);printf("data.height= %d\n", data->height);cv::Mat image = cv::imread("F:\\sxj\\predictions.jpg");unsigned char* src = image.data;cv::Mat result = cv::Mat( image.rows,image.cols,  CV_8UC3, src);unsigned char* dest = result.data;const char* model_param = "F:\\sxj\\20211201\\yolov5s_6.0.param";const char* model_bin = "F:\\sxj\\20211201\\yolov5s_6.0.bin";ConnectCppWrapper::init_model(model_param, model_bin);ConnectCppWrapper::detect_image(src, dest,  image.rows,image.cols, data);printf("data.prob= %f\n", data->prob);printf("data.label= %d\n", data->label);printf("data.x= %d\n", data->x);printf("data.y= %d\n", data->y);printf("data.width= %d\n", data->width);printf("data.height= %d\n", data->height);cv::imshow("demo", result);cv::waitKey(0);return 0;
}

connect.h头文件

#pragma once#include "yolov5.h"namespace  ConnectCppWrapper
{struct DefectResult{int label = 0;float prob = 0;int x = 0;int y = 0;int width = 0;int height = 0;};extern "C" __declspec(dllexport) int __stdcall init_model(const char* model_param, const char* bin_param);extern "C" __declspec(dllexport) int __stdcall detect_image(unsigned char* ImageBuffer, unsigned char* ImageResult,int height, int width,  DefectResult * data);}

connect.cpp文件

#include "connect.h"namespace ConnectCppWrapper
{Yolov5* yolov5Item = new Yolov5();int __stdcall init_model(const char* model_param, const char* bin_param){return yolov5Item->init_model(model_param, bin_param);}int __stdcall detect_image(unsigned char* ImageBuffer, unsigned char* ImageResult, int height,int width, DefectResult* data){cv::Mat result;cv::Mat image = cv::Mat(height,width , CV_8UC3, ImageBuffer);std::vector<Object> objects;int ok = yolov5Item->detect_yolov5(image, objects);if (ok == 0){printf("detect image is successful\n");}for (int i = 0; i < objects.size(); i++){data->label = objects[i].label;data->prob = objects[i].prob;data->x = objects[i].rect.x;data->y = objects[i].rect.x;data->width = objects[i].rect.width;data->height = objects[i].rect.height;printf("%f  %f  %f %f %d %f\n", objects[i].rect.x, objects[i].rect.y, objects[i].rect.width, objects[i].rect.height, objects[i].label, objects[i].prob);}int length = (int)(result.total() * result.elemSize());unsigned char* buffer = new unsigned char[length];memcpy(ImageResult, result.data, length * sizeof(unsigned char));return 0;}
}

yolov5.h头文件

#pragma once#include "layer.h"
#include "net.h"#if defined(USE_NCNN_SIMPLEOCV)
#include "simpleocv.h"
#else
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#endif
#include <float.h>
#include <stdio.h>
#include <vector>#define YOLOV5_V60 1 //YOLOv5 v6.0
using namespace std;
using namespace ncnn;
struct Object
{cv::Rect_<float> rect;int label;float prob;
};
class Yolov5 {
public:Yolov5();~Yolov5();
private:float  intersection_area(const Object& a, const Object& b);void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right);void qsort_descent_inplace(std::vector<Object>& faceobjects);void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold);void generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector<Object>& objects);float sigmoid(float x);
public:int detect_yolov5(cv::Mat image, std::vector<Object>& objects);void  draw_objects(const cv::Mat& image, const std::vector<Object>& objects);int init_model(const char* param_file, const char* bin_file);private:ncnn::Net m_yolov5;const int m_target_size = 640;const float m_prob_threshold = 0.25f;const float m_nms_threshold = 0.45f;};

yolov5.cpp文件


#include "yolov5.h"
#if YOLOV5_V60
#define MAX_STRIDE 64
#else
#define MAX_STRIDE 32
class YoloV5Focus : public ncnn::Layer
{
public:YoloV5Focus(){one_blob_only = true;}virtual int forward(const ncnn::Mat& bottom_blob, ncnn::Mat& top_blob, const ncnn::Option& opt) const{int w = bottom_blob.w;int h = bottom_blob.h;int channels = bottom_blob.c;int outw = w / 2;int outh = h / 2;int outc = channels * 4;top_blob.create(outw, outh, outc, 4u, 1, opt.blob_allocator);if (top_blob.empty())return -100;#pragma omp parallel for num_threads(opt.num_threads)for (int p = 0; p < outc; p++){const float* ptr = bottom_blob.channel(p % channels).row((p / channels) % 2) + ((p / channels) / 2);float* outptr = top_blob.channel(p);for (int i = 0; i < outh; i++){for (int j = 0; j < outw; j++){*outptr = *ptr;outptr += 1;ptr += 2;}ptr += w;}}return 0;}
};DEFINE_LAYER_CREATOR(YoloV5Focus)
#endif //YOLOV5_V60Yolov5::Yolov5() {
}
Yolov5::~Yolov5() {
}float Yolov5::intersection_area(const Object& a, const Object& b)
{cv::Rect_<float> inter = a.rect & b.rect;return inter.area();
}void Yolov5::qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{int i = left;int j = right;float p = faceobjects[(left + right) / 2].prob;while (i <= j){while (faceobjects[i].prob > p)i++;while (faceobjects[j].prob < p)j--;if (i <= j){// swapstd::swap(faceobjects[i], faceobjects[j]);i++;j--;}}#pragma omp parallel sections{
#pragma omp section{if (left < j) qsort_descent_inplace(faceobjects, left, j);}
#pragma omp section{if (i < right) qsort_descent_inplace(faceobjects, i, right);}}
}void Yolov5::qsort_descent_inplace(std::vector<Object>& faceobjects)
{if (faceobjects.empty())return;qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}void Yolov5::nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].rect.area();}for (int i = 0; i < n; i++){const Object& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const Object& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold)keep = 0;}if (keep)picked.push_back(i);}
}float Yolov5::sigmoid(float x)
{return static_cast<float>(1.f / (1.f + exp(-x)));
}void Yolov5::generate_proposals(const ncnn::Mat& anchors, int stride, const ncnn::Mat& in_pad, const ncnn::Mat& feat_blob, float prob_threshold, std::vector<Object>& objects)
{const int num_grid = feat_blob.h;int num_grid_x;int num_grid_y;if (in_pad.w > in_pad.h){num_grid_x = in_pad.w / stride;num_grid_y = num_grid / num_grid_x;}else{num_grid_y = in_pad.h / stride;num_grid_x = num_grid / num_grid_y;}const int num_class = feat_blob.w - 5;const int num_anchors = anchors.w / 2;for (int q = 0; q < num_anchors; q++){const float anchor_w = anchors[q * 2];const float anchor_h = anchors[q * 2 + 1];const ncnn::Mat feat = feat_blob.channel(q);for (int i = 0; i < num_grid_y; i++){for (int j = 0; j < num_grid_x; j++){const float* featptr = feat.row(i * num_grid_x + j);// find class index with max class scoreint class_index = 0;float class_score = -FLT_MAX;for (int k = 0; k < num_class; k++){float score = featptr[5 + k];if (score > class_score){class_index = k;class_score = score;}}float box_score = featptr[4];float confidence = sigmoid(box_score) * sigmoid(class_score);if (confidence >= prob_threshold){// yolov5/models/yolo.py Detect forward// y = x[i].sigmoid()// y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i]  # xy// y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i]  # whfloat dx = sigmoid(featptr[0]);float dy = sigmoid(featptr[1]);float dw = sigmoid(featptr[2]);float dh = sigmoid(featptr[3]);float pb_cx = (dx * 2.f - 0.5f + j) * stride;float pb_cy = (dy * 2.f - 0.5f + i) * stride;float pb_w = pow(dw * 2.f, 2) * anchor_w;float pb_h = pow(dh * 2.f, 2) * anchor_h;float x0 = pb_cx - pb_w * 0.5f;float y0 = pb_cy - pb_h * 0.5f;float x1 = pb_cx + pb_w * 0.5f;float y1 = pb_cy + pb_h * 0.5f;Object obj;obj.rect.x = x0;obj.rect.y = y0;obj.rect.width = x1 - x0;obj.rect.height = y1 - y0;obj.label = class_index;obj.prob = confidence;objects.push_back(obj);}}}}
}int Yolov5::init_model(const char* param_file, const char* bin_file) {m_yolov5.opt.use_vulkan_compute = true;// yolov5.opt.use_bf16_storage = true;// original pretrained model from https://github.com/ultralytics/yolov5// the ncnn model https://github.com/nihui/ncnn-assets/tree/master/models
#if YOLOV5_V60int ok0 = m_yolov5.load_param(param_file);int ok1 = m_yolov5.load_model(bin_file);
#elseyolov5.register_custom_layer("YoloV5Focus", YoloV5Focus_layer_creator);yolov5.load_param("yolov5s.param");yolov5.load_model("yolov5s.bin");
#endifreturn ok0 + ok1;
}
int Yolov5::detect_yolov5(cv::Mat image, std::vector<Object>& objects)
{int img_w = image.cols;int img_h = image.rows;// letterbox pad to multiple of MAX_STRIDEint w = img_w;int h = img_h;float scale = 1.f;if (w > h){scale = (float)m_target_size / w;w = m_target_size;h = h * scale;}else{scale = (float)m_target_size / h;h = m_target_size;w = w * scale;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(image.data, ncnn::Mat::PIXEL_BGR2RGB, img_w, img_h, w, h);// pad to target_size rectangle// yolov5/utils/datasets.py letterboxint wpad = (w + MAX_STRIDE - 1) / MAX_STRIDE * MAX_STRIDE - w;int hpad = (h + MAX_STRIDE - 1) / MAX_STRIDE * MAX_STRIDE - h;ncnn::Mat in_pad;ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 114.f);const float norm_vals[3] = { 1 / 255.f, 1 / 255.f, 1 / 255.f };in_pad.substract_mean_normalize(0, norm_vals);ncnn::Extractor ex = m_yolov5.create_extractor();ex.input("images", in_pad);std::vector<Object> proposals;// anchor setting from yolov5/models/yolov5s.yaml// stride 8{ncnn::Mat out;ex.extract("output", out);ncnn::Mat anchors(6);anchors[0] = 10.f;anchors[1] = 13.f;anchors[2] = 16.f;anchors[3] = 30.f;anchors[4] = 33.f;anchors[5] = 23.f;std::vector<Object> objects8;generate_proposals(anchors, 8, in_pad, out, m_prob_threshold, objects8);proposals.insert(proposals.end(), objects8.begin(), objects8.end());}// stride 16{ncnn::Mat out;
#if YOLOV5_V60ex.extract("376", out);
#elseex.extract("781", out);
#endifncnn::Mat anchors(6);anchors[0] = 30.f;anchors[1] = 61.f;anchors[2] = 62.f;anchors[3] = 45.f;anchors[4] = 59.f;anchors[5] = 119.f;std::vector<Object> objects16;generate_proposals(anchors, 16, in_pad, out, m_prob_threshold, objects16);proposals.insert(proposals.end(), objects16.begin(), objects16.end());}// stride 32{ncnn::Mat out;
#if YOLOV5_V60ex.extract("401", out);
#elseex.extract("801", out);
#endifncnn::Mat anchors(6);anchors[0] = 116.f;anchors[1] = 90.f;anchors[2] = 156.f;anchors[3] = 198.f;anchors[4] = 373.f;anchors[5] = 326.f;std::vector<Object> objects32;generate_proposals(anchors, 32, in_pad, out, m_prob_threshold, objects32);proposals.insert(proposals.end(), objects32.begin(), objects32.end());}// sort all proposals by score from highest to lowestqsort_descent_inplace(proposals);// apply nms with nms_thresholdstd::vector<int> picked;nms_sorted_bboxes(proposals, picked, m_nms_threshold);int count = picked.size();objects.resize(count);for (int i = 0; i < count; i++){objects[i] = proposals[picked[i]];// adjust offset to original unpaddedfloat x0 = (objects[i].rect.x - (wpad / 2)) / scale;float y0 = (objects[i].rect.y - (hpad / 2)) / scale;float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;// clipx0 = std::max(std::min(x0, (float)(img_w - 1)), 0.f);y0 = std::max(std::min(y0, (float)(img_h - 1)), 0.f);x1 = std::max(std::min(x1, (float)(img_w - 1)), 0.f);y1 = std::max(std::min(y1, (float)(img_h - 1)), 0.f);objects[i].rect.x = x0;objects[i].rect.y = y0;objects[i].rect.width = x1 - x0;objects[i].rect.height = y1 - y0;}draw_objects(image, objects);return 0;}void Yolov5::draw_objects(const cv::Mat& image, const std::vector<Object>& objects)
{static const char* class_names[] = {"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light","fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow","elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee","skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard","tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple","sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch","potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone","microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear","hair drier", "toothbrush"};for (size_t i = 0; i < objects.size(); i++){const Object& obj = objects[i];fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);cv::rectangle(image, obj.rect, cv::Scalar(255, 0, 0));char text[256];sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);int baseLine = 0;cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);int x = obj.rect.x;int y = obj.rect.y - label_size.height - baseLine;if (y < 0)y = 0;if (x + label_size.width > image.cols)x = image.cols - label_size.width;cv::rectangle(image, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),cv::Scalar(255, 255, 255), -1);cv::putText(image, text, cv::Point(x, y + label_size.height),cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));}// cv::imshow("demo", image);//cv::waitKey(0);//return image;
}

同时将构建的工程设置一下

先测试一下生成exe是没有问题的,然后在生成dll动态库,去修改一下c#的代码,进行联调即可

c#的工程也需要设置一下

c# Program.cs文件内容不变

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using OpenCvSharp;
using System.Drawing;
using OpenCvSharp.Extensions;
using System.Text;namespace WindowsFormsApp1
{static class Program{[DllImport(@"F:\sxj\20211108\detectYolov5Ncnn\x64\Release\detectYolov5Ncnn.dll",CharSet = CharSet.Ansi,CallingConvention = CallingConvention.StdCall)]public static extern int init_model(StringBuilder model_param, StringBuilder model_bin);/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){StringBuilder model_param = new StringBuilder("F:\\sxj\\20211201\\yolov5s_6.0.param");StringBuilder model_bin = new StringBuilder("F:\\sxj\\20211201\\yolov5s_6.0.bin");init_model(model_param, model_bin);Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}
}

Form1.cs文件修改一下,写了个只支持单检测类,好像很复杂 c#和c++ 进行托管内存和非托管内存传递,看的msdn有点复杂

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;using OpenCvSharp;
using System.Runtime.InteropServices;
using OpenCvSharp.Extensions;
using System.IO.Compression;
using System.Drawing.Imaging;namespace WindowsFormsApp1
{public partial class Form1 : Form{[DllImport(@"F:\sxj\20211108\detectYolov5Ncnn\x64\Release\detectYolov5Ncnn.dll", CallingConvention = CallingConvention.StdCall)]public static extern int detect_image(byte[] ImageBuffer, byte[] ImageResult, int imageHeight, int imageWidth , IntPtr ptrItem);[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]public struct DefectResult{public int label;public float prob;public int x;public int y;public int width;public int height;}public Form1(){InitializeComponent();}private void button1_Click(object sender, EventArgs e){OpenFileDialog openFileDialog = new OpenFileDialog();openFileDialog.Filter = @"jpeg|*.jpg|bmp|*.bmp|gif|*.gif";if (openFileDialog.ShowDialog() == DialogResult.OK){string fullpath = openFileDialog.FileName;FileStream fs = new FileStream(fullpath, FileMode.Open);byte[] picturebytes = new byte[fs.Length];BinaryReader br = new BinaryReader(fs);picturebytes = br.ReadBytes(Convert.ToInt32(fs.Length));MemoryStream ms = new MemoryStream(picturebytes);Bitmap bmpt = new Bitmap(ms);pictureBox1.Image = bmpt;pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;}else{MessageBox.Show("图片打开失败");}}private void button2_Click(object sender, EventArgs e){SaveFileDialog saveImageDialog = new SaveFileDialog();saveImageDialog.Title = "图片保存";saveImageDialog.Filter = @"jpeg|*.jpg|bmp|*.bmp";saveImageDialog.FileName = System.DateTime.Now.ToString("yyyyMMddHHmmss");//设置默认文件名if (saveImageDialog.ShowDialog() == DialogResult.OK){string fileName = saveImageDialog.FileName.ToString();//Console.WriteLine("fileName" + fileName);if (fileName != "" && fileName != null){string fileExtName = fileName.Substring(fileName.LastIndexOf(".") + 1).ToString();//Console.WriteLine("fileExtName" + fileExtName);System.Drawing.Imaging.ImageFormat imgformat = null;if (fileExtName != ""){switch (fileExtName){case "jpg":imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;break;case "bmp":imgformat = System.Drawing.Imaging.ImageFormat.Bmp;break;default:imgformat = System.Drawing.Imaging.ImageFormat.Jpeg;break;}try{Bitmap bit = new Bitmap(pictureBox2.Image);MessageBox.Show(fileName);pictureBox2.Image.Save(fileName, imgformat);}catch{}}}}}private void button3_Click(object sender, EventArgs e){int workStationCount = 1;int size = Marshal.SizeOf(typeof(DefectResult)) * workStationCount;byte[] bytes = new byte[size];IntPtr infosIntptr = Marshal.AllocHGlobal(size);DefectResult[] pClass = new DefectResult[workStationCount];Bitmap bmp = (Bitmap)pictureBox1.Image.Clone();byte[] source = GetBGRValues(bmp);byte[] dest = source;detect_image(source, dest, bmp.Height, bmp.Width, infosIntptr);string[] class_names = new string[]{"person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light","fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow","elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee","skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard","tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple","sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch","potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone","microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear","hair drier", "toothbrush"};for (int inkIndex = 0; inkIndex < workStationCount; inkIndex++){IntPtr pPonitor = new IntPtr(infosIntptr.ToInt64() + Marshal.SizeOf(typeof(DefectResult)) * inkIndex);pClass[inkIndex] = (DefectResult)Marshal.PtrToStructure(pPonitor, typeof(DefectResult));Console.WriteLine("{0} ", pClass[inkIndex].prob);Console.WriteLine("{0} ", class_names[pClass[inkIndex].label]);Console.WriteLine("{0} ", pClass[inkIndex].x);Console.WriteLine("{0} ", pClass[inkIndex].y);Console.WriteLine("{0} ", pClass[inkIndex].width);Console.WriteLine("{0} ", pClass[inkIndex].height);}Marshal.FreeHGlobal(infosIntptr);Bitmap bmpConvert = Byte2Bitmap(dest, bmp.Width, bmp.Height);Image images = bmpConvert;pictureBox2.Image = images;pictureBox2.SizeMode = PictureBoxSizeMode.StretchImage;}public static byte[] GetBGRValues(Bitmap bmp){var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);var bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);var rowBytes = bmpData.Width * Image.GetPixelFormatSize(bmp.PixelFormat) / 8;var imgBytes = bmp.Height * rowBytes;byte[] rgbValues = new byte[imgBytes];IntPtr ptr = bmpData.Scan0;for (var i = 0; i < bmp.Height; i++){Marshal.Copy(ptr, rgbValues, i * rowBytes, rowBytes);ptr += bmpData.Stride;}bmp.UnlockBits(bmpData);return rgbValues;}public static Bitmap Byte2Bitmap(Byte[] data, int width, int height){Bitmap image = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);BitmapData bmData = image.LockBits(rect, ImageLockMode.ReadWrite, image.PixelFormat);IntPtr ptr = bmData.Scan0;for (int i = 0; i < image.Height; i++){Marshal.Copy(data, i * image.Width * 3, ptr, image.Width * 3);ptr = (IntPtr)(ptr.ToInt64() + bmData.Stride);}image.UnlockBits(bmData);return image;}private void pictureBox1_Click(object sender, EventArgs e){}}
}

然后使用debug模式或者直接release模式输出一下 就可看到 在c++ 中完成了绘图功能,也可以把类别信息返回了

附录一下debug调试结果

还是那位可爱的小姐姐

注意 c#中的图片高 宽的参数与 c++的高宽 对应一致~~~

如果需要从c++的dll传递变长的数据结构给c#,可以使用在c++ 使用rapidjson组成字符串,然后赋值给char 数组,然后在c#序列化成json就可以完成多目标数据递送

48、OAK通过共享内存传递变长结构体(Rapidjson)进行数据和图片交互_天昼AI实验室的博客-CSDN博客

60、在Visual Studio 2019 环境下,使用C#调用C++生成的dll实现yolov5的图片检测相关推荐

  1. PaddleOCR——Visual Studio 2019 环境下C++推理部署 CMake 编译解决方案

    PaddleOCR--Visual Studio 2019 环境下C++推理部署 CMake 编译解决方案 官方文档 环境配置 Step1: 下载PaddlePaddle C++ 预测库 paddle ...

  2. Win10 在 Visual Studio 2019 环境下配置 CUDA 10.1 + TensorFlow-GPU

    1. 安装VS2019 我安装的版本是enterprise 企业版. 2. 查看Nvidia显卡支持的cuda版本 设置-->搜索控制面板->在控制面板里搜索nvidia->打开Nv ...

  3. PaddleOCR——C++服务端部署Visual Studio 2019 环境下CMake 编译错误【无法打开输入文件paddle_fluid.lib】解决方案

    问题描述 找不到paddle_fluid.lib文件 问题分析 2.0.0以上版本的Windows PaddlePaddle 预测库中没有paddle_fluid.lib文件. 解决方案 方法一:修改 ...

  4. 多版本Visual Studio 集成环境下的WCF 开发-服务库默认配置表

    多版本Visual Studio 集成环境下的WCF 开发-服务库默认配置表 Visual Studio2008 Visual Studio2008只有服务库,而没有服务应用程序,其服务库生成配置文件 ...

  5. 关于在Office 2010,Visual Studio 2010环境下开发Office 2007解决方案的问题

    Office解决方案分应用级解决方案(application-level solution 也就是Add-in)和文档级解决方案(document-level solution包括Excel temp ...

  6. 【备忘】visual studio调试状态下显示lua调用栈

    编辑文件:[VS安装目录]/Common7/Packages/Debugger/autoexp.dat,在文件最后[hresult]之上的空白插入以下代码,就可以在 visual studio调试的时 ...

  7. Visual Studio IDE环境下利用模板创建和手动配置CUDA项目教程

    目前版本的cuda是很方便的,它的一个安装里面包括了Toolkit`SDK`document`Nsight等等,而不用你自己去挨个安装,这样也避免了版本的不同步问题. 1 cuda5.5的下载地址,官 ...

  8. 关于解决Visual Studio 2019 在深色背景下括号显示异常的问题

    好久没用VS了,今天打开写了一下C#,为了检查括号是否匹配差点看瞎我的眼.百度一番,未果,但还是跟着其它博主的思路解决了这个问题,特此记录. 工具->选项->环境->字体和颜色-&g ...

  9. 【Tools】Visual Studio 2019搭建Qt开发环境

    00. 目录 文章目录 00. 目录 01. 概述 02. Visual Studio 2019安装 03. Qt6安装 04. qt-vsaddin插件下载 05. qt-vsaddin插件安装 0 ...

  10. 【Visual Studio 2019】创建 MFC 桌面程序 ( 安装 MFC 开发组件 | 创建 MFC 应用 | MFC 应用窗口编辑 | 为按钮添加点击事件 | 修改按钮文字 | 打开应用 )

    文章目录 一.安装 MFC 开发组件 二.创建 MFC 应用 三.MFC 应用窗口编辑 四.为按钮添加点击事件 五.修改按钮文字 六.打开系统其它应用 七.博客源码 一.安装 MFC 开发组件 打开 ...

最新文章

  1. 硬改TP-Link WR841N v8刷breed和OpenWrt
  2. React Native Expo开发的OW移动端项目
  3. 10 个免费的服务器监控工具推荐
  4. 大数据学习(1)Hadoop安装
  5. 设计模式-工厂方法(Factory Method)
  6. 手机屏幕宽高像素计算_2020年的智能手机拍照新设计,就全看下半年了
  7. ONNX系列五 --- 在C#中使用可移植的ONNX AI模型
  8. ionic4 hammerjs手势事件左滑右滑
  9. Filter的基本配置
  10. 第三方接口开发(短信验证码)
  11. 解决navicat闪退
  12. kalibr_calibrate_cameras 卡在 Extracting calibration target corners
  13. 复旦-华盛顿大学EMBA 二十年20人丨徐欣:从外企转战民企的变身
  14. RPA机器人流程自动化
  15. jstl标签库jar包下载
  16. 三十五、影院页面(获取卖座官网的影院信息并显示)
  17. 导入mysql文件报错:ERROR: ASCII ‘\0‘ appeared in the statement, but this is not allowed unless option --bin
  18. 看一遍你也会做!用英伟达 DIGITS 进行图像分割(上)
  19. Xilinx PCIe下行调试
  20. HDR视频和截图曝光严重,需要如何处理?

热门文章

  1. win10下什么拼音输入法好用
  2. 基于vue3.0全家桶H5模板
  3. Qt: json对象转格式化字符串
  4. Nginx常见面试题整理---40题
  5. 风车im即时通讯源码支持打包app/H5/php开源版正常搭建无报错版,带搭建教程
  6. 基于单片机倾角检测仪设计分享
  7. C++单例模式--线程安全
  8. 怎么让plsqldev恢复界面视图复位
  9. Mac Book Pro中idea常用快捷键
  10. psftp的简单使用