OpenCVSharp4で内外判定

内外判定とは

点が「領域の内側にあるか外側にあるか」判定すること

Cv2.PointPolygoonTest()を使うと判定できる。オプションで領域との距離を測定することもできる。

プログラム

OpenCVSharp4の導入はコチラ

using System;  
using OpenCvSharp;  

namespace pointPolygonTest {  
    class Program {  
        static void Main(string[] args) {  
            //4r*4rの正方形を作成  
            const int r = 100;  
            Mat mat = new Mat(new Size(r * 4, r * 4), MatType.CV_8UC1, Scalar.All(0));  


            //六角形を作図  
            Point[] hexagon = new Point[6];  
            for(int i = 0; i < 6; i++) {  
                hexagon[i] = new Point(  
                    r * Math.Cos(2 * Math.PI / 6 * i) + 2 * r,  
                    r * Math.Sin(2 * Math.PI / 6 * i) + 2 * r  
                    );  
            }  
            for(int i=0; i < 6; i++) {  
                mat.Line(hexagon[i], hexagon[(i + 1) % 6], Scalar.White);  

            }  


            //輪郭検出  
            Mat edge = new Mat();  
            Point[][] contours;  
            HierarchyIndex[] hierarchyIndices;  
            Cv2.FindContours(mat, out contours, out hierarchyIndices, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);  


            //輪郭との距離を測定  
            Mat rawDist = new Mat(mat.Size(), MatType.CV_64FC1);  
            for(int i=0;i<mat.Rows; i++) {  
                for(int j = 0; j < mat.Cols; j++) {  
                    var dist = Cv2.PointPolygonTest(contours[0], new Point2f(j, i), true);  
                    rawDist.Set<double>(i, j, dist);  
                }  
            }  



            //描画  
            Mat draw = new Mat(rawDist.Size(), MatType.CV_8UC3, Scalar.All(0));  
            double min, max;  
            Cv2.MinMaxLoc(rawDist, out min, out max);  

            rawDist *= 255 / Math.Max(max, -min);//正規化  
            for(int i = 0; i < mat.Rows; i++) {  
                for(int j = 0; j < mat.Cols; j++) {  

                    var dist = Cv2.PointPolygonTest(contours[0], new Point2f(j, i), true);  

                    Vec3b px;  
                    if(dist > 0) { px = new Vec3b(0, (byte)dist, 0); }  
                    else {         px = new Vec3b(0, 0, (byte)(-dist)); }  


                    draw.Set<Vec3b>(i, j, px);  
                }  
            }  

            Cv2.ImShow("mat", mat);  
            Cv2.ImShow("draw", draw);  
            Cv2.WaitKey();  

        }  
    }  
}  

結果

f:id:negizoku:20200920232832p:plain



f:id:negizoku:20200920232846p:plain



パプリカの断面のようである。

参考

https://docs.opencv.org/4.0.0/dc/d48/tutorial_point_polygon_test.html

OpenCVSharp4でバウンディングボックスの描画

バウンディングボックスとは

ある領域を長方形(矩形)で囲むこと。
今回は水平&鉛直方向に伸びた矩形と、
斜めの矩形で検出する。

方法

OpenCVSharp4の導入はコチラ

  1. 画像を読み込み
  2. 二値化
  3. エッジ検出
  4. 輪郭検出
  5. 親輪郭からバウンディングボックスを描画する



バウンディングボックス作成には

  • Cv2.BounfingRect()水平な長方形で囲む
  • Cv2.MinAreaRect()最小の長方形で囲む

を使う

プログラム

using System;  
using System.Collections.Generic;  
using OpenCvSharp;  

