Python遍历灰度图像中的连接组件 图像的连接组件算法结果说明注释

我有一个灰度图像,其值在0(黑色)和白色(255)之间。我有一个target矩阵,大小与灰度图像相同。我需要从灰度图像中的一个随机像素开始,然后一次遍历图像一个像素(以深度优先搜索方式),然后将其值复制到target矩阵中的相应位置。我显然只需要对非白色像素执行此操作。我怎样才能做到这一点?我以为我可以获取灰度图像的连接组件并逐个遍历每个像素,但是找不到任何合适的连接组件实现。有什么想法吗?

例如,如果我的灰度图像是:

[[255,255,255]
[255,255]]

然后我可以从(1,2)处的元素开始,然后顺时针旋转以将图形零绘制。

heishashengzhe1 回答:Python遍历灰度图像中的连接组件 图像的连接组件算法结果说明注释

您可以使用@model XXXXXXXX.ViewModels.AssetOtherEventViewModel @using (Html.BeginForm("SaveOtherEvents","AssetEvent",null)) { <div class="form-horizontal"> @Html.HiddenFor(model => model.AssetID) @Html.HiddenFor(model => model.Date) <fieldset> <div id="EditorRows"> @for (var i = 0; i < Model.OtherEvents.Count; i++) { <div class="form-group"> @Html.Label(Model.OtherEvents[i].EventType.Name,new { @class = "control-label col-md-4" }) <div class="col-md-8"> @Html.EditorFor(m => Model.OtherEvents[i].Hours,new { htmlAttributes = new { @class = "form-control" } }) </div> </div> @Html.HiddenFor(m => Model.OtherEvents[i].EventType_ID) @Html.HiddenFor(m => Model.OtherEvents[i].ID) } </div> </fieldset> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-primary" /> </div> </div> </div> }

networkx

enter image description here

from itertools import product,repeat
import numpy as np
import networkx as nx

arr = np.array(
[[255,255,255],[255,10,1 ],30,50,9 ],[51,20,9,240],80,170,20],0 ],69]])

# generate edges
shift = list(product(*repeat([-1,1],2)))
x_max,y_max = arr.shape
edges = []

for x,y in np.ndindex(arr.shape):
    for x_delta,y_delta in shift:
        x_neighb = x + x_delta
        y_neighb = y + y_delta
        if (0 <= x_neighb < x_max) and (0 <= y_neighb < y_max):
            edge = (x,y),(x_neighb,y_neighb)
            edges.append(edge)

# build graph
G = nx.from_edgelist(edges)

# draw graph
pos = {(x,y): (y,x_max-x) for x,y in G.nodes()}
nx.draw(G,with_labels=True,pos=pos,node_color='coral',node_size=1000)

enter image description here

# draw graph with numbers
labels = dict(np.ndenumerate(arr))
node_color = ['coral' if labels[n] == 255 else 'lightgrey' for n in G.nodes()]
nx.draw(G,labels=labels,node_color=node_color,node_size=1000)

enter image description here

# build subgraph
select = np.argwhere(arr < 255)
G1 = G.subgraph(map(tuple,select))

