ラベル Cognitive の投稿を表示しています。 すべての投稿を表示
ラベル Cognitive の投稿を表示しています。 すべての投稿を表示

火曜日, 11月 27, 2018

Azure Face APIサンプル 2(カメラ利用)

Azureの管理ポータルでの設定は省略。
HTMLファイルのサンプル。
カメラつき端末のHTML5対応ブラウザの利用が前提。
httpdのドキュメントルート下の /cognitive/ に配置される前提。
なお、ここで利用するPHPファイルは「Azure Face APIサンプル」を参照。

face_api2.html
----- ここから -----
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Face API (with camera)</title>

<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<style>
<!--
@media all {
  .page-title {
    color: #555555;
    font-size: x-large;
    font-weight: bold;
    text-shadow: 1px 1px 1px #999999;
  }
  #canvas {
    display: none;
  }
  #result {
    font-size: large;
    font-weight: bold;
    text-shadow: 1px 1px 1px #333333;
  }
}

@media screen and (max-width: 480px) {
  #player {
    width: 90%;
    width: 90vw;
  }
  #canvas {
    display: none;
    width: 90%;
    width: 90vw;
  }
}
-->
</style>
</head>
<body>
<div class="container">

<div class="row">
<div class="col-sm-12">
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/cognitive/">Cognitive Services Examples</a>
</nav>
</div>
</div>

<div class="row">
<div class="col-sm-12">
<h3 class="page-title">Face API (with camera)</h3>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">
<strong> カメラのある端末でかつHTML5対応ブラウザでこのページを表示し、カメラに顔を向け「カメラで撮影」ボタンをクリックしてください。</strong>
<div class="card">
<div class="card-body">
<button type="button" id="submit-btn" class="btn btn-primary">カメラで撮影</button>
 <button type="button" id="reset-btn" class="btn btn-info">リセット</button>
</div>
</div>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">
<div class="mx-auto" style="width: 100%;">
<video id="player" controls playsinline muted autoplay></video>
</div>
</div>
</div>

<div class="row">
<div class="col-sm-12">
<canvas id="canvas"></canvas>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-5">
<div id="result"></div>
</div>
</div>
<br />

</div>

<!-- template -->
<script type="text/template" id="result-table-template">
<table class="table table-bordered">
<tr>
<td colspan="2">性別</td><td><div class="float-sm-right"><%= gender %></div></td>
</tr>
<tr>
<td colspan="2">年齢</td><td><div class="float-sm-right"><%= age %></div></td>
</tr>
<tr>
<td colspan="2">眼鏡</td><td><div class="float-sm-right"><%= glasses %></div></td>
</tr>
<tr>
<td rowspan="8">感情</td><td>Anger (怒り)</td><td><div class="float-sm-right"><%= anger %></div></td>
</tr>
<tr>
<td>Contempt (軽蔑)</td><td><div class="float-sm-right"><%= contempt %></div></td>
</tr>
<tr>
<td>Disgust (嫌悪)</td><td><div class="float-sm-right"><%= disgust %></div></td>
</tr>
<tr>
<td>Fear (恐怖)</td><td><div class="float-sm-right"><%= fear %></div></td>
</tr>
<tr>
<td>Happiness (幸福)</td><td><div class="float-sm-right"><%= happiness %></div></td>
</tr>
<tr>
<td>Neutral (ニュートラル)</td><td><div class="float-sm-right"><%= neutral %></div></td>
</tr>
<tr>
<td>Sadness (悲しみ)</td><td><div class="float-sm-right"><%= sadness %></div></td>
</tr>
<tr>
<td>Surprise (驚き)</td><td><div class="float-sm-right"><%= surprise %></div></td>
</tr>
<tr>
<td rowspan="2">化粧</td><td>目</td><td><div class="float-sm-right"><%= eyeMakeup %></div></td>
</tr>
<tr>
<td>口</td><td><div class="float-sm-right"><%= lipMakeup %></div></td>
</tr>
</table>
</script>

