本稿は「ml5-examples/p5js/StyleTransfer/StyleTransfer_Image」で公開されているサンプルの解説です。
次のリンクをクリックすると、実際の動作が確認できます。「p5.jsを使った画像の画風変換」
これは、入力画像を、葛飾北斎風タッチで描くとこうなり、ピカビア風タッチで描くとこうなる、というようなことです。
HTML
<p id="statusMsg">モデルの読み込み中...</p>
<button id="transferBtn">変換</button>
<p>入力画像:</p>
<img src="img/patagonia.jpg" alt="input img" id="inputImg">
<div id="styleA">
<p>スタイルA:神奈川沖浪裏(かながわおきなみうら), 1829 - 葛飾北斎</p>
<img src="img/wave.jpg" alt="style one">
</div>
<div id="styleB">
<p>スタイル B:ウドニ―(若いアメリカの少女:ダンス), 1913 - フランシス・ピカビア</p>
<img src="img/udnie.jpg" alt="style two">
</div>
CSS:入力画像はこのCSSによって、全部正方形として扱われます。
img {
width: 250px;
height: 250px;
display: inline;
}
sketch.js
let inputImg;
let statusMsg;
let transferBtn;
let style1;
let style2;
function setup() {
noCanvas();
// ステータスメッセージを取得
statusMsg = select('#statusMsg');
// 入力イメージを取得
inputImg = select('#inputImg');
// [変換]ボタン
transferBtn = select('#transferBtn')
// ml5.styleTransfer(model, ?callback)
// 別個の訓練済みモデルを使って、2つのスタイルを作成する
style1 = ml5.styleTransfer('models/wave', modelLoaded);
style2 = ml5.styleTransfer('models/udnie', modelLoaded);
}
// モデルが読み込まれたときに呼び出される関数
function modelLoaded() {
// モデルが両方とも読み込まれたことを確認
if (style1.ready && style2.ready) {
statusMsg.html('準備完了');
transferBtn.mousePressed(transferImages);
}
}
// 変換を両方の画像に適用
function transferImages() {
statusMsg.html('画風変換を適用中...');
// 画風変換をinputに適用
// .transfer(input, ?callback)
style1.transfer(inputImg, function(err, result) {
//console.log(result)
// resultは<img src="data:image/png;base64,iVBORw..."> src値にデータURIが使われている
// resultは<img> HTML要素
// 与えられたsrcとalternateテキストで、DOMに<img>要素を作成する。
// createImg(src, [alt], [successCallback])
// https://p5js.org/reference/#/p5/createImg
// 指定された親に、要素を割り当てる
// https://p5js.org/reference/#/p5.Element
createImg(result.src).parent('styleA');
});
style2.transfer(inputImg, function(err, result) {
createImg(result.src).parent('styleB');
});
statusMsg.html('終了');
}
HTML要素で本来的に必要なのは、入力のimg要素と、変換結果を表示する要素だけです。
<img src="img/patagonia.jpg" alt="input img" id="inputImg">
<div id="styleA">
<div id="styleB">
ml5.styleTransfer()には、画風変換モデルへのパスと、モデルが読み込まれたときに呼び出すコールバック関数を与えます。すると、StyleTransferオブジェクトが得られます(style1)
style1 = ml5.styleTransfer('models/wave', modelLoaded);
変換は、StyleTransferオブジェクトのtranfer()メソッドに、入力イメージとコールバック関数を渡して実行します。ただしこれは、モデルの読み込みが終わってから行う必要があります。
style1.transfer(inputImg, function (err, result) {...
コールバック関数には変換結果(result)が、<img src="data:image/png;base64,iVBORw...">という形式で渡されます。これは、data:が頭についたデータURIという形式で、画像データはBASE64という方法でエンコードされています。この<img>要素のsrcプロパティをp5.jsのcreateImg()に渡すと変換結果の画像を作成することができます。
createImg(result.src).parent('styleA');
舞台裏ではかなり面倒なことが行われているはずなのですが、ml5.jsのおかげでJavaScriptコードは簡単です。試しに下図の左の写真を入力画像として与えると、右の葛飾北斎風タッチの画像が得られました。