<html>
<head>
  <title>サーマルカメラ</title>
  <script src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.0/socket.io.js"></script>
  <style>
    body {
      background-color: #000;
    }
    body.alert {
      background-color: #900;
    }
    #temp {
      position: absolute;
      top: 300px;
      left: 0;
      right: 0;
      margin: auto;
      font-size: 72px;
      color: #EEE;
      text-align: center;
      line-height: 1;
    }
    #thermalArea {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    #thermalArea div {
      position: relative;
      width: 240px;
      height: 240px;
      overflow: hidden;
    }
    #thermalArea img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      opacity: 0.8;
    }
    #canvas {
      position: relative;
      transform: scale(30);
      transform-origin: top left;
      z-index: -1;
      mix-blend-mode: screen;
    }
    #camera {
      position: absolute;
      top: 0;
      left: -40px;
      z-index: -2;
    }
  </style>
</head>
<body>
<main>
  <button type="button" id="thermalBtn">検温!</button>
  <div id="thermalArea">
    <div>
      <canvas id="canvas" width="8" height="8"></canvas>
      <video id="camera" width="320px"></video>
      <img src="img/silhouette.png" alt="">
    </div>
  </div>
  <div id="temp"></div>
</main>
<script>
  // サーモグラフィー彩度・明度設定
  const maxH = 250;
  const minH = 0;
  const stepH = 7;
  const maxL = 50;
  const minL = 20;
  const stepL = 7;
 
  // 体温判定
  const minTemp = 36;
  const alertTemp = 37;
 
  const socket = io();
  const video = document.getElementById('camera');
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');
  const $body = $('body');
  const $thermalBtn = $('#thermalBtn');
  const $temp = $('#temp');
  let temp = 0;
  let calibration = 9;
  let isTemp = false;
  let isNear = false;
  let tempList = [];
 
  const init = () => {
    setSocket();
    setBtn();
  };
 
  const setSocket = () => {
    let array,
        result = [];
    socket.on('thermal', (data) => {
      drawCanvas(data);
    });
  };
 
  const setBtn = () => {
    let timer;
    $thermalBtn.on('click', function(){
      const emitThermal = function(){
        socket.emit('msg', 'thermal');
      };
      isTemp = !isTemp;
 
      if(isTemp){
        setCamera();
        emitThermal();
        timer = setInterval(emitThermal, 250);
        $thermalBtn.text('計測中…');
      }else{
        stopCamera();
        clearInterval(timer);
        $thermalBtn.text('検温!');
      }
      return false;
    });
  };
 
  const drawCanvas = (data) => {
    let h,
        l,
        t = 0,
        count = 0;
    for (let i = 0; i < data.length; i++) {
      for (let j = 0; j < data[i].length; j++) {
        // 最大温度記録
        if(data[i][j] > t){
          t = data[i][j];
          // 体温ヒット数をカウント
          if(t + calibration > minTemp){
            count++;
            // 2画素越えると近すぎ
            if(count > 2){
              isNear = true;
            }else{
              isNear = false;
            }
          }
        }
        // 色相を設定
        h = maxH - data[i][j] * stepH;
        if(maxH < h){
          h = maxH;
        }else if(minH > h){
          h = minH;
        }
        // 明度を設定
        l = (data[i][j] - minL) * stepL;
        if(maxL < l){
          l = maxL;
        }
        ctx.fillStyle = 'hsl('+ h +', 100%, '+ l +'%)';
        ctx.fillRect(j, i, 1, 1);
      }
    }
    tempList.push(t);
    if(tempList.length > 10){
      tempList.shift();
    }
    // 体温中央値を取得して判定
    temp = getTemp(tempList);
    if(isNear){
      $temp.text('はなれて!');
    }else if(temp < minTemp){
      //$temp.text('');
      $temp.text(temp.toFixed(1) + '℃');// 低い温度でも表示
    }else{
      // 体温なら温度表示して平熱判定
      $temp.text(temp.toFixed(1) + '℃');
      if(temp < alertTemp){
        $body.removeClass('alert');
      }else{
        $body.addClass('alert');
      }
    }
  };
 
  const getTemp = (tempList) => {
    // 中央値の温度を返す
    let array = tempList.slice(),
        val;
    array.sort((a, b) => {
      return a - b;
    });
    let half = Math.floor(array.length / 2);
    if (array.length % 2) {
      val =  array[half];
    } else {
      val = (array[half - 1] + array[half]) / 2;
    }
    //キャリブレーション
    val += calibration;
    return val;
  };
 
  const setCamera = () => {
    // カメラ起動
    const constrains = { video: {width: 320, height: 240}, audio: false };
    navigator.mediaDevices.getUserMedia(constrains)
    .then(function(stream) {
      video.srcObject = stream;
      video.play();
    });
  };
 
  const stopCamera = () => {
    // カメラ停止
    const tracks = video.srcObject.getTracks();
    tracks.forEach(track => {
      track.stop();
    });
    video.srcObject = null;
  };
 
  init();
</script>
</body>
</html>