主頁 > .NET開發 > C# 人臉識別庫 0.1

C# 人臉識別庫 0.1

2020-09-11 17:23:51 .NET開發

[next ViewFaceCore 0.2]

.NET 人臉識別庫 ViewFaceCore

這是基于 SeetaFace6 人臉識別開發的 .NET 平臺下的人臉識別庫
這是一個使用超簡單的人臉識別庫
這是一個基于 .NET Standard 2.0 開發的庫
這個庫已經發布到 NuGet ,你可以一鍵集成到你的專案
此專案可以免費商業使用

?、開源

開源協議:Apache-2.0
GitHub地址: ViewFaceCore
十分感謝您的小星星

一、示例

示例專案地址:WinForm 攝像頭人臉檢測
示例專案效果:

 

二、使用

一分鐘在你的專案里集成人臉識別

1. 創建你的 .NET 應用

.NET Standard >= 2.0
.NET Core >= 2.0
.NET Framework >= 4.6.1^2


2. 使用 Nuget 安裝 ViewFaceCore

  • Author : View
  • Version >= 0.1.1

此 Nuget 包會自動添加依賴的 C++ 庫,以及最精簡的識別模型,
如果需要其它場景的識別模型,請下載 SeetaFace6 模型檔案,

3. 在專案中撰寫你的代碼

  • 按照 說明 自己撰寫
  • 或者參考以下代碼

簡單的呼叫示例

 1 static void Main()
 2         {
 3             ViewFace viewFace = new ViewFace((str) => { Debug.WriteLine(str); }); // 初始化人臉識別類,并設定 日志回呼函式
 4             viewFace.DetectorSetting = new DetectorSetting() { FaceSize = 20, MaxWidth = 2000, MaxHeight = 2000, Threshold = 0.5 };
 5 
 6             // 系統默認使用的輕量級識別模型,如果對精度有要求,請切換到 Normal 模式;并下載需要模型檔案 放入生成目錄的 model 檔案夾中
 7             viewFace.FaceType = FaceType.Normal;
 8             // 系統默認使用5個人臉關鍵點,//不建議改動,除非是使用口罩模型,
 9             viewFace.MarkType = MarkType.Light;
10 
11             #region 識別老照片
12             float[] oldEigenValues;
13             Bitmap oldImg = (Bitmap)Image.FromFile(@"C:\Users\yangw\OneDrive\圖片\Camera Roll\IMG_20181103_142707.jpg"/*老圖片路徑*/); // 從檔案中加載照片 // 或者視頻幀等
14             var oldFaces = viewFace.FaceDetector(oldImg); // 檢測圖片中包含的人臉資訊,(置信度、位置、大小)
15             if (oldFaces.Length > 0) //識別到人臉
16             {
17                 { // 列印人臉資訊
18                     Console.WriteLine($"識別到的人臉數量:{oldFaces.Length} ,人臉資訊:\n");
19                     Console.WriteLine($"序號\t人臉置信度\t位置X\t位置Y\t寬度\t高度");
20                     for (int i = 0; i < oldFaces.Length; i++)
21                     {
22                         Console.WriteLine($"{i + 1}\t{oldFaces[i].Score}\t{oldFaces[i].Location.X}\t{oldFaces[i].Location.Y}\t{oldFaces[i].Location.Width}\t{oldFaces[i].Location.Height}");
23                     }
24                     Console.WriteLine();
25                 }
26                 var oldPoints = viewFace.FaceMark(oldImg, oldFaces[0]); // 獲取 第一個人臉 的識別關鍵點,(人臉識別的關鍵點資料)
27                 oldEigenValues = viewFace.Extract(oldImg, oldPoints); // 獲取 指定的關鍵點 的特征值,
28             }
29             else { oldEigenValues = new float[0]; /*未識別到人臉*/ }
30             #endregion
31 
32             #region 識別新照片
33             float[] newEigenValues;
34             Bitmap newImg = (Bitmap)Image.FromFile(@"C:\Users\yangw\OneDrive\圖片\Camera Roll\IMG_20181129_224339.jpg"/*新圖片路徑*/); // 從檔案中加載照片 // 或者視頻幀等
35             var newFaces = viewFace.FaceDetector(newImg); // 檢測圖片中包含的人臉資訊,(置信度、位置、大小)
36             if (newFaces.Length > 0) //識別到人臉
37             {
38                 { // 列印人臉資訊
39                     Console.WriteLine($"識別到的人臉數量:{newFaces.Length} ,人臉資訊:\n");
40                     Console.WriteLine($"序號\t人臉置信度\t位置X\t位置Y\t寬度\t高度");
41                     for (int i = 0; i < newFaces.Length; i++)
42                     {
43                         Console.WriteLine($"{i + 1}\t{newFaces[i].Score}\t{newFaces[i].Location.X}\t{newFaces[i].Location.Y}\t{newFaces[i].Location.Width}\t{newFaces[i].Location.Height}");
44                     }
45                     Console.WriteLine();
46                 }
47                 var newPoints = viewFace.FaceMark(newImg, newFaces[0]); // 獲取 第一個人臉 的識別關鍵點,(人臉識別的關鍵點資料)
48                 newEigenValues = viewFace.Extract(newImg, newPoints); // 獲取 指定的關鍵點 的特征值,
49             }
50             else { newEigenValues = new float[0]; /*未識別到人臉*/ }
51             #endregion
52 
53             try
54             {
55                 float similarity = viewFace.Similarity(oldEigenValues, newEigenValues); // 對比兩張照片上的資料,確認是否是同一個人,
56                 Console.WriteLine($"閾值 = {Face.Threshold[viewFace.FaceType]}\t相似度 = {similarity}");
57                 Console.WriteLine($"是否是同一個人:{viewFace.IsSelf(similarity)}");
58             }
59             catch (Exception e)
60             { Console.WriteLine(e); }
61 
62             Console.ReadKey();
63         }
ViewFaceCore 使用示例

 

