Advertisement

Python计算机视觉第九章-图像分割

阅读量:

目录

9.1 图割(Graph Cut)

9.1.1 从图像创建图

9.1.2 用户交互式分割

9.2 利用聚类进行分割

9.3 变分法


9.1 图割(Graph Cut)

在图论领域中,所谓的图形通常指的是由一组明确定义的顶点与连接它们的边共同组成的系统.

Graph partitioning involves dividing a directed graph into two disjoint subsets, which is commonly applied to address a variety of computer vision challenges such as stereo depth reconstruction, image stitching, and image segmentation tasks.

图割_C_(其中_C_代表图中所有边构成的一个集合)的cost函数定义为该edge cut的所有边权重之和

E_{cut}=um_{i,jn C}w_{ij}

wij 是图中节点 i 到节点 j 的边( ij )的权重,并且是对割 C 所有的边进行求和。

基于图模型的图像分割方法旨在通过构建权重矩阵W来描述像素间的相似性,并利用最小割算法求解最优分割方案。当构建分割模型时,在原始节点基础上增加两个特殊节点——源节点s和汇节点t;然后仅计算那些能够分离这两个特殊节点的切割路径。

基于 Python-graph 工具包进行分析的小型网络拓扑结构研究是一种有效的方法

代码:

复制代码
 import networkx as nx

    
  
    
 # 创建一个有向图
    
 G = nx.DiGraph()
    
  
    
 # 添加节点和边(源节点、目标节点、容量)
    
 edges = [
    
     ('s', 'a', 10),
    
     ('s', 'b', 5),
    
     ('a', 'b', 15),
    
     ('a', 't', 10),
    
     ('b', 't', 10)
    
 ]
    
  
    
 # 添加边到图中,确保所有边都有容量属性
    
 for u, v, capacity in edges:
    
     G.add_edge(u, v, capacity=capacity)
    
  
    
 # 计算最大流和最小割
    
 try:
    
     flow_value, flow_dict = nx.maximum_flow(G, 's', 't')
    
     cut_value, partition = nx.minimum_cut(G, 's', 't')
    
  
    
     print(f"最大流: {flow_value}")
    
     print("流的分布:")
    
     for key, value in flow_dict.items():
    
     print(f"  {key}: {value}")
    
  
    
     print(f"最小割值: {cut_value}")
    
     print(f"最小割分割: {partition}")
    
  
    
 except nx.NetworkXUnbounded as e:
    
     print("检测到无限容量路径,无法计算最大流和最小割。")
    
     print(str(e))
    
    
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-14/SdGFtR8gfek2L4s7qOT6NYxoC3hM.png)

分析:

图的构建

  • 我们构建了一个有向图 G
    • 被添加了一条带权重的边被连接到源节点 $s$.
      • 源端点从 $s$.
      • 目标端点分别连接到中间节点 $a$.
        • $b$.
      • 最终连接到目标节点 $t$.

计算最大流

复制代码
 * `nx.maximum_flow(G, 's', 't')` 计算了从源节点 `'s'` 到目标节点 `'t'` 的最大流及其流分布。

计算最小割

nx.minimum_cut(G, 's', 't') 执行了将图分为两个部分的最小割容量计算,并相应地确定了相应的分割。

结果:

9.1.1 从图像创建图

给定一个邻域结构,我们可以利用图像像素作为节点定义一个图。

下面给出创建这样一个图的步骤:

• 每个像素节点都有一个从源点的传入边;

• 每个像素节点都有一个到汇点的传出边;

• 每个像素节点都有一条传入边和传出边连接到它的近邻。

实验代码:

