駆逐してやる...!飯テロ画像を...1枚残らず...!
Twitterに蔓延る飯テロ画像、許すまじ。
という事で、Twitter上の飯テロ画像を機械学習で検知し、ブロックするクローム拡張機能を作りました。
拡張機能としてリリースしているので、よろしければこちらのリンクからDLしてください!
Chrome拡張機能 - 飯テロブロッカー
ソースコードも公開中です。
https://github.com/Nemurino-kai/FoodPornBlocker
以下、拡張機能の仕組みなどについて、技術的な話をしていきます。
導入したライブラリ
次の外部のライブラリを導入しました。
-
ml5.js
- Tensorflowをjavascript上で動かすことができます。
- jQuery
- HTML操作のため。
大まかな仕組み
1.ドメインがtwitter.comならjsを起動、ページを監視
2.ページに変化があったら、ページの画像部分を抽出してくる
3.画像をImageNetで学習したMobileNetに入力し、クラス分類する。
4.分類されたクラスが食品なら、画像を置き換える。食品でなければ、そのまま表示
1.ページを監視する
var observer = new MutationObserver(replace_image);
observer.observe(document, {
childList: true,
subtree: true,
});
MutationObserverは、「Mutation(変化)」を「Observe(監視)」するオブジェクトです。
変化があったとき、ここではreplace_image関数が実行されます。
2.ページに変化があったら、ページの画像部分を抽出してくる
function replace_image() {
$('div').each(function(){
if (this.parentNode && (this.parentNode.getAttribute("aria-label")=="画像"||this.parentNode.getAttribute("aria-label")=="Image") && !this.classList.contains('processed')){
this.classList.add('processed');
var image_url = this.style.backgroundImage.match(/\"(.+)\"/)[1]
console.log("loaded:" + image_url);
// いったん隠す
this.style.backgroundImage = 'url()';
ページ内で、親ノードのaria-labelが"画像"or"Image"である要素を探します。
この条件に当てはまるノードが、画像へのリンクを持っています。
無駄な処理を避けるため、処理済のelementには'processed'クラスを付与し、繰り返し処理を行わないようにしています。
その後、elementから画像のurlを取得し、一旦画像を隠します。
3.画像をImageNetで学習したMobileNetに入力し、クラス分類する。
app(image_url).then(
(answer) =>{
if(isFood(answer)){
this.style.backgroundImage='url(' + chrome.runtime.getURL("replace.jpg") + ')';
}else{
this.style.backgroundImage='url(' + image_url + ')';
}
}
)
次は取得した画像urlをapp関数に入れます。app関数は画像のurlを入力として、予測クラスを返り値とする関数です。
ではここで、app関数を確認してみましょう。
async function app(set_src) {
const model_url = chrome.runtime.getURL("mobilenet_v1_025_224/model.json")
const classifier = ml5.imageClassifier(model_url, modelLoaded);
console.log("seted:" + set_src);
// 画像分類器に入れるelementの生成
let imgEl = document.createElement('img');
//CORSに引っかからないように
imgEl.crossOrigin = "anonymous"
imgEl.src = set_src
var answer
await classifier.classify(imgEl)
.then(results => {
console.log("classified:"+set_src);
console.log(results[0].label);
answer = results[0].label;
});
return answer
}
まず初めに、ローカルに保存しておいたMobileNetのモデルをロードします。(モデルはこちらからDLできます)
次に分類器に入力するためのElementを作成します。ml5.jsでは、imgタグをそのまま入力してあげれば、結果を返してくれます。(便利!)
このElementを分類器に入れればおkです。
4.分類されたクラスが食品なら、画像を置き換える。食品でなければ、そのまま表示
app(image_url).then(
(answer) =>{
if(isFood(answer)){
this.style.backgroundImage='url(' + chrome.runtime.getURL("replace.jpg") + ')';
}else{
this.style.backgroundImage='url(' + image_url + ')';
}
}
)
app関数の実行後、予測クラスをisFood関数に入力しています。isFood関数は、予測クラスを入力し、それが食品であるかの真偽値を返す関数です。
isFoodが真であれば、飯テロ画像として、画像のurlを置き換えます。
isFoodが偽であれば、飯テロ画像ではないので、画像のurlを元に戻します。よって、画像は普通に表示されます。
おわりに
機械学習を手軽にWeb開発に組み込める、ml5.js、他にも色々な使い道があって面白そうです。夢が膨らみますね✨