如下图所示,我可以用什么算法来检测区域1和区域2(由颜色标识)是否有边框?
http://img823.imageshack.us/img823/4477/borders.png
如果那里有一个C#示例,那就太棒了,但我真的只是在寻找任何示例代码。
编辑:使用Jaro的建议,我想出了以下内容......
public class Shape
{
private const int MAX_BORDER_DISTANCE = 15;
public List<Point> Pixels { get; set; }
public Shape()
{
Pixels = new List<Point>();
}
public bool SharesBorder(Shape other)
{
var shape1 = this;
var shape2 = other;
foreach (var pixel1 in shape1.Pixels)
{
foreach (var pixel2 in shape2.Pixels)
{
var xDistance = Math.Abs(pixel1.X - pixel2.X);
var yDistance = Math.Abs(pixel1.Y - pixel2.Y);
if (xDistance > 1 && yDistance > 1)
{
if (xDistance * yDistance < MAX_BORDER_DISTANCE)
return true;
}
else
{
if (xDistance < Math.Sqrt(MAX_BORDER_DISTANCE) &&
yDistance < Math.Sqrt(MAX_BORDER_DISTANCE))
return true;
}
}
}
return false;
}
// ...
}
单击两个共享边框的形状返回相当快,但非常距离的形状或具有大量像素的形状有时需要3秒以上。我有什么选择来优化它?
答案 0 :(得分:0)
2个有边框的区域意味着在某个小区域内应该有3种颜色:红色,黑色和绿色。
因此,一个非常无效的解决方案呈现出来:
使用Color pixelColor = myBitmap.GetPixel(x, y);
您可以扫描区域中的这3种颜色。当然,该区域必须大于边界的宽度。
当然有足够的优化空间(比如以50像素的步长并不断降低精度)。 由于黑色是最少使用的颜色,因此您首先会搜索黑色区域。
这应该解释我在本主题的各种评论中所写的内容:
namespace Phobos.Graphics
{
public class BorderDetector
{
private Color region1Color = Color.FromArgb(222, 22, 46);
private Color region2Color = Color.FromArgb(11, 189, 63);
private Color borderColor = Color.FromArgb(11, 189, 63);
private List<Point> region1Points = new List<Point>();
private List<Point> region2Points = new List<Point>();
private List<Point> borderPoints = new List<Point>();
private Bitmap b;
private const int precision = 10;
private const int distanceTreshold = 25;
public long Miliseconds1 { get; set; }
public long Miliseconds2 { get; set; }
public BorderDetector(Bitmap b)
{
if (b == null) throw new ArgumentNullException("b");
this.b = b;
}
private void ScanBitmap()
{
Color c;
for (int x = precision; x < this.b.Width; x += BorderDetector.precision)
{
for (int y = precision; y < this.b.Height; y += BorderDetector.precision)
{
c = this.b.GetPixel(x, y);
if (c == region1Color) region1Points.Add(new Point(x, y));
else if (c == region2Color) region2Points.Add(new Point(x, y));
else if (c == borderColor) borderPoints.Add(new Point(x, y));
}
}
}
/// <summary>Returns a distance of two points (inaccurate but very fast).</summary>
private int GetDistance(Point p1, Point p2)
{
return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y);
}
/// <summary>Finds the closests 2 points among the points in the 2 sets.</summary>
private int FindClosestPoints(List<Point> r1Points, List<Point> r2Points, out Point foundR1, out Point foundR2)
{
int minDistance = Int32.MaxValue;
int distance = 0;
foundR1 = Point.Empty;
foundR2 = Point.Empty;
foreach (Point r1 in r1Points)
foreach (Point r2 in r2Points)
{
distance = this.GetDistance(r1, r2);
if (distance < minDistance)
{
foundR1 = r1;
foundR2 = r2;
minDistance = distance;
}
}
return minDistance;
}
public bool FindBorder()
{
Point r1;
Point r2;
Stopwatch watch = new Stopwatch();
watch.Start();
this.ScanBitmap();
watch.Stop();
this.Miliseconds1 = watch.ElapsedMilliseconds;
watch.Start();
int distance = this.FindClosestPoints(this.region1Points, this.region2Points, out r1, out r2);
watch.Stop();
this.Miliseconds2 = watch.ElapsedMilliseconds;
this.b.SetPixel(r1.X, r1.Y, Color.Green);
this.b.SetPixel(r2.X, r2.Y, Color.Red);
return (distance <= BorderDetector.distanceTreshold);
}
}
}
很简单。以这种方式搜索只需要 2 + 4 ms (扫描并找到最近的点)。
你也可以递归地进行搜索:首先是精度= 1000,然后是精度= 100,最后精度= 10表示大图像。 FindClosestPoints实际上会给你一个估计的矩形区域,边界应该位于该区域(通常边界就是这样)。
然后你可以使用我在其他评论中描述的矢量方法。
答案 1 :(得分:0)
我把你的问题看作是问这两个点是否存在于不同的地区。它是否正确?如果是这样,我可能会使用Flood Fill的变体。它实现起来并不困难(不要递归地实现它,你几乎肯定会耗尽堆栈空间)并且它将能够看到复杂的情况,比如在>之间的边界的U形区域。 em>两点,但实际上并不是不同的区域。基本上运行泛洪填充,并在您的坐标与目标坐标匹配时返回true(或者当它足够接近以满足您的用例时,返回true)
[编辑]这是我为我的一个项目写的洪水填充an example。该项目是CPAL许可的,但实现非常具体到我用它的方式,所以不要担心复制它的一部分。并且它不使用递归,因此它应该能够缩放到像素数据。
[编辑2]我误解了这个任务。我没有任何示例代码可以完全满足您的需求,但我可以说,比较每像素像素并不是您想要做的事情。您可以通过将每个区域划分为更大的网格(可能是25x25像素)来降低复杂性,并首先比较这些扇区,如果其中任何一个足够接近,则在这两个扇区内进行像素/像素比较。
[Edit2.5] [四叉树] 3也可以帮到你。我没有很多经验,但我知道它在2D碰撞检测中很受欢迎,这与你在这里做的很相似。可能值得研究。