本稿は「ml5-examples/p5js/StyleTransfer/StyleTransfer_Video」で公開されているサンプルの解説です。
次のリンクをクリックすると、実際の動作が確認できます。「p5.jsを使ったビデオ画像の画風変換」
これは、Webカメラに映っている映像のひとコマをフランシス・ピカビアのタッチで画風変換するもので、ボタンのクリックで変換処理を止めない間は、連続して変換できます。
HTML
p id='status'>モデルの読み込み中...</p>
<img src="images/udnie.jpg" />
<div id="canvasContainer"></div>
<br/>
<button id="startStop">開始</button>
CSS
img {
width: 240px;
display: inline;
}
#canvasContainer {
display: inline;
}
sketch.js
let style;
let video;
// 変換中かどうか
let isTransferring = false;
let resultImg;
// 最初に呼び出される。
function setup() {
// キャンバスを320x240のサイズで作成し、canvasContainer div要素の子として追加する。
// ドキュメントにcanvas要素を作成し、サイズをピクセル単位で設定する。
// createCanvas(w, h, [renderer])
// https://p5js.org/reference/#/p5/createCanvas
// 指定された親に、要素を割り当てる
// https://p5js.org/reference/#/p5.Element
createCanvas(320, 240).parent('canvasContainer');
// Webカメラからの映像を映すvideo要素を作成する。
// Webカメラからのオーディオやビデオフィードを含む HTML5 video要素を作成する。
// createCapture(type, callback)
// https://p5js.org/reference/#/p5/createCapture
video = createCapture(VIDEO);
// 現在の要素を隠す。実際には、スタイルのdisplay:noneを設定。
// https://p5js.org/reference/#/p5.Element/hide
video.hide();
// 画風変換した結果の画像用 img要素を作成し、非表示にしておく。
resultImg = createImg('');
resultImg.hide();
// 変換処理を開始し停止するボタン
select('#startStop').mousePressed(startStop);
// 定義されたスタイルでStyleTransferインスタンスを作成する。
// 2つめの引数にvideoを指定する。
style = ml5.styleTransfer('models/udnie', video, modelLoaded);
}
// setup()後、毎フレーム呼び出される。
function draw() {
// カメラからの生映像と、画風変換した画像を切り替える
// 変換中ならresultImgを描画
if (isTransferring) {
// イメージをp5.jsのデフォルトcanvasに描画する。
// image(img, x, y, [width], [height])
// https://p5js.org/reference/#/p5/image
image(resultImg, 0, 0, 320, 240);
// 変換中でないならvideoを描画
}
else {
image(video, 0, 0, 320, 240);
}
}
// モデルが読み込まれたときに呼び出される関数
function modelLoaded() {
select('#status').html('モデルが読み込まれた');
}
// 変換処理を開始し停止する
function startStop() {
if (isTransferring) {
select('#startStop').html('開始');
}
else {
select('#startStop').html('停止');
// videoを使って変換する。
// .transfer(?callback)
// inputに画風変換を適用する。
style.transfer(gotResult);
}
// 変換中を切り替える
isTransferring = !isTransferring;
}
// 結果を得たら、resultImgのsrcを更新する。
function gotResult(err, img) {
// 指定された要素について、新しい属性を追加するか、既存の属性の値を変更する。
// attribute(attr, [value])
// https://p5js.org/reference/#/p5.Element/attribute
resultImg.attribute('src', img.src);
// まだ変換中(ボタンが押されていない)なら、つづけて変換する。
if (isTransferring) {
style.transfer(gotResult);
}
}
ml5.styleTransfer()でスタイルを作成し、モデルの読み込み後にtransfer()で画風変換を適用して、コールバック関数で結果を処理する、という基本的な流れは前のサンプルと同じです。
// styleを作成
style = ml5.styleTransfer('models/udnie', video, modelLoaded);
// 画風変換
style.transfer(gotResult);
// 結果を表示
function gotResult(err, img) {
resultImg.attribute('src', img.src);
画風変換の処理自体はml5.jsの働きによって簡単になっていますが、ビデオの映像をリアルタイム風なビデオに変換しているように見える(実際には変換に時間がかかるのでタイムラグがある)効果には、少し工夫がしてあります。それは、Webカメラからの映像を映す<video>と変換結果を示す<img>のhide()メソッドをコメントアウトすると分かります。
Webカメラからの映像は(1)の大きな<video>に映ります。このひとコマ(フレーム)を画風変換し、結果の画像を(2)の<img>のsrcプロパティに設定します。そしてp5.jsのimage()を使って、p5.jsのデフォルトのキャンバスに描いています((3))。image()はp5.jsのdraw()関数内で呼び出しています。draw()関数は毎フレーム、p5.jsによって自動的に呼び出されるので、”リアルタイム風”に見えるという訳です。