Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将二值图像的骨架转换为多段线_Python_Image Processing_Polyline - Fatal编程技术网

Python 将二值图像的骨架转换为多段线

Python 将二值图像的骨架转换为多段线,python,image-processing,polyline,Python,Image Processing,Polyline,我的目标(简单示例): 让我们想象一个具有一组1像素宽直线的二值图像。这些线的方向(即它们相对于x轴的角度)遵循一些(窄)分布,但也存在异常值。我希望能够: 找出异常值并将其从考虑范围中排除 确定端点彼此接近且角度相似的直线,并将其组合成一条较长的直线(即,用从第一条直线远端到第二条直线远端的直线替换) 确定直线的平均角度 然而,在我的实际用例中,线条不一定是直的 我的方法: 使用多边形线近似直线,即沿原始直线的顶点坐标组。 一旦创建了这些多边形线,剩下的就相当简单了: 如果顶点离最外层顶点

我的目标(简单示例):

让我们想象一个具有一组1像素宽直线的二值图像。这些线的方向(即它们相对于x轴的角度)遵循一些(窄)分布,但也存在异常值。我希望能够:

  • 找出异常值并将其从考虑范围中排除
  • 确定端点彼此接近且角度相似的直线,并将其组合成一条较长的直线(即,用从第一条直线远端到第二条直线远端的直线替换)
  • 确定直线的平均角度
然而,在我的实际用例中,线条不一定是直的

我的方法:

使用多边形线近似直线,即沿原始直线的顶点坐标组。 一旦创建了这些多边形线,剩下的就相当简单了: 如果顶点离最外层顶点之间的直线太远,则将多边形线拆分为一组直线,删除除这两个外部顶点以外的所有顶点,并如上所述合并直线

我的问题:


如何在Python中创建如上所述的多边形线结构?是否有可能有用的库/包?

嗯,我构建了一些似乎有效的东西。如果您有任何关于如何改进的想法和意见,我将不胜感激。我正在使用Python 3.7

首先,创建一个列表,其中包含表示8个相邻连接区域的树:

from treelib import Tree

def extract_regions(image):
"""
Extracts 8-neighbors-connected regions from a binary image.
:param image: A homogeneous list of lists containing binary values (booleans or 1s and 0s).
:return: A list of trees containing 2D pixel coordinates of connected regions. The x axis 
         is assumed to be left to right, y axis top to bottom.
"""

    regions = []
    y = 0
    for line in image:
        x = 0
        for pixel in line:
            if pixel:  # i.e. pixel is 1
                if not in_trees((x, y), regions):  # i.e. pixel is not labeled
                    # create tree of connected region
                    regions.append(create_region_tree(x, y, image_transpose))
            # keep track of pixel position
            x += 1
        y += 1

return regions

def create_region_tree(center_x, center_y, image):
    tree = Tree()
    tag = 0
    tree.create_node(tag, (center_x, center_y))  # root node
    tracker = [(center_x, center_y)]
    for p in tracker:
        # define the 3x3 neighborhood around the center pixel; I try not wrap around, not sure if that's needed
        from_x = p[0] - 1 if p[0] >= 1 else 0
        to_x = p[0] + 2 if p[0] + 2 <= len(image[0]) else len(image[0])
        from_y = p[1] - 1 if p[1] >= 1 else 0
        to_y = p[1] + 2 if p[1] + 2 <= len(image) else len(image)
        # find pixels in the neighborhood and add them to tree
        b = from_y
        for line in image[from_y:to_y]:
            a = from_x
            for neighbor in line[from_x:to_x]:
                if neighbor:
                    if not (a, b) in tree:
                        tracker.append((a, b))  # keeps track of pixels in region
                        tag += 1
                        tree.create_node(tag, (a, b), parent=p)
                a += 1
            b += 1
    return tree
然后将这些多边形线“拉直”:

最后,将这些直线相互比较,如果它们足够接近和相似(这条直线需要最长的时间才能完成,因此欢迎提供任何提示):

