C# - Finding the Boundaries of an Image (not the size) - c#

I'm developing an application to split an image grid equally and center the images (based on their similarity). So far, I could manage to fix a grid of images with small sizes, but whenever I try a larger "sprite" size (100x100, for instance), I get Stack Overflow error.
Yes I'm using recursion, but whenever a pixel is checked, I set a boolean to deactivate it, copy it to a list and go on checking the others (in all directions), until the list is filled up with an image from the grid. I'm not sure if this is the best way since for each call, I call the same method 7 times (supposing there are 7 adjacent pixels which weren't checked yet)... until there are no pixels left to check, and I can move on to the next image in the grid.
I tried tracing where the error started happening, it was after checking more or less 1600 pixels and adding them to the List. MyPixel is a class which contains 4 variables: x(int), y(int), color (Color), and checked (bool)
public void processSprite(int i, int j)
{
//OOO
//OXO
//OOO
pixeltemp.Add(new MyPixel(imap.pixels[i, j].x, imap.pixels[i, j].y, imap.pixels[i, j].color));
imap.pixels[i, j].read = true;
//OOO
//OOX
//OOO
try
{
if (!imap.pixels[i + 1, j].read)
{
if (imap.pixels[i + 1, j].color.A == 0) //Found a Border
{
imap.pixels[i + 1, j].read = true;
}
else
{
processSprite(i + 1, j);
}
}
}
//... (code goes on)
}
pixeltemp is the temporary list of pixels which holds the image (List<MyPixel>)
imap contains the entire image (List<MyPixel>)
I guess it's not a memory problem since my app just takes about 16mb tops.
My question is, why do I have this "Stack overflow" error if it's not an infinite recursion? Is there an easier way to do this? I do think my code looks ugly, I just have no idea how to make it better.
Thanks in advance !

Stack overflows aren't caused by infinite recursion but by more recursion (or, rather, call stack) than the process can handle. In your case, each recursive call to processSprite is going to do the same number of recursive calls to processSprite. So in the worst-case scenario of 1600 pixels without finding a boundary, your call tree would look like this:
processSprite(0, j)
processSprite(1, j)
processSprite(2, j)
...
processSprite(1599, j) <-- That's 1600 call frames,
enough for an overflow.
You'll want to reorganize your algorithm into a linear loop that does a depth-first search, perhaps in a spiral pattern if you're wanting to to find a pixel that's closest to the starting point. I'm sure there are already other spiffy algorithms others have already developed to solve this problem.
Edit:
I think I understand better now the problem that you're trying to solve. It sounds like you have an image that may contain multiple image tiles surrounded by 0-alpha pixels and you want to find the bounding rectangles for each of these tiles. That looked like an interesting problem to solve, so I implemented it:
IEnumerable<Rectangle> FindImageTiles(Bitmap compositeImage)
{
var result = new List<Rectangle>();
// Scan for a non-empty region that hasn't already been "captured"
for (var x = 0; x < compositeImage.Width; x++)
{
for (var y = 0; y < compositeImage.Height; y++)
{
// Only process the pixel if we don't have a rectangle that
// already contains this and if it's not empty
if (!result.Any(r => r.Contains(x, y))
&& compositeImage.GetPixel(x, y).A != 0)
{
// Now that we've found a point, create a rectangle
// surrounding that point, then expand outward until
// we have a bounding rectangle that doesn't intersect
// with the tile
var rect = new Rectangle(x - 1, y - 1, 2, 2);
bool foundBounds = false;
while (!foundBounds)
{
var xRange = Enumerable.Range(rect.Left, rect.Right)
.Where(px => px >= 0 && px < compositeImage.Width);
var yRange = Enumerable.Range(rect.Top, rect.Bottom)
.Where(py => py >= 0 && py < compositeImage.Height);
// Adjust the top
if (rect.Top >= 0
&& xRange
.Select(bx => compositeImage.GetPixel(bx, rect.Top))
.Any(p => p.A != 0))
{
rect.Y--;
rect.Height++;
}
else if (rect.Bottom < compositeImage.Height
&& xRange
.Select(bx => compositeImage.GetPixel(bx, rect.Bottom))
.Any(p => p.A != 0))
{
rect.Height++;
}
else if (rect.Left >= 0
&& yRange
.Select(by => compositeImage.GetPixel(rect.Left, by))
.Any(p => p.A != 0))
{
rect.X--;
rect.Width++;
}
else if (rect.Right < compositeImage.Width
&& yRange
.Select(by => compositeImage.GetPixel(rect.Right, by))
.Any(p => p.A != 0))
{
rect.Width++;
}
else
{
foundBounds = true;
}
}
result.Add(rect);
}
}
}
return result;
}

Related

Check if lines forming a loop

Suppose I have a simple class with properties of a vertical line on a 2D Cartesian coordinate plane such as this:
public class VerticalLine
{
int x, y1, y2;
...
}
Assume that Y1 can be smaller or larger than Y2, but Y1 and Y2 never contain the same value.
In runtime, I will gather a collection(List) of vertical lines in similar format:
List<VerticalLine> lines = new List<VerticalLine>
{
new VerticalLine() { X=12, Y1=3, Y2=15 },
new VerticalLine() { X=23, Y1=23, Y2=5 },
new VerticalLine() { X=32, Y1=12, Y2=10 },
new VerticalLine() { X=37, Y1=15, Y2=12 },
...
};
Note that there are no specific order of the lines in the list.
By drawing imaginary horizontal lines from one end of vertical line to an end of another line, I need to check that if the lines in the above list will be able to connect and form a loop. In other words, pick a line in list, connect one end to an end of another line using a horizontal line, and then to next line and so on... until the last line connect back to the original line. See image for example.
Note that:
ALL the lines in the list must be used.
The order of the lines list is not important in finding the loop. Any line can be the first line.
The directions of lines are not important in finding the loop, meaning Y2 of one line may try to connect to Y1 or Y2 of the next line, as long as they share the same Y value.
In simple words, my problem is how to write a method to check if lines can form a loop.
My first thought of doing so will be using a recursive method to exhaustively try connecting the next line. But this way will involve a large amount of list manipulations, which can be slow. I wonder if there are 'better' way to achieve the same goal.
This is a working solution I have so far. It is simple but probably not the most efficient way of doing so. It is just a recursive and exhaustive method.
public bool CheckForLoop (List<VerticalLine> lines)
{
if (lines == null || lines.Count < 2)
return false;
VerticalLine head = lines[0];
return CheckForLoop(head.Y1, head.Y2, lines.GetRange(1, lines.Count - 1));
}
// helper method for recursion
private bool CheckForLoop(int start, int last, List<VerticalLine> remaining)
{
if (remaining.Count == 1)
{
return remaining[0].Y1 == start && remaining[0].Y2 == last || remaining[0].Y2 == start && remaining[0].Y1 == last;
}
foreach (var next in remaining)
{
bool foundMatching = false;
int newLast = 0;
if (next.Y1 == last)
{
foundMatching = true;
newLast = next.Y2;
} else if (next.Y2 == last)
{
foundMatching = true;
newLast = next.Y1;
}
if (foundMatching)
{
List<VerticalLine> remaining2 = new List<VerticalLine>(remaining);
remaining2.Remove(next);
if (CheckForLoop(start, newLast, remaining2))
{
return true;
}
}
}
return false;
}
What I don't like about this is that it requires a separate helper method for the recursion. I would be best if everything is done within the one method itself.

C# Loop to scan top,bottom,left,right elements of 2d Array

I am trying to complete a game over condition for a peg solitaire game i am writing.
I have all of the movements and pieces removing as they should.
Each piece is held within a 2d array as an Ellipse UI Ellement and when a piece is taken it is replaced with a border Ui Element.
I have started to use a method adapted from a Stackoverflow post that scans the 8 adjacent squares around the array element.
public static IEnumerable<T> AdjacentElements<T>(T[,] arr, int row, int column)
{
int rows = arr.GetLength(0);
int columns = arr.GetLength(1);
for (int j = row - 1; j <= row + 1; j++)
for (int i = column - 1; i <= column + 1; i++)
if (i >= 0 && j >= 0 && i < columns && j < rows && !(j == row && i == column))
yield return arr[j, i];
}
}
The method called once a piece is taken.
var results = AdjacentElements(grid, 3, 3);
foreach (var result in results)
Debug.WriteLine(result);
When it encounters an ellipse it should check the squares directly above,below,left and right of the Ellipse, at the moment is all 8 squares, i only need four (top, bottom, left and right).
I am using grid reference 3,3 to test at the moment and it is printing out as expected but for all 8 squares.
If any of the four squares in turn encounter and ellipse the next square in a straight line should be a Border in order to be a possible move.
For example:
Circle 1 is the Ellipse being checked.
The circles below,left and right are ignored.
The Cirle 2 is chosen as Square 3 is empty.
This would produce a valid move so the game would continue.
If no valid move is found the game will end.
I not sure how to proceed with this, I was thinking of putting the method call inside a nested for loop to go over every element but I'm thinking it would be very inefficient.
var results = AdjacentElements(grid, i, j);
foreach (var result in results)
//condition here
I don't think i truly understand what you want to do. However, yes you could do nested loops. but sometimes its just easier to poke at it
Given some array x, y
var x = 23;
var y = 3;
Exmaple
var checks = List<Point>();
checks.Add(new Point(x+1,y));
checks.Add(new Point(x-1,y));
checks.Add(new Point(x,y+1));
checks.Add(new Point(x,y-1));
foreach(var check in checks)
//If Bounds check
//do what you need to do

