概要
話題の投稿"3行のソースコードを入れるだけで機械学習できると噂のindicoをNode.jsで使って機械学習入門してみる"でindicoについて初めて知ったのですが、そこのContent Filteringを使えば「アダルト画像を予め排除出来る画像アップローダー」が作れるのではないかと思って試作をしてみました。
要件
- ローカル画像を判定し、OKだった物だけをサーバーにアップロードする。
- 判定はindicoのContent Filetering APIで行う。
- ローカルでの画像プレビュー機能を持たせる。
実際の手順
1. indico APIキーの取得
以下のサイトでアカウントを作成後、Dashboardに移動してAPI Keyを取得します。
https://indico.io/
2. サンプルソース
DocsのページでImage Analysis ⇒ Content Filteringを選択し、さらにjavascriptを選択するとAPI KEYまで含んだ状態の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です。
<!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>
<?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"], "アップロードエラー";
}
}
?>