clmtraker.jsとobnizを使用して顔の表情からLチカさせる

#はじめに
プログラミングやマイコンボードに初めて触れた超初心者がjavascriptを駆使して表情検出で遊んでみた時の個人用備忘録である。
clmtracker.jsというjavascriptの表情検出APIを使って2種類の表情を捉え、obnizと連動させ、表情に合わせてLEDライトが光る仕組みを考えた。

#obniz準備
- 自分のobnizをPCと接続する。
<br>「ドキュメント」→「obnizの始め方」に記載されているようにnodejsがインストールされたVisualStudioCodeを使用する。（Nodejsは入っていなくても動いている気がする。）
<br>[obniz公式サイト](https://obniz.io/ja/)
<br>[Node.jsダウンロードページ](https://nodejs.org/ja/)
- obnizをWifiに接続し、画面上にQRコードと8桁の数字が表示された状態にする。
- 抵抗付き青色LEDを準備し、アノードをobnizのio0に、カソードをio1にさす。
<br>[obniz LEDを付ける 参照](https://obniz.io/ja/lessons/lessons/lessons_turning_on_led)


#clmtracker.js準備（今回はクラウドファイルを使用するためダウンロード不要）
- GitHubにアップロードされているclmtrcker.jsファイルをPCにダウンロードし全て解凍する。
<br>[GitHub clmtracker.js](https://github.com/auduno/clmtrackr)
- VScodeにて使用できるようにフォルダに名前を付けて保存する。



#実装
- VScodeを立ち上げて、clmtrackをダウンロードした同じフォルダの中に新しくhtmlファイルを作成する。<br>（今回は適当にhtmlファイルを作成する。「新しいファイル作成」→XXX.htmlとすれば作れる。）
- 自分のフォルダ内のclmtrackerを使用する場合は4つ分のscript内を自分のフォルダから引用するコードに変更する。
- var obniz = new Obniz("XXXX-XXXX") XXXX-XXXXの部分に自分のobnizの8桁の数字を入力することを忘れずに行う。
- PCのWEBカメラを使用する。
<br>[obniz キッズプロジェクト　参考](https://blog.obniz.io/ja/make/kids-projects-face-copying-panda/)
- サーバーを経由する必要があるためVScodeの拡張機能で[Live Server]を入れておいた。<br>[Live Server　参考](https://qiita.com/kn339264/items/e6f6a69f70a96e61fc11)


```html

<html>
<head>
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@2.0.2/obniz.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/build/clmtrackr.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/models/model_pca_20_svm.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotion_classifier.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotionmodel.js"></script>
</head>
  
<body>
  <div id="obniz-debug"></div>
  <video id="video" width="400" height="300" autoplay></video>
  <canvas id="canvas" width="400" height="300"></canvas> 
  <div id="print"></div>
<script>
  
var speaker;
var obniz = new Obniz("XXXX-XXXX")  //自分のobnizの8桁の数字を入力する
obniz.onconnect = async () => {
    var led = obniz.wired("LED", {anode:0, cathode:1});
 await startEmotion()
}

async function startEmotion() {
  var video = document.getElementById("video");
  var canvas = document.getElementById("canvas");
  var context = canvas.getContext("2d");

  var stream = await navigator.mediaDevices.getUserMedia({
    video: {facingMode: "user"},
    audio: false
  });
  video.srcObject = stream;
  video.onloadedmetadata = function(e) {
    video.play();
  };

  var tracker = new clm.tracker();
  tracker.init(pModel);
  tracker.start(video);

  var classifier = new emotionClassifier();
  classifier.init(emotionModel);

  function drawLoop() {
    requestAnimationFrame(drawLoop);
    var positions = tracker.getCurrentPosition();
    var parameters = tracker.getCurrentParameters();
    var emotion = classifier.meanPredict(parameters);   
    context.clearRect(0, 0, canvas.width, canvas.height);
    tracker.draw(canvas);
    var emotions = {};
    for (var i=0; i<emotion.length; i++) {
      emotions[emotion[i].emotion] = emotion[i].value;
    }

    $("#print").html("sad: " + emotions.sad +"happy: " + emotions.happy)
    
    var led = obniz.wired("LED", {anode:0, cathode:1});

    if(emotions.happy > emotions.sad){
     led.on();
     console.log("happy");
    } else if (emotions.happy < emotions.sad){
     led.off();
     console.log("sad");
    }
}
  drawLoop();
}
 
</script>
</body>
</html>
```

#フルカラーLEDでも試してみた
WS2811もしくはWS2812円形タイプを購入し、表情によって色が変化するようにした。<br>今回のコードは楽しいと黄色、悲しいと青になるように設定した。RGBで数値を弄れば色を変えることができる。<br>配線の方法はパーツライブラリを参照した。
[パーツライブラリ](https://obniz.io/ja/sdk/parts)

```html
<html>
<head>
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@2.0.2/obniz.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/build/clmtrackr.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/models/model_pca_20_svm.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotion_classifier.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotionmodel.js"></script>
</head>
  
<body>
  <div id="obniz-debug"></div>
  <video id="video" width="400" height="300" autoplay></video>
  <canvas id="canvas" width="400" height="300"></canvas> 
  <div id="print"></div>
<script>
  
var speaker;
var obniz = new Obniz("XXXX-XXXX")  //自分のobnizの8桁の数字を入力する
var emotions = {};

var HAPPY = false;
var SAD = false;

obniz.onconnect = async function () {
    const leds = obniz.wired("WS2812", {gnd:0, vcc: 1, din: 2});
    

    var video = document.getElementById("video");
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");

    var stream = await navigator.mediaDevices.getUserMedia({
        video: {facingMode: "user"},
        audio: false
    });
    video.srcObject = stream;
    video.onloadedmetadata = function(e) {
        video.play();
    };

    var tracker = new clm.tracker();
    tracker.init(pModel);
    tracker.start(video);

    var classifier = new emotionClassifier();
    classifier.init(emotionModel);

    
    function drawLoop() {
        requestAnimationFrame(drawLoop);
        var positions = tracker.getCurrentPosition();
        var parameters = tracker.getCurrentParameters();
        var emotion = classifier.meanPredict(parameters);   
        context.clearRect(0, 0, canvas.width, canvas.height);
        tracker.draw(canvas);
        
        for (var i=0; i<emotion.length; i++) {
            emotions[emotion[i].emotion] = emotion[i].value;
        }

        $("#print").html("sad: " + emotions.sad +"happy: " + emotions.happy)

        if(HAPPY){
        
        leds.rgbs([
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
            [255,215,0],
        ]);

    } 
    else {

        leds.rgbs([
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        [0x00, 0x00, 0xFF] , 
        [0x00, 0x00, 0xFF] ,
        ]);
    }

        if(emotions.happy > emotions.sad){
            HAPPY = true;
            SAD = false;
            obniz.display.print("happy");
        }
        else{
            SAD =false;
            HAPPY = false;
            obniz.display.print("sad");
        }
    }
    drawLoop();
}
</script>
</body>
</html>
```
```html
<html>
<head>
  <script src="https://obniz.io/js/jquery-3.2.1.min.js"></script>
  <script src="https://unpkg.com/obniz@2.0.2/obniz.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/build/clmtrackr.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/models/model_pca_20_svm.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotion_classifier.js"></script>
  <script src="https://rawgit.com/auduno/clmtrackr/dev/examples/js/emotionmodel.js"></script>
</head>

<body>
  <div id="obniz-debug"></div>
  <video id="video" width="400" height="300" autoplay></video>
  <canvas id="canvas" width="400" height="300"></canvas> 
  <div id="print"></div>
<script>

var speaker;
var obniz = new Obniz("XXXX-XXXX")  //自分のobnizの8桁の数字を入力する
var emotions = {};

var HAPPY = false;
var SAD = false;

obniz.onconnect = async function () {
    const leds = obniz.wired("WS2811", {gnd:0, vcc: 1, din: 2});


    var video = document.getElementById("video");
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");

    var stream = await navigator.mediaDevices.getUserMedia({
        video: {facingMode: "user"},
        audio: false
    });
    video.srcObject = stream;
    video.onloadedmetadata = function(e) {
        video.play();
    };

    var tracker = new clm.tracker();
    tracker.init(pModel);
    tracker.start(video);

    var classifier = new emotionClassifier();
    classifier.init(emotionModel);


    function drawLoop() {
        requestAnimationFrame(drawLoop);
        var positions = tracker.getCurrentPosition();
        var parameters = tracker.getCurrentParameters();
        var emotion = classifier.meanPredict(parameters);   
        context.clearRect(0, 0, canvas.width, canvas.height);
        tracker.draw(canvas);

        for (var i=0; i<emotion.length; i++) {
            emotions[emotion[i].emotion] = emotion[i].value;
        }

        $("#print").html("sad: " + emotions.sad +"happy: " + emotions.happy)

        if(HAPPY){

        leds.rgbs([
            [255,215,0],
        ]);

    } 
    else {

        leds.rgbs([
        [0x00, 0x00, 0xFF] , 
        ]);
    }

        if(emotions.happy > emotions.sad){
            HAPPY = true;
            SAD = false;
            // obniz.display.print("happy");
        }
        else{
            SAD =false;
            HAPPY = false;
            // obniz.display.print("sad");
        }
    }
    drawLoop();
}
</script>
</body>
</html>
```