How to convert this block to for loop

foreach (Point p in Snake.Body)
if (p.X == Food.Point.X && p.Y == Food.Point.Y)
{
Points++;
Food = new FoodSpawn();
Snake.Grow(Points + 4);
}
The error when I use it is = "Collection was modified; enumeration operation may not execute"
I assume that you want to find if the Snake.Body collection of Points contains the Food.Point, change your properties and then stop the loop. (Otherwise you could have serious problem in correctly traversing your collection and incrementing its size)
So, assumming that Body is an array of Point
for(int x = 0; x < Snake.Body.Length; x++)
{
Point p = Snake.Body[x];
if (p.X == Food.Point.X && p.Y == Food.Point.Y)
{
Points++;
Food = new FoodSpawn();
Snake.Grow(Points + 4);
break;
}
}
If Snake.Body is a List<Point> then we need to change the for loop to
for(int x = 0; x < Snake.Body.Count(); x++)
Or using LINQ
int cnt = Snake.Body.Count(x => x.X == Food.Point.X && p.Y == Food.Point.Y);
if(cnt != 0)
{
Points++;
Food = new FoodSpawn();
Snake.Grow(Points + 4);
}
The simplest way would be
foreach (Point p in Snake.Body.ToArray())
The problem in your case is that you're trying to work on the total number of points in the body of the snake,
foreach (Point p in Snake.Body)
but at the end of this loop (or in the middle) the points alter. They increase or decrease. Once they do, they cause this error in the enumeration. In your code, this point
Points++;
It is increasing the points, and thus triggering the error. And after this line of code,
Snake.Grow(Points + 4);
It is making the Snake object grow, to the points provided. This is the very place, where the error is. Actually not the error, but instead this is the place which in invoking the error to trigger at the next loop.
So, what you can do would be to create a simple int variable to and then work on it.
First, as mentioned, you're trying to alter your collection (making your snake grow) during an enumeration - which is not allowed, because it leaves the enumerator not knowing where to continue from or (worse) missing something that would have been added earlier in the iteration.
A better way to approach this particular case might be to perform a check, and then execute your growth logic after that check, or perform logic based in the .Any function of Linq
if(Snake.Body.Any(p => p.X == Food.Point.X && p.Y == Food.Point.Y))
{
Points++;
Food = new FoodSpawn();
Snake.Grow(Points + 4);
}
or, if you want feed to occur multiple times in the same loop (multiple foods available) use the .Where function
foreach(var p in Snake.Body
.Where(p => p.X == Food.Point.X && p.Y == Food.Point.Y)
.ToArray()) // This caches the result and avoids those pesky errors
{ ... }
This will prevent modification until enumeration

