ロード中

トップへ(mam-mam.net/)

JavaScriptで顔認識+年齢推定|Webカメラ×face-api.jsの実装例付き

JavaScriptで顔認識+年齢推定|Webカメラ×face-api.jsの実装例付き

JavaScriptとface-api.jsを使って、Webカメラからリアルタイムに顔認識し、AIが年齢を推定するブラウザアプリを構築する方法を紹介します。
このページでは、クライアントサイドのみで動作する実装例を掲載しており、顔検出・年齢推定・描画処理までをJavaScriptで完結させています。
「顔認識で年齢を表示するサイトを作りたい」「AIで顔年齢を推定したい」という方におすすめです。

カメラ使用を許可してください。しばらくすると、カメラ映像が表示されます

カメラに顔を映してください。顔を認識してAIが推定した年齢を表示します。マスクは外してください。
(カメラ映像が正しく映らない場合は、リロードしてみてください)


ソースコード

<div class="wait"><img src="./imgs/loading.gif" width="64" height="64"><p>ロード中</p></div>
<div><canvas id="preview"></canvas></div>

<script src="./models/face-api.min.js"></script>
<script>
//TinyFaceDetectorModelを使用する
faceapi.nets.tinyFaceDetector.loadFromUri('./models');
//AgeGenderを使用する
faceapi.nets.ageGenderNet.loadFromUri("./models");
//Expressionを使用する
faceapi.nets.faceExpressionNet.loadFromUri("./models"); 

window.addEventListener('DOMContentLoaded',function(){
  ShowWait();
});

var video,prev,prev_ctx,w,h,nw,nh,img;
window.addEventListener('load',function(event){
  glassimg=new Image();
  glassimg.addEventListener("load",function(){
      nw=glassimg.naturalWidth;
      nh=glassimg.naturalHeight;
  });
  video=document.createElement('video');
  video.setAttribute("autoplay","");
  video.setAttribute("muted","");
  video.setAttribute("playsinline","");
  video.onloadedmetadata = function(e){video.play();};
  prev=document.getElementById("preview");
  prev_ctx=prev.getContext("2d", {willReadFrequently:true});
  //左右反転表示させる
  prev.style.transform="scaleX(-1)";

  //カメラ使用の許可ダイアログが表示される
  navigator.mediaDevices.getUserMedia(
    //マイクはオフ, カメラの設定   前面カメラを希望する 640×480を希望する
    {"audio":false,"video":{"facingMode":"user","width":{"ideal":640},"height":{"ideal":480}}}
  ).then( //許可された場合
    function(stream){
      video.srcObject = stream;
      //1.0秒後にスキャンする
      setTimeout(Scan,1000,true);
    }
  ).catch(
    //許可されなかった場合
    function(){
      HideWait(1);
    }
  );

  function Scan(first){
    if(first){
      //選択された幅高さ
      w=video.videoWidth;
      h=video.videoHeight;
      //画面上の表示サイズ
      prev.style.width="100%";
      prev.style.maxWidth="640px";
      //内部のサイズ
      prev.setAttribute("width",w);
      prev.setAttribute("height",h);
      HideWait(1);
    }

    detect()
    .then(
      function(result){
        prev_ctx.drawImage(video,0,0,w,h);
        for(i=0;i<result.length;i++){
          if(result[i].age&&result[i].expressions){
            prev_ctx.beginPath();
            prev_ctx.lineWidth=4;
            prev_ctx.strokeStyle="#ff0000";
            prev_ctx.moveTo(result[i].detection.box.x, result[i].detection.box.y);
            prev_ctx.lineTo(result[i].detection.box.x+0, result[i].detection.box.y+result[i].detection.box.height);
            prev_ctx.lineTo(result[i].detection.box.x+result[i].detection.box.width, result[i].detection.box.y+result[i].detection.box.height);
            prev_ctx.lineTo(result[i].detection.box.x+result[i].detection.box.width, result[i].detection.box.y+0);
            prev_ctx.lineTo(result[i].detection.box.x+0, result[i].detection.box.y+0);
            prev_ctx.stroke();
            prev_ctx.scale(-1, 1);
            prev_ctx.font="36px 'MS ゴシック'";
            prev_ctx.fillStyle="pink";
            let age=Math.round(result[i].age)+"歳 ";
            let gender="";
            //if(result[i].gender="male"){gender="男性";}
            //if(result[i].gender="female"){gender="女性";}

            let exps=result[i].expressions.angry;
            let expstr="怒り";
            if(result[i].expressions.disgusted>exps){
              exps=result[i].expressions.disgusted;
              expstr="うんざり";
            }
            if(result[i].expressions.fearful>exps){
              exps=result[i].expressions.fearful;
              expstr="怯え";
            }
            if(result[i].expressions.happy>exps){
              exps=result[i].expressions.happy;
              expstr="幸せ";
            }
            if(result[i].expressions.neutral>exps){
              exps=result[i].expressions.neutral;
              expstr="普通";
            }
            if(result[i].expressions.sad>exps){
              exps=result[i].expressions.sad;
              expstr="悲しい";
            }
            if(result[i].expressions.surprised>exps){
              exps=result[i].expressions.surprised;
              expstr="驚き";
            }
            prev_ctx.fillText(age+gender+expstr,-result[i].detection.box.x-result[i].detection.box.width, result[i].detection.box.y-4);
            prev_ctx.setTransform(1,0,0,1,0,0);
          }
        }
        setTimeout(Scan,10,false);
      }
    )
  }
  async function detect(){
    return faceapi.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions()).withAgeAndGender().withFaceExpressions();
  }
});
function ShowWait(){
  document.querySelector(".wait").style.display="block";
}
function HideWait(interval){
  setTimeout(function(){
    document.querySelector(".wait").style.display="none";
  },interval);
}
</script>

使用したライブラリ

face-api.js(https://github.com/justadudewhohacks/face-api.js)を使用させていただきました。
face-api.jsのライセンス情報
MIT License
Copyright (c) 2018 Vincent Mühler