【问题标题】:How can I convert a solid-black and white image into a collection of polygons?如何将纯黑白图像转换为多边形集合?
【发布时间】:2012-10-20 17:29:39
【问题描述】:

我有一堆纯黑白图像,上面有各种文字和形状。我的目标是将每个图像转换为一组多边形(定义为一组顶点),围绕黑色区域(就像魔术棒工具可以在照片编辑软件中选择区域一样)。

我更喜欢在 JavaScript 中实现它,但我最感兴趣的是从概念上如何去做。谢谢!

【问题讨论】:

    标签: javascript image image-processing


    【解决方案1】:

    魔术棒在简单位图编辑器中的工作方式是:

    设颜色 C 为所选原始点的颜色。 让最后一个颜色 LC 是任何颜色。

    1. 获取选定点 (x,y)
    2. 如果 (x,y) 的颜色 = C 且像素未访问
      1. 在数组中存储坐标
      2. 对 (x+1,y)、(x-1,y)、(x,y+1)、(x,y-1) 重复算法
    3. 将像素存储在访问过的像素数组中
    4. 如果 (x,y) 的颜色 != LC
      1. 将像素标记为数组中的边框像素
      2. 设置 LC = (x,y) 的颜色

    【讨论】:

    • 又名洪水填充。但这并不会真正产生多边形,是吗?
    • @Junuxx 它选择与颜色匹配的所有像素,在 OP 的情况下为黑色。我不确定表示多边形的像素数组是否算作“多边形”。如果他想选择周围的形状,他只需反转条件即可。
    • 感谢您的快速而有帮助的回复!我应该指定:我希望将每个多边形定义为一组 vertices
    • @cmpolis 更好,但是有多少个顶点?从技术上讲,如果您拥有所有边框像素,您将拥有一个由其顶点定义的多边形;有很多。
    • @Asad 理想情况下,我会使用尽可能少的顶点,以便用两个顶点定义直线,并且圆角边缘包含足够的顶点以显得平滑。但是,使用低效(就顶点数而言)算法仍然是可以接受的——只要直线仍然看起来很平滑。此外,可以忽略“孤岛”,即如果黑色完全包含白色,则可以忽略。谢谢!
    【解决方案2】:

    首先,让我解释一下什么是“边缘”。

    边缘是两个连续像素之间的虚拟线。

    +---+---+
    | A | B |  // The middle line is the edge between pixel A and pixel B
    +---+---+
    

    边有起点和终点,因此向上、向下、“左”或“右”。

    为了处理跨越图像边界的多边形,我们将在图像周围添加一个 1 像素的白色边框。

    算法如下:

    For each row of our image {  
      For each pixel of the row except the last one {  
        If current pixel is white and next pixel is black {
          Create a new upward edge between the two pixels and add it to
          the global edge list.
        }
        Else if current pixel is black and next pixel is white {
          Create a new downward edge between the two pixels and add it to
          the global edge list.
        }
      }  
    }
    For each column of our image {  
      For each pixel of the column except the last one {  
        If current pixel is white and next pixel is black {
          Create a new "left going" edge between the two pixels and add it to
          the global edge list.
        }
        Else if current pixel is black and next pixel is white {
          Create a new "right going" edge between the two pixels and add it to
          the global edge list.
        }
      }  
    }  
    
    For each edge of the global edge list {
      Find the edge starting from the point where your current edge ends
      (for now on, we gonna call it next_edge).
      Connect the two edges to form a linked list
      (edge->next = next_edge; next_edge->previous = edge;)
    }
    
    While there is edges in the global edge list {
      Create a new polygon and add it to the polygon list
      Take the first edge of the list (for now on, we gonna call it first_edge)
      For each edge in the linked list starting at first_edge {
         Remove edge from global edge list
         Add edge to polygon's edge list
      }
    }
    

    完成,你有一个多边形列表。

    编辑

    当然,在使用它之前你必须对其进行一些优化,但这真的很容易:具有相同方向的连续边可以替换为单个较长的边。

    【讨论】:

    • 从 (0,0)->(10,3) 开始的对角边或线呢?
    • @AkiSuihkonen 不是所有边都是正交的,因为像素是正方形的吗?
    • @Asad:在某些情况下是的,但是 IMO 对 OP 问题的有意义的解释处理例如将三角形矢量化为三个顶点。
    • @AkiSuihkonen 像我在此算法中所做的那样将位图转换为“无损”多边形后,您可以通过平滑和删除不相关的点进一步将其简化为 近似 原始光栅化形状。但这是一个完全不同的问题 IMO。 OP 谈到了“魔杖”,这正是它的作用。
    【解决方案3】:

    当只需要扫描周边时,可以生成“右手在墙上”算法。

    第1步:沿着图像向右遍历找到第一个相反颜色的像素。
    第 2 步:按顺时针顺序搜索当前像素的所有相邻像素。
    第 3 步:移动到第一个可用像素。存储像素索引
    第4步:重复第2-3步,直到当前像素是第1步中的起始像素

    第 5 步:从存储的像素中检测模式,例如
    LLLLLLLLLL 的运行,[左] 上、右或下,

    形式模式

    RRRRRRR U RRRRRRR U RRRRRRRR U RRRRRRR U ...
    <-N--->   <--N-->   <--N+1->   <--N-->
    

    可以用一条线来建模,虽然用“逆bresenham”来检测一条线段的最佳起点和终点并不容易。

    无论如何都可以使用蛮力方法从当前像素到 N 个先前像素画一条线,并测试 bresenhams 算法是否产生完全相同的像素。

    【讨论】:

    • 当您说search all neighbouring pixels of the current one in clockwise order 时,是否包括对角线像素?
    • 我的意思是 4 邻域。也许该算法也适用于对角线。
    • 这可能是最好的方法,因为它在语义上最接近通过边界重建多边形。
    猜你喜欢
    • 1970-01-01
    • 2013-01-28
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多