From ca6f623a9f0e994db30e7f37b84ab2c61d0cf3d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tiago=20Concei=C3=A7=C3=A3o?= Date: Sun, 2 Oct 2022 00:44:39 +0100 Subject: v3.6.7 - **Layer:** - (Add) Property: `LayerMatBoundingRectangle` - (Add) Property: `LayerMatModelBoundingRectangle` - (Add) Method: `GetLayerMat(roi)` - **Issues:** - **Islands:** - (Improvement) Islands detection performance - (Improvement) Required area to consider an island is now real area instead of bounding box area - (Fix) Logic bug when combining with overhangs - **Overhangs:** - (Improvement) Overhangs detection performance - (Improvement) Overhangs are now split and identified as separately in the layer - (Improvement) Overhangs now shows the correct issue area and able to locate the problem region more precisely - (Improvement) Compress overhangs into contours instead of using whole pixels, resulting in better render performance and less memory to hold the issue - (Fix) Bug in overhang logic causing to detect the problem twice when combined with supports - (Improvement) Touching bounds check logic to spare cycles - **Tool - Raise platform on print finish:** - (Add) Preset "Minimum": Sets to the minimum position - (Add) Preset "Medium": Sets to half-way between minimum and maximum position - (Add) Preset "Maximum": Sets to the maximum position - (Add) Wait time: Sets the ensured wait time to stay still on the desired position. This is useful if the printer firmware always move to top and you want to stay still on the set position for at least the desired time. Note: The print time calculation will take this wait into consideration and display a longer print time. - (Add) FileFormat: AnyCubic custom machine (.pwc) - (Downgrade) OpenCV from 4.5.5 to 4.5.4 due a possible crash while detecting islands (Windows) --- CHANGELOG.md | 28 +++ README.md | 1 + RELEASE_NOTES.md | 33 ++- UVtools.Core/CoreSettings.cs | 6 + UVtools.Core/EmguCV/EmguContour.cs | 4 +- UVtools.Core/EmguCV/EmguContours.cs | 24 +- UVtools.Core/EmguCV/MatRoi.cs | 49 ++++ UVtools.Core/Extensions/EmguExtensions.cs | 27 ++- UVtools.Core/Extensions/PointExtensions.cs | 4 + UVtools.Core/FileFormats/FileFormat.cs | 1 + UVtools.Core/FileFormats/PhotonWorkshopFile.cs | 196 ++++++++-------- UVtools.Core/Layers/Layer.cs | 50 +++- UVtools.Core/Layers/LayerIssueConfiguration.cs | 4 +- UVtools.Core/Layers/MainIssue.cs | 1 + UVtools.Core/Managers/IssueManager.cs | 251 +++++++++++---------- UVtools.Core/Objects/KernelConfiguration.cs | 4 +- UVtools.Core/Operations/OperationBlur.cs | 5 +- .../Operations/OperationCalibrateElephantFoot.cs | 3 +- .../Operations/OperationCalibrateExposureFinder.cs | 12 +- .../Operations/OperationCalibrateStressTower.cs | 3 +- .../Operations/OperationCalibrateTolerance.cs | 7 +- UVtools.Core/Operations/OperationDoubleExposure.cs | 13 +- .../Operations/OperationDynamicLayerHeight.cs | 3 +- UVtools.Core/Operations/OperationInfill.cs | 4 +- UVtools.Core/Operations/OperationLithophane.cs | 6 +- .../Operations/OperationPixelArithmetic.cs | 9 +- UVtools.Core/Operations/OperationPixelDimming.cs | 4 +- UVtools.Core/Operations/OperationRaftRelief.cs | 11 +- .../Operations/OperationRaiseOnPrintFinish.cs | 37 ++- UVtools.Core/Operations/OperationRepairLayers.cs | 11 +- UVtools.Core/UVtools.Core.csproj | 9 +- UVtools.Installer/Code/HeatGeneratedFileList.wxs | 82 +------ UVtools.Installer/Code/Product.wxs | 2 +- UVtools.ScriptSample/ScriptInsetSample.cs | 8 +- .../Tools/ToolRaiseOnPrintFinishControl.axaml | 100 +++++++- UVtools.WPF/UVtools.WPF.csproj | 7 +- documentation/UVtools.Core.xml | 61 ++++- 37 files changed, 669 insertions(+), 411 deletions(-) create mode 100644 UVtools.Core/EmguCV/MatRoi.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d6e702..0cb7c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## 01/10/2022 - v3.6.7 + +- **Layer:** + - (Add) Property: `LayerMatBoundingRectangle` + - (Add) Property: `LayerMatModelBoundingRectangle` + - (Add) Method: `GetLayerMat(roi)` +- **Issues:** + - **Islands:** + - (Improvement) Islands detection performance + - (Improvement) Required area to consider an island is now real area instead of bounding box area + - (Fix) Logic bug when combining with overhangs + - **Overhangs:** + - (Improvement) Overhangs detection performance + - (Improvement) Overhangs are now split and identified as separately in the layer + - (Improvement) Overhangs now shows the correct issue area and able to locate the problem region more precisely + - (Improvement) Compress overhangs into contours instead of using whole pixels, resulting in better render performance and less memory to hold the issue + - (Fix) Bug in overhang logic causing to detect the problem twice when combined with supports + - (Improvement) Touching bounds check logic to spare cycles +- **Tool - Raise platform on print finish:** + - (Add) Preset "Minimum": Sets to the minimum position + - (Add) Preset "Medium": Sets to half-way between minimum and maximum position + - (Add) Preset "Maximum": Sets to the maximum position + - (Add) Wait time: Sets the ensured wait time to stay still on the desired position. + This is useful if the printer firmware always move to top and you want to stay still on the set position for at least the desired time. + Note: The print time calculation will take this wait into consideration and display a longer print time. +- (Add) FileFormat: AnyCubic custom machine (.pwc) +- (Downgrade) OpenCV from 4.5.5 to 4.5.4 due a possible crash while detecting islands (Windows) + ## 20/09/2022 - v3.6.6 - **UI:** diff --git a/README.md b/README.md index 95551fa..9bbb5a3 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ But also, i need victims for test subject. Proceed at your own risk! - PWSQ (Photon Workshop) - PM3 (Photon Workshop) - PM3M (Photon Workshop) +- PWC (Photon Workshop) - JXS (GKone Slicer) - ZCode (UnizMaker) - ZCodex (Z-Suite) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 57d9827..14d79fa 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,9 +1,26 @@ -- **UI:** - - (Improvement) Auto show a horizontal scroll bar on wider than screen contents on the tool windows - - (Improvement) Move normal windows to zero position if negative and auto constrain height if necessary - - (Improvement) Set a minimum width and minimum height for tool windows - - (Fix) Setting - "Allow resize the tool windows" had been lost and no effect when checked -- (Add) Windows MSI - Option: Add installation directory to system PATH environment variable -- (Improvement) Better fetch of UVtools.Core.dll on PowerShell scripts under Windows -- (Improvement) Better handling in get the process output, this fixes processor name not being shown on macOS +- **Layer:** + - (Add) Property: `LayerMatBoundingRectangle` + - (Add) Property: `LayerMatModelBoundingRectangle` + - (Add) Method: `GetLayerMat(roi)` +- **Issues:** + - **Islands:** + - (Improvement) Islands detection performance + - (Improvement) Required area to consider an island is now real area instead of bounding box area + - (Fix) Logic bug when combining with overhangs + - **Overhangs:** + - (Improvement) Overhangs detection performance + - (Improvement) Overhangs are now split and identified as separately in the layer + - (Improvement) Overhangs now shows the correct issue area and able to locate the problem region more precisely + - (Improvement) Compress overhangs into contours instead of using whole pixels, resulting in better render performance and less memory to hold the issue + - (Fix) Bug in overhang logic causing to detect the problem twice when combined with supports + - (Improvement) Touching bounds check logic to spare cycles +- **Tool - Raise platform on print finish:** + - (Add) Preset "Minimum": Sets to the minimum position + - (Add) Preset "Medium": Sets to half-way between minimum and maximum position + - (Add) Preset "Maximum": Sets to the maximum position + - (Add) Wait time: Sets the ensured wait time to stay still on the desired position. + This is useful if the printer firmware always move to top and you want to stay still on the set position for at least the desired time. + Note: The print time calculation will take this wait into consideration and display a longer print time. +- (Add) FileFormat: AnyCubic custom machine (.pwc) +- (Downgrade) OpenCV from 4.5.5 to 4.5.4 due a possible crash while detecting islands (Windows) diff --git a/UVtools.Core/CoreSettings.cs b/UVtools.Core/CoreSettings.cs index 10246be..46e86f2 100644 --- a/UVtools.Core/CoreSettings.cs +++ b/UVtools.Core/CoreSettings.cs @@ -43,6 +43,12 @@ public static class CoreSettings /// public static ParallelOptions ParallelOptions => new() {MaxDegreeOfParallelism = _maxDegreeOfParallelism}; + /// + /// Gets the ParallelOptions with set to 1 for debug purposes + /// + public static ParallelOptions ParallelDebugOptions => new() { MaxDegreeOfParallelism = 1 }; + + /// /// Gets the ParallelOptions with and the set /// diff --git a/UVtools.Core/EmguCV/EmguContour.cs b/UVtools.Core/EmguCV/EmguContour.cs index f7d3bd1..5e5bd10 100644 --- a/UVtools.Core/EmguCV/EmguContour.cs +++ b/UVtools.Core/EmguCV/EmguContour.cs @@ -224,9 +224,9 @@ public class EmguContour : IReadOnlyCollection, IDisposable, IComparable< #region Static methods public static Point GetCentroid(VectorOfPoint points) { - if (points is null || points.Length == 0) return new Point(-1, -1); + if (points is null || points.Length == 0) return EmguExtensions.AnchorCenter; using var moments = CvInvoke.Moments(points); - return moments.M00 == 0 ? new Point(-1, -1) : + return moments.M00 == 0 ? EmguExtensions.AnchorCenter : new Point( (int)Math.Round(moments.M10 / moments.M00), (int)Math.Round(moments.M01 / moments.M00)); diff --git a/UVtools.Core/EmguCV/EmguContours.cs b/UVtools.Core/EmguCV/EmguContours.cs index d0ac9a5..164fe64 100644 --- a/UVtools.Core/EmguCV/EmguContours.cs +++ b/UVtools.Core/EmguCV/EmguContours.cs @@ -322,30 +322,40 @@ public class EmguContours : IReadOnlyList, IDisposable } /// - /// Checks if two contours intersects + /// Checks if two contours intersects and return the intersecting pixel count /// /// Contour 1 /// Contour 2 - /// - public static bool ContoursIntersect(VectorOfVectorOfPoint contour1, VectorOfVectorOfPoint contour2) + /// Intersecting pixel count + public static int ContoursIntersectingPixels(VectorOfVectorOfPoint contour1, VectorOfVectorOfPoint contour2) { var contour1Rect = CvInvoke.BoundingRectangle(contour1[0]); var contour2Rect = CvInvoke.BoundingRectangle(contour2[0]); /* early exit if the bounding rectangles don't intersect */ - if (!contour1Rect.IntersectsWith(contour2Rect)) return false; + if (!contour1Rect.IntersectsWith(contour2Rect)) return 0; var totalRect = Rectangle.Union(contour1Rect, contour2Rect); using var contour1Mat = EmguExtensions.InitMat(totalRect.Size); using var contour2Mat = EmguExtensions.InitMat(totalRect.Size); - + var inverseOffset = new Point(-totalRect.X, -totalRect.Y); CvInvoke.DrawContours(contour1Mat, contour1, -1, EmguExtensions.WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, inverseOffset); CvInvoke.DrawContours(contour2Mat, contour2, -1, EmguExtensions.WhiteColor, -1, LineType.EightConnected, null, int.MaxValue, inverseOffset); CvInvoke.BitwiseAnd(contour1Mat, contour2Mat, contour1Mat); - //return !contour1Mat.IsZeroed(); - return CvInvoke.CountNonZero(contour1Mat) > 0; + return CvInvoke.CountNonZero(contour1Mat); + } + + /// + /// Checks if two contours intersects + /// + /// Contour 1 + /// Contour 2 + /// + public static bool ContoursIntersect(VectorOfVectorOfPoint contour1, VectorOfVectorOfPoint contour2) + { + return ContoursIntersectingPixels(contour1, contour2) > 0; } } \ No newline at end of file diff --git a/UVtools.Core/EmguCV/MatRoi.cs b/UVtools.Core/EmguCV/MatRoi.cs new file mode 100644 index 0000000..6bd5b3f --- /dev/null +++ b/UVtools.Core/EmguCV/MatRoi.cs @@ -0,0 +1,49 @@ +/* + * GNU AFFERO GENERAL PUBLIC LICENSE + * Version 3, 19 November 2007 + * Copyright (C) 2007 Free Software Foundation, Inc. + * Everyone is permitted to copy and distribute verbatim copies + * of this license document, but changing it is not allowed. + */ + + +using System; +using System.Drawing; +using Emgu.CV; +using UVtools.Core.Extensions; + +namespace UVtools.Core.EmguCV; + +/// +/// A disposable Mat with associated ROI Mat +/// +public class MatRoi : IDisposable +{ + #region Properties + public Mat SourceMat { get; } + public Rectangle Roi { get; } + public Mat RoiMat { get; } + + public Point RoiLocation => Roi.Location; + public Size RoiSize => Roi.Size; + + #endregion + + #region Constructor + + public MatRoi(Mat sourceMat, Rectangle roi) + { + SourceMat = sourceMat; + Roi = roi; + RoiMat = sourceMat.Roi(roi); + } + + #endregion + + + public void Dispose() + { + SourceMat.Dispose(); + RoiMat.Dispose(); + } +} \ No newline at end of file diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs index b39f829..eb74c44 100644 --- a/UVtools.Core/Extensions/EmguExtensions.cs +++ b/UVtools.Core/Extensions/EmguExtensions.cs @@ -35,7 +35,8 @@ public static class EmguExtensions public static readonly MCvScalar BlackColor = new(0, 0, 0, 255); //public static readonly MCvScalar TransparentColor = new(); - public static readonly Mat Kernel3x3Rectangle = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1)); + public static readonly Point AnchorCenter = new (-1, -1); + public static readonly Mat Kernel3x3Rectangle = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), AnchorCenter); #endregion #region Initializers methods @@ -209,13 +210,19 @@ public static class EmguExtensions /// Gets the whole data span to manipulate or read pixels, use this when possibly using ROI /// /// - public static unsafe Span2D GetDataByteSpan2D(this Mat mat) + public static unsafe Span2D GetDataSpan2D(this Mat mat) { - if (!mat.IsSubmatrix) return new(mat.GetBytePointer(), mat.Height, mat.Step, 0); var step = mat.GetRealStep(); - return new(mat.GetBytePointer(), mat.Height, step, mat.Step - step); + if (!mat.IsSubmatrix) return new(mat.DataPointer.ToPointer(), mat.Height, step, 0); + return new(mat.DataPointer.ToPointer(), mat.Height, step, mat.Step / mat.ElementSize - step); } - + + /// + /// Gets the whole data span to manipulate or read pixels, use this when possibly using ROI + /// + /// + public static Span2D GetDataByteSpan2D(this Mat mat) => mat.GetDataSpan2D(); + /// /// Gets the data span to manipulate or read pixels given a length and offset @@ -1381,8 +1388,11 @@ public static class EmguExtensions { var contours = new VectorOfVectorOfPoint(); using var hierarchyMat = new Mat(); + CvInvoke.FindContours(mat, contours, hierarchyMat, mode, method, offset); + hierarchy = new int[hierarchyMat.Cols, 4]; + if (contours.Size == 0) return contours; var gcHandle = GCHandle.Alloc(hierarchy, GCHandleType.Pinned); using (var mat2 = new Mat(hierarchyMat.Rows, hierarchyMat.Cols, hierarchyMat.Depth, 4, gcHandle.AddrOfPinnedObject(), hierarchyMat.Step)) hierarchyMat.CopyTo(mat2); @@ -1403,7 +1413,6 @@ public static class EmguExtensions public static Mat Skeletonize(this Mat src, out int iterations, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle) { if (ksize.IsEmpty) ksize = new Size(3, 3); - Point anchor = new(-1, -1); var skeleton = src.NewBlank(); var kernel = Kernel3x3Rectangle; @@ -1416,8 +1425,8 @@ public static class EmguExtensions // erode and dilate the image using the structuring element using var eroded = new Mat(); - CvInvoke.Erode(image, eroded, kernel, anchor, 1, BorderType.Reflect101, default); - CvInvoke.Dilate(eroded, temp, kernel, anchor, 1, BorderType.Reflect101, default); + CvInvoke.Erode(image, eroded, kernel, AnchorCenter, 1, BorderType.Reflect101, default); + CvInvoke.Dilate(eroded, temp, kernel, AnchorCenter, 1, BorderType.Reflect101, default); // subtract the temporary image from the original, eroded // image, then take the bitwise 'or' between the skeleton @@ -1458,7 +1467,7 @@ public static class EmguExtensions { var size = Math.Max(iterations, 1) * 2 + 1; iterations = 1; - return CvInvoke.GetStructuringElement(elementShape, new Size(size, size), new Point(-1, -1)); + return CvInvoke.GetStructuringElement(elementShape, new Size(size, size), AnchorCenter); } #endregion diff --git a/UVtools.Core/Extensions/PointExtensions.cs b/UVtools.Core/Extensions/PointExtensions.cs index 01c6c54..9d2dfb9 100644 --- a/UVtools.Core/Extensions/PointExtensions.cs +++ b/UVtools.Core/Extensions/PointExtensions.cs @@ -62,6 +62,10 @@ public static class PointExtensions } } + public static Point Invert(this Point point) => new(-point.X, -point.Y); + + public static PointF Invert(this PointF point) => new(-point.X, -point.Y); + public static Point OffsetBy(this Point point, int value)=> new(point.X + value, point.Y + value); public static Point OffsetBy(this Point point, int x, int y) => new(point.X + x, point.Y + y); public static Point OffsetBy(this Point point, Point other) => new(point.X + other.X, point.Y + other.Y); diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 35ce5c8..f60faf6 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -2423,6 +2423,7 @@ public abstract class FileFormat : BindableBase, IDisposable, IEquatable HaveLayerParameterModifier(PrintParameterModifier.RetractHeight2); public bool CanUseLayerRetractSpeed2 => HaveLayerParameterModifier(PrintParameterModifier.RetractSpeed2); public bool CanUseLayerLightOffDelay => HaveLayerParameterModifier(PrintParameterModifier.LightOffDelay); + public bool CanUseLayerAnyWaitTimeBeforeCure => CanUseLayerWaitTimeBeforeCure || CanUseLayerLightOffDelay; public bool CanUseLayerLightPWM => HaveLayerParameterModifier(PrintParameterModifier.LightPWM); public string ExposureRepresentation diff --git a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs index 688795d..815d361 100644 --- a/UVtools.Core/FileFormats/PhotonWorkshopFile.cs +++ b/UVtools.Core/FileFormats/PhotonWorkshopFile.cs @@ -82,20 +82,20 @@ public class PhotonWorkshopFile : FileFormat public enum AnyCubicMachine : byte { - AnyCubicPhotonS, - AnyCubicPhotonZero, - AnyCubicPhotonX, - AnyCubicPhotonUltra, - AnyCubicPhotonD2, - AnyCubicPhotonMono, - AnyCubicPhotonMonoSE, - AnyCubicPhotonMono4K, - AnyCubicPhotonMonoX, - AnyCubicPhotonMonoX6KM3Plus, - AnyCubicPhotonMonoSQ, - AnyCubicPhotonM3, - AnyCubicPhotonM3Max, - AnyCubicCustom, + PhotonS, + PhotonZero, + PhotonX, + PhotonUltra, + PhotonD2, + PhotonMono, + PhotonMonoSE, + PhotonMono4K, + PhotonMonoX, + PhotonMonoX6KM3Plus, + PhotonMonoSQ, + PhotonM3, + PhotonM3Max, + Custom, } #endregion @@ -1109,6 +1109,7 @@ public class PhotonWorkshopFile : FileFormat new(typeof(PhotonWorkshopFile), "pmsq", "Photon Mono SQ (PMSQ)"), new(typeof(PhotonWorkshopFile), "pm3", "Photon M3 (PM3)"), new(typeof(PhotonWorkshopFile), "pm3m", "Photon M3 Max (PM3M)"), + new(typeof(PhotonWorkshopFile), "pwc", "Anycubic Custom Machine (PWC)"), //new(typeof(PhotonWorkshopFile), "pwmb", "Photon M3 Plus (PWMB)"), }; @@ -1217,19 +1218,19 @@ public class PhotonWorkshopFile : FileFormat if (MachineSettings.DisplayWidth > 0) return MachineSettings.DisplayWidth; return PrinterModel switch { - AnyCubicMachine.AnyCubicPhotonS => 68.04f, - AnyCubicMachine.AnyCubicPhotonZero => 55.44f, - AnyCubicMachine.AnyCubicPhotonX => 192, - AnyCubicMachine.AnyCubicPhotonUltra => 102.40f, - AnyCubicMachine.AnyCubicPhotonD2 => 130.56f, - AnyCubicMachine.AnyCubicPhotonMono => 82.62f, - AnyCubicMachine.AnyCubicPhotonMonoSE => 82.62f, - AnyCubicMachine.AnyCubicPhotonMono4K => 134.40f, - AnyCubicMachine.AnyCubicPhotonMonoX => 192, - AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus => 198.15f, - AnyCubicMachine.AnyCubicPhotonMonoSQ => 120, - AnyCubicMachine.AnyCubicPhotonM3 => 163.84f, - AnyCubicMachine.AnyCubicPhotonM3Max => 298.08f, + AnyCubicMachine.PhotonS => 68.04f, + AnyCubicMachine.PhotonZero => 55.44f, + AnyCubicMachine.PhotonX => 192, + AnyCubicMachine.PhotonUltra => 102.40f, + AnyCubicMachine.PhotonD2 => 130.56f, + AnyCubicMachine.PhotonMono => 82.62f, + AnyCubicMachine.PhotonMonoSE => 82.62f, + AnyCubicMachine.PhotonMono4K => 134.40f, + AnyCubicMachine.PhotonMonoX => 192, + AnyCubicMachine.PhotonMonoX6KM3Plus => 198.15f, + AnyCubicMachine.PhotonMonoSQ => 120, + AnyCubicMachine.PhotonM3 => 163.84f, + AnyCubicMachine.PhotonM3Max => 298.08f, _ => 0 }; } @@ -1246,19 +1247,19 @@ public class PhotonWorkshopFile : FileFormat if (MachineSettings.DisplayHeight > 0) return MachineSettings.DisplayHeight; return PrinterModel switch { - AnyCubicMachine.AnyCubicPhotonS => 120.96f, - AnyCubicMachine.AnyCubicPhotonZero => 98.637f, - AnyCubicMachine.AnyCubicPhotonX => 120, - AnyCubicMachine.AnyCubicPhotonUltra => 57.60f, - AnyCubicMachine.AnyCubicPhotonD2 => 73.44f, - AnyCubicMachine.AnyCubicPhotonMono => 130.56f, - AnyCubicMachine.AnyCubicPhotonMonoSE => 130.56f, - AnyCubicMachine.AnyCubicPhotonMono4K => 84, - AnyCubicMachine.AnyCubicPhotonMonoX => 120, - AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus => 123.84f, - AnyCubicMachine.AnyCubicPhotonMonoSQ => 128, - AnyCubicMachine.AnyCubicPhotonM3 => 102.40f, - AnyCubicMachine.AnyCubicPhotonM3Max => 165.60f, + AnyCubicMachine.PhotonS => 120.96f, + AnyCubicMachine.PhotonZero => 98.637f, + AnyCubicMachine.PhotonX => 120, + AnyCubicMachine.PhotonUltra => 57.60f, + AnyCubicMachine.PhotonD2 => 73.44f, + AnyCubicMachine.PhotonMono => 130.56f, + AnyCubicMachine.PhotonMonoSE => 130.56f, + AnyCubicMachine.PhotonMono4K => 84, + AnyCubicMachine.PhotonMonoX => 120, + AnyCubicMachine.PhotonMonoX6KM3Plus => 123.84f, + AnyCubicMachine.PhotonMonoSQ => 128, + AnyCubicMachine.PhotonM3 => 102.40f, + AnyCubicMachine.PhotonM3Max => 165.60f, _ => 0 }; } @@ -1276,19 +1277,19 @@ public class PhotonWorkshopFile : FileFormat if (MachineSettings.MachineZ > 0) return MachineSettings.MachineZ; return PrinterModel switch { - AnyCubicMachine.AnyCubicPhotonS => 165, - AnyCubicMachine.AnyCubicPhotonZero => 150, - AnyCubicMachine.AnyCubicPhotonX => 245, - AnyCubicMachine.AnyCubicPhotonUltra => 165, - AnyCubicMachine.AnyCubicPhotonD2 => 165, - AnyCubicMachine.AnyCubicPhotonMono => 165, - AnyCubicMachine.AnyCubicPhotonMonoSE => 160, - AnyCubicMachine.AnyCubicPhotonMono4K => 165, - AnyCubicMachine.AnyCubicPhotonMonoX => 245, - AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus => 245, - AnyCubicMachine.AnyCubicPhotonMonoSQ => 200, - AnyCubicMachine.AnyCubicPhotonM3 => 180f, - AnyCubicMachine.AnyCubicPhotonM3Max => 300f, + AnyCubicMachine.PhotonS => 165, + AnyCubicMachine.PhotonZero => 150, + AnyCubicMachine.PhotonX => 245, + AnyCubicMachine.PhotonUltra => 165, + AnyCubicMachine.PhotonD2 => 165, + AnyCubicMachine.PhotonMono => 165, + AnyCubicMachine.PhotonMonoSE => 160, + AnyCubicMachine.PhotonMono4K => 165, + AnyCubicMachine.PhotonMonoX => 245, + AnyCubicMachine.PhotonMonoX6KM3Plus => 245, + AnyCubicMachine.PhotonMonoSQ => 200, + AnyCubicMachine.PhotonM3 => 180f, + AnyCubicMachine.PhotonM3Max => 300f, _ => 0 }; } @@ -1573,20 +1574,20 @@ public class PhotonWorkshopFile : FileFormat if (string.IsNullOrWhiteSpace(MachineSettings.MachineName)) return MachineSettings.MachineName; return PrinterModel switch { - AnyCubicMachine.AnyCubicPhotonS => "Photon S", - AnyCubicMachine.AnyCubicPhotonZero => "Photon Zero", - AnyCubicMachine.AnyCubicPhotonX => "Photon X", - AnyCubicMachine.AnyCubicPhotonUltra => "Photon Ultra", - AnyCubicMachine.AnyCubicPhotonD2 => "Photon D2", - AnyCubicMachine.AnyCubicPhotonMono => "Photon Mono", - AnyCubicMachine.AnyCubicPhotonMonoSE => "Photon Mono SE", - AnyCubicMachine.AnyCubicPhotonMono4K => "Photon Mono 4K", - AnyCubicMachine.AnyCubicPhotonMonoX => "Photon Mono X", - AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus => "Photon Mono X 6K / M3 Plus", - AnyCubicMachine.AnyCubicPhotonMonoSQ => "Photon Mono SQ", - AnyCubicMachine.AnyCubicPhotonM3 => "Photon M3", - AnyCubicMachine.AnyCubicPhotonM3Max => "Photon M3 Max", - AnyCubicMachine.AnyCubicCustom => "Custom", + AnyCubicMachine.PhotonS => "Photon S", + AnyCubicMachine.PhotonZero => "Photon Zero", + AnyCubicMachine.PhotonX => "Photon X", + AnyCubicMachine.PhotonUltra => "Photon Ultra", + AnyCubicMachine.PhotonD2 => "Photon D2", + AnyCubicMachine.PhotonMono => "Photon Mono", + AnyCubicMachine.PhotonMonoSE => "Photon Mono SE", + AnyCubicMachine.PhotonMono4K => "Photon Mono 4K", + AnyCubicMachine.PhotonMonoX => "Photon Mono X", + AnyCubicMachine.PhotonMonoX6KM3Plus => "Photon Mono X 6K / M3 Plus", + AnyCubicMachine.PhotonMonoSQ => "Photon Mono SQ", + AnyCubicMachine.PhotonM3 => "Photon M3", + AnyCubicMachine.PhotonM3Max => "Photon M3 Max", + AnyCubicMachine.Custom => "Custom", _ => base.MachineName }; } @@ -1606,70 +1607,75 @@ public class PhotonWorkshopFile : FileFormat { if (FileEndsWith(".pws")) { - return AnyCubicMachine.AnyCubicPhotonS; + return AnyCubicMachine.PhotonS; } if (FileEndsWith(".pw0")) { - return AnyCubicMachine.AnyCubicPhotonZero; + return AnyCubicMachine.PhotonZero; } if (FileEndsWith(".pwx")) { - return AnyCubicMachine.AnyCubicPhotonX; + return AnyCubicMachine.PhotonX; } if (FileEndsWith(".dlp")) { - return AnyCubicMachine.AnyCubicPhotonUltra; + return AnyCubicMachine.PhotonUltra; } if (FileEndsWith(".dl2p")) { - return AnyCubicMachine.AnyCubicPhotonD2; + return AnyCubicMachine.PhotonD2; } if (FileEndsWith(".pwmo")) { - return AnyCubicMachine.AnyCubicPhotonMono; + return AnyCubicMachine.PhotonMono; } if (FileEndsWith(".pwms")) { - return AnyCubicMachine.AnyCubicPhotonMonoSE; + return AnyCubicMachine.PhotonMonoSE; } if (FileEndsWith(".pwma")) { - return AnyCubicMachine.AnyCubicPhotonMono4K; + return AnyCubicMachine.PhotonMono4K; } if (FileEndsWith(".pwmx")) { - return AnyCubicMachine.AnyCubicPhotonMonoX; + return AnyCubicMachine.PhotonMonoX; } if (FileEndsWith(".pwmb")) { - return AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus; + return AnyCubicMachine.PhotonMonoX6KM3Plus; } if (FileEndsWith(".pmsq")) { - return AnyCubicMachine.AnyCubicPhotonMonoSQ; + return AnyCubicMachine.PhotonMonoSQ; } if (FileEndsWith(".pm3")) { - return AnyCubicMachine.AnyCubicPhotonM3; + return AnyCubicMachine.PhotonM3; } if (FileEndsWith(".pm3m")) { - return AnyCubicMachine.AnyCubicPhotonM3Max; + return AnyCubicMachine.PhotonM3Max; } - return AnyCubicMachine.AnyCubicPhotonS; + if (FileEndsWith(".pwc")) + { + return AnyCubicMachine.Custom; + } + + return AnyCubicMachine.PhotonS; } } #endregion @@ -2067,20 +2073,20 @@ public class PhotonWorkshopFile : FileFormat HeaderSettings.PerLayerOverride = System.Convert.ToUInt32(AllLayersAreUsingGlobalParameters); MachineSettings.MaxFileVersion = PrinterModel switch { - AnyCubicMachine.AnyCubicPhotonS => VERSION_1, - AnyCubicMachine.AnyCubicPhotonZero => VERSION_1, - AnyCubicMachine.AnyCubicPhotonX => VERSION_1, - AnyCubicMachine.AnyCubicPhotonUltra => VERSION_515, - AnyCubicMachine.AnyCubicPhotonD2 => VERSION_516, - AnyCubicMachine.AnyCubicPhotonMono => VERSION_515, - AnyCubicMachine.AnyCubicPhotonMonoSE => VERSION_515, - AnyCubicMachine.AnyCubicPhotonMono4K => VERSION_516, - AnyCubicMachine.AnyCubicPhotonMonoX => VERSION_516, - AnyCubicMachine.AnyCubicPhotonMonoX6KM3Plus => VERSION_517, - AnyCubicMachine.AnyCubicPhotonMonoSQ => VERSION_515, - AnyCubicMachine.AnyCubicPhotonM3 => VERSION_516, - AnyCubicMachine.AnyCubicPhotonM3Max => VERSION_516, - AnyCubicMachine.AnyCubicCustom => VERSION_516, + AnyCubicMachine.PhotonS => VERSION_1, + AnyCubicMachine.PhotonZero => VERSION_1, + AnyCubicMachine.PhotonX => VERSION_1, + AnyCubicMachine.PhotonUltra => VERSION_515, + AnyCubicMachine.PhotonD2 => VERSION_516, + AnyCubicMachine.PhotonMono => VERSION_515, + AnyCubicMachine.PhotonMonoSE => VERSION_515, + AnyCubicMachine.PhotonMono4K => VERSION_516, + AnyCubicMachine.PhotonMonoX => VERSION_516, + AnyCubicMachine.PhotonMonoX6KM3Plus => VERSION_517, + AnyCubicMachine.PhotonMonoSQ => VERSION_515, + AnyCubicMachine.PhotonM3 => VERSION_516, + AnyCubicMachine.PhotonM3Max => VERSION_516, + AnyCubicMachine.Custom => VERSION_517, _ => VERSION_517 }; } diff --git a/UVtools.Core/Layers/Layer.cs b/UVtools.Core/Layers/Layer.cs index 2f2ea02..5b7ac72 100644 --- a/UVtools.Core/Layers/Layer.cs +++ b/UVtools.Core/Layers/Layer.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Xml.Serialization; using K4os.Compression.LZ4; +using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; @@ -814,7 +815,22 @@ public class Layer : BindableBase, IEquatable, IEquatable } } - //public Mat LayerMatBoundingRectangle => new(LayerMat, BoundingRectangle); + /// + /// Gets the layer mat with roi of it bounding rectangle + /// + public MatRoi LayerMatBoundingRectangle => new(LayerMat, BoundingRectangle); + + /// + /// Gets the layer mat with roi of model bounding rectangle + /// + public MatRoi LayerMatModelBoundingRectangle => new(LayerMat, SlicerFile.BoundingRectangle); + + /// + /// Gets the layer mat with a specified roi + /// + /// Region of interest + /// + public MatRoi GetLayerMat(Rectangle roi) => new(LayerMat, roi); /// /// Gets a new Brg image instance @@ -1215,8 +1231,19 @@ public class Layer : BindableBase, IEquatable, IEquatable needDispose = true; } - NonZeroPixelCount = (uint)CvInvoke.CountNonZero(mat); - BoundingRectangle = _nonZeroPixelCount > 0 ? CvInvoke.BoundingRectangle(mat) : Rectangle.Empty; + + //NonZeroPixelCount = (uint)CvInvoke.CountNonZero(mat); + //BoundingRectangle = _nonZeroPixelCount > 0 ? CvInvoke.BoundingRectangle(mat) : Rectangle.Empty; + BoundingRectangle = CvInvoke.BoundingRectangle(mat); + if (_boundingRectangle.IsEmpty) + { + NonZeroPixelCount = 0; + } + else + { + using var roiMat = mat.Roi(_boundingRectangle); + NonZeroPixelCount = (uint)CvInvoke.CountNonZero(roiMat); + } if (needDispose) mat!.Dispose(); @@ -1590,6 +1617,23 @@ public class Layer : BindableBase, IEquatable, IEquatable #region Static Methods + /// + /// Gets the bounding rectangle that is the union of a collection of layers + /// + /// Layer collection + /// + public static Rectangle GetBoundingRectangleUnion(params Layer[] layers) + { + var rect = Rectangle.Empty; + foreach (var layer in layers) + { + if(layer.BoundingRectangle.IsEmpty) continue; + rect = rect.IsEmpty ? layer.BoundingRectangle : Rectangle.Union(rect, layer.BoundingRectangle); + } + + return rect; + } + public static float RoundHeight(float height) => (float) Math.Round(height, HeightPrecision); public static double RoundHeight(double height) => Math.Round(height, HeightPrecision); public static decimal RoundHeight(decimal height) => Math.Round(height, HeightPrecision); diff --git a/UVtools.Core/Layers/LayerIssueConfiguration.cs b/UVtools.Core/Layers/LayerIssueConfiguration.cs index 630b533..27caebd 100644 --- a/UVtools.Core/Layers/LayerIssueConfiguration.cs +++ b/UVtools.Core/Layers/LayerIssueConfiguration.cs @@ -53,7 +53,7 @@ public sealed class IslandDetectionConfiguration /// /// Combines the island and overhang detections for a better more realistic detection and to discard false-positives. (Slower) /// If enabled, and when a island is found, it will check for overhangs on that same island, if no overhang found then the island will be discarded and considered safe, otherwise it will flag as an island issue. - /// Note: Overhangs settings will be used to configure the detection.Enabling Overhangs is not required for this procedure to work. + /// Note: Overhangs settings will be used to configure the detection. Enabling Overhangs is not required for this procedure to work. /// public bool EnhancedDetection { get; set; } = true; @@ -72,7 +72,7 @@ public sealed class IslandDetectionConfiguration public byte BinaryThreshold { get; set; } = 1; /// - /// Gets the required area size (x*y) to consider process a island (0-65535) + /// Gets the required pixel area to consider process a island (0-65535) /// public ushort RequiredAreaToProcessCheck { get; set; } = 1; diff --git a/UVtools.Core/Layers/MainIssue.cs b/UVtools.Core/Layers/MainIssue.cs index be4435d..bc25d22 100644 --- a/UVtools.Core/Layers/MainIssue.cs +++ b/UVtools.Core/Layers/MainIssue.cs @@ -113,6 +113,7 @@ public class MainIssue : IReadOnlyList Childs = new[] { issue }; issue.Parent = this; PixelCount = issue.PixelsCount; + if (issue is IssueOfPoints) Area = issue.PixelsCount; } public MainIssue(IssueType type, IEnumerable issues) : this(type) diff --git a/UVtools.Core/Managers/IssueManager.cs b/UVtools.Core/Managers/IssueManager.cs index 14156c7..6ef1473 100644 --- a/UVtools.Core/Managers/IssueManager.cs +++ b/UVtools.Core/Managers/IssueManager.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.Drawing; using System.Linq; using System.Threading.Tasks; +using CommunityToolkit.HighPerformance; using UVtools.Core.EmguCV; using UVtools.Core.Extensions; using UVtools.Core.FileFormats; @@ -216,36 +217,34 @@ public sealed class IssueManager : RangeObservableCollection return; } - using (var image = layer.LayerMat) + using (var image = layer.GetLayerMat(layerIndex == 0 ? SlicerFile.BoundingRectangle : Layer.GetBoundingRectangleUnion(SlicerFile[layerIndex - 1], layer))) { - var step = image.GetRealStep(); - var span = image.GetDataByteSpan(); + var sourceSpan = image.SourceMat.GetDataByteSpan2D(); + var roiSpan = image.RoiMat.GetDataByteSpan2D(); if (touchBoundConfig.Enabled) { // TouchingBounds Checker List pixels = new(); bool touchTop = layer.BoundingRectangle.Top <= touchBoundConfig.MarginTop; - bool touchBottom = layer.BoundingRectangle.Bottom >= image.Height - touchBoundConfig.MarginBottom; + bool touchBottom = layer.BoundingRectangle.Bottom >= image.SourceMat.Height - touchBoundConfig.MarginBottom; bool touchLeft = layer.BoundingRectangle.Left <= touchBoundConfig.MarginLeft; - bool touchRight = layer.BoundingRectangle.Right >= - image.Width - touchBoundConfig.MarginRight; + bool touchRight = layer.BoundingRectangle.Right >= image.SourceMat.Width - touchBoundConfig.MarginRight; int minx = int.MaxValue; int miny = int.MaxValue; int maxx = 0; int maxy = 0; - + if (touchTop || touchBottom) { - for (int x = 0; x < image.Width; x++) // Check Top and Bottom bounds + for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++) // Check Top and Bottom bounds { if (touchTop) { - for (int y = 0; y < touchBoundConfig.MarginTop; y++) // Top + for (int y = layer.BoundingRectangle.Y; y < touchBoundConfig.MarginTop; y++) // Top { - if (span[image.GetPixelPos(x, y)] >= - touchBoundConfig.MinimumPixelBrightness) + if (sourceSpan.DangerousGetReferenceAt(y, x) >= touchBoundConfig.MinimumPixelBrightness) { pixels.Add(new Point(x, y)); minx = Math.Min(minx, x); @@ -258,12 +257,11 @@ public sealed class IssueManager : RangeObservableCollection if (touchBottom) { - for (int y = image.Height - touchBoundConfig.MarginBottom; - y < image.Height; + for (int y = image.SourceMat.Height - touchBoundConfig.MarginBottom; + y < layer.BoundingRectangle.Bottom; y++) // Bottom { - if (span[image.GetPixelPos(x, y)] >= - touchBoundConfig.MinimumPixelBrightness) + if (sourceSpan.DangerousGetReferenceAt(y, x) >= touchBoundConfig.MinimumPixelBrightness) { pixels.Add(new Point(x, y)); minx = Math.Min(minx, x); @@ -273,22 +271,20 @@ public sealed class IssueManager : RangeObservableCollection } } } - } } if (touchLeft || touchRight) { - for (int y = touchBoundConfig.MarginTop; - y < image.Height - touchBoundConfig.MarginBottom; + for (int y = layer.BoundingRectangle.Y + touchBoundConfig.MarginTop; + y < layer.BoundingRectangle.Bottom - touchBoundConfig.MarginBottom; y++) // Check Left and Right bounds { if (touchLeft) { - for (int x = 0; x < touchBoundConfig.MarginLeft; x++) // Left + for (int x = layer.BoundingRectangle.X; x < touchBoundConfig.MarginLeft; x++) // Left { - if (span[image.GetPixelPos(x, y)] >= - touchBoundConfig.MinimumPixelBrightness) + if (sourceSpan.DangerousGetReferenceAt(y, x) >= touchBoundConfig.MinimumPixelBrightness) { pixels.Add(new Point(x, y)); minx = Math.Min(minx, x); @@ -301,12 +297,11 @@ public sealed class IssueManager : RangeObservableCollection if (touchRight) { - for (int x = image.Width - touchBoundConfig.MarginRight; - x < image.Width; + for (int x = layer.BoundingRectangle.Right - touchBoundConfig.MarginRight; + x < layer.BoundingRectangle.Right; x++) // Right { - if (span[image.GetPixelPos(x, y)] >= - touchBoundConfig.MinimumPixelBrightness) + if (sourceSpan.DangerousGetReferenceAt(y, x) >= touchBoundConfig.MinimumPixelBrightness) { pixels.Add(new Point(x, y)); minx = Math.Min(minx, x); @@ -328,8 +323,59 @@ public sealed class IssueManager : RangeObservableCollection if (layerIndex > 0 && layer.PositionZ > firstLayer!.PositionZ) // No islands nor overhangs for layer 0 or on plate { - Mat? previousImage = null; - Span previousSpan = null; + MatRoi? previousImage = null; + Span2D previousSpan = null; + Mat? overhangImage = null; + var previousLayer = SlicerFile[layerIndex - 1]; + + + // Overhangs + var overhangCount = 0; + //if (!islandConfig.Enabled && overhangConfig.Enabled || + // (islandConfig.Enabled && overhangConfig.Enabled && overhangConfig.IndependentFromIslands)) + if (overhangConfig.Enabled) + { + bool canProcessCheck = true; + if (overhangConfig.WhiteListLayers is not null) // Check white list + { + if (!overhangConfig.WhiteListLayers.Contains(layerIndex)) + { + canProcessCheck = false; + } + } + + if (canProcessCheck) + { + previousImage ??= previousLayer.GetLayerMat(Layer.GetBoundingRectangleUnion(previousLayer, layer)); + + overhangImage = new Mat(); + using var vecPoints = new VectorOfPoint(); + + CvInvoke.Subtract(image.RoiMat, previousImage.RoiMat, overhangImage); + CvInvoke.Threshold(overhangImage, overhangImage, 127, 255, ThresholdType.Binary); + + CvInvoke.Erode(overhangImage, overhangImage, EmguExtensions.Kernel3x3Rectangle, + EmguExtensions.AnchorCenter, overhangConfig.ErodeIterations, BorderType.Default, default); + + //CvInvoke.MorphologyEx(subtractedImage, subtractedImage, MorphOp.Open, EmguExtensions.Kernel3x3Rectangle, + // EmguExtensions.AnchorCenter, 2, BorderType.Reflect101, default); + + using var contours = overhangImage.FindContours(out var hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxSimple, image.RoiLocation); + var contoursInGroups = EmguContours.GetPositiveContoursInGroups(contours, hierarchy); + + foreach (var contourGroup in contoursInGroups) + { + if (contourGroup[0].Size < 3) continue; // Single contour, single line, ignore + overhangCount++; + var area = EmguContours.GetContourArea(contourGroup); + if (area >= overhangConfig.RequiredPixelsToConsider) + { + var rect = CvInvoke.BoundingRectangle(contourGroup[0]); + AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfContours(layer, contourGroup.ToArrayOfArray(), rect, area))); + } + } + } + } if (islandConfig.Enabled) { @@ -350,12 +396,11 @@ public sealed class IssueManager : RangeObservableCollection { needDispose = true; islandImage = new(); - CvInvoke.Threshold(image, islandImage, islandConfig.BinaryThreshold, byte.MaxValue, - ThresholdType.Binary); + CvInvoke.Threshold(image.RoiMat, islandImage, islandConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary); } else { - islandImage = image; + islandImage = image.RoiMat; } using Mat labels = new(); @@ -373,48 +418,53 @@ public sealed class IssueManager : RangeObservableCollection } // Get array that contains details of each connected component - var ccStats = stats.GetData(); + //var ccStats = stats.GetData(); //stats[i][0]: Left Edge of Connected Component //stats[i][1]: Top Edge of Connected Component //stats[i][2]: Width of Connected Component //stats[i][3]: Height of Connected Component //stats[i][4]: Total Area (in pixels) in Connected Component - - var labelSpan = labels.GetDataSpan(); + var ccStats = stats.GetDataSpan(); + var labelSpan = labels.GetDataSpan2D(); for (int i = 1; i < numLabels; i++) { + int pos = i * stats.Cols; Rectangle rect = new( - (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Left)!, + ccStats[pos + (int)ConnectedComponentsTypes.Left], + ccStats[pos + (int)ConnectedComponentsTypes.Top], + ccStats[pos + (int)ConnectedComponentsTypes.Width], + ccStats[pos + (int)ConnectedComponentsTypes.Height] + /*(int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Left)!, (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Top)!, (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Width)!, - (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Height)!); + (int)ccStats.GetValue(i, (int)ConnectedComponentsTypes.Height)!*/ + ); - if (rect.Area() < islandConfig.RequiredAreaToProcessCheck) + if (ccStats[pos + (int)ConnectedComponentsTypes.Area] < islandConfig.RequiredAreaToProcessCheck) continue; - if (previousImage is null) + previousImage ??= previousLayer.GetLayerMat(Layer.GetBoundingRectangleUnion(previousLayer, layer)); + + if (previousSpan == null) { - previousImage = SlicerFile[layerIndex - 1].LayerMat; - previousSpan = previousImage.GetDataByteSpan(); + previousSpan = previousImage.RoiMat.GetDataByteSpan2D(); } List points = new(); uint pixelsSupportingIsland = 0; - + for (int y = rect.Y; y < rect.Bottom; y++) for (int x = rect.X; x < rect.Right; x++) { - int pixel = step * y + x; - if ( - labelSpan[pixel] != - i || // Background pixel or a pixel from another component within the bounding rectangle - span[pixel] < islandConfig.RequiredPixelBrightnessToProcessCheck // Low brightness, ignore + if (labelSpan.DangerousGetReferenceAt(y, x) != i || // Background pixel or a pixel from another component within the bounding rectangle + roiSpan.DangerousGetReferenceAt(y, x) < islandConfig.RequiredPixelBrightnessToProcessCheck // Low brightness, ignore ) continue; - points.Add(new Point(x, y)); + points.Add(new Point(image.Roi.X + x, image.Roi.Y + y)); - if (previousSpan[pixel] >= islandConfig.RequiredPixelBrightnessToSupport) + //int pixel = roiStep * y + x; + if (previousSpan.DangerousGetReferenceAt(y, x) >= islandConfig.RequiredPixelBrightnessToSupport) { pixelsSupportingIsland++; } @@ -434,101 +484,58 @@ public sealed class IssueManager : RangeObservableCollection IssueOfPoints? island = null; if (pixelsSupportingIsland < requiredSupportingPixels) { - island = new IssueOfPoints(layer, points.ToArray(), rect); + island = new IssueOfPoints(layer, points.ToArray(), new Rectangle(rect.Location.OffsetBy(image.RoiLocation), rect.Size)); } - // Check for overhangs - if (overhangConfig.Enabled && !overhangConfig.IndependentFromIslands && - island is null - || island is not null && islandConfig.EnhancedDetection && - pixelsSupportingIsland >= 10 - ) + // Check for overhangs in islands + if (island is not null && islandConfig.EnhancedDetection && pixelsSupportingIsland >= 10 && + (!overhangConfig.Enabled || (overhangConfig.Enabled && overhangCount > 0))) { - points.Clear(); - using var imageRoi = image.Roi(rect); - using var previousImageRoi = previousImage.Roi(rect); - using var subtractedImage = new Mat(); - var anchor = new Point(-1, -1); - CvInvoke.Subtract(imageRoi, previousImageRoi, subtractedImage); - CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255, - ThresholdType.Binary); - - CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle, - anchor, overhangConfig.ErodeIterations, BorderType.Default, default); - - var subtractedSpan = subtractedImage.GetDataByteSpan(); + using var islandRoi = image.RoiMat.Roi(rect); + using var previousIslandRoi = previousImage.RoiMat.Roi(rect); + + if (overhangImage is null) + { + overhangImage = new Mat(); + CvInvoke.Subtract(islandRoi, previousIslandRoi, overhangImage); + CvInvoke.Threshold(overhangImage, overhangImage, 127, 255, ThresholdType.Binary); + + CvInvoke.Erode(overhangImage, overhangImage, EmguExtensions.Kernel3x3Rectangle, + EmguExtensions.AnchorCenter, overhangConfig.ErodeIterations, BorderType.Default, default); + } + + using var subtractedImage = overhangImage.Roi(rect); + + var subtractedSpan = subtractedImage.GetDataByteSpan2D(); var subtractedStep = subtractedImage.GetRealStep(); - for (int y = 0; y < subtractedImage.Height; y++) - for (int x = 0; x < subtractedStep; x++) + int overhangPixels = 0; + + for (int y = 0; y < subtractedImage.Height && overhangPixels < overhangConfig.RequiredPixelsToConsider; y++) + for (int x = 0; x < subtractedStep && overhangPixels < overhangConfig.RequiredPixelsToConsider; x++) { int labelX = rect.X + x; int labelY = rect.Y + y; - int pixel = subtractedImage.GetPixelPos(x, y); - int pixelLabel = labelY * step + labelX; - if (labelSpan[pixelLabel] != i || subtractedSpan[pixel] == 0) + if (labelSpan[labelY, labelX] != i || subtractedSpan.DangerousGetReferenceAt(y, x) == 0) continue; - points.Add(new Point(labelX, labelY)); + overhangPixels++; } - if (points.Count >= overhangConfig.RequiredPixelsToConsider - ) // Overhang - { - AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, points, rect))); - } - else if (islandConfig.EnhancedDetection) // No overhang + if (overhangPixels < overhangConfig.RequiredPixelsToConsider) // No overhang = no island { island = null; } - } - if (island is not null) - AddIssue(new MainIssue(MainIssue.IssueType.Island, island)); - } - } - } - - // Overhangs - if (!islandConfig.Enabled && overhangConfig.Enabled || - (islandConfig.Enabled && overhangConfig.Enabled && - overhangConfig.IndependentFromIslands) ) - { - bool canProcessCheck = true; - if (overhangConfig.WhiteListLayers is not null) // Check white list - { - if (!overhangConfig.WhiteListLayers.Contains(layerIndex)) - { - canProcessCheck = false; - } - } - - if (canProcessCheck) - { - previousImage ??= SlicerFile[layerIndex - 1].LayerMat; - - using var subtractedImage = new Mat(); - using var vecPoints = new VectorOfPoint(); - var anchor = new Point(-1, -1); - - - CvInvoke.Subtract(image, previousImage, subtractedImage); - CvInvoke.Threshold(subtractedImage, subtractedImage, 127, 255, - ThresholdType.Binary); - - CvInvoke.Erode(subtractedImage, subtractedImage, EmguExtensions.Kernel3x3Rectangle, - anchor, overhangConfig.ErodeIterations, BorderType.Default, default); + } - CvInvoke.FindNonZero(subtractedImage, vecPoints); - if (vecPoints.Size >= overhangConfig.RequiredPixelsToConsider) - { - AddIssue(new MainIssue(MainIssue.IssueType.Overhang, new IssueOfPoints(layer, vecPoints.ToArray(), layer.BoundingRectangle) - )); + if (island is not null) AddIssue(new MainIssue(MainIssue.IssueType.Island, island)); } } } previousImage?.Dispose(); + overhangImage?.Dispose(); } if (resinTrapConfig.Enabled) @@ -541,12 +548,12 @@ public sealed class IssueManager : RangeObservableCollection if (resinTrapConfig.BinaryThreshold > 0) { resinTrapImage = new Mat(); - CvInvoke.Threshold(image, resinTrapImage, resinTrapConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary); + CvInvoke.Threshold(image.SourceMat, resinTrapImage, resinTrapConfig.BinaryThreshold, byte.MaxValue, ThresholdType.Binary); } else { needDispose = true; - resinTrapImage = image; + resinTrapImage = image.SourceMat; } var contourLayer = resinTrapImage.Roi(SlicerFile.BoundingRectangle); diff --git a/UVtools.Core/Objects/KernelConfiguration.cs b/UVtools.Core/Objects/KernelConfiguration.cs index 779450d..dac6fce 100644 --- a/UVtools.Core/Objects/KernelConfiguration.cs +++ b/UVtools.Core/Objects/KernelConfiguration.cs @@ -217,8 +217,8 @@ public sealed class KernelConfiguration : BindableBase, IDisposable KernelMat = CvInvoke.GetStructuringElement(shape, size, anchor); } - public void SetKernel(ElementShape shape, Size size) => SetKernel(shape, size, new Point(-1, -1)); - public void SetKernel(ElementShape shape) => SetKernel(shape, new Size(3, 3), new Point(-1, -1)); + public void SetKernel(ElementShape shape, Size size) => SetKernel(shape, size, EmguExtensions.AnchorCenter); + public void SetKernel(ElementShape shape) => SetKernel(shape, new Size(3, 3), EmguExtensions.AnchorCenter); public Mat? GetKernel() { diff --git a/UVtools.Core/Operations/OperationBlur.cs b/UVtools.Core/Operations/OperationBlur.cs index b7ae234..b9fc536 100644 --- a/UVtools.Core/Operations/OperationBlur.cs +++ b/UVtools.Core/Operations/OperationBlur.cs @@ -12,6 +12,7 @@ using System.ComponentModel; using System.Drawing; using System.Text; using System.Threading.Tasks; +using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Objects; @@ -147,9 +148,9 @@ public sealed class OperationBlur : Operation { Size size = new((int)Size, (int)Size); Point anchor = Kernel.Anchor; - if (anchor.IsEmpty) anchor = new Point(-1, -1); + if (anchor.IsEmpty) anchor = EmguExtensions.AnchorCenter; //if (size.IsEmpty) size = new Size(3, 3); - //if (anchor.IsEmpty) anchor = new Point(-1, -1); + //if (anchor.IsEmpty) anchor = EmguExtensions.AnchorCenter; var target = GetRoiOrDefault(mat); using var original = mat.Clone(); switch (BlurOperation) diff --git a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs index 58c73d6..9ffc5e1 100644 --- a/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs +++ b/UVtools.Core/Operations/OperationCalibrateElephantFoot.cs @@ -436,7 +436,6 @@ public sealed class OperationCalibrateElephantFoot : Operation public Mat[] GetLayers() { var layers = new Mat[3]; - var anchor = new Point(-1, -1); layers[0] = EmguExtensions.InitMat(SlicerFile.Resolution); layers[2] = layers[0].Clone(); @@ -655,7 +654,7 @@ public sealed class OperationCalibrateElephantFoot : Operation mask.SetTo(new MCvScalar(byte.MaxValue-brightness)); int tempIterations = DimmingWallThickness; var kernel = DimmingKernel.GetKernel(ref tempIterations); - CvInvoke.Erode(shape, erode, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(shape, erode, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); //CvInvoke.Subtract(shape, erode, diff); //CvInvoke.BitwiseAnd(diff, mask, target); //CvInvoke.Add(erode, target, target); diff --git a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs index efd6b15..45ba400 100644 --- a/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs +++ b/UVtools.Core/Operations/OperationCalibrateExposureFinder.cs @@ -1766,8 +1766,7 @@ public sealed class OperationCalibrateExposureFinder : Operation CvInvoke.BitwiseAnd(layers[0], mat, layers[1], matMask); - Point anchor = new Point(-1, -1); - //CvInvoke.MorphologyEx(layers[1], layers[1], MorphOp.Open, CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3,3), anchor), anchor, 1, BorderType.Reflect101, default); + //CvInvoke.MorphologyEx(layers[1], layers[1], MorphOp.Open, CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3,3), EmguExtensions.AnchorCenter), EmguExtensions.AnchorCenter, 1, BorderType.Reflect101, default); mat.Dispose(); matMask.Dispose(); @@ -1845,7 +1844,6 @@ public sealed class OperationCalibrateExposureFinder : Operation int partMarginXPx = (int)(_partMargin * Xppmm); int partMarginYPx = (int)(_partMargin * Yppmm); - var anchor = new Point(-1, -1); var kernel = EmguExtensions.Kernel3x3Rectangle; if (_patternModel) @@ -1921,14 +1919,14 @@ public sealed class OperationCalibrateExposureFinder : Operation if (layerCountOnHeight < _chamferLayers) { - CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default); + CvInvoke.Erode(newMatRoi, newMatRoi, kernel, EmguExtensions.AnchorCenter, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default); } if (layer.IsBottomLayer) { if (_erodeBottomIterations > 0) { - CvInvoke.Erode(newMatRoi, newMatRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default); + CvInvoke.Erode(newMatRoi, newMatRoi, kernel, EmguExtensions.AnchorCenter, _erodeBottomIterations, BorderType.Reflect101, default); } if(_patternModelTextEnabled) @@ -2114,12 +2112,12 @@ public sealed class OperationCalibrateExposureFinder : Operation if (isBottomLayer && _erodeBottomIterations > 0) { - CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default); + CvInvoke.Erode(matRoi, matRoi, kernel, EmguExtensions.AnchorCenter, _erodeBottomIterations, BorderType.Reflect101, default); } if (layerCountOnHeight < _chamferLayers) { - CvInvoke.Erode(matRoi, matRoi, kernel, anchor, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default); + CvInvoke.Erode(matRoi, matRoi, kernel, EmguExtensions.AnchorCenter, _chamferLayers - layerCountOnHeight, BorderType.Reflect101, default); } if (_multipleBrightness && brightness < 255) diff --git a/UVtools.Core/Operations/OperationCalibrateStressTower.cs b/UVtools.Core/Operations/OperationCalibrateStressTower.cs index 4ba78d6..ab14dec 100644 --- a/UVtools.Core/Operations/OperationCalibrateStressTower.cs +++ b/UVtools.Core/Operations/OperationCalibrateStressTower.cs @@ -331,8 +331,7 @@ public sealed class OperationCalibrateStressTower : Operation const byte fontThickness = 2; LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected; - var anchor = new Point(-1, -1); - var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor);*/ + var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), EmguExtensions.AnchorCenter);*/ Parallel.For(0, LayerCount, CoreSettings.ParallelOptions, layerIndex => { layers[layerIndex] = EmguExtensions.InitMat(SlicerFile.Resolution); diff --git a/UVtools.Core/Operations/OperationCalibrateTolerance.cs b/UVtools.Core/Operations/OperationCalibrateTolerance.cs index dd5d7ba..7179fa9 100644 --- a/UVtools.Core/Operations/OperationCalibrateTolerance.cs +++ b/UVtools.Core/Operations/OperationCalibrateTolerance.cs @@ -510,7 +510,6 @@ public sealed class OperationCalibrateTolerance : Operation const byte fontThickness = 2; LineType lineType = _enableAntiAliasing ? LineType.AntiAlias : LineType.EightConnected; - var anchor = new Point(-1, -1); var kernel = EmguExtensions.Kernel3x3Rectangle; var pointTextList = new List>(); @@ -647,7 +646,7 @@ public sealed class OperationCalibrateTolerance : Operation { Parallel.For(0, _bottomLayers, CoreSettings.ParallelOptions, layerIndex => { - CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, _erodeBottomIterations, BorderType.Reflect101, default); + CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, EmguExtensions.AnchorCenter, _erodeBottomIterations, BorderType.Reflect101, default); }); } @@ -656,10 +655,10 @@ public sealed class OperationCalibrateTolerance : Operation Parallel.For(0, _chamferLayers, CoreSettings.ParallelOptions, layerIndexOffset => { var iteration = _chamferLayers - layerIndexOffset; - CvInvoke.Erode(layers[layerIndexOffset], layers[layerIndexOffset], kernel, anchor, iteration, BorderType.Reflect101, default); + CvInvoke.Erode(layers[layerIndexOffset], layers[layerIndexOffset], kernel, EmguExtensions.AnchorCenter, iteration, BorderType.Reflect101, default); var layerIndex = layers.Length - 1 - layerIndexOffset; - CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, anchor, iteration, BorderType.Reflect101, default); + CvInvoke.Erode(layers[layerIndex], layers[layerIndex], kernel, EmguExtensions.AnchorCenter, iteration, BorderType.Reflect101, default); }); /*byte iterations = _chamferLayers; var layerIndex = 0; diff --git a/UVtools.Core/Operations/OperationDoubleExposure.cs b/UVtools.Core/Operations/OperationDoubleExposure.cs index bdefe8c..0123d68 100644 --- a/UVtools.Core/Operations/OperationDoubleExposure.cs +++ b/UVtools.Core/Operations/OperationDoubleExposure.cs @@ -12,6 +12,7 @@ using System; using System.Drawing; using System.Text; using System.Threading.Tasks; +using UVtools.Core.Extensions; using UVtools.Core.FileFormats; using UVtools.Core.Layers; using UVtools.Core.Objects; @@ -273,8 +274,6 @@ public class OperationDoubleExposure : Operation protected override bool ExecuteInternally(OperationProgress progress) { - var anchor = new Point(-1, -1); - var layers = new Layer[SlicerFile.LayerCount+LayerRangeCount]; // Untouched @@ -308,7 +307,7 @@ public class OperationDoubleExposure : Operation { int tempIterations = firstErodeIterations; var kernel = Kernel.GetKernel(ref tempIterations); - CvInvoke.Erode(mat, mat, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(mat, mat, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); firstLayer.LayerMat = mat; firstLayer.CopyImageTo(secondLayer); @@ -317,7 +316,7 @@ public class OperationDoubleExposure : Operation tempIterations = _secondLayerDifferenceOverlapErodeIterations; kernel = Kernel.GetKernel(ref tempIterations); using var matErode = new Mat(); - CvInvoke.Erode(mat, matErode, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(mat, matErode, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); //CvInvoke.Threshold(matErode, matErode, 127, 255, ThresholdType.Binary); CvInvoke.Subtract(mat, matErode, mat); secondLayer.LayerMat = mat; @@ -336,7 +335,7 @@ public class OperationDoubleExposure : Operation int tempIterations = firstErodeIterations; var kernel = Kernel.GetKernel(ref tempIterations); firstMat = new Mat(); - CvInvoke.Erode(mat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(mat, firstMat, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); firstLayer.LayerMat = firstMat; } @@ -345,7 +344,7 @@ public class OperationDoubleExposure : Operation int tempIterations = secondErodeIterations; var kernel = Kernel.GetKernel(ref tempIterations); secondMat = new Mat(); - CvInvoke.Erode(mat, secondMat, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(mat, secondMat, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); } if(firstMat is not null && _secondLayerDifference) @@ -357,7 +356,7 @@ public class OperationDoubleExposure : Operation { int tempIterations = _secondLayerDifferenceOverlapErodeIterations; var kernel = Kernel.GetKernel(ref tempIterations); - CvInvoke.Erode(firstMat, firstMat, kernel, anchor, tempIterations, BorderType.Reflect101, default); + CvInvoke.Erode(firstMat, firstMat, kernel, EmguExtensions.AnchorCenter, tempIterations, BorderType.Reflect101, default); //CvInvoke.Threshold(firstMat, firstMat, 127, 255, ThresholdType.Binary); } diff --git a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs index 938f1f5..2d7e0ff 100644 --- a/UVtools.Core/Operations/OperationDynamicLayerHeight.cs +++ b/UVtools.Core/Operations/OperationDynamicLayerHeight.cs @@ -566,7 +566,6 @@ public sealed class OperationDynamicLayerHeight : Operation OldPrintTime = SlicerFile.PrintTime }; - var anchor = new Point(-1, -1); var kernel = EmguExtensions.Kernel3x3Rectangle; var matCache = new MatCacheManager(this, (ushort)CacheObjectCount, ObjectsPerCache) @@ -729,7 +728,7 @@ public sealed class OperationDynamicLayerHeight : Operation break; }*/ //Debug.WriteLine($"{layerIndex} - {erodeCount}"); - CvInvoke.Erode(matXorSum, matXor, kernel, anchor, 1, BorderType.Reflect101, default); + CvInvoke.Erode(matXorSum, matXor, kernel, EmguExtensions.AnchorCenter, 1, BorderType.Reflect101, default); //CvInvoke.Imshow("Render", erodeMatXor.Roi(SlicerFile.BoundingRectangle)); //CvInvoke.WaitKey(); if (CvInvoke.CountNonZero(matXor) == 0) diff --git a/UVtools.Core/Operations/OperationInfill.cs b/UVtools.Core/Operations/OperationInfill.cs index 9085ad3..9d0c4df 100644 --- a/UVtools.Core/Operations/OperationInfill.cs +++ b/UVtools.Core/Operations/OperationInfill.cs @@ -182,7 +182,7 @@ public sealed class OperationInfill : Operation public override bool Execute(Mat mat, params object[]? arguments) { if (arguments is null || arguments.Length < 1) return false; - var anchor = new Point(-1, -1); + var kernel = EmguExtensions.Kernel3x3Rectangle; uint index = Convert.ToUInt32(arguments[0]); uint layerIndex = index - LayerIndexStart; @@ -465,7 +465,7 @@ public sealed class OperationInfill : Operation //patternMask.Save("D:\\pattern.png"); - CvInvoke.Erode(target, erode, kernel, anchor, WallThickness, BorderType.Reflect101, + CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, WallThickness, BorderType.Reflect101, default); CvInvoke.Subtract(target, erode, diff); diff --git a/UVtools.Core/Operations/OperationLithophane.cs b/UVtools.Core/Operations/OperationLithophane.cs index 86b36b7..9b076d0 100644 --- a/UVtools.Core/Operations/OperationLithophane.cs +++ b/UVtools.Core/Operations/OperationLithophane.cs @@ -371,8 +371,8 @@ public class OperationLithophane : Operation CvInvoke.GaussianBlur(mat, mat, new Size(ksize, ksize), 0); } - if (_removeNoiseIterations > 0) CvInvoke.MorphologyEx(mat, mat, MorphOp.Open, EmguExtensions.Kernel3x3Rectangle, new Point(-1, -1), _removeNoiseIterations, BorderType.Reflect101, default); - if (_gapClosingIterations > 0) CvInvoke.MorphologyEx(mat, mat, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, new Point(-1, -1), _gapClosingIterations, BorderType.Reflect101, default); + if (_removeNoiseIterations > 0) CvInvoke.MorphologyEx(mat, mat, MorphOp.Open, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, _removeNoiseIterations, BorderType.Reflect101, default); + if (_gapClosingIterations > 0) CvInvoke.MorphologyEx(mat, mat, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, _gapClosingIterations, BorderType.Reflect101, default); if (_rotate != RotateDirection.None) CvInvoke.Rotate(mat, mat, (RotateFlags) _rotate); if (_mirror) @@ -496,7 +496,7 @@ public class OperationLithophane : Operation { using var dilatedMat = new Mat(); CvInvoke.Threshold(mat, dilatedMat, 1, byte.MaxValue, ThresholdType.Binary); - CvInvoke.Dilate(dilatedMat, dilatedMat, EmguExtensions.Kernel3x3Rectangle, new Point(-1, -1), _baseMargin, BorderType.Reflect101, default); + CvInvoke.Dilate(dilatedMat, dilatedMat, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, _baseMargin, BorderType.Reflect101, default); dilatedMat.CopyToCenter(baseMat); break; } diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs index 1613018..8690208 100644 --- a/UVtools.Core/Operations/OperationPixelArithmetic.cs +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -535,7 +535,6 @@ public class OperationPixelArithmetic : Operation Mat? patternAlternateMat = null; Mat patternMatMask = null!; Mat patternAlternateMatMask = null!; - var anchor = new Point(-1, -1); if (_usePattern && IsUsePatternVisible) { @@ -639,7 +638,7 @@ public class OperationPixelArithmetic : Operation using var erode = new Mat(); int iterations = 1; var kernel = Kernel.GetKernel(ref iterations); - CvInvoke.Erode(target, erode, kernel, anchor, iterations, BorderType.Reflect101, default); + CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, iterations, BorderType.Reflect101, default); CvInvoke.Subtract(target, erode, erode); CvInvoke.Add(applyMask, erode, applyMask); @@ -649,7 +648,7 @@ public class OperationPixelArithmetic : Operation { iterations = wallThickness - 1; kernel = Kernel.GetKernel(ref iterations); - CvInvoke.Dilate(applyMask, erode, kernel, anchor, iterations, BorderType.Reflect101, default); + CvInvoke.Dilate(applyMask, erode, kernel, EmguExtensions.AnchorCenter, iterations, BorderType.Reflect101, default); erode.CopyTo(applyMask, target); } } @@ -666,7 +665,7 @@ public class OperationPixelArithmetic : Operation applyMask = new Mat(); int iterations = wallThickness; var kernel = Kernel.GetKernel(ref iterations); - CvInvoke.Erode(target, applyMask, kernel, anchor, iterations, BorderType.Reflect101, default); + CvInvoke.Erode(target, applyMask, kernel, EmguExtensions.AnchorCenter, iterations, BorderType.Reflect101, default); break; } case PixelArithmeticApplyMethod.ModelWalls: @@ -681,7 +680,7 @@ public class OperationPixelArithmetic : Operation applyMask = target.Clone(); int iterations = wallThickness; var kernel = Kernel.GetKernel(ref iterations); - CvInvoke.Erode(target, erode, kernel, anchor, iterations, BorderType.Reflect101, default); + CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, iterations, BorderType.Reflect101, default); applyMask.SetTo(EmguExtensions.BlackColor, erode); break; } diff --git a/UVtools.Core/Operations/OperationPixelDimming.cs b/UVtools.Core/Operations/OperationPixelDimming.cs index 334f597..87465f8 100644 --- a/UVtools.Core/Operations/OperationPixelDimming.cs +++ b/UVtools.Core/Operations/OperationPixelDimming.cs @@ -665,7 +665,7 @@ public class OperationPixelDimming : Operation public override bool Execute(Mat mat, params object[]? arguments) { if (arguments is null || arguments.Length < 2) return false; - var anchor = new Point(-1, -1); + var kernel = EmguExtensions.Kernel3x3Rectangle; uint layerIndex = Convert.ToUInt32(arguments[0]); @@ -690,7 +690,7 @@ public class OperationPixelDimming : Operation using var mask = GetMask(mat); - CvInvoke.Erode(target, erode, kernel, anchor, wallThickness, BorderType.Reflect101, default); + CvInvoke.Erode(target, erode, kernel, EmguExtensions.AnchorCenter, wallThickness, BorderType.Reflect101, default); if (_lighteningPixels) { diff --git a/UVtools.Core/Operations/OperationRaftRelief.cs b/UVtools.Core/Operations/OperationRaftRelief.cs index 41d5e1a..5e9cb2f 100644 --- a/UVtools.Core/Operations/OperationRaftRelief.cs +++ b/UVtools.Core/Operations/OperationRaftRelief.cs @@ -273,7 +273,7 @@ public class OperationRaftRelief : Operation const uint maxLayerCount = 1000; Mat? supportsMat = null; - var anchor = new Point(-1, -1); + var kernel = EmguExtensions.Kernel3x3Rectangle; uint firstSupportLayerIndex = _maskLayerIndex; @@ -305,9 +305,8 @@ public class OperationRaftRelief : Operation if (_dilateIterations > 0) { - CvInvoke.Dilate(supportsMat, supportsMat, - CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), new Point(-1, -1)), - new Point(-1, -1), _dilateIterations, BorderType.Reflect101, new MCvScalar()); + CvInvoke.Dilate(supportsMat, supportsMat, EmguExtensions.Kernel3x3Rectangle, + EmguExtensions.AnchorCenter, _dilateIterations, BorderType.Reflect101, new MCvScalar()); } var color = new MCvScalar(255 - _lowBrightness); @@ -386,7 +385,7 @@ public class OperationRaftRelief : Operation } // Close minor holes, round imperfections, stronger joints - CvInvoke.MorphologyEx(supportsMat, supportsMat, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, new Point(-1, -1), 1, BorderType.Reflect101, default); + CvInvoke.MorphologyEx(supportsMat, supportsMat, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, 1, BorderType.Reflect101, default); break; } @@ -412,7 +411,7 @@ public class OperationRaftRelief : Operation CvInvoke.Erode(mask, mask, kernel, anchor, operation.WallMargin, BorderType.Reflect101, new MCvScalar()); CvInvoke.Subtract(target, patternMat, target, mask);*/ - CvInvoke.Erode(target, mask, kernel, anchor, WallMargin, BorderType.Reflect101, default); + CvInvoke.Erode(target, mask, kernel, EmguExtensions.AnchorCenter, WallMargin, BorderType.Reflect101, default); CvInvoke.Subtract(mask, supportsMat, mask); CvInvoke.Subtract(target, patternMat, target, mask); } diff --git a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs index d8b083c..65c9e69 100644 --- a/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs +++ b/UVtools.Core/Operations/OperationRaiseOnPrintFinish.cs @@ -22,8 +22,9 @@ public class OperationRaiseOnPrintFinish : Operation #region Members private decimal _positionZ; - + private decimal _waitTime = 1200; // 20m private bool _outputDummyPixel = true; + #endregion #region Overrides @@ -89,7 +90,7 @@ public class OperationRaiseOnPrintFinish : Operation public override string ToString() { - var result = $"[Z={_positionZ}mm] [Dummy pixel: {_outputDummyPixel}]"; + var result = $"[Z={_positionZ}mm] [Wait: {_waitTime}s] [Dummy pixel: {_outputDummyPixel}]"; if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; return result; } @@ -98,9 +99,11 @@ public class OperationRaiseOnPrintFinish : Operation #region Properties public float MinimumPositionZ => Layer.RoundHeight(SlicerFile.PrintHeight + SlicerFile.LayerHeight); + public float MediumPositionZ => Layer.RoundHeight(MinimumPositionZ + (MaximumPositionZ - MinimumPositionZ) / 2); + public float MaximumPositionZ => Math.Max(MinimumPositionZ, Layer.RoundHeight(SlicerFile.MachineZ)); /// - /// Sets or gets the Z position to raise to + /// Gets or sets the Z position to raise to /// public decimal PositionZ { @@ -108,6 +111,17 @@ public class OperationRaiseOnPrintFinish : Operation set => RaiseAndSetIfChanged(ref _positionZ, Layer.RoundHeight(value)); } + /// + /// Gets or sets the ensured wait time to stay still on the desired position. + /// This is useful if the printer firmware always move to top and you want to stay still on the set position for at least the desired time. + /// Note: The print time calculation will take this wait into consideration and display a longer print time. + /// + public decimal WaitTime + { + get => _waitTime; + set => RaiseAndSetIfChanged(ref _waitTime, Math.Round(Math.Max(0, value), 2)); + } + /// /// True to output a dummy pixel on bounding rectangle position to avoid empty layer and blank image, otherwise set to false /// @@ -125,7 +139,10 @@ public class OperationRaiseOnPrintFinish : Operation //_outputDummyPixel = !SlicerFile.SupportsGCode; } - public OperationRaiseOnPrintFinish(FileFormat slicerFile) : base(slicerFile) + public OperationRaiseOnPrintFinish(FileFormat slicerFile) : base(slicerFile) + { } + + public override void InitWithSlicerFile() { if (_positionZ <= 0) _positionZ = (decimal)SlicerFile.MachineZ; } @@ -136,7 +153,7 @@ public class OperationRaiseOnPrintFinish : Operation protected bool Equals(OperationRaiseOnPrintFinish other) { - return _positionZ == other._positionZ && _outputDummyPixel == other._outputDummyPixel; + return _positionZ == other._positionZ && _outputDummyPixel == other._outputDummyPixel && _waitTime == other._waitTime; } public override bool Equals(object? obj) @@ -149,13 +166,19 @@ public class OperationRaiseOnPrintFinish : Operation public override int GetHashCode() { - return HashCode.Combine(_positionZ, _outputDummyPixel); + return HashCode.Combine(_positionZ, _outputDummyPixel, _waitTime); } #endregion #region Methods + public void SetToMinimumPosition() => PositionZ = (decimal)MinimumPositionZ; + public void SetToMediumPosition() => PositionZ = (decimal)MediumPositionZ; + public void SetToMaximumPosition() => PositionZ = (decimal)MaximumPositionZ; + + public void SetWaitTime(decimal time) => WaitTime = time; + protected override bool ExecuteInternally(OperationProgress progress) { var layer = SlicerFile.LastLayer!.Clone(); @@ -169,6 +192,8 @@ public class OperationRaiseOnPrintFinish : Operation layer.LayerMat = newMat; + if(_waitTime > 0) layer.SetWaitTimeBeforeCureOrLightOffDelay((float)_waitTime); + SlicerFile.SuppressRebuildPropertiesWork(() => { SlicerFile.Append(layer); diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index e0e6abc..a3ac3ea 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -250,12 +250,12 @@ public class OperationRepairLayers : Operation { var layer = SlicerFile[group.Key]; var image = layer.LayerMat; - var bytes = image.GetDataByteSpan(); + var span = image.GetDataByteSpan(); foreach (IssueOfPoints issue in group) { foreach (var issuePixel in issue.Points) { - bytes[image.GetPixelPos(issuePixel)] = 0; + span[image.GetPixelPos(issuePixel)] = 0; } progress.LockAndIncrement(); @@ -435,17 +435,16 @@ public class OperationRepairLayers : Operation if (_repairIslands && (_gapClosingIterations > 0 || _noiseRemovalIterations > 0)) { InitImage(); - using var kernel = CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), - new Point(-1, -1)); + if (_gapClosingIterations > 0) { - CvInvoke.MorphologyEx(image, image, MorphOp.Close, kernel, new Point(-1, -1), + CvInvoke.MorphologyEx(image, image, MorphOp.Close, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, (int)_gapClosingIterations, BorderType.Default, default); } if (_noiseRemovalIterations > 0) { - CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1), + CvInvoke.MorphologyEx(image, image, MorphOp.Open, EmguExtensions.Kernel3x3Rectangle, EmguExtensions.AnchorCenter, (int)_noiseRemovalIterations, BorderType.Default, default); } } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index 1be2e3a..020fa6a 100644 --- a/UVtools.Core/UVtools.Core.csproj +++ b/UVtools.Core/UVtools.Core.csproj @@ -10,7 +10,7 @@ https://github.com/sn4k3/UVtools https://github.com/sn4k3/UVtools MSLA/DLP, file analysis, calibration, repair, conversion and manipulation - 3.6.6 + 3.6.7 Copyright © 2020 PTRTECH UVtools.png AnyCPU;x64 @@ -31,16 +31,19 @@ true + ..\documentation\$(AssemblyName).xml 1701;1702;1591 true + ..\documentation\$(AssemblyName).xml 1701;1702;1591 true + ..\documentation\$(AssemblyName).xml 1701;1702;1591 @@ -69,10 +72,10 @@ - + - + diff --git a/UVtools.Installer/Code/HeatGeneratedFileList.wxs b/UVtools.Installer/Code/HeatGeneratedFileList.wxs index df7d2eb..409d104 100644 --- a/UVtools.Installer/Code/HeatGeneratedFileList.wxs +++ b/UVtools.Installer/Code/HeatGeneratedFileList.wxs @@ -356,8 +356,8 @@ - - + + @@ -395,9 +395,6 @@ - - - @@ -899,21 +896,6 @@ - - - - - - - - - - - - - - - @@ -1366,9 +1348,6 @@ - - - @@ -1383,9 +1362,6 @@ - - - @@ -1400,9 +1376,6 @@ - - - @@ -1417,9 +1390,6 @@ - - - @@ -1434,9 +1404,6 @@ - - - @@ -1451,9 +1418,6 @@ - - - @@ -1468,9 +1432,6 @@ - - - @@ -1485,9 +1446,6 @@ - - - @@ -1502,9 +1460,6 @@ - - - @@ -1519,9 +1474,6 @@ - - - @@ -1536,9 +1488,6 @@ - - - @@ -1553,9 +1502,6 @@ - - - @@ -1570,9 +1516,6 @@ - - - @@ -1696,7 +1639,7 @@ - + @@ -1709,7 +1652,6 @@ - @@ -1877,11 +1819,6 @@ - - - - - @@ -2029,67 +1966,54 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/UVtools.Installer/Code/Product.wxs b/UVtools.Installer/Code/Product.wxs index c371e6c..1e90877 100644 --- a/UVtools.Installer/Code/Product.wxs +++ b/UVtools.Installer/Code/Product.wxs @@ -133,7 +133,7 @@ - + diff --git a/UVtools.ScriptSample/ScriptInsetSample.cs b/UVtools.ScriptSample/ScriptInsetSample.cs index a9a4f96..2220d89 100644 --- a/UVtools.ScriptSample/ScriptInsetSample.cs +++ b/UVtools.ScriptSample/ScriptInsetSample.cs @@ -14,6 +14,7 @@ using UVtools.Core.Scripting; using Emgu.CV; using Emgu.CV.CvEnum; using UVtools.Core; +using UVtools.Core.Extensions; namespace UVtools.ScriptSample; @@ -86,9 +87,8 @@ public class ScriptInsetSample : ScriptGlobals /// True if executes successfully to the end, otherwise false. public bool ScriptExecute() { - var anchor = new Point(-1, -1); // Kernel anchor, -1, -1 = center var kernel = - CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), anchor); // Rectangle 3x3 kernel + CvInvoke.GetStructuringElement(ElementShape.Rectangle, new Size(3, 3), EmguExtensions.AnchorCenter); // Rectangle 3x3 kernel Progress.Reset("Inset layers", Operation.LayerRangeCount); // Sets the progress name and number of items to process @@ -104,10 +104,10 @@ public class ScriptInsetSample : ScriptGlobals var target = Operation.GetRoiOrDefault(mat); // Get ROI from mat if user selected an region // Erode original image by InsetMarginFromEdge pixels, so we get the offset margin from image and put new image on erodeMat - CvInvoke.Erode(target, erodeMat, kernel, anchor, InsetMarginFromEdge.Value, BorderType.Reflect101, default); + CvInvoke.Erode(target, erodeMat, kernel, EmguExtensions.AnchorCenter, InsetMarginFromEdge.Value, BorderType.Reflect101, default); // Now erode the eroded image with InsetThickness pixels, so we get the original-margin-thickness image and put the new image on wallMat - CvInvoke.Erode(erodeMat, wallMat, kernel, anchor, InsetThickness.Value, BorderType.Reflect101, default); + CvInvoke.Erode(erodeMat, wallMat, kernel, EmguExtensions.AnchorCenter, InsetThickness.Value, BorderType.Reflect101, default); // Subtract walls image from eroded image, so we get only the inset line pixels in white and put back into wallMat CvInvoke.Subtract(erodeMat, wallMat, wallMat); diff --git a/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml b/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml index 4218fed..917c18d 100644 --- a/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolRaiseOnPrintFinishControl.axaml @@ -6,23 +6,105 @@ MinWidth="600" x:Class="UVtools.WPF.Controls.Tools.ToolRaiseOnPrintFinishControl"> - + + VerticalAlignment="Center" + VerticalContentAlignment="Center" + Increment="1" + Minimum="{Binding Operation.MinimumPositionZ}" + Maximum="1000" + FormatString="F2" + Value="{Binding Operation.PositionZ}"/> + + + + + + Gets the ParallelOptions with set to 1 for debug purposes + + Gets the ParallelOptions with and the set @@ -241,6 +246,14 @@ True to run in parallel Array with same size with contours area + + + Checks if two contours intersects and return the intersecting pixel count + + Contour 1 + Contour 2 + Intersecting pixel count + Checks if two contours intersects @@ -249,6 +262,11 @@ Contour 2 + + + A disposable Mat with associated ROI Mat + + Gets index start number, if starts on 0 or 1 @@ -479,6 +497,12 @@ + + + Gets the whole data span to manipulate or read pixels, use this when possibly using ROI + + + Gets the whole data span to manipulate or read pixels, use this when possibly using ROI @@ -4674,6 +4698,23 @@ Gets or sets a new image instance + + + Gets the layer mat with roi of it bounding rectangle + + + + + Gets the layer mat with roi of model bounding rectangle + + + + + Gets the layer mat with a specified roi + + Region of interest + + Gets a new Brg image instance @@ -4776,6 +4817,13 @@ + + + Gets the bounding rectangle that is the union of a collection of layers + + Layer collection + + Gets or sets if the detection is enabled @@ -4791,7 +4839,7 @@ Combines the island and overhang detections for a better more realistic detection and to discard false-positives. (Slower) If enabled, and when a island is found, it will check for overhangs on that same island, if no overhang found then the island will be discarded and considered safe, otherwise it will flag as an island issue. - Note: Overhangs settings will be used to configure the detection.Enabling Overhangs is not required for this procedure to work. + Note: Overhangs settings will be used to configure the detection. Enabling Overhangs is not required for this procedure to work. @@ -4810,7 +4858,7 @@ - Gets the required area size (x*y) to consider process a island (0-65535) + Gets the required pixel area to consider process a island (0-65535) @@ -6157,7 +6205,14 @@ - Sets or gets the Z position to raise to + Gets or sets the Z position to raise to + + + + + Gets or sets the ensured wait time to stay still on the desired position. + This is useful if the printer firmware always move to top and you want to stay still on the set position for at least the desired time. + Note: The print time calculation will take this wait into consideration and display a longer print time. -- cgit v1.2.3