三、說明

命名空間:ViewFaceCore.Sharp : 人臉識別類所在的命名空間

  • 屬性說明:
  • 方法說明:
 
屬性名稱 型別 說明 默認值
ModelPath string 獲取或設定模型路徑 [ 如非必要,請勿修改 ] ./model/
FaceType FaceType 獲取或設定人臉型別 FaceType.Light
MarkType MarkType 獲取或設定人臉關鍵點型別 MarkType.Light
DetectorSetting DetectorSetting 獲取或設定人臉檢測器設定 new DetectorSetting()

 

 

 

 

 

 

 

 1 using System.Drawing;
 2 using ViewFaceCore.Sharp;
 3 using ViewFaceCore.Sharp.Model;
 4 
 5 // 識別 bitmap 中的人臉,并回傳人臉的資訊,
 6 FaceInfo[] FaceDetector(Bitmap);
 7 
 8 // 識別 bitmap 中指定的人臉資訊 info 的關鍵點坐標,
 9 FaceMarkPoint[] FaceMark(Bitmap, FaceInfo);
10 
11 // 提取人臉特征值,
12 float[] Extract(Bitmap, FaceMarkPoint[]);
13 
14 // 計算特征值相似度,
15 float Similarity(float[], float[]);
16 
17 // 判斷相似度是否為同一個人,
18 bool IsSelf(float);

四、實作

此專案受到了 SeetaFaceEngine.NET 專案的啟發

這個專案本質上來說還是呼叫了 SeetaFace 的 C++ 類別庫來實作的人臉識別功能,針對本人遇到過的相關的類別庫的使用都不太方便,而且使用的 SeetaFace 的版本較老,故萌生了自己重新開發的想法,

本專案在開發完成之后為了方便呼叫,采用了 Nuget 包的形式,將所有需要的依賴以及最小識別模型一起打包,在使用時非常簡單,只需要 nuget 安裝,撰寫代碼,運行即可,不需要多余的操作,

首先查看 SeetaFace ,已經更新到了v3(v6即v3)(上面前輩的專案是基于v1開發的),最新版本暫時沒有開源,但是可以免費商用,然后是根據以前的經驗和 SeetaFace6 檔案的指導,以及前輩的專案,做了以下操作,

1.對SeetaFace6 的介面進行了 C++ 形式的封裝,