<!-- javascript -->
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js" integrity="sha256-G7A4JrJjJlFqP0yamznwPjAApIKPkadeHfyIwiaa9e0=" crossorigin="anonymous"></script>
<script src="//code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js" integrity="sha256-9wRM03dUw6ABCs+AU69WbK33oktrlXamEXMvxUaF+KU=" crossorigin="anonymous"></script>
<script>
(function() {

"use strict";

const root = this;
const _    = root._;
const $    = root.jQuery;

const apiUrl = '/cognitive/face_api.php';
const resultTableTemplate
  = _.template($('#result-table-template').html());

const mathRound = function(val) {
  return (val * 100).toFixed(2);
};

const toBlob = function(base64, type) {
  const bin = atob(base64.replace(/^.*,/, ''));
  const buffer = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }

  const blob = new Blob([buffer.buffer], {
    type: type
  });
  return blob;
};

$(function() {
  const player = document.getElementById('player');
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const fd = new FormData();
  const maxPixels = 4096;

  navigator.mediaDevices.getUserMedia({
    audio: false,
    video: {
      facingMode: 'user'
    }
  }).then(function(stream) {
    player.srcObject = stream;
  });

  $('#reset-btn').on('click', function() {
    $('#result').html("");
    $('#canvas').hide();
    $('#player').show();
  });

  $('#submit-btn').on('click', function() {
    $('#result').html("");
    $('#player').hide();
    $('#canvas').show();

    const date = new Date();
    const fileName = date.getTime() + '.jpg';
    let scale = 1;
    if (player.videoWidth > player.videoHeight && player.videoWidth > maxPixels) {
      scale = maxPixels / player.videoWidth;
    }
    else if (player.videoWidth < player.videoHeight && player.videoHeight > maxPixels) {
      scale = maxPixels / player.videoHeight;
    }
    canvas.width = player.videoWidth * scale;
    canvas.height = player.videoHeight * scale;
    context.drawImage(player, 0, 0, player.videoWidth, player.videoHeight, 0, 0, canvas.width, canvas.height);
    fd.append("img-file", toBlob(canvas.toDataURL('image/jpeg'), 'image/jpeg'), fileName);
    $.blockUI({
      message : '<img src="/common/img/gif-load.gif" />',
      css : {
        color : '',
        border : '',
        backgroundColor : ''
      },
      overlayCSS : {
        backgroundColor: '#FFFFFF'
      }
    });

    $.ajax({
      url : apiUrl + '?api_type=detect&returnFaceAttributes=age,gender,smile,facialHair,glasses,headPose,emotion,hair,makeup,accessories',
      type : "POST",
      data: fd,
      processData: false,
      contentType : false,
      dataType : "json"
    }).done(function(data, status, xhr) {
      for (let i in data) {
        $('#result').append(
          '<span id="result' + (i + 1) + '">'
          + resultTableTemplate({
              gender : data[i].faceAttributes.gender === 'male' ? '男性' : '女性',
              age : data[i].faceAttributes.age,
              glasses : data[i].faceAttributes.glasses === 'NoGlasses' ? 'なし' : 'あり',
              anger : mathRound(data[i].faceAttributes.emotion.anger),
              contempt : mathRound(data[i].faceAttributes.emotion.contempt),
              disgust : mathRound(data[i].faceAttributes.emotion.disgust),
              fear : mathRound(data[i].faceAttributes.emotion.fear),
              happiness : mathRound(data[i].faceAttributes.emotion.happiness),
              neutral : mathRound(data[i].faceAttributes.emotion.neutral),
              sadness : mathRound(data[i].faceAttributes.emotion.sadness),
              surprise : mathRound(data[i].faceAttributes.emotion.surprise),
              eyeMakeup : data[i].faceAttributes.makeup.eyeMakeup === true ? 'あり' : 'なし',
              lipMakeup : data[i].faceAttributes.makeup.lipMakeup === true ? 'あり' : 'なし',
            })
          + '</span><br />'
        );

        context.beginPath();
        if (i % 7 === 0) {
          context.strokeStyle = 'rgb(255, 0, 0)';
          $('#result' + (i + 1)).css('color', '#FF0000');
        }
        else if (i % 7 === 1) {
          context.strokeStyle = 'rgb(0, 255, 0)';
          $('#result' + (i + 1)).css('color', '#00FF00');
        }
        else if (i % 7 === 2) {
          context.strokeStyle = 'rgb(0, 0, 255)';
          $('#result' + (i + 1)).css('color', '#0000FF');
        }
        else if (i % 7 === 3) {
          context.strokeStyle = 'rgb(0, 255, 255)';
          $('#result' + (i + 1)).css('color', '#00FFFF');
        }
        else if (i % 7 === 4) {
          context.strokeStyle = 'rgb(255, 255, 0)';
          $('#result' + (i + 1)).css('color', '#FFFF00');
        }
        else if (i % 7 === 5) {
          context.strokeStyle = 'rgb(255, 0, 255)';
          $('#result' + (i + 1)).css('color', '#FF00FF');
        }
        else if (i % 7 === 6) {
          context.strokeStyle = 'rgb(0, 0, 0)';
          $('#result' + (i + 1)).css('color', '#000000');
        }
        context.strokeRect(data[i].faceRectangle.left, data[i].faceRectangle.top, data[i].faceRectangle.width, data[i].faceRectangle.height);
      }
    }).fail(function(xhr, status, error) {
      console.log(JSON.stringify(xhr));
      console.log(status);
      console.log(error);
    }).always(function() {
      $(document).ajaxStop($.unblockUI);
    });
  });
});

}).call(this);
</script>

