C#

【WPF】OpenCVSharpで画像の中にある顔を検出する

今回は、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」を使ってみます。

プログラムの実装

さて、前回のプロジェクトに顔検出機能を追加して、試してみます。前回の記事をまだ読んでいない方はこちらからアクセスすることができます。

【WPF】OpenCVSharpで画像の読み取りC#のWPFで、OpenCVSharpで読み取りした画像を表示する方法と画像をグレースケールに変換する方法について紹介します。画像の読み取りはMatクラスを使い、引数に画像ファイルのパスを指定します。ImageコントロールのSourceプロパティに渡すために、ToWriteableBitmapメソッドでWriteableBitmap形式に変換することで表示することができます。...

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で顔を検出する方法を紹介しました。コード量も少なく、画像処理ができるのはほんと凄いです。

以上、最後まで読んでいただきありがとうございました。

プログラミングを学習したいなら…

プログラミングスキルを身に付けるなら、プログラミングを効率良く学べる
プログラミングスクール」がオススメです。

特にこんな方にオススメ!!
これからエンジニアを目指したい
プログラミングの専門性を高めたい
プログラミングを学んで副業をしたい
エンジニアに転職して年収をアップさせたい

プログラミングを触ったことがない未経験からでも、プログラミングスクールで学習すれば、エンジニアへ就職・転職することも可能です。

あなたの「行動力」と「やる気」で、あなたの人生を大きく変えるチャンスになることでしょう。

プログラミングスクールに興味がある方は是非チェックしてみてください。

> プログラミングを学ぶ <

COMMENT

メールアドレスが公開されることはありません。

CAPTCHA