def setup
createCanvas(320, 240)
@video = createCapture(VIDEO)
@results = []
@video.elt.onloadeddata = proc {
# キャンバスに対する縮小率を計算
# (ロードが完了するまで videoサイズは不定)
@sx = width / @video.width
@sy = height / @video.height
# p @sx, @sy
ML5.face(@video, @results)
@video.hide
}
end
def draw
background(220)
image(@video, 0, 0, width, height)
scale(@sx, @sy) if @sx and @sy # 検出座標をキャンバスサイズに一致させる
@results.each do |faces|
# p faces
faces.each do |face|
stroke(255)
strokeWeight(3)
noFill
rect(face._box.x,
face._box.y,
face._box.w,
face._box.h)
face[:scaledMesh].each do |p|
fill(0, 255, 0)
noStroke
ellipse(p[0], p[1], 3, 3)
end
end
end
end
カメラ映像からリアルタイムに顔を検出します。
| 引数名 | 内容 | 備考 | オプション | デフォルト値 |
|---|---|---|---|---|
| video | カメラ映像 | p5.Element | ||
| results | 検出結果をセットする配列 | 後述の備考の記載を参照 |
なし
・ML5.faceメソッドは setupメソッドで使用してください。
その後、カメラ画像に対象が検出されるたびに
配列「results」にデータがセットされるので、その内容を drawメソッドで処理してください。
・配列「results」には下記のようなデータ構造の検出結果がセットされます。
[ # 検出された人数分の要素(ハッシュ)を持つ配列
{ # 1人目
faceInViewConfidence: r, # 存在確率
boundingBox: { # 境界領域
topLeft: [ [232.28, 145.26] ], # 左上座標
bottomRight: [ [449.75, 308.36] ], # 右下座標
},
_box: { # 境界領域(boundingBoxの値をもとに設定)
x: x, # 左上x座標
y: y, # 左上y座標
w: w, # 幅
h: h # 高さ
}
mesh: [ # ポイント(468ヶ所)
[x0, y0, z0],
[x1, y1, z1],
...
...
...
[x467, y467, z467]
],
scaledMesh: [ # スケーリング済みポイント(468ヶ所)
[x0, y0, z0],
[x1, y1, z1],
...
...
...
[x467, y467, z467]
],
annotations: { # 部位ごとのスケーリング済みポイント
silhouette: [ # シルエット (36ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x35, y35, z35]
],
lipsUpperOuter: [ # 唇 上外側 (11ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x10, y10, z10]
],
lipsLowerOuter: [ # 唇 下外側 (10ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x9, y9, z9]
],
lipsUpperInner: [ # 唇 上内側 (11ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x10, y10, z10]
],
lipsLowerInner: [ # 唇 下内側 (11ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x10, y10, z10]
],
rightEyeUpper0: [ # 右目 上側0 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
rightEyeLower0: [ # 右目 下側0 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
rightEyeUpper1: [ # 右目 上側1 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
rightEyeLower1: [ # 右目 下側1 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
rightEyeUpper2: [ # 右目 上側2 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
rightEyeLower2: [ # 右目 下側2 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
rightEyeLower3: [ # 右目 下側3 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
rightEyebrowUpper: [ # 右眉 上側 (8ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x7, y7, z7]
],
rightEyebrowLower: [ # 右眉 上側 (6ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x5, y5, z5]
],
leftEyeUpper0: [ # 左目 上側0 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
leftEyeLower0: [ # 左目 下側0 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
leftEyeUpper1: [ # 左目 上側1 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
leftEyeLower1: [ # 左目 下側1 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
leftEyeUpper2: [ # 左目 上側2 (7ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x6, y6, z6]
],
leftEyeLower2: [ # 左目 下側2 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
leftEyeLower3: [ # 左目 下側3 (9ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x8, y8, z8]
],
leftEyebrowUpper: [ # 左眉 上側 (8ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x7, y7, z7]
],
leftEyebrowLower: [ # 右眉 下側 (6ヶ所)
[x0, y0, z0],
[x1, y2, z3],
...
[x5, y5, z5]
],
midwayBetweenEyes: [ # 両目の間(1ヶ所)
[x0, y0, z0]
],
noseTip: [ # 鼻 先端 (1ヶ所)
[x0, y0, z0]
],
noseBottom: [ # 鼻 底 (1ヶ所)
[x0, y0, z0]
],
noseRightCorner: [ # 鼻 右隅 (1ヶ所)
[x0, y0, z0]
],
noseLeftCorner: [ # 鼻 左隅 (1ヶ所)
[x0, y0, z0]
],
rightCheek: [ # 右頬 (1ヶ所)
[x0, y0, z0]
],
leftCheek: [ # 左頬 (1ヶ所)
[x0, y0, z0]
]
}
}
]