OpenCV 4.5.4から新しく導入された顔検出のAPIをC++で試しました。
YuNetのAIモデルをダウンロード
下記URLからface_detection_yunet_2023mar.onnx をダウンロードしてください。
https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet
ソースコード
動作環境
- Visual Studio 2019
- OpenCV 4.11.0
void main()
{
// カメラを開く
cv::VideoCapture cap(0);
if (!cap.isOpened()) {
std::cerr << "カメラを開けませんでした" << std::endl;
return;
}
// モデルのパスを指定
std::string modelPath = "face_detection_yunet_2023mar.onnx";
// FaceDetectorYNオブジェクトを作成
cv::Ptr<cv::FaceDetectorYN> detector = cv::FaceDetectorYN::create(modelPath, "", cv::Size(0, 0));
while (true) {
cv::Mat frame;
cap >> frame; // カメラからフレームを取得
if (frame.empty()) {
std::cerr << "フレームを取得できませんでした" << std::endl;
break;
}
// 入力画像のサイズを設定
detector->setInputSize(frame.size());
// 顔を検出
cv::Mat faces;
detector->detect(frame, faces);
for (size_t i = 0; i < faces.rows; i++)
{
// 顔検出
cv::rectangle(frame, cv::Rect(faces.at<float>(i,0), faces.at<float>(i, 1), faces.at<float>(i, 2), faces.at<float>(i, 3)), cv::Scalar(0, 255,0));
// ランドマーク
cv::circle(frame, cv::Point(faces.at<float>(i, 4), faces.at<float>(i, 5)), 3, cv::Scalar(255, 0, 0), cv::FILLED);
cv::circle(frame, cv::Point(faces.at<float>(i, 6), faces.at<float>(i, 7)), 3, cv::Scalar(255, 0, 0), cv::FILLED);
cv::circle(frame, cv::Point(faces.at<float>(i, 8), faces.at<float>(i, 9)), 3, cv::Scalar(255, 0, 0), cv::FILLED);
cv::circle(frame, cv::Point(faces.at<float>(i, 10), faces.at<float>(i, 11)), 3, cv::Scalar(255, 0, 0), cv::FILLED);
cv::circle(frame, cv::Point(faces.at<float>(i, 12), faces.at<float>(i, 13)), 3, cv::Scalar(255, 0, 0), cv::FILLED);
// スコア
cv::putText(frame, std::to_string(faces.at<float>(i, 14)), cv::Point(faces.at<float>(i, 0), faces.at<float>(i, 1) - 10), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255));
}
// 結果を表示
cv::imshow("Detected Faces", frame);
// 'q'キーが押されたらループを終了
if (cv::waitKey(1) == 'q') {
cv::destroyWindow("Detected Faces");
break;
}
}
}
結果
顔の位置とランドマークの表示ができています。(顔を隠すためにニコちゃんマークも重畳させました)
実際に動かしてみるとわかりますが、リアルタイムで問題なく検出できます。
OpenCVで簡単に使用できる顔検出の方法として、「カスケード型分類器による顔検出」がありますが、こちらの方が精度も速度も良いように感じました。
普段はC#を使用しているので、後にライブラリにして呼び出せるようにC++で書いてみました。
(OpenCVSharpではこちらのAPIは対応しておらず…)
OpenCVも日々バージョンアップしているので、いろいろお試しできると楽しいですね!