iSpy結合yolo複雜環境下DLL呼叫路徑攻略

2021-09-29 04:34:26 字數 4519 閱讀 8782

但我還是要吐槽一下ispy,那個**寫的真叫乙個爛!咱就不要說什麼物件導向程式設計這種高大上的要求,畢竟很多高手都不一定做的好,但是像**書寫規範這種基本要求也沒達到,縮排從來不對齊,空行滿天飛,一邊拿著嘔吐袋一邊看**!我從來沒看過這麼爛的開源**,絕對沒有之一!大量的大學生水平**,處理手法非常稚嫩,如對windows訊息的處理,windows訊息引數都是無符號的uint,它非要轉成有符號的int,真是畫蛇添足,如果訊息引數大於等於0x80000000,那就會產生overflow錯誤,這可是乙個非常嚴重的系統錯誤。他們肯定也發現了這個不穩定問題,不但不耐心從根子上解決問題,反而使用try catch流氓**蒙混過關。所以ispy從來不會因為windows訊息問題非法操作退出,貌似很穩定,但就是某些操作不起作用。像這種濫用try catch機制的行為,非常鄙視,哪怕在catch中寫個除錯控制台輸出也好啊。最後怕客戶將來找我麻煩,把ispy的windows訊息處理系統改了一遍,把我累個半死。

相比之下,yolo的**水平還是非常高的,但畢竟是c語言寫的,沒有物件導向設計概念,**生硬,不夠優雅,但呼叫邏輯層次還是清晰可人的。它是目前非常火的乙個基於深度學習的影象識別演算法,速度非常快。我在gtx1080ti上測試,例如安全帽檢測只需要38-42毫秒,要知道對影象進行標注重繪也要8-12毫秒。後來找了一塊最高端的titan測試,啟用yolo的cudnn硬體哈夫曼編譯碼編譯選項,檢測時間比重繪時間都快!確實非常適合實時檢測。

好了,前情提要交代完畢,現在開始進入正題。ispy是使用c#編寫的基於.net架構的託管型應用程式,而yolo最快的版本(那些大批基於python的版本只能用於學習研究,不適合商業應用)是乙個基於c語言的linux程式,幸好有人做了乙個非託管型windows dll封裝,讓我省了不少勁。

但即使這樣,仍然碰到了頭疼的問題。首先是體系的問題,ispy是喜愛windows理念的程式設計師編的託管型應用程式,yolo的原始版本是喜愛linux的程式設計師編的非託管型應用程式,在介面問題上是互相不鳥。ispy的對外介面,引數是windows的bitmap,yolo的對外介面,引數是影象檔名!我真是無語了,linux程式設計師還是那麼固執的喜歡命令列程式,注定他們只能學習和研究。如果我把bitmap轉成檔案再傳給yolo,硬碟的讀寫時間都會遠超檢測時間,沒有商業應用價值。幸好那個做封裝的還是有些人性,作為乙個編譯選項,引入了opencv,封裝了乙個引數是mat的檢測方法,讓我在效能上有了新機會。

在.net架構體系中,dllimport功能負責匯入dll方法,其尋找dll策略是在當前目錄、系統目錄和環境變數path中依序查詢,這跟非託管型開發環境(如vc++)是一樣的。但這個當前目錄在.net環境下非常詭異,微軟使用了目錄重定向技術,目的是為了不麻煩人的情況下最大機會載入dll,但實際上我覺得這不一定是乙個好策略,導致大量.net程式設計師稀里糊塗的就調通了程式,但當脫離開發環境,在使用者方安裝部署的時候就傻眼了,各種水土不服,於是手忙腳亂的把dll到處拷貝,企圖僥倖過關,實在蒙不過去的,甚至直接在使用者方安裝個開發環境,以debug方式執行應用來糊弄使用者。

