Python numpy二值光栅图像到多边形的变换

Python numpy二值光栅图像到多边形的变换,python,numpy,Python,Numpy,我想将2d numpy数组转换为多边形。性能对我来说非常重要,但我希望避免使用C扩展。 二值轮廓图像可以通过腐蚀来制作。然后我发现。它太慢了,有时无法应对侵蚀造成的尖刺。 钉子: 我的第一次尝试: mat = mat.copy() != 0 mat = mat - scipy.ndimage.binary_erosion(mat) vertices = np.argwhere(mat) minx = vertices.min(axis=0)[0] maxx = vertices.max(axi

我想将2d numpy数组转换为多边形。性能对我来说非常重要,但我希望避免使用C扩展。 二值轮廓图像可以通过腐蚀来制作。然后我发现。它太慢了,有时无法应对侵蚀造成的尖刺。 钉子:

我的第一次尝试:

mat = mat.copy() != 0
mat = mat - scipy.ndimage.binary_erosion(mat)

vertices = np.argwhere(mat)
minx = vertices.min(axis=0)[0]
maxx = vertices.max(axis=0)[0]

vertices_sorted = {}
for x in xrange(minx - 1, maxx + 2):
    vertices_sorted[x] = []

for vertex in vertices:
    vertices_sorted[vertex[0]].append(vertex[1])

vertex_loop = [(minx, vertices_sorted[minx][0])]
while True:
    x, y = vertex_loop[-1]
    for column, row in ((x, y + 1), (x, y - 1), 
    (x + 1, y), (x + 1, y + 1), (x + 1, y - 1),
    (x - 1, y), (x - 1, y + 1), (x - 1, y - 1)):
        if row in vertices_sorted[column]:
            vertices_sorted[column].remove(row)
            vertex_loop.append((column, row))
            break
    else:
        vertex_loop.pop()

    if vertex_loop[-1] == vertex_loop[0]:
        break
return vertex_loop[:-1]
它大部分时间都能工作,但速度不够快。 我的第二个代码很少工作,但我没有修复它,因为它比第一个代码慢很多倍:

mat = mat.copy() != 0
mat = mat - scipy.ndimage.binary_erosion(mat)

xs, ys = np.nonzero(mat)
ys = np.ma.array(ys)

vertex_loop = [(xs[0], ys[0])]
ys[0] = np.ma.masked
while True:
    x, y = vertex_loop[-1]
    start = np.searchsorted(xs, x-1, side="left")
    end = np.searchsorted(xs, x+1, side="right")

    for i in xrange(start, end):
        if ys[i] == y or ys[i] == y + 1 or ys[i] == y - 1:
            vertex_loop.append((xs[i], ys[i]))
            ys[i] = np.ma.masked
            break
    else:
        if np.all(ys.mask):
            break
        else:
            vertex_loop.pop()
return vertex_loop
#import time
#t1 = time.time()
mat = mat.copy() != 0
mat = mat - scipy.ndimage.binary_erosion(mat)

xs, ys = np.nonzero(mat)
#t2 = time.time()
minx = xs[0]
maxx = xs[-1]

# Ketju pakosti käy läpi kaikki rivit minx:n ja maxx:n välissä, sillä se ON KETJU
xlist = range(minx - 1, maxx + 2)
# starts ja ends ovat dictit jotka kertovat missä slicessä x == key
tmp = np.searchsorted(xs, xlist, side="left")
starts = dict(zip(xlist, tmp))
tmp = np.searchsorted(xs, xlist, side="right")
ends = dict(zip(xlist, tmp))

unused = np.ones(len(xs), dtype=np.bool)
#t3 = time.time()
vertex_loop = [(xs[0], ys[0])]
unused[0] = 0
count = 0
while True:
    count += 1
    x, y = vertex_loop[-1]
    for i in xrange(starts[x - 1], ends[x + 1]):
        row = ys[i]
        if unused[i] and (row == y or row == y + 1 or row == y - 1):
            vertex_loop.append((xs[i], row))
            unused[i] = 0
            break
    else:
        if abs(x - xs[0]) <= 1 and abs(y - ys[0]) <= 1:
            break
        else:
            vertex_loop.pop()
#t4 = time.time()
#print abs(t1-t2)*1000, abs(t2-t3)*1000, abs(t3-t4)*1000
return vertex_loop
如何进一步提高速度

编辑:看起来numpy掩码数组非常慢。此实现几乎与第一个实现一样快:

mat = mat.copy() != 0
mat = mat - scipy.ndimage.binary_erosion(mat)

xs, ys = np.nonzero(mat)
ys = np.ma.array(ys)

vertex_loop = [(xs[0], ys[0])]
ys[0] = np.ma.masked
while True:
    x, y = vertex_loop[-1]
    start = np.searchsorted(xs, x-1, side="left")
    end = np.searchsorted(xs, x+1, side="right")

    for i in xrange(start, end):
        if ys[i] == y or ys[i] == y + 1 or ys[i] == y - 1:
            vertex_loop.append((xs[i], ys[i]))
            ys[i] = np.ma.masked
            break
    else:
        if np.all(ys.mask):
            break
        else:
            vertex_loop.pop()
return vertex_loop
#import time
#t1 = time.time()
mat = mat.copy() != 0
mat = mat - scipy.ndimage.binary_erosion(mat)

xs, ys = np.nonzero(mat)
#t2 = time.time()
minx = xs[0]
maxx = xs[-1]

# Ketju pakosti käy läpi kaikki rivit minx:n ja maxx:n välissä, sillä se ON KETJU
xlist = range(minx - 1, maxx + 2)
# starts ja ends ovat dictit jotka kertovat missä slicessä x == key
tmp = np.searchsorted(xs, xlist, side="left")
starts = dict(zip(xlist, tmp))
tmp = np.searchsorted(xs, xlist, side="right")
ends = dict(zip(xlist, tmp))

unused = np.ones(len(xs), dtype=np.bool)
#t3 = time.time()
vertex_loop = [(xs[0], ys[0])]
unused[0] = 0
count = 0
while True:
    count += 1
    x, y = vertex_loop[-1]
    for i in xrange(starts[x - 1], ends[x + 1]):
        row = ys[i]
        if unused[i] and (row == y or row == y + 1 or row == y - 1):
            vertex_loop.append((xs[i], row))
            unused[i] = 0
            break
    else:
        if abs(x - xs[0]) <= 1 and abs(y - ys[0]) <= 1:
            break
        else:
            vertex_loop.pop()
#t4 = time.time()
#print abs(t1-t2)*1000, abs(t2-t3)*1000, abs(t3-t4)*1000
return vertex_loop
#导入时间
#t1=时间。时间()
mat=mat.copy()!=0
mat=mat-scipy.ndimage.binary_侵蚀(mat)
xs,ys=np.非零(mat)
#t2=时间。时间()
minx=xs[0]
maxx=xs[-1]
#凯特朱·帕科斯蒂·凯皮·凯基·里维特·明克斯(Ketju pakosti käy läpi kaikki rivit minx):n ja maxx:n välissä,凯特朱岛上的silläse
xlist=范围(最小值-1,最大值+2)
#开始和结束ovat DICTT jotka kertovat missäslicessäx==键
tmp=np.searchsorted(xs,xlist,side=“left”)
开始=dict(zip(xlist,tmp))
tmp=np.searchsorted(xs,xlist,side=“right”)
ends=dict(zip(xlist,tmp))
unused=np.one(len(xs),dtype=np.bool)
#t3=时间。时间()
顶点_循环=[(xs[0],ys[0])]
未使用的[0]=0
计数=0
尽管如此:
计数+=1
x、 y=顶点_循环[-1]
对于x范围内的i(开始[x-1],结束[x+1]):
行=ys[i]
如果未使用[i]和(行==y或行==y+1或行==y-1):
vertex_loop.append((xs[i],行))
未使用的[i]=0
打破
其他:
如果abs(x-xs[0])“我想把一个2d numpy数组转换成一个多边形”,你能澄清一下吗?您的意思是只需要边元素(在原始网格上)的标签数组,还是多边形顶点坐标的有序列表

例如:这是吗

[ [ 0, 0, 0, 0, 0 ], 
  [ 0, 1, 1, 1, 0 ], 
  [ 0, 1, 1, 1, 0 ], 
  [ 0, 1, 1, 1, 0 ], 
  [ 0, 0, 0, 0, 0 ] ]
转化成这样:

[ [ 0, 0, 0, 0, 0 ], 
  [ 0, 1, 1, 1, 0 ], 
  [ 0, 1, 0, 1, 0 ], 
  [ 0, 1, 1, 1, 0 ], 
  [ 0, 0, 0, 0, 0 ] ]
或者进入顶点列表
((1,1)、(3,1)、(3,3)、(1,3))

还是要先找到边,然后再找到顶点

我将根据您想要的只是边缘的假设简要回答(因为您正在谈论侵蚀等)。听起来像是在尝试使用形态学操作(侵蚀等)进行边缘检测。边缘检测也可以直接完成。使用
ndimage.sobel
scikits.filter.canny
,或此:

import numpy as np
import scipy.ndimage as ndimage
import matplotlib.pyplot as pyplot
im = np.zeros((32, 32))
im[8:-8, 8:-8] = 1
im = ndimage.rotate(im, 15)
im = numpy.where(im > 0.5, 1, 0)
edges = (ndimage.filters.maximum_filter(im, size=2) == ndimage.filters.minimum_filter(im, size=2))
pyplot.imshow(edges, interpolation='nearest')
pyplot.show()

如果您的数据已经设置了阈值(0和1),并且边缘没有过多的噪声,那么任何边缘检测器都会工作得很好。

这是一种非常快速的获取二进制numpy阵列轮廓的方法

outline.py:

from scipy.weave import inline, converters

_code = open("outline.c", "r").read()

def outline(data, every):
    width, height = data.shape
    return inline(_code, ['data', 'width', 'height', 'every'], type_converters=converters.blitz)
大纲c:

/*
Modifioitu pygame.mask.Mask.outline
Input: data, width, height, every
*/

PyObject *plist, *value;
int x, y, e, firstx, firsty, secx, secy, currx, curry, nextx, nexty, n;
int a[14], b[14];
a[0] = a[1] = a[7] = a[8] = a[9] = b[1] = b[2] = b[3] = b[9] = b[10] = b[11]= 1;
a[2] = a[6] = a[10] = b[4] = b[0] = b[12] = b[8] = 0;
a[3] = a[4] = a[5] = a[11] = a[12] = a[13] = b[5] = b[6] = b[7] = b[13] = -1;

plist = NULL;
plist = PyList_New (0);
/*if (!plist) En ymmärrä mihin tätä tarvii
    return NULL;*/

every = 1;
n = firstx = firsty = secx = x = 0;

/*if(!PyArg_ParseTuple(args, "|i", &every)) {
    return NULL;
}

 by copying to a new, larger mask, we avoid having to check if we are at
   a border pixel every time.  
bitmask_draw(m, c, 1, 1); */

e = every;

/* find the first set pixel in the mask */
for (y = 1; y < height-1; y++) {
    for (x = 1; x < width-1; x++) {
        if (data(x, y)) {
             firstx = x;
             firsty = y;
             value = Py_BuildValue("(ii)", x-1, y-1);
             PyList_Append(plist, value);
             Py_DECREF(value);
             break;
        }
    }
    if (data(x, y))
        break;
}



/* covers the mask having zero pixels or only the final pixel
Pikseleitä on ainakin kymmenen
if ((x == width-1) && (y == height-1)) {
    return plist;
}        */

/* check just the first pixel for neighbors */
for (n = 0;n < 8;n++) {
    if (data(x+a[n], y+b[n])) {
        currx = secx = x+a[n];
        curry = secy = y+b[n];
        e--;
        if (!e) {
            e = every;
            value = Py_BuildValue("(ii)", secx-1, secy-1);
            PyList_Append(plist, value);
            Py_DECREF(value);
        }
        break;
    }
}       

/* if there are no neighbors, return
Pikseleitä on ainakin kymmenen
if (!secx) {
    return plist;
}*/

/* the outline tracing loop */
for (;;) {
    /* look around the pixel, it has to have a neighbor */
    for (n = (n + 6) & 7;;n++) {
        if (data(currx+a[n], curry+b[n])) {
            nextx = currx+a[n];
            nexty = curry+b[n];
            e--;
            if (!e) {
                e = every;
                if ((curry == firsty && currx == firstx) && (secx == nextx && secy == nexty)) {
                    break;
                }
                value = Py_BuildValue("(ii)", nextx-1, nexty-1);
                PyList_Append(plist, value);
                Py_DECREF(value);
            }
            break;
        }
    }
    /* if we are back at the first pixel, and the next one will be the
       second one we visited, we are done */
    if ((curry == firsty && currx == firstx) && (secx == nextx && secy == nexty)) {
        break;
    }

    curry = nexty;
    currx = nextx;
}

return_val = plist;
/*
Modifioitu pygame.mask.mask.outline
输入:数据、宽度、高度、间隔
*/
PyObject*plist,*值;
int x,y,e,firstx,firsty,secx,secy,currx,curry,nextx,nexty,n;
INTA[14],b[14];
a[0]=a[1]=a[7]=a[8]=a[9]=b[1]=b[2]=b[3]=b[9]=b[10]=b[11]=1;
a[2]=a[6]=a[10]=b[4]=b[0]=b[12]=b[8]=0;
a[3]=a[4]=a[5]=a[11]=a[12]=a[13]=b[5]=b[6]=b[7]=b[13]=1;
plist=NULL;
plist=PyList_New(0);
/*如果(!plist)是ymmärrämihin tätätarvii
返回NULL*/
每个=1;
n=firstx=firsty=secx=x=0;
/*if(!PyArg_ParseTuple(args、|i、&every)){
返回NULL;
}
通过复制到一个新的、更大的掩码,我们避免了检查我们是否处于
每次都有一个边界像素。
位掩码(m,c,1,1)*/
e=每一个;
/*查找遮罩中的第一组像素*/
对于(y=1;y
很抱歉我的问题不够清晰。我有一个没有洞的多边形的二值图像,我想把它转换成一个多边形。问题是对边缘像素进行排序,以便它们形成多边形。二进制2d numpy数组-->列表或数组coordinates@Joonazan:你的多边形是凸的吗?这会简化问题,但不幸的是不是。