uoz 作業日記

様々な作業の記録を共有するブログです。

HTML5 canvas要素を利用して、Webカメラで撮影した画像をOpenCVで画像処理して表示するRailsアプリを作ってみた

ブラウザとwebカメラで遊べるので気軽で楽しい。

f:id:uoz:20140225152102p:plain

f:id:uoz:20140225152108p:plain

概要

Webカメラで撮影した画像を取得できるWeb RTC APIを使って、
javascriptで画像を取得。

それをtoDataURLメソッドでバイナリ文字列に変換し、
ajaxrailsのサーバ側に送る。

railsは受け取った画像をOpenCVで処理してクライアントに返す。
クライアントは受け取った画像を表示する。

公開コード
https://saoyagi@bitbucket.org/saoyagi/pose2.git



*このアプリは人のいろんなポーズを集めるために作ったので、
Poseというキーワードがいろんな所に出てくる。

関係サイト
ヒューマンインタフェース学会若手の会
HI2013ワークショップ
http://www.his.gr.jp/_sig/hist.html


事前準備

前提として、画像処理が簡単にできるライブラリOpenCVと、
それをrubyから使えるようにするラッパーであるruby-opencvをインストールしておかないとダメ


OpenCV公式
http://opencv.org/

OpenCVのインストールの参考になるサイト
http://www.kkaneko.com/rinkou/opencv/opencvinstalllinux.html

ruby-opencvgithubページ
https://github.com/ruby-opencv/ruby-opencv

ruby-opencvインストールの参考になるサイト
http://www.kkaneko.com/rinkou/ruby/rubyopencv.html
http://ser1zw.hatenablog.com/entry/2013/01/29/235320

私はmaccentosでやってるがどっちも普通に動いた

重要部分のコード

Webカメラの画像を取得するjavascriptコード

参考
http://davidwalsh.name/browser-camera
http://slowquery.hatenablog.com/entry/2013/01/30/001310

htmlファイルに

 <video id="video" autoplay></video>

というvideo要素を作ったとすると、そこにwebカメラからのリアルタイム入力動画を出力するのは
下のようなコードがあれば良いらしい。

Pose.videoElement = $("#video").get(0);
    var videoObj = { "video": true };
    var falseCallback = function(error) {
        console.log("Video capture error: ", error.name);
    };

navigator.getUserMedia(videoObj, function(stream) {
   Pose.videoElement.src = stream;
   Pose.videoElement.play();
}, falseCallback);

video要素と、謎の{video:true}という内容のオブジェクト、それに取得失敗時のコールバックの3つを用意し、
navigator.getUserMediaメソッドを呼び出す。
成功時コールバックでは、引数としてもらえるstreamオブジェクトをvideo要素のsrcにセットして、
play()メソッドを呼ぶ。


実際にはブラウザごとの違いとかがあって以下のように書く。

function initCamera(){
    Pose.videoElement = $("#video").get(0);
    var videoObj = { "video": true };
    var falseCallback = function(error) {
        console.log("Video capture error: ", error.name);
    };

    Pose.videoElement.setAttribute("width",Pose.MAGE_WIDTH);
    Pose.videoElement.setAttribute("height",Pose.IMAGE_HEIGHT);

	//ビデオリスナーのセット
    if(navigator.getUserMedia) { // Standard
        navigator.getUserMedia(videoObj, function(stream) {
            Pose.videoElement.src = stream;
            Pose.videoElement.play();
        }, falseCallback);
    } else if(navigator.webkitGetUserMedia) { // WebKit-prefixed
        navigator.webkitGetUserMedia(videoObj, function(stream){
            Pose.videoElement.src = window.webkitURL.createObjectURL(stream);
            Pose.videoElement.play();
        }, falseCallback);
    } else if(navigator.mozGetUserMedia) { // Firefox-prefixed
        navigator.mozGetUserMedia(videoObj, function(stream){
            Pose.videoElement.src = window.URL.createObjectURL(stream);
            Pose.videoElement.play();
        }, falseCallback);
    }

}


そこから画像をとるには、document.createElement('canvas')でcanvas要素を作成し、
canvas.getContext("2d")でコンテクストというよく分からんオブジェクトとをもらって、
そいつのdrawImageメソッドの第一引数にセットして呼び出すだけ
canvasの中に画像が入るので、あとはcanvasをどっかの要素にappendしたりすれば良い

実際にはcanvasのサイズとかを指定しないと行けないのでこんな感じ

    var canvas = document.createElement('canvas');
    canvas.setAttribute("width", Pose.IMAGE_WIDTH);
    canvas.setAttribute("height", Pose.IMAGE_HEIGHT);
    context = canvas.getContext("2d"),
    context.drawImage(Pose.videoElement, 0, 0, Pose.IMAGE_WIDTH, Pose.IMAGE_HEIGHT);
画像のバイナリ文字列をなんか配列みたいなのに変換していろんな処理するrubyコード

疲れたので参考ページのみ



参考
ruby-opencvのリファレンス
http://rubydoc.info/gems/ruby-opencv/frames