def组合直线(直线列表,距离公差平方=5**2,
角度公差=2,最小线长度平方=15**2):
临时行列表=行列表。复制()
新行列表=[]
#将距离列表的索引(见下文)转换为相应的向量
#两行中的一行(0:起始向量,1:结束向量)
向量表=[(0,0),(1,0),(0,1),(1,1)]
#最后一行
对于i,枚举中的第0行(临时行列表):
保持不变
#i之后的所有行,以避免“双重参照”(a->b&b->a)
对于范围内的j(i+1,len(临时线列表)):
第1行=临时列表[j]
距离=(
向量距离平方(直线0[0],直线1[0]),
向量距离平方(直线0[1],直线1[0]),
向量距离平方(直线0[0],直线1[1]),
向量距离平方(直线0[1],直线1[1]))
最小距离=最小(距离)
#检查最近的线向量是否足够接近

如果最小距离那么,我构建了一个似乎有效的东西。如果您有任何关于如何改进的想法和意见,我将不胜感激。我正在使用Python 3.7

首先,创建一个列表,其中包含表示8个相邻连接区域的树:

from treelib import Tree

def extract_regions(image):
"""
Extracts 8-neighbors-connected regions from a binary image.
:param image: A homogeneous list of lists containing binary values (booleans or 1s and 0s).
:return: A list of trees containing 2D pixel coordinates of connected regions. The x axis 
         is assumed to be left to right, y axis top to bottom.
"""

    regions = []
    y = 0
    for line in image:
        x = 0
        for pixel in line:
            if pixel:  # i.e. pixel is 1
                if not in_trees((x, y), regions):  # i.e. pixel is not labeled
                    # create tree of connected region
                    regions.append(create_region_tree(x, y, image_transpose))
            # keep track of pixel position
            x += 1
        y += 1

return regions

def create_region_tree(center_x, center_y, image):
    tree = Tree()
    tag = 0
    tree.create_node(tag, (center_x, center_y))  # root node
    tracker = [(center_x, center_y)]
    for p in tracker:
        # define the 3x3 neighborhood around the center pixel; I try not wrap around, not sure if that's needed
        from_x = p[0] - 1 if p[0] >= 1 else 0
        to_x = p[0] + 2 if p[0] + 2 <= len(image[0]) else len(image[0])
        from_y = p[1] - 1 if p[1] >= 1 else 0
        to_y = p[1] + 2 if p[1] + 2 <= len(image) else len(image)
        # find pixels in the neighborhood and add them to tree
        b = from_y
        for line in image[from_y:to_y]:
            a = from_x
            for neighbor in line[from_x:to_x]:
                if neighbor:
                    if not (a, b) in tree:
                        tracker.append((a, b))  # keeps track of pixels in region
                        tag += 1
                        tree.create_node(tag, (a, b), parent=p)
                a += 1
            b += 1
    return tree
然后将这些多边形线“拉直”:

最后,将这些直线相互比较,如果它们足够接近和相似(这条直线需要最长的时间才能完成,因此欢迎提供任何提示):

