【創新實訓7】手勢識別WEB端遷移整合

之前在Python上實現了最基礎的手勢方向的判定功能,因爲整體項目是WEB端的,所以需要向WEB平臺遷移...

通過查閱資料,opencv.js官方文檔還是給的比較全的,不過裏面部分函數的輸出實在是不太能看的明白....在後面實現的時候帶來的很大的困擾...甚至一度影響了進度..

1.頁面準備

在WEB頁面的攝像頭獲取基本邏輯和Python中的差不多,只不過在html中需要<video>和<canvas>兩個標籤,這兩個都是H5中的新標籤,<video> 標籤定義視頻,比如電影片段或其他視頻流而<canvas>標籤定義圖形,比如圖表和其他圖像。需要注意的是<canvas> 標籤只是圖形容器,必須使用腳本來繪製圖形.

所以一個基本的用於獲取攝像頭視頻流並展示的html頁面如下:

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title>Video Capture Example</title>
</head>

<body>
	<video id="videoInput" height=240 width=320></video>
	<canvas id="canvasFrame" height=240 width=320 ></canvas>
	<canvas id="FirstFrame" height=240 width=320 ></canvas>
	<canvas id="canvasOutput" height=240 width=320 ></canvas>
    <script src="js代碼邏輯" type="text/javascript"></script>
</body>

</html>

2.視頻捕獲

清楚了<video>和<canvas>兩個基本標籤以後,我們就可以開始寫js的邏輯了.

最簡單的視頻獲取如下:

主要是基本參數設置.

let video = document.getElementById("videoInput"); 
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
    .then(function (stream) {
    video.height = HEIGHT;
    video.width = WIDTH;
    video.srcObject = stream;
    video.play();
    }).catch(function (err) {
    console.log("An error occured! " + err);
});

3.視頻輸出

在獲取了視頻以後,我們後續的步驟是一系列的視頻圖像處理和輸出,所以首先解決輸出問題.

輸出的邏輯主要是獲取到Canvas,然後通過canvas容器得到上下文,drawImage.

對於processVideo中的內容,則是一個循環的調用,從而實現持續的視頻流輸出.否則只是輸出了單幀.

let canvasFrame = document.getElementById("canvasFrame"); // canvasFrame is the id of <canvas>
let context = canvasFrame.getContext("2d");
let src = new cv.Mat(height, width, cv.CV_8UC4);
let dst = new cv.Mat(height, width, cv.CV_8UC1);
const FPS = 30;
function processVideo() {
    let begin = Date.now();
    context.drawImage(video, 0, 0, width, height);
    src.data.set(context.getImageData(0, 0, width, height).data);
    cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
    cv.imshow("canvasOutput", dst); // canvasOutput is the id of another <canvas>;
    // schedule next one.
    let delay = 1000/FPS - (Date.now() - begin);
    setTimeout(processVideo, delay);
}
// schedule first one.
setTimeout(processVideo, 0);

結果展示:

 上圖是輸入,下圖是輸出,無任何區別.

4.視頻處理

在清楚了基本的視頻流輸入輸出以後,就按照之前Python中處理的邏輯遷移過來(這個過程是十分辛苦心酸的...在Python中的函數調用返回以及文檔都十分全面,網絡上的各類解答也很詳細,然而到了opencv.js以後...除了官方文檔,其他的信息和資料幾乎沒有,而且官方文檔的輸入輸出以及一些特殊的數據形式和python的區別還是不小的...用的時候十分頭疼)

4.1 基本處理

var frame = new cv.Mat(HEIGHT, WIDTH, cv.CV_8UC4);
cap.read(frame);
// cv.bilateralFilter(frame, frame, 5, 50, 100,cv.BORDER_DEFAULT)  // 雙邊濾波 gg
cv.flip(frame,frame,1)  // 反轉
var gray = new cv.Mat();
cv.cvtColor(frame, gray, cv.COLOR_RGBA2GRAY);
var ksize = new cv.Size(21,21);
cv.GaussianBlur(gray,gray, ksize ,0)
// console.log(firstFrame)

這部分主要是對圖像反轉,進行雙邊濾波,然後灰度化+高斯模糊,方便後面的幀間差分.

但是這個地方有一個問題一直沒解決,就是雙邊濾波! 參數的傳入是嚴格按照官方文檔寫的,但是報錯...(希望大家給出解決建議...)

___________________________________new

雙邊濾波問題解決,一個是順序問題.不知道爲什麼,必須要先灰度化才能做雙邊濾波 ,還有就是src和dst不能是同一個變量...

看來js和python之間還是有一些習慣上的差別..

4.2 幀間差分

每次記錄保存前一幀,然後和當前做幀間差分,隨後膨脹+二值化.這樣我們就可以捕獲當前畫面中正在移動的物體.

			if(firstFrame == null){
				console.log('enter')
				firstFrame = gray;
				setTimeout(processVideo,0)
			}
			cv.imshow("FirstFrame", frame); 
			// let frameDelta = new cv.Mat(HEIGHT, WIDTH, cv.CV_8UC1);
			var frameDelta = new cv.Mat();
			cv.absdiff(first    Frame, gray, frameDelta)  // 幀間差
			var thresh = new cv.Mat();
			cv.threshold(frameDelta,thresh, 25, 255, cv.THRESH_BINARY)
			let M = new cv.Mat();
			let anchor = new cv.Point(-1, -1);
			cv.dilate(thresh,thresh,M,anchor, 2)

這部分完成後效果如下:

當畫面發生變化,也就是有運動物體時,如下:

4.3 變化追蹤判斷

這部分邏輯很清晰,就是利用 goodFeaturesToTrack尋找到輪廓的中心點,然後可以根據中心點變化判斷運動方向

然而因爲goodFeaturesToTrack在js中...輸出十分混亂且沒看懂,所以問題先留到後面

_____________________________update

通過分析輸出的mat矩陣,如下圖可以發現.數值的變化是有規律的,最後經過測試發現2 6分別代表x,y的輸出座標.

有了座標以後就好做了,只需要計算座標變化,計算出運動方向即可.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章