成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用JavaScript實現Motion Detection

移動開發
本篇文章作者Romuald Quantin,他成為一個前端開發者已經13年有余,在過去的13年中,他一直嘗試動畫、視頻等方面的開發。Romuald在音樂、藝術欣賞、聲音設計和音樂方面都頗有造詣,這些使他在開發過程當中受益匪淺,下面我們就來看一下他是如何使用JavaScript實現Motion Detection的吧。

Romuald Quantin

一、知識必備

初、中級水平的JavaScript

初、中級水平的HTML5

對jQuery有基本了解

由于使用getUserMdedia和Audio API,Demo需要使用Chrome Canary,并通過本機Web服務器運行。

第三方必備產品

Chrome Canary

本機Web服務器,例:Xampp(Mac,Windows,Linux)Mamp(Mac)

Webm格式文件的視頻解碼器,例:

Online ConVert Video converter to convert to the WebM format (VP8)

Mp4格式文件的視頻解碼器,例:

· Adobe Media Encoder

· Adobe After Effects

· HandBrake

Ogg格式文件的視頻解碼器,例:

Easy HTML5 Video

二、示例文件

javascript_motion_detection_soundstep.zip

我會在本篇文章中,跟大家討論一下如何在JavaScript中使用webcam stream追蹤用戶的操作。Demo中展示了從用戶的網絡攝像頭中搜集到的一段視頻,用戶可以實時地在使用HTML開發的木琴上彈奏,就像彈奏真正的樂器一樣。這個Demo是使用HTML5中的兩個“仍需改進”的接口開發的,它們是:getUserMedia API,用來調用用戶的網絡攝像頭,和Audio API,用來彈奏木琴。

同時,我也給大家展示了如何使用混合模式來捕獲用戶的操作。幾乎所有提供了圖片處理接口的語言都提供了混合模式。這里引用Wikipedia評論混合模式的一句話,“數字圖像編輯中的混合模式通常用來判斷兩個圖層是否能夠融合在一起。”JavaScript本身不支持混合模式,而混合模式不過是對像素進行的數學運算,因此我創建了一個混合模式“difference”。

我制作了一個Demo,用來展示web技術的的發展方向。JavaScript和HTML5將會為新型的交互式Web應用提供接口。這是多么激動人心的一件事啊!

  HTML5 getUserMedia API

一直到我寫這篇文章的時候,getUserMedia API仍在完善中。W3C第5次發布的HTML,新增了相當多的接口,用于訪問本機硬件。navigator.getUserMediaAPI()接口提供了一些工具,使網站能夠捕獲音頻和視頻。

一些技術博客中有很多關于如何使用這些接口的文章,所以,這里我不再贅述。如果你想學習更多相關的內容,文章的最后我給大家提供了一些有關getUserMedia的鏈接。下面,我們一起來看一下我是如何使用該接口做出示例中的內容的。

目前,getUserMedia API只能在Opera 12和Chrome Canary中使用,并且兩款瀏覽器都沒有公開發布。針對該示例,你只能使用Chrome Canary,因為該瀏覽器允許使用AudioContext彈奏木琴。Opera暫還不支持AudioContext。

Chrome Canary 完成安裝并啟動,然后啟用API。在地址欄中輸入:about:flags。在啟用Media

Figure 1. Enable MediaStream.

圖1. 啟用Media Stream

最后,由于安全限制,攝像機不允許以本地文件:///的形式進行訪問,你必須使用自己的本機web服務器運行示例文件。

現在萬事俱備,只需添加一個視頻標簽用來播放攝像頭流媒體文件。使用JavaScript把接收到的流媒體文件添加到視頻標簽的src屬性中。

  1. <video id=”webcam” autoplay width=”640″ height=”480″></video> 

在JavaSript代碼段中,首先要做兩項準備工作,第一項是確保用戶的瀏覽器能夠使用getUserMedia API。

  1. function hasGetUserMedia() { return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); } 

第二項工作就是嘗試從用戶的網絡攝像頭中獲取流媒體文件。