</body>
</html>
----- ここまで -----

木曜日, 7月 27, 2017

Azure Speech Service API (Text To Speech) サンプル

Azureの管理ポータルでの設定は省略。
HTMLファイルとPHPファイルのサンプル。
HTMLファイルだけで実装することも可能だが、APIを利用するためのキーの文字列をソースに入れたくないので、APIを利用する部分はPHPで実装する。
いずれのファイルもhttpdのドキュメントルート下の /cognitive/ に配置される前提。
サーバサイドは、Apache 2.2、PHP 5.3。
クライアントはHTML5 (Web Audio API)対応ブラウザ。
Windows10のChrome、Edgeで確認。IEはダメ。

text_to_speech.html
----- ここから -----
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Speech Service API (Text To Speech)</title>

<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<style>
<!--
.page-title {
  color: #555555;
  font-size: x-large;
  font-weight: bold;
  text-shadow: 1px 1px 1px #999999;
}
-->
</style>
</head>
<body>
<div class="container">

<div class="row">
<div class="col-sm-12">
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/cognitive/">Cognitive Services Examples</a>
</nav>
</div>
</div>

<div class="row">
<div class="col-sm-12">
<h3 class="page-title">Speech Service API (Text To Speech)</h3>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">
<strong> ロケールを選択し、テキストを入力して「送信」ボタンをクリックしてください。</strong>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">

<div class="card">
<div class="card-body">
<form id="text-to-speech-form" method="POST">

<div class="form-group row">
<label for="locale" class="col-sm-2 col-form-label">ロケール</label>
<div class="col-sm-4">
<select name="locale" id="locale" class="form-control">
<option value="ja">ja</option>
<option value="en">en</option>
</select>
</div>
</div>

<div class="form-group row">
<label for="text_for_speech" class="col-sm-2 col-form-label">テキスト</label>
<div class="col-sm-10">
<textarea name="text_for_speech" id="text_for_speech" cols="100" rows="8" class="form-control"></textarea>
</div>
</div>

<div class="form-group row">
<div class="col-sm-10 ml-sm-auto">
<button type="button" id="submit-btn" class="btn btn-primary">送信</button>
</div>
</div>

</form>
</div>
</div>

</div>
</div>

</div>