def组合直线(直线列表,距离公差平方=5**2,
角度公差=2,最小线长度平方=15**2):
临时行列表=行列表。复制()
新行列表=[]
#将距离列表的索引(见下文)转换为相应的向量
#两行中的一行(0:起始向量,1:结束向量)
向量表=[(0,0),(1,0),(0,1),(1,1)]
#最后一行
对于i,枚举中的第0行(临时行列表):
保持不变
#i之后的所有行,以避免“双重参照”(a->b&b->a)
对于范围内的j(i+1,len(临时线列表)):
第1行=临时列表[j]
距离=(
向量距离平方(直线0[0],直线1[0]),
向量距离平方(直线0[1],直线1[0]),
向量距离平方(直线0[0],直线1[1]),
向量距离平方(直线0[1],直线1[1]))
最小距离=最小(距离)
#检查最近的线向量是否足够接近
如果距离最小
def straight_line_segments_from_polylines(poly_line_list, max_distance_from_line_squared=4 ** 2):        

    # turn a polyline into a (set of) straight line(s)
    def straighten(p_line):
        end = len(p_line) - 1
        # get coordinates of first and last vector of the polyline
        x1, y1 = p_line[0]
        x2, y2 = p_line[-1]
        # for distance calculation, see below
        factor_x = y2 - y1
        factor_y = x2 - x1
        term = x2 * y1 - y2 * x1
        denominator = (y2 - y1) ** 2 + (x2 - x1) ** 2

        for i in range(1, end):  # i.e. all vectors except first and last
            x0, y0 = p_line[i]
            # calculate distance between current vector and the line between first and last vector of the polyline
            numerator = abs(factor_x * x0 - factor_y * y0 + term) ** 2
            distance = numerator / denominator

            if distance > max_distance_from_line_squared:  # i.e. current vector is too far away from the straight line
                # split the polyline at current vector, convert the now 2 polylines into sets of straight lines
                return straight_line_segments_from_polylines([p_line[:i], p_line[i:]])
        # return start and end vector of the polyline (a now straight line)
        straight_line = [[p_line[0], p_line[-1]]]
        return straight_line

    straight_lines = []
    for polyline in poly_line_list:
        straight_lines += straighten(polyline)

    return straight_lines
def combine_straight_lines(line_list, distance_tolerance_squared=5 ** 2,
                angle_tolerance=2, min_line_length_squared=15 ** 2):

    temp_line_list = line_list.copy()
    new_line_list = []
    # translates indices of the distance list (see below) to the corresponding vectors
    # of the 2 lines (0: starting vector, 1: end vector)
    vector_nr_table = [(0, 0), (1, 0), (0, 1), (1, 1)]

    # all lines before last
    for i, line0 in enumerate(temp_line_list):
        keeping = True
        # all lines_after i, as to not "double reference" (a -> b & b -> a)
        for j in range(i+1, len(temp_line_list)):
            line1 = temp_line_list[j]
            distance = (
                vector_distance_squared(line0[0], line1[0]),
                vector_distance_squared(line0[1], line1[0]),
                vector_distance_squared(line0[0], line1[1]),
                vector_distance_squared(line0[1], line1[1]))
            smallest_distance = min(distance)
            # check if the closest line vectors are close enough
            if smallest_distance <= distance_tolerance_squared:  
                # calculate angle of the lines and compare
                angle0 = get_straight_line_angle(line0)['deg']
                angle1 = get_straight_line_angle(line1)['deg']
                angle_diff = abs(angle1 - angle0)
                if angle_diff <= angle_tolerance:  # i.e. angles are similar
                    # determine the vectors of each line farthest away from each other,
                    # replace j with these vectors
                    max_dist = int(np.argmax(distance))
                    v0, v1 = vector_nr_table[max_dist]
                    temp_line_list[j] = [line0[v0], line1[v1]]
                    # line i has been incorporated into j, can be left out from result
                    keeping = False
                    break
        if keeping:
            line_length = get_straight_line_length_squared(line0)
            # skip if too short
            if line_length >= min_line_length_squared:
                new_line_list.append(line0)
    return new_line_list     


def vector_distance_squared(vec0, vec1):
    np_vec0 = np.array(vec0)
    np_vec1 = np.array(vec1)
    result = np.sum(ce.np.square(np_vec0 - np_vec1))
    return result


def get_straight_line_angle(line):
    numerator = line[1][1] - line[0][1]
    denominator = line[1][0] - line[0][0]
    angle = np.pi / 2 if denominator == 0 else np.arctan(numerator / denominator)
    result = {'rad': angle, 'deg': 180 * angle / np.pi}
    return result


def get_straight_line_length_squared(line):
    a = line[1][0] - line[0][0]
    b = line[1][1] - line[0][1]
    length_squared = a ** 2 + b ** 2
    return length_squared