namespace BoundingBoxAndCircles {  
    class Program {  
        static void Main(string[] args) {  
            //1.画像を読み込み  
            Mat src = new Mat(@"D:\Learning\OpenCV\thumbnail_small_star.jpg");  


            //2.二値化  
            Mat bw = src.CvtColor(ColorConversionCodes.BGR2GRAY);  
            Cv2.Threshold(bw, bw, 100, 255, ThresholdTypes.Otsu);  


            //3.エッジ検出  
            Mat edges = new Mat();  
            Cv2.Canny(bw, edges, 200, 20);  


            //4.輪郭検出  
            OpenCvSharp.Point[][] contours;  
            HierarchyIndex[] hierarchyIndices;  
            edges.FindContours(out contours, out hierarchyIndices, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);  


            //5.親輪郭を構成する点を描画  
            Mat dst = src.Clone();  
            var points = new List<OpenCvSharp.Point>();  
            for(int i = 0; i < contours.GetLength(0); ++i) {  
                //親輪郭を集める  
                if(hierarchyIndices[i].Parent != -1) { continue; }  
                {  
                    //バウンディングボックスを計算  
                    var boundingBox = Cv2.BoundingRect(contours[i]);  
                    var minBox = Cv2.MinAreaRect(contours[i]);  

                    //バウンディングボックスの描画  
                    dst.Rectangle(boundingBox, Scalar.Red, 2);  
                    var pts = minBox.Points();  
                    for(int x = 0; x<pts.Length; x++) {  
                        Cv2.Line(dst, (int)pts[x].X, (int)pts[x].Y, (int)pts[(x + 1) % 4].X, (int)pts[(x + 1) % 4].Y, Scalar.Green, 2);  
                        Console.WriteLine(pts[x].X);  
                    }  
                }  

            }  


            //6.表示  
            Cv2.ImShow("dst", dst);  
            Cv2.ImWrite(@"D:\Learning\OpenCV\thumbnail_small_star2.jpg", dst);  
            Cv2.WaitKey();  

        }  
    }  
}  

OpenCVSharp4で多角形凸包してみた

はじめに

多角形凸包とは、ある領域を「ヘコみのない多角形」で囲むことである。

方法

  1. 画像を読み込む
  2. 輪郭を求める
  3. 輪郭の点の集合を集める
  4. 集めた点で凸包する
  5. 表示

プログラム

ほとんどmotchy様のプログラムを参考にしたが、現バージョンに合わせて書き換えた。

using System.Collections.Generic;  
using OpenCvSharp;  

namespace convexHull {  
    class Program {  
        static void Main(string[] args) {  

            //1.画像をグレースケールで読み込む  
            Mat img = Cv2.ImRead(@"D:\tamanegi_onion (1).png", ImreadModes.Grayscale);  

            //2.輪郭を求める  
            Point[][] contours;  
            HierarchyIndex[] hierarchyIndexes;  
            img.FindContours(out contours, out hierarchyIndexes, RetrievalModes.CComp, ContourApproximationModes.ApproxSimple);  

            //3.親輪郭を構成する点を全て集める  
            var points = new List<Point>();  
            for(int idx_cnt = 0; idx_cnt < contours.GetLength(0); ++idx_cnt) {  
                if(hierarchyIndexes[idx_cnt].Parent != -1) { continue; }  
                points.AddRange(contours[idx_cnt]);  
            }  

            //4.凸包を求めて描画する  
            Cv2.CvtColor(img, img, ColorConversionCodes.GRAY2BGR);  
            img.Polylines(new Point[1][] { Cv2.ConvexHull(points.ToArray()) }, true,  Scalar.Red);  

            //5.表示  
            Cv2.ImShow("img", img);  
            Cv2.ImWrite(@"D:\totuho.png", img);  
            Cv2.WaitKey();  
        }  
    }  
}  

結果

f:id:negizoku:20200920232359p:plain



参考

https://motchy99.blog.fc2.com/blog-entry-153.html
https://teratail.com/questions/222754
https://docs.opencv.org/4.0.0/d7/d1d/tutorial_hull.html
https://ja.wikipedia.org/wiki/%E5%87%B8%E5%8C%85
https://white-rabbit.jp/quickhull/

Huモーメントについて

Huモーメント(Hu moments)とは

