Video(Webカメラ)の映像をCanvasに描画するライブラリを書いた

投稿日:

というわけでVideo(Webカメラ)の映像をCanvasに描画するライブラリを書いてみました。

リポジトリはここです。
https://github.com/shimabox/v2c

デモ

v2c(video2canvas) example

デモでおこなっているエフェクトは

のロジックを利用させていただいています。
(自分でもなにか考えます。。)

これはなに?

冒頭で述べましたが、Video(Webカメラ)の映像をCanvasに描画するライブラリです。
※正確にはWebカメラの映像をvideoタグに反映してそれをcanvasに描画するものになります

Webカメラで映しているものに対してサクッとなにかをしたい

というニーズ(があればそれ)に応えられるものになっていると思います。

特徴

  • Webカメラの映像をcanvasにサクッと描画できます
    • Webカメラ必須です
    • スマホの場合はスマホのカメラが使えます
  • callbackでcanvasを参照できるので好きなエフェクトがかけられます
  • 前面カメラと背面カメラ(あれば)の切り替えが可能です
  • canvasに映っているものをpng画像で出力できます
    • 無音でキャプチャ可能です(悪いことに使っちゃダメだぞ)
    • ただし画質は悪いです
  • data: URL として出力する機能も持っています

なんでつくったのか

以前にこういった簡単な顔認識をするプログラムを書いて遊んでみたのですが、Webカメラの映像をcanvasに映す部分を書くのが毎度めんどくさかったので自分なりに抽象化した結果の副産物になります。

いちおう、Webカメラで映しているものに対してサクッとなにかをするという部分に関しては満たされているのではないかと思っています。

使い方

以下にサクッと使い方を書いていきます。

基本的な使い方

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>

<!-- Prepare a wrapper element -->
<div id="v2c-wrapper"></div>

<!-- Load v2c.js -->
<script src="js/v2c.js"></script>
<script>
// Create instance.
// Wrapper element selector is required.
// `option` is optional.
const v2c = new V2C('#v2c-wrapper'/*, option */);

// Start.
v2c.start();
</script>
</body>
</html>
  • videoタグとcanvasタグをラップする要素が必要です
  • インスタンスを生成するときにラッパー要素のセレクタが必要になります
  • インスタンスを生成したらstart()でcanvas要素に描画します
    • カメラ使用許可を求められるので許可してください
  • optionは後述します

ラッパー要素の中身

new V2C('#v2c-wrapper') でインスタンスを生成すると、指定したラッパー要素の中に以下の要素が作られます。

<div id="v2c-wrapper" style="display: flex; flex-direction: column; width: min-content;">
  <video width="640" height="320" id="video" playsinline autoplay style="position: absolute; z-index: -1; transform: scaleX(-1);"></video>
  <canvas width="640" height="320" id="canvas" style="transform: scaleX(-1);"></canvas>
</div>

video要素の上にcanvas要素を被せるっていう寸法です。
※サイズは後述しますが変更可能です

なぜラッパー要素が必要なのか

video要素はdisplay:noneとか、visibility:hiddenでうまく隠せばいいじゃんと思うかもしれませんが、自分が試したところiOSとかだとvideo要素が可視化というかきちんと見えている場所にないと(うまく説明できない)、映像が出力されませんでした。※canvas要素が真っ黒になる

そのため上記の方法をとっています。

描画停止

v2c.stop();

キャプチャ

v2c.capture();

こうすると、canvasに映っているものをpng画像として出力します。
ファイル名はcapture.pngになります。

もしファイル名を変更したい場合は以下の通りにします。

v2c.capture('modify'); // => modify.png

こうすると、ファイル名はmodify.pngとなります。

iOS(iPhone, iPad) はdata: URLで一旦画像が表示されるので、その画像を長押しして保存します。

描画中のcanvasを参照する

描画中のcanvas要素を参照する場合は、start()にcallbackを渡します。

v2c.start((canvas, t) => {
    // こんな感じでcanvasを参照できるのでゴニョゴニョできます
    // (`t`は最後の描画からの経過時間です)
    const ctx = canvas.getContext('2d');
    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imageData.data;
});

簡単な使い方は以上になりますが、インスタンス生成時にoption: objectを渡すと振る舞いを変更できるのでそれについて説明します。

Option

name default type
longSideSize 640 int
canvasId ‘canvas’ string
videoId ‘video’ string
useFrontCamera true boolean
constraintsForFront {video: {facingMode : 'user'}} object
constraintsForRear {video: {facingMode : {exact : 'environment'}}} object
callbackOnAfterInit null function
callbackOnOrientationChange null function
callbackOnLoadedmetadataVideo null function
callbackOnVideoLoadSuccess v2c._callbackOnVideoLoadSuccess function
callbackOnVideoLoadError v2c._callbackOnVideoLoadError function
callbackOnAfterVideoLoadError null function

それぞれ説明

longSideSize

  • canvasの長辺サイズを指定します
  • 短辺のサイズは長辺に対する比率に合わせて設定されます
  • ここでいう比率は video.videoWidthvideo.videoHeight の比率になります