目前主要實作了 人臉檢測,關鍵點提取,特征值提取,特征值對比幾個人臉識別中的基礎介面,有了這幾個介面,可以完整的實作一套人臉識別和驗證的流程,

  • c++封裝的介面代碼如下:
  1 #include "seeta/FaceDetector.h"
  2 #include "seeta/FaceLandmarker.h"
  3 #include "seeta/FaceRecognizer.h"
  4 
  5 #include <time.h>
  6 
  7 #define View_Api extern "C" __declspec(dllexport)
  8 
  9 using namespace std;
 10 
 11 typedef void(_stdcall* LogCallBack)(const char* logText);
 12 
 13 string modelPath = "./model/"; // 模型所在路徑
 14 LogCallBack logger = NULL; // 日志回呼函式
 15 
 16 // 列印日志
 17 void WriteLog(string str) { if (logger != NULL) { logger(str.c_str()); } }
 18 
 19 void WriteMessage(string fanctionName, string message) { WriteLog(fanctionName + "\t Message:" + message); }
 20 void WriteModelName(string fanctionName, string modelName) { WriteLog(fanctionName + "\t Model.Name:" + modelName); }
 21 void WriteRunTime(string fanctionName, int start) { WriteLog(fanctionName + "\t Run.Time:" + to_string(clock() - start) + " ms"); }
 22 void WriteError(string fanctionName, const std::exception& e) { WriteLog(fanctionName + "\t Error:" + e.what()); }
 23 
 24 // 注冊日志回呼函式
 25 View_Api void V_SetLogFunction(LogCallBack writeLog)
 26 {
 27     logger = writeLog;
 28     WriteMessage(__FUNCDNAME__, "Successed.");
 29 }
 30 
 31 // 設定人臉模型目錄
 32 View_Api void V_SetModelPath(const char* path)
 33 {
 34     modelPath = path;
 35     WriteMessage(__FUNCDNAME__, "Model.Path:" + modelPath);
 36 }
 37 // 獲取人臉模型目錄
 38 View_Api bool V_GetModelPath(char** path)
 39 {
 40     try
 41     {
 42 #pragma warning(disable:4996)
 43         strcpy(*path, modelPath.c_str());
 44 
 45         return true;
 46     }
 47     catch (const std::exception& e)
 48     {
 49         WriteError(__FUNCDNAME__, e);
 50         return false;
 51     }
 52 }
 53 
 54 seeta::FaceDetector* v_faceDetector = NULL;
 55 
 56 // 人臉檢測結果
 57 static SeetaFaceInfoArray detectorInfos;
 58 // 人臉數量檢測器
 59 View_Api int V_DetectorSize(unsigned char* imgData, int width, int height, int channels, double faceSize = 20, double threshold = 0.9, double maxWidth = 2000, double maxHeight = 2000, int type = 0)
 60 {
 61     try {
 62         clock_t start = clock();
 63 
 64         SeetaImageData img = { width, height, channels, imgData };
 65         if (v_faceDetector == NULL) {
 66             seeta::ModelSetting setting;
 67             setting.set_device(SEETA_DEVICE_CPU);
 68             string modelName = "face_detector.csta";
 69             switch (type)
 70             {
 71             case 1: modelName = "mask_detector.csta"; break;
 72             }
 73             setting.append(modelPath + modelName);
 74             WriteModelName(__FUNCDNAME__, modelName);
 75             v_faceDetector = new seeta::FaceDetector(setting);
 76         }
 77 
 78         if (faceSize != 20) { v_faceDetector->set(seeta::FaceDetector::Property::PROPERTY_MIN_FACE_SIZE, faceSize); }
 79         if (threshold != 0.9) { v_faceDetector->set(seeta::FaceDetector::Property::PROPERTY_THRESHOLD, threshold); }
 80         if (maxWidth != 2000) { v_faceDetector->set(seeta::FaceDetector::Property::PROPERTY_MAX_IMAGE_WIDTH, maxWidth); }
 81         if (maxHeight != 2000) { v_faceDetector->set(seeta::FaceDetector::Property::PROPERTY_MAX_IMAGE_HEIGHT, maxHeight); }
 82 
 83         auto infos = v_faceDetector->detect(img);
 84         detectorInfos = infos;
 85 
 86         WriteRunTime("V_Detector", start); // 此方法已經是人臉檢測的全程序,故計時器顯示為 人臉識別方法
 87         return infos.size;
 88     }
 89     catch (const std::exception& e)
 90     {
 91         WriteError(__FUNCDNAME__, e);
 92         return -1;
 93     }
 94 }
 95 // 人臉檢測器
 96 View_Api bool V_Detector(float* score, int* x, int* y, int* width, int* height)
 97 {
 98     try
 99     {
100         //clock_t start = clock();
101 
102         for (int i = 0; i < detectorInfos.size; i++, detectorInfos.data++)
103         {
104             *score = detectorInfos.data->score;
105             *x = detectorInfos.data->pos.x;
106             *y = detectorInfos.data->pos.y;
107             *width = detectorInfos.data->pos.width;
108             *height = detectorInfos.data->pos.height;
109             score++, x++, y++, width++, height++;
110         }
111         detectorInfos.data =https://www.cnblogs.com/view12138/p/ NULL;
112         detectorInfos.size = NULL;
113 
114         //WriteRunTime(__FUNCDNAME__, start); // 此方法只是將 人臉數量檢測器 獲取到的資料賦值傳遞,并不耗時,故不顯示此方法的呼叫時間
115         return true;
116     }
117     catch (const std::exception& e)
118     {
119         WriteError(__FUNCDNAME__, e);
120         return false;
121     }
122 }
123 
124 
125 seeta::FaceLandmarker* v_faceLandmarker = NULL;
126 // 人臉關鍵點數量
127 View_Api int V_FaceMarkSize(int type = 0)
128 {
129     try
130     {
131         clock_t start = clock();
132 
133         if (v_faceLandmarker == NULL) {
134             seeta::ModelSetting setting;
135             setting.set_device(SEETA_DEVICE_CPU);
136             string modelName = "face_landmarker_pts68.csta";
137             switch (type)
138             {
139             case 1: modelName = "face_landmarker_mask_pts5.csta"; break;
140             case 2: modelName = "face_landmarker_pts5.csta"; break;
141             }
142             setting.append(modelPath + modelName);
143             WriteModelName(__FUNCDNAME__, modelName);
144             v_faceLandmarker = new seeta::FaceLandmarker(setting);
145         }
146         int size = v_faceLandmarker->number();
147 
148         WriteRunTime(__FUNCDNAME__, start);
149         return size;
150     }
151     catch (const std::exception& e)
152     {
153         WriteError(__FUNCDNAME__, e);
154         return -1;
155     }
156 }
157 // 人臉關鍵點
158 View_Api bool V_FaceMark(unsigned char* imgData, int width, int height, int channels, int x, int y, int fWidth, int fHeight, double* pointX, double* pointY, int type = 0)
159 {
160     try
161     {
162         clock_t start = clock();
163 
164         SeetaImageData img = { width, height, channels, imgData };
165         SeetaRect face = { x, y, fWidth, fHeight };
166         if (v_faceLandmarker == NULL) {
167             seeta::ModelSetting setting;
168             setting.set_device(SEETA_DEVICE_CPU);
169             string modelName = "face_landmarker_pts68.csta";
170             switch (type)
171             {
172             case 1: modelName = "face_landmarker_mask_pts5.csta"; break;
173             case 2: modelName = "face_landmarker_pts5.csta"; break;
174             }
175             setting.append(modelPath + modelName);
176             WriteModelName(__FUNCDNAME__, modelName);
177             v_faceLandmarker = new seeta::FaceLandmarker(setting);
178         }
179         std::vector<SeetaPointF> _points = v_faceLandmarker->mark(img, face);
180 
181         if (!_points.empty()) {
182             for (auto iter = _points.begin(); iter != _points.end(); iter++)
183             {
184                 *pointX = (*iter).x;
185                 *pointY = (*iter).y;
186                 pointX++;
187                 pointY++;
188             }
189 
190             WriteRunTime(__FUNCDNAME__, start);
191             return true;
192         }
193         else { return false; }
194     }
195     catch (const std::exception& e)
196     {
197         WriteError(__FUNCDNAME__, e);
198         return false;
199     }
200 }
201 
202 seeta::FaceRecognizer* v_faceRecognizer = NULL;
203 // 獲取人臉特征值長度
204 View_Api int V_ExtractSize(int type = 0)
205 {
206     try
207     {
208         clock_t start = clock();
209 
210         if (v_faceRecognizer == NULL) {
211             seeta::ModelSetting setting;
212             setting.set_id(0);
213             setting.set_device(SEETA_DEVICE_CPU);
214             string modelName = "face_recognizer.csta";
215             switch (type)
216             {
217             case 1: modelName = "face_recognizer_mask.csta"; break;
218             case 2: modelName = "face_recognizer_light.csta"; break;
219             }
220             setting.append(modelPath + modelName);
221             WriteModelName(__FUNCDNAME__, modelName);
222             v_faceRecognizer = new seeta::FaceRecognizer(setting);
223         }
224         int length = v_faceRecognizer->GetExtractFeatureSize();
225 
226         WriteRunTime(__FUNCDNAME__, start);
227         return length;
228     }
229     catch (const std::exception& e)
230     {
231         WriteError(__FUNCDNAME__, e);
232         return -1;
233     }
234 }
235 // 提取人臉特征值
236 View_Api bool V_Extract(unsigned char* imgData, int width, int height, int channels, SeetaPointF* points, float* features, int type = 0)
237 {
238     try
239     {
240         clock_t start = clock();
241 
242         SeetaImageData img = { width, height, channels, imgData };
243         if (v_faceRecognizer == NULL) {
244             seeta::ModelSetting setting;
245             setting.set_id(0);
246             setting.set_device(SEETA_DEVICE_CPU);
247             string modelName = "face_recognizer.csta";
248             switch (type)
249             {
250             case 1: modelName = "face_recognizer_mask.csta"; break;
251             case 2: modelName = "face_recognizer_light.csta"; break;
252             }
253             setting.append(modelPath + modelName);
254             WriteModelName(__FUNCDNAME__, modelName);
255             v_faceRecognizer = new seeta::FaceRecognizer(setting);
256         }
257         int length = v_faceRecognizer->GetExtractFeatureSize();
258         std::shared_ptr<float> _features(new float[v_faceRecognizer->GetExtractFeatureSize()], std::default_delete<float[]>());
259         v_faceRecognizer->Extract(img, points, _features.get());
260 
261         for (int i = 0; i < length; i++)
262         {
263             *features = _features.get()[i];
264             features++;
265         }
266 
267         WriteRunTime(__FUNCDNAME__, start);
268         return true;
269 
270     }
271     catch (const std::exception& e)
272     {
273         WriteError(__FUNCDNAME__, e);
274         return false;
275     }
276 }
277 // 人臉特征值相似度計算
278 View_Api float V_CalculateSimilarity(float* leftFeatures, float* rightFeatures, int type = 0)
279 {
280     try
281     {
282         clock_t start = clock();
283 
284         if (v_faceRecognizer == NULL) {
285             seeta::ModelSetting setting;
286             setting.set_id(0);
287             setting.set_device(SEETA_DEVICE_CPU);
288             string modelName = "face_recognizer.csta";
289             switch (type)
290             {
291             case 1: modelName = "face_recognizer_mask.csta"; break;
292             case 2: modelName = "face_recognizer_light.csta"; break;
293             }
294             setting.append(modelPath + modelName);
295             WriteModelName(__FUNCDNAME__, modelName);
296             v_faceRecognizer = new seeta::FaceRecognizer(setting);
297         }
298 
299         auto similarity = v_faceRecognizer->CalculateSimilarity(leftFeatures, rightFeatures);
300         WriteMessage(__FUNCDNAME__, "Similarity = " + to_string(similarity));
301         WriteRunTime(__FUNCDNAME__, start);
302         return similarity;
303     }
304     catch (const std::exception& e)
305     {
306         WriteError(__FUNCDNAME__, e);
307         return -1;
308     }
309 }
310 
311 // 釋放資源
312 View_Api void V_Dispose()
313 {
314     if (v_faceDetector != NULL) delete v_faceDetector;
315     if (v_faceLandmarker != NULL) delete v_faceLandmarker;
316     if (v_faceRecognizer != NULL) delete v_faceRecognizer;
317 }
C++ 封裝層