平行移動、回転移動、拡大縮小、鏡映しても値が変わらない「モーメント不変量」のひとつ。(ただし、鏡映したときはh7の符号が反転する)
Huモーメントは7つのパラメータ[h1,h2,h3,h4,h5,h6,h7]をもつ。

平行移動、回転移動、拡大縮小、鏡映しても値が変わらない特徴から、
データ間の類似度を測るのに用いられる。

Huモーメントの定義

相当に汚い式。おそらく覚える必要はない。
h1=μ20+μ02h2=(μ20μ02)2+4μ112h3=(μ303μ12)2+(3μ213μ03)2h4=(μ30+3μ12)2+(μ21+μ03)2h5=(μ303μ12)(μ30+3μ12)[(μ30+μ12)23(μ21+μ03)2]+3(μ21μ03)(μ21+μ12)[3(μ30+μ12)2(μ21+μ03)2]h6=(μ20μ02)[(μ30+μ12)2(μ21+μ03)2]+4μ11(μ30+μ12)(μ21+mu03)h7=(3μ21μ03)(μ30+μ12)[(μ30+μ12)23(μ21+μ03)2](μ303μ12)(μ21+μ03)[3(μ30+μ12)2(μ21+μ03)2]

Huモーメントの問題点

  • 独立でない
    単純なノルム(h12+h22+h32+h42+h52+h62+h72等)では正確に比較できない
  • 解像度により値が変わること
    これは拡大縮小の際に考慮すべきである

参考

https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#humoments
https://youtu.be/uEVrJrJfa0s

画像のモーメントってなんだよと思ったので

定義

画像のモーメントには

  • 素モーメント(raw moment)
  • 中心モーメント(central moment)

の2種類がある。素モーメントは画像基準の座標系、中心モーメントは重心を原点とした座標系である。

素モーメント(raw moment)の定義

m×nの画像Ixyに対して画像の素モーメントMpqMpq=x=0m1y=0n1xpyqIxyで与えられる。

素モーメントの性質

p=q=0かつ画像Ixyが二値画像のとき、M00=x=0m1y=0n1Ixyとなり、画像の面積を表す。
(画像Ixyが密度のときはM00は質量を表す)

