前回のJavaScript ユーザーが画像をアップロードできる15パズルをつくるではユーザーが自由に画像をアップロードできるようにしたのですが、これだと不適切な画像(エロ画像など)をアップすることもできてしまいます。それでは困るので不適切な画像を検出して削除できるようにします。まずは不適切な画像を検出できるようにします。
では、どうすれば不適切な画像を検出することができるのでしょうか? 探してみるとNSFW JSが見つかりました。
Contents
NSFW JSを使ってみる
NSFWとはNot Safe For Workの略で、職場では見るのに相応しくないコンテンツのことを指すようです。またNSFW JSというJavaScriptライブラリはTensorFlow.jsのマシンラーニングをベースに画像を識別するものです。画像を以下の5つのクラスに分類します。
Neutral:普通の画像
Porn:ポルノ画像
Sexy:性的画像
Hentai:読んで字のごとし
Drawing:わからない
GitHubでコードを公開しています。example/minimal_demo/index.htmlを見てみるとクライアントサイドでも動いてくれるようです。これを以下のように書き換えてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>画像をテストする</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> td { padding: 5px 15px 5px 15px; border: 1px solid #333; } </style> </head> <body> <input type="file" id = "filename"> <button id = "check" onclick="check()">チェック</button> <p><img src = "" id = "image"></p> <div id = "result"></div> <script src="https://unpkg.com/@tensorflow/tfjs@2.6.0"></script> <script src="https://unpkg.com/nsfwjs@2.3.0"></script> <script> class Rediction { constructor(className, probability){ this.ClassName = className; this.Probability = probability; } } let $result = document.getElementById('result'); let $check = document.getElementById('check'); // モデルがロードされるまで操作できないようにする $result.innerHTML = '準備中'; $check.disabled = "disabled"; let model; nsfwjs.load().then((m) => { model = m; $result.innerHTML = 'チェック可能です'; $check.disabled = null; }); // [チェック]ボタンがクリックされたら選択された画像を判定する async function check(){ let files = document.getElementById('filename').files; // ファイルが選択されていないときはなにもしない if(files.length == 0) return; // 判定中はボタンをクリック不可にする $check.disabled = "disabled"; $result.innerHTML = '判定中です'; // 選択されているファイルをDataURLに変換(非同期処理) let dataUrl = await new Promise(resolve => { const reader = new FileReader(); reader.readAsDataURL(files[0]); reader.onload = (e) => { resolve(e.target.result); } }); // 結果を受け取りRedictionオブジェクトの配列に格納する let predictions = await new Promise(resolve => { $image.src = dataUrl; $image.onload = () => { model.classify($image).then(predictions => { let text = ''; let arr = []; for(let i=0; i<predictions.length; i++) arr.push(new Rediction(predictions[i].className, predictions[i].probability)); resolve(arr); }); }; }); // Redictionオブジェクトの配列から結果と確率をパーセンテージ(小数点1桁まで)で表示する let table = '<table>'; for(let i=0; i < predictions.length; i++){ let probability = Math.round(predictions[i].Probability * 1000) / 10; table += `<tr><td>${predictions[i].ClassName}</td><td>${probability} %</td></tr>`; } table += '</table>'; $result.innerHTML = table; $check.disabled = null; } </script> </body> </html> |
エロ画像は検出できるのか?
実際に画像を選んで動作させてみると
最後のPorn(ポルノ)が6.7%とやや高いのが気になります(おかしいな)。
また実際の画像は掲載しませんがエロ画像で実験してみると、Pornが高い数値となりNeutralは非常に小さな値になります。これでエロ画像対策はできそうです。