Shape Detection API を用いて、前回は 1 次元 2 次元バーコードを読み取りしました。
今回は、顔の読み取りをしてみます。
最終的に、こんなことができました。
目次
参考
用意
今回も、Experimental Web Platform feature
を有効化する必要があります。
WebNFC を試そう を参照して設定ください。
今回動作を確認していたところ、Android 版 Chrome では動作しませんでした。
Windows 版 Chrome を使用します。
実装 1(画像の中の顔を読み取る)
まずは、画像の中の顔を読み取りしてみます。
test1.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <html> <head> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous" ></script> </head> <body> <img src="face.png" id="source" /> <div id="result"></div>
<script type="text/javascript" src="app1.js"></script> </body> </html>
|
app1.js1 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
| const faceDetector = new FaceDetector();
const image = document.getElementById("source");
let faces = null;
window.onload = async () => { try { faces = await faceDetector.detect(image); } catch (e) { $("#result").text("ERROR"); }
if (faces == null) { return; }
let resultText = ""; for (const face of faces) { resultText += ` <ul> <li>width = ${face.boundingBox.width}</li> <li>height = ${face.boundingBox.height}</li> <li>x = ${face.boundingBox.x}</li> <li>y = ${face.boundingBox.y}</li> </ul> `; } $("#result").html(resultText); };
|
detect()
のレスポンスには、顔の座標だけではなくlandmarks
という配列で顔のパーツを返す要素があるのですが取得できていませんでした。
今回は表示項目からも省略しています。
確認 1
動かしてみた様子は、以下のようになります。
顔写真の素材は、photoAC - AI 人物素材(ベータ版)を使用しました。
最初は、レナを使おうかと考えたんですが、どうも利用にあたっての問題点がありそうなので取りやめています。
実装 2 カメラから読み込む
バーコードの読み取りの時と同様に、カメラから取得した画像顔の位置を取得し、枠を描いてみます。
test2.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <html> <head> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=" crossorigin="anonymous" ></script> </head> <body> <div id="canvas_area"> <canvas id="result" width="300" height="300"></canvas> </div> <div id="result_text"></div> <script type="text/javascript" src="app2.js"></script> </body> </html>
|
app2.js1 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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| const faceDetector = new FaceDetector();
const image = document.createElement("video");
const offscreen_canvas = document.createElement("canvas"); const offscreen_context = offscreen_canvas.getContext("2d");
const canvas = document.querySelector("#result"); const context = canvas.getContext("2d");
offscreen_canvas.width = canvas.width; image.videoWidth = canvas.width; offscreen_canvas.height = canvas.height; image.videoHeight = canvas.height;
let faces = null;
window.onload = async () => { const stream = await navigator.mediaDevices.getUserMedia({ video: true, });
image.srcObject = stream; image.play();
analysis();
setInterval(() => { reflesh(); }, 800); };
const analysis = async () => { offscreen_context.drawImage(image, 0, 0);
try { faces = await faceDetector.detect(image); } catch { window.requestAnimationFrame(analysis); return; }
let state = true;
if (faces == null) { state = false; } if (state == true && faces.length == 0) { state = false; }
console.log(faces);
if (state) { offscreen_context.strokeStyle = "rgb(255, 0, 0) "; offscreen_context.lineWidth = 10;
faces.forEach((face) => { offscreen_context.beginPath(face.boundingBox.x, face.boundingBox.y); offscreen_context.lineTo( face.boundingBox.x + face.boundingBox.width, face.boundingBox.y ); offscreen_context.lineTo( face.boundingBox.x + face.boundingBox.width, face.boundingBox.y + face.boundingBox.height ); offscreen_context.lineTo( face.boundingBox.x, face.boundingBox.y + face.boundingBox.height ); offscreen_context.lineTo(face.boundingBox.x, face.boundingBox.y); offscreen_context.closePath(); offscreen_context.stroke(); }); } context.drawImage(offscreen_canvas, 0, 0, canvas.width, canvas.height); window.requestAnimationFrame(analysis); };
const reflesh = () => { $("#result_text").empty(); if (faces == null) { $("#result_text").text("ERROR"); return; } if (faces.length == 0) { $("#result_text").text("ERROR"); return; } let resultText = ""; for (const face of faces) { resultText += ` <ul> <li>width = ${face.boundingBox.width}</li> <li>height = ${face.boundingBox.height}</li> <li>x = ${face.boundingBox.x}</li> <li>y = ${face.boundingBox.y}</li> </ul> `; }
$("#result_text").html(resultText); };
|
確認 2
photoAC - AI 人物素材(ベータ版)のサイトをカメラで撮影してみまた。
複数人の顔を取得し、枠を描くことができました。
気が付いたこととして、サイズが違いすぎる(遠近の差があるなど)と小さいほうが取得されにくいようです。
今回は、Shape Detection API で顔の座標取得を試みました。
実験的な API だけあり、Android と Mac で動作ができず、気が付くのに小一時間要してしまいました。
バーコードの時は Android でできていたのでそこに固執してしまったんですね。
反省です。
ではでは。