Exemple #1
0
        public void BoxPoints()
        {
            var rotatedRect = new RotatedRect(new Point2f(10, 10), new Size2f(10, 10), (float)(Math.PI / 4));
            var points      = Cv2.BoxPoints(rotatedRect);

            Assert.Equal(4, points.Length);
            Assert.Equal(4.932f, points[0].X, 3);
            Assert.Equal(14.931f, points[0].Y, 3);
            Assert.Equal(5.069f, points[1].X, 3);
            Assert.Equal(4.932f, points[1].Y, 3);
            Assert.Equal(15.068f, points[2].X, 3);
            Assert.Equal(5.069f, points[2].Y, 3);
            Assert.Equal(14.931f, points[3].X, 3);
            Assert.Equal(15.068f, points[3].Y, 3);
        }
Exemple #2
0
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            Mat src = new Mat(@"./t2.png");

            src = src.Resize(new Size(src.Width / 4, src.Height / 4));
            Cv2.ImShow("src", src);

            var binary = BinarizationMat(src);

            Cv2.ImShow("bin", binary);
            var fScreenMat = FindContoursMat(binary, src);

            fScreenMat = new Mat(fScreenMat,
                                 new Rect((int)(fScreenMat.Width * 0.025), (int)(fScreenMat.Height * 0.05),
                                          fScreenMat.Width - (int)(fScreenMat.Width * 0.05), fScreenMat.Height - (int)(fScreenMat.Height * 0.1)));
            Cv2.ImShow("Screen", fScreenMat);

            var m2 = SaturationGain(fScreenMat, 255);

            Cv2.ImShow("SaturationGain", m2);
            Cv2.CvtColor(m2, m2, ColorConversionCodes.BGR2GRAY);
            Mat b2 = m2.Threshold(100, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);

            Cv2.FindContours(b2, out var contours, out var hierarchy, RetrievalModes.Tree,
                             ContourApproximationModes.ApproxSimple);
            Cv2.ImShow("b2", b2);
            var dst = fScreenMat;

            foreach (var itemPoint in contours)
            {
                Console.WriteLine("_________________");
                var epsilon = 0.0075 * Cv2.ArcLength(itemPoint, true);
                var approx  = Cv2.ApproxPolyDP(itemPoint, epsilon, true);
                if (approx.FirstOrDefault().X == 0 || approx.FirstOrDefault().Y == 0)
                {
                    continue;
                }

                Cv2.DrawContours(dst, new IEnumerable <Point>[] { approx }, -1, Scalar.Green, 3);

                Console.WriteLine("Approx Angle:" + approx.Length);
                if (approx.Length == 3)
                {
                    Cv2.PutText(dst, "Triangle", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5, Scalar.Red);
                }
                else if (approx.Length == 4)
                {
                    var rect        = Cv2.BoundingRect(approx);
                    var rotatedRect = Cv2.MinAreaRect(approx);
                    var box         = Cv2.BoxPoints(rotatedRect);
                    Console.WriteLine(rotatedRect.Angle);
                    Cv2.PutText(dst, rotatedRect.Angle.ToString("0.0"), approx.LastOrDefault(),
                                HersheyFonts.HersheyComplex, 0.5, Scalar.Yellow);
                    Cv2.Line(dst, new Point(box[2].X, box[2].Y), new Point(box[0].X, box[0].Y), Scalar.White, 2);

                    var aspectRatio = rect.Width / rect.Height;
                    if (aspectRatio >= 0.9 && aspectRatio <= 1.1)
                    {
                        if ((Math.Abs(rotatedRect.Angle) >= 80 && Math.Abs(rotatedRect.Angle) <= 100) ||
                            Math.Abs(rotatedRect.Angle) <= 10)
                        {
                            Cv2.PutText(dst, "Square", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5,
                                        Scalar.Red);
                        }
                        else
                        {
                            Cv2.PutText(dst, "Diamond", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5,
                                        Scalar.Red);
                        }
                    }
                    else
                    {
                        Cv2.PutText(dst, "Rectangle", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5,
                                    Scalar.Red);
                    }
                }
                else if (approx.Length == 5)
                {
                    Cv2.PutText(dst, "Pentagon", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5, Scalar.Red);
                }
                else if (approx.Length == 10)
                {
                    Cv2.PutText(dst, "Star", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5, Scalar.Red);
                }
                else if (approx.Length > 10)
                {
                    Cv2.PutText(dst, "Circle", approx.FirstOrDefault(), HersheyFonts.HersheyComplex, 0.5,
                                Scalar.Red);
                }

                foreach (var item in approx)
                {
                    Console.WriteLine(item.X + " " + item.Y);
                    Cv2.Circle(dst, item.X, item.Y, 5, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias);
                    Cv2.ImShow("dst", dst);
                    //Cv2.WaitKey();
                }
                //foreach (var item in itemPoint) Console.WriteLine(item.X + " " + item.Y);
                //Console.WriteLine("_________________");
            }
            Cv2.ImShow("dst", dst);
            Cv2.WaitKey();
        }
        /// <summary>
        /// Recognizes letter on the image
        /// </summary>
        /// <param name="clean">B&W thresholded image with black background and white letters</param>
        /// <param name="possibilities">ROI as a list of RotatedRect's with letters marked</param>
        /// <returns>Recognized letters</returns>
        IList <RecognizedLetter> RecognizeLetters(Mat clean, IList <RotatedRect> possibilities)
        {
            var result = new List <RecognizedLetter>();

            foreach (RotatedRect potentialLetter in possibilities)
            {
                var corners = Cv2.BoxPoints(potentialLetter);
                var chunk   = clean.UnwrapShape(corners, 64);

                // check aspect as scanner might have issues with some "tall" chars like { I, J, L, etc. } and we can
                // help the detection here: as there are no that much wide chars, we presume all strongly disproportional chars
                // to be tall and reduce rotation options
                Mat[] letters = null;
                var   size    = potentialLetter.Size;
                var   aspect  = Math.Max(size.Width, size.Height) / Math.Min(size.Width, size.Height);
                if (aspect >= 2.0)
                {
                    if (chunk.Cols > chunk.Rows)                     // it lays on "broadside", let's make it tall again
                    {
                        chunk = chunk.Rotate(RotateFlags.Rotate90Clockwise);
                    }

                    letters = new Mat[]
                    {
                        chunk,
                        chunk.Rotate(RotateFlags.Rotate180)
                    };
                }
                else
                {
                    // as we're not sure how exactly is letter rotated, we recognize it 4 times,
                    // with a 90 degrees step, than the best match (best "confidence" from OCR) is
                    // used
                    letters = new Mat[]
                    {
                        chunk,
                        chunk.Rotate(RotateFlags.Rotate90Clockwise),
                        chunk.Rotate(RotateFlags.Rotate180),
                        chunk.Rotate(RotateFlags.Rotate90CounterClockwise)
                    };
                }

                // ocr
                int selectedIndex     = -1;
                RecognizedLetter unit = null;
                var lowerCases        = new List <KeyValuePair <int, RecognizedLetter> >();
                for (int i = 0; i < letters.Length; ++i)
                {
                    var local = ProcessSingleLetter(letters[i]);
                    if (null != local && (null == unit || (null != unit && null != local && local.Confidence > unit.Confidence)))
                    {
                        if (char.IsUpper(local.Data[0]))
                        {
                            unit          = local;
                            selectedIndex = i;
                        }
                        else
                        {
                            lowerCases.Add(new KeyValuePair <int, RecognizedLetter>(i, local));
                        }
                    }

                    //Cv2.ImShow(string.Format("#{0}: {1} - {2:00.0}%", i, local.Data, local.Confidence * 100), letters[i]);
                }

                if (null == unit && lowerCases.Count > 0)
                {
                    lowerCases.Sort((x, y) => x.Value.Confidence.CompareTo(y.Value.Confidence));
                    unit          = lowerCases[0].Value;
                    selectedIndex = lowerCases[0].Key;
                }

                // process
                if (null != unit)
                {
                    // this will show each separated letter in it's own window, un-wrapped and rotated according
                    // to the best found match
                    //Cv2.ImShow(unit.Data, letters[selectedIndex]);

                    unit.Rect  = Array.ConvertAll(corners, p => new Point(Math.Round(p.X), Math.Round(p.Y)));
                    unit.Angle = (potentialLetter.Angle + selectedIndex * 90) % 360;
                    result.Add(unit);
                }
            }

            return(result);
        }
        /// <summary>
        /// 処理実行
        /// </summary>
        public ImageProcessValue Execute(SettingsObj obj)
        {
            try
            {
                // webカメラキャプチャ
                var camera = new OpenCvSharp.VideoCapture(0)
                {
                    //// 解像度の指定
                    FrameWidth  = 1920,
                    FrameHeight = 1080
                };

                using (camera)
                {
                    // カメラ内部パラメータ格納用
                    Mat mtx  = new Mat();
                    Mat dist = new Mat();

                    // ymlファイルを読み来み計算パラメータを取得
                    using (var fs = new FileStorage(obj.CalibratinFilePath, FileStorage.Mode.Read))
                    {
                        mtx  = fs["mtx"].ReadMat();
                        dist = fs["dist"].ReadMat();
                    }

                    var src = new Mat();

                    // 撮影画像の読み取り
                    camera.Read(src);

                    if (src.Empty())
                    {
                        return(null);
                    }

                    Mat calib = new Mat();
                    // 歪み補正
                    Cv2.Undistort(src, calib, mtx, dist);

                    // 画像処理
                    var tmp = new Mat();
                    // OpenCVのカラーの並びに変換
                    Cv2.CvtColor(calib, tmp, OpenCvSharp.ColorConversionCodes.RGB2BGR);
                    // BGR画像をHSV画像に変換
                    var hsv = new Mat();
                    Cv2.CvtColor(tmp, hsv, OpenCvSharp.ColorConversionCodes.BGR2HSV);
                    // inRange関数で範囲指定2値化 -> マスク画像として使う
                    var msk = new Mat();
                    Cv2.InRange(hsv, new Scalar(obj.HueMin, obj.SaturationMin, obj.ValueMin), new Scalar(obj.HueMax, obj.SaturationMax, obj.ValueMax), msk);

                    // bitwise_andで元画像にマスクをかける -> マスクされた部分の色だけ残る
                    var msk_src = new Mat();
                    Cv2.BitwiseAnd(hsv, hsv, msk_src, msk);
                    var show_msk = new Mat();
                    // 元の色に戻す
                    Cv2.CvtColor(msk_src, show_msk, ColorConversionCodes.HSV2BGR);
                    // グレースケール変換
                    var gray = new Mat();
                    Cv2.CvtColor(show_msk, gray, ColorConversionCodes.BGR2GRAY);
                    // 2値化
                    var th = new Mat();
                    Cv2.Threshold(gray, th, 130, 255, ThresholdTypes.Otsu);

                    // ブロブとラベリング
                    var label              = new Mat();
                    var stats              = new Mat();
                    var centroids          = new Mat();
                    ConnectedComponents cc = Cv2.ConnectedComponentsEx(th);

                    if (cc.LabelCount <= 1)
                    {
                        return(null);
                    }
                    // draw labels
                    //cc.RenderBlobs(show_msk);
                    // draw bonding boxes except background
                    foreach (var blob in cc.Blobs.Skip(1))
                    {
                        show_msk.Rectangle(blob.Rect, Scalar.Red);
                    }

                    // filter maximum blob
                    var maxBlob  = cc.GetLargestBlob();
                    var filtered = new Mat();
                    cc.FilterByBlob(show_msk, filtered, maxBlob);

                    // 矩形探索
                    // マスク画像から矩形探索
                    Point[][]        contours;
                    HierarchyIndex[] hierarchy;
                    Cv2.FindContours(th, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxNone);
                    // 見つからなかったら何もしない
                    if (contours.Length == 0)
                    {
                        return(null);
                    }

                    // 回転を考慮した外接矩形
                    foreach (var cont in contours)
                    {
                        var rect = Cv2.MinAreaRect(cont);
                        var box  = Cv2.BoxPoints(rect).Select(x => (Point)x);
                    }

                    Cv2.DrawContours(show_msk, contours, -1, Scalar.Yellow, 3);
                    //Cv2.ImShow("show_msk", show_msk);

                    // 画像、画像上の位置発火
                    var val = new ImageProcessValue();
                    val.CameraImage = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(show_msk);
                    val.Blob        = maxBlob;

                    // メモリリーク対策でDispose呼ぶ
                    mtx.Dispose();
                    dist.Dispose();
                    calib.Dispose();
                    tmp.Dispose();
                    hsv.Dispose();
                    msk.Dispose();
                    msk_src.Dispose();
                    show_msk.Dispose();
                    gray.Dispose();
                    th.Dispose();
                    label.Dispose();
                    stats.Dispose();
                    centroids.Dispose();
                    filtered.Dispose();

                    return(val);
                }
            }
            catch (Exception e)
            {
                throw e;
            }
        }