以下は、ml5-examples/p5js/ImageClassification/ImageClassificationで公開されているサンプルのコード解説です。
次のリンクをクリックすると、このサンプルの動作が確認できます。
MobileNetとp5.jsを使った画像分類サンプル
HTML
<!--
Copyright (c) 2018 ml5
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
<html>
<head>
<meta charset="UTF-8">
<title>Image classification using MobileNet and p5.js</title>
<link rel="shortcut icon" href="../../favicon.ico">
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.min.js"></script>
<!-- https://p5js.org/reference/#/libraries/p5.dom -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.dom.min.js"></script>
<script src="https://unpkg.com/ml5@0.1.2/dist/ml5.min.js" type="text/javascript"></script>
</head>
<body>
<h1>MobileNetとp5.jsを使った画像分類</h1>
<p id="status">モデルのロード...</p>
<p>MobileNetモデルは<span id="probability">...</span>の信頼度で<span id="result">...</span>と分類しました。 </p>
<script src="sketch.js"></script>
</body>
</html>
sketch.js
// Copyright (c) 2018 ml5
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
/* ===
ml5サンプル
MobileNetとp5.jsを使った画像分類
このサンプルでは、分類器の作成にコールバックパターンを使用している。
=== */
let classifier = null;
// 分類したい画像を保持する変数
let img = null;
// エラーと結果を得たとき実行する関数
const gotResult = (err, results) => {
console.log('gotResult()が呼び出された');
// コンソールにエラーを表示する。
if (err) {
console.error(err);
}
// 結果を保持する配列の要素数
//console.log(results.length);
// 配列内のオブジェクトが持つクラス名と確率を出力
for (let i = 0; i < results.length; i++) {
//console.log(results[i].className);
//console.log(results[i].probability);
}
// 結果は、確率(probability)順に並んだ配列に保持されている
select('#result').html(results[0].className);
// https://p5js.org/reference/#/p5/nf
// 数値を文字列にフォーマットするためのユーティリティー関数
select('#probability').html(nf(results[0].probability, 0, 2));
console.log('結果を表示した');
}
// 画像の読み込みが完了したら、その画像に関する推測を得る。
const imageReady = () => {
console.log('imageReady()が呼び出された');
// https://ml5js.org/docs/ImageClassifier
// .predict(input, ?callback)
// 与えられた画像やビデオについて、クラス名と確率を含むオブジェクトの配列を返す
classifier.predict(img, gotResult);
console.log('分類器による推測開始を開始し、結果が出たらgotResult()を呼び出す');
// クラスの量は希望するだけいくつでも指定できる。
// classifier.predict(img, 10, gotResult);
}
// モデルが読み込まれたら状態を変更する
const modelReady = () => {
console.log('modelReady()が呼び出された');
// https://p5js.org/reference/#/p5/select
// 与えられたID、クラス、タグ名を持つ要素を検索し、それをp5.Elementとして返す
// https://p5js.org/reference/#/p5.Element/html
// 引数が与えられた場合には、要素のinnerHTMLを設定し、既存のhtmlを置き換える。
// ID値がstatusの要素を検索し、innerHTMLプロパティ値を設定する。
select('#status').html('モデルのロード完了');
// https://p5js.org/reference/#/p5/noCanvas
// p5スケッチ用のデフォルトキャンバスは要らないので、削除する。
noCanvas();
// イメージをロード
// https://p5js.org/reference/#/p5/createImg
// createImg(src, [alt], [successCallback])
// 与えられたsrcとalternateテキストで、DOMに<img>要素を作成する。
img = createImg('images/bird.jpg', imageReady);
// https://p5js.org/reference/#/p5.Element/size
// 要素の幅と高さを設定する
img.size(400, 400);
console.log('<img>の作成後、imageReady()を呼び出す');
}
// https://p5js.org/reference/#/p5/setup
// プログラムのスタート時、1回だけ呼び出される関数。constは使えない
function setup() {
console.log('setup()が呼び出された');
//const setup = () => {
// ml5.imageClassifier()メソッドをMobileNetで初期化、コールバックを渡す必要がある。
// ml5.imageClassifier(model, ?callback)
// 訓練済MobileNetモデルを使った画像分類器をロードする。
// ロードが終わったらmodelReady()関数を呼び出す。
console.log('分類器の読み込み後、modelReadyを呼び出す');
classifier = ml5.imageClassifier('MobileNet', modelReady);
}
「1_2:Promiseとコールバックの使用に関する留意点」でも述べられているように、ml5.jsを使った機械学習アプリでは、「作業が終わるのを待つ」ことがポイントで、このサンプルでは、下図に示す順番で関数が呼び出されます。
setup()関数はp5.jsで定義されている特別な関数で、プログラムのスタート時に1回だけ呼び出されます。この関数の定義にはconstは使えません。setup()ではimageClassifier()を使って、訓練済MobileNetモデルによる画像分類器をインターネット経由で読み込み、それが終わったらmodelReady()関数を呼び出します。
modelReady()関数では、p5.dom.jsのcreateImg()を使って、p5.jsで扱える<img>要素を作成し、それが終わったらimageReady()関数を呼び出します。
imageReady()関数では、分類器のpredict()を使って画像を推測し、それが終わったら、gotResult()関数を呼び出します。
gotResult()には分類器による推測結果の配列(results)が渡されるので、それを処理してページに表示します。
resultsは配列なので、その要素数はlengthプロパティで分かります。resultsの要素はオブジェクトで、MobileNetのモデルが分類した名前(className)とその確率(probability)をプロパティとして持っています。これは次のようなコードで確認できます。
// 結果を保持する配列の要素数
console.log(results.length);
// 配列内のオブジェクトが持つクラス名と確率を出力
for (let i = 0; i < results.length; i++) {
console.log(results[i].className);
console.log(results[i].probability);
}
このサンプルではまた、p5.jsやp5.dom.jsの関数が多く使用されています。
- select() – 与えられたID、クラス、タグ名を持つ要素を検索し、それをp5.Elementとして返す。
- html() – 引数が与えられた場合には、要素のinnerHTMLを設定し、既存のhtmlを置き換える。
- noCanvas() – p5用のデフォルトキャンバスは要らないので、削除する。
- createImg() -与えられたsrcとalternateテキストでDOMに要素を作成する。createImg(src, [alt], [successCallback])