C++とOpenCVで目を検出する方法|Haarカスケード分類器の使い方を解説
OpenCVを使えば、カメラ映像から顔や目をリアルタイムに検出することができます。本記事では、C++言語とOpenCVを組み合わせて目を検出する方法を、実際のサンプルコードとともに詳しく解説します。
目の検出に必要な準備
目の検出には、OpenCVに標準で同梱されているhaarcascade_eye.xmlというカスケード分類器を使用します。このファイルは、OpenCVをインストールしたフォルダ内の「C:/opencv/sources/data/haarcascades」ディレクトリに格納されています。
また、プログラムには以下のヘッダーファイルをインクルードする必要があります。
- <iostream>:C++標準ライブラリの入出力ストリーム用ヘッダーです。
- highgui.hpp:画像の読み書きやウィンドウ表示など、ユーザーインターフェース関連の機能が定義されています。
- imgproc.hpp:画像の品質向上やフィルタリングなど、画像処理のための機能を提供します。
- objdetect.hpp:顔や目といった物体の検出機能を提供するヘッダーです。
目を検出・追跡するサンプルコード
以下のプログラムは、Webカメラから映像を取得し、まず顔を検出して楕円で囲み、さらにその顔領域(ROI)の中から目を検出して青い円でマーキングする例です。ESCキーを押すと終了します。
#include<iostream>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/objdetect/objdetect.hpp>
using namespace cv;
using namespace std;
int main() {
Mat frame;//ビデオフレームを格納する行列//
namedWindow("Detect");//結果を表示するためのウィンドウ//
VideoCapture image(0);//デフォルトカメラから映像を取得//
if (!image.isOpened()){ //映像ソースが見つからない場合のエラー表示//
cout << "Couldn't load video from the source.Make sure your camera is working properly." << endl;
system("pause");
return 0;
}
double height = image.set(CAP_PROP_FRAME_HEIGHT, 480);//各フレームの高さを設定//
double width = image.set(CAP_PROP_FRAME_WIDTH, 640);//各フレームの幅を設定//
CascadeClassifier face_cascade, eyes_cascade;//CascadeClassifierオブジェクトの宣言//
face_cascade.load("C:/opencv/sources/data/haarcascades/haarcascade_frontalface_alt.xml");//顔検出用カスケード分類器の読み込み//
eyes_cascade.load("C:/opencv/sources/data/haarcascades/haarcascade_eye.xml");//目検出用カスケード分類器の読み込み//
while (true) {
bool temp = image.read(frame);//映像ソースからフレームを読み込む//
vector<Rect>faces;//facesという名前のベクターを宣言//
face_cascade.detectMultiScale(frame, faces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(100, 100));//顔を検出//
for (int i = 0; i < faces.size(); i++){ //顔の位置を特定//
Point center(faces[i].x + faces[i].width * 0.5, faces[i].y + faces[i].height * 0.5);//顔の中心座標を取得//
ellipse(frame, center, Size(faces[i].width * 0.5, faces[i].height * 0.5), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0);//顔に楕円を描画//
Mat faceROI = frame(faces[i]);//顔領域を目の検出対象(ROI)として取得//
vector<Rect>eyes;//eyesという名前のベクターを宣言//
eyes_cascade.detectMultiScale(faceROI, eyes, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(5, 5));//各顔の中から目を検出//
for (size_t j = 0; j < eyes.size(); j++){ //目の位置を特定//
Point center(faces[i].x + eyes[j].x + eyes[j].width * 0.5, faces[i].y + eyes[j].y + eyes[j].height * 0.5);//両目の中心座標を取得//
int radius = cvRound((eyes[j].width + eyes[j].height) * 0.25);//目を囲む円の半径を算出//
circle(frame, center, radius, Scalar(255, 0, 0), 4, 8, 0);//両目の周りに円を描画//
}
}
imshow("Detect", frame);//「Detect」ウィンドウに結果を表示//
if (waitKey(30) == 27){ //各フレームの待機時間は30ミリ秒(ESCキーで終了)//
break;
}
}
return 0;
}
コードのポイント解説
このプログラムの処理の流れは以下の通りです。
- カメラの初期化:
VideoCapture image(0)でデフォルトカメラを開き、フレームサイズを480×640に設定します。カメラが開けない場合はエラーメッセージを表示して終了します。 - 分類器の読み込み:
CascadeClassifierオブジェクトを2つ宣言し、それぞれ顔検出用(haarcascade_frontalface_alt.xml)と目検出用(haarcascade_eye.xml)の分類器を読み込みます。 - 顔の検出:
detectMultiScale()メソッドでフレーム全体から顔を検出し、検出された顔の中心にellipse()でマゼンタ色の楕円を描画します。 - 目の検出:検出した顔領域を
Mat faceROI = frame(faces[i])として切り出し、その範囲内のみを目の検出対象にすることで、誤検出を減らしつつ処理速度も向上させています。 - 結果の表示:両目の中心座標と半径を計算し、
circle()で青い円を描画。imshow()で「Detect」ウィンドウに結果を表示し、30ミリ秒ごとにESCキーの入力をチェックします。
実行結果
プログラムを実行すると、「Detect」という名前のウィンドウが開き、カメラ映像上の顔がマゼンタ色の楕円で、目が青い円でリアルタイムに囲まれて表示されます。なお、照明条件や顔の向きによって検出精度が変動するため、明るい環境でカメラに正面向きになると、より安定した検出結果が得られます。
-
C++とOpenCVを使って線を描画する方法
OpenCVで線を描画するには、始点と終点の2つの座標が必要です。さらに、線を描くためのキャンバス(描画領域)も用意しなければなりません。線を描画するために必要な要素OpenCVでは、キャンバスとして機能するのが行列(Mat)です。この行列に対して、線の始点と終点を定義し、加えて以下の情報も指定します。線の色線の太さつまり、OpenCVで線を描くには、行列・2つの点・色・線の太さという4つの要素を宣言しておく必要があります。必要なヘッダーファイルOpenCVで線を描画するには、<imgproc.hpp>ヘッダーをインクルードします。これは、line()関数がこのヘッダー内で定義され
-
C++とOpenCVで画像のチャンネル数を取得する方法を解説
この記事では、OpenCVを使って画像のチャンネル数を調べる方法について解説します。プログラムを実行すると、コンソール画面に画像のチャンネル数が表示されます。チャンネル数を取得するには、OpenCVが提供するchannels()メソッドを使用します。このメソッドは、Matクラスのオブジェクト(画像行列)に対して呼び出すことで、その画像のチャンネル数を整数値として返します。例えば、カラー画像の場合は通常3チャンネル(BGR)、グレースケール画像の場合は1チャンネルとなります。画像処理を行う際、扱っている画像がカラーかモノクロかを判別するために、チャンネル数の確認は非常に重要な処理の一つです。以下