<!-- javascript -->
<script src="//code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js" integrity="sha256-9wRM03dUw6ABCs+AU69WbK33oktrlXamEXMvxUaF+KU=" crossorigin="anonymous"></script>
<script>
(function() {

"use strict";

const root = this;
const $    = root.jQuery;

root.AudioContext = root.AudioContext || root.webkitAudioContext; 
const context = new AudioContext();

// Audio 用の buffer を読み込む
const getAudioBuffer = function(url, param, fn) {
  $.blockUI({
    message : '<img src="/common/img/gif-load.gif" />',
    css : {
      color : '',
      border : '',
      backgroundColor : ''
    },
    overlayCSS : {
      backgroundColor: '#FFFFFF'
    }
  });

  const req = new XMLHttpRequest();
  req.open('POST', url, true);
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  req.responseType = 'arraybuffer';

  req.onreadystatechange = function() {
    $.unblockUI();
    if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
      context.decodeAudioData(req.response, function(buffer) {
        fn(buffer);
      });
    }
  };

  param = encodeURI(param);
  req.send(param);
};

// サウンドを再生
const playSound = function(buffer) {
  const source = context.createBufferSource();
  source.buffer = buffer;
  source.connect(context.destination);
  source.start(0);
};

$(function() {
  $('#submit-btn').on('click', function() {
    if (!$('#text_for_speech').val()) {
      alert('テキストを入力してください。');
      return false;
    }

    const textForSpeech = $('#text_for_speech').val();
    getAudioBuffer('/cognitive/text_to_speech.php', 'text_for_speech=' + textForSpeech + '&locale=' + $('#locale').val(), function(buffer) {
      playSound(buffer);
    });
  });
});

}).call(this);
</script>

</body>
</html>
----- ここまで -----

text_to_speech.php
----- ここから -----
<?php
/**
 * Azure Cognitive ServicesのSpeech Service API (Text To Speech) を使って
 * 音声データを取得する
 */

global $php_errormsg;

$access_token_url = 'https://westus.api.cognitive.microsoft.com/sts/v1.0/issuetoken';
$api_key = 'Azure管理ポータルで取得したキー';
$tts_service_url = 'https://westus.tts.speech.microsoft.com/cognitiveservices/v1';
$user_agent = 'Azure管理ポータル上のリソース名';
$output_format = 'riff-24khz-16bit-mono-pcm';
$voice_fonts = array(
  'ja' => array('ja-JP', 'ja-JP, Ichiro, Apollo'),
  'en' => array('en-US', 'en-US, BenjaminRUS')
);

// リクエストパラメータの文字列を変換した音声データを取得
if (!empty($_POST['text_for_speech'])
    and !empty($_POST['locale'])) {
  $request = 'curl -Ss -X POST "' . $access_token_url . '"'
           . ' -H "Content-type: application/x-www-form-urlencoded"'
           . ' -H "Content-Length: 0"'
           . ' -H "Ocp-Apim-Subscription-Key: ' . $api_key . '"';
  $access_token = `$request`;

  if (!$access_token) {
    throw new Exception("Problem with $access_token_url, $php_errormsg");
  }
  else {
    $locale = $_POST['locale'];
    $text_for_speech = $_POST['text_for_speech'];
    $voice_font_locale = $voice_fonts[$locale][0];
    $voice_font = $voice_fonts[$locale][1];
    $request = 'curl -Ss -X POST "' . $tts_service_url . '"'
             . ' -A "' . $user_agent . '"'
             . ' -H "X-Microsoft-OutputFormat: ' . $output_format . '"'
             . ' -H "Content-Type: application/ssml+xml"'
             . ' -H "Authorization: Bearer ' . $access_token . '"'
             . ' -H "cache-control: no-cache"'
             . ' --data'
             . ' "<speak version=\"1.0\" xml:lang=\"' . $voice_font_locale . '\">'
             . '<voice xml:lang=\"' . $voice_font_locale . '\" xml:gender=\"Male\" name=\"Microsoft Server Speech Text to Speech Voice (' . $voice_font . ')\">' . $text_for_speech . '</voice>'
             . '</speak>"';
    $result = `$request`;

    if (!$result) {
      throw new Exception("Problem with $tts_service_url, $php_errormsg");
    }
    else {
      header('Content-Type: audio/x-wav');
      header('Content-Length: ' . strlen($result));
      echo $result;
    }
  }
}
?>
----- ここまで -----

月曜日, 1月 30, 2017

Azure Face APIサンプル

