diff --git a/UVtools.Core/EmguCV/EmguContour.cs b/UVtools.Core/EmguCV/EmguContour.cs index b4af097..1878a7a 100644 --- a/UVtools.Core/EmguCV/EmguContour.cs +++ b/UVtools.Core/EmguCV/EmguContour.cs @@ -22,7 +22,7 @@ namespace UVtools.Core.EmguCV; /// /// A contour cache for OpenCV /// -public class EmguContour : IReadOnlyCollection, IDisposable, IComparable, IComparer +public class EmguContour : IReadOnlyList, IComparable, IComparer, IDisposable { #region Constants @@ -35,7 +35,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< #region Members - private VectorOfPoint _points; + private VectorOfPoint _contour; private Rectangle? _bounds; private RotatedRect? _boundsBestFit; private CircleF? _minEnclosingCircle; @@ -56,13 +56,13 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< public int YMax => Bounds.Bottom; - public Rectangle Bounds => _bounds ??= CvInvoke.BoundingRectangle(_points); + public Rectangle Bounds => _bounds ??= CvInvoke.BoundingRectangle(_contour); - public RotatedRect BoundsBestFit => _boundsBestFit ??= CvInvoke.MinAreaRect(_points); + public RotatedRect BoundsBestFit => _boundsBestFit ??= CvInvoke.MinAreaRect(_contour); - public CircleF MinEnclosingCircle => _minEnclosingCircle ??= CvInvoke.MinEnclosingCircle(_points); + public CircleF MinEnclosingCircle => _minEnclosingCircle ??= CvInvoke.MinEnclosingCircle(_contour); - public bool IsConvex => _isConvex ??= CvInvoke.IsContourConvex(_points); + public bool IsConvex => _isConvex ??= CvInvoke.IsContourConvex(_contour); /// /// Gets the area of the contour @@ -73,7 +73,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< { if (double.IsNaN(_area)) { - _area = CvInvoke.ContourArea(_points); + _area = CvInvoke.ContourArea(_contour); } return _area; @@ -89,13 +89,13 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< { if (double.IsNaN(_perimeter)) { - _perimeter = CvInvoke.ArcLength(_points, true); + _perimeter = CvInvoke.ArcLength(_contour, true); } return _perimeter; } } - public Moments Moments => _moments ??= CvInvoke.Moments(_points); + public Moments Moments => _moments ??= CvInvoke.Moments(_contour); /// /// Gets the centroid of the contour @@ -108,13 +108,13 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< /// /// Gets or sets the contour points /// - public VectorOfPoint Points + public VectorOfPoint Contour { - get => _points; + get => _contour; set { Dispose(); - _points = value ?? throw new ArgumentNullException(nameof(Points)); + _contour = value ?? throw new ArgumentNullException(nameof(Contour)); } } @@ -122,16 +122,18 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< /// /// Gets if this contour have any point /// - public bool IsEmpty => _points.Size == 0; + public bool IsEmpty => _contour.Size == 0; #endregion #region Constructor - public EmguContour(VectorOfPoint points) : this(points.ToArray()) - { } - public EmguContour(Point[] points) + public EmguContour(VectorOfPoint points) + { + _contour = points; + } + + public EmguContour(Point[] points) : this(new VectorOfPoint(points)) { - Points = new VectorOfPoint(points); } #endregion @@ -152,19 +154,19 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< public bool IsInside(Point point) { if (!IsInsideBounds(point)) return false; - return CvInvoke.PointPolygonTest(_points, point, false) >= 0; + return CvInvoke.PointPolygonTest(_contour, point, false) >= 0; } public double MeasureDist(Point point) { if (!IsInsideBounds(point)) return -1; - return CvInvoke.PointPolygonTest(_points, point, true); + return CvInvoke.PointPolygonTest(_contour, point, true); } public IOutputArray ContourApproximation(double epsilon = 0.1) { var mat = new Mat(); - CvInvoke.ApproxPolyDP(_points, mat, epsilon*Perimeter, true); + CvInvoke.ApproxPolyDP(_contour, mat, epsilon*Perimeter, true); return mat; } @@ -237,14 +239,12 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< public EmguContour Clone() { - var clone = (EmguContour)MemberwiseClone(); - clone._points = new VectorOfPoint(_points.ToArray()); - return clone; + return new EmguContour(Contour.ToArray()); } public IEnumerator GetEnumerator() { - return (IEnumerator) _points.ToArray().GetEnumerator(); + return (IEnumerator) _contour.ToArray().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() @@ -252,16 +252,20 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< return GetEnumerator(); } - public int Count => _points.Size; + public int Count => _contour.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 Point this[sbyte index] => _contour[index]; + public Point this[byte index] => _contour[index]; + public Point this[short index] => _contour[index]; + public Point this[ushort index] => _contour[index]; + public Point this[int index] => _contour[index]; + public Point this[uint index] => _contour[(int) index]; + public Point this[long index] => _contour[(int) index]; + public Point this[ulong index] => _contour[(int) index]; public void Dispose() { - _points.Dispose(); + _contour.Dispose(); _moments?.Dispose(); _moments = null; } @@ -272,7 +276,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< protected bool Equals(EmguContour other) { if (Count != other.Count) return false; - return _points.ToArray().SequenceEqual(other.ToArray()); + return _contour.ToArray().SequenceEqual(other.ToArray()); } public override bool Equals(object? obj) @@ -285,7 +289,7 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< public override int GetHashCode() { - return _points.GetHashCode(); + return _contour.GetHashCode(); } public int CompareTo(EmguContour? other) diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index bc82109..0915351 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -25,49 +25,43 @@ namespace UVtools.Core.EmguCV; /// public class EmguContours : IReadOnlyList, IDisposable { + #region Members private readonly EmguContour[] _contours; - public IEnumerator GetEnumerator() - { - return ((IEnumerable)_contours).GetEnumerator(); - } + /// + /// Gets the contours inside + /// + public readonly VectorOfVectorOfPoint VectorOfContours; - IEnumerator IEnumerable.GetEnumerator() - { - return _contours.GetEnumerator(); - } + /// + /// Gets the contours hierarchy + /// + public readonly int[,] Hierarchy = new int[0, 0]; - public int Count => _contours.Length; + #endregion - public readonly int[,] Hierarchy = new int[0,0]; + #region Properties + /// + /// Gets if this collection have any contours + /// + public bool IsEmpty => Count == 0; + #endregion - - public EmguContour this[int index] => _contours[index]; - - private EmguContours(int count, int[,] hierarchy) - { - _contours = new EmguContour[count]; - Hierarchy = hierarchy; - } - - public EmguContours(Point[][] points) - { - _contours = new EmguContour[points.Length]; - for (int i = 0; i < points.Length; i++) - { - _contours[i] = new EmguContour(points[i]); - } - } + #region Constructor public EmguContours(VectorOfVectorOfPoint vectorOfPointsOfPoints) { - _contours = new EmguContour[vectorOfPointsOfPoints.Size]; - for (int i = 0; i < _contours.Length; i++) + VectorOfContours = vectorOfPointsOfPoints; + _contours = new EmguContour[VectorOfContours.Size]; + for (int i = 0; i < Count; i++) { - _contours[i] = new EmguContour(vectorOfPointsOfPoints[i]); + _contours[i] = new EmguContour(VectorOfContours[i]); } } + public EmguContours(Point[][] points) : this(new VectorOfVectorOfPoint(points)) + { } + public EmguContours(Point[][] points, int[,] hierarchy) : this(points) { Hierarchy = hierarchy; @@ -80,13 +74,41 @@ public class EmguContours : IReadOnlyList, IDisposable public EmguContours(IInputOutputArray mat, RetrType mode = RetrType.List, ChainApproxMethod method = ChainApproxMethod.ChainApproxSimple, Point offset = default) { - using var contours = mat.FindContours(out Hierarchy, mode, method, offset); - _contours = new EmguContour[contours.Size]; + VectorOfContours = mat.FindContours(out Hierarchy, mode, method, offset); + _contours = new EmguContour[VectorOfContours.Size]; for (int i = 0; i < _contours.Length; i++) { - _contours[i] = new EmguContour(contours[i]); + _contours[i] = new EmguContour(VectorOfContours[i]); } } + #endregion + + #region Indexers + + public EmguContour this[sbyte index] => _contours[index]; + public EmguContour this[byte index] => _contours[index]; + public EmguContour this[short index] => _contours[index]; + public EmguContour this[ushort index] => _contours[index]; + public EmguContour this[int index] => _contours[index]; + public EmguContour this[uint index] => _contours[index]; + public EmguContour this[ulong index] => _contours[index]; + public EmguContour this[long index] => _contours[index]; + + #endregion + + #region IReadOnlyList Implementation + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_contours).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _contours.GetEnumerator(); + } + + public int Count => _contours.Length; + #endregion public (int Index, EmguContour Contour, double Distance)[][] CalculateCentroidDistances(bool includeOwn = false, bool sortByDistance = true) { @@ -118,35 +140,19 @@ public class EmguContours : IReadOnlyList, IDisposable public EmguContours Clone() { - var clone = new EmguContours(_contours.Length, Hierarchy); - - for (int i = 0; i < _contours.Length; i++) - { - clone._contours[i] = this[i].Clone(); - } - - return clone; + return new EmguContours(VectorOfContours.ToArrayOfArray(), Hierarchy); } public void Dispose() { + VectorOfContours.Dispose(); foreach (var contour in _contours) { contour.Dispose(); } } - - - - - - - - - - - + #region Static methods /// /// Gets contours inside a point /// @@ -402,4 +408,6 @@ public class EmguContours : IReadOnlyList, IDisposable { return ContoursIntersectingPixels(contour1, contour2) > 0; } + + #endregion } \ No newline at end of file diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index ae65ea8..7bbd958 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -1041,7 +1041,7 @@ public class Layer : BindableBase, IEquatable, IEquatable if (_contours is null) { using var mat = LayerMat; - _contours = new EmguContours(mat); + _contours = new EmguContours(mat, RetrType.Tree); } return _contours; @@ -1053,7 +1053,7 @@ public class Layer : BindableBase, IEquatable, IEquatable /// public EmguContours GetContours(IInputOutputArray mat) { - return _contours ??= new EmguContours(mat); + return _contours ??= new EmguContours(mat, RetrType.Tree); } #endregion diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index c6f0ea8..9503a26 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -336,7 +336,7 @@ public class OperationRaftRelief : Operation break; case RaftReliefTypes.LinkedLines: { - using var contours = new EmguContours(supportsMatOriginal.FindContours()); + using var contours = new EmguContours(supportsMatOriginal); using var supportsRedraw = _linkedExternalSupports ? supportsMatOriginal.Clone() : null; using var supportsBrightnessCorrection = _highBrightness < byte.MaxValue ? supportsMat.Clone() : null; @@ -423,7 +423,7 @@ public class OperationRaftRelief : Operation break; case RaftReliefTypes.Tabs: { - using var contours = mat.FindContours(RetrType.External); + using var contours = new EmguContours(mat, RetrType.External); var span = mat.GetDataByteSpan(); var minX = 10; @@ -443,10 +443,8 @@ public class OperationRaftRelief : Operation }; var color = new MCvScalar(_highBrightness); - - for (var i = 0; i < contours.Size; i++) + foreach(var contour in contours) { - using var contour = new EmguContour(contours[i]); if(contour.Centroid.IsAnyNegative() || contour.Area < 10000) continue; for (var dir = 0; dir < directions.Length; dir++) @@ -473,7 +471,7 @@ public class OperationRaftRelief : Operation continue; } - if (CvInvoke.PointPolygonTest(contours[i], new PointF(x, y), false) == 0) // Must be on edge + if (CvInvoke.PointPolygonTest(contour.Contour, new PointF(x, y), false) == 0) // Must be on edge { foundPoint = true; break; diff --git a/documentation/UVtools.Core.xml b/documentation/UVtools.Core.xml index 3236aba..cb9550d 100644 --- a/documentation/UVtools.Core.xml +++ b/documentation/UVtools.Core.xml @@ -137,7 +137,7 @@ Gets the centroid of the contour - + Gets or sets the contour points @@ -167,6 +167,21 @@ Use only with Tree type + + + Gets the contours inside + + + + + Gets the contours hierarchy + + + + + Gets if this collection have any contours + + Gets contours inside a point