[Initial Image Segmentation Generator]论文实现:Efficient Graph-Based Image Segmentation

简介: [Initial Image Segmentation Generator]论文实现:Efficient Graph-Based Image Segmentation

一、完整代码

作者在文章开头地址中使用C++实现了这一过程,为了便于python学者理解,这里我们使用python代码进行实现

1.1 Python 完整程序

import cv2
import matplotlib.pyplot as plt
import numpy as np
class UnionFindSet:
    """
    创建一个交并集
    """
    def __init__(self, datalist):
        self.father = {}
        self.size = {}
        for data in datalist:
            self.father[data] = data
            self.size[data] = 1
    def find(self, node):
        father = self.father[node]
        if father != node:
            if father != self.father[father]:
                self.size[father] -= 1
            father = self.find(father)
        self.father[node] = father
        return father
    def union(self, node_a, node_b):
        if node_a is None or node_b is None:
            return
        a_head = self.find(node_a)
        b_head = self.find(node_b)
        if a_head != b_head:
            a_size = self.size[a_head]
            b_size = self.size[b_head]
            if a_size >= b_size:
                self.father[b_head] = a_head
                self.size[a_head] += b_size
            else:
                self.father[a_head] = b_head
                self.size[b_head] += a_size
    def the_same(self, node_a, node_b):
        return self.find(node_a) == self.find(node_b)
class Edge:
    def __init__(self, vi, vj, weight):
        self.vi = vi
        self.vj = vj
        self.weight = weight
def gaussian_blur(img, kernel_size, sigma):
    """
    img: the pic
    kernel_size: size
    sigma: Gaussian sigma
    output: img
    """
    img = cv2.GaussianBlur(np.array(img),(kernel_size,kernel_size),sigma,sigma)
    return img
def calculate_weight(img, x1, y1, x2, y2):
    r = img[x1][y1][0] - img[x2][y2][0]
    g = img[x1][y1][1] - img[x2][y2][1]
    b = img[x1][y1][2] - img[x2][y2][2]
    return np.sqrt(np.sum(np.square(np.array([r, g, b]))))
def calculate_weights(img, h, w):
    lst = []
    for i in range(h):
        for j in range(w):
            # 右上角
            if i != 0 and j < w - 1:
                weight = calculate_weight(img, i, j, i - 1, j + 1)
                edge = Edge(i * w + j, (i - 1) * w + j + 1, weight)
                lst.append(edge)
            # 右
            if j < w - 1:
                weight = calculate_weight(img, i, j, i, j + 1)
                edge = Edge(i * w + j, i * w + j + 1, weight)
                lst.append(edge)
            # 右下角
            if i != h - 1 and j < w - 1:
                weight = calculate_weight(img, i, j, i + 1, j + 1)
                edge = Edge(i * w + j, (i + 1) * w + j + 1, weight)
                lst.append(edge)
            # 下
            if i != h - 1:
                weight = calculate_weight(img, i, j, i + 1, j)
                edge = Edge(i * w + j, (i + 1) * w + j, weight)
                lst.append(edge)
    lst = sorted(lst, key=lambda x: x.weight)
    return lst
def get_size(S, node):
    return S.size[S.find(node)]
def main(img, k, min_size):
    h, w, chanles = img.shape
    lst = calculate_weights(img.astype(int), h, w)
    S = UnionFindSet(np.arange(h * w))
    Int = np.zeros(shape=h * w)
    # 因为从小到大排列 diff 就是weight
    for edge in lst:
        vi = edge.vi
        vj = edge.vj
        weight = edge.weight
        if not S.the_same(vi, vj) and weight <= min(Int[vi] + k / get_size(S, vi), Int[vj] + k / get_size(S, vj)):
            S.union(vi, vj)
            Int[vi] = Int[vj] = max(Int[vi], Int[vj], weight)
    for edge in lst:
        vi = edge.vi
        vj = edge.vj
        if not S.the_same(vi, vj) and (get_size(S, vi) <= min_size or get_size(S, vj) <= min_size):
            S.union(vi, vj)
            Int[vi] = Int[vj] = max(Int[vi], Int[vj], weight)
    trade = dict(zip(set(S.father.values()), range(len(set(S.father.values())))))
    img_ = np.array([trade[item] for item in list(S.father.values())]).reshape(h, w)
    return img_
def felzenszwalb(img, k, min_size):
    img = gaussian_blur(img, 5,5)
    img_ = main(img, k, min_size)
    return img_
