python - 使用 Python、Numpy 和 Scikit-Image/OpenCV 根据中间分水线的长度合并图像段

标签 python numpy opencv image-segmentation scikit-image

我正在研究一种基于分水岭的分割算法来分割荧光图像,例如:
enter image description here
结果,我获得了一个带有每个段标签的 Numpy 数组。如果荧光图像中的相应区域在它们之间具有足够大的强度下降,则它们由分水岭线分隔。对于非常大的强度下降,它们通过简单的阈值完全分离。上图的结果是这样的:
enter image description here
我的算法在绝大多数情况下都表现良好。但是,有时它有轻微的过度分割趋势。例如上图中的这种情况:
enter image description here
由于这些情况很难通过进一步处理基于强度的分割本身来改进(并且我冒着破坏其他东西的风险),我想根据它们之间的分水线的长度有选择地合并相邻的片段和上下两段的平均最大宽度。
我知道我必须逐个像素地做些什么:

  • 查找在其直接邻域中具有两个不同标签值的像素。为每个分段对(带有相应的分段标签)分别存储这些像素。
  • 计算每对相邻线段的这些像素的数量,以获得分水岭线的长度。
  • 计算相邻段的最大宽度(为简单起见为水平方向)。
  • 如果分水岭线长于两个线段平均宽度的给定阈值分数(用户定义),则合并相邻线段。我可以通过将标签转换为二进制掩码、在适用的情况下使用存储的像素填充分水岭线并重新标记二进制掩码来做到这一点。

  • 由于在 Python 中迭代单个像素通常很慢,我不确定如何为此编写高性能代码。因此,我正在寻找有关如何使用 Numpy 和 Skimage 实现此功能的建议(OpenCV 也是一种选择)。

    最佳答案

    您没有提供如何获得初始分割。尽管如此,我认为改进分水岭线可以解决您的问题,这可以在分水岭层次结构框架中完成,使用 Higra包裹。
    我通过图像补码指定分水岭的初始排序,并使用另一个属性(体积)重新计算其分水岭线。
    您描述的强度下降和面积是体积属性,您可以通过层次结构中的阈值来控制分割。
    这是一个工作示例:

    import cv2
    import numpy as np
    import higra as hg
    from skimage.morphology import remove_small_objects, label
    import matplotlib.pyplot as plt
    
    def main():
        img_path = "fig.png"
        img = cv2.imread(img_path)
        img = img[:,:,0].copy()
        img = img.max() - img
    
        size = img.shape[:2]
        graph = hg.get_4_adjacency_graph(size)
        
        edge_weights = hg.weight_graph(graph, img, hg.WeightFunction.mean)
        tree, altitudes = hg.quasi_flat_zone_hierarchy(graph, edge_weights)
    
        attr = hg.attribute_volume(tree, altitudes)
    
        saliency = hg.saliency(tree, attr) 
        # Take a look at this :)
        # grid = hg.graph_4_adjacency_2_khalimsky(graph, saliency)
        # plt.imshow(grid)
        # plt.show()
        
        attr_thold = np.mean(saliency) / 4  # arbitrary
        area_thold = 500  # arbitrary 
    
        segments = hg.labelisation_horizontal_cut_from_threshold(tree, attr, attr_thold)
        segments = label(remove_small_objects(segments, area_thold))
    
        plt.imshow(segments)
        plt.show()
    
    if __name__ == "__main__":
        main()
    
    这是结果。
    Result

    关于python - 使用 Python、Numpy 和 Scikit-Image/OpenCV 根据中间分水线的长度合并图像段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62896061/

    相关文章:

    python - Node js vs Django vs Flask用于从python流式传输多个视频

    python - 监听端口上的所有传入流量

    python - 寻找解析文件的策略

    python - 如何用分数标记 x 轴?

    opencv - 旋转矩形 - ANGLE 返回 - Opencv

    android - 从Android中的图像进行数字检测

    python - 收集推特数据

    python - 如何连接到只支持 xterm 终端的 linux 服务器

    python - 如何交换数组中的二维

    python - 使用 numpy 和 jax 进行非传递子类化