Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ARコンテンツ作成勉強会:ARライトセーバーを作りながら覚えるOpenCV

 ARコンテンツ作成勉強会:ARライトセーバーを作りながら覚えるOpenCV

OpenCVを使った画像処理の基礎に関する勉強会資料

TakashiYoshinaga

March 17, 2018
Tweet

More Decks by TakashiYoshinaga

Other Decks in Technology

Transcript

  1. 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が追加される ②自分で追加
  2. Webカメラの画像を取得 using OpenCvSharp; //OpenCvSharpの機能をインポート using OpenCvSharp.Extensions; namespace CVTest { public

    partial class Form1 : Form { VideoCapture video = new VideoCapture(0); //Videoキャプチャ Mat bgr; //カラー画像を扱うクラス public Form1() { InitializeComponent(); } private void timer1_Tick(object sender, EventArgs e) { } /*****以下省略*****/
  3. Webカメラの画像を取得 VideoCapture video = 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(); }
  4. Webカメラの画像のサイズを調べる VideoCapture video = new VideoCapture(0); Mat bgr; public Form1()

    { InitializeComponent(); this.Text = video.FrameWidth + "," + video.FrameHeight; } 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(); }
  5. 画像を左右反転させよう 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
  6. 画像処理用の画像を作成 VideoCapture video; Mat bgr; Mat img1; //画像処理に用いる画像を保持 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 = img1.ToBitmap(); bgr.Release(); img1.Release(); } //以下の関数に画像処理のコードを書いていく private void ImageProcessing() { img1 = bgr.Clone(); }
  7. OpenCVに慣れよう:グレースケール変換 Mat bgr; Mat img1; //画像処理に用いる画像を保持 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 = img1.ToBitmap(); bgr.Release(); img1.Release(); } private void ImageProcessing() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); } CvtColorで画像変換
  8. 二値化 Mat img1; //画像処理に用いる画像を保持 private void timer1_Tick(object sender, EventArgs e)

    { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = img1.ToBitmap(); bgr.Release(); img1.Release(); } private void ImageProcessing() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 = img1.Threshold(128, 255, ThresholdTypes.Binary); } しきい値
  9. GUIでしきい値(Threshold)を変化させよう Mat src; Mat dst; int th = 128; /*コンストラクタ省略*/

    private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = img1.ToBitmap(); bgr.Release(); img1.Release(); } private void ImageProcessing() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 = img1.Threshold(th, 255, ThresholdTypes.Binary); } private void trackBar1_Scroll(object sender, EventArgs e) { th = trackBar1.Value; } しきい値をthに変更 trackBar1_Scroll関数が追加される
  10. Erode(収縮)を用いたノイズ除去 Mat img1; Mat img2; int th; /* 中略 */

    private void ImageProcessing2() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 = img1.Threshold(th, 255, ThresholdTypes.Binary); img2 = img1.Erode(new Mat(), null, 3); } 白画素の縮小を数回行う http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/index.html 成功例 やり過ぎ例
  11. ノイズ除去後の画像を表示 private void timer1_Tick(object sender, EventArgs e) { bgr =

    video.RetrieveMat(); Cv2.Flip(bgr, bgr, FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = img1.ToBitmap(); pictureBox3.BackgroundImage = img2.ToBitmap(); bgr.Release(); img1.Release(); img2.Release(); }
  12. Dilate(膨張)を用いた復元 Mat img1; Mat img2; int th; /* 中略 */

    private void ImageProcessing2() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 = img1.Threshold(th, 255, ThresholdTypes.Binary); img2 = img1.Erode(new Mat(), null, 3); img2 = img2.Dilate(new Mat(), null, 4); } 白画素が小さすぎる場合は膨張させる Erode Dilate
  13. 輪郭の取得 private void ImageProcessing() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 =

    img1.Threshold(th, 255, ThresholdTypes.Binary); img2 = img1.Erode(new Mat(), null, 3); img2 = img2.Dilate(new Mat(), null, 3); OpenCvSharp.Point[][] contours; HierarchyIndex[] h; img2.FindContours( out contours, out h, RetrievalModes.External, ContourApproximationModes.ApproxSimple); }
  14. 輪郭の描画 private void ImageProcessing() { img1 = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); img1 =

    img1.Threshold(th, 255, ThresholdTypes.Binary); img2 = img1.Erode(new Mat(), null, 3); img2 = img2.Dilate(new Mat(), null, 3); OpenCvSharp.Point[][] contours; HierarchyIndex[] h; img2.FindContours( out contours, out h, RetrievalModes.External, ContourApproximationModes.ApproxSimple); } このあと、ここにコードを追加する
  15. 輪郭の描画 for (int i = 0; i < contours.Length; i++)

    { bgr.DrawContours(contours, i, Scalar.Cyan); }
  16. 最大面積かつ縦長な領域を求める(1/3) double maxArea = -1; OpenCvSharp.Point[] track = null; for

    (int i = 0; i < contours.Length; i++){ bgr.DrawContours(contours, i, Scalar.Cyan); double area = Cv2.ContourArea(contours[i]); RotatedRect rect = Cv2.MinAreaRect(contours[i]); double aspect = rect.Size.Height / rect.Size.Width; if (aspect < 1){ aspect = 1 / aspect; } if (area > maxArea){ maxArea = area; track=contours[i]; } } if (area > maxArea && aspect>2.0){
  17. 最大面積かつ縦長な領域を求める(2/3) OpenCvSharp.Point[] track = null; RotatedRect maxRect = new RotatedRect();

    for (int i = 0; i < contours.Length; i++){ bgr.DrawContours(contours, i, Scalar.Cyan); double area = Cv2.ContourArea(contours[i]); RotatedRect rect = Cv2.MinAreaRect(contours[i]); double aspect = rect.Size.Height / rect.Size.Width; if (aspect < 1){ aspect = 1 / aspect; } if (area > maxArea && aspect>2.0){ maxArea = area; track=contours[i]; maxRect=rect; } }
  18. 最大面積かつ縦長な領域を求める(3/3) double maxArea = -1; OpenCvSharp.Point[] track = null; RotatedRect

    maxRect = new RoatedRect(); for (int i = 0; i < contours.Length; i++){ /*最大かつ縦長の領域探し(前頁参照)*/ } if(track!=null){ Point2f[] p = maxRect.Points(); //四隅の座標を用いて矩形を描画 Cv2.Line(bgr, p[0], p[1], Scalar.Red); Cv2.Line(bgr, p[1], p[2], Scalar.Red); Cv2.Line(bgr, p[2], p[3], Scalar.Red); Cv2.Line(bgr, p[3], p[0], Scalar.Red); }
  19. 楕円近似と傾きの算出 OpenCvSharp.Point[] track = null; for (int i = 0;

    i < contours.Length; i++){ /*最大かつ縦長の領域探し(前頁参照)*/ } if(track!=null && track.Count()>4){ Point2f[] p = maxRect.Points(); //四隅の座標を用いて矩形を描画 Cv2.Line(bgr, p[0], p[1], Scalar.Red); Cv2.Line(bgr, p[1], p[2], Scalar.Red); Cv2.Line(bgr, p[2], p[3], Scalar.Red); Cv2.Line(bgr, p[3], p[0], Scalar.Red); RotatedRect ellipse = Cv2.FitEllipse(track); Cv2.Ellipse(bgr, ellipse, Scalar.Green); this.Text=ellipse.Angle.ToString(); } 解説
  20. 楕円近似と傾きの修正と矩形の位置取得 float angle; float cx, cy; private void ImageProcessing() {

    /*二値化→ノイズ除去→最大かつ縦長の領域探し(省略)*/ if(track!=null && track.Count()>4){ /*Cv2.Lineで矩形描画(省略)*/ RotatedRect ellipse = Cv2.FitEllipse(track); Cv2.Ellipse(bgr, ellipse, Scalar.Green); angle = ellipse.Angle; if (angle < 90){ angle -= 180; } this.Text=angle.ToString(); cx = maxRect.Center.X; cy = maxRect.Center.Y; } }
  21. 描画しよう Bitmap saber = new Bitmap("saber.png"); private void pictureBox1_Paint(object sender,

    PaintEventArgs e) { Graphics g = e.Graphics; g.TranslateTransform(cx, cy); g.RotateTransform(angle); int h = saber.Height; int w = saber.Width; g.DrawImage(saber, 0, 0, w, h); } private void pictureBox1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; /*中略*/ //画像のデフォルト位置を横半分ずらす g.DrawImage(saber, -w/2, 0, w, h); }