复制代码
 import cv2

    
 import numpy as np
    
 import networkx as nx
    
 import matplotlib.pyplot as plt
    
  
    
  
    
 def image_to_graph(image_path):
    
     # 读取图像并转换为灰度图
    
     image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    
  
    
     # 获取图像的尺寸
    
     height, width = image.shape
    
  
    
     # 创建一个无向图
    
     G = nx.Graph()
    
  
    
     # 遍历图像中的每一个像素点
    
     for y in range(height):
    
     for x in range(width):
    
         node = (x, y)
    
         G.add_node(node)
    
  
    
         # 添加与右边和下边的邻接边
    
         if x < width - 1:
    
             neighbor = (x + 1, y)
    
             G.add_edge(node, neighbor)
    
         if y < height - 1:
    
             neighbor = (x, y + 1)
    
             G.add_edge(node, neighbor)
    
  
    
     return G, image
    
  
    
  
    
 def analyze_graph(G):
    
     # 计算节点数量和边数量
    
     num_nodes = G.number_of_nodes()
    
     num_edges = G.number_of_edges()
    
     return num_nodes, num_edges
    
  
    
  
    
 def plot_graph(G, image):
    
     # 绘制图像和图
    
     plt.figure(figsize=(10, 10))
    
  
    
     # 绘制图像
    
     plt.subplot(1, 2, 1)
    
     plt.imshow(image, cmap='gray')
    
     plt.title('Image')
    
  
    
     # 绘制图
    
     plt.subplot(1, 2, 2)
    
     pos = {node: (node[0], -node[1]) for node in G.nodes()}  # 将 y 轴翻转,以便图形正确显示
    
     nx.draw(G, pos, with_labels=False, node_size=1, width=0.1, node_color='blue')
    
     plt.title('Graph')
    
  
    
     plt.show()
    
  
    
  
    
 if __name__ == "__main__":
    
     image_path = r'C:\Users\86156\PycharmProjects\pythonProject\qushuiyin\python\1.jpg'  # 替换为你的图像文件路径
    
  
    
     # 从图像创建图
    
     G, image = image_to_graph(image_path)
    
  
    
     # 分析图
    
     num_nodes, num_edges = analyze_graph(G)
    
     print(f'Number of nodes: {num_nodes}')
    
     print(f'Number of edges: {num_edges}')
    
  
    
     # 绘制图像和图
    
     plot_graph(G, image)
    
    
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-14/Wp7HTDgJN25hr1BKwGceES3untxf.png)

分析:

加载和处理图像 * 将图像进行灰度化处理。 * 将图像数据转化为图论中的节点及其之间的连接关系。

构建图结构:借助 networkx 工具构建图模型。

  • 将每个像素视为图中的一个节点。
  • 节点间的连接关系反映了像素之间的邻接情况。

分析图 * 计算图的节点数量和边数量。
* 可视化图。

结果:

9.1.2 用户交互式分割

采用若干方法将图像分割技术与用户体验进行集成。其中一种方式是让用户在一张图片中对前景和背景进行标注。此外还有一种技术是通过边界框(bounding box)或Lasso工具来限定前景区域。

实验代码:

复制代码
 import numpy as np

    
 import cv2
    
 from skimage import segmentation, color, graph
    
 from skimage.segmentation import slic
    
 import matplotlib.pyplot as plt
    
  
    
  
    
 def load_image(image_path):
    
     # 读取图像
    
     image = cv2.imread(image_path)
    
     image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)  # 转换为RGB格式
    
     return image
    
  
    
  
    
 def load_labels(label_path):
    
     # 读取标注图像,假设标注图像中的像素值为0(背景),1(前景)
    
     labels = cv2.imread(label_path, cv2.IMREAD_GRAYSCALE)
    
     return labels
    
  
    
  
    
 def apply_graph_cut(image, labels):
    
     # 将图像转换为Lab颜色空间
    
     image_lab = cv2.cvtColor(image, cv2.COLOR_RGB2Lab)
    
  
    
     # 创建超级像素
    
     segments = slic(image_lab, n_segments=400, compactness=10)
    
  
    
     # 创建区域邻接图
    
     g = graph.rag_mean_color(image_lab, segments, mode='distance')
    
  
    
     # 应用图割算法
    
     labels = segmentation.watershed(-color.rgb2gray(image), markers=labels, connectivity=1)
    
  
    
     return labels
    
  
    
  
    
 def plot_results(image, labels, result):
    
     fig, axes = plt.subplots(1, 3, figsize=(15, 5))
    
     ax = axes.ravel()
    
  
    
     ax[0].imshow(image)
    
     ax[0].set_title('Original Image')
    
     ax[0].axis('off')
    
  
    
     ax[1].imshow(labels, cmap='gray')
    
     ax[1].set_title('Labels')
    
     ax[1].axis('off')
    
  
    
     ax[2].imshow(result, cmap='gray')
    
     ax[2].set_title('Graph Cut Result')
    
     ax[2].axis('off')
    
  
    
     plt.show()
    
  
    
  
    
 if __name__ == "__main__":
    
     image_path = r'C:\Users\86156\PycharmProjects\pythonProject\qushuiyin\python\1.jpg'  # 替换为图像文件路径
    
     label_path = r'C:\Users\86156\PycharmProjects\pythonProject\qushuiyin\python\1_blurred.jpg'  # 替换为标注文件路径
    
  
    
     # 载入图像和标注
    
     image = load_image(image_path)
    
     labels = load_labels(label_path)
    
  
    
     # 应用图割分割
    
     result = apply_graph_cut(image, labels)
    
  
    
     # 可视化结果
    
     plot_results(image, labels, result)
    
    
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-14/W04rulDsepCwmzZPvSxYkRM7iOnN.png)