Azureの管理ポータルでの設定は省略。
HTMLファイルとPHPファイルのサンプル。
HTMLファイルだけで実装することも可能だが、APIを利用するためのキーの文字列をソースに入れたくないので、その部分だけPHPで実装する。
いずれのファイルもhttpdのドキュメントルート下の /cognitive/ に配置される前提。
画像ファイルを一時的に置くため、httpdのドキュメントルート下に /tmp/cognitive/ というディレクトリを作成。
アップロードする画像が規定より大きい場合、canvasを使ってリサイズしたものをアップロードするようにしてある。要HTML5。


face_api.html
----- ここから -----
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="robots" content="noindex,nofollow" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Face API</title>

<link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<style>
<!--
@media all {
  .page-title {
    color: #555555;
    font-size: x-large;
    font-weight: bold;
    text-shadow: 1px 1px 1px #999999;
  }
  #result {
    font-size: large;
    font-weight: bold;
    text-shadow: 1px 1px 1px #333333;
  }
}

@media screen and (max-width: 480px) {
  #canvas {
    width: 90%;
    width: 90vw;
  }
}
-->
</style>
</head>
<body>
<div class="container">

<div class="row">
<div class="col-sm-12">
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="/cognitive/">Cognitive Services Examples</a>
</nav>
</div>
</div>

<div class="row">
<div class="col-sm-12">
<h3 class="page-title">Face API</h3>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">
<strong> 画像ファイルを選択し、「送信」ボタンをクリックしてください。</strong>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">

<div class="card">
<div class="card-body">
<form id="upload-img-file-form" method="POST">

<div class="form-group row">
<label for="img-file" class="col-sm-2 col-form-label">画像ファイル</label>
<div class="col-sm-4">
<input type="file" id="img-file" name="img-file"  class="form-control btn btn-default" />
</div>
</div>

<div class="form-group row">
<div class="col-sm-10 ml-sm-auto">
<button type="button" id="submit-btn" class="btn btn-primary">送信</button>
</div>
</div>

</form>
</div>
</div>

</div>
</div>
<br />

<div class="row">
<div class="col-sm-12">
<canvas id="canvas"></canvas>
</div>
</div>
<br />

<div class="row">
<div class="col-sm-4">
<div id="result"></div>
</div>
</div>

</div>

<!-- template -->
<script type="text/template" id="result-table-template">
<table class="table table-bordered">
<tr>
<td colspan="2">性別</td><td><div class="float-sm-right"><%= gender %></div></td>
</tr>
<tr>
<td colspan="2">年齢</td><td><div class="float-sm-right"><%= age %></div></td>
</tr>
<tr>
<td colspan="2">眼鏡</td><td><div class="float-sm-right"><%= glasses %></div></td>
</tr>
<tr>
<td rowspan="8">感情</td><td>Anger (怒り)</td><td><div class="float-sm-right"><%= anger %></div></td>
</tr>
<tr>
<td>Contempt (軽蔑)</td><td><div class="float-sm-right"><%= contempt %></div></td>
</tr>
<tr>
<td>Disgust (嫌悪)</td><td><div class="float-sm-right"><%= disgust %></div></td>
</tr>
<tr>
<td>Fear (恐怖)</td><td><div class="float-sm-right"><%= fear %></div></td>
</tr>
<tr>
<td>Happiness (幸福)</td><td><div class="float-sm-right"><%= happiness %></div></td>
</tr>
<tr>
<td>Neutral (ニュートラル)</td><td><div class="float-sm-right"><%= neutral %></div></td>
</tr>
<tr>
<td>Sadness (悲しみ)</td><td><div class="float-sm-right"><%= sadness %></div></td>
</tr>
<tr>
<td>Surprise (驚き)</td><td><div class="float-sm-right"><%= surprise %></div></td>
</tr>
<tr>
<td rowspan="2">化粧</td><td>目</td><td><div class="float-sm-right"><%= eyeMakeup %></div></td>
</tr>
<tr>
<td>口</td><td><div class="float-sm-right"><%= lipMakeup %></div></td>
</tr>
</table>
</script>

