public static Point[] GetContours(Mat edgesImg) { Point[] screenCut = null; Point[][] contours; Mat heirchy = Mat.Zeros(edgesImg.Size(), edgesImg.Type()); HierarchyIndex[] hIndex; Cv2.FindContours(edgesImg, out contours, out hIndex, RetrievalModes.List, ContourApproximationModes.ApproxSimple); var query = contours .OrderBy(n => Cv2.ContourArea(n)) .Reverse(); foreach (var contour in query) { double peri = Cv2.ArcLength(contour, true); Point[] approx = Cv2.ApproxPolyDP(contour, 0.02 * peri, true); if (approx.Length == 4) { screenCut = approx; break; } } return(screenCut); }
/// <summary> /// Takes candidate shape and combined hull and returns best match /// </summary> /// <param name="areaSize">Area size</param> /// <param name="candidates">Candidates</param> /// <param name="hull">Hull</param> /// <returns></returns> private Point[] GetBestMatchingContour(double areaSize, List <Point[]> candidates, Point[] hull) { Point[] result = hull; if (candidates.Count == 1) { result = candidates[0]; } else if (candidates.Count > 1) { List <Point> keys = new List <Point>(); foreach (var c in candidates) { keys.AddRange(c); } Point[] joinedCandidates = Cv2.ConvexHull(keys); Point[] joinedHull = Cv2.ApproxPolyDP(joinedCandidates, Cv2.ArcLength(joinedCandidates, true) * 0.01, true); result = joinedHull; } // check further if (Settings.DropBadGuess) { double area = Cv2.ContourArea(result); if (area / areaSize < Settings.ExpectedArea * 0.75) { result = null; } } return(result); }
private OpenCvSharp.Point[] findMostOuterBoxCorner(Mat canny) { OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(canny, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS); int maxIndex = 0; double maxArea = double.MinValue; for (int i = 0; i < contours.Length; i++) { OpenCvSharp.Point[] p = contours[i]; double length = Cv2.ArcLength(p, true); double area = Cv2.ContourArea(p, true); //if (Math.Abs(area) > maxArea) if (area > maxArea) { maxArea = area;//maxArea = Math.Abs(area); maxIndex = i; } } OpenCvSharp.Point[] p1 = contours[maxIndex]; double length1 = Cv2.ArcLength(p1, true); OpenCvSharp.Point[] pp = Cv2.ApproxPolyDP(p1, 0.02 * length1, true); return(pp); }
public static OpenCvSharp.Point[] Square(Mat src) { Mat[] split = Cv2.Split(src); Mat blur = new Mat(); Mat binary = new Mat(); OpenCvSharp.Point[] squares = new OpenCvSharp.Point[4]; int N = 10; double max = src.Size().Width *src.Size().Height * 0.9; double min = src.Size().Width *src.Size().Height * 0.1; for (int channel = 0; channel < 3; channel++) { Cv2.GaussianBlur(split[channel], blur, new OpenCvSharp.Size(5, 5), 1); for (int i = 0; i < N; i++) { Cv2.Threshold(blur, binary, i * 255 / N, 255, ThresholdTypes.Binary); OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxTC89KCOS); for (int j = 0; j < contours.Length; j++) { double perimeter = Cv2.ArcLength(contours[j], true); OpenCvSharp.Point[] result = Cv2.ApproxPolyDP(contours[j], perimeter * 0.02, true); double area = Cv2.ContourArea(result); bool convex = Cv2.IsContourConvex(result); } } } }
public static int GetApprox(Point [] contor) { double peri = Cv2.ArcLength(contor, true); Point [] approx = Cv2.ApproxPolyDP(contor, 0.02 * peri, true); return(approx.Count( )); }
private bool findContourOfPuzzle() { Point[] simple; double maxArea = 0; double area; bool found = false; foreach (Point[] contour in contours) { var curve = new List <Point>(contour); double eps = Cv2.ArcLength(curve, true) * .08; simple = Cv2.ApproxPolyDP(curve, eps, true); if (simple.Length == 4 && Cv2.IsContourConvex(simple)) { area = Cv2.ContourArea(contour); if (area > maxArea) { likelyCandidate = simple; maxArea = area; found = true; } } } return(found); }
//边缘检测 private void ucBtnExt_A2_BtnClick(object sender, EventArgs e) { //提取苹果轮廓 Cv2.FindContours(apple_mask, out apple_contours, out apple_hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone); background_mask = new Mat(fruit_mask.Size(), MatType.CV_8UC1, new Scalar(0)); //绘制苹果轮廓 Cv2.DrawContours(background_mask, apple_contours, -1, new Scalar(255), 2, LineTypes.Link8, apple_hierarchy); //提取梨子轮廓 Cv2.FindContours(pear_mask, out pear_contours, out pear_hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone); //绘制梨子轮廓 for (int i = 0; i < pear_contours.Length; i++) { if (Cv2.ArcLength(pear_contours[i], true) > 40) //轮廓周长大于40时绘制 { Cv2.DrawContours(background_mask, pear_contours, i, new Scalar(255), 2, LineTypes.Link8, pear_hierarchy); } } this.pictureBoxIpl_img.ImageIpl = background_mask; //输出日志 newLog = "[msg] 边缘检测完毕"; }
//目标质心坐标点计算 private void ucBtnExt_A4_BtnClick(object sender, EventArgs e) { //计算苹果质心 apple_moment = Cv2.Moments(apple_contours[0]); apple_center.X = (int)(apple_moment.M10 / apple_moment.M00); apple_center.Y = (int)(apple_moment.M01 / apple_moment.M00); //绘制出来 Cv2.Circle(background_mask, apple_center, 3, new Scalar(255), -1); //计算梨子质心 for (int i = 0; i < pear_contours.Length; i++) { if (Cv2.ArcLength(pear_contours[i], true) > 40) //轮廓周长大于40时计算 { pear_moment = Cv2.Moments(pear_contours[i]); pear_center.X = (int)(pear_moment.M10 / pear_moment.M00); pear_center.Y = (int)(pear_moment.M01 / pear_moment.M00); //绘制出来 Cv2.Circle(background_mask, pear_center, 3, new Scalar(255), -1); } } this.pictureBoxIpl_img.ImageIpl = background_mask; //输出日志 newLog = "[msg] 苹果在图像中的质心:(" + apple_center.X.ToString() + "," + apple_center.Y.ToString() + "), " + "梨子在图像中的质心:(" + pear_center.X.ToString() + "," + pear_center.Y.ToString() + ")"; }
public override IEnumerable <ContourResult> DetectDocumentContours(Mat image, Mat sourceImage) { var contours = Cv2 .FindContoursAsArray(image, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple); var results = new List <ContourResult>(); foreach (var contour in contours) { var convexHull = Cv2.ConvexHull(contour); var peri = Cv2.ArcLength(convexHull, true); var simplifiedContour = Cv2.ApproxPolyDP(convexHull, _epsilon * peri, true); if (simplifiedContour != null) { results.Add(new ContourResult { Score = Score, Points = simplifiedContour }); } } return(results); }
//轮廓像素坐标点计算 private void ucBtnExt_A3_BtnClick(object sender, EventArgs e) { //输出苹果所有像素坐标点 for (int i = 0; i < apple_contours[0].Length; i++) { apple_coordinate += " (" + apple_contours[0][i].X.ToString() + "," + apple_contours[0][i].Y.ToString() + "), "; } //存储苹果二维坐标点 ShareData.Apple_contour = apple_contours[0]; newLog = "[msg] 苹果所有轮廓坐标点:" + apple_coordinate; Cv2.WaitKey(200); //输出梨子所有像素坐标点 for (int i = 0; i < pear_contours.Length; i++) { //轮廓周长大于40时计算 if (Cv2.ArcLength(pear_contours[i], true) > 40) { for (int j = 0; j < pear_contours[i].Length; j++) { pear_coordinate += " (" + pear_contours[i][j].X.ToString() + "," + pear_contours[i][j].Y.ToString() + ") "; } //存储梨子二维坐标点 ShareData.Pear_contour = pear_contours[i]; } } newLog = "[msg] 梨子所有轮廓坐标点:" + pear_coordinate; }
/// <summary> /// Crops and Transforms Image to return an image of a card /// </summary> /// <param name="imageInput"></param> /// <returns>Mat[]</returns> public Mat GetCardImage(Mat imageInput) { Mat image = imageInput; image = new Mat(image, roi); Mat transformedImage = GetTransformedImage(image); Mat returnCardImage = new Mat(); Point[][] contours; HierarchyIndex[] outputArray; //Only external contours... we don't want the textbox or the art of the card Cv2.FindContours(transformedImage, out contours, out outputArray, RetrievalModes.External, ContourApproximationModes.ApproxSimple); //FindContours will give array of contours detected in image. Here we filter to get the contour of a card by... foreach (Point[] contour in contours) { //shaping out the contour Point[] contourPoints = Cv2.ApproxPolyDP(contour, .01 * Cv2.ArcLength(contour, true), true); //and then checking if it's a rectangle and is big enough to be our card... if (contourPoints.Length == 4 && Cv2.ContourArea(contour) > 600) { //if it is, apply a perspective transform to get a flat image and return it Point2f[] arrayContourPoints = new Point2f[4]; for (int i = 0; i < arrayContourPoints.Length; i++) { arrayContourPoints[i] = contourPoints[i]; } var corners = GetCorners(arrayContourPoints); if (corners == null) { returnCardImage = null; break; } int rotation = GetCardRotation(corners); ///The order of the corners matter for the perspective transform. The order differs depending on rotation. if (rotation < 0) { arrayContourPoints[0] = corners["leftCorner"]; arrayContourPoints[1] = corners["topCorner"]; arrayContourPoints[2] = corners["bottomCorner"]; arrayContourPoints[3] = corners["rightCorner"]; } else { arrayContourPoints[0] = corners["topCorner"]; arrayContourPoints[1] = corners["rightCorner"]; arrayContourPoints[2] = corners["leftCorner"]; arrayContourPoints[3] = corners["bottomCorner"]; } Point2f[] destinationPoints = { new Point(0, 0), new Point(672, 0), new Point(0, 936), new Point(672, 936) }; Mat perspective = Cv2.GetPerspectiveTransform(arrayContourPoints, destinationPoints); Cv2.WarpPerspective(image, returnCardImage, perspective, new Size(672, 936)); break; } } return(returnCardImage); }
public void ArcLength() { var arc = new[] { new Point2f(0, 0), new Point2f(10, 0), new Point2f(10, 10), new Point2f(0, 10), }; var length1 = Cv2.ArcLength(arc, true); Assert.Equal(40, length1); var length2 = Cv2.ArcLength(arc, false); Assert.Equal(30, length2); }
public Mat MinEnclosing(Mat img) { Mat binary = new Mat(); Mat morp = new Mat(); Mat image = new Mat(); Mat dst = img.Clone(); int pointX = 0, pointY = 0; string text; Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(3, 3)); OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchy; Cv2.Threshold(img, binary, 230, 255, ThresholdTypes.Binary); Cv2.MorphologyEx(binary, morp, MorphTypes.Close, kernel, new OpenCvSharp.Point(-1, -1), 2); Cv2.BitwiseNot(morp, image); Cv2.FindContours(image, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS); for (int i = 0; i < contours.Length; i++) { double perimeter = Cv2.ArcLength(contours[i], true); double epsilon = perimeter * 0.01; OpenCvSharp.Point[] approx = Cv2.ApproxPolyDP(contours[i], epsilon, true); OpenCvSharp.Point[][] draw_approx = new OpenCvSharp.Point[][] { approx }; Cv2.DrawContours(dst, draw_approx, -1, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias); Cv2.MinEnclosingCircle(contours[i], out Point2f center, out float radius); Cv2.Circle(dst, new OpenCvSharp.Point(center.X, center.Y), (int)radius, Scalar.Red, 2, LineTypes.AntiAlias); pointX += (int)center.X; pointY += (int)center.Y; for (int j = 0; j < approx.Length; j++) { Cv2.Circle(dst, approx[j], 1, new Scalar(0, 0, 255), 3); } } if (contours.Length > 0) { pointX = pointX / contours.Length; pointY = pointY / contours.Length; text = pointX.ToString(); text = text + ":" + pointY.ToString(); Cv2.PutText(dst, text, new OpenCvSharp.Point(3300, 2700), HersheyFonts.HersheyPlain, 5, Scalar.White, 5); } return(dst); }
public static OpenCvSharp.Point[] Square(Mat src) { Mat[] split = Cv2.Split(src); Mat blur = new Mat(); Mat binary = new Mat(); OpenCvSharp.Point[] squares = new OpenCvSharp.Point[4]; int N = 10; double max = src.Size().Width *src.Size().Height * 0.9; double min = src.Size().Width *src.Size().Height * 0.1; for (int channel = 0; channel < 3; channel++) { Cv2.GaussianBlur(split[channel], blur, new OpenCvSharp.Size(5, 5), 1); for (int i = 0; i < N; i++) { Cv2.Threshold(blur, binary, i * 255 / N, 255, ThresholdTypes.Binary); OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxTC89KCOS); Mat test = src.Clone(); Cv2.DrawContours(test, contours, -1, new Scalar(0, 0, 255), 3); for (int j = 0; j < contours.Length; j++) { double perimeter = Cv2.ArcLength(contours[j], true); OpenCvSharp.Point[] result = Cv2.ApproxPolyDP(contours[j], perimeter * 0.02, true); double area = Cv2.ContourArea(result); bool convex = Cv2.IsContourConvex(result); if (result.Length == 4 && area > min && area < max && convex) { double cos = 0; for (int k = 1; k < 5; k++) { double t = Math.Abs(Angle(result[(k - 1) % 4], result[k % 4], result[(k + 1) % 4])); cos = cos > t ? cos : t; } if (cos < 0.15) { squares = result; } } } } } return(squares); }
static void Main(string[] args) { Console.WriteLine("Hello World!"); Mat src = new Mat(@"./1.jpg"); src = src.Resize(new Size(src.Width / 2, src.Height / 2)); 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.01 * Cv2.ArcLength(itemPoint, true); var approx = Cv2.ApproxPolyDP(itemPoint, epsilon, true); Console.WriteLine("Approx Angle:" + approx.Length); 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.DrawContours(dst, contours, -1, Scalar.Green, 3); Cv2.ImShow("dst", dst); Cv2.WaitKey(); }
public static bool CheckRectCorners(Point[] contour) { var epsilon = 0.1 * Cv2.ArcLength(contour, true); var approximation = Cv2.ApproxPolyDP(contour, epsilon, true); var hasFourCorners = approximation.Length == 4; var isConvex = Cv2.IsContourConvex(approximation); if (hasFourCorners && isConvex) { return(true); } else { return(false); } }
//블럭화 private void button1_Click(object sender, EventArgs e) { ((DataTable)dataGridView1.DataSource).Rows.Clear(); //Row값만 초기화 int counter = 1; Mat dst = Threshold.Clone(); double img_area = MyImage.Width * MyImage.Height; OpenCvSharp.Point[][] contours; //윤곽선의 실제 값 HierarchyIndex[] hierarchy; // 윤곽선들의 계층 구조 Mat preprocess_Value = new Mat(); //색상 공간 변환 Cv2.InRange(Threshold, new Scalar(threshold_Value, threshold_Value, threshold_Value), new Scalar(255, 255, 255), preprocess_Value); //Cv2.FindContours(원본 배열, 검출된 윤곽선, 계층 구조, 검색 방법, 근사 방법, 오프셋) Cv2.FindContours(preprocess_Value, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS); foreach (OpenCvSharp.Point[] p in contours) { double length = Cv2.ArcLength(p, true); //길이 double area = Cv2.ContourArea(p, true); //면적 //if (length < 200 || length > 2000) continue; //길이가 너무 작은 윤관석 삭제 Rect boundingRect = Cv2.BoundingRect(p); //사각형 계산 OpenCvSharp.Point[] hull = Cv2.ConvexHull(p, true); //블록 Moments moments = Cv2.Moments(p, false); //중심점 Cv2.Rectangle(dst, boundingRect, Scalar.Red, 2); //사각형 그리기 //Cv2.FillConvexPoly(dst, hull, Scalar.Red); //내부 채우기 //Cv2.Polylines(dst, new OpenCvSharp.Point[][] { hull }, true, Scalar.Red, 1); //다각형 그리기 Cv2.DrawContours(dst, new OpenCvSharp.Point[][] { hull }, -1, Scalar.Black, 3); //윤곽석 그리기 double mean = (boundingRect.Width + boundingRect.Height) / 2; table.Rows.Add(" " + counter++, " " + (int)(moments.M10 / moments.M00) + ", " + (int)(moments.M01 / moments.M00), " " + Math.Truncate(length * 10) / 10, " " + Math.Abs(area - img_area), " " + area, " " + Math.Max(boundingRect.Width, boundingRect.Height), " " + Math.Min(boundingRect.Width, boundingRect.Height), " " + mean, boundingRect); } pictureBox2.Image = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(dst); }
private Point[] GetContour(Mat edged) { Point[][] contours; HierarchyIndex[] hierarchyIndexes; Cv2.FindContours(edged.Clone(), out contours, out hierarchyIndexes, ContourRetrieval.List, ContourChain.ApproxSimple); var area = edged.Height * edged.Width; var sortedContours = contours.Where(c => GetContourArea(c) / area > 0.3).OrderByDescending(GetContourArea); foreach (var contour in sortedContours) { var epsilon = 0.02 * Cv2.ArcLength(contour, true); var approx = Cv2.ApproxPolyDP(contour, epsilon, true); if (approx.Length == 4) { return(approx); } } return(null); }
//显示目标识别结果 private void ucBtnExt_A5_BtnClick(object sender, EventArgs e) { //计算最小外接矩形 apple_rect = Cv2.BoundingRect(apple_contours[0]); for (int i = 0; i < pear_contours.Length; i++) { if (Cv2.ArcLength(pear_contours[i], true) > 40) //轮廓周长大于40时绘制 { pear_rect = Cv2.BoundingRect(pear_contours[i]); } } //绘制结果 beifenImg.CopyTo(resultImage); Cv2.Rectangle(resultImage, apple_rect, new Scalar(255, 0, 0), 2); Cv2.Rectangle(resultImage, pear_rect, new Scalar(255, 0, 0), 2); Cv2.Circle(resultImage, apple_center, 7, new Scalar(0, 0, 0), -1); Cv2.Circle(resultImage, pear_center, 7, new Scalar(0, 0, 0), -1); this.pictureBoxIpl_img.ImageIpl = resultImage; newLog = "[msg] 目标识别成功~"; }
static void Main(string[] args) { Mat src = Cv2.ImRead("chess.png"); Mat gray = new Mat(); Mat binary = new Mat(); Mat morp = new Mat(); Mat image = new Mat(); Mat dst = src.Clone(); Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); Point[][] contours; HierarchyIndex[] hierarchy; Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY); Cv2.Threshold(gray, binary, 230, 255, ThresholdTypes.Binary); Cv2.MorphologyEx(binary, morp, MorphTypes.Close, kernel, new Point(-1, -1), 2); Cv2.BitwiseNot(morp, image); Cv2.FindContours(image, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxTC89KCOS); for (int i = 0; i < contours.Length; i++) { double perimeter = Cv2.ArcLength(contours[i], true); double epsilon = perimeter * 0.01; Point[] approx = Cv2.ApproxPolyDP(contours[i], epsilon, true); Point[][] draw_approx = new Point[][] { approx }; Cv2.DrawContours(dst, draw_approx, -1, new Scalar(255, 0, 0), 2, LineTypes.AntiAlias); for (int j = 0; j < approx.Length; j++) { Cv2.Circle(dst, approx[j], 1, new Scalar(0, 0, 255), 3); } } Cv2.ImShow("dst", dst); Cv2.WaitKey(0); Cv2.DestroyAllWindows(); }
public void findTextRegion(Mat dilation) { // 1. 查找轮廓 OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchly; Rect biggestContourRect = new Rect(); Cv2.FindContours(dilation, out contours, out hierarchly, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple); // 2. 筛选那些面积小的 int i = 0; foreach (OpenCvSharp.Point[] contour in contours) { double area = Cv2.ContourArea(contour); //面积小的都筛选掉 if (area < 1000) { continue; } //轮廓近似,作用很小 double epsilon = 0.001 * Cv2.ArcLength(contour, true); //找到最小的矩形 biggestContourRect = Cv2.BoundingRect(contour); if (biggestContourRect.Height > (biggestContourRect.Width * 1.2)) { continue; } //画线 mat.Rectangle(biggestContourRect, new Scalar(0, 255, 0), 2); } pictureBox1.Image = mat.ToBitmap(); //Cv2.ImShow("img", mat); }
void FindRect(out Point[] corners) { //頂点をnull(空)で初期化 corners = null; //輪郭を構成する点と階層 Point[][] contours; HierarchyIndex[] h; //輪郭認識 bin.FindContours(out contours, out h, RetrievalModes.External, ContourApproximationModes.ApproxSimple); //面積が最大となる輪郭を処理対象とする double maxArea = 0; for (int i = 0; i < contours.Length; i++) { //輪郭の長さを算出 double length = Cv2.ArcLength(contours[i], true); //多角形近似(全周の1%以内の誤差を許容) Point[] tmp = Cv2.ApproxPolyDP(contours[i], length * 0.01f, true); double area = Cv2.ContourArea(contours[i]); if (tmp.Length == 4 && area > maxArea) { maxArea = area; corners = tmp; } } //面積最大の輪郭cornersをbgr上に描画 /*if (corners != null) * { * bgr.DrawContours(new Point[][] { corners }, 0, Scalar.Red, 5); * //各頂点の位置に円を描画 * for (int i = 0; i < corners.Length; i++) * { * bgr.Circle(corners[i], 20, Scalar.Blue, 5); * } * }*/ }
/// <summary> /// Filter the contours which was found by Canny algorithm /// </summary> /// <param name="canny">The result of Canny detector work</param> /// <returns>Filtered contours</returns> static List <Point[]> FindContoures(Mat canny) { Point[][] contoures = Cv2.FindContoursAsArray(canny, RetrievalModes.External, ContourApproximationModes.ApproxSimple); List <Point[]> contoures2 = new List <Point[]>(); foreach (var cont in contoures) { if (Math.Abs(Cv2.ContourArea(cont)) < 2) { continue; // square } if (Cv2.ArcLength(cont, true) < 15) { continue; // perimeter } if (cont.Length < 5) { continue; // number of pixels in the contour } contoures2.Add(cont); } return(contoures2); }
public List <Point2f> GetCorners() { if (mFrame != null) { Point[][] contours = new Point[][] { }; HierarchyIndex[] hierarchy = new HierarchyIndex[] { }; Cv2.FindContours(mFrame, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxSimple); Point[] rect = new Point[] { }; double maxLength = 0; foreach (var item in contours) { var perimeter = Cv2.ArcLength(item, true); var approx = Cv2.ApproxPolyDP(item, 0.015 * perimeter, true); if (approx.Length == 4 && perimeter > maxLength) { maxLength = perimeter; rect = approx; } } mResult = new List <Point2f>(); foreach (var item in rect) { mResult.Add(new Point2f(item.X, item.Y)); } OrderPoints(); return(mResult); } return(null); }
/// <summary> /// The magic is here /// </summary> private void CalculateOutput() { Mat matGray = null; // instead of regular Grayscale, we use BGR -> HSV and take Hue channel as // source if (Settings.GrayMode == ScannerSettings.ColorMode.HueGrayscale) { var matHSV = matInput_.CvtColor(ColorConversionCodes.RGB2HSV); Mat[] hsvChannels = matHSV.Split(); matGray = hsvChannels[0]; } // Alternative: just plain BGR -> Grayscale else { matGray = matInput_.CvtColor(ColorConversionCodes.BGR2GRAY); } // scale down if necessary var matScaled = matGray; float sx = 1, sy = 1; if (Settings.Scale != 0) { if (matGray.Width > Settings.Scale) { sx = (float)Settings.Scale / matGray.Width; } if (matGray.Height > Settings.Scale) { sy = (float)Settings.Scale / matGray.Height; } matScaled = matGray.Resize(new Size(Math.Min(matGray.Width, Settings.Scale), Math.Min(matGray.Height, Settings.Scale))); } // reduce noise var matBlur = matScaled; if (Settings.NoiseReduction != 0) { int medianKernel = 11; // calculate kernel scale double kernelScale = Settings.NoiseReduction; if (0 == Settings.Scale) { kernelScale *= Math.Max(matInput_.Width, matInput_.Height) / 512.0; } // apply scale medianKernel = (int)(medianKernel * kernelScale + 0.5); medianKernel = medianKernel - (medianKernel % 2) + 1; if (medianKernel > 1) { matBlur = matScaled.MedianBlur(medianKernel); } } // detect edges with our 'adaptive' algorithm that computes bounds automatically with // image's mean value var matEdges = matBlur.AdaptiveEdges(Settings.EdgesTight); // now find contours Point[][] contours; HierarchyIndex[] hierarchy; Cv2.FindContours(matEdges, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxNone, null); // check contours and drop those we consider "noise", all others put into a single huge "key points" map // also, detect all almost-rectangular contours with big area and try to determine whether they're exact match List <Point> keyPoints = new List <Point>(); List <Point[]> goodCandidates = new List <Point[]>(); double referenceArea = matScaled.Width * matScaled.Height; foreach (Point[] contour in contours) { double length = Cv2.ArcLength(contour, true); // drop mini-contours if (length >= 25.0) { Point[] approx = Cv2.ApproxPolyDP(contour, length * 0.01, true); keyPoints.AddRange(approx); if (approx.Length >= 4 && approx.Length <= 6) { double area = Cv2.ContourArea(approx); if (area / referenceArea >= Settings.ExpectedArea) { goodCandidates.Add(approx); } } } } // compute convex hull, considering we presume having an image of a document on more or less // homogeneous background, this accumulated convex hull should be the document bounding contour Point[] hull = Cv2.ConvexHull(keyPoints); Point[] hullContour = Cv2.ApproxPolyDP(hull, Cv2.ArcLength(hull, true) * 0.01, true); // find best guess for our contour Point[] paperContour = GetBestMatchingContour(matScaled.Width * matScaled.Height, goodCandidates, hullContour); if (null == paperContour) { shape_ = null; dirty_ = false; matOutput_ = matInput_; return; } // exact hit - we have 4 corners if (paperContour.Length == 4) { paperContour = SortCorners(paperContour); } // some hit: we either have 3 points or > 4 which we can try to make a 4-corner shape else if (paperContour.Length > 2) { // yet contour might contain too much points: along with calculation inaccuracies we might face a // bended piece of paper, missing corner etc. // the solution is to use bounding box RotatedRect bounds = Cv2.MinAreaRect(paperContour); Point2f[] points = bounds.Points(); Point[] intPoints = Array.ConvertAll(points, p => new Point(Math.Round(p.X), Math.Round(p.Y))); Point[] fourCorners = SortCorners(intPoints); // array.ClosestElement is not efficient but we can live with it since it's quite few // elements to search for System.Func <Point, Point, double> distance = (Point x, Point y) => Point.Distance(x, y); Point[] closest = new Point[4]; for (int i = 0; i < fourCorners.Length; ++i) { closest[i] = paperContour.ClosestElement(fourCorners[i], distance); } paperContour = closest; } // scale contour back to input image coordinate space - if necessary if (sx != 1 || sy != 1) { for (int i = 0; i < paperContour.Length; ++i) { Point2f pt = paperContour[i]; paperContour[i] = new Point2f(pt.X / sx, pt.Y / sy); } } // un-wrap var matUnwrapped = matInput_; bool needConvertionToBGR = true; if (paperContour.Length == 4) { matUnwrapped = matInput_.UnwrapShape(Array.ConvertAll(paperContour, p => new Point2f(p.X, p.Y))); // automatic color converter bool convertColor = (ScannerSettings.DecolorizationMode.Always == Settings.Decolorization); if (ScannerSettings.DecolorizationMode.Automatic == Settings.Decolorization) { convertColor = !IsColored(matUnwrapped); } // perform color conversion to b&w if (convertColor) { matUnwrapped = matUnwrapped.CvtColor(ColorConversionCodes.BGR2GRAY); // we have some constants for Adaptive, but this can be improved with some 'educated guess' for the constants depending on input image if (ScannerSettings.ScanType.Adaptive == Settings.ColorThreshold) { matUnwrapped = matUnwrapped.AdaptiveThreshold(255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.Binary, 47, 25); } // Otsu doesn't need our help, decent on it's own else { matUnwrapped = matUnwrapped.Threshold(0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu); } } else { needConvertionToBGR = false; } } // assign result shape_ = paperContour; matOutput_ = matUnwrapped; if (needConvertionToBGR) { matOutput_ = matOutput_.CvtColor(ColorConversionCodes.GRAY2BGR); // to make it compatible with input texture } // mark we're good dirty_ = false; }
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(); }
private void LoadImage(string filePath) { try { using (Mat image = new Mat(filePath)) using (Mat resized = image.Resize(GetTargetSize(image.Size()))) //Scale the image, so we are working with something consistent { AddImage(resized); using (Mat gray = resized.CvtColor(ColorConversionCodes.BGR2GRAY)) //Convert to gray scale since we don't want the color data using (Mat blur = gray.GaussianBlur(new Size(5, 5), 0)) //Smooth the image to eliminate noise using (Mat autoCanny = blur.AutoCanny()) //Apply canny edge filter to find edges using (Mat structuringElement = Cv2.GetStructuringElement(MorphShapes.Ellipse, new Size(9, 9))) { AddImage(autoCanny); //Smooth over small possible breaks in edges Cv2.Dilate(autoCanny, autoCanny, structuringElement); AddImage(autoCanny); Cv2.Erode(autoCanny, autoCanny, structuringElement); AddImage(autoCanny); //Change the RetrievalModes to CComp if you want internal polygons too, not just the outer most one. Point[][] contours = autoCanny.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxSimple); //Draw all of the found polygons for reference using (Mat allFound = resized.Clone()) { for (int i = 0; i < contours.Length; i++) { Cv2.DrawContours(allFound, contours, i, Scalar.RandomColor(), 2); } AddImage(allFound); } //Find the largest polygons that four corners var found = (from contour in contours let permimiter = Cv2.ArcLength(contour, true) let approx = Cv2.ApproxPolyDP(contour, 0.04 * permimiter, true) where approx.Length == 4 //Rectange let area = Cv2.ContourArea(approx) orderby area descending //We are looking for the biggest thing select approx).Take(3).ToArray(); //Grabbing three just for comparison //Colors the found polygons Green->Yellow->Red to indicate best matches. for (int i = 0; i < found.Length; i++) { Scalar color; switch (i) { case 0: color = Scalar.Green; break; case 1: color = Scalar.Yellow; break; case 2: color = Scalar.Red; break; default: color = Scalar.RandomColor(); break; } resized.DrawContours(found, i, color, 3); } AddImage(resized); } } } catch (Exception e) { Debug.WriteLine(e.ToString()); } Size GetTargetSize(Size size, int longSize = 512) { if (size.Width > size.Height) { return(new Size(longSize, (int)(longSize * (double)size.Height / size.Width))); } return(new Size((int)(longSize * (double)size.Width / size.Height), longSize)); } }
private void Test3(string img, double canny1, double canny2, double blur) { var tess = new TesseractEngine(@"./wwwroot/tessdata", "eng", EngineMode.LstmOnly); byte[] imageData = System.IO.File.ReadAllBytes(@"./wwwroot/images/" + img); Mat img1 = Mat.FromImageData(imageData, ImreadModes.Color); //Convert the img1 to grayscale and then filter out the noise Mat gray1 = Mat.FromImageData(imageData, ImreadModes.Grayscale) /*.PyrDown().PyrUp()*/; //gray1 = gray1.GaussianBlur(new OpenCvSharp.Size(blur, blur), 0); //gray1 = gray1.AdaptiveThreshold(255, AdaptiveThresholdTypes.MeanC, ThresholdTypes.BinaryInv, (int)canny1, canny2); // 11,2 ; 75,10 ; 60,255 gray1 = gray1.GaussianBlur(new Size(blur, blur), 0); //gray1 = gray1.Threshold(128, 255, ThresholdTypes.Binary); //Canny Edge Detector //Image<Gray, Byte> cannyGray = gray1.Canny(20, 50); //Image<Bgr, Byte> imageResult = img1.Copy(); Mat cannyGray = gray1.Canny(canny1, canny2); //var cannyGray = gray1; // treba aj GaussianBlur, adaptiveThreshold Random r = new Random(); //int lastY = 0; Point[][] contours; //vector<vector<Point>> contours; HierarchyIndex[] hierarchy; //vector<Vec4i> hierarchy; //int draw = 0; Cv2.FindContours(cannyGray, out contours, out hierarchy, mode: RetrievalModes.Tree, method: ContourApproximationModes.ApproxSimple); Debug.WriteLine("po�et - " + contours.Length); Mat copy = img1.Clone(); Cv2.DrawContours(copy, contours, -1, Scalar.Orange); var j = 0; while (j != -1) { var index = hierarchy[j]; if (index.Parent != -1) { j = index.Next; continue; } Scalar scalar = Scalar.FromRgb(r.Next(0, 255), r.Next(0, 255), r.Next(0, 255)); Cv2.DrawContours(copy, contours, j, scalar); var edges = contours[j]; Point[] contoursAp = Cv2.ApproxPolyDP(edges, Cv2.ArcLength(edges, true) * 0.01, true); //Debug.WriteLine(j + "," + contoursAp.Length); /*var rect = Cv2.BoundingRect(edges); * var roi2 = img1.Clone(rect); * roi2.SaveImage("pozri-" + j + ".png");*/ j = index.Next; } var m = 0; foreach (var c in contours) { //var rect = Cv2.BoundingRect(c); //var roi2 = img1.Clone(rect); //roi2.SaveImage("pozri-" + m + ".png"); m++; } copy.SaveImage("wwwroot/images/output.png"); }
private Mat FindContoursAndDraw(Bitmap originalMap, string objectName, int minArea = 500, int maxArea = 10000) { //var houghBitmap = HoughTransform(originalMap); //var invertedHoughBitmap = InvertImage(houghBitmap); Mat originalMat = BitmapConverter.ToMat(originalMap); //Mat invertedHoughMat = BitmapConverter.ToMat(invertedHoughBitmap); Mat blackWhiteMat = new Mat(); Mat edgesMat = new Mat(); Cv2.CvtColor(originalMat, blackWhiteMat, ColorConversionCodes.BGRA2GRAY); if (MapObjectsColors.GetInstance().Tight.Contains(objectName)) { Bitmap edgesMap = BitmapConverter.ToBitmap(blackWhiteMat); edgesMap = ImageFilter.SobelFilter(edgesMap, grayscale: true); edgesMat = BitmapConverter.ToMat(edgesMap); Cv2.CvtColor(edgesMat, edgesMat, ColorConversionCodes.BGRA2GRAY); } else { Cv2.Canny(blackWhiteMat, edgesMat, 50, 100); } OpenCvSharp.Point[][] contours; HierarchyIndex[] hierarchyIndexes; Cv2.FindContours( edgesMat, out contours, out hierarchyIndexes, mode: RetrievalModes.CComp, method: ContourApproximationModes.ApproxSimple); var componentCount = 0; var contourIndex = 0; var objectDict = mapObjects.getObjectDictionary(); if (contours.Length != 0) { if (objectDict.ContainsKey(objectName)) { objectDict[objectName] = contours; } else { objectDict.Add(objectName, contours); } while ((contourIndex >= 0)) { var contour = contours[contourIndex]; var boundingRect = Cv2.BoundingRect(contour); var boundingRectArea = boundingRect.Width * boundingRect.Height; var ca = Cv2.ContourArea(contour) * Convert.ToDouble(scaleBox.SelectedItem) / 100; var cal = Cv2.ArcLength(contour, closed: true) * Convert.ToDouble(scaleBox.SelectedItem) / 100; //if (boundingRectArea > minArea) //{ Cv2.PutText(originalMat, $"A:{ca.ToString("#.##")} km2", new OpenCvSharp.Point(boundingRect.X, boundingRect.Y + 10), HersheyFonts.HersheyPlain, 1, Scalar.White, 1); Cv2.PutText(originalMat, $"L:{cal.ToString("#.##")} km", new OpenCvSharp.Point(boundingRect.X, boundingRect.Y + 25), HersheyFonts.HersheyPlain, 1, Scalar.White, 1); //} //Cv2.DrawContours( // originalMat, // contours, // contourIndex, // color: Scalar.All(componentCount + 1), // thickness: -1, // lineType: LineTypes.Link8, // hierarchy: hierarchyIndexes, // maxLevel: int.MaxValue); componentCount++; contourIndex = hierarchyIndexes[contourIndex].Next; } } return(originalMat); }
private Dictionary <string, List <ImageObjectParameters> > FragmentObjectsSizeAnalyze(Dictionary <string, bool> presenceObjectsDict) { var imageBitmap = new Bitmap(ImageFile.GetInstance().Image); var objectParameters = new Dictionary <string, List <ImageObjectParameters> >(); if (!coordinatesAnalyze.IsEmpty) { var xstart = coordinatesAnalyze.X - RectSide / 2; var ystart = coordinatesAnalyze.Y - RectSide / 2; var xend = coordinatesAnalyze.X + RectSide / 2; var yend = coordinatesAnalyze.Y + RectSide / 2; var xmin = xstart > 0 ? xstart : 0; var ymin = ystart > 0 ? ystart : 0; var xmax = xend < Width ? xend : Width; var ymax = yend < Height ? yend : Height; var newWidth = xmax - xmin; var newHeight = ymax - ymin; var objectsDict = mapObjects.getObjectDictionary(); var colorsObject = MapObjectsColors.GetInstance(); var relief = colorsObject.Relief; var tight = colorsObject.Tight; var presenceObjects = presenceObjectsDict.Where(obj => obj.Value).Where(obj => !relief.Contains(obj.Key)).Select(obj => obj.Key).ToList(); foreach (var objectName in presenceObjects) { if (objectsDict.ContainsKey(objectName)) { objectParameters.Add(objectName, new List <ImageObjectParameters>()); var contours = objectsDict[objectName]; foreach (var contour in contours) { var matchContour = contour.Any(point => point.X > xstart && point.X <xend && point.Y> ystart && point.Y < yend); if (matchContour) { var cArea = Cv2.ContourArea(contour) * Convert.ToDouble(scaleBox.SelectedItem) / 100; var cLength = Cv2.ArcLength(contour, closed: true) * Convert.ToDouble(scaleBox.SelectedItem) / 100; objectParameters[objectName].Add(new ImageObjectParameters { ArcLength = cLength, Area = cArea }); } } } } } else { MessageBox.Show("Выберите участок карты!", "Информация", MessageBoxButtons.OK, MessageBoxIcon.Error); } using (StreamWriter writer = new StreamWriter($@"{ProjectDir}\matchPoints.txt")) { foreach (var elem in objectParameters) { writer.WriteLine(elem.Key); foreach (var item in elem.Value) { writer.WriteLine($"Area = {item.Area}, ArcLength = {item.ArcLength}"); } } } return(objectParameters); }