分析:

  1. load_image : 读取并获取原始图像数据并将其转换至RGB颜色空间。
  2. load_labels : 读取标注信息时,默认将背景区域像素值设为0,默认前景区域像素值设为1。
  3. apply_graph_cut : 采用图割技术执行分割操作。这里采用了watershed算法作为简化方案;在实际应用中建议采用更为复杂的实现以提高分割精度。
  4. plot_results : 展示原始图像、对应的标注信息以及分割结果的可视化界面。

结果:

9.2 利用聚类进行分割

另外一种实现图像分割的技术是基于谱图理论中的归一化分割算法。这种算法通过结合像素间的相似性和空间邻近关系来进行图像划分。其原理是引入一个新的目标函数用于衡量图像分区效果,并综合考虑组的数量以及区域划分数量来实现比例调节。经过这样的设计后,在方程(9.1)的基础上重新构建了改进后的目标函数表达式:

E_{ncut}=rac{E_{cut}}{um_{in A}w_{ix}}+rac{E_{cut}}{um_{jn B}w_{jx}}

谱图理论中的归一化切割方法(Normalized Cut, Ncut)是一种图像分割技术,在其基础原理上融合了图论与谱分析。该方法的核心在于将图像构建为节点网络,在此网络中各节点间的连接强度反映其相似性特征。归一化切割方法通过最小化割裂程度来优化分割方案,并在此频域内完成计算过程

下面是用谱图理论的归一化分割算法进行图像分割的实验:

实验代码:

复制代码
 import numpy as np

    
 from skimage import color, io, segmentation, feature
    
 from skimage.color import rgb2gray
    
 from sklearn.cluster import KMeans
    
 from scipy.sparse.csgraph import laplacian
    
 from scipy.sparse.linalg import eigsh
    
 import matplotlib.pyplot as plt
    
  
    
 # 读取图像
    
 image = io.imread('your_image.jpg')
    
 gray_image = rgb2gray(image)
    
  
    
 # 计算图像的相似度矩阵(使用欧氏距离)
    
 def construct_similarity_matrix(image, sigma=1.0):
    
     rows, cols = image.shape
    
     similarity_matrix = np.zeros((rows * cols, rows * cols))
    
     for i in range(rows):
    
     for j in range(cols):
    
         idx = i * cols + j
    
         for p in range(rows):
    
             for q in range(cols):
    
                 idx_pq = p * cols + q
    
                 distance = np.sqrt((i - p) ** 2 + (j - q) ** 2)
    
                 similarity_matrix[idx, idx_pq] = np.exp(-distance**2 / (2 * sigma**2))
    
     return similarity_matrix
    
  
    
 # 构建相似度矩阵
    
 similarity_matrix = construct_similarity_matrix(gray_image)
    
  
    
 # 计算拉普拉斯矩阵
    
 laplacian_matrix = laplacian(similarity_matrix, normed=True)
    
  
    
 # 计算拉普拉斯矩阵的前k个特征向量
    
 num_clusters = 2
    
 eigenvalues, eigenvectors = eigsh(laplacian_matrix, k=num_clusters, which='SM')
    
  
    
 # 使用K-means对特征向量进行聚类
    
 kmeans = KMeans(n_clusters=num_clusters, random_state=0).fit(eigenvectors)
    
 labels = kmeans.labels_.reshape(gray_image.shape)
    
  
    
 # 显示分割结果
    
 plt.figure(figsize=(12, 6))
    
 plt.subplot(1, 2, 1)
    
 plt.title('Original Image')
    
 plt.imshow(image)
    
 plt.subplot(1, 2, 2)
    
 plt.title('Segmented Image')
    
 plt.imshow(labels, cmap='gray')
    
 plt.show()
    
    
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-14/QBSNefZqPt4CJKWraYA0chysmxpD.png)

