[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

得到结果如下:

四、整体总结

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


目录
相关文章
|
9月前
|
算法 PyTorch 算法框架/工具
论文解读:LaMa:Resolution-robust Large Mask Inpainting with Fourier Convolutions
论文解读:LaMa:Resolution-robust Large Mask Inpainting with Fourier Convolutions
371 0
|
9月前
|
机器学习/深度学习 自然语言处理 算法
SS-AGA:Multilingual Knowledge Graph Completion with Self-Supervised Adaptive Graph Alignment 论文解读
预测知识图(KG)中缺失的事实是至关重要的,因为现代知识图远未补全。由于劳动密集型的人类标签,当处理以各种语言表示的知识时,这种现象会恶化。
61 0
|
9月前
|
机器学习/深度学习 编解码 自然语言处理
DeIT:Training data-efficient image transformers & distillation through attention论文解读
最近,基于注意力的神经网络被证明可以解决图像理解任务,如图像分类。这些高性能的vision transformer使用大量的计算资源来预训练了数亿张图像,从而限制了它们的应用。
265 0
|
9月前
|
自然语言处理 算法 知识图谱
DEGREE: A Data-Efficient Generation-Based Event Extraction Model论文解读
事件抽取需要专家进行高质量的人工标注,这通常很昂贵。因此,学习一个仅用少数标记示例就能训练的数据高效事件抽取模型已成为一个至关重要的挑战。
77 0
|
9月前
|
机器学习/深度学习 数据采集 自然语言处理
Efficient Zero-shot Event Extraction with Context-Definition Alignment论文解读
事件抽取(EE)是从文本中识别感兴趣的事件提及的任务。传统的工作主要以监督的方式为主。然而,这些监督的模型不能概括为预定义本体之外的事件类型。
58 0
|
11月前
|
数据可视化 数据挖掘
【论文解读】Dual Contrastive Learning:Text Classification via Label-Aware Data Augmentation
北航出了一篇比较有意思的文章,使用标签感知的数据增强方式,将对比学习放置在有监督的环境中 ,下游任务为多类文本分类,在低资源环境中进行实验取得了不错的效果
250 0
|
机器学习/深度学习 算法 图形学
Deep learning based multi-scale channel compression feature surface defect detection system
简述:首先应用背景分割和模板匹配技术来定义覆盖目标工件的ROI区域。提取的感兴趣区域被均匀地裁剪成若干个图像块,每个块被送到基于CNN的模型,以分类杂乱背景中不同大小的表面缺陷。最后,对空间上相邻且具有相同类别标签的图像块进行合并,以生成各种表面缺陷的识别图。
108 0
|
机器学习/深度学习 移动开发 算法
DISCOBOX: Weakly Supervised Instance Segmentation and Semantic Correspondence from Box Supervision
定位和识别物体的能力是人类视觉的核心。这促使视觉社区研究对象检测 [1] 作为一项基本的视觉识别任务。在检测之上进一步引入实例分割 [2] 以预测前景对象掩码,从而实现像素级精度的定位。
60 0
|
机器学习/深度学习 算法 数据挖掘
【多标签文本分类】Improved Neural Network-based Multi-label Classification with Better Initialization ……
【多标签文本分类】Improved Neural Network-based Multi-label Classification with Better Initialization ……
【多标签文本分类】Improved Neural Network-based Multi-label Classification with Better Initialization ……
|
机器学习/深度学习 编解码 文字识别
Hybrid Multiple Attention Network for Semantic Segmentation in Aerial Images(二)
Hybrid Multiple Attention Network for Semantic Segmentation in Aerial Images
151 0
Hybrid Multiple Attention Network for Semantic Segmentation in Aerial Images(二)