2.采用 C# 對上訴介面進行了匯入,

因為C++的專案測CPU架構區分x86和x64,所以C# 層也需要區分架構封裝

using System.Runtime.InteropServices;
using System.Text;
using ViewFaceCore.Sharp.Model;

namespace ViewFaceCore.Plus
{
    /// <summary>
    /// 日志回呼函式
    /// </summary>
    /// <param name="logText"></param>
    public delegate void LogCallBack(string logText);

    class ViewFacePlus64
    {
        const string LibraryPath = @"FaceLibraries\x64\ViewFace.dll";
        /// <summary>
        /// 設定日志回呼函式(用于日志列印)
        /// </summary>
        /// <param name="writeLog"></param>
        [DllImport(LibraryPath, EntryPoint = "V_SetLogFunction", CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetLogFunction(LogCallBack writeLog);

        /// <summary>
        /// 設定人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        [DllImport(LibraryPath, EntryPoint = "V_SetModelPath", CallingConvention = CallingConvention.Cdecl)]
        private extern static void SetModelPath(byte[] path);
        /// <summary>
        /// 設定人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        public static void SetModelPath(string path) => SetModelPath(Encoding.UTF8.GetBytes(path));

        /// <summary>
        /// 釋放使用的資源
        /// </summary>
        [DllImport(LibraryPath, EntryPoint = "V_Dispose", CallingConvention = CallingConvention.Cdecl)]
        public extern static void ViewDispose();

        /// <summary>
        /// 獲取人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        [DllImport(LibraryPath, EntryPoint = "V_GetModelPath", CallingConvention = CallingConvention.Cdecl)]
        private extern static bool GetModelPathEx(ref string path);
        /// <summary>
        /// 獲取人臉模型的目錄
        /// </summary>
        public static string GetModelPath() { string path = string.Empty; GetModelPathEx(ref path); return path; }

        /// <summary>
        /// 人臉檢測器檢測到的人臉數量
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="faceSize">最小人臉是人臉檢測器常用的一個概念,默認值為20,單位像素,
        /// <para>最小人臉和檢測器性能息息相關,主要方面是速度,使用建議上,我們建議在應用范圍內,這個值設定的越大越好,SeetaFace采用的是BindingBox Regresion的方式訓練的檢測器,如果最小人臉引數設定為80的話,從檢測能力上,可以將原圖縮小的原來的1/4,這樣從計算復雜度上,能夠比最小人臉設定為20時,提速到16倍,</para>
        /// </param>
        /// <param name="threshold">檢測器閾值默認值是0.9,合理范圍為[0, 1],這個值一般不進行調整,除了用來處理一些極端情況,這個值設定的越小,漏檢的概率越小,同時誤檢的概率會提高</param>
        /// <param name="maxWidth">可檢測的影像最大寬度,默認值2000,</param>
        /// <param name="maxHeight">可檢測的影像最大高度,默認值2000,</param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_DetectorSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int DetectorSize(byte[] imgData, int width, int height, int channels, double faceSize = 20, double threshold = 0.9, double maxWidth = 2000, double maxHeight = 2000, int type = 0);
        /// <summary>
        /// 人臉檢測器
        /// <para>呼叫此方法前必須先呼叫 <see cref="DetectorSize(byte[], int, int, int, double, double, double, double, int)"/></para>
        /// </summary>
        /// <param name="score">人臉置信度集合</param>
        /// <param name="x">人臉位置集合</param>
        /// <param name="y">人臉位置集合</param>
        /// <param name="width">人臉大小集合</param>
        /// <param name="height">人臉大小集合</param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_Detector", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool Detector(float[] score, int[] x, int[] y, int[] width, int[] height);

        /// <summary>
        /// 人臉關鍵點數量
        /// </summary>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_FaceMarkSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int FaceMarkSize(int type = 0);
        /// <summary>
        /// 人臉關鍵點
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="fWidth"></param>
        /// <param name="fHeight"></param>
        /// <param name="pointX"></param>
        /// <param name="pointY"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_FaceMark", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool FaceMark(byte[] imgData, int width, int height, int channels, int x, int y, int fWidth, int fHeight, double[] pointX, double[] pointY, int type = 0);

        /// <summary>
        /// 提取特征值
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="points"></param>
        /// <param name="features"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_Extract", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool Extract(byte[] imgData, int width, int height, int channels, FaceMarkPoint[] points, float[] features, int type = 0);
        /// <summary>
        /// 特征值大小
        /// </summary>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_ExtractSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int ExtractSize(int type = 0);

        /// <summary>
        /// 計算相似度
        /// </summary>
        /// <param name="leftFeatures"></param>
        /// <param name="rightFeatures"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_CalculateSimilarity", CallingConvention = CallingConvention.Cdecl)]
        public extern static float Similarity(float[] leftFeatures, float[] rightFeatures, int type = 0);
    }
    class ViewFacePlus32
    {
        const string LibraryPath = @"FaceLibraries\x86\ViewFace.dll";
        /// <summary>
        /// 設定日志回呼函式(用于日志列印)
        /// </summary>
        /// <param name="writeLog"></param>
        [DllImport(LibraryPath, EntryPoint = "V_SetLogFunction", CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetLogFunction(LogCallBack writeLog);

        /// <summary>
        /// 設定人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        [DllImport(LibraryPath, EntryPoint = "V_SetModelPath", CallingConvention = CallingConvention.Cdecl)]
        private extern static void SetModelPath(byte[] path);
        /// <summary>
        /// 設定人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        public static void SetModelPath(string path) => SetModelPath(Encoding.UTF8.GetBytes(path));

        /// <summary>
        /// 釋放使用的資源
        /// </summary>
        [DllImport(LibraryPath, EntryPoint = "V_Dispose", CallingConvention = CallingConvention.Cdecl)]
        public extern static void ViewDispose();

        /// <summary>
        /// 獲取人臉模型的目錄
        /// </summary>
        /// <param name="path"></param>
        [DllImport(LibraryPath, EntryPoint = "V_GetModelPath", CallingConvention = CallingConvention.Cdecl)]
        private extern static bool GetModelPathEx(ref string path);
        /// <summary>
        /// 獲取人臉模型的目錄
        /// </summary>
        public static string GetModelPath() { string path = string.Empty; GetModelPathEx(ref path); return path; }

        /// <summary>
        /// 人臉檢測器檢測到的人臉數量
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="faceSize">最小人臉是人臉檢測器常用的一個概念,默認值為20,單位像素,
        /// <para>最小人臉和檢測器性能息息相關,主要方面是速度,使用建議上,我們建議在應用范圍內,這個值設定的越大越好,SeetaFace采用的是BindingBox Regresion的方式訓練的檢測器,如果最小人臉引數設定為80的話,從檢測能力上,可以將原圖縮小的原來的1/4,這樣從計算復雜度上,能夠比最小人臉設定為20時,提速到16倍,</para>
        /// </param>
        /// <param name="threshold">檢測器閾值默認值是0.9,合理范圍為[0, 1],這個值一般不進行調整,除了用來處理一些極端情況,這個值設定的越小,漏檢的概率越小,同時誤檢的概率會提高</param>
        /// <param name="maxWidth">可檢測的影像最大寬度,默認值2000,</param>
        /// <param name="maxHeight">可檢測的影像最大高度,默認值2000,</param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_DetectorSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int DetectorSize(byte[] imgData, int width, int height, int channels, double faceSize = 20, double threshold = 0.9, double maxWidth = 2000, double maxHeight = 2000, int type = 0);
        /// <summary>
        /// 人臉檢測器
        /// <para>呼叫此方法前必須先呼叫 <see cref="DetectorSize(byte[], int, int, int, double, double, double, double, int)"/></para>
        /// </summary>
        /// <param name="score">人臉置信度集合</param>
        /// <param name="x">人臉位置集合</param>
        /// <param name="y">人臉位置集合</param>
        /// <param name="width">人臉大小集合</param>
        /// <param name="height">人臉大小集合</param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_Detector", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool Detector(float[] score, int[] x, int[] y, int[] width, int[] height);

        /// <summary>
        /// 人臉關鍵點數量
        /// </summary>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_FaceMarkSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int FaceMarkSize(int type = 0);
        /// <summary>
        /// 人臉關鍵點
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="fWidth"></param>
        /// <param name="fHeight"></param>
        /// <param name="pointX"></param>
        /// <param name="pointY"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_FaceMark", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool FaceMark(byte[] imgData, int width, int height, int channels, int x, int y, int fWidth, int fHeight, double[] pointX, double[] pointY, int type = 0);

        /// <summary>
        /// 提取特征值
        /// </summary>
        /// <param name="imgData"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="channels"></param>
        /// <param name="points"></param>
        /// <param name="features"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_Extract", CallingConvention = CallingConvention.Cdecl)]
        public extern static bool Extract(byte[] imgData, int width, int height, int channels, FaceMarkPoint[] points, float[] features, int type = 0);
        /// <summary>
        /// 特征值大小
        /// </summary>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_ExtractSize", CallingConvention = CallingConvention.Cdecl)]
        public extern static int ExtractSize(int type = 0);

        /// <summary>
        /// 計算相似度
        /// </summary>
        /// <param name="leftFeatures"></param>
        /// <param name="rightFeatures"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [DllImport(LibraryPath, EntryPoint = "V_CalculateSimilarity", CallingConvention = CallingConvention.Cdecl)]
        public extern static float Similarity(float[] leftFeatures, float[] rightFeatures, int type = 0);
    }
}
C# 匯入層

3.采用 C# 的面向物件的封裝

因為C#的專案默認都是 AnyCPU,所以為了簡化呼叫,在這一層封裝的時候增加了架構判斷,當在你的專案中參考的時候,不用做任何修改,

且因為C++的C#匯入方法在和原生的C#寫法略有差異,且資料的轉換和傳遞比較麻煩,所以類別庫中對外隱藏了 C# 匯入層,并使用大家都更熟悉的C#的面向物件的方式進行進一步的封裝和簡化,

  1     /// <summary>
  2     /// 人臉識別類
  3     /// </summary>
  4     public class ViewFace
  5     {
  6         bool Platform64 { get; set; } = false;
  7         // <para>需要模型:<see langword=""/></para>
  8 
  9         // ctor
 10         /// <summary>
 11         /// 使用默認的模型目錄初始化人臉識別類
 12         /// </summary>
 13         public ViewFace() : this("./model/") { }
 14         /// <summary>
 15         /// 使用指定的模型目錄初始化人臉識別類
 16         /// </summary>
 17         /// <param name="modelPath">模型目錄</param>
 18         public ViewFace(string modelPath)
 19         {
 20             Platform64 = IntPtr.Size == 8;
 21             if (Platform64)
 22             { ViewFacePlus64.SetModelPath(modelPath); }
 23             else
 24             { ViewFacePlus32.SetModelPath(modelPath); }
 25         }
 26         /// <summary>
 27         /// 使用指定的日志回呼函式初始化人臉識別類
 28         /// </summary>
 29         /// <param name="action">日志回呼函式</param>
 30         public ViewFace(LogCallBack action) : this("./model/", action) { }
 31         /// <summary>
 32         /// 使用指定的模型目錄、日志回呼函式初始化人臉識別類
 33         /// </summary>
 34         /// <param name="modelPath">模型目錄</param>
 35         /// <param name="action">日志回呼函式</param>
 36         public ViewFace(string modelPath, LogCallBack action) : this(modelPath)
 37         {
 38             if (Platform64)
 39             { ViewFacePlus64.SetLogFunction(action); }
 40             else
 41             { ViewFacePlus32.SetLogFunction(action); }
 42         }
 43 
 44         // public property
 45         /// <summary>
 46         /// 獲取或設定模型路徑
 47         /// </summary>
 48         public string ModelPath
 49         {
 50             get
 51             {
 52                 if (Platform64)
 53                 { return ViewFacePlus64.GetModelPath(); }
 54                 else
 55                 { return ViewFacePlus32.GetModelPath(); }
 56             }
 57             set
 58             {
 59                 if (Platform64)
 60                 { ViewFacePlus64.SetModelPath(value); }
 61                 else
 62                 { ViewFacePlus32.SetModelPath(value); }
 63             }
 64         }
 65         /// <summary>
 66         /// 獲取或設定人臉型別
 67         /// <para>
 68         /// <listheader>此屬性可影響到以下方法:</listheader><br />
 69         ///<c><see cref="FaceDetector(Bitmap)"/></c><br />
 70         ///<c><see cref="Extract(Bitmap, FaceMarkPoint[])"/></c><br />
 71         ///<c><see cref="Similarity(float[], float[])"/></c><br />
 72         /// </para>
 73         /// </summary>
 74         public FaceType FaceType { get; set; } = FaceType.Light;
 75         /// <summary>
 76         /// 獲取或設定人臉關鍵點型別
 77         /// <para>
 78         /// <listheader>此屬性可影響到以下方法:</listheader><br />
 79         ///<c><see cref="FaceMark(Bitmap, FaceInfo)"/></c><br />
 80         /// </para>
 81         /// </summary>
 82         public MarkType MarkType { get; set; } = MarkType.Light;
 83         /// <summary>
 84         /// 獲取或設定人臉檢測器設定
 85         /// </summary>
 86         public DetectorSetting DetectorSetting { get; set; } = new DetectorSetting();
 87 
 88 
 89         // public method
 90         /// <summary>
 91         /// 識別 <paramref name="bitmap"/> 中的人臉,并回傳人臉的資訊,
 92         /// <para>
 93         ///<c><see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Normal"/> <see langword="||"/> <see cref="FaceType.Light"/></c> 時, 需要模型:<see langword="face_detector.csta"/><br/>
 94         ///<c><see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Mask"/></c> 時, 需要模型:<see langword="mask_detector.csta"/><br/>
 95         /// </para>
 96         /// </summary>
 97         /// <param name="bitmap">包含人臉的圖片</param>
 98         /// <returns></returns>
 99         public FaceInfo[] FaceDetector(Bitmap bitmap)
100         {
101             byte[] bgr = ImageSet.Get24BGRFromBitmap(bitmap, out int width, out int height, out int channels);
102             int size;
103             if (Platform64)
104             { size = ViewFacePlus64.DetectorSize(bgr, width, height, channels, DetectorSetting.FaceSize, DetectorSetting.Threshold, DetectorSetting.MaxWidth, DetectorSetting.MaxHeight, (int)FaceType); }
105             else
106             { size = ViewFacePlus32.DetectorSize(bgr, width, height, channels, DetectorSetting.FaceSize, DetectorSetting.Threshold, DetectorSetting.MaxWidth, DetectorSetting.MaxHeight, (int)FaceType); }
107             float[] _socre = new float[size];
108             int[] _x = new int[size];
109             int[] _y = new int[size];
110             int[] _width = new int[size];
111             int[] _height = new int[size];
112             if (Platform64)
113             { _ = ViewFacePlus64.Detector(_socre, _x, _y, _width, _height); }
114             else
115             { _ = ViewFacePlus32.Detector(_socre, _x, _y, _width, _height); }
116             List<FaceInfo> infos = new List<FaceInfo>();
117             for (int i = 0; i < size; i++)
118             {
119                 infos.Add(new FaceInfo() { Score = _socre[i], Location = new FaceRect() { X = _x[i], Y = _y[i], Width = _width[i], Height = _height[i] } });
120             }
121             return infos.ToArray();
122         }
123 
124         /// <summary>
125         /// 識別 <paramref name="bitmap"/> 中指定的人臉資訊 <paramref name="info"/> 的關鍵點坐標,
126         /// <para>
127         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Normal"/> 時, 需要模型:<see langword="face_landmarker_pts68.csta"/><br/>
128         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Mask"/> 時, 需要模型:<see langword="face_landmarker_mask_pts5.csta"/><br/>
129         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Light"/> 時, 需要模型:<see langword="face_landmarker_pts5.csta"/><br/>
130         /// </para>
131         /// </summary>
132         /// <param name="bitmap">包含人臉的圖片</param>
133         /// <param name="info">指定的人臉資訊</param>
134         /// <returns></returns>
135         public FaceMarkPoint[] FaceMark(Bitmap bitmap, FaceInfo info)
136         {
137             byte[] bgr = ImageSet.Get24BGRFromBitmap(bitmap, out int width, out int height, out int channels);
138             int size;
139             if (Platform64)
140             { size = ViewFacePlus64.FaceMarkSize((int)MarkType); }
141             else
142             { size = ViewFacePlus32.FaceMarkSize((int)MarkType); }
143             double[] _pointX = new double[size];
144             double[] _pointY = new double[size];
145             bool val;
146             if (Platform64)
147             { val = ViewFacePlus64.FaceMark(bgr, width, height, channels, info.Location.X, info.Location.Y, info.Location.Width, info.Location.Height, _pointX, _pointY, (int)MarkType); }
148             else
149             { val = ViewFacePlus32.FaceMark(bgr, width, height, channels, info.Location.X, info.Location.Y, info.Location.Width, info.Location.Height, _pointX, _pointY, (int)MarkType); }
150             if (val)
151             {
152                 List<FaceMarkPoint> points = new List<FaceMarkPoint>();
153                 for (int i = 0; i < size; i++)
154                 { points.Add(new FaceMarkPoint() { X = _pointX[i], Y = _pointY[i] }); }
155                 return points.ToArray();
156             }
157             else
158             { throw new Exception("人臉關鍵點獲取失敗"); }
159         }
160 
161         /// <summary>
162         /// 提取人臉特征值,
163         /// <para>
164         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Normal"/> 時, 需要模型:<see langword="face_recognizer.csta"/><br/>
165         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Mask"/> 時, 需要模型:<see langword="face_recognizer_mask.csta"/><br/>
166         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Light"/> 時, 需要模型:<see langword="face_recognizer_light.csta"/><br/>
167         /// </para>
168         /// </summary>
169         /// <param name="bitmap"></param>
170         /// <param name="points"></param>
171         /// <returns></returns>
172         public float[] Extract(Bitmap bitmap, FaceMarkPoint[] points)
173         {
174             byte[] bgr = ImageSet.Get24BGRFromBitmap(bitmap, out int width, out int height, out int channels);
175             float[] features;
176             if (Platform64)
177             { features = new float[ViewFacePlus64.ExtractSize((int)FaceType)]; }
178             else
179             { features = new float[ViewFacePlus32.ExtractSize((int)FaceType)]; }
180 
181             if (Platform64)
182             { ViewFacePlus64.Extract(bgr, width, height, channels, points, features, (int)FaceType); }
183             else
184             { ViewFacePlus32.Extract(bgr, width, height, channels, points, features, (int)FaceType); }
185             return features;
186         }
187 
188         /// <summary>
189         /// 計算特征值相似度,
190         /// <para>只能計算相同 <see cref="FaceType"/> 計算出的特征值</para>
191         /// <para>
192         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Normal"/> 時, 需要模型:<see langword="face_recognizer.csta"/><br/>
193         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Mask"/> 時, 需要模型:<see langword="face_recognizer_mask.csta"/><br/>
194         ///<see cref="FaceType"/> <see langword="="/> <see cref="FaceType.Light"/> 時, 需要模型:<see langword="face_recognizer_light.csta"/><br/>
195         /// </para>
196         /// </summary>
197         /// <exception cref="ArgumentException"/>
198         /// <exception cref="ArgumentNullException"/>
199         /// <param name="leftFeatures"></param>
200         /// <param name="rightFeatures"></param>
201         /// <returns></returns>
202         public float Similarity(float[] leftFeatures, float[] rightFeatures)
203         {
204             if (leftFeatures.Length == 0 || rightFeatures.Length == 0)
205                 throw new ArgumentNullException("引數不能為空", nameof(leftFeatures));
206             if (leftFeatures.Length != rightFeatures.Length)
207                 throw new ArgumentException("兩個引數長度不一致");
208 
209 
210             if (Platform64)
211             { return ViewFacePlus64.Similarity(leftFeatures, rightFeatures, (int)FaceType); }
212             else
213             { return ViewFacePlus32.Similarity(leftFeatures, rightFeatures, (int)FaceType); }
214         }
215 
216         /// <summary>
217         /// 判斷相似度是否為同一個人,
218         /// </summary>
219         /// <param name="similarity">相似度</param>
220         /// <returns></returns>
221         public bool IsSelf(float similarity) => similarity > Face.Threshold[FaceType];
222 
223         /// <summary>
224         /// 釋放資源
225         /// </summary>
226         ~ViewFace()
227         {
228             if (Platform64)
229             { ViewFacePlus64.ViewDispose(); }
230             else
231             { ViewFacePlus32.ViewDispose(); }
232         }
233     }
C# 面向物件層

 

五、也許…

  • 此專案還未實作 SeetaFace6 中的許多特性,也許:

    想起 GitHub 密碼,持續更新…
    洗掉代碼倉庫跑路…

  • 如果在使用程序中遇到問題,你也許可以:

    在 GitHub 報告Bug…
    向我 發送郵件

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/6429.html

標籤:.NET Core

上一篇:FreeSql.Generator命令列代碼生成器是如何實作的

下一篇:MongoDB via Dotnet Core資料映射詳解

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more