經測試表明,.net程式無論是否在開發環境下,無論debug版本還是release版本,在執行時都會檢測程式的載入路徑,如果發現貌似bin//這樣的目錄(其中可能是x86或x64,可能是debug或release),它就會把當前目錄重定向為bin的上一級目錄,也就是專案的根路徑,方便呼叫專案資源檔案,但有時候也不盡然是這樣,因為在debug或release目錄下,它還按照專案路徑複製了乙份專案依賴檔案,所以有時候搞不懂它呼叫的是根路徑下的dll還是debug或release目錄下的dll,或者兩者都有。

對於當前目錄的這種混亂情況,某些聰明的程式設計師就不趟這趟渾水,使用系統目錄好了,這也不失為乙個好策略。但很多人都使用windows/system32這個系統目錄,這可就不太好了。這個目錄檔案非常多,檔案重名概率比較高,而且不易維護,碰到公升級替換這種事,誤操作的可能性也比較高。那麼怎麼做才是使用系統目錄的最佳體驗呢?咱們就從吐槽一下windows的系統目錄開始。了解一下歷史先,第一代windows系統是基於intel8086開發的,這是乙個16位的cpu,所以第一代windows系統是16位的作業系統,其系統目錄為windows/system,到了80386時代,cpu變成了32位,第二代windows系統也就成為32位的作業系統,這時候歷史包袱就來了,16位程式和32位程式是不相容的,但是作業系統一定要保持歷史相容性這是微軟稱霸世界的乙個重要策略,怎麼辦呢?於是windows系統就有了2個系統目錄,windows/system存放16位應用程式,windows/system32存放32位應用程式,這個設計只要是正常人類都能理解。到了64位cpu時代,第三代windows系統也就成為64位的作業系統,歷史負擔越來越重,它需要相容16位、32位和64位的應用程式,於是就有了3個系統目錄,windows/system存放16位應用程式,windows/system32存放64位應用程式,windows/syswow64存放32位應用程式,驚不驚喜,神不神奇,估計一定是總設計師換了,沒有一脈相承,腦洞大開的設計,唉,這個我就不吐槽了,只要他高興就好。總之,在任何windows作業系統中,都有乙個windows/system系統目錄,這個目錄中的檔案在32位作業系統時代都已經非常少了,在64位windows7以後,乾脆就是空目錄,但目錄結構還是保留的,所以我在除錯時,經常把dll放到windows/system這個目錄裡,保證能夠載入正確的dll,同時還易於操作維護管理。

但現在綠色環保軟體是潮流,免安裝,即考即用,向系統目錄拷貝檔案還需要使用者確認等都不人性化,所以這個方法也僅僅用於快速原型測試,作為商業級應用還是要下功夫解決這些問題,而不是逃避這些問題。

既然.net能自動重定向,那我們就手動重定向。經研究表明,當前目錄是載入程序的乙個屬性,由作業系統的程序排程系統初始化,但其後可以在任意時刻修改,.net的重定向機制也是源於此。

第一步,我們需要知道發起呼叫dllimport功能的檔案所在位置,但在.net中有一大堆獲取載入檔案路徑的方法,由於對.net不熟,只好乙個乙個試,最後找到了乙個在任何情況下都很靠譜的方法this.gettype().assembly.location,這方法能拿到這行**編譯後所在的二進位制**檔案在載入時的絕對全路徑檔名。

第二步,根據dllimport功能的檔案所在位置和dll部署結構拼裝dll所在目錄,然後在呼叫有dllimport功能的dll前,通過設定system.environment.currentdirectory這個變數的方法來修改當前目錄,這樣就能保證載入指定目錄中的dll。

void currentdirectoryupdate(string strpathin)

這個方法作了一定的封裝,只需要傳入dll子目錄名稱,此方法幫你自動生成全路徑,並設定為當前目錄。

