在2d点集找到漏洞?

时间:2014-02-16 21:01:49

标签: c# algorithm geometry computational-geometry

我有一套2d points。它们是X,Y coordinates标准笛卡尔网格系统(在本例中为UTM zone)。我需要在该点集中找到孔,最好能够设置找到这些孔的算法的灵敏度。通常这些点集非常密集,但有些可能密度低得多。

它们是什么,如果它有帮助的话,那就是田间土壤被采样的点,农业人们显然发现它们有用。有时在这些点样品中有巨大的岩石或沼泽地或满是小湖泊和池塘。

从点集中,他们希望我找到粗略定义这些孔的凹多边形。

我已经编写了找到外部凹面边界多边形的算法,并允许它们设置由算法形成的多边形粗糙或平滑的灵敏度。在那之后,他们想找到洞并将那些洞作为凹多边形给予它们,我猜在某些情况下可能是凸的,但这将是边缘情况而非常态。

问题是我听过的关于这个问题的唯一论文是那些想要在太空中找到大空虚的天文学家完成的论文,我从来没有能够找到他们的一篇论文,并且显示了实际的算法在它们中除了粗略的概念描述之外的其他任何东西。

我已经尝试了谷歌和各种学术论文搜索等,但到目前为止我还没有找到很多有用的东西。这让我想知道是否有这个问题的名字,我不知道所以我在寻找错误的东西或什么?

所以经过那个冗长的解释之后,我的问题是:有没有人知道我应该寻找什么,最好用定义明确的算法找到这方面的论文,或者有人知道一个他们可以指出我的算法吗?

帮助我解决这个问题的任何事情都非常有用,非常感激,即使只是正确的搜索短语,也会发现潜在的算法或论文。

这将实现的语言将是C#,但是从Mathematica包到MATLAB or ASM, C, C++, Python, Java or MathCAD等的任何示例都可以,只要该示例中没有一些调用{ {1}}等。FindTheHole未定义或是实施软件的专有权,例如FindTheHole示例通常包含很多内容。

以下是两个实际点集的示例,一个是密集的,一个是稀疏的,我需要找到它们中的区域: Sparse points set example Dense points set example

8 个答案:

答案 0 :(得分:7)

