My goal is to detect the different regions within a simple drawing constructed of various lines. Please click the following link to view a visual example of my goal for clarification. I am of course able to get the position of the drawn lines, but since one line can cross multiple 'regions' I don't think this information alone will be sufficient.
Any ideas, suggestions or points to other websites are welcome. I am using C# in combination with WPF - I am not certain which search words might lead to an answer to this problem. I did come across this shape checker article from AForge, but it seems to focus on detecting shapes that are already there, not so much on regions that still have to be 'discovered'. As a side note, I hope to find a solution that works not only with rectangles but also with other types of shapes.
Thank you very much in advance.
Update:
foreach (Line canvasObject in DrawingCanvas.Children.OfType<Line>())
{
LineGeometry lineGeometry1 = new LineGeometry();
lineGeometry1.StartPoint = new Point(canvasObject.X1, canvasObject.Y1);
lineGeometry1.EndPoint = new Point(canvasObject.X2, canvasObject.Y2);
if (canvasObject.X1 != canvasObject.X2) {
foreach (Line canvasObject2 in DrawingCanvas.Children.OfType<Line>()) {
if (canvasObject.X1 == canvasObject2.X1 && canvasObject.X2 == canvasObject2.X2 &&
canvasObject2.Y1 == canvasObject2.Y2 && canvasObject.Y2 == canvasObject2.Y2) {
return;
// prevent the system from 'colliding' the same two lines
}
LineGeometry lineGeometry2 = new LineGeometry {
StartPoint = new Point(canvasObject2.X1, canvasObject2.Y1),
EndPoint = new Point(canvasObject2.X2, canvasObject2.Y2)
};
if (lineGeometry1.FillContainsWithDetail(lineGeometry2).ToString() != "Empty") {
//collision detected
Rectangle rectangle = new Rectangle {
Width = Math.Abs(canvasObject.X2 - canvasObject.X1),
Height = 20,
Fill = Brushes.Red
};
//rectangle.Height = Math.Abs(canvasObject.Y2 - canvasObject.Y1);
DrawingCanvas2.Children.Add(rectangle);
Canvas.SetTop(rectangle, canvasObject.Y1);
Canvas.SetLeft(rectangle, canvasObject.X1);
}
}
}
}
I have experimented with the following code - to give you an impression of how I tried to tackle this problem. Initially I thought I had found a partial solution, by checking for collision between lines. Unfortunately I just created a second line of each line (which of course collided 'with itself'). After I added a simple if check (see below) this no longer occurs, but now I don't get any collisions anymore.. so will probably need a new technique.
Update 2:
After some more digging and searching the internet for solutions, I have a new potential solution in mind. Hopefully this can also be of use to anyone looking for answers in the future. Using a flood-fill algorithm I am able to 'fill' each region with a specific color - much like the paint bucket tool in an image editing application. Summarized, this done by taking a 'screenshot' of the Canvas element, starting at a certain pixel and expanding over and over until a pixel with a different color is found (these would be the lines). It works pretty well and is able to return an image with the various regions. However - my current problem is accessing these regions as 'objects' in C#/WPF. I would like to draw the regions myself (using polyobject or something similar?) - making it possible to use the objects for further calculations or interactions.
I have tried saving the position of the smallest and largest X and Y positions in the FloodFill algorithm after each pixel check, but this makes the algorithm work very very slow. If anyone has an idea, I would love to know. :)
Related
I am using NetTopologySuite with C# to filter points inside the precise boundaries of a country with a pretty simple way:
var path = "fr.shp"; // "big" country and boundaries need to be precise in my case
var reader = new ShapeDataReader(path);
var mbr = reader.ShapefileBounds;
var result = reader.ReadByMBRFilter(mbr);
var polygons = new List<Geometry>();
using (var coll = result.GetEnumerator())
{
while (coll.MoveNext())
{
var item = coll.Current;
if (item == null)
{
continue;
}
polygons.Add(item.Geometry);
}
}
var polygon = new GeometryCombiner(polygons).Combine();
var points = new List<Point>();
List<Point> pointsToFilterWithBorders; // loaded from DB, not visible here but we have 1,350,000 points to filter
Parallel.ForEach(pointsToFilterWithBorders, point =>
{
if (polygon.Contains(point))
points.Add(point);
});
It's working fine (filtering works great!) but it's pretty slow... like one day to do the filtering for only 1,350,000 points!
Any idea on how to improve that?
I tried to use Parallel.ForEach but still very long and I tried to find something like a "batch" compare in NetTopologySuite but couldn't find a quicker solution to filter my points in this big shapefile...
Presumably the polygon that defines the border is quite large and detailed, otherwise it should not take such a long time. There are a few approaches I would consider
Do an initial Bounding box check
Create an axis aligned bounding box for the polygon. Start by testing if the point is inside this box before continuing with any more complex check. This should be very easy to implement.
Render the polygon
create a large bitmap and render your polygon to the bitmap, you will need so use some kind of transform to translate between GIS coordinate and pixel coordinates. Then you can simply check the pixel value for each point in the bitmap. Just make sure to disable anti aliasing. Note that this would be an approximate solution.
You could also do something like rendering the polygon in one color and then rendering the border in another color using a pen more than one pixel wide. Any points on the border would be uncertain and may need a more accurate test, while any points outside the border should be guaranteed to be either inside or outside your polygon.
Use a tree structure to speed up the check
The typical approach to speed up any kind of repeated search is to build some kind of search structure to speedup the search, this is often a tree. In this specific case I believe a Binary Space Partition tree might be suitable. The advantage here is that the number of checks needed would be O(log n) where n is the number of lines in your polygons, instead of the O(n) that I suspect the .Contains() method is.
But I have never implemented a BSP tree, so I refer to other sources on how to implement one.
You could also consider simplifying your border polygon, but it might be more difficult to ensure the simplified polygon is either fully contained by the true polygon, or fully contains the true polygon.
Note that most methods assume that you are using a planar coordinate system, so you might need to do some conversions if you are using anything else.
If possible use spatial capabilities of your database and only load points into your set that possibly intersect with your MultiPolygon, i.e. they are withing the bounding box of it.
Then for 1:n geometry checks use NetTopologySuite's PreparedGeometry predicates:
// polygon and pointsToFilterWithBorders from sample above
var prep = NetTopologySuite.Geometries.Prepared.PreparedGeometryFactory.Prepare(polygon);
var points = new System.Collections.Generic<Point>();
foreach(var pt in pointsToFilterWithBorders)
if (prep.Contains(pt)) points.Add(pt);
Thanks everyone for the help! At the end, I found a solution inspired by your ideas :-)
I can't share the code itself because it's inside a closed source code but here is the idea:
My code was already grouping all the points by squares of 1 kmĀ² for other purposes: I just had to check if these polygons where not fully inside the boundaries. When that was not the case, I just had to do a check on all points inside these squares!
Still a little bit long (but less than one hour for France and it's mixed with other pieces of code which were already a little bit slow) but clearly quicker!
This question is quite difficult for me to explain, so I'll be illustrating with some images as well as text.
For a steel engraving machine I need to use .NET's normal graphics framework to create a "document" that is sent to the engraving machine - it is treated just like a normal printer. The machine in question is this one:
http://www.rolanddga.com/products/impactprinters/mpx90/features.asp
I can print a text-outline on it in C# with this:
// ALL UNITS ARE SET IN MILIMETERS (MM)
Graphics g = <instantiated from my printers-printpage-event>;
// The following values are set as "constants" here for the purpose of my question
// they normally are passed as parameters
string s = "ABC";
float fontSize = 4.0F;
RectangleF r = new RectangleF(0, 30.0F, 100.0F, 40.0F);
StringFormat sfDraw = new StringFormat();
sfDraw.Alignment = StringAlignment.Center;
FontStyle fStyle = FontStyle.Regular;
using (var gpDraw = new GraphicsPath())
{
gpDraw.AddString(text, fFamily, (int)fStyle, fSize, r, sfDraw);
SolidBrush brushFG = new SolidBrush(Color.Black);
Pen pen = new Pen(brushFG, 0.01F);
g.DrawPath(pen, gpDraw);
}
It gives an output similar to this: http://i47.tinypic.com/mruu4j.jpg
What I want now is to fill this outline. Not simply with a brush-fill (as can easily be accomplished with g.FillPath(brushFG, gpDraw).
It should instead be "filled" with smaller and smaller outlines, like shown on this image: http://i46.tinypic.com/b3kb29.png
(the different line colors are only used to make the example clearer).
As I made the example in Photoshop, I realized that what I am actually trying to do is to mimmick the functionality, that you find in Photoshop's Select/Modify/Contract.
But I am at my wit's end as to how I accomplish this.
Any help? I'm not looking for a complete solution, but I am at the moment completely stuck. I've tried simple scaling, which probably is the wrong way (since it does not produce the right result...)
UPDATE 2012-07-16: I am now using the Clipper Library http://www.angusj.com/delphi/clipper.php which has a wonderful function called OffsetPolygons.
My test-code is shown here: http://pastie.org/4264890
It works fine with "single" polygons - e.g. a "C" since it only consists of a single polygon. An "O" consist of two polygons - an inside and outside. Likewise with "A". And these give me some trouble. See these images:
C: http://i46.tinypic.com/ap304.png
O: http://i45.tinypic.com/35k60xg.jpg
A: http://i50.tinypic.com/1zyaibm.png
B: http://i49.tinypic.com/5lbb40.png
You get the picture (heh heh... ;-)
I think the problem is, that I extract everything from GraphicsPath as a single polygon, when there are actually 2 (in the case of A and O), and 3 in the case of a B.
Clipper's OffsetPolygons actually takes an array of polygons, so I guess it is able to do this right. But I don't know how to extract my paths from GraphicsPath as seperate polygons.
UPDATE 2012-07-16 (later in the day):
Okay I've actually managed to pull it off now, and will explain it in an answer, in the hope that it might help others with similar problems.
And a big thank you to everybody who helped along the way! Only reason that I accept my own answer is so that others might benefit from this question with a full-baked solution.
Take a look at An algorithm for inflating/deflating (offsetting, buffering) polygons -- the questioner there is actually asking about the reverse operation, but the answers there apply to your case as well. One of them (the highest rated) has a pointer to an open source library that has a C# version.
The usual name for the operation you describe is "polygon offsetting", by the way.
Using the Clipper library was only half of the battle.
I extracted all points from GraphicsPath in a single array, thus inadvertently creating a misshapen polygon based on 2 seperate polygons (in the case of "A").
Instead I needed to examine the PointTypes array property on GraphicsPath. Everytime a point has a PointType == 0 it means the beginning of a new polygon. So the extracting method should use this and instead return an array of polygons instead of just a single polygon:
private ClipperPolygons graphicsPathToPolygons(GraphicsPath gp)
{
ClipperPolygons polyList = new ClipperPolygons();
ClipperPolygon poly = null;
for (int i = 0; i < gp.PointCount; i++)
{
PointF p = gp.PathPoints[i];
byte pType = gp.PathTypes[i];
if (pType == 0)
{
if (poly != null)
polyList.Add(poly);
poly = new ClipperPolygon();
}
IntPoint ip = new IntPoint();
ip.X = (int)(p.X * pointScale);
ip.Y = (int)(p.Y * pointScale);
poly.Add(ip);
}
if (poly != null)
polyList.Add(poly);
return polyList;
}
Clipper's OffsetPolygons actually WANTS a list of polygons, so this ought to have been obvious to me earlier.
The entire code can be seen here: http://pastie.org/4265265
And if you're curious, I've zipped the entire test-project here to open in Visual Studio and compile.
http://gehling.dk/wp-content/uploads/2012/07/TestClipper.zip
It has not been optimized for speed in any way.
/ Carsten
I am simulating a thermal camera effect. I have a webcam at a party pointed at people in front of a wall. I went with background subtraction technique and using Aforge blobcounter I get blobs that I want to fill with gradient coloring. My problem = GetBlobsEdgePoints doesn't return sorted point cloud so I can't use it with, for example, PathGradientBrush from GDI+ to simply draw gradients.
I'm looking for simple,fast, algorithm to trace blobs into path (can make mistakes).
A way to track blobs received by blobcounter.
A suggestion for some other way to simulate the effect.
I took a quick look at Emgu.CV.VideoSurveillance but didn't get it to work (examples are for v1.5 and I went with v2+) but I gave up because people say it's slow on forums.
thanks for reading.
sample code of aforge background removal
Bitmap bmp =(Bitmap)e.VideoFrame.Clone();
if (backGroundFrame == null)
{
backGroundFrame = (Bitmap)e.VideoFrame.Clone();
difference.OverlayImage = backGroundFrame;
}
difference.ApplyInPlace(bmp);
bmp = grayscale.Apply(bmp);
threshold.ApplyInPlace(bmp);
Well, could you post some sample image of the result of GetBlobsEdgePoints, then it might be easier to understand what types if image processing algorithms are needed.
1) You may try a greedy algorithm, first pick a point at random, mark that point as "taken", pick the closest point not marked as "taken" and so on.
You need to find suitable termination conditions. If there can be several disjunct paths you need to find out a definition of how far away points need to be to be part of disjunct paths.
3) If you have a static background you can try to create a difference between two time shifted images, like 200ms apart. Just do a pixel by pixel difference and use abs(diff) as index in your heat color map. That will give more like an edge glow effect of moving objects.
This is the direction i'm going to take (looks best for now):
Define a set of points on the blob by my own logic (color of skin blobs should be warmer etc..)
draw gradients around those points
GraphicsPath gp=new GraphicsPath();
var rect = new Rectangle(CircumferencePoint.X - radius, CircumferencePoint.Y - radius, radius*2, radius*2);
gp.AddEllipse(rect);
GradientShaper = new PathGradientBrush(gp);
GradientShaper.CenterColor = Color.White;
GradientShaper.SurroundColors = surroundingColors;
drawBmp.FillPath(GradientShaper,gp);
mask those gradients with blob shape
blobCounter.ExtractBlobsImage(bmp,blob,true);
mask.OverlayImage = blob.Image;
mask.ApplyInPlace(rslt);
colorize with color remapping
tnx for the help #Albin
I have a set of points, drawn by the user. They will be drawing around some objects.
I need to somehow turn this set of points into a shape, so I can find the area to detect collisions.
An image will clarify:
Set of points represented as shape http://www.imagechicken.com/uploads/1277188630025178800.jpg
.
The best idea I have had so far involves iterating over every pixel determining if it is 'inside' or 'outside' the shape, but that would be horribly slow, and I'm not even sure how to do the determining 'inside'/'outside' bit...
Any hints? I am using .NET (C# and XNA) if that helps you help me!
You can think of your shape as an union of several shapes each of which is a simple closed polygon.
the check for every object if it is inside any of the polygons in the following manner:
All dots connected by lines - each line has an equation defining it.
For every object - build an equation for a line passing through this object.
now - for each object equation you need to check how many lines (those between the dots) intersects this object equation - but count only the intersection points that are in the rage between the two dots (and not in the rest of the line outside the two dots) and only the intersection points that are in one side of the object (pick a side - doesn't matter).
If the count is even - the object is outside the shape - otherwise it is inside.
Just a precursor to anything I will say, I have no experience in this field, this is just how I would go about the problem.
A tactic a lot of games use for this is known as Hit Boxes. It is much easier to detect if a point is inside a square than any other figure. But this doesn't give you an exact collision, it could be right outside your desired object.
I've seen Collision 'Bubbles' used before. Here is a link I found for you. This explains the use of Collision Bubbles in the console game Super Smash Brothers.
Given a point, the distance formula, and a radius, you can easily implement collision bubbles.
To take it even one step forward, I did a little bit of research, I saw a nifty little algorithm (more advanced that the top two suggestions), the "Gilbert-Johnson-Keerthi Collision detection algorithm for convex objects." Here is a link for ya. The implementation provided is written in D. If your working in C# it shouldn't be too hard to translate (I would highly suggest digesting the algorithm too).
Hope this gives you some direction.
Well I got it working thanks to some help on another forum.
I used the GraphicsPath class to do all the hard work for me.
This is what my method ended up looking like:
public bool IsColliding(Vector2 point)
{
GraphicsPath gp = new GraphicsPath();
Vector2 prevPoint = points[0];
for (int i = 1; i < points.Count; i++)
{
Vector2 currentPoint = points[i];
gp.AddLine(prevPoint.X, prevPoint.Y, currentPoint.X, currentPoint.Y);
prevPoint = currentPoint;
}
gp.CloseFigure(); //closing line segment
return gp.IsVisible(point.X, point.Y);
}
Thanks for your suggestions both of you
Edit:
Simple code I used to solve the problem in case anyone is interested (thanks to Fredrik):
int windowOverlap(Rectangle rect1, Rectangle rect2)
{
if (rect1.IntersectsWith(rect2))
{
Rectangle overlap = Rectangle.Intersect(rect1, rect2);
if (overlap.IsEmpty)
return overlap.Width * overlap.Height;
}
return 0;
}
Original Question:
I'd like to know a quick and dirty way to check if two rectangles overlap and if they do calculate the area of the overlap. For curiosities sake I'm interested in the case where 1) all the lines in both rectangles are either vertical or horizontal or 2) the general case for any two rectangles, but the only answer I really need is case 1.
I'm thinking along the lines of:
double areaOfOverlap( Rect A, Rect B)
{
if ( A.Intersects(B) )
{
// calculate area
// return area
}
return 0;
}
For A.Intersects() I was thinking of using the separating axis test, but if the rectangles have only horizontal and vertical lines is there an even simpler (faster) way to check?
And for calculating the area where they intersect is there an quick way to do it if the rectangles only horizontal and vertical lines?
Finally, this is unrelated to the question but I'd appreciate any advice someone may have on a good book / webpage where I could review the math for computer graphics. I've been out of college for a while and feel like I'm forgetting everything :)! Anyone else have that problem?
( NOTE: I found this question different than this which seems more complicated and doesn't directly answer the question. )
Maybe I misinterpret your question, but doesn't the Rectangle.Intersect method do the job? It returns the intersecting area, and then you can easily calculate the area of it.
Sounds like basic Collision Detection. Have you looked at this page on Wikipedia?
Mike
edit: Fredrik make his response at the same time I made this one, his answer got my upvote ( :