例)

  • video.videoWidthが、300px
  • video.videoHeightが、150px

となった場合、比率は2:1なのでlongSideSizeで600と指定すると

  • canvas.widthが、600px
  • canvas.heightが、300px

となります。

canvasId

  • canvas要素のID名を指定します

videoId

  • video要素のID名を指定します

useFrontCamera

  • 最初に使うカメラを指定できます
  • true
    • trueにすると最初にフロント(前面)カメラを使用します
  • false
    • falseにすると最初にリア(背面)カメラを使用します

constraintsForFront

constraintsForRear

callbackOnAfterInit

  • 初期化処理後に呼ばれるcallback関数を指定します
  • v2cインスタンスが引数として渡されます
    const _callbackOnAfterInit = function(v2c) {
        console.log(v2c);
    }
    

callbackOnOrientationChange

  • orientationchangeイベントが発生したときのcallback関数を指定します
    const _callbackOnOrientationChange = function() {
        // Do something.
    }
    

callbackOnLoadedmetadataVideo

  • loadedmetadataイベントが発生したときのcallback関数を指定します
  • video要素が引数として渡されます
    const _callbackOnLoadedmetadataVideo = function(video) {
        console.log(video);
    }
    

callbackOnVideoLoadSuccess

  • videoの読み込み(Webカメラからの)に成功したときに呼ばれるcallback関数を指定します
  • canvas要素が引数として渡されます
    const _callbackOnVideoLoadSuccess = function(canvas) {
        console.log(canvas);
    }
    

callbackOnVideoLoadError

  • videoの読み込み(Webカメラからの)に失敗したときに呼ばれるcallback関数を指定します
  • エラーメッセージ, canvas要素, フロントカメラを利用しているかどうかのフラグ, callback(callbackOnAfterVideoLoadError) が引数として渡されます
    const _callbackOnVideoLoadError = function(err, canvas, useFrontCamera, callback) {
        // `callbackOnAfterVideoLoadError`が指定されていない場合、callbackはnullです
        console.log(err, canvas, useFrontCamera, callback);
    }
    

callbackOnAfterVideoLoadError

  • videoの読み込み(Webカメラからの)に失敗した後に呼ばれるcallback関数を指定します
  • エラーメッセージが引数として渡されます
    const _callbackOnAfterVideoLoadError = function(err) {
        console.log(err);
    }
    

Example.

// Callback when after initialization.
const _callbackOnAfterInit = function(v2c) {
    console.log(v2c);
}

// Callback when failed to load video.
const _callbackOnVideoLoadError = function(err, canvas, useFrontCamera) {
    console.log(err, canvas, useFrontCamera);
}

// Override option.
const option = {
    'longSideSize': 360,
    'useFrontCamera': false, // 最初からリア(背面)カメラを使う場合
    'callbackOnAfterInit': _callbackOnAfterInit,
    'callbackOnVideoLoadError': _callbackOnVideoLoadError
};

const v2c = new V2C('#v2c-wrapper', option);
// もちろん直接指定できます
const v2c = new V2C('#v2c-wrapper', {'useFrontCamera': false});

v2c.start();

Public Function

start()

  • 描画を開始します
  • コールバック関数を指定して、描画中のcanvas要素を参照することができます
    v2c.start((canvas, t) => {
        // こんな感じでcanvasを参照できるのでゴニョゴニョできます
        // (`t`は最後の描画からの経過時間です)
        const ctx = canvas.getContext('2d');
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const data = imageData.data;
    });
    

stop()

  • 描画を停止します

getCanvas() :HTMLElement

  • canvas要素を返却します

getVideo() :HTMLElement

  • video要素を返却します

useFrontCamera() :boolean

  • フロントカメラを使っているかどうかのフラグを返します
  • trueであれば、フロントカメラを利用していることになります
  • falseであれば、リアカメラを利用していることになります

switchCamera()

  • フロントカメラとリアカメラを切り替えます

changeLongSideSize(size: int)

  • size は必須です
  • canvasの長辺サイズを変更します
    • 短辺のサイズは長辺に対する比率に合わせて設定されます
    • ここでいう比率は video.videoWidthvideo.videoHeight の比率になります

getDataUrl() :string

  • canvas のイメージに対する data: URL を返します

capture(filename: string)

  • canvasをpng画像に変換します
  • filename はオプションです
    • ファイル名を変えたい場合は、引数でファイル名を指定してください
    • デフォルトは'capture'(capture.png)です

おわりに

Webカメラで映しているものに対してサクッとなにかをしたい 人がいればぜひ使ってみてください。(自分は先人のエフェクトを利用しましたが)独自のエフェクトをかけたりとか、ある周期で画像をサーバーに送ってなにかゴニョゴニョするとか活用できるかと思います。

あと、特徴でも書いていますがキャプチャは無音で実行できるので悪いことに使っちゃだめですよ。

ではでは。

作成者: shimabox

Web系のプログラマをやっています。 なるべく楽しく生きていきたい。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください