Files
UVtools/UVtools.Core/EmguCV/Contour.cs
T
Tiago Conceição ac51b7c4e0 v2.14.0
- **File Formats:**
   - (Add) SL1S: Prusa SL1S Speed
   - (Add) CTB v4 support (318570758)
   - (Improvement) PHOTON, CBDDLP, CTB v2, PHZ: Disallow per layer settings, beside the format support, printers never use it
   - (Improvement) Longer Orange format with new found keys
   - (Improvement) CXDLP: Fix the resolution from CL-60 and trigger an error when a invalid resolution was set and unable to detect the printer model
- **Prusa Slicer:**
   - (Add) UVtools Prusa SL1S SPEED
   - (Add) Longer Orange 120
   - (Fix) Creality HALOT-ONE CL-60: Flip resolution & display and remove mirror
   - (Fix) Creality HALOT-SKY CL-89: Remove mirror
   - (Improvement) Longer Orange printers with better default settings
- (Add) Button on `Help - Sponsor`: Open github sponsor webpage
2021-07-03 01:16:17 +01:00

241 lines
6.9 KiB
C#

/*
* 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
}
}