Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UVtools.Core/EmguCV/Contour.cs')
-rw-r--r--UVtools.Core/EmguCV/Contour.cs240
1 files changed, 240 insertions, 0 deletions
diff --git a/UVtools.Core/EmguCV/Contour.cs b/UVtools.Core/EmguCV/Contour.cs
new file mode 100644
index 0000000..20d216e
--- /dev/null
+++ b/UVtools.Core/EmguCV/Contour.cs
@@ -0,0 +1,240 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Drawing;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using Emgu.CV.Structure;
+using Emgu.CV.Util;
+using UVtools.Core.Extensions;
+
+namespace UVtools.Core.EmguCV
+{
+ /// <summary>
+ /// A contour cache for OpenCV
+ /// </summary>
+ public class Contour : IReadOnlyCollection<Point>, IDisposable
+ {
+ #region Members
+
+ private VectorOfPoint _points;
+ private Rectangle? _bounds;
+ private RotatedRect? _boundsBestFit;
+ private CircleF? _minEnclosingCircle;
+ private bool? _isConvex;
+ private double _area = double.NaN;
+ private double _perimeter = double.NaN;
+ private Moments _moments;
+ private Point? _centroid;
+
+ #endregion
+
+ #region Properties
+ public int XMin => Bounds.X;
+
+ public int YMin => Bounds.Y;
+
+ public int XMax => Bounds.Right;
+
+ public int YMax => Bounds.Bottom;
+
+ public Rectangle Bounds => _bounds ??= CvInvoke.BoundingRectangle(_points);
+
+ public RotatedRect BoundsBestFit => _boundsBestFit ??= CvInvoke.MinAreaRect(_points);
+
+ public CircleF MinEnclosingCircle => _minEnclosingCircle ??= CvInvoke.MinEnclosingCircle(_points);
+
+ public bool IsConvex => _isConvex ??= CvInvoke.IsContourConvex(_points);
+
+ /// <summary>
+ /// Gets the area of the contour
+ /// </summary>
+ public double Area
+ {
+ get
+ {
+ if (double.IsNaN(_area))
+ {
+ _area = CvInvoke.ContourArea(_points);
+ }
+
+ return _area;
+ }
+ }
+
+ /// <summary>
+ /// Gets the perimeter of the contours
+ /// </summary>
+ public double Perimeter
+ {
+ get
+ {
+ if (double.IsNaN(_perimeter))
+ {
+ _perimeter = CvInvoke.ArcLength(_points, true);
+ }
+ return _perimeter;
+ }
+ }
+
+ public Moments Moments => _moments ??= CvInvoke.Moments(_points);
+
+ /// <summary>
+ /// Gets the centroid of the contour
+ /// </summary>
+ public Point Centroid => _centroid ??= Moments.M00 == 0 ? Point.Empty :
+ new Point(
+ (int)Math.Round(_moments.M10 / _moments.M00),
+ (int)Math.Round(_moments.M01 / _moments.M00));
+
+ /// <summary>
+ /// Gets or sets the contour <see cref="Point"/>
+ /// </summary>
+ public VectorOfPoint Points
+ {
+ get => _points;
+ set
+ {
+ Dispose();
+ _points = value ?? throw new ArgumentNullException(nameof(Points));
+ }
+ }
+
+
+ /// <summary>
+ /// Gets if this contour have any point
+ /// </summary>
+ public bool IsEmpty => _points.Size == 0;
+ #endregion
+
+ #region Constructor
+ public Contour(VectorOfPoint points) : this(points.ToArray())
+ { }
+
+ public Contour(Point[] points)
+ {
+ Points = new VectorOfPoint(points);
+ }
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Checks if a given <see cref="Point"/> is inside the contour rectangle bounds
+ /// </summary>
+ /// <param name="point"></param>
+ /// <returns></returns>
+ public bool IsInsideBounds(Point point) => Bounds.Contains(point);
+
+ /// <summary>
+ /// Gets if a given <see cref="Point"/> is inside the contour
+ /// </summary>
+ /// <param name="point"></param>
+ /// <returns></returns>
+ public bool IsInside(Point point)
+ {
+ if (!IsInsideBounds(point)) return false;
+ return CvInvoke.PointPolygonTest(_points, point, false) >= 0;
+ }
+
+ public double MeasureDist(Point point)
+ {
+ if (!IsInsideBounds(point)) return -1;
+ return CvInvoke.PointPolygonTest(_points, point, true);
+ }
+
+ public IOutputArray ContourApproximation(double epsilon = 0.1)
+ {
+ var mat = new Mat();
+ CvInvoke.ApproxPolyDP(_points, mat, epsilon*Perimeter, true);
+ return mat;
+ }
+
+ /*
+ /// <summary>
+ /// Calculate the X/Y min/max boundary
+ /// </summary>
+ private void CalculateMinMax()
+ {
+ Bounds = Rectangle.Empty;
+
+ if (_contourPoints.Length == 0)
+ {
+ _xMin = -1;
+ _yMin = -1;
+ _xMax = -1;
+ _yMax = -1;
+ return;
+ }
+
+ _xMin = int.MaxValue;
+ _yMin = int.MaxValue;
+ _xMax = int.MinValue;
+ _yMax = int.MinValue;
+
+ for (int i = 0; i < _contourPoints.Length; i++)
+ {
+ _xMin = Math.Min(_xMin, _contourPoints[i].X);
+ _yMin = Math.Min(_yMin, _contourPoints[i].Y);
+
+ _xMax = Math.Max(_xMax, _contourPoints[i].X);
+ _yMax = Math.Max(_yMax, _contourPoints[i].Y);
+ }
+
+ Bounds = new Rectangle(_xMin, _yMin, _xMax - _xMin, _yMax - _yMin);
+ }
+ */
+
+ public void FitCircle(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
+ {
+ CvInvoke.Circle(src,
+ MinEnclosingCircle.Center.ToPoint(),
+ (int) Math.Round(MinEnclosingCircle.Radius),
+ color,
+ thickness,
+ lineType,
+ shift);
+ }
+
+ /*public void FitEllipse(Mat src, MCvScalar color, int thickness = 1, LineType lineType = LineType.EightConnected, int shift = 0)
+ {
+ var ellipse = CvInvoke.FitEllipse(_points);
+ CvInvoke.Ellipse(src, ellipse.Center.ToPoint(), ellipse.Size.ToSize(), ellipse.Angle, 0, 0);
+ }*/
+ #endregion
+
+ #region Implementations
+
+ public IEnumerator<Point> GetEnumerator()
+ {
+ return (IEnumerator<Point>) _points.ToArray().GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public int Count => _points.Size;
+
+ public Point this[int index] => _points[index];
+ public Point this[uint index] => _points[(int) index];
+ public Point this[long index] => _points[(int) index];
+ public Point this[ulong index] => _points[(int) index];
+
+ public void Dispose()
+ {
+ _points?.Dispose();
+ _moments?.Dispose();
+ }
+ #endregion
+ }
+}