I have a list with over a million objects in it, trying to find the fastest way to search through it

I have a list that stores well over a million objects within it. I need to look through the list and update the objects found through the below query as efficiently as possible.
I was thinking of using a Dictionary or HashSet, but I'm relatively new to C# and could not figure out how to implement these other two approaches. My current code is simply a LINQ statement searching through an IList.
public IList<LandObject> landObjects = new List<LandObject>();
var lObjsToUpdate = from obj in landObjects
where
obj.position.x >= land.x - size.x &&
obj.position.x <= land.x + size.x &&
obj.position.y >= land.y - size.y &&
obj.position.y <= land.y + size.y
select obj;
foreach(var item in lObjsToUpdate)
{
//do what I need to do with records I've found
}
Could anyone be so kind as to suggest how I might approach this efficiently?
The real answer should involve performance tests and comparisons, and depends on your runtime environment (memory vs. cpu, etc).
The first thing I would try, since land and size are constant, you can maintain a simple HashSet<LandObject> of objects that fit the criteria (in addition to a list or set of all objects or just all other objects). Every time a new object is added, check if it fits the criteria and if so - add it to that set of objects. I don't know how good HashSet is at avoiding collisions when working with object references, but you can try to measure it.
BTW, this related question discussing memory limits of HashSet<int> might interest you. With .NET < 4.5 your HashSet should be ok up to several million items. From what I understand, with some configuration .NET 4.5 removes the limitation of 2gb max object size and you'll be able to go crazy, assuming you have a 64-bit machine.
One thing that will probably help with that many iterations is do the calculations once and use different variables inside your query. Also it should help some to increase the range by 1 on each end and eliminate checking for equals:
public IList<LandObject> landObjects = new List<LandObject>();
float xmax = land.x + size.x + 1;
float xmin = land.x - size.x - 1;
float ymax = land.y + size.y + 1;
float ymin = land.y - size.y - 1;
var lObjsToUpdate = from obj in landObjects
where
obj.position.x > xmin &&
obj.position.x < xmax &&
obj.position.y > ymin &&
obj.position.y < ymax
select obj;
Ideally you need a way to partition elements so that you don't have to test every single one to find the ones that fit and the ones that should be thrown out. How you partition will depend on how dense the items are - it might be as simple as partitioning on the integer portion of the X coordinate for instance, or by some suitable scaled value of that coordinate.
Given a method (let's call it Partition for now) that takes an X coordinate and returns a partition value for it, you can filter on the X coordinate fairly quickly as a first-pass to reduce the total number of nodes you need to check. You might need to play with the partition function a little to get the right distribution though.
For example, say that you have floating-point coordinates in the range -100 < X <= 100, with your 1,000,000+ objects distributed fairly uniformly across that range. That would divide the list into 200 partitions of (on average) 5000 entries if partitioned on integer values of X. That means that for every integer step in the X dimension of your search range you only have ~5,000 entries to test.
Here's some code:
public interface IPosition2F
{
float X { get; }
float Y { get; }
}
public class CoordMap<T> where T : IPosition2F
{
SortedDictionary<int, List<T>> map = new SortedDictionary<int,List<T>>();
readonly Func<float, int> xPartition = (x) => (int)Math.Floor(x);
public void Add(T entry)
{
int xpart = xPartition(entry.X);
List<T> col;
if (!map.TryGetValue(xpart, out col))
{
col = new List<T>();
map[xpart] = col;
}
col.Add(entry);
}
public T[] ExtractRange(float left, float top, float right, float bottom)
{
var rngLeft = xPartition(left) - 1;
var rngRight = xPartition(right) + 1;
var cols =
from keyval in map
where keyval.Key >= rngLeft && keyval.Key <= rngRight
select keyval.Value;
var cells =
from cell in cols.SelectMany(c => c)
where cell.X >= left && cell.X <= right &&
cell.Y >= top && cell.Y <= bottom
select cell;
return cells.ToArray();
}
public CoordMap()
{ }
// Create instance with custom partition function
public CoordMap(Func<float, int> partfunc)
{
xPartition = partfunc;
}
}
That will partition on the X coordinate, reducing your final search space. If you wanted to take it a step further you could also partition on the Y coordinate... I'll leave that as an exercise for the reader :)
If your parition function is very finely grained and could result in a large number of partitions, it might be useful to add a ColumnRange function similar to:
public IEnumerable<List<T>> ColumnRange(int left, int right)
{
using (var mapenum = map.GetEnumerator())
{
bool finished = mapenum.MoveNext();
while (!finished && mapenum.Current.Key < left)
finished = mapenum.MoveNext();
while (!finished && mapenum.Current.Key <= right)
{
yield return mapenum.Current.Value;
finished = mapenum.MoveNext();
}
}
}
The ExtractRange method can then use that like so:
public T[] ExtractRange(float left, float top, float right, float bottom)
{
var rngLeft = xPartition(left) - 1;
var rngRight = xPartition(right) + 1;
var cells =
from cell in ColumnRange(rngLeft, rngRight).SelectMany(c => c)
where cell.X >= left && cell.X <= right &&
cell.Y >= top && cell.Y <= bottom
select cell;
return cells.ToArray();
}
I used SortedDictionary for convenience, and because it makes it possible to do an ExtractRange method that is reasonably quick. There are other container types that are possibly better suited to the task.

Implementing the Bentley-Ottmann algorithm

I am having some trouble correctly implementing the Bentley-Ottmann algorithm in C#. I am trying to implement it according to the pseudocode here. I have posted my main code below. Assuming my BST and PriorityQueue classes are implemented correctly, do you see any problems with the code?
There are no errors, but not all intersection points are found, only some. My guess is that there's an error in the else part of the code (when the current event is an intersection point). I'm not sure what the pseudocode means by swapping the position of two segments in the BST. Is the way I do it fine? Because in the end, the two aren't really swapped in the BST. I can't just change their positions either, because that could break the BST properties.
Also, am I right in assuming that segments are ordered in the BST by the Y-coordinate of their left endpoint?
Another error I've noticed that I can't seem to be able to track down is that sometimes the point (0, 0) gets into eventList. (0, 0) is outputted by Geometry.Intersects in case there is no intersection, but in that case the if conditions should stop it from getting added. I have no idea how it gets in. If I print the contents of eventList after adding a point in, (0, 0) never shows up. If I print the contents after extracting and popping an element, (0, 0) sometimes shows up. Could this have anything to do with the Pop() method messing with the references, or is it definitely a problem in my PriorityQueue implementation?
If needed I can show my implementations for the BST and priority queue as well.
static class BentleyOttman
{
private static void AddIntersectionEvent(PriorityQueue eventList, Segment segEv, Segment segA, SegPoint i)
{
i.IntersectingSegments = new Tuple<Segment, Segment>(segEv, segA);
i.Type = SegmentPointType.IntersectionPoint;
eventList.Add(i);
}
public static void Solve(Panel surface, TextBox debug)
{
debug.Clear();
var segList = Generator.SegList;
PriorityQueue eventList = new PriorityQueue();
foreach (Segment s in segList)
{
eventList.Add(new SegPoint(s.A, s, SegmentPointType.LeftEndpoint));
eventList.Add(new SegPoint(s.B, s, SegmentPointType.RightEndpoint));
}
BST sweepLine = new BST();
while (!eventList.Empty)
{
SegPoint ev = eventList.Top();
eventList.Pop();
if (ev.Type == SegmentPointType.LeftEndpoint)
{
Segment segEv = ev.Segment;
sweepLine.Insert(segEv);
Segment segA = sweepLine.InorderPre(segEv);
Segment segB = sweepLine.InorderSuc(segEv);
SegPoint i = new SegPoint();
if (segA != null && Geometry.Intersects(segEv, segA, out i.Point))
{
AddIntersectionEvent(eventList, segA, segEv, i);
}
if (segB != null && Geometry.Intersects(segEv, segB, out i.Point))
{
AddIntersectionEvent(eventList, segEv, segB, i);
}
}
else if (ev.Type == SegmentPointType.RightEndpoint)
{
Segment segEv = ev.Segment;
Segment segA = sweepLine.InorderPre(segEv);
Segment segB = sweepLine.InorderSuc(segEv);
sweepLine.Remove(segEv);
SegPoint i = new SegPoint();
if (segA != null && segB != null && Geometry.Intersects(segA, segB, out i.Point))
{
AddIntersectionEvent(eventList, segA, segB, i);
}
}
else
{
Generator.DrawPoint(ev.Point, surface, Brushes.Red);
Segment seg1 = ev.IntersectingSegments.Item1;
Segment seg2 = ev.IntersectingSegments.Item2;
sweepLine.Remove(seg1);
sweepLine.Remove(seg2);
Segment t = new Segment(seg1);
seg1 = new Segment(seg2);
seg2 = new Segment(t);
sweepLine.Insert(seg1);
sweepLine.Insert(seg2);
Segment segA = sweepLine.InorderPre(seg2);
Segment segB = sweepLine.InorderSuc(seg1);
SegPoint i = new SegPoint();
if (segA != null && Geometry.Intersects(seg2, segA, out i.Point))
AddIntersectionEvent(eventList, segA, seg2, i);
if (segB != null && Geometry.Intersects(seg1, segB, out i.Point))
AddIntersectionEvent(eventList, seg1, segB, i);
}
}
}
}
I really cannot understand your code without some idea of what exactly the other classes do, but I can answer some of your other questions.
The segments are ordered in the BST by the Y coordinate of their intersection with the sweep line. So when we encounter a left endpoint we add the segment to the tree using the y coordinate of the left endpoint of the entering segment (comparing it with the Y coordinate of the intersection of the other segment with the sweep line). When we encounter a right endpoint we remove the segment from the tree. When we encounter an intersection, then the order of the intersections of the two segments with the sweep line switches, so we swap the two segments in the tree. For example consider the two segments
A = {(-1,1),(1,-1)} and
B = {(-1,-1),(1,1)}
When the X coordinate of the sweep line is less than 0 then the intersection of segment A with the sweep line is greater than the intersection of segment B with the sweep line. and if the sweep line is greater than 0 the reverse is true. (Draw a picture.)
It is probably instructive to draw a simple example, and trace what is going on step by step, drawing the sweep line for each event and labeling the segments in columns between the events, then keeping track of the BST and verifying that the BST keeps the same order as the segments in the region where it is valid. (I'm sorry if that is not as clear as it could be.)
Note: This assumes that your segments are in "general position", i.e. that no segment is vertical, no more than two segments intersect at a given point, etc. Dealing with segments not in general position is outlined on the wikipedia page

Categories