C# graphics drawlines draws broken lines - c#

I am drawing ECG graphs using C# Graphics. I draw the curve using drawlines method. however line joints look broken. I tried all available options of smoothing mode and capstyle, none helps. here is sample graph1 and sample graph2
code is below:
private void DrawCurve(Graphics g, cPoint[] data)
{
List<Point> ps = new List<Point>();
for (int i = 0; i < data.Length - 1; i++)
{
int x = data[i].x;
int y = data[i].y;
if (x > 0 && x < (Width))
{
ps.Add(new Point(x, y));
}
else if (x > Width)
{
using (Pen p = new Pen(Color.Yellow))
{
if (ps.Count > 0)
{
g.DrawLines(p, ps.ToArray());
ps.Clear();
}
}
}
}
}

To avoid broken lines especially when the lines are drawn at sharp angles you need to choose the right values for these Properties:
p.MiterLimit = p.Width * 1.25f;
p.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
The MiterLimit has a default of 10f, which is way to big for thin lines!
The LineJoin also has a default (Miter) that does not help.
You should also experiment a little with the MiterLimit value (keep in the range of the Pen's width) and maybe also with your Pen's width itself; do note that Pen.Width is a float so you could raise it to 1.25 or so..
If you are actually talking about the smudgy look at some spots, this is due to anti-aliasing; usually a good thing, but for crisper lines turn it off for your Graphics object:
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None
The LineCaps are only for the start- and the end-point of the lines-sequence, so they do not matter much for your graph.

You are drawing short lines. This is why you are getting that result. Instead of DrawLines(), try to use DrawCurve().
g.DrawCurve(p, ps.ToArray());

Related

Zoomable, printable, scrollable train movement graphs in c#

I need to build a graphic train schedule visualisation tool in C#. Actually I have to rebuild this perfect tool in C#.
Marey's Trains
The graphs have to be zoomable, scrollable and printable/exportable to PDF with vector graphical elements.
Could you give me some tips? How should I start it? What sort of libraries should I use?
Is it worth to try using graphing libraries like OxyPlot? Maybe it's not the best because of the special axes and irregular grids - as I think. What's your opinion?
No matter which chart tool you use, once you need special types of display you will always have to add some extra coding.
Here is an example of using the MSChart control.
Do note that it has a limitation wrt to exporting vector formats:
It can export to various formats, including 3 EMF types; however only some application can actually use those. Not sure about the PDF libary you use..!
If you can't use the Emf formats you can get nice results by making the Chart control really big, exporting to Png and then making the Dpi resolution much larger the the default screen resolution it has after saving.. Setting it to 600 or 1200dpi should do for most pdf uses..
Now lets look at an example:
A few notes:
I have made my life easier in a number of ways. I have only coded for one direction and I have not reversed the rooster, so it goes only bottom to top.
I have not used real data but made them up.
I have not created one or more classes to hold the station data; instead I use a very simple Tuple.
I have not created a DataTable to hold the train data. Instead I make them up and add them to the chart on the fly..
I didn't test, but zooming and scrolling should work as well..
Here is the List<Tuple> that holds my station data:
// station name, distance, type: 0=terminal, 1=normal, 2=main station
List<Tuple<string, double, int>> TrainStops = null;
Here is how I set up the chart:
Setup24HoursAxis(chart1, DateTime.Today);
TrainStops = SetupTrainStops(17);
SetupTrainStopAxis(chart1);
for (int i = 0; i < 23 * 3; i++)
{
AddTrainStopSeries(chart1, DateTime.Today.Date.AddMinutes(i * 20),
17 - rnd.Next(4), i% 5 == 0 ? 1 : 0);
}
// this exports the image above:
chart1.SaveImage("D:\\trains.png", ChartImageFormat.Png);
This creates one train every 20 minutes with 14-17 stops and every 5th train a fast one.
Here are the routines I call:
Setting up the x-axis for hold one day's worth of data is straightforward.
public static void Setup24HoursAxis(Chart chart, DateTime dt)
{
chart.Legends[0].Enabled = false;
Axis ax = chart.ChartAreas[0].AxisX;
ax.IntervalType = DateTimeIntervalType.Hours;
ax.Interval = 1;
ax.Minimum = dt.ToOADate();
ax.Maximum = (dt.AddHours(24)).ToOADate();
ax.LabelStyle.Format = "H:mm";
}
Creating a List of stations with random distances is also very simple. I made the 1st and last ones terminals and every 5th a main station.
public List<Tuple<string, double, int>> SetupTrainStops(int count)
{
var stops = new List<Tuple<string, double, int>>();
Random rnd = new Random(count);
for (int i = 0; i < count; i++)
{
string n = (char)(i+(byte)'A') + "-Street";
double d = 1 + rnd.Next(3) + rnd.Next(4) + rnd.Next(5) / 10d;
if (d < 3) d = 3; // a minimum distance so the label won't touch
int t = (i == 0 | i == count-1) ? 0 : rnd.Next(5)==0 ? 2 : 1;
var ts = new Tuple<string, double, int>(n, d, t);
stops.Add(ts);
}
return stops;
}
Now that we have the train stops we can set up the y-axis:
public void SetupTrainStopAxis(Chart chart)
{
Axis ay = chart.ChartAreas[0].AxisY;
ay.LabelStyle.Font = new Font("Consolas", 8f);
double totalDist = 0;
for (int i = 0; i < TrainStops.Count; i++)
{
CustomLabel cl = new CustomLabel();
cl.Text = TrainStops[i].Item1;
cl.FromPosition = totalDist - 0.1d;
cl.ToPosition = totalDist + 0.1d;
totalDist += TrainStops[i].Item2;
cl.ForeColor = TrainStops[i].Item3 == 1 ? Color.DimGray : Color.Black;
ay.CustomLabels.Add(cl);
}
ay.Minimum = 0;
ay.Maximum = totalDist;
ay.MajorGrid.Enabled = false;
ay.MajorTickMark.Enabled = false;
}
A few notes are called for here:
As the values are quite dynamic we can't use normal Labels which would come with the fixed Interval spacing.
So we create CustomLabels instead.
For these we need two values to determine the space into which they shall be centered. So we create a small span by adding/subtracting 0.1d.
We have calculated the total distance and use it to set up the Maximum of the y-axis. Again: To mimick the schedule you show you will have to do some reversing here and there..
By adding CustomLabels the normal ones are turned off automatically. As we need MajorGridlines at the irregular intervals we also turn the normal ones off. Hence we must draw them ourselves. Not really hard as you can see..:
For this we code one of the xxxPaint events:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
Axis ay = chart1.ChartAreas[0].AxisY;
Axis ax = chart1.ChartAreas[0].AxisX;
int x0 = (int) ax.ValueToPixelPosition(ax.Minimum);
int x1 = (int) ax.ValueToPixelPosition(ax.Maximum);
double totalDist = 0;
foreach (var ts in TrainStops)
{
int y = (int)ay.ValueToPixelPosition(totalDist);
totalDist += ts.Item2;
using (Pen p = new Pen(ts.Item3 == 1 ? Color.DarkGray : Color.Black,
ts.Item3 == 1 ? 0.5f : 1f))
e.ChartGraphics.Graphics.DrawLine(p, x0 + 1, y, x1, y);
}
// ** Insert marker drawing code (from update below) here !
}
Note the use of the ValueToPixelPosition conversion functions of the axes!
Now for the last part: How to add a Series of train data..:
public void AddTrainStopSeries(Chart chart, DateTime start, int count, int speed)
{
Series s = chart.Series.Add(start.ToShortTimeString());
s.ChartType = SeriesChartType.Line;
s.Color = speed == 0 ? Color.Black : Color.Brown;
s.MarkerStyle = MarkerStyle.Circle;
s.MarkerSize = 4;
double totalDist = 0;
DateTime ct = start;
for (int i = 0; i < count; i++)
{
var ts = TrainStops[i];
ct = ct.AddMinutes(ts.Item2 * (speed == 0 ? 1 : 1.1d));
DataPoint dp = new DataPoint( ct.ToOADate(), totalDist );
totalDist += TrainStops[i].Item2;
s.Points.Add(dp);
}
}
Note that since my data don't contain real arrival/departure times I calculated them from the distance and some speed factor. You, of course, would use your data!
Also note that I have used a Line chart with extra Marker circles.
Also note that each train series can easily be disabled/hidden or brought back again.
Let's show only the fast trains:
private void cbx_ShowOnlyFastTrains_CheckedChanged(object sender, EventArgs e)
{
foreach (Series s in chart1.Series)
s.Enabled = !cbx_ShowOnlyFastTrains.Checked || s.Color == Color.Brown;
}
Of course for a robust application you will not rely ona magic color ;-)
Instead you could add a Tag object to the Series to hold all sorts of train info.
Update: As you noticed the drawn GridLines cover the Markers. You can insert this piece of code here (**); it will owner-draw the Markers at the end of the xxxPaint event.
int w = chart1.Series[0].MarkerSize;
foreach(Series s in chart1.Series)
foreach(DataPoint dp in s.Points)
{
int x = (int) ax.ValueToPixelPosition(dp.XValue) - w / 2;
int y = (int) ay.ValueToPixelPosition(dp.YValues[0])- w / 2;
using (SolidBrush b = new SolidBrush(dp.Color))
e.ChartGraphics.Graphics.FillEllipse(b, x, y, w, w);
}
Close-up:

Plotting 2D heat map

I have a chart on which I want to plot a heat map; the only data I have is humidity and temperature, which represent a point in the chart.
How do I get the rectangular type of heat map on the chart in c#?
What I want is similar to picture below :
What I really want is a rectangular region in the chart which is plotted in different color based on the point that i get from the list of points and form the colorful section in the chart.
You have a choice of at least three ways to create a chart with colored rectangles that make up a heat map.
Here is one example
that uses/abuses a DataGridView. While I would not suggest this, the post contains a useful function that creates nice color lists to use in your task.
Then there is the option to draw the chart using GDI+ methods, namely Graphics.FillRectangle. This not hard at all but once you want to get those nice extras a Chart control offers, like scaling, axes, tooltips etc the work adds up.. See below!
So let's have a look at option three: Using the Chart control from the DataVisualization namespace.
Let's first assume that you have created a list of colors:
List<Color> colorList = new List<Color>();
And that you have managed to project your data onto a 2D array of int indices that point into the color list:
int[,] coloredData = null;
Next you have to pick a ChartType for your Series S1 There really is only one I can think of that will help:
S1.ChartType = SeriesChartType.Point;
Points are displayed by Markers. We want the DataPoints not really displayed as one of the standard MarkerTypes.
Square would be ok, if we wanted to display squares; but for rectangles it will not work well: Even if we let them overlap there will still be points at the borders that have a different size because they don't fully overlap..
So we use a custom marker by setting the MarkerImage of each point to a bitmap of a suitable size and color.
Here is a loop that adds the DataPoints to our Series and sets each to have a MarkerImage:
for (int x = 1; x < coloredData.GetLength(0); x++)
for (int y = 1; y < coloredData.GetLength(1); y++)
{
int pt = S1.Points.AddXY(x, y);
S1.Points[pt].MarkerImage = "NI" + coloredData[x,y];
}
This takes some explaining: To set a MarkerImage that is not at a path on the disk, it has to reside in the Chart's Images collection. This means is needs to be of type NamedImage. Any image will do, but it has to have a unique name string added to identify it in the NamedImagesCollection . I chose the names to be 'NI1', 'NI2'..
Obviously we need to create all those images; here is a function to do that:
void createMarkers(Chart chart, int count)
{
// rough calculation:
int sw = chart.ClientSize.Width / coloredData.GetLength(0);
int sh = chart.ClientSize.Height / coloredData.GetLength(1);
// clean up previous images:
foreach(NamedImage ni in chart1.Images) ni.Dispose();
chart.Images.Clear();
// now create count images:
for (int i = 0; i < count; i++)
{
Bitmap bmp = new Bitmap(sw, sh);
using (Graphics G = Graphics.FromImage(bmp))
G.Clear(colorList[i]);
chart.Images.Add(new NamedImage("NI" + i, bmp));
}
}
We want all markers to have at least roughly the right size; so whenever that size changes we set it again:
void setMarkerSize(Chart chart)
{
int sx = chart1.ClientSize.Width / coloredData.GetLength(0);
int sy = chart1.ClientSize.Height / coloredData.GetLength(1);
chart1.Series["S1"].MarkerSize = (int)Math.Max(sx, sy);
}
This doesn't care much about details like the InnerPlotPosition, i.e. the actual area to draw to; so here is some room for refinement..!
We call this when we set up the chart but also upon resizing:
private void chart1_Resize(object sender, EventArgs e)
{
setMarkerSize(chart1);
createMarkers(chart1, 100);
}
Let's have a look at the result using some cheap testdata:
As you can see resizing works ok..
Here is the full code that set up my example:
private void button6_Click(object sender, EventArgs e)
{
List<Color> stopColors = new List<Color>()
{ Color.Blue, Color.Cyan, Color.YellowGreen, Color.Orange, Color.Red };
colorList = interpolateColors(stopColors, 100);
coloredData = getCData(32, 24);
// basic setup..
chart1.ChartAreas.Clear();
ChartArea CA = chart1.ChartAreas.Add("CA");
chart1.Series.Clear();
Series S1 = chart1.Series.Add("S1");
chart1.Legends.Clear();
// we choose a charttype that lets us add points freely:
S1.ChartType = SeriesChartType.Point;
Size sz = chart1.ClientSize;
// we need to make the markers large enough to fill the area completely:
setMarkerSize(chart1);
createMarkers(chart1, 100);
// now we fill in the datapoints
for (int x = 1; x < coloredData.GetLength(0); x++)
for (int y = 1; y < coloredData.GetLength(1); y++)
{
int pt = S1.Points.AddXY(x, y);
// S1.Points[pt].Color = coloredData[x, y];
S1.Points[pt].MarkerImage = "NI" + coloredData[x,y];
}
}
A few notes on limitations:
The point will always sit on top of any gridlines. If you really needs those you will have to draw them on top in one of the the Paint events.
The labels as shown are referring to the integers indices of the data array. If you want to show the original data, one way would be to add CustomLabels to the axes.. See here for an example!
This should give you an idea of what you can do with a Chart control; to complete your confusion here is how to draw those rectangles in GDI+ using the same colors and data:
Bitmap getChartImg(float[,] data, Size sz, Padding pad)
{
Bitmap bmp = new Bitmap(sz.Width , sz.Height);
using (Graphics G = Graphics.FromImage(bmp))
{
float w = 1f * (sz.Width - pad.Left - pad.Right) / coloredData.GetLength(0);
float h = 1f * (sz.Height - pad.Top - pad.Bottom) / coloredData.GetLength(1);
for (int x = 0; x < coloredData.GetLength(0); x++)
for (int y = 0; y < coloredData.GetLength(1); y++)
{
using (SolidBrush brush = new SolidBrush(colorList[coloredData[x,y]]))
G.FillRectangle(brush, pad.Left + x * w, y * h - pad.Bottom, w, h);
}
}
return bmp;
}
The resulting Bitmap looks familiar:
That was simple; but to add all the extras into the space reserved by the padding will not be so easy..