这样的位图+矢量方法如何:

  1. 获取点云区域覆盖的边界框

    如果还不知道,请执行此操作。它应该简单O(N)循环所有点。

  2. 创建该区域的map[N][N]

    这是一个'位图'容易数据密度计算的区域只需从area(x,y) -> map[i][j]创建投影,例如使用简单的比例。网格大小 N也是输出的准确度必须大于平均点距离!!! 所以{{1}内的每个单元格覆盖至少有一个点的区域(如果不在孔区域内)。

  3. 计算map[][]

    的每个单元格的数据密度

    很简单,只需清除map[][](积分计数器)至map[][].cnt并按简单zero周期计算,O(N)所有map[i][j].cnt++

  4. 创建未使用区域的列表points(x,y)(map[][].cnt==0)

    为简单起见,我通过水平和垂直线来完成

  5. 细分输出

    只需将同一个孔的线组合在一起(相交的......矢量方法),也可以通过泛洪填充(位图方法)在子弹#4 中完成

  6. 输出多边形

    获取同一个洞/组的 H,V行的所有边缘点并创建多边形(对它们进行排序,使它们的连接不与任何东西相交)。关于此,有很多库,算法和源代码。

  7. 此方法的源代码:

    (map[][].cnt<=treshold)

    请忽略我的void main_compute(int N) { // cell storage for density computation struct _cell { double x0,x1,y0,y1; // bounding area of points inside cell int cnt; // points inside cell _cell(){}; _cell(_cell& a){ *this=a; }; ~_cell(){}; _cell* operator = (const _cell *a) { *this=*a; return this; }; /*_cell* operator = (const _cell &a) { ...copy... return this; };*/ }; // line storage for hole area struct _line { double x0,y0,x1,y1; // line edge points int id; // id of hole for segmentation/polygonize int i0,i1,j0,j1; // index in map[][] _line(){}; _line(_line& a){ *this=a; }; ~_line(){}; _line* operator = (const _line *a) { *this=*a; return this; }; /*_line* operator = (const _line &a) { ...copy... return this; };*/ }; int i,j,k,M=N*N; // M = max N^2 but usualy is much much less so dynamic list will be better double mx,my; // scale to map _cell *m; // cell ptr glview2D::_pnt *p; // point ptr double x0,x1,y0,y1; // used area (bounding box) _cell **map=NULL; // cell grid _line *lin=NULL; // temp line list for hole segmentation int lins=0; // actual usage/size of lin[M] // scan point cloud for bounding box (if it is known then skip it) p=&view.pnt[0]; x0=p->p[0]; x1=x0; y0=p->p[1]; y1=y0; for (i=0;i<view.pnt.num;i++) { p=&view.pnt[i]; if (x0>p->p[0]) x0=p->p[0]; if (x1<p->p[0]) x1=p->p[0]; if (y0>p->p[1]) y0=p->p[1]; if (y1<p->p[1]) y1=p->p[1]; } // compute scale for coordinate to map index conversion mx=double(N)/(x1-x0); // add avoidance of division by zero if empty point cloud !!! my=double(N)/(y1-y0); // dynamic allocation of map[N][N],lin[M] lin=new _line[M]; map=new _cell*[N]; for (i=0;i<N;i++) map[i]=new _cell[N]; // reset map[N][N] for (i=0;i<N;i++) for (j=0;j<N;j++) map[i][j].cnt=0; // compute point cloud density for (k=0;k<view.pnt.num;k++) { p=&view.pnt[k]; i=double((p->p[0]-x0)*mx); if (i<0) i=0; if (i>=N) i=N-1; j=double((p->p[1]-y0)*my); if (j<0) j=0; if (j>=N) j=N-1; m=&map[i][j]; if (!m->cnt) { m->x0=p->p[0]; m->x1=p->p[0]; m->y0=p->p[1]; m->y1=p->p[1]; } if (m->cnt<0x7FFFFFFF) m->cnt++; // avoid overflow if (m->x0>p->p[0]) m->x0=p->p[0]; if (m->x1<p->p[0]) m->x1=p->p[0]; if (m->y0>p->p[1]) m->y0=p->p[1]; if (m->y1<p->p[1]) m->y1=p->p[1]; } // find holes (map[i][j].cnt==0) or (map[i][j].cnt<=treshold) // and create lin[] list of H,V lines covering holes for (j=0;j<N;j++) // search lines { for (i=0;i<N;) { int i0,i1; for (;i<N;i++) if (map[i][j].cnt==0) break; i0=i-1; // find start of hole for (;i<N;i++) if (map[i][j].cnt!=0) break; i1=i; // find end of hole if (i0< 0) continue; // skip bad circumstances (edges or no hole found) if (i1>=N) continue; if (map[i0][j].cnt==0) continue; if (map[i1][j].cnt==0) continue; _line l; l.i0=i0; l.x0=map[i0][j].x1; l.i1=i1; l.x1=map[i1][j].x0; l.j0=j ; l.y0=0.25*(map[i0][j].y0+map[i0][j].y1+map[i1][j].y0+map[i1][j].y1); l.j1=j ; l.y1=l.y0; lin[lins]=l; lins++; } } for (i=0;i<N;i++) // search columns { for (j=0;j<N;) { int j0,j1; for (;j<N;j++) if (map[i][j].cnt==0) break; j0=j-1; // find start of hole for (;j<N;j++) if (map[i][j].cnt!=0) break; j1=j; // find end of hole if (j0< 0) continue; // skip bad circumstances (edges or no hole found) if (j1>=N) continue; if (map[i][j0].cnt==0) continue; if (map[i][j1].cnt==0) continue; _line l; l.i0=i ; l.y0=map[i][j0].y1; l.i1=i ; l.y1=map[i][j1].y0; l.j0=j0; l.x0=0.25*(map[i][j0].x0+map[i][j0].x1+map[i][j1].x0+map[i][j1].x1); l.j1=j1; l.x1=l.x0; lin[lins]=l; lins++; } } // segmentate lin[] ... group lines of the same hole together by lin[].id // segmentation based on vector lines data // you can also segmentate the map[][] directly as bitmap during hole detection for (i=0;i<lins;i++) lin[i].id=i; // all lines are separate for (;;) // join what you can { int e=0,i0,i1; _line *a,*b; for (a=lin,i=0;i<lins;i++,a++) { for (b=a,j=i;j<lins;j++,b++) if (a->id!=b->id) { // do 2D lines a,b intersect ? double xx0,yy0,xx1,yy1; double kx0,ky0,dx0,dy0,t0; double kx1,ky1,dx1,dy1,t1; double x0=a->x0,y0=a->y0; double x1=a->x1,y1=a->y1; double x2=b->x0,y2=b->y0; double x3=b->x1,y3=b->y1; // discart lines with non intersecting bound rectangles double a0,a1,b0,b1; if (x0<x1) { a0=x0; a1=x1; } else { a0=x1; a1=x0; } if (x2<x3) { b0=x2; b1=x3; } else { b0=x3; b1=x2; } if (a1<b0) continue; if (a0>b1) continue; if (y0<y1) { a0=y0; a1=y1; } else { a0=y1; a1=y0; } if (y2<y3) { b0=y2; b1=y3; } else { b0=y3; b1=y2; } if (a1<b0) continue; if (a0>b1) continue; // compute intersection kx0=x0; ky0=y0; dx0=x1-x0; dy0=y1-y0; kx1=x2; ky1=y2; dx1=x3-x2; dy1=y3-y2; t1=divide(dx0*(ky0-ky1)+dy0*(kx1-kx0),(dx0*dy1)-(dx1*dy0)); xx1=kx1+(dx1*t1); yy1=ky1+(dy1*t1); if (fabs(dx0)>=fabs(dy0)) t0=divide(kx1-kx0+(dx1*t1),dx0); else t0=divide(ky1-ky0+(dy1*t1),dy0); xx0=kx0+(dx0*t0); yy0=ky0+(dy0*t0); // check if intersection exists if (fabs(xx1-xx0)>1e-6) continue; if (fabs(yy1-yy0)>1e-6) continue; if ((t0<0.0)||(t0>1.0)) continue; if ((t1<0.0)||(t1>1.0)) continue; // if yes ... intersection point = xx0,yy0 e=1; break; } if (e) break; // join found ... stop searching } if (!e) break; // no join found ... stop segmentation i0=a->id; // joid ids ... rename i1 to i0 i1=b->id; for (a=lin,i=0;i<lins;i++,a++) if (a->id==i1) a->id=i0; } // visualize lin[] for (i=0;i<lins;i++) { glview2D::_lin l; l.p0.p[0]=lin[i].x0; l.p0.p[1]=lin[i].y0; l.p1.p[0]=lin[i].x1; l.p1.p[1]=lin[i].y1; // l.col=0x0000FF00; l.col=(lin[i].id*0x00D00C10A)+0x00800000; // color is any function of ID view.lin.add(l); } // dynamic deallocation of map[N][N],lin[M] for (i=0;i<N;i++) delete[] map[i]; delete[] map; delete[] lin; } //--------------------------------------------------------------------------- 内容(这是我的gfx几何渲染引擎)

    • glview2D是您的积分的动态列表(由随机生成)
    • view.pnt[]是动态列表输出 H,V行仅供可视化使用
    • view.lin[]是您的线路输出

    这是输出:

    holes preview

    我太懒了添加polygonize现在你可以看到分割工作(着色)。如果你还需要帮助polyize然后评论我,但我认为这不应该是任何问题。

    复杂性估计取决于整体空穴覆盖率

    但是对于大部分代码而言,它是lin[],并且用于洞搜索/分段O(N)其中:

    • ~O((M^2)+(U^2))是点数
    • N是地图网格尺寸
    • M H,V行依赖于孔...
    • U

    正如您在上面的图片上看到的M << N, U << M*M3783网格一样,它在我的设置上花了差不多30x30

    [Edit1]使用矢量多边形一点

    bordered holes

    对于简单的洞来说很好但是对于更复杂的洞来说还有一些小问题

    [Edit2]终于有了一点时间,所以这就是:

    这是一个简单的洞/多边形搜索类,以更愉快/可管理的形式:

    9ms

    您只需要将//--------------------------------------------------------------------------- class holes { public: int xs,ys,n; // cell grid x,y - size and points count int **map; // points density map[xs][ys] // i=(x-x0)*g2l; x=x0+(i*l2g); // j=(y-y0)*g2l; y=y0+(j*l2g); double mg2l,ml2g; // scale to/from global/map space (x,y) <-> map[i][j] double x0,x1,y0,y1; // used area (bounding box) struct _line { int id; // id of hole for segmentation/polygonize int i0,i1,j0,j1; // index in map[][] _line(){}; _line(_line& a){ *this=a; }; ~_line(){}; _line* operator = (const _line *a) { *this=*a; return this; }; /*_line* operator = (const _line &a) { ...copy... return this; };*/ }; List<_line> lin; int lin_i0; // start index for perimeter lines (smaller indexes are the H,V lines inside hole) struct _point { int i,j; // index in map[][] int p0,p1; // previous next point int used; _point(){}; _point(_point& a){ *this=a; }; ~_point(){}; _point* operator = (const _point *a) { *this=*a; return this; }; /*_point* operator = (const _point &a) { ...copy... return this; };*/ }; List<_point> pnt; // class init and internal stuff holes() { xs=0; ys=0; n=0; map=NULL; mg2l=1.0; ml2g=1.0; x0=0.0; y0=0.0; x1=0.0; y1=0.0; lin_i0=0; }; holes(holes& a){ *this=a; }; ~holes() { _free(); }; holes* operator = (const holes *a) { *this=*a; return this; }; holes* operator = (const holes &a) { xs=0; ys=0; n=a.n; map=NULL; mg2l=a.mg2l; x0=a.x0; x1=a.x1; ml2g=a.ml2g; y0=a.y0; y1=a.y1; _alloc(a.xs,a.ys); for (int i=0;i<xs;i++) for (int j=0;j<ys;j++) map[i][j]=a.map[i][j]; return this; } void _free() { if (map) { for (int i=0;i<xs;i++) if (map[i]) delete[] map[i]; delete[] map; } xs=0; ys=0; } void _alloc(int _xs,int _ys) { int i=0; _free(); xs=_xs; ys=_ys; map=new int*[xs]; if (map) for (i=0;i<xs;i++) { map[i]=new int[ys]; if (map[i]==NULL) { i=-1; break; } } else i=-1; if (i<0) _free(); } // scann boundary box interface void scann_beg(); void scann_pnt(double x,double y); void scann_end(); // dynamic allocations void cell_size(double sz); // compute/allocate grid from grid cell size = sz x sz // scann holes interface void holes_beg(); void holes_pnt(double x,double y); void holes_end(); // global(x,y) <- local map[i][j] + half cell offset inline void l2g(double &x,double &y,int i,int j) { x=x0+((double(i)+0.5)*ml2g); y=y0+((double(j)+0.5)*ml2g); } // local map[i][j] <- global(x,y) inline void g2l(int &i,int &j,double x,double y) { i= double((x-x0) *mg2l); j= double((y-y0) *mg2l); } }; //--------------------------------------------------------------------------- void holes::scann_beg() { x0=0.0; y0=0.0; x1=0.0; y1=0.0; n=0; } //--------------------------------------------------------------------------- void holes::scann_pnt(double x,double y) { if (!n) { x0=x; y0=y; x1=x; y1=y; } if (n<0x7FFFFFFF) n++; // avoid overflow if (x0>x) x0=x; if (x1<x) x1=x; if (y0>y) y0=y; if (y1<y) y1=y; } //--------------------------------------------------------------------------- void holes::scann_end() { } //--------------------------------------------------------------------------- void holes::cell_size(double sz) { int x,y; if (sz<1e-6) sz=1e-6; x=ceil((x1-x0)/sz); y=ceil((y1-y0)/sz); _alloc(x,y); ml2g=sz; mg2l=1.0/sz; } //--------------------------------------------------------------------------- void holes::holes_beg() { int i,j; for (i=0;i<xs;i++) for (j=0;j<ys;j++) map[i][j]=0; } //--------------------------------------------------------------------------- void holes::holes_pnt(double x,double y) { int i,j; g2l(i,j,x,y); if ((i>=0)&&(i<xs)) if ((j>=0)&&(j<ys)) if (map[i][j]<0x7FFFFFFF) map[i][j]++; // avoid overflow } //--------------------------------------------------------------------------- void holes::holes_end() { int i,j,e,i0,i1; List<int> ix; // hole lines start/stop indexes for speed up the polygonization _line *a,*b,l; _point *aa,*bb,p; lin.num=0; lin_i0=0;// clear lines ix.num=0; // clear indexes // find holes (map[i][j].cnt==0) or (map[i][j].cnt<=treshold) // and create lin[] list of H,V lines covering holes for (j=0;j<ys;j++) // search lines for (i=0;i<xs;) { int i0,i1; for (;i<xs;i++) if (map[i][j]==0) break; i0=i-1; // find start of hole for (;i<xs;i++) if (map[i][j]!=0) break; i1=i; // find end of hole if (i0< 0) continue; // skip bad circumstances (edges or no hole found) if (i1>=xs) continue; if (map[i0][j]==0) continue; if (map[i1][j]==0) continue; l.i0=i0; l.i1=i1; l.j0=j ; l.j1=j ; l.id=-1; lin.add(l); } for (i=0;i<xs;i++) // search columns for (j=0;j<ys;) { int j0,j1; for (;j<ys;j++) if (map[i][j]==0) break; j0=j-1; // find start of hole for (;j<ys;j++) if (map[i][j]!=0) break; j1=j ; // find end of hole if (j0< 0) continue; // skip bad circumstances (edges or no hole found) if (j1>=ys) continue; if (map[i][j0]==0) continue; if (map[i][j1]==0) continue; l.i0=i ; l.i1=i ; l.j0=j0; l.j1=j1; l.id=-1; lin.add(l); } // segmentate lin[] ... group lines of the same hole together by lin[].id // segmentation based on vector lines data // you can also segmentate the map[][] directly as bitmap during hole detection for (i=0;i<lin.num;i++) lin[i].id=i; // all lines are separate for (;;) // join what you can { for (e=0,a=lin.dat,i=0;i<lin.num;i++,a++) { for (b=a,j=i;j<lin.num;j++,b++) if (a->id!=b->id) { // if a,b not intersecting or neighbouring if (a->i0>b->i1) continue; if (b->i0>a->i1) continue; if (a->j0>b->j1) continue; if (b->j0>a->j1) continue; // if they do mark e for join groups e=1; break; } if (e) break; // join found ... stop searching } if (!e) break; // no join found ... stop segmentation i0=a->id; // joid ids ... rename i1 to i0 i1=b->id; for (a=lin.dat,i=0;i<lin.num;i++,a++) if (a->id==i1) a->id=i0; } // sort lin[] by id for (e=1;e;) for (e=0,a=&lin[0],b=&lin[1],i=1;i<lin.num;i++,a++,b++) if (a->id>b->id) { l=*a; *a=*b; *b=l; e=1; } // re id lin[] and prepare start/stop indexes for (i0=-1,i1=-1,a=&lin[0],i=0;i<lin.num;i++,a++) if (a->id==i1) a->id=i0; else { i0++; i1=a->id; a->id=i0; ix.add(i); } ix.add(lin.num); // polygonize lin_i0=lin.num; for (j=1;j<ix.num;j++) // process hole { i0=ix[j-1]; i1=ix[j]; // create border pnt[] list (unique points only) pnt.num=0; p.used=0; p.p0=-1; p.p1=-1; for (a=&lin[i0],i=i0;i<i1;i++,a++) { p.i=a->i0; p.j=a->j0; map[p.i][p.j]=0; for (aa=&pnt[0],e=0;e<pnt.num;e++,aa++) if ((aa->i==p.i)&&(aa->j==p.j)) { e=-1; break; } if (e>=0) pnt.add(p); p.i=a->i1; p.j=a->j1; map[p.i][p.j]=0; for (aa=&pnt[0],e=0;e<pnt.num;e++,aa++) if ((aa->i==p.i)&&(aa->j==p.j)) { e=-1; break; } if (e>=0) pnt.add(p); } // mark not border points for (aa=&pnt[0],i=0;i<pnt.num;i++,aa++) if (!aa->used) // ignore marked points if ((aa->i>0)&&(aa->i<xs-1)) // ignore map[][] border points if ((aa->j>0)&&(aa->j<ys-1)) { // ignore if any non hole cell around if (map[aa->i-1][aa->j-1]>0) continue; if (map[aa->i-1][aa->j ]>0) continue; if (map[aa->i-1][aa->j+1]>0) continue; if (map[aa->i ][aa->j-1]>0) continue; if (map[aa->i ][aa->j+1]>0) continue; if (map[aa->i+1][aa->j-1]>0) continue; if (map[aa->i+1][aa->j ]>0) continue; if (map[aa->i+1][aa->j+1]>0) continue; aa->used=1; } // delete marked points for (aa=&pnt[0],e=0,i=0;i<pnt.num;i++,aa++) if (!aa->used) { pnt[e]=*aa; e++; } pnt.num=e; // connect neighbouring points distance=1 for (i0= 0,aa=&pnt[i0];i0<pnt.num;i0++,aa++) if (aa->used<2) for (i1=i0+1,bb=&pnt[i1];i1<pnt.num;i1++,bb++) if (bb->used<2) { i=aa->i-bb->i; if (i<0) i=-i; e =i; i=aa->j-bb->j; if (i<0) i=-i; e+=i; if (e!=1) continue; aa->used++; if (aa->p0<0) aa->p0=i1; else aa->p1=i1; bb->used++; if (bb->p0<0) bb->p0=i0; else bb->p1=i0; } // try to connect neighbouring points distance=sqrt(2) for (i0= 0,aa=&pnt[i0];i0<pnt.num;i0++,aa++) if (aa->used<2) for (i1=i0+1,bb=&pnt[i1];i1<pnt.num;i1++,bb++) if (bb->used<2) if ((aa->p0!=i1)&&(aa->p1!=i1)) if ((bb->p0!=i0)&&(bb->p1!=i0)) { if ((aa->used)&&(aa->p0==bb->p0)) continue; // avoid small closed loops i=aa->i-bb->i; if (i<0) i=-i; e =i*i; i=aa->j-bb->j; if (i<0) i=-i; e+=i*i; if (e!=2) continue; aa->used++; if (aa->p0<0) aa->p0=i1; else aa->p1=i1; bb->used++; if (bb->p0<0) bb->p0=i0; else bb->p1=i0; } // try to connect to closest point int ii,dd; for (i0= 0,aa=&pnt[i0];i0<pnt.num;i0++,aa++) if (aa->used<2) { for (ii=-1,i1=i0+1,bb=&pnt[i1];i1<pnt.num;i1++,bb++) if (bb->used<2) if ((aa->p0!=i1)&&(aa->p1!=i1)) if ((bb->p0!=i0)&&(bb->p1!=i0)) { i=aa->i-bb->i; if (i<0) i=-i; e =i*i; i=aa->j-bb->j; if (i<0) i=-i; e+=i*i; if ((ii<0)||(e<dd)) { ii=i1; dd=e; } } if (ii<0) continue; i1=ii; bb=&pnt[i1]; aa->used++; if (aa->p0<0) aa->p0=i1; else aa->p1=i1; bb->used++; if (bb->p0<0) bb->p0=i0; else bb->p1=i0; } // add connected points to lin[] ... this is hole perimeter !!! // lines are 2 x duplicated so some additional code for sort the order of line swill be good idea l.id=lin[ix[j-1]].id; for (i0=0,aa=&pnt[i0];i0<pnt.num;i0++,aa++) { l.i0=aa->i; l.j0=aa->j; // [edit3] this avoid duplicating lines if (aa->p0>i0) { bb=&pnt[aa->p0]; l.i1=bb->i; l.j1=bb->j; lin.add(l); } if (aa->p1>i0) { bb=&pnt[aa->p1]; l.i1=bb->i; l.j1=bb->j; lin.add(l); } //if (aa->p0>=0) { bb=&pnt[aa->p0]; l.i1=bb->i; l.j1=bb->j; lin.add(l); } //if (aa->p1>=0) { bb=&pnt[aa->p1]; l.i1=bb->i; l.j1=bb->j; lin.add(l); } } } } //--------------------------------------------------------------------------- 模板替换为List<T>或其他(我无法共享的模板)。它是std::list ...

    的动态一维数组
    • TList<int> x;
    • 相同
    • int x[];将空项添加到x
    • x.add();将项目添加到x
    • x.add(a);清除数组
    • x.reset()预分配空间以避免运行缓慢的重新分配
    • x.allocate(size)是x []中的项目数...项目中使用的尺寸
    原始代码中的

    只是静态数组,所以如果你感到困惑,请用它来检查。

    现在该怎么用:

    x.num

    其中h.scann_beg(); for (i=0;i<view.pnt.num;i++) { p=view.pnt[i].p0.p; h.scann_pnt(p[0],p[1]); } h.scann_end(); h.cell_size(2.5); h.holes_beg(); for (i=0;i<view.pnt.num;i++) { p=view.pnt[i].p0.p; h.holes_pnt(p[0],p[1]); } h.holes_end(); 是输入点列表及其内部:view.pnt[]

    输出位于view.pnt[i].p0.p[ 2 ]= { x,y }h.lin[]其中:

    • lin_i0是内部H,V行
    • h.lin[i] i= < 0,lin_i0 )是周边

    周边线没有排序并且重复两次,所以只需对它们进行排序并删除重复项(对于它来说太懒)。 h.lin[i] i= < lin_i0,h.lin.num )lin[]是该行所属的id .. id0,1,2,3,...,而i,j位于地图内。因此,为了正确输出到您的世界坐标,请执行以下操作:

    int i,j;
    holes h;                // holes class
    double *p;              // input point list ptr
    
    h.scann_beg(); for (i=0;i<view.pnt.num;i++) { p=view.pnt[i].p0.p; h.scann_pnt(p[0],p[1]); } h.scann_end();
    h.cell_size(2.5);
    h.holes_beg(); for (i=0;i<view.pnt.num;i++) { p=view.pnt[i].p0.p; h.holes_pnt(p[0],p[1]); } h.holes_end();
    
    DWORD coltab[]=
        {
        0x000000FF,
        0x0000FF00,
        0x00FF0000,
        0x0000FFFF,
        0x00FFFF00,
        0x00FF00FF,
        0x00FFFFFF,
        0x00000088,
        0x00008800,
        0x00880000,
        0x00008888,
        0x00888800,
        0x00880088,
        0x00888888,
        };
    
    for (i=0;i<h.lin.num;i++)                   // draw lin[]
        {
        glview2D::_lin a;
        holes::_line *b=&h.lin[i];
        h.l2g(a.p0.p[0],a.p0.p[1],b->i0,b->j0);
        h.l2g(a.p1.p[0],a.p1.p[1],b->i1,b->j1);
        if (i<h.lin_i0) // H,V lines inside hole(b->id) .. gray  [edit3] was <= which is wrong and miss-color first perimeter line
            {
            a.col=0x00808080;
            }
        else{               // hole(b->id) perimeter lines ... each hole different collor
            if ((b->id>=0)&&(b->id<14)) a.col=coltab[b->id];
            if (b->id==-1) a.col=0x00FFFFFF;    // special debug lines
            if (b->id==-2) a.col=0x00AA8040;    // special debug lines
            }
        view.lin.add(a); // here draw your line or add it to your polygon instead
        }
    
    • 我的view.lin[]有成员:p0,p1,分为view.pnt[]col为彩色

    当孔太小时我只看到一个问题(diameter < 3 cells)否则就可以了

    [edit4]重新排序外围线

    这样做而不是:

            /* add connected points to lin[] ... this is hole perimeter !!!
            // lines are 2 x duplicated so some additional code for sort the order of line swill be good idea
            l.id=lin[ix[j-1]].id;
            for (i0=0,aa=&pnt[i0];i0<pnt.num;i0++,aa++)
                {
                l.i0=aa->i;
                l.j0=aa->j;
                // [edit3] this avoid duplicating lines
                if (aa->p0>i0) { bb=&pnt[aa->p0]; l.i1=bb->i; l.j1=bb->j; lin.add(l); }
                if (aa->p1>i0) { bb=&pnt[aa->p1]; l.i1=bb->i; l.j1=bb->j; lin.add(l); }
                //if (aa->p0>=0) { bb=&pnt[aa->p0]; l.i1=bb->i; l.j1=bb->j; lin.add(l); }
                //if (aa->p1>=0) { bb=&pnt[aa->p1]; l.i1=bb->i; l.j1=bb->j; lin.add(l); }
                } */
    

    这样做:

        // add connected points to lin[] ... this is hole perimeter !!!
        l.id=lin[ix[j-1]].id;
        // add index of points instead points
        int lin_i1=lin.num;
        for (i0=0,aa=&pnt[i0];i0<pnt.num;i0++,aa++)
            {
            l.i0=i0;
            if (aa->p0>i0) { l.i1=aa->p0; lin.add(l); }
            if (aa->p1>i0) { l.i1=aa->p1; lin.add(l); }
            }
        // reorder perimeter lines
        for (i0=lin_i1,a=&lin[i0];i0<lin.num-1;i0++,a++)
         for (i1=i0+1  ,b=&lin[i1];i1<lin.num  ;i1++,b++)
            {
            if (a->i1==b->i0) { a++; l=*a; *a=*b; *b=l;                                a--; break; }
            if (a->i1==b->i1) { a++; l=*a; *a=*b; *b=l; i=a->i0; a->i0=a->i1; a->i1=i; a--; break; }
            }
        // convert point indexes to points
        for (i0=lin_i1,a=&lin[i0];i0<lin.num;i0++,a++)
            {
            bb=&pnt[a->i0]; a->i0=bb->i; a->j0=bb->j;
            bb=&pnt[a->i1]; a->i1=bb->i; a->j1=bb->j;
            }
    

    [Edit5] holes::holes_end内的多边形如何工作

    作为此输入,您需要按洞和密度图lin[]划分/分组/排序的所有 H,V行 map[][]的列表。

    1. 遍历所有漏洞

      1. 循环遍历处理孔的所有H,V行

        创建所有唯一行端点pnt[]的列表(无重复项)。因此,为每一行获取2个端点,并查看每个点是否已在列表中。如果没有添加它,则忽略它。

      2. 从列表中删除所有非边界点

        通过查看密度map[][]

      3. 中的4个邻居,删除所有与非孔区域无接触的点
      4. 对点进行连通组件分析

        1. used=0; p0=-1; p1=-1;列表
        2. 中的所有点设置pnt[]
        3. 使用distance=1

          连接点

          使用pnt[]循环遍历所有点used<2,这意味着它们尚未完全使用,并且对于每个此类点,再次搜索pnt[]另一个此类点distance = 1它。这意味着它是4邻居,应该连接,以便添加连接信息到它们的展位(使用p0p1索引,这些索引一直未使用{{1} })并增加两个点的使用。

        4. 尝试使用(-1)

          连接点

          #2 几乎相同,除了现在选择8个邻居的对角线的距离。这次也避免了闭环,所以不要连接已经连接的点。

        5. 尝试连接最近点

          再次与#2,#3 几乎相同,但选择最近的点并避免闭环。

        6. distance=sqrt(2)

          形成多边形

          所以选择列表中的第一个点并将其添加到多边形。然后将连接点添加到它(无论你从哪个方向开始pnt[]p0)。然后添加其连接点(不同于先前添加的点到多边形以避免后退和前进循环)。在p1

        7. 中添加积分