<!-- javascript -->
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js" integrity="sha256-G7A4JrJjJlFqP0yamznwPjAApIKPkadeHfyIwiaa9e0=" crossorigin="anonymous"></script>
<script src="//code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.blockUI/2.70/jquery.blockUI.min.js" integrity="sha256-9wRM03dUw6ABCs+AU69WbK33oktrlXamEXMvxUaF+KU=" crossorigin="anonymous"></script>
<script>
(function() {

"use strict";

const root = this;
const _    = root._;
const $    = root.jQuery;

const apiUrl = '/cognitive/face_api.php';
const resultTableTemplate
  = _.template($('#result-table-template').html());

const mathRound = function(val) {
  return (val * 100).toFixed(2);
};

const toBlob = function(base64, type) {
  const bin = atob(base64.replace(/^.*,/, ''));
  const buffer = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }

  const blob = new Blob([buffer.buffer], {
    type: type
  });
  return blob;
};

$(function() {
  let file;
  const canvas = document.getElementById('canvas');
  const context = canvas.getContext('2d');
  const fd = new FormData();
  const mimeType = new Array('image/jpeg', 'image/png', 'image/gif', 'image/bmp');
  const maxFileSizeMB = 4;
  const minPix = 36;
  const maxPixels = 4096;

  $('#img-file').on('change', function(evt) {
    $('#result').html("");
    file = evt.target.files[0];
    if (mimeType.indexOf(file.type) == -1) {
      alert('ファイル形式がJPEG、PNG、GIF、BMP以外です。');
      return false;
    }
    if (file.size > maxFileSizeMB * 1024 * 1024) {
      alert('ファイルサイズが' + maxFileSizeMB + 'MBを超えています。');
      return false;
    }

    const reader = new FileReader();
    reader.onload = (function(f) {
      return function(e) {
        const imgPath = e.target.result;
        const img = new Image();
        img.src = imgPath;
        img.onload = function() {
          if (img.width < minPix || img.height < minPix) {
            alert('画像のサイズは、縦横ともに' + minPix + 'ピクセル以上必要です。');
            return false;
          }
          let scale = 1;
          if (img.width > img.height && img.width > maxPixels) {
            scale = maxPixels / img.width;
            alert('画像の最大幅が' + maxPixels + 'ピクセルを超えています。');
            return false;
          }
          else if (img.width < img.height && img.height > maxPixels) {
            scale = maxPixels / img.height;
            alert('画像の最大高が' + maxPixels + 'ピクセルを超えています。');
            return false;
          }
          canvas.width = img.width * scale;
          canvas.height = img.height * scale;
          context.drawImage(this, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
          fd.append("img-file", toBlob(canvas.toDataURL(file.type), file.type), file.name);
        };
      };
    })(file);
    reader.readAsDataURL(file);
  });

  $('#submit-btn').on('click', function() {
    $('#result').html("");
    if (!file) {
      alert('画像を選択してください。');
      return false;
    }

    $.blockUI({
      message : '<img src="/common/img/gif-load.gif" />',
      css : {
        color : '',
        border : '',
        backgroundColor : ''
      },
      overlayCSS : {
        backgroundColor: '#FFFFFF'
      }
    });

    $.ajax({
      url : apiUrl + '?api_type=detect&returnFaceAttributes=age,gender,smile,facialHair,glasses,headPose,emotion,hair,makeup,accessories',
      type : "POST",
      data : fd,
      processData : false,
      contentType : false,
      dataType : "json"
    }).done(function(data, status, xhr) {
      for (let i in data) {
        $('#result').append(
          '<span id="result' + (i + 1) + '">'
          + resultTableTemplate({
              gender : data[i].faceAttributes.gender === 'male' ? '男性' : '女性',
              age : data[i].faceAttributes.age,
              glasses : data[i].faceAttributes.glasses === 'NoGlasses' ? 'なし' : 'あり',
              anger : mathRound(data[i].faceAttributes.emotion.anger),
              contempt : mathRound(data[i].faceAttributes.emotion.contempt),
              disgust : mathRound(data[i].faceAttributes.emotion.disgust),
              fear : mathRound(data[i].faceAttributes.emotion.fear),
              happiness : mathRound(data[i].faceAttributes.emotion.happiness),
              neutral : mathRound(data[i].faceAttributes.emotion.neutral),
              sadness : mathRound(data[i].faceAttributes.emotion.sadness),
              surprise : mathRound(data[i].faceAttributes.emotion.surprise),
              eyeMakeup : data[i].faceAttributes.makeup.eyeMakeup === true ? 'あり' : 'なし',
              lipMakeup : data[i].faceAttributes.makeup.lipMakeup === true ? 'あり' : 'なし',
            })
          + '</span><br />'
        );

        context.beginPath();
        if (i % 7 === 0) {
          context.strokeStyle = 'rgb(255, 0, 0)';
          $('#result' + (i + 1)).css('color', '#FF0000');
        }
        else if (i % 7 === 1) {
          context.strokeStyle = 'rgb(0, 255, 0)';
          $('#result' + (i + 1)).css('color', '#00FF00');
        }
        else if (i % 7 === 2) {
          context.strokeStyle = 'rgb(0, 0, 255)';
          $('#result' + (i + 1)).css('color', '#0000FF');
        }
        else if (i % 7 === 3) {
          context.strokeStyle = 'rgb(0, 255, 255)';
          $('#result' + (i + 1)).css('color', '#00FFFF');
        }
        else if (i % 7 === 4) {
          context.strokeStyle = 'rgb(255, 255, 0)';
          $('#result' + (i + 1)).css('color', '#FFFF00');
        }
        else if (i % 7 === 5) {
          context.strokeStyle = 'rgb(255, 0, 255)';
          $('#result' + (i + 1)).css('color', '#FF00FF');
        }
        else if (i % 7 === 6) {
          context.strokeStyle = 'rgb(0, 0, 0)';
          $('#result' + (i + 1)).css('color', '#000000');
        }
        context.strokeRect(data[i].faceRectangle.left, data[i].faceRectangle.top, data[i].faceRectangle.width, data[i].faceRectangle.height);
      }
    }).fail(function(xhr, status, error) {
      console.log(JSON.stringify(xhr));
      console.log(status);
      console.log(error);
    }).always(function() {
      $(document).ajaxStop($.unblockUI);
    });
  });
});

}).call(this);
</script>

