狠狠撸

狠狠撸Share a Scribd company logo
ARコンテンツ作成勉強会
C#ではじめようOpenCV
#AR_Fukuoka
今回の内容 (for 狠狠撸 Share)
OpenCVを使ったカラートラッキングをやります
ただし???
画像処理プログラミングの基礎も勉強するため
ストレートに目的コンテンツには到達しません
あらかじめご了承ください
本日作成するコンテンツ
https://youtu.be/vnLiZFIe09I
Visual Studioを起動
プロジェクトを作成 (1/2)
①File
②New
③Project
プロジェクトを作成 (2/2)
①VisualC#
②Windows
③Windows Form Application
④OK
动作确认
Start
OpenCvSharpの導入 (1/4)
①Tools
②NuGet Package Manager
③Manage Nuget Package...
OpenCvSharpの導入 (2/4)
①Brows ②nuget.org
OpenCvSharpの導入 (3/4)
①OpenCVsharp3
②OpenCvSharp3 AnyCPU
③パッケージのチェックをON
④3.2.0.20170324
⑤Install
OpenCvSharpの導入 (4/4)
NuGetを閉じる
画像表示領域を作成 (1/5)
①ToolBoxタブ
②PictureBox
画像表示領域を作成 (2/5)
フォーム内をクリック
画像表示領域を作成 (3/5)
①PictureBoxをクリック
②Sizeを400,300
(なんでもOK)
画像表示領域を作成 (4/5)
BackgroundImageLayoutを
Zoomに変更
Zoomにするとアスペクト比を保持したままPictureBoxのサイズに合わせて画像を拡大?縮小
画像表示領域を作成 (5/5)
Formを選択してサイズを
PictureBoxに合わせる
Formのサイズはあとで変えるから適当でOK
Timerを用いた更新周期の設定
Timerを使うと任意に設定した間隔で処理(画像の取得?表示)を実行させられる
①ToolBoxタブ
②Timer
Timerを用いた更新周期の設定
①Form上をクリック
②Timerが追加される
?Intervalを30[ms]
に変更(なんでもOK)
一定時間毎に呼び出される関数を作成
①Timerをダブルクリック
一定時間毎に呼び出される関数を作成
timer1_Tick関数が追加される
一定時間沖にtimer1_Tick関数の内部に書いた処理が実行される(予定)
Formが表示されたらTimerをスタートさせる
①Form1[Design]
②Form1をクリック
Formが表示されたらTimerをスタートさせる
①?をクリック
②Shownをダブルクリック
Formが表示されたらTimerをスタートさせる
namespace CVTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
}
private void Form1_Shown(object sender, EventArgs e)
{
timer1.Start(); //タイマーの動作を開始
}
}
}
①Form1_Shownが追加される
②自分で追加
Webカメラの画像を取得
using OpenCvSharp; //OpenCvSharpの機能をインポート
using OpenCvSharp.Extensions;
namespace CVTest
{
public partial class Form1 : Form
{
VideoCapture video = /ssuserc0d7fb/arcopencv/new VideoCapture(0); //Videoキャプチャ
Mat bgr; //カラー画像を扱うクラス
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
}
/以下省略/
Webカメラの画像を取得
VideoCapture video = /ssuserc0d7fb/arcopencv/new VideoCapture(0);;
Mat bgr;
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat(); //カメラから画像を取得しbgrに記録
pictureBox1.BackgroundImage = bgr.ToBitmap();
bgr.Release(); //画像を記録したメモリを開放
}
private void Form1_Shown(object sender, EventArgs e)
{
timer1.Start();
}
画像を左右反転させよう
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
pictureBox1.BackgroundImage = bgr.ToBitmap();
bgr.Release();
}
x
y
Flip
画像処理の過程を表示させよう
横方向に広げる
(大体2倍くらい)
画像処理の過程を表示させよう
PictureBoxをコピペ
pictureBox1 pictureBox2
画像処理用の画像を作成
VideoCapture video;
Mat bgr;
Mat src; //画像処理に用いる画像を保持
public Form1(){ /*省略*/ }
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
ImageProcessing();
pictureBox1.BackgroundImage = bgr.ToBitmap();
pictureBox2.BackgroundImage = src.ToBitmap();
bgr.Release();
src.Release();
}
//以下の関数に画像処理のコードを書いていく
private void ImageProcessing()
{
src = /ssuserc0d7fb/arcopencv/bgr.Clone();
}
动作确认
OpenCVに慣れよう:グレースケール変換
Mat bgr;
Mat src; //画像処理に用いる画像を保持
public Form1(){ /*省略*/ }
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
ImageProcessing();
pictureBox1.BackgroundImage = bgr.ToBitmap();
pictureBox2.BackgroundImage = src.ToBitmap();
bgr.Release();
src.Release();
}
private void ImageProcessing()
{
src = /ssuserc0d7fb/arcopencv/bgr.Clone();
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2GRAY);
}
CvtColorで画像変換
动作确认
グレースケール画像を二値化してみよう
0 255
0 255
0~255の輝度値を、ある値(しきい値)以上か未満かで分離することを二値化と呼ぶ。
画像処理すべき領域か否かをはっきり分けることができるため非常に重要なテクニック。
二値化の下準備
dstを表示
Mat src; //画像処理に用いる画像を保持
Mat dst; //二値化結果を保持
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
ImageProcessing();
pictureBox1.BackgroundImage = bgr.ToBitmap();
pictureBox2.BackgroundImage = dst.ToBitmap();
bgr.Release();
src.Release();
dst.Release();
}
private void ImageProcessing()
{
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2GRAY);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
}
符号なし8bit(0~255)
二値化(ImageProcessing関数内)
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2GRAY);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
MatOfByte b_src = /ssuserc0d7fb/arcopencv/new MatOfByte(src);
MatOfByte b_dst = /ssuserc0d7fb/arcopencv/new MatOfByte(dst);
var index_src = b_src.GetIndexer();
var index_dst = b_dst.GetIndexer();
for(int y = 0; y < src.Height; y++)
{
for(int x = 0; x < src.Width; x++)
{
if (index_src[y, x] >= 100)
{
index_dst[y, x] = 255;
}
else
{
index_dst[y, x] = 0;
}
}
}
追加
动作确认
GUIでしきい値(Threshold)を変化させよう
Trackbar
GUIでしきい値(Threshold)を変化させよう
Trackbarを配置
GUIでしきい値(Threshold)を変化させよう
① Trackbarをクリックして
Propertiesを選択
③Maximumを255
TrackBarで入力できる値を0~255に設定
② をクリック
GUIでしきい値(Threshold)を変化させよう
①?をクリック
②Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
GUIでしきい値(Threshold)を変化させよう
Mat src;
Mat dst;
int th1;
/*省略*/
private void ImageProcessing ()
{
/*省略*/
for(int y = 0; y < src.Height; y++){
for(int x = 0; x < src.Width; x++){
if (index_src[y, x] >= th1){
index_dst[y, x] = 255;
}
else{
index_dst[y, x] = 0;
}
}
}
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
th1 = trackBar1.Value;
}
しきい値をth1に変更
trackBar1_Scroll関数が追加される
複数のしきい値を設定しよう
Trackbarをコピペ
複数のしきい値を設定しよう
① 2つめのTrackbarを
クリックしてPropertiesを選択
③Valueを255
2つめのTrackBarのデフォルト値を255に変更
② をクリック
複数のしきい値を設定しよう
①?をクリック
②Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
複数のしきい値を設定しよう
int th1;
int th2 = 255;
/*省略*/
private void ImageProcessing (){
/*省略*/
for(int y = 0; y < src.Height; y++){
for(int x = 0; x < src.Width; x++){
if (index_src[y, x] >= th1 && index_src[y, x] <= th2){
index_dst[y, x] = 255;
}
else{
index_dst[y, x] = 0;
}
}
}
}
/*省略*/
private void trackBar2_Scroll(object sender, EventArgs e){
th2 = trackBar2.Value;
}
2つめのしきい値を設定
trackBar2_Scroll関数が追加される
动作确认
色の抽出をしよう
【問題点】
? GrayScaleの場合は1変数なので抽出が容易
? 色をR(赤)G(緑)B(青)での組み合わせで表現
した場合、特定の色の抽出はちょっと面倒。
? RGBでは色の境目を直線的に引くことも困難
【解決策】
? 色空間としてHSVを使用する
? H(Hue)は色相で0~360°で表現される
※OpenCVの場合0~180
? S(Saturation)は彩度、0~100%で表現。
値が大きいほうがその色らしさが強い。
※OpenCVの場合0~255
? V(Value)は明度、 0~100%で表現。
値が大きいほど明るい
※OpenCVの場合0~255
GrayScale
RGB
HSV
色検出用の関数を作成
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
ImageProcessing2();
pictureBox1.BackgroundImage = bgr.ToBitmap();
pictureBox2.BackgroundImage = dst.ToBitmap();
bgr.Release();
src.Release();
dst.Release();
}
private void ImageProcessing()
{
/*二値化処理*/
}
private void ImageProcessing2()
{
/*二値化処理*/
}
GrayScaleの二値化を行うImageProcessing関数をサンプルとして取っておく場合
②ImageProcessing2に変更
①コピペした後、関数名を
ImageProcessing2に変更
HSV変換と指定範囲の二値化
private void ImageProcessing2()
{
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
MatOfByte3 b_src = /ssuserc0d7fb/arcopencv/new MatOfByte3(src);
MatOfByte b_dst = /ssuserc0d7fb/arcopencv/new MatOfByte(dst);
var index_src = b_src.GetIndexer();
var index_dst = b_dst.GetIndexer();
for (int y = 0; y < src.Height; y++)
{
for (int x = 0; x < src.Width; x++)
{
if (index_src[y, x][0] >= th1 && index_src[y, x][0] <= th2)
{
index_dst[y, x] = 255;
}
else
{
index_dst[y, x] = 0;
}
/*以下省略*/
①変換方法を変更
②MatOfByte3に変更
③ index_src[y, x][0]
抽出したい色のHueの確認
private void ImageProcessing2()
{
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
MatOfByte3 b_src = /ssuserc0d7fb/arcopencv/new MatOfByte3(src);
MatOfByte b_dst = /ssuserc0d7fb/arcopencv/new MatOfByte(dst);
/*省略(二値化処理など)*/
this.Text = th1 * 2 + “~” + th2 * 2; //Hueの範囲を表示
}
抽出したい色に
関するHueの範囲を
メモしておこう
彩度S(Saturation)の設定もしよう
1つめのTrackbarをコピペ
彩度S(Saturation)の設定もしよう
②?をクリック
③Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
① 3つめのTrackbarを
右クリックしてPropertiesを選択
複数のしきい値を設定しよう
int th2 = 255;
int th3;
/*省略*/
private void ImageProcessing2 (){
/*省略*/
for(int y = 0; y < src.Height; y++){
for(int x = 0; x < src.Width; x++){
if (index_src[y, x][0] >= th1 && index_src[y, x][0] <= th2
&& index_src[y, x][1] >= th3){
index_dst[y, x] = 255;
}
else{
index_dst[y, x] = 0;
}
}
}
}
/*省略*/
private void trackBar3_Scroll(object sender, EventArgs e){
th3 = trackBar3.Value;
}
3つめのしきい値を設定
trackBar3_Scroll関数が追加される
抽出したい色のSaturationの確認
private void ImageProcessing2()
{
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
MatOfByte3 b_src = /ssuserc0d7fb/arcopencv/new MatOfByte3(src);
MatOfByte b_dst = /ssuserc0d7fb/arcopencv/new MatOfByte(dst);
/*省略(二値化処理など)*/
this.Text = th1 * 2 + "~" + th2 * 2 + ": " + th3;
}
抽出したい色に
関するSの値を
メモしておこう
动作确认
ノイズが残ることもある
Erodeを用いたノイズ除去
private void ImageProcessing2()
{
src = /ssuserc0d7fb/arcopencv/bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = /ssuserc0d7fb/arcopencv/new Mat(bgr.Height, bgr.Width, MatType.CV_8U);
/*省略(二値化処理など)*/
dst = dst.Erode(null,null,2);
this.Text = th1 * 2 + "~" + th2 * 2+": "+th3;
}
白画素の縮小を2回行う
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/index.html
成功例 やり過ぎ例
白画素の中から面積最大のもの(Blob)を探す
private void ImageProcessing2()
{
/*省略(HSV変換から二値化まで)*/
dst = dst.Erode(null, null, 2);
ConnectedComponents cc = Cv2.ConnectedComponentsEx(dst);
if (cc.Blobs.Count > 0) //0で落ちる場合は1に変更
{
ConnectedComponents.Blob blob = cc.GetLargestBlob();
if (blob.Area > 2000)
{
bgr.Circle(blob.Centroid, 30, Scalar.Cyan, 4);
}
}
this.Text = th1 * 2 + "~" + th2 * 2+": "+th3;
}
追
加
动作确认
円がついてくるはず
線を描く
List<OpenCvSharp.Point2d> list = /ssuserc0d7fb/arcopencv/new List<OpenCvSharp.Point2d>();
private void ImageProcessing2(){
/*省略(HSV変換から二値化、ノイズ除去まで)*/
ConnectedComponents cc = Cv2.ConnectedComponentsEx(dst);
if (cc.Blobs.Count > 0){
ConnectedComponents.Blob blob = cc.GetLargestBlob();
if (blob.Area > 2000){
list.Add(blob.Centroid);
for (int i = 1; i < list.Count; i++)
{
bgr.Line(list[i - 1], list[i], Scalar.Red, 3);
}
if (list.Count > 20)
{
list.RemoveAt(0);
}
}
}
this.Text = th1 * 2 + "~" + th2 * 2+": "+th3;
}
追
加
完成

More Related Content

础搁コンテンツ作成勉强会:颁#ではじめよう翱辫别苍颁痴(カラートラッキング编)