"Parameter must be positive and < Height. Parameter name: y" error

I am developing a component for Grasshopper 3D, which is a Rhino (architecture) plugin.
Using the Render() method, this draws a heatmap image onto the canvas. Isolating my other methods and constructors, I highly believe this method is causing my issue.
protected override void Render(Grasshopper.GUI.Canvas.GH_Canvas canvas, Graphics graphics, Grasshopper.GUI.Canvas.GH_CanvasChannel channel) {
// Render the default component.
base.Render(canvas, graphics, channel);
// Now render our bitmap if it exists.
if (channel == Grasshopper.GUI.Canvas.GH_CanvasChannel.Wires) {
KT_HeatmapComponent comp = Owner as KT_HeatmapComponent;
if (comp == null)
return;
List<HeatMap> maps = comp.CachedHeatmaps;
if (maps == null)
return;
if (maps.Count == 0)
return;
int x = Convert.ToInt32(Bounds.X + Bounds.Width / 2);
int y = Convert.ToInt32(Bounds.Bottom + 10);
for (int i = 0; i < maps.Count; i++) {
Bitmap image = maps[i].Image;
if (image == null)
continue;
Rectangle mapBounds = new Rectangle(x, y, maps[i].Width * 10, maps[i].Height * 10);
mapBounds.X -= mapBounds.Width / 2;
Rectangle edgeBounds = mapBounds;
edgeBounds.Inflate(4, 4);
GH_Capsule capsule = GH_Capsule.CreateCapsule(edgeBounds, GH_Palette.Normal);
capsule.Render(graphics, Selected, false, false);
capsule.Dispose();
// Unnecessary graphics.xxxxxx methods and parameters
y = edgeBounds.Bottom + 10;
}
}
}
The error I receive when I attempt to render things onto my canvas is:
1. Solution exception:Parameter must be positive and < Height.
Parameter name: y
From my research, it appears to happen the most when you encounter array overflows.
My research links:
http://www.codeproject.com/Questions/158055/Help-in-subtraction-of-two-images
Exception on traveling through pixels BMP C#
http://www.c-sharpcorner.com/Forums/Thread/64792/
However, the above examples apply mostly to multi-dimensional arrays, while I have a one-dimensional.
I was wondering if anyone else has had this issue before, and could give me some pointers and guidance?
Thanks.
int x = Convert.ToInt32(Bounds.X + Bounds.Width / 2);
int y = Convert.ToInt32(Bounds.Bottom + 10);
Your error is telling you that y must be less than the height, but you are setting it to 10 more than your height because you are adding to the Bounds.Bottom.
maps[i].Height * 10
You also need to make sure that your calculated Height is what you think it is and compare that y is valid against it.

