indico apiを用いたコンテンツフィルタ付き画像アップローダー

# 概要
話題の投稿["3行のソースコードを入れるだけで機械学習できると噂のindicoをNode.jsで使って機械学習入門してみる"](http://qiita.com/n0bisuke/items/868b43411078bc2018fb
)でindicoについて初めて知ったのですが、そこのContent Filteringを使えば「アダルト画像を予め排除出来る画像アップローダー」が作れるのではないかと思って試作をしてみました。
# 要件
1. ローカル画像を判定し、OKだった物だけをサーバーにアップロードする。
2. 判定は[indico](https://indico.io/)のContent Filetering APIで行う。
3. ローカルでの画像プレビュー機能を持たせる。

# 実際の手順
## 1. indico APIキーの取得
以下のサイトでアカウントを作成後、Dashboardに移動してAPI Keyを取得します。<br>
https://indico.io/
## 2. サンプルソース
DocsのページでImage Analysis ⇒ Content Filteringを選択し、さらにjavascriptを選択するとAPI KEYまで含んだ状態のexampleソースが表示されます。

```example.
// single example
$.post(
  'https://apiv2.indico.io/contentfiltering',
  JSON.stringify({
    'api_key': "<API_KEY>",
    'data': "<IMAGE>"
  })
).then(function(res) { console.log(res) });

// batch example
$.post(
  'https://apiv2.indico.io/contentfiltering/batch',
  JSON.stringify({
    'api_key': "<API_KEY>",
    'data': [
      "<IMAGE>",
      "<IMAGE>"
    ]
  })
).then(function(res) { console.log(res) });
```
## 3. \<IMAGE>について
＜IMAGE>は画像ファイルのURL、もしくはBase 64でエンコードした画像データです。画像を公開しないで判定を行う必要が有りますので、FileReader()のreadAsDataURL()メソッドを使いローカル画像ファイルから\<IMAGE>を作成しました。


## 4. 判定
結果は実数0～1のJSON形式で返されます。こんな感じです。
{"results": 0.2925722002983093}
アダルトコンテンツとみなされるほど数字が大きくなりますので、予め設定したスレッシュと比較して処理を分岐させています。

# ソース全体
プレビューにはcanvasとFileReader()を使い、アップロードはFormData()をAJAXでPOSTすることで行っています。サーバ側の処理はPHPです。

```index.html
<!DOCTYPE HTML>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <script src="http://code.jquery.com/jquery-latest.js" type="text/javascript"></script>
        <title>画像ファイルをCANVSでプレビュー</title>
    </head>
    <body>
        <h1>画像を選択してください。</h1>
        <form id="my_form">
            <input id="ufile" name="ufile" type="file" accept="image/jpeg,image/png"><br>
            <button id="upload" type="button">アップロード</button>クリックでアップロードを開始します。
        </form>
        <hr>
        <div>
            <h1>プレビュー</h1>
            <canvas id="cnvs"></canvas>
        </div>

        <script>
         $(function(){

             // Global変数
             var fr = new FileReader(); // FileReader()をインスタンス化
             var canvas = $("#cnvs");   // HTMLのCanvas要素の取得
             var gheight = 240;         // Previewイメージの高さ
             var thresh = 0.95;         // 判定基準値 さじ加減

             // id="ufile"の変化でコールバック
             $("#ufile").change(function(){
                 // 選択ファイルの有無をチェック
                 if (!this.files.length) {
                     alert('ファイルが選択されていません');
                     return;
                 }

                 // Formからファイルを取得
                 var file = this.files[0];

                 // getContext()メソッドで描画機能を有効にする
                 var ctx = canvas[0].getContext('2d');

                // 描画イメージインスタンス化
                 var image = new Image();

                // ファイル読み込み読み込み完了後に実行 [非同期処理]
                 fr.onload = function(evt) {

                     // 画像がロードされた後にcanvasに描画を行う [非同期処理]
                     image.onload = function() {
                         // プレビュー(Cnavas)のサイズを指定
                         var cnvsH = gheight;
                         var cnvsW = image.naturalWidth*cnvsH/image.naturalHeight;
                         // Cnavasにサイズアトリビュートを設定する
                         canvas.attr('width', cnvsW);
                         canvas.attr('height', cnvsH);
                         // 描画
                         ctx.drawImage(image, 0, 0, cnvsW, cnvsH);
                     }
                     // 読み込んだ画像をimageのソースに設定
                     image.src = evt.target.result; // == fr.result
                 }

                 // fileを読み込む データはBase64エンコードされる
                 fr.readAsDataURL(file);
             })

             $("#upload").click(function(){
                 if(!fr.result){
                     alert('ファイルを選択して下さい');
                     return;
                 };

                $.post(
                     'https://apiv2.indico.io/contentfiltering',
                     JSON.stringify({
                         'api_key': "<API_KRY>",
                         'data': fr.result
                     })
                 ).then(function(res) {
                     var jst = JSON.parse(res);
                     if (jst.results > thresh) {
                         alert('この画像は規約によりアップロード出来ません rating='+jst.results);
                     } else {
                         console.log(res);
                         // FormDataを用意
                         var fd = new FormData($("#my_form").get(0));
                         // ajaxでphpに送信
                         $.ajax({
                             url: 'uploader.php',
                             type: 'POST',
                             processData: false,
                             contentType: false,
                             dataType: 'html',
                             data: fd
                         }).done(function(data, textStatus, jwXHR){
                             alert(data);
                         }).fail(function(jqXHR, textStatus, errorThrown){
                             alert('エラーが発生しました : ' + textStatus
                                   + "\nHTTP status : " + errorThrown);
                         })
                     }},
                     function() {
                         alert('エラーが発生しました。時間をおいて再試行下さい。');
                     })
             })
         })
        </script>
    </body>
</html>
```

```uploader.php
<?php
define('FILE_PATH','./upload/'); //保存するパスを指定

// アップロードされたファイルが有る事を確認
if (is_uploaded_file($_FILES["ufile"]["tmp_name"])) {
    $saveFilename = FILE_PATH . $_FILES['ufile']['name'];

    // ファイルの移動
    if (move_uploaded_file($_FILES["ufile"]["tmp_name"], $saveFilename)) {
        //chmod($saveFilename, 0644);
        echo $_FILES["ufile"]["name"], "をアップロードしました";
    } else {
        echo $_FILES["ufile"]["name"], "アップロードエラー";
    }
}
?>
```

# 結果確認
Firefox 47.0での確認です。
ダメ出しされた場合
![NG.jpg](https://qiita-image-store.s3.amazonaws.com/0/121242/04ad60c4-5674-67b5-7388-f4628ac801f9.jpeg)

OKの場合
![OK.jpg](https://qiita-image-store.s3.amazonaws.com/0/121242/4737062f-3cda-8ada-5c86-b72756a1a32d.jpeg)