</body>
</html>
----- ここまで -----

face_api.php
----- ここから -----
<?php
require_once 'HTTP/Request2.php';

if ($_FILES['img-file']['tmp_name']) {
    $req_param = "";
    foreach ($_GET as $key => $value) {
        if ($key != 'api_type') {
            $req_param .= '&' . $key . '=' . $value;
        }
    }
    if ($req_param) {
        $req_param = ltrim($req_param, '&');
        $req_param = '?' . $req_param;
    }

    $request = new Http_Request2('https://westus.api.cognitive.microsoft.com/face/v1.0/' . $_GET['api_type'] . $req_param);
    $request->setAdapter('curl');

    $headers = array(
        'Content-Type' => 'application/json',
        'Ocp-Apim-Subscription-Key' => 'Azureの管理ポータルでコピーしたキー文字列',
    );

    $request->setHeader($headers);
    $request->setMethod(HTTP_Request2::METHOD_POST);

    $uploaddir = '/var/www/html/tmp/cognitive/';
    $uploadfile = $uploaddir . basename($_FILES['img-file']['name']);
    move_uploaded_file($_FILES['img-file']['tmp_name'], $uploadfile);
    $url_base = 'http://www.hpi-portal.com/tmp/cognitive/';
    $url = $url_base . basename($_FILES['img-file']['name']);
    $data = array('url' => $url);
    $request->setBody(json_encode($data));

    try {
        $response = $request->send();
        echo $response->getBody();
        if (file_exists($uploadfile)) {
            unlink($uploadfile);
        }
    }
    catch (HttpException $ex) {
        echo $ex;
        if (file_exists($uploadfile)) {
            unlink($uploadfile);
        }
    }
}
?>
----- ここまで -----

土曜日, 1月 07, 2017