Cannot draw an image using this program?

I am trying to process an image for finding out red color regions in it. I scan pixels and if they are found to be ENOUGH red, they are converted to black, otherwise white.
To speed up the process, I skip certain pixels, and need to draw blocks of black or white at their place. I am using this function but it seems to be wrong somewhere. The image I get in the end is completely blank.
public void ProcessFrame( ref Bitmap image )
{
int skip = 10;
Graphics g = Graphics.FromImage(System.Drawing.Image.FromHbitmap(image.GetHbitmap()));
SolidBrush black = new SolidBrush(Color.Black);
SolidBrush white = new SolidBrush(Color.White);
for (int i = 0; i < image.Width; i=i+skip)
{
for (int j = 0; j < image.Height; j = j + skip)
{
Color cl = image.GetPixel(i, j);
if (cl.R > (cl.G * 2) && cl.R > (cl.B * 2))
{
g.FillRectangle(black, new Rectangle(i, j, skip, skip));
}
else
{
g.FillRectangle(white, new Rectangle(i, j, skip, skip));
}
}
}
}
Can you point out the mistake, OR any other better method to achieve what I aim for?
By blank, do you mean white?
I don't know your image, but if (cl.R > (cl.G * 2) && cl.R > (cl.B * 2)) is not a good test for redness. #010000 passes that test, but is basically black. And, #ffaaaa looks red, but won't pass.
If you had a very dark image, lots of pixels might pass that test that aren't very red. With a light image, lots of red pixels won't pass.
Probably the best way is to convert the color the HSL, and then use values of H(ue) that are in the red zone, but only if S(aturation) and L(uminance) are sufficiently bright and non-gray (over a threshhold to not just be black or gray).
Try to use:
Graphics g = Graphics.FromImage(image);
Then consider other replies to improve your color testing.
You didn't show where image is created, but this code will draw into a bitmap:
var bmp = new Bitmap(200,200);
using (g = Graphics.FromImage(bmp))
{
g.FillEllipse(Brushes.Red, 10, 10, 50, 50);
}
Note that you need to call Graphics.Dispose (or use using) before using the image.
Also note that using GetPixel for image processing will be very slow. Use low-level memory access (Bitmap.LockBits) or try some image processing library for .NET (like AForge).

