/// <summary> /// Merges line segment blobs (connected components) in one LineDetection. /// </summary> /// <param name="segment">Detected blobs.</param> /// <param name="Input">Input image.</param> /// <param name="OX">Delta between the data array and actual position, X component.</param> /// <param name="OY">Delta between the data array and actual position, Y component.</param> /// <returns>A LineDetection from the blobs.</returns> static LineDetection MergeBlobs(DetectionSegment segment, double[,] Input, int OX, int OY) { double Xmean = 0, Ymean = 0; double XX = 0, XY = 0, YY = 0; double Flux = 0; double XBmean = 0, YBmean = 0; List <double> PValues = new List <double>(); List <IntPoint> MergedPoints = segment.Blobs.Aggregate(new List <IntPoint>(), (x, y) => { x.AddRange(y.Points); return(x); }); foreach (IntPoint pt in MergedPoints) { double Val = Input[pt.Y, pt.X]; Xmean += pt.X; Ymean += pt.Y; XBmean += Val * pt.X; YBmean += Val * pt.Y; XX += pt.X * pt.X; XY += pt.X * pt.Y; YY += pt.Y * pt.Y; Flux += Val; PValues.Add(Val); } Xmean /= MergedPoints.Count; Ymean /= MergedPoints.Count; XBmean /= Flux; YBmean /= Flux; XX /= MergedPoints.Count; XY /= MergedPoints.Count; YY /= MergedPoints.Count; XX -= Xmean * Xmean; XY -= Xmean * Ymean; YY -= Ymean * Ymean; double Msq = Sqrt(XX * XX + 4 * XY * XY - 2 * XX * YY + YY * YY); double L1 = 1.0 / 2 * (XX + YY - Msq); double L2 = 1.0 / 2 * (XX + YY + Msq); double A1 = Atan2(2 * XY, -(-XX + YY + Msq)); double A2 = Atan2(2 * XY, -(-XX + YY - Msq)); LineDetection ld = new LineDetection() { Points = MergedPoints.Select((x) => new PixelPoint() { X = x.X + OX, Y = x.Y + OY }).ToList(), EigenValue1 = L1, EigenValue2 = L2, EigenAngle1 = A1, EigenAngle2 = A2, Barycenter = new PixelPoint() { X = XBmean + OX, Y = YBmean + OY }, PointsCenter = new PixelPoint() { X = Xmean + OX, Y = Ymean + OY }, Flux = Flux, PointValues = PValues }; return(ld); }
/// <summary> /// Scans a line on the image for line segments using a hysteresis connected component algorithm. /// </summary> /// <param name="Input">Input data.</param> /// <param name="AnalyzeMask">Mask for marking visited pixels.</param> /// <param name="Height">Data height.</param> /// <param name="Width">Data width.</param> /// <param name="Rho">Distance from origin to line.</param> /// <param name="Theta">Line angle.</param> /// <param name="HighTh">Upper hysteresis threshold.</param> /// <param name="LowTh">Lower hysteresis threshold.</param> /// <param name="MaxIgnore">Maximum interblob distance.</param> /// <param name="ScanWidth">Width of the scanned area.</param> /// <param name="OX">Image data origin X coordinate.</param> /// <param name="OY">Image data origin Y coordinate.</param> /// <returns>A list of line segment detections.</returns> internal static List <LineDetection> AnalyzeLine(double[,] Input, bool[,] AnalyzeMask, int Height, int Width, double Rho, double Theta, double HighTh, double LowTh, int MaxIgnore, int ScanWidth, int OX, int OY) { /* Unit vector in the direction of the line */ Vector LineVector = new Vector() { X = Cos(Theta), Y = Sin(Theta) }; /* Origin of the line */ Vector LineOrigin = new Vector() { X = -Rho *Sin(Theta), Y = Rho * Cos(Theta) }; /* Unit vector perpendicular to the line */ Vector LONormal = new Vector() { X = -Sin(Theta), Y = Cos(Theta) }; /* Compute the intersections with the bounding box */ Vector LeftIntersect; double LDist; if (!LineIntersection.IntersectLeft(LineOrigin, LineVector, Width, Height, out LeftIntersect, out LDist)) { return(null); } Vector RightIntersect; double RDist; if (!LineIntersection.IntersectRight(LineOrigin, LineVector, Width, Height, out RightIntersect, out RDist)) { return(null); } /* Sort the intersections */ double Start = Min(LDist, RDist); double End = Max(LDist, RDist); Vector StVec, EVec; if (Start == LDist && End == RDist) { StVec = LeftIntersect; EVec = RightIntersect; } else if (Start == RDist && End == LDist) { StVec = RightIntersect; EVec = LeftIntersect; } else { throw new ApplicationException("Geometry error."); } /* Scan line for blobs */ int k; int N = (int)(End - Start); Vector pt = StVec; List <Vector> LineIntervals = new List <Vector>(); List <DetectionBlob> Blobs = new List <DetectionBlob>(); for (k = 0; k < N; k++, pt.Increment(LineVector)) { int l; Vector vl = pt; for (l = -ScanWidth; l < ScanWidth; l++, vl.Increment(LONormal)) { int X = (int)Round(vl.X); int Y = (int)Round(vl.Y); if (X < 0 || X >= Width) { continue; } if (Y < 0 || Y >= Height) { continue; } if (AnalyzeMask[Y, X]) { continue; } double Val = Input[Y, X]; if (Val > HighTh) { DetectionBlob db = BitmapFill(Input, new IntPoint() { X = X, Y = Y }, AnalyzeMask, LowTh, Theta); LineIntervals.Add(new Vector() { X = db.LineStart, Y = db.LineEnd }); Blobs.Add(db); } } } /* Merge blobs into line segments */ List <DetectionSegment> FoundSegments = new List <DetectionSegment>(); DetectionSegment cseg = default(DetectionSegment); /* Current segment */ for (k = 0; k < LineIntervals.Count; k++) { if (cseg.Blobs != null) { /* If still within threshold of current segment */ if (Min(LineIntervals[k].X, LineIntervals[k].Y) < cseg.End + MaxIgnore) { cseg.Blobs.Add(Blobs[k]); cseg.End = Max(cseg.End, Max(LineIntervals[k].Y, LineIntervals[k].X)); continue; } else { FoundSegments.Add(cseg); cseg = default(DetectionSegment); } } /* Create new current segment */ cseg.Blobs = new List <DetectionBlob>(); cseg.Angle = Theta; cseg.Blobs.Add(Blobs[k]); cseg.Start = Min(LineIntervals[k].X, LineIntervals[k].Y); cseg.End = Max(LineIntervals[k].Y, LineIntervals[k].X); } if (cseg.Blobs != null) { FoundSegments.Add(cseg); } List <LineDetection> Detections = FoundSegments.Select((x) => MergeBlobs(x, Input, OX, OY)).ToList(); return(Detections); }