環境資訊中心綜合外電;姜唯 編譯;林大利 審校
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
摘錄自2019年10月7日聯合報報導
「反抗滅絕」在全球發起公民不服從運動7日展開,在澳洲和紐西蘭各地有數十名示威者遭到逮捕。這場運動將為期2週,目的為了要求各國政府採取緊急行動對抗氣候變遷。
數百人在繁忙的雪梨市中心道路上靜坐封路,最後警察拖走示威者終止這場活動,隨後有30人遭到起訴。
在布里斯本,一小群運動人士把自己鎖在一座橋上。警方表示,他們在現場逮捕7人,並全數加以起訴。
同一時間在紐西蘭首都威靈頓,示威者用鎖鏈把自己與一輛閃亮的粉紅色車輛綁住,造成市區部分交通癱瘓。警方表示,今天稍晚有30人遭到逮捕,但無人被起訴。
荷蘭阿姆斯特丹市7日出現數百名氣候變遷倡議人士,他們無視警方的禁令,高喊「反抗」,阻礙市中心交通。警方已警告在先,將逮捕違令人士,防止他們擾亂通勤民眾。
荷蘭警方已核准倡議團體「反抗滅絕」(Extinction Rebellion, XR)的抗議計劃,前提是不能阻礙交通。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!
摘錄自2019年10月8日中央社報導
抗議厄瓜多總統莫雷諾(Lenin Moreno)政府取消燃料補助導致油價飆漲的示威活動越演越烈,警方和示威者8日在國民議會爆發衝突。
根據Ecuavisa頻道報導,示威者成功突破國會大廈封鎖線,當中有許多是手持棍棒和鞭子的原住民男子。示威者衝進會議室霸占講台,但幾分鐘後就遭到安全部隊驅離。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※南投搬家公司費用需注意的眉眉角角,別等搬了再說!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※教你寫出一流的銷售文案?
摘錄自2019年10月8日中央社報導
自今年9月以來,大片黑色和黏稠的原油油污持續被海水拖動到巴西東北海岸沙灘,目前已在阿拉戈斯(Alagoas)、巴伊亞(Bahia)、塞阿拉(Ceara)、馬蘭豪(Maranhao)、巴萊巴(Paraiba)、培南布可(Pernambuco)、彼奧(Piaui)、北大河(Rio Grande do Norte)和謝吉貝(Sergipe)9州共61個城市超過132個地點發現油污紀錄,至少8隻海洋動物遭油污覆蓋全身窒息死亡。
巴西環保署(Ibama)稱,已經與受影響各州的消防隊合作,採取一系列措施紓緩油污問題,同時與海軍和巴西石油公司(Petrobras)的技術團隊一起試圖查明海上原油油污的來源。
謝吉貝州政府已宣布進入緊急狀態,並要求居民遠離沙灘,即使民眾有意幫忙淨灘活動。生物」。
沒有人知道這些油污來自何處,但專家擔心將對當地生物造成毀滅性的影響。巴西海龜保護計劃(Tamar)也因這個問題而暫停把新孵化出的小海龜放生到大海中。生物學家擔心污染會擴散得更嚴重,危及海龜、海豚和座頭鯨等海洋動物的繁殖。
阿拉戈斯州生物保護研究所長奧里維拉(Bruno Stefanis Santos Pereira de Oliveira)說,當務之急是防止油污擴散和造成更多損害,防止動物和人類接觸油污,避免其他人誤食體積較小的魚貝類而受到污染。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※想知道最厲害的網頁設計公司"嚨底家"!
※幫你省時又省力,新北清潔一流服務好口碑
※別再煩惱如何寫文案,掌握八大原則!
摘錄自2019年10月8日聯合報報導
英荷消費品生產商聯合利華(Unilever)今天表示,2025年前將減半旗下產品使用的新塑膠量,從目前1年使用70萬公噸塑膠,減半到低於35萬公噸。
聯合利華將減少35萬公噸「原生塑膠」用量,其中10萬公噸來自直接減少塑膠包裝,例如製造可重複利用的包裝或可補充包裝、採用替代包裝,或是完全不會用到塑膠的「裸賣」方式。
另25公噸減量目標則會從使用回收塑膠下手。
本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※新北清潔公司,居家、辦公、裝潢細清專業服務
※別再煩惱如何寫文案,掌握八大原則!
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※超省錢租車方案
※教你寫出一流的銷售文案?
斑點分析的算法非常簡單:在圖像中,相關對象的像素(也稱為前景)通過其灰度值來識別。例如,圖中示例显示了液體中的組織顆粒。這些粒子是明亮的,液體(背景)是暗的。通過選擇明亮的像素(閾值),可以很容易檢測到顆粒。在許多應用中,暗像素和亮像素的簡單條件不再成立,但結果相同可以通過額外的預處理或像素選擇/分組的替代方法來實現。
在這種情況下,斑點分析的優點是HALCON提供了大量算子使其具有極大的靈活性。此外,這些方法通常具有很高的性能。斑點分析也可以與許多其他視覺任務相結合,例如作為預處理步驟,靈活地生成交互區域。
斑點分析主要包括三個部分:
獲取圖像
分割圖像
採集圖像后,接下來的任務是選擇前景像素。這也稱為分割。結果
在HALCON中通常將此過程為Blob(二進制大對象),數據類型為區域(a region)。
提取目標特徵
在最後一步中,將計算出諸如面積(像素數),重心或方向之類的特徵
該基本概念的一個示例是以下程序,該程序屬於上述示例。在此,從文件中獲取圖像。使用閾值(threshold)選擇大於120的所有像素。然後,引入了一個不太明顯的步驟:算子連接(connection)將所有亮像素的集合分離為所謂的連接組件。此步驟的效果是我們將劃分出多個區域,而不是閾值(threshold)返回的單個區域。該程序的最後一步是一些功能的計算。在此,算子area_center確定了大小(像素數)和重心。請注意area_center返回了三個值(每個參數有一個值)。
read_image (Image, 'particle')
threshold (Image, BrightPixels, 120, 255)
connection (BrightPixels, Particles)
area_center (Particles, Area, Row, Column)
在許多情況下,斑點分析將比上述示例更高級。原因是混亂或不均勻的照明。此外,經常需要進行后處理,例如將元素特徵轉換為真實世界單位或結果可視化。
可以通過使用感興趣區域來加快斑點分析。搜索的斑點區域被限制越多。搜索將更快更強大。
在某些應用中,關注區域必須相對於另一個對象對齊。或者圖像本身可以對齊,例如通過旋轉或裁剪。
與對齊類似,可能需要校正圖像,例如消除鏡頭畸變或轉換圖像的參考點。
下一個重要部分是圖像的預處理。在這裏,像mean_image或gauss_filter這樣的運算符可用於消除噪音。一個快速但不太完美的替代方案是binomial_filter。運算符middle_image對於抑制小斑點或細線很有用。算子anisotropic_diffusion(各向異性擴散)對保留邊緣的平滑很有用,最後使用fill_interlace消除由隔行交錯相機(攝像機視頻流圖像)引起的缺陷
代替使用固定的閾值,可以為每個圖像動態提取它們。例如具有多個峰值的灰度值直方圖,每個對象類別一個。在這裏,您可以使用算子gray_histo_abs和histo_to_thresh。作為高級替代方案,可以將算子intensity與參考圖像結合使用,僅適用於背景:在設置過程中,將確定背景區域的平均灰度值。如果平均灰度值已更改,則可以相應調整閾值。
對於分割,可以使用各種方法。最簡單的方法是threshold(閾值),指定一個屬於前景對象的值範圍。另一個非常常見的方法是dyn_threshold。在此,第二張圖像將作為參考圖像。通過這種方法,使用局部閾值而不是全局閾值。這些局部閾值存儲在參考圖像中。可以通過拍攝空背景圖片將其設為靜態作為參考圖像,也可以使用平滑濾鏡(例如mean_image)
一旦斑點區域被分割。通常需要對其進行修改,例如,通過抑制小區域,給定方向或接近其他區域的區域。在這種情況下,形態算子open_circle和opening_rectangle通常可用於抑制噪聲,closeing_circle和closing-rectanglel填補空白。可以使用select_shape,select_shape_std和select-proto-proto選擇具有特定功能的斑點。
最終處理時,將提取斑點的特徵,所需功能的類型取決於應用程序。類型列表可以在參考手冊的“Regions/Features”和”Image/Features”中找到。
諸如面積或重心之類的要素通常必須轉換為世界坐標。這可以通過HALCON相機
校準實現。
最後,你可能要显示圖像的斑點(區域)和特徵。
靈感來源於Halcon官方文檔
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※推薦評價好的iphone維修中心
.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
此 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++ 形式的封裝。
目前主要實現了 人臉檢測,關鍵點提取,特徵值提取,特徵值對比幾個人臉識別中的基礎接口。有了這幾個接口,可以完整的實現一套人臉識別和驗證的流程。
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; 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# 面向對象層
五、也許…
想起 GitHub 密碼,持續更新…
刪除代碼倉庫跑路…
在 GitHub 報告Bug…
向我 發送郵件
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案
用好數據映射,MongoDB via Dotnet Core開發變會成一件超級快樂的事。
MongoDB這幾年已經成為NoSQL的頭部數據庫。
由於MongoDB free schema的特性,使得它在互聯網應用方面優於常規數據庫,成為了相當一部分大廠的主數據選擇;而它的快速布署和開發簡單的特點,也吸引着大量小開發團隊的支持。
關於MongoDB快速布署,我在15分鐘從零開始搭建支持10w+用戶的生產環境(二)里有寫,需要了可以去看看。
作為一個數據庫,基本的操作就是CRUD。MongoDB的CRUD,不使用SQL來寫,而是提供了更簡單的方式。
方式一、BsonDocument方式
BsonDocument方式,適合能熟練使用MongoDB Shell的開發者。MongoDB Driver提供了完全覆蓋Shell命令的各種方式,來處理用戶的CRUD操作。
這種方法自由度很高,可以在不需要知道完整數據集結構的情況下,完成數據庫的CRUD操作。
方式二、數據映射方式
數據映射是最常用的一種方式。準備好需要處理的數據類,直接把數據類映射到MongoDB,並對數據集進行CRUD操作。
下面,對數據映射的各個部分,我會逐個說明。
為了防止不提供原網址的轉載,特在這裏加上原文鏈接:https://www.cnblogs.com/tiger-wang/p/13185605.html
這個Demo的開發環境是:Mac + VS Code + Dotnet Core 3.1.2。
建立工程:
% dotnet new sln -o demo
The template "Solution File" was created successfully.
% cd demo
% dotnet new console -o demo
The template "Console Application" was created successfully.
Processing post-creation actions...
Running 'dotnet restore' on demo/demo.csproj...
Determining projects to restore...
Restored demo/demo/demo.csproj (in 162 ms).
Restore succeeded.
% dotnet sln add demo/demo.csproj
Project `demo/demo.csproj` added to the solution.
建立工程完成。
下面,增加包mongodb.driver到工程:
% cd demo
% dotnet add package mongodb.driver
Determining projects to restore...
info : Adding PackageReference for package 'mongodb.driver' into project 'demo/demo/demo.csproj'.
info : Committing restore...
info : Writing assets file to disk. Path: demo/demo/obj/project.assets.json
log : Restored /demo/demo/demo.csproj (in 6.01 sec).
項目準備完成。
看一下目錄結構:
% tree .
.
├── demo
│ ├── Program.cs
│ ├── demo.csproj
│ └── obj
│ ├── demo.csproj.nuget.dgspec.json
│ ├── demo.csproj.nuget.g.props
│ ├── demo.csproj.nuget.g.targets
│ ├── project.assets.json
│ └── project.nuget.cache
└── demo.sln
mongodb.driver是MongoDB官方的數據庫SDK,從Nuget上安裝即可。
創建數據映射的模型類CollectionModel.cs,現在是個空類,後面所有的數據映射相關內容會在這個類進行說明:
public class CollectionModel
{
}
並修改Program.cs,準備Demo方法,以及連接數據庫:
class Program
{
private const string MongoDBConnection = "mongodb://localhost:27031/admin";
private static IMongoClient _client = new MongoClient(MongoDBConnection);
private static IMongoDatabase _database = _client.GetDatabase("Test");
private static IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");
static async Task Main(string[] args)
{
await Demo();
Console.ReadKey();
}
private static async Task Demo()
{
}
}
從上面的代碼中,我們看到,在生成Collection對象時,用到了CollectionModel:
IMongoDatabase _database = _client.GetDatabase("Test");
IMongoCollection<CollectionModel> _collection = _database.GetCollection<CollectionModel>("TestCollection");
這兩行,其實就完成了一個映射的工作:把MongoDB中,Test數據庫下,TestCollection數據集(就是SQL中的數據表),映射到CollectionModel這個數據類中。換句話說,就是用CollectionModel這個類,來完成對數據集TestCollection的所有操作。
保持CollectionModel為空,我們往數據庫寫入一行數據:
private static async Task Demo()
{
CollectionModel new_item = new CollectionModel();
await _collection.InsertOneAsync(new_item);
}
執行,看一下寫入的數據:
{
"_id" : ObjectId("5ef1d8325327fd4340425ac9")
}
OK,我們已經寫進去一條數據了。因為映射類是空的,所以寫入的數據,也只有_id一行內容。
但是,為什麼會有一個_id呢?
MongoDB數據集中存放的數據,稱之為文檔(Document)。每個文檔在存放時,都需要有一個ID,而這個ID的名稱,固定叫_id。
當我們建立映射時,如果給出_id字段,則MongoDB會採用這個ID做為這個文檔的ID,如果不給出,MongoDB會自動添加一個_id字段。
例如:
public class CollectionModel
{
public ObjectId _id { get; set; }
public string title { get; set; }
public string content { get; set; }
}
和
public class CollectionModel
{
public string title { get; set; }
public string content { get; set; }
}
在使用上是完全一樣的。唯一的區別是,如果映射類中不寫_id,則MongoDB自動添加_id時,會用ObjectId作為這個字段的數據類型。
ObjectId是一個全局唯一的數據。
當然,MongoDB允許使用其它類型的數據作為ID,例如:string,int,long,GUID等,但這就需要你自己去保證這些數據不超限並且唯一。
例如,我們可以寫成:
public class CollectionModel
{
public long _id { get; set; }
public string title { get; set; }
public string content { get; set; }
}
我們也可以在類中修改_id名稱為別的內容,但需要加一個描述屬性BsonId:
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
}
這兒特別要注意:BsonId屬性會告訴映射,topic_id就是這個文檔數據的ID。MongoDB在保存時,會將這個topic_id轉成_id保存到數據集中。
在MongoDB數據集中,ID字段的名稱固定叫_id。為了代碼的閱讀方便,可以在類中改為別的名稱,但這不會影響MongoDB中存放的ID名稱。
修改Demo代碼:
private static async Task Demo()
{
CollectionModel new_item = new CollectionModel()
{
title = "Demo",
content = "Demo content",
};
await _collection.InsertOneAsync(new_item);
}
跑一下Demo,看看保存的結果:
{
"_id" : ObjectId("5ef1e1b1bc1e18086afe3183"),
"title" : "Demo",
"content" : "Demo content"
}
就是常規的數據字段,直接寫就成。
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int favor { get; set; }
}
保存后的數據:
{
"_id" : ObjectId("5ef1e9caa9d16208de2962bb"),
"title" : "Demo",
"content" : "Demo content",
"favor" : NumberInt(100)
}
說Decimal特殊,是因為MongoDB在早期,是不支持Decimal的。直到MongoDB v3.4開始,數據庫才正式支持Decimal。
所以,如果使用的是v3.4以後的版本,可以直接使用,而如果是以前的版本,需要用以下的方式:
[BsonRepresentation(BsonType.Double, AllowTruncation = true)]
public decimal price { get; set; }
其實就是把Decimal通過映射,轉為Double存儲。
把類作為一個數據集的一個字段。這是MongoDB作為文檔NoSQL數據庫的特色。這樣可以很方便的把相關的數據組織到一條記錄中,方便展示時的查詢。
我們在項目中添加兩個類Contact和Author:
public class Contact
{
public string mobile { get; set; }
}
public class Author
{
public string name { get; set; }
public List<Contact> contacts { get; set; }
}
然後,把Author加到CollectionModel中:
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int favor { get; set; }
public Author author { get; set; }
}
嗯,開始變得有點複雜了。
完善Demo代碼:
private static async Task Demo()
{
CollectionModel new_item = new CollectionModel()
{
title = "Demo",
content = "Demo content",
favor = 100,
author = new Author
{
name = "WangPlus",
contacts = new List<Contact>(),
}
};
Contact contact_item1 = new Contact()
{
mobile = "13800000000",
};
Contact contact_item2 = new Contact()
{
mobile = "13811111111",
};
new_item.author.contacts.Add(contact_item1);
new_item.author.contacts.Add(contact_item2);
await _collection.InsertOneAsync(new_item);
}
保存的數據是這樣的:
{
"_id" : ObjectId("5ef1e635ce129908a22dfb5e"),
"title" : "Demo",
"content" : "Demo content",
"favor" : NumberInt(100),
"author" : {
"name" : "WangPlus",
"contacts" : [
{
"mobile" : "13800000000"
},
{
"mobile" : "13811111111"
}
]
}
}
這樣的數據結構,用着不要太爽!
枚舉字段在使用時,跟類字段相似。
創建一個枚舉TagEnumeration:
public enum TagEnumeration
{
CSharp = 1,
Python = 2,
}
加到CollectionModel中:
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int favor { get; set; }
public Author author { get; set; }
public TagEnumeration tag { get; set; }
}
修改Demo代碼:
private static async Task Demo()
{
CollectionModel new_item = new CollectionModel()
{
title = "Demo",
content = "Demo content",
favor = 100,
author = new Author
{
name = "WangPlus",
contacts = new List<Contact>(),
},
tag = TagEnumeration.CSharp,
};
/* 後邊代碼略過 */
}
運行后看數據:
{
"_id" : ObjectId("5ef1eb87cbb6b109031fcc31"),
"title" : "Demo",
"content" : "Demo content",
"favor" : NumberInt(100),
"author" : {
"name" : "WangPlus",
"contacts" : [
{
"mobile" : "13800000000"
},
{
"mobile" : "13811111111"
}
]
},
"tag" : NumberInt(1)
}
在這裏,tag保存了枚舉的值。
我們也可以保存枚舉的字符串。只要在CollectionModel中,tag聲明上加個屬性:
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int favor { get; set; }
public Author author { get; set; }
[BsonRepresentation(BsonType.String)]
public TagEnumeration tag { get; set; }
}
數據會變成:
{
"_id" : ObjectId("5ef1ec448f1d540919d15904"),
"title" : "Demo",
"content" : "Demo content",
"favor" : NumberInt(100),
"author" : {
"name" : "WangPlus",
"contacts" : [
{
"mobile" : "13800000000"
},
{
"mobile" : "13811111111"
}
]
},
"tag" : "CSharp"
}
日期字段會稍微有點坑。
這個坑其實並不源於MongoDB,而是源於C#的DateTime類。我們知道,時間根據時區不同,時間也不同。而DateTime並不準確描述時區的時間。
我們先在CollectionModel中增加一個時間字段:
public class CollectionModel
{
[BsonId]
public ObjectId topic_id { get; set; }
public string title { get; set; }
public string content { get; set; }
public int favor { get; set; }
public Author author { get; set; }
[BsonRepresentation(BsonType.String)]
public TagEnumeration tag { get; set; }
public DateTime post_time { get; set; }
}
修改Demo:
private static async Task Demo()
{
CollectionModel new_item = new CollectionModel()
{
/* 前邊代碼略過 */
post_time = DateTime.Now, /* 2020-06-23T20:12:40.463+0000 */
};
/* 後邊代碼略過 */
}
運行看數據:
{
"_id" : ObjectId("5ef1f1b9a75023095e995d9f"),
"title" : "Demo",
"content" : "Demo content",
"favor" : NumberInt(100),
"author" : {
"name" : "WangPlus",
"contacts" : [
{
"mobile" : "13800000000"
},
{
"mobile" : "13811111111"
}
]
},
"tag" : "CSharp",
"post_time" : ISODate("2020-06-23T12:12:40.463+0000")
}
對比代碼時間和數據時間,會發現這兩個時間差了8小時 – 正好的中國的時區時間。
MongoDB規定,在數據集中存儲時間時,只會保存UTC時間。
如果只是保存(像上邊這樣),或者查詢時使用時間作為條件(例如查詢post_time < DateTime.Now的數據)時,是可以使用的,不會出現問題。
但是,如果是查詢結果中有時間字段,那這個字段,會被DateTime默認設置為DateTimeKind.Unspecified類型。而這個類型,是無時區信息的,輸出显示時,會造成混亂。
為了避免這種情況,在進行時間字段的映射時,需要加上屬性:
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime post_time { get; set; }
這樣做,會強制DateTime類型的字段為DateTimeKind.Local類型。這時候,從显示到使用就正確了。
但是,別高興的太早,這兒還有一個但是。
這個但是是這樣的:數據集中存放的是UTC時間,跟我們正常的時間有8小時時差,如果我們需要按日統計,比方每天的銷售額/點擊量,怎麼搞?上面的方式,解決不了。
當然,基於MongoDB自由的字段處理,可以把需要統計的字段,按年月日時分秒拆開存放,像下面這樣的:
class Post_Time
{
public int year { get; set; }
public int month { get; set; }
public int day { get; set; }
public int hour { get; set; }
public int minute { get; set; }
public int second { get; set; }
}
能解決,但是Low哭了有沒有?
下面,終極方案來了。它就是:改寫MongoDB中對於DateTime字段的序列化類。噹噹當~~~
先創建一個類MyDateTimeSerializer:
public class MyDateTimeSerializer : DateTimeSerializer
{
public override DateTime Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var obj = base.Deserialize(context, args);
return new DateTime(obj.Ticks, DateTimeKind.Unspecified);
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, DateTime value)
{
var utcValue = new DateTime(value.Ticks, DateTimeKind.Utc);
base.Serialize(context, args, utcValue);
}
}
代碼簡單,一看就懂。
注意,使用這個方法,上邊那個對於時間加的屬性[BsonDateTimeOptions(Kind = DateTimeKind.Local)]一定不要添加,要不然就等着哭吧:P
創建完了,怎麼用?
如果你只想對某個特定映射的特定字段使用,比方只對CollectionModel的post_time字段來使用,可以這麼寫:
[BsonSerializer(typeof(MyDateTimeSerializer))]
public DateTime post_time { get; set; }
或者全局使用:
BsonSerializer.RegisterSerializer(typeof(DateTime), new MongoDBDateTimeSerializer());
BsonSerializer是MongoDB.Driver的全局對象。所以這個代碼,可以放到使用數據庫前的任何地方。例如在Demo中,我放在Main里了:
static async Task Main(string[] args)
{
BsonSerializer.RegisterSerializer(typeof(DateTime), new MyDateTimeSerializer());
await Demo();
Console.ReadKey();
}
這回看數據,數據集中的post_time跟當前時間显示完全一樣了,你統計,你分組,可以隨便霍霍了。
這個需求很奇怪。我們希望在一個Key-Value的文檔中,保存一個Key-Value的數據。但這個需求又是真實存在的,比方保存一個用戶的標籤和標籤對應的命中次數。
數據聲明很簡單:
public Dictionary<string, int> extra_info { get; set; }
MongoDB定義了三種保存屬性:Document、ArrayOfDocuments、ArrayOfArrays,默認是Document。
屬性寫法是這樣的:
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<string, int> extra_info { get; set; }
這三種屬性下,保存在數據集中的數據結構有區別。
DictionaryRepresentation.Document:
{
"extra_info" : {
"type" : NumberInt(1),
"mode" : NumberInt(2)
}
}
DictionaryRepresentation.ArrayOfDocuments:
{
"extra_info" : [
{
"k" : "type",
"v" : NumberInt(1)
},
{
"k" : "mode",
"v" : NumberInt(2)
}
]
}
DictionaryRepresentation.ArrayOfArrays:
{
"extra_info" : [
[
"type",
NumberInt(1)
],
[
"mode",
NumberInt(2)
]
]
}
這三種方式,從數據保存上並沒有什麼區別,但從查詢來講,如果這個字段需要進行查詢,那三種方式區別很大。
如果採用BsonDocument方式查詢,DictionaryRepresentation.Document無疑是寫着最方便的。
如果用Builder方式查詢,DictionaryRepresentation.ArrayOfDocuments是最容易寫的。
DictionaryRepresentation.ArrayOfArrays就算了。數組套數組,查詢條件寫死人。
我自己在使用時,多數情況用DictionaryRepresentation.ArrayOfDocuments。
上一章介紹了數據映射的完整內容。除了這些內容,MongoDB還給出了一些映射屬性,供大家看心情使用。
這個屬性是用來改數據集中的字段名稱用的。
看代碼:
[BsonElement("pt")]
public DateTime post_time { get; set; }
在不加BsonElement的情況下,通過數據映射寫到數據集中的文檔,字段名就是變量名,上面這個例子,字段名就是post_time。
加上BsonElement后,數據集中的字段名會變為pt。
看名稱就知道,這是用來設置字段的默認值的。
看代碼:
[BsonDefaultValue("This is a default title")]
public string title { get; set; }
當寫入的時候,如果映射中不傳入值,則數據庫會把這個默認值存到數據集中。
這個屬性是用來在映射類中的數據類型和數據集中的數據類型做轉換的。
看代碼:
[BsonRepresentation(BsonType.String)]
public int favor { get; set; }
這段代表表示,在映射類中,favor字段是int類型的,而存到數據集中,會保存為string類型。
前邊Decimal轉換和枚舉轉換,就是用的這個屬性。
這個屬性用來忽略某些字段。忽略的意思是:映射類中某些字段,不希望被保存到數據集中。
看代碼:
[BsonIgnore]
public string ignore_string { get; set; }
這樣,在保存數據時,字段ignore_string就不會被保存到數據集中。
數據映射本身沒什麼新鮮的內容,但在MongoDB中,如果用好了映射,開發過程從效率到爽的程度,都不是SQL可以相比的。正所謂:
一入Mongo深似海,從此SQL是路人。
謝謝大家!
(全文完)
本文的配套代碼在https://github.com/humornif/Demo-Code/tree/master/0015/demo
|
微信公眾號:老王Plus 掃描二維碼,關注個人公眾號,可以第一時間得到最新的個人文章和內容推送 本文版權歸作者所有,轉載請保留此聲明和原文鏈接 |
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※Google地圖已可更新顯示潭子電動車充電站設置地點!!
※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益
※別再煩惱如何寫文案,掌握八大原則!

