Skip to content

Commit

Permalink
Merge pull request #23 from byancey/master
Browse files Browse the repository at this point in the history
Support for 4-connected-neighbor island detection
  • Loading branch information
sn4k3 authored Aug 17, 2020
2 parents 747a292 + a28575f commit 06598b7
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 61 deletions.
9 changes: 9 additions & 0 deletions UVtools.Core/Layer/LayerIssue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ public class IslandDetectionConfiguration
/// Gets the required brightness of supporting pixels to count as a valid support (0-255)
/// </summary>
public byte RequiredPixelBrightnessToSupport { get; set; } = 150;

/// <summary>
/// Gets the setting for whether or not diagonal bonds are considered when evaluation islands.
/// If true, all 8 neighbors of a pixel (including diagonals) will be considered when finding
/// individual components on the layer, if false only 4 neighbors (right, left, above, below)
/// will be considered..
/// </summary>
///
public bool AllowDiagonalBonds { get; set; } = false;
}

public class ResinTrapDetectionConfiguration
Expand Down
124 changes: 63 additions & 61 deletions UVtools.Core/Layer/LayerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
using UVtools.Core.FileFormats;
using UVtools.Core.Operations;
using UVtools.Core.PixelEditor;
using System.Runtime.InteropServices;

namespace UVtools.Core
{
Expand Down Expand Up @@ -839,56 +840,60 @@ public List<LayerIssue> GetAllIssues(
}
}

VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();

if (islandConfig.BinaryThreshold > 0)
{
using (var thresholdImage = new Mat())
{
CvInvoke.Threshold(image, thresholdImage, 1, 255, ThresholdType.Binary);
CvInvoke.FindContours(thresholdImage, contours, hierarchy, RetrType.Ccomp,
ChainApproxMethod.ChainApproxSimple);
}
}
else
using (Mat labels = new Mat())
using (Mat stats = new Mat())
using (Mat centroids = new Mat())
{
CvInvoke.FindContours(image, contours, hierarchy, RetrType.Ccomp,
ChainApproxMethod.ChainApproxSimple);
}

var arr = hierarchy.GetData();
//
//hierarchy[i][0]: the index of the next contour of the same level
//hierarchy[i][1]: the index of the previous contour of the same level
//hierarchy[i][2]: the index of the first child
//hierarchy[i][3]: the index of the parent
//

Mat previousImage = null;
Span<byte> previousSpan = null;

for (int i = 0; i < contours.Size; i++)
{
if ((int) arr.GetValue(0, i, 2) == -1 && (int) arr.GetValue(0, i, 3) != -1)
continue;
var rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.GetArea() < islandConfig.RequiredAreaToProcessCheck)
continue;
int numLabels;

if (ReferenceEquals(previousImage, null))
if (islandConfig.BinaryThreshold > 0)
{
previousImage = this[layer.Index - 1].LayerMat;
previousSpan = previousImage.GetPixelSpan<byte>();
using (var thresholdImage = new Mat())
{
CvInvoke.Threshold(image, thresholdImage, 1, 255, ThresholdType.Binary);
// Evaluate number of connected components using the 4-connected neighbor approach
numLabels = CvInvoke.ConnectedComponentsWithStats(thresholdImage, labels, stats, centroids,
islandConfig.AllowDiagonalBonds ? LineType.EightConnected : LineType.FourConnected);
}
}
else
{
// Evaluate number of connected components 4-connected neighbor approach
numLabels = CvInvoke.ConnectedComponentsWithStats(image, labels, stats, centroids,
islandConfig.AllowDiagonalBonds?LineType.EightConnected:LineType.FourConnected);
}

// Get array that contains details of each connected component
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

List<Point> points = new List<Point>();
uint pixelsSupportingIsland = 0;
Span<int> labelSpan = MemoryMarshal.Cast<byte, int>(labels.GetPixelSpan<byte>());
Mat previousImage = null;
Span<byte> previousSpan = null;

using (Mat contourImage = image.CloneBlank())
for (int i = 1; i < numLabels; i++)
{
CvInvoke.DrawContours(contourImage, contours, i, new MCvScalar(255), -1);
var contourImageSpan = contourImage.GetPixelSpan<byte>();
Rectangle rect = new Rectangle((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));

if (rect.GetArea() < islandConfig.RequiredAreaToProcessCheck)
continue;

if (ReferenceEquals(previousImage, null))
{
previousImage = this[layer.Index - 1].LayerMat;
previousSpan = previousImage.GetPixelSpan<byte>();
}

List<Point> points = new List<Point>();
uint pixelsSupportingIsland = 0;

for (int y = rect.Y; y < rect.Bottom; y++)
{
Expand All @@ -897,11 +902,10 @@ public List<LayerIssue> GetAllIssues(
int pixel = step * y + x;
if (span[pixel] < islandConfig.RequiredPixelBrightnessToProcessCheck)
continue; // Low brightness, ignore
if (contourImageSpan[pixel] != 255)
continue; // Not inside contour, ignore

//if (CvInvoke.PointPolygonTest(contours[i], new PointF(x, y), false) < 0) continue; // Out of contour SLOW!
//Debug.WriteLine($"Layer: {layer.Index}, Coutour: {i}, X:{x} Y:{y}");
if (labelSpan[pixel] != i)
continue; // Background pixel or a pixel from another component within the bounding rectangle

points.Add(new Point(x, y));

if (previousSpan[pixel] >=
Expand All @@ -911,26 +915,24 @@ public List<LayerIssue> GetAllIssues(
}
}
}
}

if (points.Count == 0) continue;
if (pixelsSupportingIsland >= islandConfig.RequiredPixelsToSupport)
continue; // Not a island, bounding is strong, i think...
if (pixelsSupportingIsland > 0 &&
points.Count < islandConfig.RequiredPixelsToSupport &&
pixelsSupportingIsland >= Math.Max(1, points.Count / 2))
continue; // Not a island, but maybe weak bounding...

if (points.Count == 0) continue;
if (pixelsSupportingIsland >= islandConfig.RequiredPixelsToSupport)
continue; // Not a island, bounding is strong, i think...
if (pixelsSupportingIsland > 0 &&
points.Count < islandConfig.RequiredPixelsToSupport &&
pixelsSupportingIsland >= Math.Max(1, points.Count / 2))
continue; // Not a island, but maybe weak bounding...

var issue = new LayerIssue(layer, LayerIssue.IssueType.Island, points.ToArray(),
rect);
result.Add(issue);
}

var issue = new LayerIssue(layer, LayerIssue.IssueType.Island, points.ToArray(),
rect);
result.Add(issue);
}

contours.Dispose();
hierarchy.Dispose();
previousImage?.Dispose();
previousImage?.Dispose();
}
}

lock (progress.Mutex)
Expand Down

0 comments on commit 06598b7

Please sign in to comment.