C#/VB.NETで画像処理⑧<画像の輪郭抽出>
こんにちは、SKです。
C#/VB.NETで画像処理シリーズの第8弾。
カメラ画像の輪郭抽出の処理を実装します。
輪郭抽出とは、二値化画像の白領域と黒領域の境界線(輪郭)を検出する処理のことを言います。
↓参考
オブジェクト輪郭検出 | OpenCV / findContours を使用して画像中のオブジェクトの輪郭を検出する方法
OpenCVSharpとは?
(前回)画像を二値化する
動画手順
①輪郭抽出ボタンを配置する
フォーム上にボタンを配置します。
このボタンを押すと、輪郭抽出フラグを立て、カメラ画像を二値化・輪郭抽出し、輪郭に赤い線を描画してPictureBoxに表示するようにします。
表示テキストは"Contours"に。
輪郭抽出フラグのbool変数isContoursを追加します。
②画像を二値化する
まずは前回記事同様、画像を二値化します。
ただ今回、PictureBoxには元のRGB画像に輪郭線のみ描画したカラー画像を表示するため、二値化の出力画像は別変数grayに格納します。
まずはこう。
局所的に使用するMat型変数は、.NETの最強のリソース開放システムである"Using"を使って定義するようにしましょう!
Usingについて
C# Tips −usingを使え、使えったら使え(^^)−
③輪郭を抽出する
輪郭抽出には、OpenCVのFindContours関数を使用しますが、輪郭座標のみ抜き出す場合は、その派生関数である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); } } } } }
このあたりは記述方法にやや癖があるかもしれませんが、慣れて使いこなしていきましょう!
実行します。
起動時
Contours押下時
最後に、コード全文を載せておきます。
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; } } }
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です。
C#/VB.NETで画像処理シリーズの第7弾。
カメラの画像を二値化します。
二値化、つまり画像を白(255)か黒(0)の二値に振り分ける処理は、画像処理の分野で最も多く使われる処理の1つなので、是非マスターしましょう。
OpenCVSharpとは?
カメラ画像表示アプリケーション
(前回)画像を回転する
動画手順
①二値化ボタンを配置する
前回同様、フォーム上にボタンを配置します。
このボタンを押すと、二値化フラグを立て、カメラ画像を二値化してPictureBoxに表示するようにします。
表示テキストは"Threshold"に。
二値化フラグのbool変数isThreshを追加します。
②画像を二値化する
画像を二値化するには、まずOpenCVのCvtColor関数でカラー画像をグレースケール画像に変換した後、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); } }
実行します。
起動時
Threshold押下時
最後に、コード全文を載せておきます。
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; } } }
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です。
C#/VB.NETで画像処理シリーズの第6弾。
カメラの画像を回転します。
OpenCVSharpとは?
カメラ画像表示アプリケーション
(前回)画像の一部を拡大する
動画手順
①回転ボタンを配置する
前回同様、フォーム上にボタンを配置します。
このボタンを押すと、カメラ画像を30度ずつ回転させるようにします。
表示テキストは"Rotate"に。
回転角度のint型変数angleを追加します。
ボタンクリック時に、angleを30ずつ増加させるようにします。
②画像を回転する
画像を回転するには、OpenCVのGetRotationMatrix2D関数で回転行列を求め、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()); } }
実行します。
起動時
Rotate押下時
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; } } }
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です。
C#/VB.NETで画像処理シリーズの第5弾。
カメラの画像の一部を拡大します。
OpenCVSharpとは?
カメラ画像表示アプリケーション
動画手順
①拡大ボタンを配置する
フォーム上に、ボタンを1つ追加します。
このボタンを押すと画像の中心部を切り出して拡大し、もう1度押すと元の画像に戻すようにします。
表示テキストは"Zoom"にしておきます。
]
デザイナー上のボタンをダブルクリックし、コードにクリックイベントを自動生成しておきます。
画像拡大用フラグ変数isZoomを追加します。
ボタンクリック時にこのフラグのTrue/Falseを切り替えます。
フラグがTrueの時、②の処理を行います。
②画像の中心を切り出し拡大する
OpenCVのSubMat関数を使い、画像の任意領域を抜き出します。
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); } } }
実行します。
起動時
拡大時
最後に、コード全文を載せておきます。
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; } } }
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です。
C#/VB.NETで画像処理シリーズの第4弾。
カメラの画像にモザイク処理をかけていきます。
OpenCVSharpとは?
(前回)カメラ画像をぼかす
動画手順
①モザイク処理をかける
画像にモザイクをかけるには、一度画像を縮小し画素情報を減らし、そして元のサイズに戻すだけです。
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)); }
実行します。
画像全体にモザイクがかかりました。
↓前回のGaussianBlurと似ているようで違う処理なのが分かりますでしょうか?
最後に、コード全文を載せておきます。
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)); } } }
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です。
C#/VB.NETで画像処理シリーズの第3弾。
カメラの画像をぼかす(=平滑化)処理をかけていきます。
OpenCVSharpとは?
カメラ画像取得プロジェクト
動画手順
①画像処理用の関数を追加する
カメラ画像を加工する関数”Process”を追加します。引数に画像データ"img"を渡します。Mat型の引数は自動的に参照渡しになるので、後はこのimgを加工していくだけで、PictureBoxにその画像が表示されます。
②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); }
実行します。
画像全体が強めにぼかされました。
超簡単!このように、様々な画像の加工を関数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); } } }
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です。
C#/VB.NETで画像処理シリーズの第2弾。
カメラの画像を.NETの画像表示コントロールPictureBoxに表示していきます。
OpenCVSharpとは?
プロジェクトの作成方法
動画手順
①FormにPictureBoxを配置する
Form1のデザイナー画面に、ツールボックスからPictureBoxを配置。
追加したPictureBox1のプロパティで、下記の設定を行います。
Dock→"Fill"
SizeMode→"Zoom"
BackColor→"Black"
こんな感じです。
②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); } }
PCにWebカメラを繋げてプログラムを実行すると、カメラ画像が表示されます。
※映っているのは以前出張で行ったフランスのアルザス地方のマグカップです。
③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); } } });
実行すると、フォームのPictureBox上にカメラ画像が表示されました。
これで準備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