# draw subgraph
pos = {(x,y in G1.nodes()}
labels1 = {n:labels[n] for n in G1.nodes()}
nx.draw(G1,labels=labels1,node_color='lightgrey',node_size=1000)

输出:

# find connected components and DFS trees
for i in nx.connected_components(G1):
    source = next(iter(i))
    idx = nx.dfs_tree(G1,source=source)
    print(arr[tuple(np.array(idx).T)])
,

因此,在对连接组件的适当实现进行了大量研究之后,我想到了解决方案。为了在性能方面达到最佳状态,我依靠以下规则:

  1. 请勿使用networkx,因为根据此benchmark来说它很慢
  2. 根据this answer,基于Python的迭代速度很慢,因此请尽可能使用向量化操作。

我在这里实现图像的连接部分的算法只是因为我相信这是这个问题的必要部分。

图像的连接组件算法

import numpy as np
import numexpr as ne
import pandas as pd
import igraph

def get_coords(arr):
    x,y = np.indices(arr.shape)
    mask = arr != 255
    return  np.array([x[mask],y[mask]]).T

def compare(r1,r2):
    #assuming r1 is a sorted array,returns:
    # 1) locations of r2 items in r1
    # 2) mask array of these locations
    idx = np.searchsorted(r1,r2)
    idx[idx == len(r1)] = 0
    mask = r1[idx] == r2
    return idx,mask

def get_reduction(coords,s):
    d = {'s': s,'c0': coords[:,0],'c1': coords[:,1]}
    return ne.evaluate('c0*s+c1',d)

def get_bounds(coords,increment):
    return np.max(coords[1]) + 1 + increment

def get_shift_intersections(coords,shifts):
    # instance that consists of neighbours found for each node [[0,1,2],...]
    s = get_bounds(coords,10)
    rdim = get_reduction(coords,s)
    shift_mask,shift_idx = [],[]
    for sh in shifts:
        sh_rdim = get_reduction(coords + sh,s)
        sh_idx,sh_mask = compare(rdim,sh_rdim)
        shift_idx.append(sh_idx)
        shift_mask.append(sh_mask)
    return np.array(shift_idx).T,np.array(shift_mask).T,def connected_components(coords,shifts):
    shift_idx,shift_mask = get_shift_intersections(coords,shifts)
    x,y = np.indices((len(shift_idx),len(shift_idx[0])))
    vertices = np.arange(len(coords))
    edges = np.array([x[shift_mask],shift_idx[shift_mask]]).T

    graph = igraph.Graph()
    graph.add_vertices(vertices)
    graph.add_edges(edges)
    graph_tags = graph.clusters().membership
    values = pd.DataFrame(graph_tags).groupby([0]).indices
    return values

coords = get_coords(arr)
shifts=((0,1),(1,0),(-1,1))
comps = connected_components(coords,shifts=shifts)

for c in comps:
    print(coords[comps[c]].tolist()) 

结果

[[1,[1,3],[2,4],[3,[4,4]]
[[1,6],[5,[6,6]]

说明

算法包括以下步骤:

  • 我们需要将图像转换为非白细胞的坐标。可以使用以下功能完成:

    def get_coords(arr):
        x,y = np.indices(arr.shape)
        mask = arr != 255
        return np.array([y[mask],x[mask]]).T
    

    为清楚起见,我将用X命名输出数组。这是该数组的输出,从视觉上看:

    enter image description here

  • 接下来,我们需要考虑与X相交的每个班次的所有单元格:

    enter image description here

    为此,我们应该解决几天前发布的problem of intersections。我使用多维numpy数组找到了quite difficult to do。多亏了Divakar,他提出了a nice way的降维方法,该包使用numexpr包,其操作速度甚至比numpy还要快。我在这里通过以下功能实现它:

    def get_reduction(coords,d)
    

    为了使其正常工作,我们应该设置一个边界s,该边界可以使用函数自动计算

    def get_bounds(coords,increment):
        return np.max(coords[1]) + 1 + increment
    

    或手动输入。由于算法需要增加坐标,因此对 坐标可能超出范围,因此我在这里使用了一个轻微的increment。最后,作为我在这里提到的帖子的一种解决方案,可以通过以下方式访问与任何其他坐标数组X(也减少为1D)相交的Y坐标索引(简化为1D)功能

    def compare(r1,r2):
        # assuming r1 is a sorted array,mask
    
  • 插入所有相应的arrays班次。如我们所见,上述函数输出两个变量:主集X中的索引位置数组及其掩码数组。可以使用idx[mask]找到合适的索引,并且由于此过程适用于每个班次,因此我在这种情况下实现了get_shift_intersections(coords,shifts)方法。

  • 最终:构造节点和边并从igraph获取输出。这里的要点是igraph仅在从0开始的连续整数节点上表现良好。这就是为什么我的脚本被设计为对X中的项目位置使用基于掩码的访问。我将在这里简要说明如何使用igraph

    • 我已经计算出坐标对:

        [[1,6]]
      
    • 然后我为它们分配了整数:

        [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15]
      
    • 我的边缘看起来像这样:

        [[0,5],7],8],9],[7,10],[8,12],[9,13],[10,11],[11,[13,14],[14,15]]
      
    • graph.clusters().membership的输出如下所示:

        [0,1]
      
    • 最后,我使用了groupby的{​​{1}}方法来查找单独组的索引(我在这里使用Pandas是因为我发现它是Python中最有效的分组方式)

注释

下载Pandas is not straightforward,您可能需要从非官方二进制文件安装它。

本文链接:https://www.f2er.com/2842641.html

大家都在问