はじめに
株式会社マイスター・ギルド新規事業部のウサギーです。
弊社新規事業部では、新規サービスの立ち上げを目指して
日々、アイディアの検証やプロトタイプの作成を行っています。
今回も、face-api.jsネタです。
リサイズを使いこなしたい
face-api.jsを気に入ってよく使っています。
特によく使っているのが、
・顔検出
・68点顔ランドマーク検出
の2つの機能。
face-api.jsには標準で検出のリサイズ方法があるのですが、
実はあんまり使ったことが無く…
今回はここへの理解を深めたいと思います。
リサイズが必要な理由
検出に使ったソースの解像度と、画面に表示するキャンバスのサイズが一致するとは限りません。
例えば、検出ソースは640×480、表示は1280×960のような場合、
結果は「640×480の画像に対してどの位置に顔があるのか」を示すので、
そのまま表示してしまうと大きさも位置も一致しません。
イメージとしてはこんな感じ↓
そのため、検出結果を「1280×960で表示した場合」に変換する必要があります。
リサイズについて調べる
リサイズの使い方
早速悩んだのですが、
ググるととリサイズの使い方が2つ出てきました。
↑ページの Displaying Detection Results
Drawing the detected faces into a canvas:
const detections = await faceapi.detectAllFaces(input)
// resize the detected boxes in case your displayed image has a different size then the original
const detectionsForSize = faceapi.resizeResults(detections, { width: input.width, height: input.height })
// draw them into a canvas
const canvas = document.getElementById('overlay')
canvas.width = input.width
canvas.height = input.height
faceapi.drawDetection(canvas, detectionsForSize, { withScore: true })
https://github.com/justadudewhohacks/face-api.js#displaying-detection-results
↑ページの Displaying Detection Results
Preparing the overlay canvas:
const displaySize = { width: input.width, height: input.height }
// resize the overlay canvas to the input dimensions
const canvas = document.getElementById('overlay')
faceapi.matchDimensions(canvas, displaySize)
Preparing the overlay canvas:face-api.js predefines some highlevel drawing functions, which you can utilize:
/* Display detected face bounding boxes */
const detections = await faceapi.detectAllFaces(input)
// resize the detected boxes in case your displayed image has a different size than the original
const resizedDetections = faceapi.resizeResults(detections, displaySize)
// draw detections into the canvas
faceapi.draw.drawDetections(canvas, resizedDetections)
さくっと結論を述べると、
後者の matchDimensions と resizeResults を使う方法が◎です。
前者の手法は古いようです。
一つずつ見ていきます。
顔検出BOXをリサイズして表示
結果を表示するオーバーレイキャンバスを準備します。
const displaySize = { width: input.width, height: input.height };
const canvas = document.getElementById('overlay')
faceapi.matchDimensions(canvas, displaySize)
まず、表示サイズとして、検出ソースと同じwidth,heightを定義。
肝になるのは faceapi.matchDimensions(canvas, displaySize) で、
オーバーレイキャンバスを、表示サイズ(入力ソースサイズ)に変換します。
顔検出と結果の描画。
const detections = await faceapi.detectAllFaces(input)
const resizedDetections = faceapi.resizeResults(detections, displaySize)
faceapi.draw.drawDetections(canvas, resizedDetections)
1行目は顔検出を実施していますが、リサイズのためにオプションをつけたりというのは特になく、実際にリサイズにかかわる処理は2行目。
faceapi.resizeResults(detections, displaySize)
ここでリサイズを行い、3行目の draw.drawDetections はcanvasに描画しているのみ。
整理すると
matchDimensions で結果表示するキャンバスのサイズをリサイズ
resizeResults で検出結果をリサイズ
できるということです。
68点顔ランドマークをリサイズして表示
68点顔ランドマークの場合も見ておきます。
const detectionsWithLandmarks = await faceapi
.detectAllFaces(input)
.withFaceLandmarks()
const resizedResults = faceapi.resizeResults(detectionsWithLandmarks, displaySize)
faceapi.draw.drawDetections(canvas, resizedResults)
faceapi.draw.drawFaceLandmarks(canvas, resizedResults)
リサイズはBOXの場合と同じく resizeResults
表示に drawFaceLandmarks を使うだけですね。
resizeResultsは何が違うのか?
検出結果を表示するだけなら用意してある描画関数で十分ですが、
顔検出、特に顔ランドマークを利用する場合は座標値を使ったりするので、
リサイズによって何が変わっているのか、
どんなデータが入っているのか見ておこうと思います。
データ形式は同じ
顔検出のリサイズを行うと
alignedRectとdetectionの boxの座標値 と、imageDimsが変化
68点顔ランドマークのデータ(landmarksとunshiftedLandmarks)の
Dimensions、positionsの各座標、imageHeight,imgWidth、shiftの座標がリサイズされる
各データが何を表しているか?は未学習なのでスルーしますが、
例えば、顔ランドマークの各点を利用して何か描画したい!
というときは、リサイズ後のpositionsを利用すれば大丈夫そうです。
おわりに
これまでリサイズの使い方がわからなくて、入力データと表示データのサイズが異なる場合に挑むのに不安を感じることが多かったのですが、これでリサイズも使っていけそうです🐰