答案 1 :(得分:4)

Delauney triangulation可以提供帮助。它具有在三角测量中任何三角形的外接圆内没有输入点的特性。因此,孔边界点将通过覆盖该孔的较大/较宽的三角形连接。在你的情况下,三角测量将有许多相似大小的三角形,以及一些覆盖孔的较大尺寸的三角形。可能它足以过滤较大的并连接它们以找到一个洞。

答案 2 :(得分:3)

这是我非常热心的非科学解决方案:

1 - 以最小预定义步长(dx,dy)扫描所有2D区域。对于每个步骤,coord找到可以适合的较大圆圈而没有任何内部。丢弃半径小于预定义大小的所有圆圈。

enter image description here

2 - 现在找到所有碰撞圆群,轻松测试距离和半径,在分离的列表中存储和分组。 (问一下,如果你想了解更多关于如何对它们进行分组的细节,真的很容易)

enter image description here

3 - 找到每组圆的凹面边界多边形,非常类似于算法,找到你已编写的一组点周围的凸多边形,并且矢量之间的最后一个问题角度是相关的。

enter image description here

备注

优化提示:在步骤1之前,您可以将所有点存储在网格中,以便简化距离计算并将其限制为给定圆半径的近网格方格。

精度:对于较小的扫描步长值和允许的最小圆半径,您可以获得更高的精度。

