.NET 人臉識別庫 ViewFaceCore 這是基於 SeetaFace6 人臉識別開發的 .NET 平臺下的人臉識別庫這是一個使用超簡單的人臉識別庫這是一個基於 .NET Standard 2.0 開發的庫這個庫已經發佈到 NuGet ,你可以一鍵集成到你的項目此項目可以免費商業使用 ⭐、開源 ...
.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 = 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