Google Chromeの拡張を作ってみたい③ 顔認識

スポンサーリンク
スポンサーリンク

顔認識

外部ライブラリとか、WebAPIとか、無くても、ブラウザだけあれば顔認識ができるみたいなので試してみる。

スポンサーリンク

Shape Detection API

flags

試験的な機能なようで、有効化しないと使えない。

chrome://flags/#enable-experimental-web-platform-features

に行って、Experimental Web Platform featuresをEnabledにする。

スポンサーリンク

contentScript.js

動作確認

ていうかそもそも拡張機能内で使えるんだろうかと、ふと頭をよぎったのでお試しをしてみる。

https://dalomo.net/blog/files/face/index.html

こんなんを作りまして、codepenのコードを参考に

debugger;
var image = document.getElementsByTagName('img');

try {
    if (window.FaceDetector == undefined) {
        console.error('Face Detection not supported');
        throw new Error();
    }

    console.log('ignition!');
    var faceDetector = new FaceDetector();
    faceDetector.detect(image[0])
        .then(faces => {
            console.log('yes');
        });

} catch (error) {

}

うーんと、imgタグをgetElementsByTagNameで取得。facedetecterが有効化されてるかチェック。元はreturnだったけど、returnだとなんかエラーになるのでtry-catchでexit代わり。そしてnewして、faceDetector.detect(image[0])で検出。image[0]の部分は

It takes an image object (either a CanvasImageSource, Blob, ImageData or an <img> element)

が対応してるみたい。thenだから非同期処理なんだなこれ。そんで、動かしてみますと

あっ!なんかいい感じな気がする!!!!!やっぱりlandmark(目・鼻・口)の位置はまだ取れないみたい。そしたらcanvasのあれやこれやを追加してみる。

canvasを追加

debugger;
var image = document.getElementsByTagName('img');
var ccan = document.createElement('canvas');
document.body.insertBefore(ccan, image[0].nextSibling);

ccan.width = image[0].width;
ccan.height = image[0].height;

var canvas = document.getElementsByTagName('canvas');

var ctx = canvas[0].getContext("2d");
ctx.drawImage(image[0],
    0, 0, image[0].width, image[0].height,
    0, 0, ccan.width, ccan.height);

const scale = ccan.width / image[0].width;

try {
    if (window.FaceDetector == undefined) {
        console.error('Face Detection not supported');
        throw new Error();
    }

    console.log('ignition!');
    var faceDetector = new FaceDetector();
    faceDetector.detect(image[0])
        .then(faces => {
            console.log('yes');

            ctx.lineWidth = 2;
            for (var i = 0; i < faces.length; i++) {
                const face = faces[i].boundingBox;
                ctx.beginPath();
                ctx.strokeStyle = "red";
                ctx.rect(Math.floor(face.x * scale),
                    Math.floor(face.y * scale),
                    Math.floor(face.width * scale),
                    Math.floor(face.height * scale));
                ctx.stroke();
            }
        });

} catch (error) {

}

愚直に書いた。えーとまず、canvasタグは自分の方には書いてなかったのでcreateElementでcanvasを作る。body直下、子っていうらしい、にimgタグがあるのでimgタグのすぐ下にcanvasが作られるようdocument.body.insertBefore(ccan, image[0].nextSibling)てやる。canvasのサイズと画像サイズを同じにして、getContext(“2d”)で描画機能を有効にする。で、drawImageで元画像をcanas上に描画する。コピーみたいな感じか。サイズの比率をとっとく。これはあんま使わなかった。lineWidthで線の太さを2にして、facesの中の顔の数だけ描画してく。faces.boundingBoxには画像内の顔を囲む四角の座標が入ってる。x, yが四角の左上の座標、width, heightが四角の幅と高さ、top, right, bottom, leftがそれぞれ画像内での、上辺のy座標・右辺のx座標・底辺のy座標・左辺のx座標と思われる。そしたら描画に入ってく。beginPathでリセット…初期化みたいなもんかな、して、strokeStyleで色を設定。rectで矩形の設定、左上の座標と幅高をさっきのboundingBoxの値を利用する。Math.floorで切り捨て整数にする。strokeで描画を実行しますと、結果は

うおー!できた!けどあれ?なんか2人いる…。なんでかなーと思ってcodepenの例見たらhtmlのいmgタグにhidden属性が付けられてた。ただこの状態からelem.style.visibility = “hidden”するとそこだけぽっかり空くみたいな感じになるみたい。なので、image[0].style.display = “none”ってした。

1人になった!よっしゃ、そしたら全部の画像にやってみよう。

サンプルページ内の画像全てに適用