分析:

  • 代码解释 :在代码中首先获取并进行灰度化处理。
    接着,在图像处理阶段构建每对像素点之间的相似度矩阵。
    通过高斯核函数评估各像素之间的相似性程度。
    随后,在构建图结构后进行拉普拉斯矩阵的特征值分解。
    最后,在获得所有特征向量后应用K-means算法完成聚类。

结果:

改写说明

9.3 变分法

大量通过最小化代价函数或能量函数来解决计算机视觉问题的案例存在;例如,在图像处理中最小化割这一技术得到了广泛应用;同样地,在这些优化方法中可以看到诸如 ROF 降噪、K-means 和 SVM 的应用实例;这些都是典型的优化问题。

在处理函数的情形下,则称这个问题为变分问题;而用于解决此类问题的技术则被称为变分法

变分法的基本概念在于:对于一个给定的泛函J[u](记号),我们需要确定在所有满足特定边界条件和连续性要求的可变函数u(x)中所对应的那个使该泛函J[u]取得极值的具体函数形式。这一过程一般会采用通过寻找使该泛函达到极值的方式实现对未知函数u(x)的确定。在实际应用中,我们通常会借助变分学中的欧拉-拉格朗日方程来系统地求解这类问题。

在数学上,如果我们想找到使泛函J[u] 取极值的函数u,我们可以考虑泛函的变分(也就是其对函数的变化的导数)。如果 u是一个极值点,那么泛函J[u] 关于u 的变分为零:
rac{elta J}{elta u}=0

实验代码:

复制代码
 import numpy as np

    
 import scipy.optimize as opt
    
 import matplotlib.pyplot as plt
    
  
    
 # Define the functional J[u] we want to minimize
    
 def functional(u, x):
    
     # Assuming u is a 1D array representing the function values
    
     u = np.array(u)
    
     u_prime = np.gradient(u, x)  # Approximate the derivative
    
     return np.trapz(0.5 * u_prime**2 + V(x) * u**2, x)
    
  
    
 # Potential function V(x)
    
 def V(x):
    
     return 1.0  # Constant potential for simplicity
    
  
    
 # Define the grid for x
    
 x = np.linspace(0, 1, 100)
    
 u_init = np.sin(np.pi * x)  # Initial guess for the function
    
  
    
 # Optimize the function
    
 result = opt.minimize(lambda u: functional(u, x), u_init, method='BFGS')
    
  
    
 # Extract optimized function
    
 u_opt = result.x
    
  
    
 # Plot results
    
 plt.plot(x, u_init, label='Initial Guess')
    
 plt.plot(x, u_opt, label='Optimized Function')
    
 plt.xlabel('x')
    
 plt.ylabel('u(x)')
    
 plt.legend()
    
 plt.title('Variational Method Optimization')
    
 plt.show()
    
  
    
 # Output the result
    
 print("Optimized function:", u_opt)
    
    
    
    
    python
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-07-14/shvOcjmTwAYot5Zi1qLBRfSDKnEe.png)

分析:

  • 初步判断 :是基于假设的一个初始估计(例如三角函数)。
    • 计算结果 :经过求解过程得到的目标函数表达式u(x)。一般而言,在优化过程中我们会期望最终所得的目标函数形式能够更好地反映实际问题的本质需求。

结果:

全部评论 (0)

还没有任何评论哟~