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); }
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; } }