我自己没有测试过,但我确信它有效。祝你好运!

答案 3 :(得分:1)

使用Delaunay三角测量法找到Gabriel graph可能会更好。然后,您对Gabriel图进行角度排序,并进行圆形行走以生成凸多边形列表。然后,您可以按区域对这些多边形进行排序。你会对面积最大的那些感兴趣。

修改角度排序图表也是更有效的,您可以按照从A到B的路径,然后顺时针或逆时针(从角度排序)查看下一步。字典的词典可能是有用的,其定义类似于“图[A] [B] =(顺时针,逆时针)”。使用字典词典方法的示例算法(python)。

pre_graph = gabriel_graph(point)
graph = {}
for a in pre_graph:
    graph[a] = {}
    angle_sorted = sort(pre_graph[a], key=calc_angle_from(a))
    for i,b in enumerate(angle_sorted):
        clockwise = angle_sorted[(i - 1) % len(angle_sorted)]
        counterclockwise = angle_sorted[(i + 1) % len(angle_sorted)]
        graph[a][b] = (clockwise, counterclockwise)

polygons = []
for A in points:
    for B in graph[A]:
        for direction in [0,1]:
            polygon = [A]
            next_point = B:
            while next != A:
                polygon.append(next)
                next_point = graph[A][B][direction]
            if polygon[0] == min(polygon): # This should avoid duplicates
                polygons.add(polygon)

