private void TraceContiguousJunctions(Component component) { Dictionary <Discrete2D, BranchNode> junctionPoints = component.junctions; foreach (KeyValuePair <Discrete2D, BranchNode> starting in junctionPoints) { Debug.Assert(notVisitedMap[starting.Key.I, starting.Key.J]); MarkVisited(starting.Key); // n.b. not unmarked later, to prevent marking contiguous segments twice // Determine the 8-connected neighbours int neighbourhood = BitImage.Analysis.Neighbourhood(notVisitedMap, starting.Key.I, starting.Key.J); // TODO: Is it possible to have diagonally connected junctions? List <Discrete2D> relativeNeighbours = new List <Discrete2D>(BitImage.Analysis.ConnectedRelativeCoordinates(neighbourhood)); foreach (Discrete2D relativeNeighbour in relativeNeighbours) { Discrete2D neighbour = starting.Key + relativeNeighbour; // Check that the starting point hasn't already been consumed by the other branchpoint if (IsJunction(neighbour)) { SegmentNode segment = new SegmentNode(); segment.PreviousBranch = starting.Value; segment.NextBranch = junctionPoints[neighbour]; } } } }
/// <summary> /// Modify the neighbourhood to remove 8-neighbours which could be reached via /// 4-neighbour junction points, which have been temporarily removed. /// </summary> /// <param name="current">The pixel whose neighbourhood should be modified</param> /// <param name="neighbourhood">The neighbourhood to be modified</param> /// <returns>The modified neighbourhood</returns> private int AccountForJunctions(Discrete2D?current, int neighbourhood) { // Could use the same technique with a rotating bit pattern... Discrete2D west = current.Value.West(); Discrete2D north = current.Value.North(); Discrete2D south = current.Value.South(); Discrete2D east = current.Value.East(); if (connectivityMap.IsInRange(west.I, west.J) && IsJunction(west)) { neighbourhood &= 431; } if (connectivityMap.IsInRange(north.I, north.J) && IsJunction(north)) { neighbourhood &= 491; } if (connectivityMap.IsInRange(south.I, south.J) && IsJunction(south)) { neighbourhood &= 191; } if (connectivityMap.IsInRange(east.I, east.J) && IsJunction(east)) { neighbourhood &= 251; } return(neighbourhood); }
private void TraceEightConnectedFromJunctions(Component component) { // TODO: This case only occurs if there are segments which are 8-connected // at BOTH ends to branch points. NOT YET TESTED. Dictionary <Discrete2D, BranchNode> junctionPoints = component.junctions; // Trace line segments from each junction point's 8-connected neighbours foreach (KeyValuePair <Discrete2D, BranchNode> starting in junctionPoints) { Debug.Assert(notVisitedMap[starting.Key.I, starting.Key.J]); MarkVisited(starting.Key); // Prevent tracings immediately returning to their origin // Determine the 8-connected neighbours int neighbourhood = BitImage.Analysis.Neighbourhood(notVisitedMap, starting.Key.I, starting.Key.J); int fourNeighbourhood = BitImage.Analysis.ToFourDiagonal(neighbourhood); // One line different int filteredNeighbourhood = RemoveJunctions(starting.Key, fourNeighbourhood); // NOT NEEDED?? List <Discrete2D> fourNeighbours = new List <Discrete2D>(BitImage.Analysis.ConnectedRelativeCoordinates(filteredNeighbourhood)); foreach (Discrete2D relativeNeighbour in fourNeighbours) { Discrete2D neighbour = starting.Key + relativeNeighbour; // Check that the starting point hasn't already been consumed, for example, // by a single line looping back so the same branch point it originated from if (notVisitedMap[neighbour.I, neighbour.J]) { SegmentNode segment = TraceSegment(component, neighbour); segment.PreviousBranch = starting.Value; } else { Console.WriteLine("Model contains single segment loops!"); // TODO: Test this } } UnmarkVisited(starting.Key); // Unmark, so that this junction point can be found for other segment ends } }
private Discrete2D?AppendToSegment(Discrete2D?current, SegmentNode segment, Discrete2D neighbour) { // Add a point to the segment, and move on Debug.Assert(current.HasValue); Debug.Assert(IsStem(neighbour)); Point2D currentGeographic = GridToGeographic(current.Value.I, current.Value.J); segment.Add(currentGeographic); MarkVisited(current.Value); return(neighbour); }
private Discrete2D?Follow(Component component, Discrete2D?current, SegmentNode segment) { Debug.Assert(current.HasValue); int neighbourhood = BitImage.Analysis.Neighbourhood(notVisitedMap, current.Value.I, current.Value.J); neighbourhood = AccountForJunctions(current, neighbourhood); int eightConnectivity = BitImage.Analysis.EightConnectivity(neighbourhood); Debug.Assert(eightConnectivity > 0); if (eightConnectivity == 1) { List <Discrete2D> neighbours = new List <Discrete2D>(BitImage.Analysis.ConnectedRelativeCoordinates(neighbourhood)); Debug.Assert(eightConnectivity == neighbours.Count); Discrete2D neighbour = current.Value + neighbours[0]; if (IsJunction(neighbour)) { current = EndSegmentAtJunction(component, current, segment, neighbour); } else if (IsFreeEnd(neighbour)) { current = EndSegmentAtFreeEnd(current, segment, neighbour); } else // neighbour is segment stem { current = AppendToSegment(current, segment, neighbour); } } else // eightConnectivity > 1 { // Determine numFour - the number of 4-connected neighbours int fourConnectivity = BitImage.Analysis.FourConnectivity(neighbourhood); Debug.Assert(fourConnectivity == 1); int fourNeighbourhood = BitImage.Analysis.ToFourCross(neighbourhood); List <Discrete2D> fourNeighbours = new List <Discrete2D>(BitImage.Analysis.ConnectedRelativeCoordinates(fourNeighbourhood)); Debug.Assert(fourConnectivity == fourNeighbours.Count); Discrete2D neighbour = current.Value + fourNeighbours[0]; if (IsJunction(neighbour)) { current = EndSegmentAtJunction(component, current, segment, neighbour); } else // Extend to neighbour { Debug.Assert(IsStem(neighbour)); current = AppendToSegment(current, segment, neighbour); } } return(current); }
private SegmentNode TraceSegment(Component component, Discrete2D start) { Discrete2D?current = start; Debug.Assert(connectivityMap[current.Value.I, current.Value.J] != 0); SegmentNode segment = network.CreateSegmentNode(); while (current.HasValue) { current = Follow(component, current, segment); } return(segment); }
/// <summary> /// Modify the neighbourhood to remove any eight-connected junctions. Useful /// when processing contiguous junction points /// </summary> /// <param name="current">The pixel whose neighbourhood should be modified</param> /// <param name="neighbourhood">The neighbourhood to be modified</param> /// <returns>The modified neighbourhood</returns> private int RemoveJunctions(Discrete2D?current, int neighbourhood) { Debug.Assert(current.HasValue); const int allExceptCenter = 510; // TODO: Make this and the next line static List <Discrete2D> allNeighbours = new List <Discrete2D>(BitImage.Analysis.ConnectedRelativeCoordinates(allExceptCenter)); Int32 bits = 1; foreach (Discrete2D relativeCoord in allNeighbours) { bits <<= 1; Discrete2D neighbour = current.Value + relativeCoord; if (connectivityMap.IsInRange(neighbour.I, neighbour.J) && IsJunction(neighbour)) { Int32 mask = ~bits; neighbourhood &= mask; Debug.Assert(neighbourhood >= 0 && neighbourhood < 512); } } return(neighbourhood); }
private bool IsJunction(Discrete2D coord) { Debug.Assert(connectivityMap != null); return(connectivityMap[coord.I, coord.J] >= 3); }
private bool IsStem(Discrete2D coord) { Debug.Assert(connectivityMap != null); return(connectivityMap[coord.I, coord.J] == 2); }
private bool IsFreeEnd(Discrete2D coord) { Debug.Assert(connectivityMap != null); return(connectivityMap[coord.I, coord.J] == 1); }
private void UnmarkVisited(Discrete2D point) { notVisitedMap[point.I, point.J] = true; }
private void MarkVisited(Discrete2D point) { notVisitedMap[point.I, point.J] = false; }
private Discrete2D?EndSegmentAtFreeEnd(Discrete2D?current, SegmentNode segment, Discrete2D neighbour) { // End segment here by adding last node Debug.Assert(current.HasValue); Point2D currentGeographic = GridToGeographic(current.Value.I, current.Value.J); segment.Add(currentGeographic); Point2D neighbourGeographic = GridToGeographic(neighbour.I, neighbour.J); segment.Add(neighbourGeographic); segment.NextBranch = null; MarkVisited(current.Value); MarkVisited(neighbour); return(null); }
private Discrete2D?EndSegmentAtJunction(Component component, Discrete2D?current, SegmentNode segment, Discrete2D junction) { // End segment here with edge to junction Debug.Assert(current.HasValue); BranchNode branchPoint = component.junctions[junction]; Point2D geographic = GridToGeographic(current.Value.I, current.Value.J); segment.Add(geographic); segment.NextBranch = branchPoint; MarkVisited(current.Value); return(null); }
private static void test01(int n) //****************************************************************************80 // // Purpose: // // TEST01 looks at a 20x20 region. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 16 December 2012 // // Author: // // John Burkardt // // Parameters: // // Input, int N, the number of sample points to be generated. // { int n1 = 0; int n2 = 0; Console.WriteLine(""); Console.WriteLine("TEST01"); Console.WriteLine(" Consider data skewed toward the upper left corner of the unit square."); Console.WriteLine(" Generate " + n + " samples"); // // Get the dimensions of the PDF data. // Discrete2D.get_discrete_pdf_size1(ref n1, ref n2); Console.WriteLine(" PDF data is on a " + n1 + " by " + n2 + " grid."); // // Construct a PDF from the data. // double[] pdf = Discrete2D.get_discrete_pdf_data1(n1, n2); // // "Integrate" the data over rows and columns of the region to get the CDF. // double[] cdf = Discrete2D.set_discrete_cdf(n1, n2, pdf); // // Choose N CDF values at random. // int seed = 123456789; double[] u = UniformRNG.r8vec_uniform_01_new(n, ref seed); // // Find the cell corresponding to each CDF value, // and choose a random point in that cell. // double[] xy = Discrete2D.discrete_cdf_to_xy(n1, n2, cdf, n, u, ref seed); // // Write data to a file for examination, plotting, or analysis. // string filename = "test01.txt"; typeMethods.r8mat_write(filename, 2, n, xy); Console.WriteLine(""); Console.WriteLine(" Wrote sample data to file \"" + filename + "\"."); }