/// <summary> /// Finds exactly the cluster closest to the cluster whose label matches color1 and the points /// in each cluster that are closest, along with the square distance between them. /// </summary> /// <param name="color1">Label of the cluster whose nearest neighbor is being sought.</param> /// <returns>A point in the cluster corresponding to color1, the closest point to it /// from another cluster, the square distance between the points, and the label of the other cluster. /// NOTE: ClosestPair.Color1 will equal color1. /// </returns> public ClosestPair FindClusterExhaustively(TLabel color1) { var shortest = new ClosestPair(); foreach (var p1 in Clusters.PointsInClass(color1)) { foreach (var pc in Clusters.Points() .Select(p => new { Point = p, Color = Clusters.GetClassLabel(p) }) .Where(pc => !color1.Equals(pc.Color))) { var d = p1.Measure(pc.Point); if (d < shortest.SquareDistance) { shortest.SquareDistance = d; shortest.Color1 = color1; shortest.Point1 = p1; shortest.Color2 = pc.Color; shortest.Point2 = pc.Point; } } } //TODO: If there is only one cluster, the if statement above will not be triggered and shortest will // be ill-defined and cause a Null Pointer exception in Swap. return(shortest.Swap(color1)); }
/// <summary> /// Finds an approximately closest pair of points (one of each color) by using the ordering found in SortedPoints. /// /// This compares points in two of the clusters, ignoring points in all other clusters. /// /// NOTE: This was a good idea, but yields results too poor to be used. /// </summary> /// <param name="color1">Label of the first cluster.</param> /// <param name="color2">Label of the second cluster.</param> /// <returns>The point with Color1, the point with Color2 and the square distance between them. /// </returns> public ClosestPair FindPairApproximately(TLabel color1, TLabel color2) { var shortest = new ClosestPair(); UnsignedPoint prevP = null; TLabel prevColor = default(TLabel); foreach (var pc in SortedPoints .Select(p => new { Point = p, Color = Clusters.GetClassLabel(p) }) .Where(pc => pc.Color.Equals(color1) || pc.Color.Equals(color2))) { if (prevP != null && !prevColor.Equals(pc.Color)) { var d = pc.Point.Measure(prevP); if (d < shortest.SquareDistance) { shortest.SquareDistance = d; shortest.Color1 = prevColor; shortest.Point1 = prevP; shortest.Color2 = pc.Color; shortest.Point2 = pc.Point; } } prevP = pc.Point; prevColor = pc.Color; } return(shortest.Swap(color1)); }
/// <summary> /// Merge the clusters containing two points if the distance separating them does not exceed MergeSquareDistance. /// The points given here may be HilbertPoints frmo a HilbertIndex or UnsignedPoints already present in the Classification. /// In case of the former, a lookup is performed based on the id to find the proper UnsignedPoint corresponding to the HilbertPoint. /// </summary> /// <returns><c>true</c>, if a new merge performed, <c>false</c> if too far to merge or already merged.</returns> /// <param name="p1">First point to compare.</param> /// <param name="p2">Second point.</param> /// <param name="maxSquareDistance">If a positive value, use this as the maximum distance permitted between points. /// Otherwise, use MergeSquareDistance.</param> private bool MergeIfNear(UnsignedPoint p1, UnsignedPoint p2, long maxSquareDistance = 0) { var p1InClusters = IdsToPoints[p1.UniqueId]; var p2InClusters = IdsToPoints[p2.UniqueId]; maxSquareDistance = (maxSquareDistance <= 0) ? MergeSquareDistance : maxSquareDistance; if (p1InClusters.SquareDistanceCompare(p2InClusters, maxSquareDistance) <= 0) { var c1 = Clusters.GetClassLabel(p1InClusters); var c2 = Clusters.GetClassLabel(p2InClusters); return(Clusters.Merge(c1, c2)); } else { return(false); } }
/// <summary> /// Merge the clusters to which the two points belong, if their sizes permit. /// /// No more than one of the clusters may have a size greater than or equal to UnmergeableSize. /// </summary> /// <param name="p1">Point belonging to first cluster to merge.</param> /// <param name="p2">Point belonging to second cluster to merge.</param> /// <param name="forceMerge">If true and UnmergeableSize is the sole obstacle to the merge, perform the merge anyways. /// If false, honor UnmergeableSize.</param> /// <returns>True if the merge was performed successfully, false otherwise.</returns> private bool Merge(UnsignedPoint p1, UnsignedPoint p2, bool forceMerge = false) { var category1 = Clusters.GetClassLabel(p1); var category2 = Clusters.GetClassLabel(p2); if (category1.Equals(category2)) { return(false); } var size1 = Clusters.PointsInClass(category1).Count; var size2 = Clusters.PointsInClass(category2).Count; if (size1 >= UnmergeableSize && size2 >= UnmergeableSize && !forceMerge) { return(false); } return(Clusters.Merge(category1, category2)); }