与Ante的建议结合也可能有用。

答案 4 :(得分:0)

我不知道我头脑中的任何算法,但你可能尝试的一件事(这是我在这里的第一次冲动)类似于无网格方法(如smoothed-particle hydrodynamics中的密度计算方式。

如果您可以计算空间中任何点的密度值(而不仅仅是您给出的样本点),则可以将孔的边界定义为密度函数的水平设置曲线。即这些洞是密度低于某个阈值的地方(您可能允许用户配置)。您可以使用类似marching squares的内容找到边界。

如果您想要了解这些密度插值函数的工作原理,我可以根据您的喜好提供(尽我所能和知识)。

我不知道这实际上会有多好,但希望它会给你一些指导。

答案 5 :(得分:0)

这是一个想法:

  • 对于每个点x,找到距离d(x,y)(其中yx的最近邻居)。如上定义f(x)=d(x,y)
  • 查找f(x)
  • 的均值和方差
  • 找出'异常值' - 他们的f值与平均值相差很远的点,远远超过\ alpha标准偏差。 (\ alpha是算法的参数)。
  • 这将检测“洞” - 你现在要做的就是设置每个洞的离群值。

答案 6 :(得分:0)

似乎你可以通过图像上的(二进制)数学形态来解决这个问题。

创建白色图像并绘制所有点。然后将它们“膨胀”成矩形,这些矩形大于正常的水平和垂直间距。你可以通过带有矩形结构元素的所谓侵蚀操作来实现。

这样做你将填满飞机,除了点太稀疏的地方。

您以这种方式检测到的未填充区域小于实际空隙。您将通过使用相同的结构元素应用扩张来恢复到完整大小。

两种变换组合称为开放。

http://en.wikipedia.org/wiki/Mathematical_morphology

答案 7 :(得分:0)

  • 将所有扫描点放入k-d tree
  • server.port=8081 spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/shopping_cart?useSSL=false spring.datasource.username=root spring.datasource.password= 指定为应导致检测到孔的最小半径(或某个方向上孔的最小直径的一半)。
  • 对于双嵌套循环中的rx,在图像上执行网格搜索。步长y应该是s的一半,或者甚至要小得多,这取决于您要在检测小孔(并检测孔边界上的所有点)的能力方面进行权衡)与处理时间。
    • 对于每个网格点r,使用k-d树找到最近的邻居点(x, y)
    • 如果从网格点到最近的邻居点的距离大于或等于(nx, ny),则网格点在孔内,而最近的邻居在孔的边界上。这样标记这些点。

然后可以使用Union-Find datastructure(或仅使用深度优先flood fill algorithm)来查找孔内所有已连接的组件网格点。然后,您可以通过围绕构成孔的网格点的轮廓进行跟踪,并按顺序将最近的相邻点串在一起来找到孔的轮廓(您可能必须稍微调整一下此顺序才能产生非自相交的多边形)。

最近的邻居算法可能会漏掉一些可能在孔边界上的点,但是r的值越小,可能性就越小。