【Nodejs】记一次图像识别的冒险
筆者的團隊最近接到了一個有關圖像識別的需求,本來應該由后端團隊提供能力,native提供容器,前端團隊僅負責一些“外圍的形式工作”,不過由于各種各樣的原因,最后的結果變成了前端團隊在原有工作基礎上,承擔了圖像識別的能力,后端專注其他服務,于是一場圖像識別的冒險就此開始。
?
因為項目背景是圖像識別,而筆者僅有一些本科時圖形學的一些皮毛知識,自己實現想來基本不太可能了,于是一開始就準備伸出手去探索下有沒有現實可用的既成方案。因為最開始的項目需求是文字識別,首先進入筆者視野的就是OCR(Optical Character Recognition),也就是光學字符識別,畢竟node具備完全的服務端能力,只需要找到庫和調用庫的nodejs包,實現就很簡單了,不過簡單查找了一下,筆者發現業界還是挺有這方面的探索的,首當其沖的就是google的開源項目tesseract-ocr,但是經過筆者的測試發現,tesseract對于印刷字的識別能力確實不錯,但是筆者的項目更接近去年支付寶掃“?!弊值膱鼍?#xff0c;在自然光和手寫字的各種干擾下,ocr整個一套方案的可行性就有待商榷了,而更讓筆者吃驚的是,識別的耗時也是筆者的業務場景所不太能接受的,基于種種原因,筆者不得不放棄在OCR上的繼續探索,轉而尋找其他方案,筆者心想:既然識別文字做不到,那么如果是把文字抽象為圖形,只是和圖形做比較呢?抱著這樣的思想,筆者開始將目光轉向一些圖片相似度比較的庫,此時opencv就進入了筆者的視野,也適逢opencv在人臉識別上備受青睞,也堅定了筆者使用opencv這一方案的信心。
?
方向定了之后,實施起來就快了。本應該是如此,但是筆者雖然找到了opencv binding nodejs的包,不過這個包的作者寫的readme適合并不是面向筆者這個場景的,里面的api和demo也傾向于在表達opencv在人臉識別上的能力,而不是筆者所需要的圖片相似度比較的能力,經過一些抹黑的探索之后,筆者在node-opencv庫里找到了一個由作者實現了但并沒有記錄在文檔中的api——ImageSimilarity,看到這個名字的時候,筆者好似終于在茫茫的C++黑夜里找到了一絲光明一般,果不其然,這個api果然是進行圖片的相似度比較的,最終代碼很簡單:
var similarityResult = yield new Promise((resolve, reject) => {opencv.ImageSimilarity(matrixSource, matrixTarget, function(err, dissimilarity){resolve({dissimilarity:dissimilarity});}); });經過一些簡單的nodejs webservice開發之后,整個業務需求的服務就告一段落了,不過正當筆者準備測試上線的時候,卻又迎來了新的問題。筆者本地使用的是mac os作為開發環境,而服務器使用的都是linux centos的操作系統,在安裝opencv上也有很多不同的地方,而且還需要考慮到上線后服務器擴容的問題,在線上服務器上一臺臺安裝環境筆者心想可不是件容易事,經過自己的整理和運維同事的溝通,最后通過rpm包的方式解決了該問題。
?
想想雖然一波三折,不過事情總算是做完了的時候,筆者突然回想起了之前ocr性能堪憂的問題,于是多了個心眼,想壓測下opencv的處理性能,畢竟就筆者所了解的圖形學的知識來說,圖形處理其實是很消耗cpu的,這和筆者之前單純使用nodejs作為restful的場景畢竟不同,雖然本地有100ms以下的不錯表現(雖然和正常的node service差距已經很大),但考慮到實際還有目標圖片傳輸、讀寫、比較過程,于是筆者拿出jmeter進行了一次壓力測試,結果果然令人大吃一驚:并發的請求數稍大就直接占滿了CPU,而筆者所面對的業務場景的目標DAU有1200w,這種性能肯定是撐不住的!
?
怎么辦!即使有能力進行圖片識別,但是圖片識別的并發量不足依然不能滿足筆者的業務需求,經過筆者的一些觀察發現,最核心的耗時發生在opencv的ImageSimilarity這個方法的執行上,但是這個api對于筆者而言幾乎接近于黑盒,筆者只是善意的第三調用方,而且node-opencv也只是作為對c++底層庫的中間層,底層實現完全依賴c++,對筆者而言,基本短時間內就不存在了優化的余地,于是筆者只能從其他方面曲線救國了:首先,為了增加硬實力,筆者不得不才用擴容的方式,從物理上提高圖片識別服務的并發處理能力;然后,因為物理的擴容并不能夠解決高峰期瞬時流量過大的問題,筆者不得不采取“非常手段”,保證服務不至于崩潰,于是筆者做好容災預案了。不過筆者還是糾結于使用怎樣的容災實現比較好:要么是顯示每秒的總請求數,將會導致過載的請求直接降級處理,要么就是實施監控cpu的使用率,保證cpu正常的情況下做降級處理。兩種方案取舍的關鍵在于哪一種能夠真正反應服務器的負載狀況,基于種種考慮,筆者最終選擇了數量的方案(由于nodejs獲取的cpu數據總是滯后,當然這只是筆者了解的情況)。最終代碼如下:
let processResult = yield new Promise((resolve, reject) => {limiter.removeTokens(1, (err, remainingRequests) => {if (remainingRequests <= 0) {resolve(co(disasterRecoveryBranch.bind(this)));} else {resolve(co(normalBranch.bind(this)));}})});? 到此,整個需求才算是真正告一段落?;剡^頭來我們重新review下整個業務流程:
1、native 上傳圖片,調用node服務
2、nodejs 接受請求
3、將各種參數從請求體中讀出(當然包括上傳的圖片)
4、將上傳圖片轉換為待比較的格式,并且取出比較的源圖片(當然你也可以在一開始就把它們取出來放內存里,不過由于筆者的源圖片可以動態變更,所以這里的額外io并沒有省)
5、調用ImageSimilarity方法,比較目標圖片和源圖片(容災情況則直接會執行降級方案,然后繼續往下進行)
6、通過得出的比較值,調用對應的后端服務
7、拿到后端的響應結果,經過封裝后回傳回客戶端
?
其實其中還有不少可以優化的點,最重要的是,opencv本身由于對于筆者其實是個黑匣子,這也一定程度上導致了最后的服務可用性的問題的出現。不過雖然話需要怎么說,但是反過來,筆者也慶幸能生活于這個互聯網時代,能夠享受如此多的時代文明之光的饋贈,更深覺還有好多好多這樣早已出現,卻還不曾發光的事物等待我們去發現,去鏈接,去讓那些埋藏于浮華深處的光芒,重新發出耀眼的光輝。
轉載于:https://www.cnblogs.com/mfoonirlee/p/6869130.html
總結
以上是生活随笔為你收集整理的【Nodejs】记一次图像识别的冒险的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (初学者)安装hadoop集群注意事项
- 下一篇: 解决使用CoreData时报duplic