普通自動車免許の学科試験に落ちました。アジャストの渕上です。
前回は、笑い男ビデオチャットを実装するための方法を検討しました。
今回は、その方法に沿ってコードを書いてみようと思います。
前提条件
node.jsとsocket.ioを利用して実装する
今回の目標
今回は下記を、ブラウザの更新なしで実現したいと思います。
- Webカメラで撮影しているデータをWebサーバに渡す
- Webサーバ上でOpenCVによる加工を行う
- 加工したデータをブラウザに返す
実践
1. Webサーバに送る準備、ローカルで動画をキャプチャする
まずはWebカメラで撮影している動画を、videoタグで再生して画像としてキャプチャします。
コード
下記のHTML+Javascriptで実現することができました
1. PC内蔵カメラで撮影した動画をvideo要素で再生
2. timeupdateイベント発生ごとにcanvasに転写
3. canvasの内容をdataURIに変換し、img要素のsrcに設定
しています
<html>
<head>
<script>
var video,
canvas,
localCapture;
function initialize(){
video = document.createElement('video');
video.setAttribute('width', 640);
video.setAttribute('height', 480);
canvas = document.createElement('canvas');
canvas.setAttribute('width', 640);
canvas.setAttribute('height', 480);
localCapture = document.getElementById('local-capture');
navigator.getUserMedia = ( navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
if (navigator.getUserMedia) {
navigator.getUserMedia (
{ video: true, audio: true },
function(localMediaStream) {
// カメラの動画をvideoタグで再生(src属性にセット)
video.src = window.URL.createObjectURL(localMediaStream);
// timeupdateイベントにリスナーを追加
video.addEventListener('timeupdate', webCameraTimeUpdate);
video.play();
},
function(err) {
console.log("The following error occured: " + err);
}
);
}
else {
console.log("getUserMedia not supported");
}
}
function webCameraTimeUpdate(){
// videoタグのキャプチャをcanvasに転写
var ctx = canvas.getContext('2d');
ctx.drawImage(video,0,0);
// キャンバスの内容をdataURLに変換して、img要素にセット
var data = canvas.toDataURL();
localCapture.setAttribute('src', data);
}
window.addEventListener('load', initialize);
</script>
</head>
<body>
<img id="local-capture">
</body>
</html>
実行結果
上記のコードをブラウザで実行した結果のキャプチャです。
わかりずらいのですが、img要素のsrcを切り替えつづけた結果、動画のように見えています。
(きたないおっさんの顔でスミマセン・・・)
2. キャプチャした画像をWebSocket(socket.io)で送信(クライアント側処理)
ここからはコードが長くなってきますので、部分的に紹介し、最後に一式を添付します。
下記は添付ファイルの ./template/index.html
の一部です。
先ほど定義したwebCameraTimeUpdateに処理を追加します。
コード
function webCameraTimeUpdate(){
// videoタグのキャプチャをcanvasに転写
var ctx = canvas.getContext('2d');
ctx.drawImage(video,0,0);
// キャンバスの内容をdataURLに変換して、img要素にセット
var data = canvas.toDataURL();
localCapture.setAttribute('src', data);
// socket.io を使って、dataURLをサーバに送信
socket.emit('video-update', data);
}
3. WebSocketで受け取った画像をOpenCVで加工して、クライアントに送信(サーバ側処理)
下記は添付ファイルの ./index.js
の一部です
コード
io.sockets.on('connection', function(socket){
// クライアントからvideo-updateを受け取る
socket.on('video-update', function(dataURL){
// dataURLのプリフィックスを削除してデータ部分だけをBase64デコードし、Bufferに変換する
var prefix = 'data:image/png;base64,';
var bitmap = dataURL.replace(prefix,'');
var buf = new Buffer(dataURL.replace(prefix,''), 'base64');
// openCVでバッファを読み込む
opencv.readImage(buf, function(err, im){
//
im.detectObject('./node_modules/opencv/data/haarcascade_frontalface_alt.xml', {}, function(err, faces) {
// 読み込んだ画像の顔部分を判別し、枠で囲う
for (var i = 0, max=faces.length; i < max; i++) {
var face = faces[i];
im.ellipse(face.x + face.width / 2, face.y + face.height / 2, face.width / 2, face.height / 2);
}
// 加工した画像を再度dataURL化し、クライアントに送信する
io.emit('capture-send', { dataURL: prefix+im.toBuffer().toString('base64')});
});
});
});
});
4. 加工された画像を受け取ってimg要素にセット(クライアント側処理)
コード
socket.ioで受け取ったdataURLを、img要素にセットします
下記は添付ファイルの ./template/index.html
の一部です。
socket.on('capture-send', function(data){
remoteCapture.setAttribute('src', data.dataURL);
});
実行結果
今回のコードの実行結果です。
向かって左が、サーバに送る前のカメラのキャプチャ画像
向かって右が、サーバで加工した画像になっています。
右側は、顔の部分が赤丸で囲われています。
今回作成したファイル一式
今回作成したファイル一式を添付します。
node.jsとOpenCVがインストールされている環境で実行できるかと思います。
ソースコード一式
次回予定
このテーマもずいぶん引っ張ってしまっているので、
次回でなんとか積み残し課題を解決したいと思います。
- 1対1での双方向送受信をできるようにする
- nodeのOpenCVモジュールでも笑い男画像でマスクできるようにする
です。2はOpenCVだけでは骨が折れるので、ほかのモジュールの利用も視野に入れておきます。
2016年3月17日 at 9:52 PM
こちらのサイトを参考にさせて頂いています
シンプルで分かりやすいです
ありがとうございます
[その3 node.js] ですが、 最終的なソース一式に index.html や index.js が入っていないようです できればこちらをUPしていただけないでしょうか
私は サーバーに設置した WEBカメラの画像を node.js + socket.io
でブロードキャストしたいと思っているのですが なかなかうまくいきません
多分このサイトが一番近いのかと思ってます
どうぞよろしくお願いいたします
阪本
2016年3月22日 at 9:58 PM
コメントいただきありがとうございます。この記事を書いたものです。
> 最終的なソース一式に index.html や index.js が入っていないようです できればこちらをUPしていただけないでしょうか
クライアント側のソース一式という理解でよろしいでしょうか。
templates/index.html がそれになっています。
サーバの”/path/to/test”にファイル一式をアップしたとすると、
でIPアドレスをご自身のサーバのIPに合わせた後、
でnodeを立ち上げ、”http://サーバのIP:8080/”にアクセスしてみていただけるでしょうか。
あと、この記事を書いたとき、勝手がわからずnode_moduleまでzipに含めていました。
無駄にファイル容量が大きくなっていますので、後日アップロードしなおしてみようと思います。
2016年11月6日 at 2:46 PM
すごい!やりたかったことが全部網羅されています!
ありがとうございます。