def get_color(origin_img, img):
    """
    orgin_img: origin img
    img: after process img
    """
    new_img = np.zeros_like(origin_img)
    for i in range(np.max(img)):
        new_img[:,:,0][img == i] = np.random.randint(255)
        new_img[:,:,1][img == i] = np.random.randint(255)
        new_img[:,:,2][img == i] = np.random.randint(255)
    return new_img
if __name__ == '__main__':
    img = plt.imread('../data/cat-dog.jpg')
    img_ = felzenszwalb(img, 5000, 500)
    new_img = get_color(img, img_)
    plt.imshow(new_img)
    plt.show()

1.2 skimage 导包

import numpy as np
from skimage import segmentation
import matplotlib.pyplot as plt
def get_color(origin_img, img):
    """
    orgin_img: origin img
    img: after process img
    """
    new_img = np.zeros_like(origin_img)
    for i in range(np.max(img)):
        new_img[:,:,0][img == i] = np.random.randint(255)
        new_img[:,:,1][img == i] = np.random.randint(255)
        new_img[:,:,2][img == i] = np.random.randint(255)
    return new_img
if __name__ == '__main__':
    img = plt.imread('../data/cat-dog.jpg')
    img_ = segmentation.felzenszwalb(
        img,
        scale=500,
        sigma=0.95
    )
    new_img = get_color(img, img_)
    plt.imshow(new_img)
    plt.show()

二、论文解读

2.1 目的及其意义

目的:将图像根据颜色RGB特征按区域进行分割

意义:将分割后得到的区域进行有效筛选后,可以进行例如区域推荐,目标检测等等高级操作,当然其本质还是图像分割

特点:该方法的一个重要特点是,它能够在低变异性图像区域保留细节,而忽略了高变异性图像区域的细节。也就是说在变化不大的区域可以对细节进行保留,在变化很大的区域对细节进行剔除,这个效果根据后面合并小区域得到的。

2.2 框架以及指标

正如论文题目所说,这个方法是基于Graph的图像分割技术,是用图 G = ( V , E ) G=(V,E) G=(V,E) 来表达问题并对问题进行分析的,在此问题中, G G G是无向图,其 V V V中每一个节点对应于图中每一个像素点的位置, E E E中的每一条边表示相邻的两个节点的信息以及其差异值即权重。


权重定义如下:

Screenshot_20240510_101950.jpg

其中 r i , g i , b i r_i, g_i,b_i ri,gi,bi是图像在三个通道上的值的大小;


现在问题已经表示完毕,接下来我们需要对图像进行分割,其本质就是把 V V V中的节点分成不同的集合 C 1 , C 2 , … , C m . C_1,C_2, \dots,C_m. C1,C2,,Cm. 为了表示集合中节点的整体关系,我们定义一个指标:

 

Screenshot_20240510_102131.jpg

Int(C)=eMST(C,E)maxw(vi,vj) 这个指标可以表示集合内部的最大差异,其中MST是最小生成树,论文的算法主要是围绕MST的Kruskal算法展开,在这里我们可以通过对 E E E排序的方式进行构造,并不需要对MST的生成有要求,感兴趣可以自行了解;


上面对集合内部节点整体关系定义了一个指标,我们还需要对集合之间的关系定义一个指标,指标如下:


Screenshot_20240510_102233.jpg

该指标可以表示集合之间的最小差异;


我们如何对集合之间进行合并呢?定义以下指标: Screenshot_20240510_101514.jpg