Algorithm for finding a painted region on a canvas

Update: I am attempting to pull a little clutter out of this post and sum it up more concisely. Please see the original edit if needed.
I am currently attempting to trace a series of single colored blobs on a Bitmap canvas.
e.g. An example of the bitmap I am attempting to trace would look like the following:
alt text http://www.refuctored.com/polygons.bmp
After successfully tracing the outlines of the 3 blobs on the image, I would have a class that held the color of a blob tied to a point list representing the outline of the blob (not all the pixels inside of the blobs).
The problem I am running into is logic in instances where a neighboring pixel has no surrounding pixels other than the previous pixel.
e.g The top example would trace fine, but the second would fail because the pixel has no where to go since the previous pixels have already been used.
alt text http://www.refuctored.com/error.jpg
I am tracing left-to-right, top-to-bottom, favoring diagonal angles over right angles. I must be able to redraw an exact copy of the region based off the data I extract, so the pixels in the list must be in the right order for the copy to work.
Thus far, my attempt has been riddled with failure, and a couple days of pulling my hair out trying to rewrite the algorithms a little different each time to solve the issue. Thus far I have been unsuccessful. Has anyone else had a similar issue like mine who has a good algorithm to find the edges?
One simple trick to avoiding these cul-de-sacs is to double the size of the image you want to trace using a nearest neighbor scaling algorithm before tracing it. Like that you will never get single strips.
The alternative is to use a marching squares algorithm - but it seems to still have one or two cases where it fails: http://www.sakri.net/blog/2009/05/28/detecting-edge-pixels-with-marching-squares-algorithm/
Have you looked at blob detection algorithms? For example, http://opencv.willowgarage.com/wiki/cvBlobsLib if you can integrate OpenCV into your application. Coupled with thresholding to create binary images for each color (or color range) in your image, you could easily find the blobs that are the same color. Repeat for each color in the image, and you have a list of blobs sorted by color.
If you cannot use OpenCV directly, perhaps the paper referenced by that library ("A linear-time component labeling algorithm using contour tracing technique", F.Chang et al.) would provide a good method for finding blobs.
Rather than using recursion, use a stack.
Pseudo-code:
Add initial pixel to polygon
Add initial pixel to stack
while(stack is not empty) {
pop pixel off the stack
foreach (neighbor n of popped pixel) {
if (n is close enough in color to initial pixel) {
Add n to polygon
Add n to stack
}
}
}
This will use a lot less memory than the same solution using recursion.
Just send your 'Image' to BuildPixelArray function and then call the FindRegions.
After that the 'colors' variable will be holding your colors list and pixel coordinates in every list member.
I've copied the source from one of my projects, there may be some undefined variables or syntax erors.
public class ImageProcessing{
private int[,] pixelArray;
private int imageWidth;
private int imageHeight;
List<MyColor> colors;
public void BuildPixelArray(ref Image myImage)
{
imageHeight = myImage.Height;
imageWidth = myImage.Width;
pixelArray = new int[imageWidth, imageHeight];
Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height);
Bitmap temp = new Bitmap(myImage);
BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int remain = bmpData.Stride - bmpData.Width * 3;
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
for (int j = 15; j < bmpData.Height; j++)
{
for (int i = 0; i < bmpData.Width; i++)
{
pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256;
ptr += 3;
}
ptr += remain;
}
}
temp.UnlockBits(bmpData);
}
public void FindRegions()
{
colors = new List<MyColor>();
for (int i = 0; i < imageWidth; i++)
{
for (int j = 0; j < imageHeight; j++)
{
int tmpColorValue = pixelArray[i, j];
MyColor tmp = new MyColor(tmpColorValue);
if (colors.Contains(tmp))
{
MyColor tmpColor = (from p in colors
where p.colorValue == tmpColorValue
select p).First();
tmpColor.pointList.Add(new MyPoint(i, j));
}
else
{
tmp.pointList.Add(new MyPoint(i, j));
colors.Add(tmp);
}
}
}
}
}
public class MyColor : IEquatable<MyColor>
{
public int colorValue { get; set; }
public List<MyPoint> pointList = new List<MyPoint>();
public MyColor(int _colorValue)
{
colorValue = _colorValue;
}
public bool Equals(MyColor other)
{
if (this.colorValue == other.colorValue)
{
return true;
}
return false;
}
}
public class MyPoint
{
public int xCoord { get; set; }
public int yCoord { get; set; }
public MyPoint(int _xCoord, int _yCoord)
{
xCoord = _xCoord;
yCoord = _yCoord;
}
}
If you're getting a stack overflow I would guess that you're not excluding already-checked pixels. The first check on visiting a square should be whether you've been here before.
Also, I was working on a related problem not too long ago and I came up with a different approach that uses a lot less memory:
A queue:
AddPointToQueue(x, y);
repeat
x, y = HeadItem;
AddMaybe(x - 1, y); x + 1, y; x, y - 1; x, y + 1;
until QueueIsEmpty;
AddMaybe(x, y):
if Visited[x, y] return;
Visited[x, y] = true;
AddPointToQueue(x, y);
The point of this approach is that you end up with your queue basically holding a line wrapped around the mapped area. This limits memory usage better than a stack can.
If relevant it also can be trivially modified to yield the travel distance to any square.
Try using AForge.net. I would go for Filter by colors, Threshold and then you could do some Morphology to decrement the black/White zones to lose contact between the objects. Then you could go for the Blobs.

Categories

Resources