我有一张卫星图像,我想获得所有果岭区域。在实践中,我需要从 bmp 加载图像,选择一种颜色和容差并获得许多多边形,这些多边形是照片中的绿色区域。我如何在 C# 中做到这一点?(我需要这个用于飞行模拟)
3 回答
好吧,第一步是确定给定像素是否在该区域内。我认为这很简单。然后,您可以创建“开”或“关”的像素区域。
然后你需要将像素转换为多边形。如何做到这一点取决于您需要的粒度。如果您想要高精度,您可以使用Marching Squares从您的区域获取多边形。如果您需要简单的多边形,您将需要更高级的方法来追踪边界。
唔。听起来像“魔棒”算法(来自具有该名称的 PhotoShop/PSP 中的控件,允许您单击一个像素以选择某个颜色阈值内的所有相邻像素)。
因此,第一步是在位图上选择一个被标识为“绿色”的像素,该像素应该是多边形的一部分。然后,您可以从该点递归地向左、向右、向上和向下移动,并测试该点的像素是否在您从原始像素颜色设置的阈值范围内。如果该点在阈值内且尚未在集合中,则将其添加到集合中,并继续遍历;如果该点不够“绿色”,或者已经被映射,则返回。有一些方法可以通过限制后续递归调用可以遍历的方向来限制“回溯”。例如,假设我们打了四个电话,向上、向下、向左和向右移动。从“原点”向左传输的呼叫只能从该点开始向左或向上传输的进一步呼叫。
现在你有了一组像素,大致对应一组几何点。然后,您必须确定定义多边形边界的这些点的子集。这被称为计算这些点的“凸包”,维基百科有许多可以用 C# 实现的算法:http ://en.wikipedia.org/wiki/Convex_hull_algorithms 。
最容易理解的可能是格雷厄姆扫描:将所有点排列成一个列表,从第一个点(A)开始,画一条线到第二个点(B),然后判断从B点到第三个点(C)的直线是否构成“左转” ” 或从 A 到 B 的方向“右转”。如果是“左转”,请通过绘制从 B 到 C 的线来转弯,然后像以前一样将该线与从 C 到 D 的线进行比较. 如果是“右转”,那么忘记 B 作为凸包的可能顶点,从 A 画到 C,然后检查 C 到 D 的线是否是左转。每当您看到“右转”时,请忽略定义线的三个点的当前“中间点”,而是在其他两个点之间画一条线。继续,将列表从最后一点绕回 A,直到这些点定义了一系列线,这些线都从最后一条线的方向“左转”。这是点集的“凸包”,它可以在 NlogN 时间内对任何点列表完成。
明白“凸壳”就是这样;您永远无法从中获得凹形(如星星)。如果这很重要,您将需要调整算法以允许一些“右转”但不允许任何线段交叉。
使用 LockBits 并遍历每个字节(取决于像素格式 - 索引图像使用调色板,因此您需要首先询问它以获得在容差范围内的调色板索引 - 对于非索引(和非 1bpp/16ppgreyscale),您可以访问直接颜色通道 - 请参阅GDI+ FAQ以获取帮助)。您范围内的每个彩色像素都可以直接写入另一个图像(即只有您想要的像素 - 其余像素填充不可见像素 - Alpha 0) - 或写入集合。我个人会先做前者。这非常快(如果您使用 LockBits)。然后是使用像素行走算法的边缘检测来计算“碎片”(如果您愿意,可以使用不规则多边形)。AForge 库可能会在这里为您提供帮助。