手把手教你用c#制作一個小型桌面程序
封裝dll
首先使用visual stdio 創(chuàng)建Dll新項目,然后屬性管理器導入自己的工程屬性表(如果沒有可以參考visual stdio 如何配置opencv等其他環(huán)境)
創(chuàng)建完成后 系統(tǒng)會自動生成一些文件,其中 pch.cpp 先不要修改,pch.h中先導入自己需要用到的庫,下面是我的代碼
pch.h
#pragma once #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <iostream> #include <string>
現(xiàn)在編寫我們的接口代碼,我封裝的是resnet18的代碼:
首先添加源文件ResNetDll.cpp:
ResNetDll.cpp
#include "pch.h" #include "ResNetDll.h" // 全局變量,用于存儲模型路徑和圖像路徑 static std::string g_imagePath; static std::string g_modelPath; // 圖像預處理函數(shù) cv::Mat transformImage(const std::string& imagePath) { cv::Mat image = cv::imread(imagePath); if (image.empty()) { throw std::runtime_error("Failed to load image."); } cv::Mat resizedImage; cv::resize(image, resizedImage, cv::Size(224, 224)); cv::Mat floatImage; resizedImage.convertTo(floatImage, CV_32F, 1.0 / 255.0); cv::Mat normalizedImage; cv::Scalar mean(0.485, 0.456, 0.406); cv::Scalar stdDev(0.229, 0.224, 0.225); cv::subtract(floatImage, mean, normalizedImage); cv::divide(normalizedImage, stdDev, normalizedImage); // 從 BGR 轉(zhuǎn)換到 RGB cv::Mat rgbImage; cv::cvtColor(normalizedImage, rgbImage, cv::COLOR_BGR2RGB); return rgbImage; } // 推理函數(shù) const char* run_inference() { static std::string result; try { // 加載 ONNX 模型 cv::dnn::Net net = cv::dnn::readNetFromONNX(g_modelPath); if (net.empty()) { result = "Failed to load the model."; return result.c_str(); } // 預處理圖像 cv::Mat rgbImage = transformImage(g_imagePath); // 創(chuàng)建 blob 并設置為網(wǎng)絡輸入 cv::Mat blob = cv::dnn::blobFromImage(rgbImage, 1.0, cv::Size(224, 224), cv::Scalar(), true, false); net.setInput(blob); // 執(zhí)行推理 cv::Mat output = net.forward(); // 處理輸出 cv::Mat prob = output.reshape(1, 1); // 變換成 1D 張量 cv::Point classIdPoint; double confidence; // 用來找到矩陣或圖像中元素的最小值和最大值,以及它們所在的位置 cv::minMaxLoc(prob, 0, &confidence, 0, &classIdPoint); int classId = classIdPoint.x; // 根據(jù)預測結(jié)果返回相應的標簽 result = "Predicted Class ID: " + std::to_string(classId) + " with confidence: " + std::to_string(confidence); return result.c_str(); } catch (const std::exception& e) { result = "Error occurred during inference: " + std::string(e.what()); return result.c_str(); } } // DLL 暴露的函數(shù),用于設置圖像路徑 extern "C" RESNETDLL_API void set_image_path(const char* imagePath) { g_imagePath = imagePath; } // DLL 暴露的函數(shù),用于設置模型路徑 extern "C" RESNETDLL_API void set_model_path(const char* modelPath) { g_modelPath = modelPath; } // DLL 暴露的函數(shù),運行推理 extern "C" RESNETDLL_API const char* run_resnet() { return run_inference(); }
ResNetDll.h:
#pragma once #ifdef RESNETDLL_EXPORTS #define RESNETDLL_API __declspec(dllexport) #else #define RESNETDLL_API __declspec(dllimport) #endif extern "C" { // 設置圖像路徑 RESNETDLL_API void set_image_path(const char* imagePath); // 設置模型路徑 RESNETDLL_API void set_model_path(const char* modelPath); // 運行推理 RESNETDLL_API const char* run_resnet(); }
點擊生成dll,就封裝成了windows動態(tài)庫
制作Demo
創(chuàng)建.NET Framework新項目,將之前生成的dll放在Demo文件夾的bin ->debug或是 release中(看你自己用的什么模式),
新建NativeMethods.cs 這個文件用于 導入 dll中的接口函數(shù)或類
我的代碼如下
NativeMethods.cs
using System; using System.Runtime.InteropServices; namespace ResNetApp { public static class NativeMethods { // 導入 DLL 中的 set_image_path 函數(shù) [DllImport("ResNetDll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void set_image_path(string imagePath); // 導入 DLL 中的 set_model_path 函數(shù) [DllImport("ResNetDll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void set_model_path(string modelPath); // 導入 DLL 中的 run_resnet 函數(shù) [DllImport("ResNetDll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr run_resnet(); } }
然后在窗口中拉入你想要的控件,這是我的窗口布局
布局完了之后會自動生成Form1.Designer.cs 的窗口設計代碼,點擊控件按F4 還可以修改他們的屬性
Form1.cs
這個代碼 編寫你想要每個控件實現(xiàn)的功能:
using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Windows.Forms; namespace ResNetApp { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void buttonSelectImage_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "圖像文件|*.bmp;*.jpg;*.jpeg;*.png"; if (openFileDialog.ShowDialog() == DialogResult.OK) { textBoxImagePath.Text = openFileDialog.FileName; // 顯示選擇的圖像路徑 } } private void buttonSelectModel_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.Filter = "ONNX 模型文件|*.onnx"; if (openFileDialog.ShowDialog() == DialogResult.OK) { textBoxModelPath.Text = openFileDialog.FileName; // 顯示選擇的模型路徑 } } private void button1_Click(object sender, EventArgs e) { try { string imagePath = textBoxImagePath.Text; string modelPath = textBoxModelPath.Text; if (string.IsNullOrEmpty(imagePath) || string.IsNullOrEmpty(modelPath)) { textBox1.Text = "請選擇圖像和模型路徑。"; return; } textBox1.Text = "開始運行 ResNet ..."; // 設置圖像路徑和模型路徑 NativeMethods.set_image_path(imagePath); NativeMethods.set_model_path(modelPath); // 調(diào)用 DLL 執(zhí)行推理 IntPtr resultPtr = NativeMethods.run_resnet(); // 將返回的指針轉(zhuǎn)換為字符串 string result = Marshal.PtrToStringAnsi(resultPtr); // 顯示結(jié)果 textBox1.Text = result; } catch (Exception ex) { textBox1.Text = "錯誤: " + ex.Message; } } } }
Program.cs
我們還需要一個入口主程序
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace ResNetApp { static class Program { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } } }
完成之后點擊生成 就可以在bin中出現(xiàn)的你的.exe文件咯,是不是很簡單呀~[狗頭]
總結(jié)
到此這篇關于用c#制作一個小型桌面程序的文章就介紹到這了,更多相關c#制作小型桌面程序內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#使用linq計算執(zhí)行元素在列表中出現(xiàn)次數(shù)的方法
這篇文章主要介紹了C#使用linq計算執(zhí)行元素在列表中出現(xiàn)次數(shù)的方法,涉及C#使用linq擴展進行列表查詢的技巧,需要的朋友可以參考下2015-04-04C#調(diào)用和實現(xiàn)WebService,純手工打造!
C#調(diào)用和實現(xiàn)WebService,純手工打造! 需要的朋友可以參考一下2013-02-02Unity3D利用DoTween實現(xiàn)卡牌翻轉(zhuǎn)效果
這篇文章主要為大家詳細介紹了Unity3D利用DoTween實現(xiàn)卡牌翻轉(zhuǎn)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-02-02c#?使用線程對串口serialPort進行收發(fā)數(shù)據(jù)(四種)
本文主要介紹了c#?使用線程對串口serialPort進行收發(fā)數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07