SKProgramLab

Let's Enjoy Programming! ~画像処理/IoT/機械学習など~

C#/VB.NETで画像処理⑧<画像の輪郭抽出>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第8弾。
カメラ画像の輪郭抽出の処理を実装します。

輪郭抽出とは、二値化画像の白領域と黒領域の境界線(輪郭)を検出する処理のことを言います。
↓参考
オブジェクト輪郭検出 | OpenCV / findContours を使用して画像中のオブジェクトの輪郭を検出する方法

OpenCVSharpとは?

skprogramlab.hatenablog.com

(前回)画像を二値化する

skprogramlab.hatenablog.com

動画手順

youtu.be

①輪郭抽出ボタンを配置する

フォーム上にボタンを配置します。
このボタンを押すと、輪郭抽出フラグを立て、カメラ画像を二値化・輪郭抽出し、輪郭に赤い線を描画してPictureBoxに表示するようにします。 表示テキストは"Contours"に。 f:id:SKProgramLab:20200406213402p:plain

輪郭抽出フラグのbool変数isContoursを追加します。
f:id:SKProgramLab:20200406213459p:plain

②画像を二値化する

まずは前回記事同様、画像を二値化します。
ただ今回、PictureBoxには元のRGB画像に輪郭線のみ描画したカラー画像を表示するため、二値化の出力画像は別変数grayに格納します。

まずはこう。
f:id:SKProgramLab:20200406214128p:plain
局所的に使用するMat型変数は、.NETの最強のリソース開放システムである"Using"を使って定義するようにしましょう!

Usingについて

C# Tips −usingを使え、使えったら使え(^^)−

③輪郭を抽出する

輪郭抽出には、OpenCVFindContours関数を使用しますが、輪郭座標のみ抜き出す場合は、その派生関数であるFindContoursAsArrayを使うほうがシンプルに記述できます。これはOpenCVには無く、OpenCVSharp特有の便利関数ですね。

FindContoursAsArray関数

Cv2.FindContoursAsArray Method
・第1引数:入力画像(二値化)
・第2引数:輪郭抽出モード
・第3引数:輪郭近似モード

第2/3引数のモードについては、下記リンクが参考になります。 https://www.pynote.info/entry/opencv-findcontours
ここでは、RetrievalModes.Tree、ContourApproximationModes.ApproxSimpleを使います。

また、戻り値はPoint型の二次元配列になっているため、二重Forループで座標を抜き出し、Cv2.Line関数で輪郭に赤い線を描画しています。

