确定解决迷宫的最小线段数

时间:2015-06-17 22:03:09

标签: algorithm geometry

我有一个问题,我需要定义一个多边形的顶点,该顶点相交或包含图像中不透明的每个像素(设N是图像中的像素数)。我唯一的假设是图像在其边界(孔)内不能包含透明像素,并且至少两个像素是不透明的。举个例子,假设我有以下图片:

Source Image

我对算法的想法是:

1)确定边缘像素。
这是通过迭代每个像素在O(N)时间内完成的,并确定是否有任何邻居(在上面的四个像素中) ,正确,以及它下面)是空的。存储像素以及哪些邻居不透明,通过线性索引键入像素数组。让P边缘像素,如下面的橙色所示。

Step 1 Processing

2)获取边缘像素的邻接列表。
这是在O(P)时间内通过选择一个边缘像素并根据空邻居选择方向来完成的。 。例如,如果像素具有底部和右侧邻居,则下一个像素将是右上角中的一个像素,或者紧邻右侧的像素。从剩余边缘像素的字典中选择下一个边缘像素。将该像素附加到列表,直到算法返回到起始像素。下面的示例图像中有27个边缘像素(有些边缘像素不止一次)。

Step 2 Processing

3)绘制一个迷宫,所有边都必须介于两者之间。
这是通过迭代邻接列表并在所有边上添加边来在O(P)时间内完成的。那些没有邻居的像素。另外,基于到下一个边缘像素的方向,将边缘添加到形状的内部。如果像素表示具有单个像素宽度的半岛,则将内边缘添加到边缘的中间而不是像素顶点。迷宫的内部以红色显示。请注意,迷宫边界是步骤2中找到的所有边缘像素的超集。

Step 3 Processing

4)找到一个多边形,其中几乎没有边缘,不会碰到迷宫边框。
这是我需要帮助的部分。有没有人建议您如何从步骤#3转到如下的解决方案?

One Possible Solution

2 个答案:

答案 0 :(得分:0)

我没有图像处理方面的背景,但我昨天遇到了Ramer–Douglas–Peucker algorithm,我觉得它可能会有所帮助。

从我对维基百科文章的快速扫描中,它减少了曲线中的点数,因此我会在每一行上运行此算法,其中线的点是您有和还将线条的正方形边界设置为点。

enter image description here

我在这张图片中标出了两行你可以运行算法,我认为它会起作用。

如何找到每一行以及何时停止 - 不是100%肯定,但我希望这很有用。

答案 1 :(得分:0)

  1. find holes in 2D point set
    • 这是非常相似的问题
    • 只需反转地图并使用每个网格方块的中点作为点
    • 将导致这一点:
    • outer polygon
    • 因为你想要内部多边形,那么有两个选择
    • 在应用之前将形状缩小1个单元(可以在形状上松散一些细节)
    • 更改H / V线,使它们缩短1个单元(两侧一半)
    • 会导致类似这样的事情:
    • HV lines shrinked
    • 在代码中进行一些更改后,我现在可以使用2x多次采样,结果是:
    • HV line shrinked
    • 现在您可以加入具有相同斜率的连接线
    • 并在找到的角上应用类似耳朵剪裁的内容,以便更接近所需的多边形
  2. 这里是C ++中反转的源代码(可能还有一些漏洞注释):

    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    /*  usage:
        int i;
        pntcloud_polygons h;
        pnt2D point[points];
    
        h.scann_beg(); for (i=0;i<points;i++) { p=point[i]; h.scann_pnt(p.x,p.y); } h.scann_end();
        h.cell_size(2.5);   // or (h.x1-h.x0)*0.01  ... cell size >> avg point distance
        h.holes_beg(); for (i=0;i<points;i++) { p=point[i]; h.holes_pnt(p.x,p.y); } h.holes_end();
    
    */
    //---------------------------------------------------------------------------
    class pntcloud_polygons
        {
    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
            float 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
        pntcloud_polygons()  { 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; };
        pntcloud_polygons(pntcloud_polygons& a){ *this=a; };
        ~pntcloud_polygons() { _free(); };
        pntcloud_polygons* operator = (const pntcloud_polygons *a) { *this=*a; return this; };
        pntcloud_polygons* operator = (const pntcloud_polygons &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 pntcloud_polygons 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); }
        inline void l2g(double &x,double &y,float i,float 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 pntcloud_polygons::scann_beg()
        {
        x0=0.0; y0=0.0; x1=0.0; y1=0.0; n=0;
        }
    //---------------------------------------------------------------------------
    void pntcloud_polygons::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 pntcloud_polygons::scann_end()
        {
        }
    //---------------------------------------------------------------------------
    void pntcloud_polygons::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 pntcloud_polygons::holes_beg()
        {
        int i,j;
        for (i=0;i<xs;i++)
         for (j=0;j<ys;j++)
          map[i][j]=0;
        }
    //---------------------------------------------------------------------------
    void pntcloud_polygons::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 pntcloud_polygons::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 pntcloud_polygons (map[i][j].cnt!=0) or (map[i][j].cnt>=treshold)
        // and create lin[] list of H,V lines covering pntcloud_polygons
        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 polygon
            for (;i<xs;i++) if (map[i][j]==0) break; i1=i;      // find end of polygon
            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+0.5;
            l.i1=i1-0.5;
            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 polygon
            for (;j<ys;j++) if (map[i][j]==0) break; j1=j  ;    // find end of polygon
            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+0.5;
            l.j1=j1-0.5;
            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 losed 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;
            // 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;
                }
            }
        }
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    //---------------------------------------------------------------------------
    
    • 与上面的孔链接中的代码相同
    • 仅将map[][]条件反转为搜索多边形而不是孔
    • 发现HV线从每侧缩小了一半的细胞
    • _lin坐标现在为float所以需要进行4倍多重采样
    • 最佳结果是2x多次采样(避免多边形宽度= 1)
    • 因此地图中的每个单元格都会在单元格区域中添加2x2点
    • 我还添加了2个角点,以便更好地对齐map[][]和您的图片