對綠能產業相當關心的 Google,在分散式能源領域,當然也不會缺席。今年 5 月,Google 公布「小盒計畫(Little Box Challenge)」,若是有員工能研發出縮小版的逆變器,就能獲得 100 萬美元(約合新台幣 3,000 萬元)獎金。而最近 Google 將這項計畫對外開放,參賽者在 2015 年 7 月前都能報名參加,最後交由電機電子工程師學會(IEEE)評選出優勝者。
逆變器在綠能產業中扮演關鍵性的角色,舉凡連接太陽能板、風力發電機、電動車等電網裝置,都需要逆變器參與其中,而逆變器的主要用途就是把太陽能板接收能量後產生的直流電,轉換為交流電併入電網當中;電動車作為電力儲存裝置時,若要將電能輸出,也同樣必須藉由逆變器轉換為交流電併入電網。

逆變器體積縮小有利微電網發展 然而,隨著屋頂太陽能板裝設越來越普遍,龐大的逆變器顯得笨重又不符合效益,這也是 Google 為何如此迫切希望大幅縮小逆變器的體積與重量的原因。 Google 的「小盒計畫」希望將逆變器體積縮為現在的十分之一,相當於一台小筆電的大小;每立方英吋的功率密度須超過 50 瓦特;轉換效率最低值至少為 95%;耐高溫性的部分,須可承受攝氏 60 度的高溫,其他標準則包括一些能讓逆變器與電網連結作用的規格限制。而綜結以上這些規格,最重要的,當然還是參賽者能否縮小逆變器的體積大小了。