また、M00,M10,M01を使って重心Gの座標(Gx,Gy)を表すことができる。(重心が素モーメントで表せる簡単な説明はコチラ
(Gx,Gy)=(xIxyIxy,yIxyIxy)=(M10M00,M01M00)

中心モーメント(central moment)の定義

m×nの画像Ixyに対して画像の中心モーメントμpqμpq=x=0m1y=0n1(xGx)p(yGy)qIxyで与えられる。ここで、Gx,Gyは前述の通り重心Gの座標である。

中心モーメントの性質

定義より明らかだが、

  1. μ00=M00
  2. μ01=μ10=0
  3. μ11μ00は共分散である。
  4. μ20,μ02は断面二次モーメントである。
  5. μ20μ00,μ02μ00x,yそれぞれの分散である。
  6. μ11μ20μ02xy相関係数である。
  7. 画像Ixyを平行移動してもμpqは変わらない。

参考

https://en.wikipedia.org/wiki/Image_moment
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html
http://www.wakariyasui.sakura.ne.jp/p/mech/gou/jyuusinn.html

OpenCVSharp4で画像に等高線を描く

ゲームをやっていたら等高線を書きたくなったので。。。

方法

  1. 画像の正規化
  2. 画像の階調を粗くする
  3. 階調を荒くした画像でエッジ検出
  4. 階調を荒くした画像にエッジを上描き
  5. 等高線画像の表示

プログラム

using System;  
using OpenCvSharp;  

namespace 等高線 {  
    class Program {  
        static void Main(string[] args) {  
            //ガウス関数を作成  
            float[,] wave = new float[500, 500];  
            for(int i = 0; i < 500; i++) {  
                for(int j =0; j < 500; j++) {  
                    double r = Math.Sqrt(i * i + 2 * j * j);  
                    wave[i, j] = (float)Math.Pow(Math.E, -r * r / 150000);  
                }  
            }  
            Mat src = new Mat(500, 500, MatType.CV_32FC1, wave);  

            //①0~255に正規化して、MatTypeをCV_8UC1に変更  
            Cv2.Normalize(src, src, 0, 255, NormTypes.MinMax);  
            src.ConvertTo(src, MatType.CV_8UC1);  
            Cv2.ImShow("src", src);  
            src.ImWrite(@"D:\src1.png");  

            //⑤画像の表示  
            Mat dst = drawContourLine(src, 20, Scalar.Brown, ColormapTypes.Pink);  

            Cv2.ImShow("dst", dst);  
            dst.ImWrite(@"D:\dst1.png");  
            Cv2.WaitKey();  
        }  


        static Mat drawContourLine(Mat src, int contourNum,Scalar contourColor, ColormapTypes? colormapTypes = ColormapTypes.Hot) {  
            //②等高線の数から、等高線の高さを計算  
            int step = 256 / contourNum;  

            //②階調変換用のヒストグラムを作成  
            byte[] lut = new byte[256];  
            for(int i = 0; i < lut.Length; i++) {  
                lut[i] = (byte)(Math.Floor((double)i / step) * step);  
            }  

            Console.WriteLine(Scalar.White);  

            //②階調を粗くする  
            Mat roughGrad = new Mat();  
            Cv2.LUT(src, lut, roughGrad);  

            //③階調を荒くした画像のエッジ検出  
            Mat contour = new Mat();  
            Cv2.Canny(roughGrad, contour, 1, step - 1);  

            //④カラーマップに変更  
            if(colormapTypes == null) {   
                return contour;  
            }  
            else {  
                Cv2.ApplyColorMap(roughGrad, roughGrad, (ColormapTypes)colormapTypes);  
                for(int j = 0; j < contour.Rows; j++) {  
                    for(int i = 0; i < contour.Cols; i++) {  

                    if(contour.At<char>(j, i) > 0) roughGrad.Set<Vec3b>(j, i, new Vec3b((byte)contourColor[0], (byte)contourColor[1], (byte)contourColor[2]));  
                    }  
                }   
            }  



            return roughGrad;  
        }  

    }  
}  

結果

入力画像

f:id:negizoku:20200920231910p:plain



出力画像

f:id:negizoku:20200920231922p:plain



OpenCVSharp4で輪郭検出

輪郭検出とは

cannyなどで検出したエッジから、輪郭(ループ)を取り出すこと。
OpenCVCv2.FindContours()では輪郭同士の包含関係も取り出せる

プログラム

OpenCVSharp4の導入はコチラ

using System;  
using OpenCvSharp;  

namespace Finding_contours {  
    class Program {  
        static void Main(string[] args) {  
            //画像をグレースケールとして読み込み、平滑化する  
            Mat src = new Mat(@"C:\Users\taman\source\repos\Finding_contours\Finding_contours\picture.png", ImreadModes.Grayscale);  
            src.Blur(new Size(3, 3));  

            //エッジ検出  
            Mat edge = new Mat();  
            Cv2.Canny(src, edge, 100, 200);  
            Point[][] contours;  
            HierarchyIndex[] hierarchy;  

            //輪郭検出  
            Cv2.FindContours(edge, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple);  

            //ランダムカラーで描画  
            Mat drawing = Mat.Zeros(edge.Size(), MatType.CV_8UC3);  
            for(int i = 0; i < contours.Length; i++) {  
                Scalar color = new Scalar(new Random().Next(0, 255), new Random().Next(0, 255), new Random().Next(0, 255));  
                Cv2.DrawContours(drawing, contours, i, color, 2, LineTypes.Link8, hierarchy, 0);  
            }  
            Cv2.ImShow("contours", drawing);  

            Cv2.WaitKey();  
        }  
    }  
}  

参考

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_contours/py_contours_hierarchy/py_contours_hierarchy.html
https://docs.opencv.org/4.0.0/df/d0d/tutorial_find_contours.html