はじめに
WindowsアプリでOpenCVを利用した画像処理を行ってみましょう。
OpenCVはC++で利用できますが「難易度が高い!」という人でも、C#でOpenCVSharpを使うと簡単で便利です。
今回使用する環境は以下です。
公式のOpenCvSharpの開発リポジトリは以下です。
他にも私のブログで、OpenCVSharpについて解説している記事がありますのでご覧ください。
ソースコードについて
今回使用するソースコードは、githubに公開しております。
Visual Studio 2022インストール
まずは、Visual Studio 2022をインストールします。
以下の記事にまとめましたので、参考ください。
C#プロジェクトの作成
Visual Studioを起動します。
新しいプロジェクトを作成します。

検索ボックスに、「C#」と入力し、Windowsフォームアプリケーションを作成します。

プロジェクト名や保存場所は任意で入力します。

プロジェクトが作成されました。

OpenCVSharpインストール
OpenCVとは、画像処理や解析などを行うことができるライブラリです。
OpenCVSharpを使用できるように準備します。
プロジェクト→NuGetパッケージの管理を開きます。

参照をクリックし、「opencvsharp4」と入力します。
今回は、Windowsアプリを作成しますので、OpenCvSharp4.Windowsをインストールします。


OpenCvSharp4.Windowsを選択し、インストールを行います。

OKボタンをクリックし進めます。

正常に終了しました。

基本
サンプル画像を用意しました。


任意の場所に「img」フォルダを作りファイルを保存しました。
例:
D:\cs_source\img\neko.jpg
フォームにボタンを配置します。

usingディレクティブに以下を指定します。
using OpenCvSharp;ボタンのクリックイベントを以下のようにしました。
最低限のサンプルコードです。
private void B_sample1_Click(object sender, EventArgs e)
{
    // 画像の読み込み
    using (Mat mat = new Mat(@"D:\cs_source\img\neko.jpg"))
    {
        // 画像をウィンドウに表示
        Cv2.ImShow("sample_show", mat);
    }
}Matインスタンスは、開放する必要があります。(usingを使用しています)
画像パスは、保存したパスを指定しています。
ImShowを使用し、新しいウィンドウに画像を表示します。
第一引数はウィンドウ名を指定します。
それでは実行してみましょう。
フォームが表示されますので、ボタンをクリックします。

新しいウィンドウに画像が表示されました。

今回はこの基本操作をもとに、画像処理の実用例を以下にまとめました。
グレースケールに変換
ボタンを追加します。

private void B_grayscale_Click(object sender, EventArgs e)
{
    // 画像の読み込み
    using (Mat mat = new Mat(@"D:\cs_source\img\neko.jpg"))
    using (Mat matGray = mat.CvtColor(ColorConversionCodes.BGR2GRAY))
    {
        // 画像をウィンドウに表示
        Cv2.ImShow("grayscale_show", matGray);
    }
}実行してみると、グレースケールになりました。

画像ファイルの保存
加工した画像をファイルに保存する方法です。
// 画像の保存
Cv2.ImWrite(@"D:\cs_source\img\output.jpg", mat);画像の切り抜き
画像を切り抜き、ファイルに保存する方法です。
// 画像の切り抜き
var mat2 = mat.Clone(new Rect(100, 100, 200, 150));
Cv2.ImWrite(@"D:\cs_source\img\output.jpg", mat2);テンプレートマッチング
テンプレートマッチングを行います。
ある画像の中に、検索したい画像を探して赤枠をつけます。
ボタンを追加します。

探したいテンプレート画像も用意します。
猫ちゃんの顔にしました。

