從前端工程師到AR工程師
為了入門AR,我做了一個簡單的圖像識別的Demo,將不同的圖片對準檢測區域,會出現不同天氣效果,效果如下圖

AR Demo和github地址(PC網站,不推薦手機使用)
玩這個Demo的時候需要一個有攝像頭的電腦和手機,將鼠標移動到天氣圖標上會有二維碼,用手機掃描二維碼進入天氣圖標網頁,將圖標對準屏幕的黑色區域進行掃描就可以出現對應的動畫。
這是一個Marker Base的AR程序,Marker Base就是識別一個特定的圖像(高級的二維碼...),而高級的AR可能是Marker Less的,比如識別環境中的車,房子,行人。目前由於技術限制,大部分AR都是Marker base的。
其實我覺得AR比VR更困難,VR只需要給用戶“輸出”一個虛擬世界,而AR還需要根據用戶“輸入”的現實世界,“輸出”虛擬的效果。 “輸出”動畫對於前端來說已經是輕車熟路,但是如何處理用戶“輸入”的現實世界是很大的技術挑戰。雖然現代JS引擎已經很快了,但是在處理攝像頭傳入的圖像數據任務時,單線程的JS的馬車遠遠落後於同時代的Native火箭。
在WebRTC技術的支持下,我們可以拿到攝像頭的數據並且把它繪製成圖片,一旦用JS處理像素,噩夢就開始了。 AR應用對計算速度要求極高,動畫至少要30幀才能被用戶勉強接受,也就是說,要在34ms的時間內,完成圖像處理-->圖像識別-->動畫渲染。這三個操作都是CPU密集運算,如果全部用JS來做,肯定無法在一般配置的電腦上保證幀數。這時就要拿出WebGL和WebWorker一起來加速了。
圖像識別的第一步都是圖像處理,彩色圖片中包含太多無用的訊息,同時攝像頭在連續拍攝的時候有噪點,需要把主要訊息提取出來,並且盡可能降噪。
轉換成黑白的算法有很多,但是基於GPU計算的話,大多還是邊緣檢測,邊緣特徵的點會被標記成白色,另外再對圖中的白點大小進行篩選,太小的白點可能是攝像頭的噪點。這樣兩步操作用WebGL來做60幀毫無壓力。
接下來把黑白的圖片像素讀取出來進行圖像識別,用GPU實現的圖像識別還真的很少見,這裡我選用了第三方的神經網絡( ConvNetJS )進行圖像識別。對於前端來說,神經網絡還是一個比較陌生的詞語。大概就是機器學習的一種算法,如果探索精神不是特別強,可以把它當成一個工具而不用了解具體實現,就像你用React不需要知道具體怎麼將jsx轉化成DOM。接下來主要講一下怎麼用神經網絡進行識別。
“機器學習”一般和“大數據”同時出現,我們建立一個神經網絡之後,將分類好的圖片傳入神經網絡後,神經網絡會根據圖片分類自動調整參數,當傳入的圖片足夠多的時候,神經網絡就能學習出正確的參數來區分圖片類型。
整個訓練過程也在網頁中進行,首先對每種Maker圖像進行取樣,JS對這些樣本進行旋轉,縮放和位移的變化生成大量的圖片訓練集,訓練完成後的神經網絡就可以用作識別圖像。當我們改變Maker圖片後,可以重新訓練新的神經網絡。
任何一張圖片傳入神經網絡之後一定會被進行分類,這會導致當用戶沒有提供任何Maker時,神經網絡錯誤地進行分類,所以訓練的時候增加了一個異常類,這類圖片可以是任何數據。
一個樣本變化後的訓練集

當神經網絡訓練好之後,我們可以通過JSON來進行保存和加載。為了保證頁面的渲染幀數,神經網絡識別圖像的算法放在WebWorker裡面,這樣就算圖像識別不能達到30幀,動畫渲染也可流暢運行,對於性能比較差的電腦,用戶識別圖像的過程可能會比較慢,但不會影響動畫播放。
然後說動畫, 。 。 。其實並不想說動畫,由於瀏覽器天生的優越性,無論是WebGL,Canvas2D或者SVG動畫都能在現代瀏覽器中行雲流水。但是要作出驚豔的動畫還是需要一定的技術積累。
神經網絡主要進行圖像分類是很好用的,但是它不是真正的圖像識別。我們的應用可能會在Maker上顯示一個3d模型,當Maker位置發生變化的時候,3d模型也跟著位移。主流的AR應用採用SIFT算法實現此功能,這個算法計算量很大,JS扛不住這樣的實時計算,不過這個算法有GPU實現版本,如果哪個大牛能用WebGL實現SIFT,那麼將對WebAR產生歷史性地推動作用。
這個Demo已經站在技術時代的前沿了-.-;;結合機器學習技術實現了AR。 Web端因為各種MV*框架始終代表著UI界面的先進生產力,但是到了圖像圖形這一塊就簡直是石器時代,基本的算法都沒有,運算性能還特別差,甚至現在的iOS還不能調用WebRTC。但外國有句名言:“凡是能寫成JS的最終都會被寫成JS”,畢竟WebAR所需要的技術都已經到位了,WebRTC提供了攝像頭的能力,WebGL提供了並行計算能力,WebWorker提供了多線程能力,現在就差相應的高效率算法了,而這些算法也是在Native中已經成熟了,不久的將來也一定會被翻譯成JS。 WebAR火起來只是時間的問題~~也許哪天你的React代碼裡面就會這樣寫
render(){ return <ARCamera size={size}></ARCamera> }
當然我個人的技術視野也是有限的,如果您有更好的算法或者相關技術鏈接請在評論指出,也麻煩您點個贊!