debugger;
var image = document.getElementsByTagName('img');
var ctx = [];
for (var j = 0; j < image.length; j++) {
    var ccan = document.createElement('canvas');
    document.body.insertBefore(ccan, image[j].nextSibling);

    ccan.width = image[j].width;
    ccan.height = image[j].height;

    var canvas = document.getElementsByTagName('canvas');

    ctx[j] = canvas[j].getContext("2d");
    ctx[j].drawImage(image[j],
        0, 0, image[j].width, image[j].height,
        0, 0, ccan.width, ccan.height);

    const scale = ccan.width / image[j].width;

    try {
        if (window.FaceDetector == undefined) {
            console.error('Face Detection not supported');
            throw new Error();
        }
        image[j].style.display = "none";
        console.log('ignition!');
        var faceDetector = new FaceDetector();

        var n = 0;
        faceDetector.detect(image[j])
            .then(faces => {
                console.log('yes');
                
                ctx[n].lineWidth = 2;
                for (var i = 0; i < faces.length; i++) {
                    const face = faces[i].boundingBox;
                    ctx[n].beginPath();
                    ctx[n].strokeStyle = "red";
                    ctx[n].rect(Math.floor(face.x * scale),
                        Math.floor(face.y * scale),
                        Math.floor(face.width * scale),
                        Math.floor(face.height * scale));
                    ctx[n].stroke();
                }
                n++;
            });
            
    } catch (error) {

    }
}

おあー…、動くことを目指して一生懸命書いてたらすっげぇ汚ねぇコードになった…。forで回せばなんとかなんだろと思って書き始めたけど、forのブロック内に非同期処理があると、非同期処理側の処理の完了を待たずに処理が進んでしまうので、思ったとおりに動かない。非同期処理なんだから当たり前なんだけど。どうしよっかなーとデバッガでステップインしながら辻褄が合うように直してったらこうなった。ステップインしながら思ったんだけど、for内のカウントが全部終了してから非同期処理の中をやってくみたいな順番なのね。それはそういうもんなのか、それとも自分の書き方がこうだからそうなったのか、調べる気力は湧きませんでした。

赤線の所まで行ったらforの最初に戻って繰り返し。条件式満たしたら、then以下が実行されてった。うーん、どうすればキレイに書けるんだろう、検索語句も分からん。あ、で、結果

できた…!精度的には真正面だとまぁ認識されて、逆さ顔とか、横顔とか画像がちっちゃかったりとかだとあんま認識されないみたい。まぁサンプル数=1だからなんとなくだけど。

スポンサーリンク

参考

Accelerated Shape Detection in Images
Face Detection in Images Demo w/ overlay AND landmarks
Example code for the Accelerated Shape Detection in Images WICG Spec (
Face detection in-browser
...
Face detection using Shape Detection API - Modern Web Development with Chrome by Paul Kinlan
JSで3分でできる「 顔認識 」Shape Detection API(ネット接続も不要) - Qiita
WebAPI使わず「 顔認識 」ができる Shape Detection API とは? Shape Detection API は現在最新のChromeブラウザで動作確認できるJavaScriptAPIです。 Shape...
顔にモザイクをかけてFace Detection APIを試してみました | フロントエンドBlog | ミツエーリンクス
Google Chrome 70よりOrigin Trialsとされた、Shape Detection API内で定義されているFace Detection APIを試してみました。 Face Detection APIは画像内から...
Face Detection APIを試したメモ - console.lealog();
なんと、ブラウザで顔認識ができる時代になりました。試したのはChrome 70.0.3530.0(Official Build)canary (64 ビット)で。 仕様 Accelerated Shape Detection in Images Accelerated Shape Detection in Images...
canvasの画像の上に描画する方法について|teratail
const SW = window.innerWidth;const SH = window.screen.height;let can = document.getElementById("can");let ctx = can.getContext("2d");const img01 = new
Document.createElement()
HTML 文書において、 document.createElement() メソッドは tagName で指定された HTML 要素を生成し、または tagName が認識できない場合は HTMLUnknownElement を生成します。
ライブラリを使わない素のJavaScriptでDOM操作 - Qiita
jQueryやその他ライブラリを使わない、素のJavaScriptでのDOM操作関連をまとめてみました。 要素の検索 //id指定 document.getElementById('id'); //class指定 docume...
JavaScript try catchによる例外処理 exit() のような処理 エラー処理
Show/hide image with JavaScript
I have an HTML page with an image that I set to be invisible by CSS visibility: hidden. I want to make a link called "Show image", so that when I click on it, t...

コメント

タイトルとURLをコピーしました