我想做什么:
我想从多边形中获得最少量的点,这将创建相同的多边形:
例如,如果我有这个多边形:
(0,0),(1,0),(2,0),(3,0),(4,0),(0,4),(4,4)
它将创建一个多边形,其位置为0,0,宽度为4,高度为4。
如果我要将此多边形输入到假设算法,它将返回:
(0,0),(4,0),(0,4),(4,4)
为什么我要这样做:
我正在创建一个游戏,游戏中有动画,每个动画都有自己的图像和多边形(图像的边界),我已经有了动画的图像,但我没有多边形当然,我可以自己创建多边形但是为100多张图像手动创建多边形会很费劲,而不是谈论添加/修改动画。
我尝试了什么:
我的想法是:
逐个像素地扫描图像,检查像素是否为空白,如果不是,则将其添加到列表中,一旦完成,使用某种算法获取最少量的点来创建相同的多边形。
我做了一些研究,我认为LLTS(Long Live The Square)算法就是我所需要的,所以我在C#中使用cansik的implementation编写了这段代码:< / p>
private readonly Bitmap _image;
private Point[] _result;
private void Calculate()
{
List<Vector2D> points = new List<Vector2D>();
for (int x = 0; x < _image.Width; x++)
{
for (int y = 0; y < _image.Height; y++)
{
// Check if the pixel is blank
if (_image.GetPixel(x, y).ToArgb() != 16777215)
{
// If the pixel isn't blank, add it to the list
points.Add(new Vector2D(x, y));
}
}
}
Vector2D[] resultInVectors = GeoAlgos.MonotoneChainConvexHull(points.ToArray());
_result = new Point[resultInVectors.Length];
for (int i = 0; i < resultInVectors.Length; i++)
{
_result[i] = new Point((int)resultInVectors[i].X, (int)resultInVectors[i].Y);
}
}
我添加了油漆代码:
private void Form_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawPolygon(Pens.Black, _result);
e.Graphics.DrawImage(_image, new Point(100, 0));
}
最后,我运行程序,这就是我得到的:
这并不是我想到的,至少可以说,我预计它会是这样的:
有什么想法吗?
编辑 - 最终解决了这个问题
我使用Wowa对Trevor Elliott的question的回答,然后,我通过使用我创建的函数最小化了结果中的点数:
private static List<Point> MinimizePoints(List<Point> points)
{
if (points.Count < 3)
{
return points;
}
List<Point> minimumPoints = new List<Point>(points);
for (int i = minimumPoints.Count - 1; i > 2; i -= 3)
{
List<Point> currentPoints = minimumPoints.GetRange(i - 3, 3);
try
{
if ((currentPoints[2].X - currentPoints[0].X) / (currentPoints[1].X - currentPoints[0].X) ==
(currentPoints[2].Y - currentPoints[0].Y) / (currentPoints[1].Y - currentPoints[0].Y))
{
minimumPoints.Remove(minimumPoints[i + 1]);
}
}
catch (DivideByZeroException)
{
// Ignore
}
}
return minimumPoints;
}
我使用Oliver Charlesworth对Prashant C的question的答案。
第二次编辑 - 更优化的解决方案
我没有使用自己的meh算法来减少点数,而是使用Ramer-Douglas-Peucker算法并将ε(容差)设置为0.这是我使用的实现:
private static class DouglasPeuckerReduction
{
public static Point[] ReducePoints(Point[] existingPolygon)
{
if (existingPolygon == null || existingPolygon.Length < 3)
return existingPolygon;
int firstPoint = 0;
int lastPoint = existingPolygon.Length - 1;
List<int> pointIndexsToKeep = new List<int>();
//Add the first and last index to the keepers
pointIndexsToKeep.Add(firstPoint);
pointIndexsToKeep.Add(lastPoint);
//The first and the last point cannot be the same
while (existingPolygon[firstPoint].Equals(existingPolygon[lastPoint]))
{
lastPoint--;
}
ReducePoints(existingPolygon, firstPoint, lastPoint,
0, ref pointIndexsToKeep);
pointIndexsToKeep.Sort();
return pointIndexsToKeep.Select(index => existingPolygon[index]).ToArray();
}
/// <summary>
/// Douglases the peucker reduction.
/// </summary>
/// <param name="points">The points.</param>
/// <param name="firstPoint">The first point.</param>
/// <param name="lastPoint">The last point.</param>
/// <param name="tolerance">The tolerance.</param>
/// <param name="pointIndexesToKeep">The point index to keep.</param>
private static void ReducePoints(IReadOnlyList<Point> points, int firstPoint, int lastPoint, double tolerance,
ref List<int> pointIndexesToKeep)
{
double maxDistance = 0;
int indexFarthest = 0;
for (int index = firstPoint; index < lastPoint; index++)
{
double distance = PerpendicularDistance
(points[firstPoint], points[lastPoint], points[index]);
if (distance > maxDistance)
{
maxDistance = distance;
indexFarthest = index;
}
}
if (maxDistance > tolerance && indexFarthest != 0)
{
//Add the largest point that exceeds the tolerance
pointIndexesToKeep.Add(indexFarthest);
ReducePoints(points, firstPoint,
indexFarthest, tolerance, ref pointIndexesToKeep);
ReducePoints(points, indexFarthest,
lastPoint, tolerance, ref pointIndexesToKeep);
}
}
/// <summary>
/// The distance of a point from a line made from point1 and point2.
/// </summary>
/// <param name="pt1">The PT1.</param>
/// <param name="pt2">The PT2.</param>
/// <param name="p">The p.</param>
/// <returns></returns>
private static double PerpendicularDistance
(Point Point1, Point Point2, Point Point)
{
//Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle
//Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle*
//Area = .5*Base*H *Solve for height
//Height = Area/.5/Base
double area = Math.Abs(.5 * (Point1.X * Point2.Y + Point2.X *
Point.Y + Point.X * Point1.Y - Point2.X * Point1.Y - Point.X *
Point2.Y - Point1.X * Point.Y));
double bottom = Math.Sqrt(Math.Pow(Point1.X - Point2.X, 2) +
Math.Pow(Point1.Y - Point2.Y, 2));
double height = area / bottom * 2;
return height;
}
}
答案 0 :(得分:1)
我认为@ShashwatKumar的答案误解了你的问题?如果没有,那我就误解了!
我阅读你的帖子的方式,你正在寻找你的人物的多边形轮廓。这称为跟踪二进制图像的轮廓/轮廓,其第一步, 正如@EugeneKomisarenko在评论中所说,是&#34;边缘检测&#34; (但那时 是进一步的步骤)。 搜索这些短语的变体会影响许多算法,例如:
<小时/>