はじめに
この記事は下記のリアルタイム動作版です.一部の説明を省略しています.
本記事のコードが書かれているColabノートブック
本記事のコードは下記のノートブックでも公開されています.
動作環境
2022.11.25時点の最新版です.
- Google Colaboratory https://colab.research.google.com/
- MacBook Air (M2, 2022)
- Chrome 107.0.5304.121(Official Build) (arm64)
コードの紹介
本記事では,Google Colaboratory (以下Colab) でWebカメラをリアルタイムキャプチャし,フレームごとにdnn
モジュールにより顔検出を適用するプログラムを紹介しています.本記事のコードは,下記の記事を大いに参考にしております.ありがとうございます.
ネットワークと学習済みのモデルを入手します.今回はColab上でコマンドを実行する場合の例を挙げます.
!wget -N https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
!wget -N https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel
以下がColabにおけるリアルタイム顔検出のコードです.
# Google Driveをマウント
from google.colab import drive
drive.mount('/content/gdrive')
# imshowサポートパッチのインポート
from google.colab.patches import cv2_imshow
# dnn用
from IPython.display import display, Javascript
from google.colab.output import eval_js
from base64 import b64decode
# その他パッケージ
import imutils
import numpy as np
import matplotlib.pyplot as plt
import cv2
import IPython
from google.colab import output
from PIL import Image
from io import BytesIO
import base64
def face_detection(_img):
# 幅400画素になるようにリサイズする
_img = imutils.resize(_img, width=400)
(h, w) = _img.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(_img, (300, 300)), 1.0, (300, 300), (104.0, 177.0, 123.0))
# 物体検出器にblobを適用する
# print("[INFO] computing object detections...")
net.setInput(blob)
detections = net.forward()
# confidenceが0.5を超える領域をマーク
for i in range(0, detections.shape[2]):
# ネットワークが出力したconfidenceの値を抽出する
confidence = detections[0, 0, i, 2]
# confidenceの値が0.5以上の領域のみを検出結果として描画する
if confidence > 0.5:
# 対象領域のバウンディングボックスの座標を計算する
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
# バウンディングボックスとconfidenceの値を描画する
text = "{:.2f}%".format(confidence * 100)
y = startY - 10 if startY - 10 > 10 else startY + 10
cv2.rectangle(_img, (startX, startY), (endX, endY), (0, 0, 255), 2)
cv2.putText(_img, text, (startX, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
return _img
def run(img_str):
#decode to image
decimg = base64.b64decode(img_str.split(',')[1], validate=True)
decimg = Image.open(BytesIO(decimg))
decimg = np.array(decimg, dtype=np.uint8);
decimg = cv2.cvtColor(decimg, cv2.COLOR_BGR2RGB)
out_img = face_detection(decimg)
#encode to string
_, encimg = cv2.imencode(".jpg", out_img, [int(cv2.IMWRITE_JPEG_QUALITY), 80])
img_str = encimg.tobytes()
img_str = "data:image/jpeg;base64," + base64.b64encode(img_str).decode('utf-8')
return IPython.display.JSON({'img_str': img_str})
output.register_callback('notebook.run', run)
def use_cam(quality=0.8):
js = Javascript('''
async function useCam(quality) {
const div = document.createElement('div');
document.body.appendChild(div);
//video element
const video = document.createElement('video');
video.style.display = 'None';
const stream = await navigator.mediaDevices.getUserMedia({video: true});
div.appendChild(video);
video.srcObject = stream;
await video.play();
//canvas for display. frame rate is depending on display size and jpeg quality.
display_size = 500
const src_canvas = document.createElement('canvas');
src_canvas.width = display_size;
src_canvas.height = display_size * video.videoHeight / video.videoWidth;
const src_canvasCtx = src_canvas.getContext('2d');
src_canvasCtx.translate(src_canvas.width, 0);
src_canvasCtx.scale(-1, 1);
div.appendChild(src_canvas);
const dst_canvas = document.createElement('canvas');
dst_canvas.width = src_canvas.width;
dst_canvas.height = src_canvas.height;
const dst_canvasCtx = dst_canvas.getContext('2d');
div.appendChild(dst_canvas);
//exit button
const btn_div = document.createElement('div');
document.body.appendChild(btn_div);
const exit_btn = document.createElement('button');
exit_btn.textContent = 'Exit';
var exit_flg = true
exit_btn.onclick = function() {exit_flg = false};
btn_div.appendChild(exit_btn);
// Resize the output to fit the video element.
google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);
var send_num = 0
// loop
_canvasUpdate();
async function _canvasUpdate() {
src_canvasCtx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, src_canvas.width, src_canvas.height);
if (send_num<1){
send_num += 1
const img = src_canvas.toDataURL('image/jpeg', quality);
const result = google.colab.kernel.invokeFunction('notebook.run', [img], {});
result.then(function(value) {
parse = JSON.parse(JSON.stringify(value))["data"]
parse = JSON.parse(JSON.stringify(parse))["application/json"]
parse = JSON.parse(JSON.stringify(parse))["img_str"]
var image = new Image()
image.src = parse;
image.onload = function(){dst_canvasCtx.drawImage(image, 0, 0)}
send_num -= 1
})
}
if (exit_flg){
requestAnimationFrame(_canvasUpdate);
}else{
stream.getVideoTracks()[0].stop();
}
};
}
''')
display(js)
data = eval_js('useCam({})'.format(quality))
# ネットワークと学習済みモデルをロードする
print("[INFO] loading model...")
prototxt = 'deploy.prototxt'
model = 'res10_300x300_ssd_iter_140000.caffemodel'
net = cv2.dnn.readNetFromCaffe(prototxt, model)
use_cam()
動作結果
(顔検出部でなく) カメラキャプチャ部がフレームレートに影響しています.M2 MacBook Airでは概ね1fps程度でした.