diff options
Diffstat (limited to 'UVtools.Core/Operations/OperationRepairLayers.cs')
-rw-r--r-- | UVtools.Core/Operations/OperationRepairLayers.cs | 220 |
1 files changed, 219 insertions, 1 deletions
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs index 2622432..e46d3c2 100644 --- a/UVtools.Core/Operations/OperationRepairLayers.cs +++ b/UVtools.Core/Operations/OperationRepairLayers.cs @@ -7,9 +7,19 @@ */ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Drawing; +using System.Linq; using System.Text; +using System.Threading.Tasks; using System.Xml.Serialization; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using Emgu.CV.Util; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; using UVtools.Core.Objects; namespace UVtools.Core.Operations @@ -17,13 +27,17 @@ namespace UVtools.Core.Operations [Serializable] public class OperationRepairLayers : Operation { + #region Members private bool _repairIslands = true; private bool _repairResinTraps = true; private bool _removeEmptyLayers = true; private byte _removeIslandsBelowEqualPixelCount = 5; private ushort _removeIslandsRecursiveIterations = 4; private uint _gapClosingIterations = 1; - private uint _noiseRemovalIterations = 0; + private uint _noiseRemovalIterations; + #endregion + + #region Overrides public override bool CanROI => false; public override string Title => "Repair layers and issues"; public override string Description => null; @@ -48,7 +62,9 @@ namespace UVtools.Core.Operations return new StringTag(sb.ToString()); } + #endregion + #region Properties public bool RepairIslands { get => _repairIslands; @@ -96,5 +112,207 @@ namespace UVtools.Core.Operations [XmlIgnore] public List<LayerIssue> Issues { get; set; } + #endregion + + #region Methods + + public override bool Execute(FileFormat slicerFile, OperationProgress progress = null) + { + progress ??= new OperationProgress(); + + + + // Remove islands + if (!ReferenceEquals(Issues, null) + && !ReferenceEquals(IslandDetectionConfig, null) + && RepairIslands + && RemoveIslandsBelowEqualPixelCount > 0 + && RemoveIslandsRecursiveIterations != 1) + { + progress.Reset("Removed recursive islands", 0); + ushort limit = RemoveIslandsRecursiveIterations == 0 + ? ushort.MaxValue + : RemoveIslandsRecursiveIterations; + + var recursiveIssues = Issues; + ConcurrentBag<uint> islandsToRecompute = null; + + var islandConfig = IslandDetectionConfig; + var overhangConfig = new OverhangDetectionConfiguration(false); + var touchingBoundsConfig = new TouchingBoundDetectionConfiguration(false); + var resinTrapsConfig = new ResinTrapDetectionConfiguration(false); + var emptyLayersConfig = false; + + islandConfig.Enabled = true; + islandConfig.RequiredAreaToProcessCheck = (byte)Math.Ceiling(RemoveIslandsBelowEqualPixelCount / 2m); + + for (uint i = 0; i < limit; i++) + { + if (i > 0) + { + /*var whiteList = islandsToRecompute.GroupBy(u => u) + .Select(grp => grp.First()) + .ToList();*/ + islandConfig.WhiteListLayers = islandsToRecompute.ToList(); + recursiveIssues = slicerFile.LayerManager.GetAllIssues(islandConfig, overhangConfig, resinTrapsConfig, touchingBoundsConfig, emptyLayersConfig); + //Debug.WriteLine(i); + } + + var issuesGroup = + recursiveIssues + .Where(issue => issue.Type == LayerIssue.IssueType.Island && + issue.Pixels.Length <= RemoveIslandsBelowEqualPixelCount) + .GroupBy(issue => issue.LayerIndex); + + if (!issuesGroup.Any()) break; // Nothing to process + islandsToRecompute = new ConcurrentBag<uint>(); + Parallel.ForEach(issuesGroup, group => + { + if (progress.Token.IsCancellationRequested) return; + Layer layer = slicerFile[group.Key]; + Mat image = layer.LayerMat; + Span<byte> bytes = image.GetPixelSpan<byte>(); + foreach (var issue in group) + { + foreach (var issuePixel in issue.Pixels) + { + bytes[image.GetPixelPos(issuePixel)] = 0; + } + + lock (progress.Mutex) + { + progress++; + } + } + + var nextLayerIndex = group.Key + 1; + if (nextLayerIndex < slicerFile.LayerCount) + islandsToRecompute.Add(nextLayerIndex); + + layer.LayerMat = image; + }); + + if (islandsToRecompute.IsEmpty) break; // No more leftovers + } + } + + progress.Reset(ProgressAction, LayerRangeCount); + if (RepairIslands || RepairResinTraps) + { + Parallel.For(LayerIndexStart, LayerIndexEnd, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + Layer layer = slicerFile[layerIndex]; + Mat image = null; + + void initImage() + { + image ??= layer.LayerMat; + } + + if (Issues is not null) + { + if (RepairIslands && RemoveIslandsBelowEqualPixelCount > 0 && RemoveIslandsRecursiveIterations == 1) + { + Span<byte> bytes = null; + foreach (var issue in Issues) + { + if ( + issue.LayerIndex != layerIndex || + issue.Type != LayerIssue.IssueType.Island || + issue.Pixels.Length > RemoveIslandsBelowEqualPixelCount) continue; + + initImage(); + if (bytes == null) + bytes = image.GetPixelSpan<byte>(); + + foreach (var issuePixel in issue.Pixels) + { + bytes[image.GetPixelPos(issuePixel)] = 0; + } + } + /*if (issues.TryGetValue((uint)layerIndex, out var issueList)) + { + var bytes = image.GetPixelSpan<byte>(); + foreach (var issue in issueList.Where(issue => + issue.Type == LayerIssue.IssueType.Island && issue.Pixels.Length <= removeIslandsBelowEqualPixels)) + { + foreach (var issuePixel in issue.Pixels) + { + bytes[image.GetPixelPos(issuePixel)] = 0; + } + } + }*/ + } + + if (RepairResinTraps) + { + foreach (var issue in Issues.Where(issue => issue.LayerIndex == layerIndex && issue.Type == LayerIssue.IssueType.ResinTrap)) + { + initImage(); + using var vec = new VectorOfVectorOfPoint(new VectorOfPoint(issue.Pixels)); + CvInvoke.DrawContours(image, + vec, + -1, + EmguExtensions.WhiteByte, + -1); + } + } + } + + if (RepairIslands && (GapClosingIterations > 0 || NoiseRemovalIterations > 0)) + { + initImage(); + using Mat 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), + (int)GapClosingIterations, BorderType.Default, default); + } + + if (NoiseRemovalIterations > 0) + { + CvInvoke.MorphologyEx(image, image, MorphOp.Open, kernel, new Point(-1, -1), + (int)NoiseRemovalIterations, BorderType.Default, default); + } + } + + if (!ReferenceEquals(image, null)) + { + layer.LayerMat = image; + image.Dispose(); + } + + lock (progress.Mutex) + { + progress++; + } + }); + } + + if (RemoveEmptyLayers) + { + List<uint> removeLayers = new List<uint>(); + for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) + { + if (slicerFile[layerIndex].NonZeroPixelCount == 0) + { + removeLayers.Add(layerIndex); + } + } + + if (removeLayers.Count > 0) + { + OperationLayerRemove.RemoveLayers(slicerFile, removeLayers, progress); + } + } + + progress.Token.ThrowIfCancellationRequested(); + + return true; + } + + #endregion } } |