此法在一般情況下都是適用的,但我就遇到了二般的情況。這就得先吐槽一下emgu,當時在選型託管型opencv庫時,網上對emgu大加推崇,像我這種.net影象處理的小白,就選用了emgu。當寫完**,進行聯調時,才發現emgu的mat,opencv根本就不認識,翻看了一下emgu和opencv的原始碼,才知道emgu早已經跟opencv分道揚鑣,自成一體了。雖然emgu裡面還保留有乙個cv::mat的資料結構,這個是跟opencv完全一致的,但沒有方法在使用這個資料結構,無法轉換。最後,找到了乙個跟opencv資料結構100%一致的託管型opencv庫opencvsharp,這個日本程式設計師真是有恆心,硬生生的把opencv用c#重寫了一遍!資料結構封送與opencv一模一樣,所以非常靠譜。我覺得emgu這種另起爐灶的做法是給自己掘墓,因為誰也不能保證.net程式不需要呼叫第三方opencv,這種不相容的問題會大大遏制emgu的適用範圍,而opencvsharp的這種相容策略一定會前途無量。但opencvsharp的這種相容性也是有代價的,它必須跟隨opencv版本的公升級而公升級,工作量非常大,這就是我佩服那個日本程式設計師的原因,這事要是換了我,打死也不幹。所以opencvsharp目前只有2.4.1和4.1.1版本,分別對應opencv的2.4.1和4.1.1版本,而yolo windows dll封裝版只能使用opencv3.2.0版本,其他版本各種出錯。這樣整個系統需要兩套不同版本的opencv執行時庫,而opencv執行時庫的不同版本中,有部分檔名是一樣的,無法放到同乙個目錄中。

這個世界總是這樣,要讓你登峰造極,必讓你苦其心志,勞其筋骨,餓其體膚,空乏其身。思前想後,看來只有修改環境變數path才是終極**。事實證明,這也是明智之舉。因為後來客戶要求應用程式能夠自適應作業系統,在32位系統上自動執行32位應用程式,在64位系統上自動執行64位應用程式。這就要求,應用程式應編譯為32位和64位兩套版本,dll也應該帶32位和64位兩套版本,但是32位的dll檔名和64位的dll檔名可都是一模一樣的,它們也沒法放在同乙個目錄裡。幸好前面功課做足,這個需求就是小菜一碟。

關於修改環境變數,網上的**也是一大堆,經認真甄選,找到了乙個最優雅的方法。其沒有修改系統配置的環境變數,而只是修改了程序的系統環境變數,在執行時即刻生效,退出程式後對系統環境變數沒有影響,非常綠色環保。

void environmentpathsadd(ienumerablestringspathsnew)

這是我做的乙個封裝方法,網上的方法有一些瑕疵,就是把新增的目錄放到path環境變數的最後面,這有一定的不確定性,如果前面的path目錄中包含目標dll的同名檔案,你的應用又開始在某些環境下水土不服,所以一定要把目標路徑加到path環境變數的最前面,保證萬無一失。

好了,世界終於安靜下來,大家也都滿足了吧。文中若有不妥之處,望請海涵,如能不賜斧正,在下感激不盡。[email protected]

結合php PHP與jQuery結合的功能

主要問題難點在於 獲取後台填充資料沒問題,但是當後台資料已失效,前台資料已獲取後,這種歷史遺留資料處理比較棘手,原來的資料填充和釋放只針對後台所有的資料,沒有把版本迭代後的狀態考慮進去,這裡的主要問題就是當使用者不重新整理頁面,還要解決後台傳輸的無效資料和有效資料的區分,不會在前台展現有效資料把無效...

mysql左右結合 結合左右加入mysql查詢

唯一可以做到的就是使用union.mysql不像mssql那樣支援full joinjust.select from tbl1 t1 left join tbl2 t2 on t1.col t2.col union select from tbl1 t1 right join tbl2 t2 on ...

Vim Markdown 結合使用

vim 編輯文件,通過瀏覽器實時檢視 markdown 文件。使用示例 鏈結 或者git clone vim bundle vundle.vim 配置 vimrc set nocompatible filetype off set rtp vim bundle vundle.vim vundle 安...