假如能研發出小型逆變器,Google 希望能在偏遠地區打造低成本的微電網,或在停電時,能以電動車維持穩定電力供給,讓他們在綠能產業的佈局上,又多了一項投資。 在矽谷科技企業中,Google 對綠能的投資可說是最不手軟的一家,綠能涉獵範圍也最廣泛。就如先前《科技新報》提到的,過去 5 年內,Google 在太陽能與風能等綠能上的投資,已超過 10 億美元。除了持有「大西洋風能網」項目建設中 37.5 % 的股份、投資美國離岸風力發電骨幹計畫 50 億美元外,在電網上的參與也越來越活躍。 過去幾個月以來,Google 開發名為「顛覆電網(Bottom Up Grid)」的專案,陸續招募電力或電子工程師,希望能從最根本改善電力轉換系統。看來現在除了佈局綠能產業外,與綠能息息相關的電網系統,也成為 Google 改革的目標了。 本文全文授權自《科技新報》──
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※新北清潔公司,居家、辦公、裝潢細清專業服務
※推薦評價好的iphone維修中心
特斯拉當家電動車 Model S 不再完美無缺,去年給予 Model S 最高評級的消費者評鑒雜誌《Consumer Reports》點出 Model S 仍許多有小缺陷待改進。 《Consumer Reports》指出,Model S 多在跑超過一萬哩後出現問題,例如里程數超過 1.2 萬哩後,中央控制螢幕在會有反白的狀況,使多項功能無法操作。此外,還有車頂會發出異常噪音,前置行李箱蓋會自動開啟等問題。 Model S 去年 5 月在《Consumer Reports》評鑑中拿下 99 分(滿分 100 分),創史上最高分,之後並在 11 月獲選為年度 10 大好車第一名。 對此,特斯拉執行長 Elon Musk 7 月 31 日曾坦承,較早一批出廠的 Model S 的確有些生產上的瑕疵,但目前出廠的新車多已作修正。 (圖片來源:)
本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線
※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整
※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!
※教你寫出一流的銷售文案?
※超省錢租車方案