从Python中的二进制图像获取像素边界坐标(不是边)

从Python中的二进制图像获取像素边界坐标(不是边),python,image,image-processing,polygon,scikit-image,Python,Image,Image Processing,Polygon,Scikit Image,我有一个二进制图像,其中包含一个连续的斑点,没有孔。我想基于边缘像素的外部边缘创建一个多边形对象。我知道如何获得边缘像素本身,但我想要像素边界的实际坐标,顺时针或逆时针排序。所有像素都有整数坐标 例如,假设我在(2,2)处有一个像素。多边形的顶点将是: (2.5, 2.5) (2.5, 1.5) (1.5, 1.5) (1.5, 2.5) (2.5,2.5) 有没有一种精确的、非近似的方法可以做到这一点?最好使用Python?参见示例: from PIL import Image, ImageF

我有一个二进制图像,其中包含一个连续的斑点,没有孔。我想基于边缘像素的外部边缘创建一个多边形对象。我知道如何获得边缘像素本身,但我想要像素边界的实际坐标,顺时针或逆时针排序。所有像素都有整数坐标

例如,假设我在(2,2)处有一个像素。多边形的顶点将是: (2.5, 2.5) (2.5, 1.5) (1.5, 1.5) (1.5, 2.5) (2.5,2.5)

有没有一种精确的、非近似的方法可以做到这一点?最好使用Python?

参见示例:

from PIL import Image, ImageFilter, ImageChops
import math

a = [['.','.','.','.','.','.','.'],
      ['.','.','.','.','.','.','.'],
      ['.','.','.','.','.','.','.'],
      ['.','#','#','#','.','.','.'],
      ['.','#','#','#','.','.','.'],
      ['.','.','.','.','.','.','.']]      

sz=(len(a[0]), len(a))
flat_list = [j=='.' for i in a for j in i]
image=Image.new('1', sz)
image.putdata(flat_list)
contour=ImageChops.difference(image, image.filter(ImageFilter.MinFilter(3)))
contour_list=list(contour.getdata())
points=[divmod(i,sz[0]) for i in range(len(contour_list)) if contour_list[i]]
points_x,points_y=zip(*points)
avg=lambda x: sum(x)/len(x)
mean_x=avg(points_x)
mean_y=avg(points_y)
phase=[(math.atan2(points_y[i]-mean_y, points_x[i]-mean_x),i) \
       for i in range(len(points))] 
phase.sort()
for i in range(len(points)):
    print(*points[phase[i][1]])
见示例:

from PIL import Image, ImageFilter, ImageChops
import math

a = [['.','.','.','.','.','.','.'],
      ['.','.','.','.','.','.','.'],
      ['.','.','.','.','.','.','.'],
      ['.','#','#','#','.','.','.'],
      ['.','#','#','#','.','.','.'],
      ['.','.','.','.','.','.','.']]      

sz=(len(a[0]), len(a))
flat_list = [j=='.' for i in a for j in i]
image=Image.new('1', sz)
image.putdata(flat_list)
contour=ImageChops.difference(image, image.filter(ImageFilter.MinFilter(3)))
contour_list=list(contour.getdata())
points=[divmod(i,sz[0]) for i in range(len(contour_list)) if contour_list[i]]
points_x,points_y=zip(*points)
avg=lambda x: sum(x)/len(x)
mean_x=avg(points_x)
mean_y=avg(points_y)
phase=[(math.atan2(points_y[i]-mean_y, points_x[i]-mean_x),i) \
       for i in range(len(points))] 
phase.sort()
for i in range(len(points)):
    print(*points[phase[i][1]])

根据评论,以下是我实施的方法:

  • 将所有像素坐标乘以10,这样我们只处理整数

  • 对于每个像素,通过添加+/-5生成4个角点。例如,对于(20,20),角是(25,25)(25,15)(15,15)(15,25)(25,25)。并将所有角存储在列表中

  • 计算每个角点的出现次数。如果计数是奇数,那么它就是一个点。使坐标为整数使这一步变得容易。计算浮动有问题

  • 将水滴角点坐标除以10,得到原始分辨率

  • 使用标准算法顺时针排序角点


  • 根据评论,以下是我实施的方法:

  • 将所有像素坐标乘以10,这样我们只处理整数

  • 对于每个像素,通过添加+/-5生成4个角点。例如,对于(20,20),角是(25,25)(25,15)(15,15)(15,25)(25,25)。并将所有角存储在列表中

  • 计算每个角点的出现次数。如果计数是奇数,那么它就是一个点。使坐标为整数使这一步变得容易。计算浮动有问题

  • 将水滴角点坐标除以10,得到原始分辨率

  • 使用标准算法顺时针排序角点


  • 每个坐标加/减0.5,“装箱”2x2坐标。你在说什么?你是说,把所有像素上所有角点的出现次数加起来,保留那些奇数的角点?那很优雅。那么如何对剩余顶点排序?我不知道这些组合是什么意思。不,我在你的描述中漏掉了一个短语。按时钟顺序排序的点在许多地方在线解决;在你的研究中寻找“形状”算法。这些还将为您提供查找边界框(平凡)、凸面容器的点或边的例程和步骤。我没有答案,但假设您已经知道如何按顺序获取周长像素(我使用opencv的findContours),接近边界的一种方法是以更高的分辨率对blob图像重新采样。不幸的是,这可能是昂贵的,它不会让你的原始像素的边缘,所以这不是一个好主意。但这是我能马上想到的唯一一个。另一个帮助确定最终多边形中应包含的像素角点集的技巧:连接像素中心(同样,假设您知道如何获取周长),然后删除像素中心多边形内的所有像素角点坐标。每个坐标加/减0.5,“装箱”2x2联合国。你在说什么?你是说,把所有像素上所有角点的出现次数加起来,保留那些奇数的角点?那很优雅。那么如何对剩余顶点排序?我不知道这些组合是什么意思。不,我在你的描述中漏掉了一个短语。按时钟顺序排序的点在许多地方在线解决;在你的研究中寻找“形状”算法。这些还将为您提供查找边界框(平凡)、凸面容器的点或边的例程和步骤。我没有答案,但假设您已经知道如何按顺序获取周长像素(我使用opencv的findContours),接近边界的一种方法是以更高的分辨率对blob图像重新采样。不幸的是,这可能是昂贵的,它不会让你的原始像素的边缘,所以这不是一个好主意。但这是我能马上想到的唯一一个。另一个帮助确定最终多边形中应包含的像素角点集的技巧:连接像素中心(同样,假设您知道如何获取周长),然后删除像素中心多边形内的所有像素角点坐标。谢谢您的示例,但是这段代码会找到与“#”相邻的像素。我需要将“#”与边界分开的轮廓坐标,其中的值应为0.5。感谢您提供的示例,但此代码查找“#”边界的像素。我需要将“#”与边界分开的轮廓坐标,其中的值应为0.5。