private void Process(Mat img)
{
    if (isContours)
    {
        using (Mat gray = new Mat(img.Size(), MatType.CV_8UC1))
        {
            Cv2.CvtColor(img, gray, ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(gray, gray, 0, 255, ThresholdTypes.Otsu);
            Point[][] contours = Cv2.FindContoursAsArray(gray, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
            foreach (Point[] points in contours)
            {
                for (int i = 0; i < points.Length; i++)
                {
                    Cv2.Line(img, points[i], points[(i + 1) % points.Length], Scalar.Red, 2);
                }
            }
        }
    }
}

f:id:SKProgramLab:20200406220053p:plain
このあたりは記述方法にやや癖があるかもしれませんが、慣れて使いこなしていきましょう!

実行します。
起動時 f:id:SKProgramLab:20200406220559p:plain
Contours押下時
f:id:SKProgramLab:20200406220719p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private bool isContours = false;
        
        private void Process(Mat img)
        {
            if (isContours)
            {
                using (Mat gray = new Mat(img.Size(), MatType.CV_8UC1))
                {
                    Cv2.CvtColor(img, gray, ColorConversionCodes.BGR2GRAY);
                    Cv2.Threshold(gray, gray, 0, 255, ThresholdTypes.Otsu);
                    Point[][] contours = Cv2.FindContoursAsArray(gray, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);
                    foreach (Point[] points in contours)
                    {
                        for (int i = 0; i < points.Length; i++)
                        {
                            Cv2.Line(img, points[i], points[(i + 1) % points.Length], Scalar.Red, 2);
                        }
                    }
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            isContours = !isContours;
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private isContours As Boolean = False

    Private Sub Process(ByVal img As Mat)
        If isContours Then
            Using gray As New Mat(img.Size, MatType.CV_8UC1)
                Cv2.CvtColor(img, gray, ColorConversionCodes.BGR2GRAY)
                Cv2.Threshold(gray, gray, 0, 255, ThresholdTypes.Otsu)
                Dim contours As Point()() = Cv2.FindContoursAsArray(gray, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple)
                For Each points As Point() In contours
                    For i = 0 To points.Length - 1
                        Cv2.Line(img, points(i), points((i + 1) Mod points.Length), Scalar.Red, 2)
                    Next
                Next
            End Using
        End If
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        isContours = Not isContours
    End Sub

End Class


C#/VB.NETで画像処理⑦<画像を二値化する>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第7弾。
カメラの画像を二値化します。

二値化、つまり画像を白(255)か黒(0)の二値に振り分ける処理は、画像処理の分野で最も多く使われる処理の1つなので、是非マスターしましょう。

OpenCVSharpとは?

skprogramlab.hatenablog.com

カメラ画像表示アプリケーション

skprogramlab.hatenablog.com

(前回)画像を回転する

skprogramlab.hatenablog.com

動画手順

youtu.be

①二値化ボタンを配置する

前回同様、フォーム上にボタンを配置します。
このボタンを押すと、二値化フラグを立て、カメラ画像を二値化してPictureBoxに表示するようにします。 表示テキストは"Threshold"に。 f:id:SKProgramLab:20200405132452p:plain

二値化フラグのbool変数isThreshを追加します。
f:id:SKProgramLab:20200405132540p:plain

②画像を二値化する

画像を二値化するには、まずOpenCVCvtColor関数でカラー画像をグレースケール画像に変換した後、Threshold関数で二値化します。

二値化の閾値処理にはいくつか種類がありますが、ここでは大津(Otsu)の手法というものを使います。これは、画像全体の輝度値の分布から最適な閾値を自動で決めてくれる便利な手法です。

CvtColor関数

http://opencv.jp/opencv-2.1/cpp/miscellaneous_image_transformations.html#cv-cvtcolor
・第1引数:入力画像
・第2引数:出力画像
・第3引数:色変換の種類(BGR2GRAY)

Threshold関数

http://opencv.jp/opencv-2svn/cpp/miscellaneous_image_transformations.html?highlight=threshold#cv-threshold
・第1引数:入力画像
・第2引数:出力画像
・第3引数:閾値
・第4引数:最大値
・第5引数:閾値処理の種類(Otsu)

private void Process(Mat img)
{
    if (isThresh)
    {
        Cv2.CvtColor(img, img, ColorConversionCodes.BGR2GRAY);
        Cv2.Threshold(img, img, 0, 255, ThresholdTypes.Otsu);
    }
}

f:id:SKProgramLab:20200405133311p:plain

実行します。
起動時 f:id:SKProgramLab:20200405133422p:plain
Threshold押下時
f:id:SKProgramLab:20200405133446p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private bool isThresh = false;

        private void Process(Mat img)
        {
            if (isThresh)
            {
                Cv2.CvtColor(img, img, ColorConversionCodes.BGR2GRAY);
                Cv2.Threshold(img, img, 0, 255, ThresholdTypes.Otsu);
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            isThresh = !isThresh;
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private isThresh As Boolean = False

    Private Sub Process(ByVal img As Mat)
        If isThresh Then
            Cv2.CvtColor(img, img, ColorConversionCodes.BGR2GRAY)
            Cv2.Threshold(img, img, 0, 255, ThresholdTypes.Otsu)
        End If
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        isThresh = Not isThresh
    End Sub

End Class


C#/VB.NETで画像処理⑥<画像を回転する>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第6弾。
カメラの画像を回転します。

OpenCVSharpとは?

skprogramlab.hatenablog.com

カメラ画像表示アプリケーション

skprogramlab.hatenablog.com

(前回)画像の一部を拡大する

skprogramlab.hatenablog.com

動画手順

youtu.be

①回転ボタンを配置する

前回同様、フォーム上にボタンを配置します。
このボタンを押すと、カメラ画像を30度ずつ回転させるようにします。
表示テキストは"Rotate"に。 f:id:SKProgramLab:20200404213309p:plain

回転角度のint型変数angleを追加します。
ボタンクリック時に、angleを30ずつ増加させるようにします。
f:id:SKProgramLab:20200404213631p:plain

②画像を回転する

画像を回転するには、OpenCVGetRotationMatrix2D関数で回転行列を求め、WarpAffine関数で回転することができます。

GetRotationMatrix2D関数

http://opencv.jp/opencv-2svn/cpp/geometric_image_transformations.html#cv-getrotationmatrix2d
・第1引数:回転中心座標
・第2引数:回転角度(反時計回り)
・第3引数:スケール

WarpAffine関数

http://opencv.jp/opencv-2.1/cpp/geometric_image_transformations.html#cv-warpaffine
・第1引数:入力画像
・第2引数:出力画像
・第3引数:回転行列
・第4引数:出力画像サイズ

private void Process(Mat img)
{
    using (Mat trans = Cv2.GetRotationMatrix2D(new Point2f(img.Width / 2, img.Height / 2), angle, 1))
    {
        Cv2.WarpAffine(img, img, trans, img.Size());
    }
}

f:id:SKProgramLab:20200404214341p:plain

実行します。
起動時 f:id:SKProgramLab:20200404214600p:plain
Rotate押下時
f:id:SKProgramLab:20200404214623p:plain
Rotateを押す度に、画像が反時計回りに30度ずつ回転します。

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private int angle = 0;
        
        private void Process(Mat img)
        {
            using (Mat trans = Cv2.GetRotationMatrix2D(new Point2f(img.Width / 2, img.Height / 2), angle, 1))
            {
                Cv2.WarpAffine(img, img, trans, img.Size());
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            angle += 30;
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private angle As Integer = 0

    Private Sub Process(ByVal img As Mat)
        Using trans As Mat = Cv2.GetRotationMatrix2D(New Point2f(img.Width / 2, img.Height / 2), angle, 1)
            Cv2.WarpAffine(img, img, trans, img.Size)
        End Using
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        angle += 30
    End Sub

End Class


C#/VB.NETで画像処理⑤<画像の一部を拡大する>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第5弾。
カメラの画像の一部を拡大します。

OpenCVSharpとは?

skprogramlab.hatenablog.com

カメラ画像表示アプリケーション

skprogramlab.hatenablog.com

動画手順

youtu.be

①拡大ボタンを配置する

フォーム上に、ボタンを1つ追加します。
このボタンを押すと画像の中心部を切り出して拡大し、もう1度押すと元の画像に戻すようにします。
表示テキストは"Zoom"にしておきます。 f:id:SKProgramLab:20200404140317p:plain]

デザイナー上のボタンをダブルクリックし、コードにクリックイベントを自動生成しておきます。
f:id:SKProgramLab:20200404140541p:plain

画像拡大用フラグ変数isZoomを追加します。
ボタンクリック時にこのフラグのTrue/Falseを切り替えます。
f:id:SKProgramLab:20200404141035p:plain
フラグがTrueの時、②の処理を行います。

②画像の中心を切り出し拡大する

OpenCVSubMat関数を使い、画像の任意領域を抜き出します。
SubMat関数の引数にはRect型で領域を指定します。今回は、画像の中心部の幅・高さが1/2の領域を切り出しています。

画像の中心部を切り出した後、前回使用したResize関数で元画像に大きさに拡大し、更にCopyTo関数で元画像imgにコピーすればOKです。

private void Process(Mat img)
{
    if (isZoom)
    {
        using (Mat roi = img.SubMat(new Rect(img.Width / 4, img.Height / 4, img.Width / 2, img.Height / 2)))
        {
            Cv2.Resize(roi, roi, img.Size());
            roi.CopyTo(img);
        }
    }
}

f:id:SKProgramLab:20200404141618p:plain

実行します。
起動時
f:id:SKProgramLab:20200404141811p:plain
拡大時
f:id:SKProgramLab:20200404141855p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private bool isZoom = false;

        private void Process(Mat img)
        {
            if (isZoom)
            {
                using (Mat roi = img.SubMat(new Rect(img.Width / 4, img.Height / 4, img.Width / 2, img.Height / 2)))
                {
                    Cv2.Resize(roi, roi, img.Size());
                    roi.CopyTo(img);
                }
            }
        }
        
        private void button1_Click(object sender, EventArgs e)
        {
            isZoom = !isZoom;
        }
    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private isZoom As Boolean = False

    Private Sub Process(ByVal img As Mat)
        If isZoom Then
            Using roi As Mat = img.SubMat(New Rect(img.Width / 4, img.Height / 4, img.Width / 2, img.Height / 2))
                Cv2.Resize(roi, roi, img.Size)
                roi.CopyTo(img)
            End Using
        End If
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        isZoom = Not isZoom
    End Sub

End Class


C#/VB.NETで画像処理④<画像にモザイクをかける>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第4弾。
カメラの画像にモザイク処理をかけていきます。

OpenCVSharpとは?

skprogramlab.hatenablog.com

(前回)カメラ画像をぼかす

skprogramlab.hatenablog.com

動画手順

youtu.be

①モザイク処理をかける

画像にモザイクをかけるには、一度画像を縮小し画素情報を減らし、そして元のサイズに戻すだけです。
OpenCV"Resize"関数を使い、簡単に画像の拡大/縮小ができます。

Resize関数

http://opencv.jp/opencv-2svn/cpp/geometric_image_transformations.html#cv-resize
・第1引数:入力画像(Mat)
・第2引数:出力画像(Mat)
・第3引数:変換サイズ

前回同様、Process関数に処理を記述していきます。
ここでは、画像サイズを1/10に縮小した後、元のサイズに戻します。

private void Process(Mat img)
{
    int w = img.Width;
    int h = img.Height;
    Cv2.Resize(img, img, new Size(w / 10, h / 10));
    Cv2.Resize(img, img, new Size(w, h));
}

f:id:SKProgramLab:20200403221742p:plain
実行します。
f:id:SKProgramLab:20200403221829p:plain
画像全体にモザイクがかかりました。

↓前回のGaussianBlurと似ているようで違う処理なのが分かりますでしょうか? f:id:SKProgramLab:20200403222041p:plain

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private void Process(Mat img)
        {
            int w = img.Width;
            int h = img.Height;
            Cv2.Resize(img, img, new Size(w / 10, h / 10));
            Cv2.Resize(img, img, new Size(w, h));
        }

    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private Sub Process(ByVal img As Mat)
        Dim w As Integer = img.Width
        Dim h As Integer = img.Height
        Cv2.Resize(img, img, New Size(w / 10, h / 10))
        Cv2.Resize(img, img, New Size(w, h))
    End Sub

End Class


C#/VB.NETで画像処理③<画像をぼかす(平滑化)>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第3弾。
カメラの画像をぼかす(=平滑化)処理をかけていきます。

OpenCVSharpとは?

skprogramlab.hatenablog.com

カメラ画像取得プロジェクト

skprogramlab.hatenablog.com

動画手順

youtu.be

①画像処理用の関数を追加する

カメラ画像を加工する関数”Process”を追加します。引数に画像データ"img"を渡します。Mat型の引数は自動的に参照渡しになるので、後はこのimgを加工していくだけで、PictureBoxにその画像が表示されます。 f:id:SKProgramLab:20200403210707p:plain

②GaussianBlurでぼかす

OpenCVには平滑化処理がいくつか用意されていますが、代表的なGaussianBlur(ガウシアンブラー)を使います。

GaussianBlur関数

http://opencv.jp/opencv-2svn/cpp/imgproc_image_filtering.html#cv-gaussianblur
・第1引数:入力画像(Mat)
・第2引数:出力画像(Mat)
・第3引数:カーネルサイズ=ぼかしの強さ(Size型、正の奇数を指定)
・第4引数:0でOK

private void Process(Mat img)
{
    Cv2.GaussianBlur(img, img, new Size(31, 31), 0);
}

f:id:SKProgramLab:20200403212743p:plain
実行します。
f:id:SKProgramLab:20200403212437p:plain
画像全体が強めにぼかされました。
超簡単!このように、様々な画像の加工を関数1つ呼ぶだけで実現できるのがOpenCVというライブラリです。

最後に、コード全文を載せておきます。
C#

using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        Process(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }

        private void Process(Mat img)
        {
            Cv2.GaussianBlur(img, img, new Size(31, 31), 0);
        }

    }
}


VB.NET

Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0)
                         Using img As New Mat
                             Do
                                 v.Read(img)
                                 Process(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                         End Using
                     End Using
                 End Sub)
    End Sub

    Private Sub Process(ByVal img As Mat)
        Cv2.GaussianBlur(img, img, New Size(31, 31), 0)
    End Sub

End Class


C#/VB.NETで画像処理②<カメラ画像をPictureBoxに表示>

こんにちは、SKです。
f:id:SKProgramLab:20200310203357p:plain:w100

C#/VB.NETで画像処理シリーズの第2弾。
カメラの画像を.NETの画像表示コントロールPictureBoxに表示していきます。

OpenCVSharpとは?

skprogramlab.hatenablog.com

プロジェクトの作成方法

skprogramlab.hatenablog.com

動画手順

youtu.be

①FormにPictureBoxを配置する

Form1のデザイナー画面に、ツールボックスからPictureBoxを配置。 f:id:SKProgramLab:20200311221646p:plain

追加したPictureBox1のプロパティで、下記の設定を行います。
Dock→"Fill"
f:id:SKProgramLab:20200311221844p:plain
SizeMode→"Zoom"
f:id:SKProgramLab:20200311224555p:plain
BackColor→"Black"
f:id:SKProgramLab:20200311222103p:plain

こんな感じです。 f:id:SKProgramLab:20200311222133p:plain

Webカメラ画像を取得する

OpenCVSharpのVideoCaptureを使ってWebカメラから画像を取得し、まずはCv2.ImShowで表示してみます。 Form1のLoadイベント内に下記を記述します。

using (VideoCapture v = new VideoCapture(0))
using (Mat img = new Mat())
{
    while (true)
    {
        v.Read(img);
        Cv2.ImShow("img", img);
        Cv2.WaitKey(1);
    }
}

f:id:SKProgramLab:20200311222835p:plain

PCにWebカメラを繋げてプログラムを実行すると、カメラ画像が表示されます。
f:id:SKProgramLab:20200311223154p:plain
※映っているのは以前出張で行ったフランスのアルザス地方のマグカップです。

③MatをBitmapに変換しPictureBoxに表示する

PictureBoxに表示する画像はBitmap型である必要があります。
"OpenCVSharp.Extensions"をインポートし、"BitmapConverter.ToBitmap"関数でMat→Bitmap変換できます。

また、画像取得の無限ループとフォームのメインスレッドを分離するために、ループをサブスレッド化します。
マルチスレッドについては詳しく説明しませんが、.NETではとりあえず"Task.Run"の中にぶっこめばOKです。(←超簡単!)

Task.Run(() =>
{
    using (VideoCapture v = new VideoCapture(0))
    using (Mat img = new Mat())
    {
        while (true)
        {
            v.Read(img);
            pictureBox1.Image = BitmapConverter.ToBitmap(img);
        }
    }
});

f:id:SKProgramLab:20200311225428p:plain

実行すると、フォームのPictureBox上にカメラ画像が表示されました。 f:id:SKProgramLab:20200311225208p:plain
これで準備OK!
次回から画像処理でカメラ画像をこねくり回していきましょう。

最後に、コード全文を載せておきます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Task.Run(() =>
            {
                using (VideoCapture v = new VideoCapture(0))
                using (Mat img = new Mat())
                {
                    while (true)
                    {
                        v.Read(img);
                        pictureBox1.Image = BitmapConverter.ToBitmap(img);
                    }
                }
            });
        }
    }
}
Imports OpenCvSharp
Imports OpenCvSharp.Extensions

Public Class Form1

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Task.Run(Sub()
                     Using v As New VideoCapture(0),
                           img As New Mat
                             Do
                                 v.Read(img)
                                 PictureBox1.Image = img.ToBitmap
                             Loop
                     End Using
                 End Sub)
    End Sub

End Class


個人的には、VB.NETの方が記述がシンプルで好きです。
C#セミコロン書かなくていい仕様にならないですかね?笑