README仕上げてみました / https://t.co/ekKloLC9LH
— しまぶ (@shimabox) May 7, 2019
というわけでVideo(Webカメラ)の映像をCanvasに描画するライブラリを書いてみました。
リポジトリはここです。
https://github.com/shimabox/v2c
デモ
デモでおこなっているエフェクトは
- Canvas とピクセル操作 – ウェブデベロッパーガイド | MDN
- ウェブカメラの映像を Canvas でリアルタイム画像処理 | NARITA YUSUKE
- グレースケール,セピア調化-JavaScript入門(HTML5編)
のロジックを利用させていただいています。
(自分でもなにか考えます。。)
これはなに?
冒頭で述べましたが、Video(Webカメラ)の映像をCanvasに描画するライブラリです。
※正確にはWebカメラの映像をvideoタグに反映してそれをcanvasに描画するものになります
Webカメラで映しているものに対してサクッとなにかをしたい
というニーズ(があればそれ)に応えられるものになっていると思います。
特徴
- Webカメラの映像をcanvasにサクッと描画できます
- Webカメラ必須です
- スマホの場合はスマホのカメラが使えます
- callbackでcanvasを参照できるので好きなエフェクトがかけられます
- 前面カメラと背面カメラ(あれば)の切り替えが可能です
- canvasに映っているものを
png画像
で出力できます- 無音でキャプチャ可能です(悪いことに使っちゃダメだぞ)
- ただし画質は悪いです
data: URL
として出力する機能も持っています
なんでつくったのか
以前にこういった簡単な顔認識をするプログラムを書いて遊んでみたのですが、Webカメラの映像をcanvasに映す部分を書くのが毎度めんどくさかったので自分なりに抽象化した結果の副産物になります。
いちおう、Webカメラで映しているものに対してサクッとなにかをする
という部分に関しては満たされているのではないかと思っています。
使い方
以下にサクッと使い方を書いていきます。
基本的な使い方
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <!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')
でインスタンスを生成すると、指定したラッパー要素の中に以下の要素が作られます。
1 2 3 4 | < 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要素が真っ黒になる
そのため上記の方法をとっています。
描画停止
1 | v2c.stop(); |
キャプチャ
1 | v2c.capture(); |
こうすると、canvasに映っているものをpng画像として出力します。
ファイル名はcapture.png
になります。
もしファイル名を変更したい場合は以下の通りにします。
1 | v2c.capture( 'modify' ); // => modify.png |
こうすると、ファイル名はmodify.png
となります。
iOS(iPhone, iPad) はdata: URL
で一旦画像が表示されるので、その画像を長押しして保存します。
描画中のcanvasを参照する
描画中のcanvas要素を参照する場合は、start()
にcallbackを渡します。
1 2 3 4 5 6 7 | 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.videoWidth
とvideo.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
- フロントカメラ使用時の制約指定ができます
- MediaDevices.getUserMedia() – Web APIs | MDN
constraintsForRear
- リアカメラ使用時の制約指定ができます
- MediaDevices.getUserMedia() – Web APIs | MDN
callbackOnAfterInit
- 初期化処理後に呼ばれるcallback関数を指定します
- v2cインスタンスが引数として渡されます
123
const _callbackOnAfterInit =
function
(v2c) {
console.log(v2c);
}
callbackOnOrientationChange
orientationchange
イベントが発生したときのcallback関数を指定します123const _callbackOnOrientationChange =
function
() {
// Do something.
}
callbackOnLoadedmetadataVideo
loadedmetadata
イベントが発生したときのcallback関数を指定します- video要素が引数として渡されます
123
const _callbackOnLoadedmetadataVideo =
function
(video) {
console.log(video);
}
callbackOnVideoLoadSuccess
- videoの読み込み(Webカメラからの)に成功したときに呼ばれるcallback関数を指定します
- canvas要素が引数として渡されます
123
const _callbackOnVideoLoadSuccess =
function
(canvas) {
console.log(canvas);
}
callbackOnVideoLoadError
- videoの読み込み(Webカメラからの)に失敗したときに呼ばれるcallback関数を指定します
- エラーメッセージ, canvas要素, フロントカメラを利用しているかどうかのフラグ, callback(
callbackOnAfterVideoLoadError
) が引数として渡されます1234const _callbackOnVideoLoadError =
function
(err, canvas, useFrontCamera, callback) {
// `callbackOnAfterVideoLoadError`が指定されていない場合、callbackはnullです
console.log(err, canvas, useFrontCamera, callback);
}
callbackOnAfterVideoLoadError
- videoの読み込み(Webカメラからの)に失敗した後に呼ばれるcallback関数を指定します
- エラーメッセージが引数として渡されます
123
const _callbackOnAfterVideoLoadError =
function
(err) {
console.log(err);
}
Example.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // 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要素を参照することができます
1234567
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.videoWidth
とvideo.videoHeight
の比率になります
getDataUrl() :string
- canvas のイメージに対する
data: URL
を返します
capture(filename: string)
- canvasをpng画像に変換します
filename
はオプションです- ファイル名を変えたい場合は、引数でファイル名を指定してください
- デフォルトは
'capture'
(capture.png)です
おわりに
Webカメラで映しているものに対してサクッとなにかをしたい 人がいればぜひ使ってみてください。(自分は先人のエフェクトを利用しましたが)独自のエフェクトをかけたりとか、ある周期で画像をサーバーに送ってなにかゴニョゴニョするとか活用できるかと思います。
あと、特徴でも書いていますがキャプチャは無音で実行できるので悪いことに使っちゃだめですよ。
ではでは。