注:在這篇文章和示例中,我使用的是jQuery框架,但是你可以根據自己的需要選擇其它框架,例如,本地文件.querySelector

  1. var webcamError = function(e) { 
  2.  
  3. alert(‘Webcam error!’, e); 
  4.  
  5. }; 
  6.  
  7. var video = $(‘#webcam’)[0]; 
  8.  
  9. if (navigator.getUserMedia) { 
  10.  
  11. navigator.getUserMedia({audio: true, video: true}, function(stream) { video.src = stream
  12.  
  13. }, webcamError); } 
  14.  
  15. else if (navigator.webkitGetUserMedia) { navigator.webkitGetUserMedia(‘audio, video’, function(stream) { video.src = window.webkitURL.createObjectURL(stream); 
  16.  
  17. }, webcamError); 
  18.  
  19. } else { 
  20.  
  21. //video.src = ’video.webm’; // fallback. 
  22.  

現在你已經可以在HTML頁面中展示網絡攝像頭流媒體文件了。在下一部分中,我將為大家介紹總體結構以及示例中用到的一些資源。

如果你希望在自己的程序中使用getUserMedia,我推薦以下Addy Osmani的作品。Addy使用Flash fallback創建了getUserMedia API的shim。

 Blend mode difference

接下來,我給大家展示的是使用blend mode difference來檢測用戶的操作。

把兩張圖片融合在一起就是對像素值進行加減計算。在Wikipedia的文章中有關混合模式的部分對difference進行了描述。“兩個圖層相減取其絕對值作為Difference,任何圖片和黑片進行融合,所有顏色的值都為0.”

為了實現這個操作,首先需要兩張圖片。代碼反復計算圖片的每一個像素,第一張圖片的色彩通道值減去第二上的色彩通道值。

比如說,兩張紅色的圖片的每個像素中的色彩通道值為:

-紅色:255(0xFF)

-綠色:0

-藍色:0

下面的操作表示從這些圖片減去這些顏色的值:

-紅色:255-255=0

-綠色:0-0=0

-藍色:0-0=0

換句話說,把blend mode difference應用于兩張相同的圖片會產生一張黑色的圖片。下面我們來討論一下blend mode difference的實用性和圖片的獲取途徑。

我們逐步地完成這個過程,首先設定一個時間間隔,使用canvas元素畫出webcam stream中的一張圖片,如示例中所示,我設定為1分鐘畫60張圖片,這個數量遠遠超過你所需要的。當前顯示在webcam stream中的圖片是你要融合的圖片中的第一張。

第二張圖片是從webcam中捕獲的另外一張圖片。現在就有了兩張圖片,要做的就是對它們的像素值進行差值計算。這意味著,如果兩張照片是相同的—換句話說,如果用戶沒有任何操作—運算結果會是一張黑色的圖片。

當用戶開始操作,奇妙的事情發生了。當前時間間隔獲取到的圖片與在之前的時間間隔中獲取到圖片相比,有細微的變化。如果對不同的像素值進行差值計算,一些顏色出現了。這意味著這兩幀圖畫之間發生了變化。

[[71207]]

圖2.融合后的兩張圖片

Motion detection這個過程就完成了。最后一步就是反復計算融合后的圖片中的所有像素,是看否存在不為黑色的像素。   

  必備資源

為了運行這個Demo,還需要一般網站的必備元素。首先,你需要一個包含了canvas和video tags 元素的HTML頁面,還需要JavaScript來運行這個應用程序。

然后,還需要一張木琴的圖片,最好是透明背景(png格式)。另外,還需要木琴每個按鍵的圖片,每一張圖片都需要有輕微的變化。這樣使用rollover 效果,這些圖片就可以給用戶視覺上的反饋,彈奏的琴鍵會高亮顯示。

我們還需要一個音頻文件,包含了每一個音符的聲音,mp3文件即可。如果彈奏木琴沒有聲音就會沒有任何樂趣可言了。

最后,這時候,需要新創建一個視頻,把它解碼成MP4格式,下一節,我將為大家介紹一些用來解碼視頻的工具,你可以在Demo的zip文件中找到所有的資源。

  解碼HTML5視頻文件

把視頻Demo后備文件解碼成用來展示HTML5視頻文件常見的三種格式。

在把文件解碼成Webm格式的過程中,我遇到了點麻煩,我選擇的解碼工具不允許選擇碼率,而碼率恰恰決定了視頻的大小和質量,通常被稱作kbps(千比特每秒)。我不再使用Online ConVert Video轉換器來轉換WebM格式(VP8),這個問題迎刃而解:

MP4格式:你可以使用Adobe Creative Suite中的工具,例如Adobe Media Encoder或者After Effects。或者你可以使用免費的視頻轉換軟件,如Handbrake。

Ogg格式:我通常使用一些工具一次性轉換所有格式。我對解碼后生產webm和MP4格式視頻的質量感覺不是很滿意,雖然我沒辦法更改輸出的質量,而Ogg格式的視頻的質量比較樂觀。你可以使用Easy HTNL5 Video或者Miro等轉換器。

Demo中用到的HTML代碼段

這是開始JavaScript編碼之前的最后一步。添加HTML標簽,下面是需要用到的HTML標簽(我不會列出我用到的所有內容,比如說視頻Demo中的fallback代碼,你可以在資源中下載得到。)

這里需要一個簡單的視頻標簽,用來接收用戶的webcam feed。不要忘記設計自動播放屬性,如果忘記了,stream會暫停在接收到的第一幀畫面上。

  1. <video id=”webcam” autoplay width=”640″ height=”480″></video> 

視頻標簽不會顯示出來,它的CSS顯示樣式被設置成none,另外,添加一個canvas元素,用來繪制webcam stream。

  1. <canvas id=”canvas-source” width=”640″ height=”480″></canvas> 

這時還需要另外添加一個canvas用來顯示motion detection中實時的變動。

  1. <canvas id=”canvas-blended” width=”640″ height=”480″></canvas> 

下面添加一個DIV標簽,把木琴的圖片放入其中。把木琴放到webcam的上部:用戶可以身入其境地彈奏木琴。把音符放在木琴的上方,將其屬性設置為隱藏,當音符被觸發的時候,將會顯示在rollover中。

  1. <div id=”xylo”> 
  2.  
  3. <div id=”back”><img id=”xyloback” src=”images/xylo.png”/></div>   <div id=”front”> <img id=”note0″ src=”images/note1.png”/> <img id=”note1″ src=”images/note2.png”/> <img id=”note2″ src=”images/note3.png”/> <img id=”note3″ src=”images/note4.png”/> <img id=”note4″ src=”images/note5.png”/> <img id=”note5″ src=”images/note6.png”/> <img id=”note6″ src=”images/note7.png”/> <img id=”note7″ src=”images/note8.png”/> </div> </div> 

  JavaScript motion detection

使用以下JavaScript代碼段完成Demo

確認應用是否可以使用getUserMedia API。

確認已接收webcam stream

載入木琴琴譜的音頻

啟動時間間隔,并調用Update函數

在每一個時間間隔中,在canvas中繪制一張webcam feed。

在每一個時間間隔中,把當前的webcam中的圖片和之前的一張融合在一起。

在每一個時間間隔中,在canvas中繪制出融合后的圖片。

在每一個時間間隔中,在木琴的琴譜中檢查像素的顏色值。

在每一個時間間隔中,如果發現用戶進行操作,彈奏出特定的木琴音符。

 第一步:聲明變量

聲明一些用于存儲drawing和motion detection過程中使用到的變量。首先,需要兩個對canvas元素的引用,一個用來存儲每個canvas上下文

的變量,一個用來存儲已繪制的webcam stream。也需要存儲每個音符x軸線的位置,彈奏的樂譜,和一些與聲音相關的變量。

  1. var notesPos = [0, 82, 159, 238, 313, 390, 468, 544]; 
  2.  
  3. var timeOut, lastImageData; 
  4.  
  5. var canvasSource = $(“#canvas-source”)[0]; 
  6.  
  7. var canvasBlended = $(“#canvas-blended”)[0]; 
  8.  
  9. var contextSource = canvasSource.getContext(’2d’); 
  10.  
  11. var contextBlended = canvasBlended.getContext(’2d’); 
  12.  
  13. var soundContext, bufferLoader; 
  14.  
  15. var notes = []; 

同時還需要倒置webcam stream的軸線,讓用戶感覺自己是在鏡子前。這使得他們的彈奏更順暢。下面是完成該功能需要的代碼段: 

contextSource.translate(canvasSource.width, 0);          contextSource.scale(-1, 1);

  第二步:升級和視頻繪制

創建一個名為Update的函數,并設定一秒鐘執行60次,其中調用其它一些函數。

  1. function update() { 
  2.  
  3. drawVideo(); 
  4.  
  5. blend(); 
  6.  
  7. checkAreas(); 
  8.  
  9. timeOut = setTimeout(update, 1000/60); 
  10.  

把視頻繪制到canvas中非常簡單,只有一行代碼

  1. function drawVideo() { 
  2.  
  3. contextSource.drawImage(video, 0, 0, video.width, video.height); } 

 第三步:創建blend mode difference

創建一個幫助函數,確保像素相減得到的值務必為正值。你可以使用內置函數Math.abs,不過我用二進制運算符寫了一個差不多的函數.大部分情況下,使用二進制運算結構能夠提升性能,你不一定要了解其中的細節,只要使用以下的代碼即可:

  1. function fastAbs(value) { 
  2.  
  3. // equivalent to Math.abs(); return (value ^ (value >> 31)) - (value >> 31); 
  4.  

下面我們寫一個blend mode difference函數,這個函數包含三個形參:

一個二維數組用來存儲減法得到的結果

一個二維數組存儲當前webcam stream中圖片的像素

一個二維數組存儲之前webcam stream中圖片的像素

存儲像素的數組是二維的,并且還包含了Red,Green,Blue和Alpha等通道值.

· pixels[0] = red value

· pixels[1] = green value

· pixels[2] = blue value

· pixels[3] = alpha value

· pixels[4] = red value

· pixels[5] = green value

等等……

如示例中所示,webcam stream的寬為640像素,高為480像素。因此,數組的大小為640*480*4=1228000.

循環處理這些像素數組的最好方式是把值擴大4倍(紅、綠、藍、Alpha),這意味著現在只需要處理 307,200 次–情況更加樂觀一些。

  1. function difference(target, data1, data2) { 
  2.  
  3. var i = 0
  4.  
  5. while (i < (data1.length / 4)) { 
  6.  
  7. var red = data1[i*4]; 
  8.  
  9. var green = data1[i*4+1]; 
  10.  
  11. var blue = data1[i*4+2]; var alpha = data1[i*4+3]; ++i; 
  12.  
  13.  

現在你可以對圖片中的像素進行減法運算—從效果上來看,會有大不同—如果顏色通道值已經為0,停止減法運行,并自動設置Alpha的值為255(0xFF)。下面是已經完成的blend mode difference函數(可以進行自主優化):

function difference(target, data1, data2) { // blend mode difference if (data1.length != data2.length) return null; var i = 0; while (i < (data1.length * 0.25)) { target[4*i] = data1[4*i] == 0 ? 0 : fastAbs(data1[4*i] - data2[4*i]); target[4*i+1] = data1[4*i+1] == 0 ? 0 : fastAbs(data1[4*i+1] - data2[4*i+1]); target[4*i+2] = data1[4*i+2] == 0 ? 0 : fastAbs(data1[4*i+2] - data2[4*i+2]); target[4*i+3] = 0xFF; ++i; } }

我在Demo中使用的是一個稍微不同的版本,以取得更好的準確性。我寫了一個threshold函數,應用于顏色值。當顏色值低于臨界值的時候,該方法把顏色像素值更改為黑色,而高于臨界值時,將顏色像素值更改為白色。你可以使用以下的代碼段完成該功能。

  1. function threshold(value) { 
  2.  
  3. return (value > 0×15) ? 0xFF : 0; 
  4.  

同樣我也計算了三種顏色通道的平均值,它們在圖片中的像素為黑色或白色 。

  1. function differenceAccuracy(target, data1, data2) 
  2.  
  3. if (data1.length != data2.length) return null
  4.  
  5. var i = 0; 
  6.  
  7. while (i < (data1.length * 0.25)) { 
  8.  
  9. var average1 = (data1[4*i] + data1[4*i+1] + data1[4*i+2]) / 3; 
  10.  
  11. var average2 = (data2[4*i] + data2[4*i+1] + data2[4*i+2]) / 3; 
  12.  
  13. var diff = threshold(fastAbs(average1 - average2)); target[4*i] = diff; 
  14.  
  15. target[4*i+1] = diff; 
  16.  
  17. target[4*i+2] = diff; target[4*i+3] = 0xFF; ++i; } 
  18.  

結果如下面的黑白圖片所示:

[[71208]]

圖3.blend mode difference的結果以黑白圖片顯示

  第4步:blend canvas

下面我們創建一個函數,用來融合圖片,你只需要把正確的值傳給它:像素數組。

JavaScript的繪圖API提供了一個方法,。這個對象包含了很多有用的屬性,比如寬度和高度,同樣還有數據屬性,正是你需要用到的像素數組。同樣,創建一個空的ImageData示例,用來存儲結果,并把當前的webcam圖片存儲下來,以便重復使用。下面是在canvas中融合并繪制結果所用到的代碼段:

  1. function blend() { 
  2.  
  3. var width = canvasSource.width; 
  4.  
  5. var height = canvasSource.height; 
  6.  
  7. // get webcam image data 
  8.  
  9. var sourceData = contextSource.getImageData(0, 0, width, height); 
  10.  
  11. // create an image if the previous image doesn’t exist  
  12.  
  13. if (!lastImageData) lastImageData = contextSource.getImageData(0, 0, width, height); 
  14.  
  15. // create a ImageData instance to receive the blended result 
  16.  
  17. var blendedData = contextSource.createImageData(width, height); 
  18.  
  19. // blend the 2 images 
  20.  
  21. differenceAccuracy(blendedData.data, sourceData.data, lastImageData.data); 
  22.  
  23. // draw the result in a canvas contextBlended.putImageData(blendedData, 0, 0); 
  24.  
  25. // store the current webcam image lastImageData = sourceData; 
  26.  

第5步:search for pixel

這是完成Demo的最后一步,這就是motion detection,這里用到上一節我們創建的融合的圖片。

在準備階段,放入了8張木琴音符的圖片。用這些音符的位置和大小作融合后圖像的像素。然后,重復找出白色的像素。

在重復的過程中,計算出顏色通道的平均值,使用變量存儲計算結果。重復過程完成之后,計算出整張圖片的像素的總平均值。

通過設定臨界值10來避免噪音和一些細微的操作,如果你發現大于10的數值,則應當考慮到在最后一張圖像之后,用戶進行了操作。這就是運動追蹤。

然后,播放出相對應的音符,并展示出note rollover。此處用到的函數如下所示:

  1. function checkAreas() { 
  2.  
  3. // loop over the note areas 
  4.  
  5. for (var r=0; r<8; ++r) 
  6.  
  7.  
  8. // get the pixels in a note area from the blended image 
  9.  
  10. var blendedData = contextBlended.getImageData 
  11.  
  12. ( notes[r].area.x, 
  13.  
  14. notes[r].area.y, 
  15.  
  16. notes[r].area.width, 
  17.  
  18. notes[r].area.height); 
  19.  
  20. var i = 0; 
  21.  
  22. var average = 0; 
  23.  
  24. // loop over the pixels 
  25.  
  26. while (i < (blendedData.data.length / 4)) { 
  27.  
  28. // make an average between the color channel 
  29.  
  30. average += (blendedData.data[i*4] + blendedData.data[i*4+1] + blendedData.data[i*4+2]) / 3; 
  31.  
  32. ++i; } 
  33.  
  34. // calculate an average between of the color values of the note area 
  35.  
  36. average = Math.round(average / (blendedData.data.length / 4)); 
  37.  
  38. if (average > 10) { 
  39.  
  40. // over a small limit, consider that a movement is detected 
  41.  
  42. // play a note and show a visual feedback to the user 
  43.  
  44. playSound(notes[r]); 
  45.  
  46. notes[r].visual.style.display = ”block”; 
  47.  
  48. $(notes[r].visual).fadeOut(); } } 

相關鏈接

我希望這篇文章可以帶給你更多的靈感,開發出基于視頻的交互程序,

通過以下的Chrome和Opera瀏覽器的開發和W3C網站上有關API的草案,你可以學到更多有關getUserMedia接口的知識。

如果你想找到更多關于motion detection或者基于視頻的應用的靈感,我推薦你遵照以下一些Flash開發人員和motion設計者使用After Effects得出的意見。現在Java Script和HTML勢頭正勁,開發出了很多新的功能。要學習的內容有很多,你可以參考以下資源:

· Soundstep blog

· Blend modes

· GetUserMedia API

· GetUserMedia article to get started

· GetUserMedia shim

· 優秀的開發者博客,如何找到靈感以及寫出更好的代碼:

· Grant Skinner’s blog

· Keith Peter’s blog

· Motion 設計者的博客,Video Copilot

· ProVideo Coalition

· Motionographer

責任編輯:佚名 來源: Web App Trend
相關推薦

2013-02-22 10:11:42

leap motion體感交互

2020-04-29 14:40:19

JavaScript繼承編程語言

2009-08-03 17:31:26

.NET驗證控件

2011-08-19 13:51:12

2019-12-09 15:20:09

JavascriptPromise前端

2019-04-03 10:50:09

Javascript區塊鏈技術

2009-06-30 15:05:52

JSP數據JavaScript數

2013-02-22 10:27:31

leap motion體感交互

2021-03-12 10:01:24

JavaScript 前端表單驗證

2014-08-21 15:40:53

Material De真實動作

2021-04-29 06:14:49

CSS 文字動畫Motion Path

2020-12-07 13:48:48

EditorAndroid開發者

2021-03-27 11:02:04

JavaScript隊列編程語言

2020-03-27 22:18:55

JavaScript編程語言代碼

2014-07-16 13:36:30

MotionLinux監控

2021-04-02 10:01:00

JavaScript前端Web項目

2022-10-13 00:03:00

JavaScripSQL函數

2024-09-14 15:07:03

2020-08-10 14:46:30

JavaScriptStack

2021-05-09 19:41:35

JavaScript 前端同源通信
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九导航| 国际精品鲁一鲁一区二区小说 | 69精品久久久久久 | wwww.xxxx免费 | 日韩精品一区二区三区中文字幕 | 91国产在线播放 | 欧美九九九 | 国产综合网站 | 国产亚洲一区二区三区在线观看 | 久久婷婷色 | 欧美日韩中文在线 | 日韩欧美一区二区三区 | 搞黄网站在线观看 | 天天草av | 有码在线| 蜜臀网 | 精品国产一区二区三区性色 | 一区二区精品 | av网站免费| 成人免费观看男女羞羞视频 | 成人久久18免费网站 | 久久国产精99精产国高潮 | 亚洲男人的天堂网站 | 亚洲精品一区二区三区中文字幕 | 久久这里有精品 | av男人的天堂在线 | 中文字幕国产第一页 | 亚洲一区二区在线播放 | 国产99久久精品一区二区300 | 国产伦精品一区二区三区照片91 | 黄色在线| 久久精品国产99国产精品 | 一区2区| 国产精品一区二区久久 | 自拍视频国产 | 国产成人久久精品一区二区三区 | 日本aa毛片a级毛片免费观看 | 久久国产综合 | 国产精品久久久久久久久久久久久久 | 天天干天天玩天天操 | 综合久久久久久久 |