{TrueDiff(C1,C2)>MInt(C1,C2)Falseotherwise

\begin{cases} True & \quad Diff(C_1, C_2) > MInt(C_1, C_2)\\ False & \quad otherwise \end{cases}

其中True表示合并,False表示不合并,其中


Screenshot_20240510_101810.jpg


这里 k k k是一个常数, ∣ C ∣ |C| C表示集合 C C C的大小;

指标定义到此完毕!

2.3 论文算法流程

算法原文如下图所示:

  1. 初始化,设  V中有n个节点,  E中有m条边, image.png 即每一个节点单独成一个集合
  2. 对 E E E根据权重进行由小到大的排序
  3. 遍历 E E E中每个元素,得到每个元素中包含的两个节点 image.png 找到 image.png 属于的集合 image.png 如果 C i = C j ,则跳过,否则计算 D ( C i , C j )判断是否合并,得到 image.png
  4. image.png

三、过程实现

为了适应实际情况,实现分为三步走:高斯模糊 -> 主要算法 -> 随机上色

3.1 导包

import cv2
import numpy as np
import matplotlib.pyplot as plt

3.2 高斯模糊

由于是对图像进行分割,所以不需要图片过于细节,把图像进行高斯平滑处理,去掉细节

def gaussian_blur(img, kernel_size, sigma):
    """
    img: the pic
    kernel_size: size
    sigma: Gaussian sigma
    output: img
    """
    img = cv2.GaussianBlur(np.array(img),(kernel_size,kernel_size),sigma,sigma)
    return img

在kernel_size = 5, sigma = 5 得到结果如下:

左边是原图, 右边是平滑过后的图像;

3.3 主要算法

首先创建一个并查集类

class UnionFindSet:
    """
    创建一个并查集
    """
    def __init__(self, datalist):
        self.father = {}
        self.size = {}
        for data in datalist:
            self.father[data] = data
            self.size[data] = 1
    
    def find(self, node):
        father = self.father[node]
        if father != node:
            if father != self.father[father]:
                self.size[father] -= 1
            father = self.find(father)
        self.father[node] = father
        return father
    def union(self, node_a, node_b):
        if node_a is None or node_b is None:
            return 
        
        a_head = self.find(node_a)
        b_head = self.find(node_b)
        if a_head != b_head:
            a_size = self.size[a_head]
            b_size = self.size[b_head]
            if a_size >= b_size:
                self.father[b_head] = a_head
                self.size[a_head] += b_size
            else:
                self.father[a_head] = b_head
                self.size[b_head] += a_size
    
    def the_same(self, node_a, node_b):
        return self.find(node_a) == self.find(node_b)

再进行主要算法计算

class Edge:
    def __init__(self, vi, vj, weight):
        self.vi = vi
        self.vj = vj
        self.weight = weight
def calculate_weight(img, x1, y1, x2, y2):
    r = img[x1][y1][0] - img[x2][y2][0]
    g = img[x1][y1][1] - img[x2][y2][1]
    b = img[x1][y1][2] - img[x2][y2][2]
    return np.sqrt(np.sum(np.square(np.array([r,g,b]))))
def calculate_weights(img, h, w):
    lst = []
    for i in range(h):
        for j in range(w):
            # 右上角
            if i != 0 and j < w-1:
                weight = calculate_weight(img, i, j, i-1,j+1)
                edge = Edge(i*w+j, (i-1)*w+j+1, weight)
                lst.append(edge)
            # 右
            if j < w-1:
                weight = calculate_weight(img, i, j, i, j+1)
                edge = Edge(i*w+j, i*w+j+1, weight)
                lst.append(edge)
            # 右下角
            if i != h-1 and j < w-1:
                weight = calculate_weight(img, i, j, i+1, j+1)
                edge = Edge(i*w+j, (i+1)*w+j+1, weight)
                lst.append(edge)
            # 下
            if i != h-1:
                weight = calculate_weight(img, i, j, i+1, j)
                edge = Edge(i*w+j, (i+1)*w+j, weight)
                lst.append(edge)
    lst = sorted(lst, key=lambda x:x.weight)
    return lst
def get_size(S, node):
    return S.size[S.find(node)]
def main(k, min_size):
    h, w, chanles = img.shape
    lst = calculate_weights(img.astype(int), h, w)
    S = UnionFindSet(np.arange(h*w))
    Int = np.zeros(shape=h*w)
    
    # 因为从小到大排列 diff 就是weight
    for edge in lst:
        vi = edge.vi
        vj = edge.vj
        weight = edge.weight
        if not S.the_same(vi, vj) and weight <= min(Int[vi] + k/get_size(S, vi), Int[vj] + k/get_size(S, vj)):
            S.union(vi, vj)
            Int[vi] = Int[vj] = max(Int[vi], Int[vj], weight)
            
    # 合并小集合
    for edge in lst:
        vi = edge.vi
        vj = edge.vj
        if not S.the_same(vi, vj) and (get_size(S, vi) <= min_size or get_size(S, vj) <= min_size):
            S.union(vi, vj)
            Int[vi] = Int[vj] = max(Int[vi], Int[vj], weight)
    trade = dict(zip(set(S.father.values()),range(len(set(S.father.values())))))
    img_ = np.array([trade[item] for item in list(S.father.values())]).reshape(h,w)
    return img_
main(5000,500)

3.4 随机上色

相比于之前的步骤,这一步很简单

def get_color(origin_img, img):
    """
    orgin_img: the original img
    img: the img after process
    """
    new_img = np.zeros_like(origin_img)
    for i in range(np.max(img)):
        new_img[:,:,0][img == i] = np.random.randint(255)
        new_img[:,:,1][img == i] = np.random.randint(255)
        new_img[:,:,2][img == i] = np.random.randint(255)
    return new_img

得到结果如下:

四、整体总结

没什么好总结的,干就完了!


目录
相关文章
|
3月前
|
机器学习/深度学习 人工智能 文件存储
【小样本图像分割-3】HyperSegNAS: Bridging One-Shot Neural Architecture Search with 3D Medical Image Segmentation using HyperNet
本文介绍了一种名为HyperSegNAS的新方法,该方法结合了一次性神经架构搜索(NAS)与3D医学图像分割,旨在解决传统NAS方法在3D医学图像分割中计算成本高、搜索时间长的问题。HyperSegNAS通过引入HyperNet来优化超级网络的训练,能够在保持高性能的同时,快速找到适合不同计算约束条件的最优网络架构。该方法在医疗分割十项全能(MSD)挑战的多个任务中展现了卓越的性能,特别是在胰腺数据集上的表现尤为突出。
34 0
【小样本图像分割-3】HyperSegNAS: Bridging One-Shot Neural Architecture Search with 3D Medical Image Segmentation using HyperNet
|
5月前
|
机器学习/深度学习 编解码 自然语言处理
【文献学习】An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale
本文介绍了如何使用纯Transformer模型进行图像识别,并讨论了模型的结构、训练策略及其在多个图像识别基准上的性能。
186 3
|
7月前
|
人工智能 自然语言处理 PyTorch
CLIP(Contrastive Language-Image Pre-training)
CLIP(Contrastive Language-Image Pre-training)
370 0
|
算法 PyTorch 算法框架/工具
论文解读:LaMa:Resolution-robust Large Mask Inpainting with Fourier Convolutions
论文解读:LaMa:Resolution-robust Large Mask Inpainting with Fourier Convolutions
787 0
|
机器学习/深度学习 编解码 人工智能
Text to image综述阅读(2)A Survey and Taxonomy of Adversarial Neural Networks for Text-to-Image Synthesis
这是一篇用GAN做文本生成图像(Text to Image)的综述阅读报告。 综述名为:《A Survey and Taxonomy of Adversarial Neural Networks for Text-to-Image Synthesis》,发表于2019年,其将文本生成图像分类为Semantic Enhancement GANs, Resolution Enhancement GANs, Diversity Enhancement GANs, Motion Enhancement GANs四类,并且介绍了代表性model。
Text to image综述阅读(2)A Survey and Taxonomy of Adversarial Neural Networks for Text-to-Image Synthesis
|
机器学习/深度学习 自然语言处理 算法
SS-AGA:Multilingual Knowledge Graph Completion with Self-Supervised Adaptive Graph Alignment 论文解读
预测知识图(KG)中缺失的事实是至关重要的,因为现代知识图远未补全。由于劳动密集型的人类标签,当处理以各种语言表示的知识时,这种现象会恶化。
117 0
|
机器学习/深度学习 编解码 自然语言处理
DeIT:Training data-efficient image transformers & distillation through attention论文解读
最近,基于注意力的神经网络被证明可以解决图像理解任务,如图像分类。这些高性能的vision transformer使用大量的计算资源来预训练了数亿张图像,从而限制了它们的应用。
578 0
|
机器学习/深度学习 移动开发 算法
DISCOBOX: Weakly Supervised Instance Segmentation and Semantic Correspondence from Box Supervision
定位和识别物体的能力是人类视觉的核心。这促使视觉社区研究对象检测 [1] 作为一项基本的视觉识别任务。在检测之上进一步引入实例分割 [2] 以预测前景对象掩码,从而实现像素级精度的定位。
103 0
|
机器学习/深度学习 传感器 编解码
Spatial-Spectral Transformer for Hyperspectral Image Classification_外文翻译
 由于成像光谱学的进步,高光谱传感器倾向于以越来越高的空间和光谱分辨率捕获给定场景的反射强度[1]。获得的高光谱图像(HSI)同时包含空间特征和不同物体的连续诊断光谱[2]。因此,获得的丰富信息使HSI在许多领域有用,包括有效测量农业绩效[3]、植物病害检测[4]、矿物鉴定[5]、疾病诊断和图像引导手术[6]、生态系统测量[7],和地球监测[8]。为了充分利用获得的HSI,已经探索了许多数据处理技术,例如解混合、检测和分类[8]。
241 0
|
数据挖掘
【多标签文本分类】Initializing neural networks for hierarchical multi-label text classification
【多标签文本分类】Initializing neural networks for hierarchical multi-label text classification
135 0
【多标签文本分类】Initializing neural networks for hierarchical multi-label text classification

热门文章

最新文章