今回は、OpenCVSharpを使って画像の中に写っている人の顔を検出できる画像処理を実装してみます。
コード量も短く、比較的簡単に顔検出の処理ができるので、1回試してみることをオススメします。
顔検出の仕組み
カスケード識別器
人間の顔検出を行うために、どんな特徴を人間の顔が持っているのかを予め学習をさせておく必要があります。人間の顔を含む学習用画像と含まない学習用画像を用意し、顔の特徴点を抽出します。これらの学習用画像のすべての特徴点をまとめたデータのことを「カスケード識別器」と呼びます。このデータを使うことで学習させる手間を省いて、画像の中から顔を検出することができるのです。
カスケード識別器の種類
OpenCvでは、顔だけ出なく目や鼻、体全体などを検出できるカスケード識別器の学習済みファイルを事前に用意されています。このファイルはOpenCvSharpでも利用することが可能です。
学習済みファイルは下記リンク先からダウンロードできます。
ファイル名とそのファイルが検出できる部位を以下にまとめています。
haarcascade_eye.xml | 目 |
---|---|
haarcascade_eye_tree_eyeglasses.xml | 眼鏡 |
haarcascade_frontalcatface.xml | 猫の顔(正面) |
haarcascade_frontalcatface_extended.xml | 猫の顔(正面) |
haarcascade_frontalface_alt.xml | 顔(正面) |
haarcascade_frontalface_alt2.xml | 顔(正面) |
haarcascade_frontalface_alt_tree.xml | 顔(正面) |
haarcascade_frontalface_default.xml | 顔(正面) |
haarcascade_fullbody.xml | 全身 |
haarcascade_lefteye_2splits.xml | 左目 |
haarcascade_licence_plate_rus_16stages.xml | ロシアのナンバープレート(全体) |
haarcascade_lowerbody.xml | 下半身 |
haarcascade_profileface.xml | 顔(証明写真) |
haarcascade_righteye_2splits.xml | 右目 |
haarcascade_russian_plate_number.xml | ロシアのナンバープレート(数字) |
haarcascade_smile.xml | 笑顔 |
haarcascade_upperbody.xml | 上半身 |
今回は顔検出のために「haarcascade_frontalface_default.xml」を使ってみます。
プログラムの実装
さて、前回のプロジェクトに顔検出機能を追加して、試してみます。前回の記事をまだ読んでいない方はこちらからアクセスすることができます。
UI画面の実装
顔を検出するボタンを追加します。UIの変更点は以上です。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel
Grid.ColumnSpan="2"
HorizontalAlignment="Left"
Orientation="Horizontal">
<materialDesign:PackIcon
Width="35"
Height="35"
Margin="10"
VerticalAlignment="Center"
Foreground="{StaticResource PrimaryHueMidBrush}"
Kind="PictureInPictureTopRightOutline" />
<TextBlock
Margin="0,0,40,0"
HorizontalAlignment="Center"
Style="{StaticResource MaterialDesignHeadline4TextBlock}"
Text="OpenCvSharp" />
</StackPanel>
<StackPanel
Grid.ColumnSpan="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
x:Name="btnGetImage"
Width="60"
Margin="10,0"
Click="btnGetImage_Click"
Content="取得" />
<Button
x:Name="btnProcessImage"
Width="60"
Margin="10,0"
Click="btnProcessImage_Click
Content="変換" />
<Button
x:Name="btnSaveImage"
Width="60"
Margin="10,0"
Click="btnSaveImage_Click"
Content="保存" />
<Button
x:Name="btnDetectionImage"
Width="60"
Margin="10,0"
Click="btnDetectionImage_Click"
Content="検出" />
</StackPanel>
<Image
x:Name="ImageData"
Grid.Row="1"
Grid.ColumnSpan="2"
Margin="5,5,5,5"/>
</Grid>
コードの実装
顔検出のボタンがクリックされたら、予めデスクトップ上にダウンロードしていたカスケード識別器を読み込みをします。
次にusingステートメントを用いて、引数に画像ファイルのパスを指定してMatクラスのインスタンスを生成します。
同様にusingステートメントを用いて、引数にカスケード識別器ファイルのパスを指定してCascadeClassifierクラスのインスタンスを生成します。
顔検出に使っているのはcascade.detectMultiScaleメソッドです。検出した結果があれば、座標とサイズを取得して赤枠で画像に表示をしています。
private void btnDetectionImage_Click(object sender, RoutedEventArgs e)
{
//識別器の読み込み
var face_cascade = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "haarcascade_frontalface_default.xml");
//顔の矩形を抽出
using (Mat mat = new Mat(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "freepicture.jpg")))
{
//顔を検知
using (CascadeClassifier cascade = new CascadeClassifier(face_cascade))
{
foreach (OpenCvSharp.Rect rectFace in cascade.DetectMultiScale(mat))
{
// 検知した顔を赤枠で囲む
OpenCvSharp.Rect rect = new OpenCvSharp.Rect(rectFace.X, rectFace.Y, rectFace.Width, rectFace.Height);
Cv2.Rectangle(mat, rect, new OpenCvSharp.Scalar(0, 0, 255), 3);
}
}
//加工した画像を表示する
ImageData.Source = mat.ToWriteableBitmap();
}
}
顔検出の結果
上記のプログラムで動かした結果がこちらです。
河村友歌さんの顔の一部は検出できていますが、顔ではない部分(左側の木の枝)も顔として検出されてしまっています。
精度を上げるための方法として、カラー画像ではなくモノクロの画像をdetectMultiScaleメソッドに渡すことで改善されました。画像をグレースケールにする処理を行い、detectMultiScaleメソッドにグレースケールに加工した画像を渡す様にコードを変更しました。
private void btnDetectionImage_Click(object sender, RoutedEventArgs e)
{
//識別器の読み込み
var face_cascade = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "haarcascade_frontalface_default.xml");
//顔の矩形を抽出
using (Mat mat = new Mat(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "freepicture.jpg")))
{
//グレースケールに加工
var gray = new Mat();
OpenCvSharp.Cv2.CvtColor(mat, gray, OpenCvSharp.ColorConversionCodes.RGB2GRAY);
//顔を検知
using (CascadeClassifier cascade = new CascadeClassifier(face_cascade))
{
foreach (OpenCvSharp.Rect rectFace in cascade.DetectMultiScale(gray))
{
// 検知した顔を赤枠で囲む
OpenCvSharp.Rect rect = new OpenCvSharp.Rect(rectFace.X, rectFace.Y, rectFace.Width, rectFace.Height);
Cv2.Rectangle(mat, rect, new OpenCvSharp.Scalar(0, 0, 255), 3);
}
}
//加工した画像を表示する
ImageData.Source = mat.ToWriteableBitmap();
}
}
コードを変更した後に、再度顔検出をした結果がこちらです。誤判定がなくなりました。
ただ、上の画像のような横顔の画像で顔を検出するのは苦手のようで、正面に顔が写っている画像に切り替えてみます。
顔全体に赤枠が表示され、しっかりと顔が検出されました。
さいごに
OpenCVが用意しているカスケード識別器を使用して、OpenCVSharpで顔を検出する方法を紹介しました。コード量も少なく、画像処理ができるのはほんと凄いです。
以上、最後まで読んでいただきありがとうございました。