private List <LineCluster> ClusterLines(CachedJunctionCombinations cachedJunctionCombinations, List <Line> possibleSol) { Dictionary <int, Junction[]> junctionsPerGroup = VerticalGroupJunctions(cachedJunctionCombinations, possibleSol); List <LineCluster> lineClusters = HorizontalGroupLines(junctionsPerGroup); return(lineClusters); }
private Dictionary <int, Junction[]> VerticalGroupJunctions(CachedJunctionCombinations cachedJunctionCombinations, List <Line> possibleSol) { // Let's merge near junctions. (vertical line) // We assign a group id for each clusters. Dictionary <int, int> junctionToGroupId = new Dictionary <int, int>(); int nextGroupId = 1; foreach (var curSolution in possibleSol) { if (curSolution.Junctions.First().GroupId == 0) { for (int i = 0; i < curSolution.Junctions.Length; i++) { ref var j = ref curSolution.Junctions[i]; j.GapX = curSolution.GapX; } // Not assigned yet. // Find near junction. int groupId = 0; foreach (var item in curSolution.Junctions) { var alreadyClassified = cachedJunctionCombinations.cacheNearJunction[BuildDictionaryId(item.X, item.Y)] .Where(m => // Doesn't work with struct. //m.GroupId != 0 && //TODO: More hardcoding Math.Abs(m.X - item.X) <= 5 && Math.Abs(m.Y - item.Y) <= 3 // Doesn't work with struct. //Math.Abs(m.GapX - item.GapX) <= 2 ).Where(m => junctionToGroupId.ContainsKey(BuildDictionaryId(m.X, m.Y))); if (alreadyClassified.Any()) { Junction junction = alreadyClassified.First(); groupId = junctionToGroupId[BuildDictionaryId(junction.X, junction.Y)]; //groupId = alreadyClassified.First().GroupId; break; } } if (groupId == 0) { // Not found. // Create a new group. nextGroupId++; groupId = nextGroupId; } for (int i = 0; i < curSolution.Junctions.Length; i++) { ref var j = ref curSolution.Junctions[i]; j.GroupId = groupId; int id = BuildDictionaryId(j.X, j.Y); if (!junctionToGroupId.ContainsKey(id)) { junctionToGroupId.Add(id, groupId); } } }
private List <Line> GetLines(List <Junction> listJunction, CachedJunctionCombinations junctionCombinations) { // Let's check the list of points. int numSol = 0; List <Line> possibleSol = new List <Line>(); // We use a dictionary here because we need a fast way to remove entry. // We reduce computation and we also merge solutions. var elements = listJunction.OrderBy(m => m.Y).ToDictionary(m => BuildDictionaryId(m.X, m.Y), m => m); int skipSol = 0; while (elements.Any()) { var start = elements.First().Value; elements.Remove(BuildDictionaryId(start.X, start.Y)); Dictionary <int, List <int> > usedJunctionsForGapX = new Dictionary <int, List <int> >(); List <Line> listSolutions = new List <Line>(); var junctionsForGap = junctionCombinations.cacheNearJunction[BuildDictionaryId(start.X, start.Y)]; for (int iGap = 0; iGap < junctionsForGap.Length; iGap++) { var gap = junctionsForGap[iGap]; // Useless because it's already done with: cacheNearJunction. /* * var gapY = Math.Abs(gap.Y - start.Y); * if (gapY > 2) * { * continue; * }*/ var gapX = Math.Abs(gap.X - start.X); if (gapX <= this.Options.minX || gapX > this.Options.maxX) { continue; } // We will reduce list of solution by checking if the solution is already found. //if (listSolutions.Any(m => Math.Abs(m.GapX - gapX) < 2 && m.Junctions.Contains(start))) if (usedJunctionsForGapX.ContainsKey(BuildDictionaryId(gap.X, gap.Y)) && usedJunctionsForGapX[BuildDictionaryId(gap.X, gap.Y)].Any(m => Math.Abs(m - gapX) < 10)) { skipSol++; continue; } List <Junction> curSolution = new List <Junction>(); curSolution.Add(start); int numElementsRight = FindElementsOnDirection(junctionCombinations.cachePossibleNextJunctionRight, start, gapX, curSolution); int numElementsLeft = FindElementsOnDirection(junctionCombinations.cachePossibleNextJunctionLeft, start, -gapX, curSolution); int numElements = numElementsLeft + numElementsRight; if (numElements >= this.Options.MinNumElements) { if (numSol == this.Options.MaxSolutions) { // Something wrong happen. Too much solution for now. // If we continue, we would spend too much time processing the image. // Let's suppose we don't know. throw (new Exception("Too much solution somehow")); /* * return new OpenFieldReaderResult * { * // Too much solution. You may want to increase MaxSolutions. * ReturnCode = 30 * }; */ } numSol++; listSolutions.Add(new Line { GapX = gapX, Junctions = curSolution.ToArray() }); foreach (var item in curSolution) { List <int> listGapX; if (!usedJunctionsForGapX.ContainsKey(BuildDictionaryId(item.X, item.Y))) { listGapX = new List <int>(); usedJunctionsForGapX.Add(BuildDictionaryId(item.X, item.Y), listGapX); } else { listGapX = usedJunctionsForGapX[BuildDictionaryId(item.X, item.Y)]; } listGapX.Add(gapX); } } } Line bestSol = listSolutions.OrderByDescending(m => m.Junctions.Count()).FirstOrDefault(); if (bestSol != null) { // Too slow. (faster if we skip removal) // But, we have more solutions. foreach (var item in bestSol.Junctions) { elements.Remove(BuildDictionaryId(item.X, item.Y)); } possibleSol.Add(bestSol); } } if (this.Options.Verbose) { this.Logger.LogSolutions(numSol, possibleSol, skipSol); } return(possibleSol); }