ソースコードは以下です。
テンプレートマッチングを行い、見つかった箇所に赤枠を表示しています。
private void B_template1_Click(object sender, EventArgs e)
{
    // 検索対象の画像とテンプレート画像
    using (Mat mat = new Mat(@"D:\cs_source\img\neko.jpg"))
    using (Mat temp = new Mat(@"D:\cs_source\img\template1.jpg"))
    using (Mat result = new Mat())
    {
        // テンプレートマッチ
        Cv2.MatchTemplate(mat, temp, result, TemplateMatchModes.CCoeffNormed);
        // 類似度が最大/最小となる画素の位置を調べる
        OpenCvSharp.Point minloc, maxloc;
        double minval, maxval;
        Cv2.MinMaxLoc(result, out minval, out maxval, out minloc, out maxloc);
        // しきい値で判断
        var threshold = 0.9;
        if (maxval >= threshold)
        {
            // 最も見つかった場所に赤枠を表示
            Rect rect = new Rect(maxloc.X, maxloc.Y, temp.Width, temp.Height);
            Cv2.Rectangle(mat, rect, new OpenCvSharp.Scalar(0, 0, 255), 2);
            // ウィンドウに画像を表示
            Cv2.ImShow("template1_show", mat);
        } else
        {
            // 見つからない
            MessageBox.Show("見つかりませんでした");
        }
    }
}見つかった箇所に赤枠が表示されます。

テンプレートマッチング 複数検索
先程の例ですと、1つのテンプレートを検索し最も近い場所に赤枠を表示しました。
複数の画像もマッチングし検索することができます。
試しに猫ちゃんの顔をコピーした画像を用意しました。
これで2箇所検索されるはずです。

ソースコードは以下です。
private void B_template2_Click(object sender, EventArgs e)
{
    // 検索対象の画像とテンプレート画像
    using (Mat mat = new Mat(@"D:\cs_source\img\neko2.jpg"))
    using (Mat temp = new Mat(@"D:\cs_source\img\template1.jpg"))
    using (Mat result = new Mat())
    {
        // テンプレートマッチ
        Cv2.MatchTemplate(mat, temp, result, TemplateMatchModes.CCoeffNormed);
        // しきい値の範囲に絞る
        Cv2.Threshold(result, result, 0.8, 1.0, ThresholdTypes.Tozero);
        while (true)
        {
            // 類似度が最大/最小となる画素の位置を調べる
            OpenCvSharp.Point minloc, maxloc;
            double minval, maxval;
            Cv2.MinMaxLoc(result, out minval, out maxval, out minloc, out maxloc);
            var threshold = 0.8;
            if (maxval >= threshold)
            {
                // 見つかった場所に赤枠を表示
                Rect rect = new Rect(maxloc.X, maxloc.Y, temp.Width, temp.Height);
                Cv2.Rectangle(mat, rect, new OpenCvSharp.Scalar(0, 0, 255), 2);
                // 見つかった箇所は塗りつぶす
                Rect outRect;
                Cv2.FloodFill(result, maxloc, new OpenCvSharp.Scalar(0), out outRect, new OpenCvSharp.Scalar(0.1),
                            new OpenCvSharp.Scalar(1.0), FloodFillFlags.Link4);
            } else { 
                break;
            }
        }
        // ウィンドウに画像を表示
        Cv2.ImShow("template2_show", mat);
    }
}実行してみると2箇所赤枠が表示され、見つかりました。

顔抽出
写真の中の顔を認識してみたいと思います。
サンプル画像を用意しました。(各自ご用意ください)


ボタンを追加します。

ソースコードは以下です。
private void B_face_Click(object sender, EventArgs e)
{
    //顔の矩形を抽出
    using (Mat mat = new Mat(@"D:\cs_source\img\face.jpg"))
    {
        // 分類機の用意
        using (CascadeClassifier cascade = new CascadeClassifier(@"D:\cs_source\haarcascade_frontalface_default.xml"))
        {
            foreach (Rect rectFace in cascade.DetectMultiScale(mat))
            {
                // 見つかった場所に赤枠を表示
                Rect rect = new Rect(rectFace.X, rectFace.Y, rectFace.Width, rectFace.Height);
                Cv2.Rectangle(mat, rect, new OpenCvSharp.Scalar(0, 0, 255), 2);
            }
        }
        // ウィンドウに画像を表示
        Cv2.ImShow("face_show", mat);
    }
}haarcascade_frontalface_default.xmlは以下のgithubからダウンロードしました。
任意の場所に配置してパスを指定します。
顔を認識することができました。

画像の中の顔や体などの物体検出については、以下の記事で更に詳しく解説しています。
さいごに
基本的な操作例から、実用的な実装例をまとめました。
OpenCVは非常に強力なライブラリですので、沢山の画像処理を行うことができます。
色々と試してみてください?
他にも私のブログで、OpenCVSharpについて解説している記事がありますのでご覧ください。
 
  
  
  
 






コメント