图像分割
图割(graph cut)
图割(Graph Cut)是图像处理领域中的一种基本技术,用于分割图像或者其他类型的图形。它的基本思想是将图像表示成一个图(图论中的概念),其中图的节点代表图像中的像素或者其他感兴趣的区域,图的边则代表节点之间的关系。
图割的主要应用之一是图像分割,即将图像中的像素分成若干个不同的区域或物体。这在计算机视觉领域中有广泛的应用,比如目标检测、图像编辑、医学图像分析等。
图割算法的基本原理是通过在图上定义一个能量函数(或成本函数),然后通过最小化这个能量函数来找到一个图的切割,将图分成若干部分。在图割中,割(Cut)指的是将图中的某些边删除,从而将图分成两个部分。最小割(Minimum Cut)就是寻找一个割,使得被割断的边的权重之和最小。
寻找最小割(minimum cut 或 min cut)等同于在源点和汇点间寻找最大流(maximum flow 或 max flow)
最大流不可能大于最小割,因为最大流所有的水流都一定经过最小割那些割边,流过的水流怎么可能比水管容量还大呢?
最大流不可能小于最小割,如果小,那么说明水管容量没有物尽其用,可以继续加大水流。
最大流 :把有向图看作是水管,容量就是能够通过该水管段最高单位流量。基于此类比,最大流就是从起点到终点所能达到的最高单位流量。
由于网上无法找到用最大流去做分割的相关文档以及相应的库,因此我推断,这种方法基本上已经被弃用,因此这里只做介绍不再进行实验。
利用聚类进行分割
利用聚类进行分割是一种计算机视觉和图像处理中常见的技术,它的目标是将图像中的像素分成几个不同的组(或类),每个组内的像素具有相似的特征。聚类分割的基本思想是根据像素之间的相似性将它们分组,从而形成具有某种内在结构的区域。
以下是利用聚类进行图像分割的基本步骤:
特征提取: 首先,需要从图像中提取用于聚类的特征。这些特征可以是像素的颜色、纹理、亮度等。在一些情况下,可以将图像像素的坐标也作为特征。
数据准备: 将提取的特征整理成一个特征向量,每个向量代表一个像素点的特征。这将形成一个数据集,其中每个样本都是一个特征向量。
选择聚类算法: 选择适合问题的聚类算法。K-Means是最常见的聚类算法之一,它将数据分成K个簇,每个簇由一个中心点代表。其他聚类算法包括层次聚类、DBSCAN等。
执行聚类: 使用选择的聚类算法对特征向量进行聚类。聚类算法将数据点分成不同的簇,每个簇都具有相似的特征。每个数据点都被分配到一个簇中。
分割图像: 根据聚类的结果,将原始图像中的像素分配给相应的簇。这将生成一个分割后的图像,其中每个像素都被赋予其所属的簇的特征。
后处理: 可能需要对分割结果进行一些后处理,以去除噪声、填充孔洞或平滑边界。这可以通过应用图像处理技术来实现。
显示结果: 将原始图像和分割后的图像进行对比显示,以便观察分割的效果。
聚类分割的优点在于它不需要预先定义对象的外观或位置,而是根据数据的内在结构来自动确定区域。然而,它也有一些限制,例如对初始聚类中心的敏感性、对聚类数的选择以及可能对噪声和变化敏感。
下面是用聚类进行分割的一个简单示例:
import cv2
import numpy as np
from sklearn.cluster import KMeans
# Load the image
image = cv2.imread('empire.jpg')
height, width, _ = image.shape
new_height = height // 5
new_width = width // 5
image = cv2.resize(image, (new_width, new_height))
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Reshape image into a feature vector
rows, cols, channels = image_rgb.shape
features = image_rgb.reshape(rows * cols, channels)
# Perform K-Means clustering
num_clusters = 3
kmeans = KMeans(n_clusters=num_clusters)
kmeans.fit(features)
# Get cluster labels for each pixel
cluster_labels = kmeans.labels_
# Assign pixels to cluster centers
segmented_image = kmeans.cluster_centers_[cluster_labels]
segmented_image = segmented_image.reshape(rows, cols, channels)
# Convert back to BGR for display
segmented_image_bgr = cv2.cvtColor(segmented_image.astype(np.uint8), cv2.COLOR_RGB2BGR)
# Display original and segmented images
cv2.imshow('Original Image', image)
cv2.imshow('Segmented Image', segmented_image_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到结果还不错,能够比较正确的区分不同区域的物体。
变分法
Chan-Vese 分割模型对于待分割图像区域假定一个分片常数图像模型。这里我们集中关注两个区域的情形,比如前景和背景,不过这个模型也可以拓展到多区域,
用一组曲线将图像分成两个区域,分割通过最小化模型能量给出:
E
(
Γ
)
=
λ
length
(
Γ
)
+
∫
Ω
1
(
I
−
c
1
)
2
d
x
+
∫
Ω
2
(
I
−
c
2
)
2
d
x
E(\Gamma)=\lambda\operatorname{length}(\Gamma)+\int_{\Omega_1}(I-c_1)^2\mathrm{d}\mathbf{x}+\int_{\Omega_2}(I-c_2)^2\mathrm{d}\mathbf{x}
E(Γ)=λlength(Γ)+∫Ω1(I−c1)2dx+∫Ω2(I−c2)2dx
简单的示例代码:
import rof
from numpy import *
from PIL import Image
im = array(Image.open('houses.png').convert("L"))
U,T = rof.denoise(im,im,tolerance=0.001)
t = 0.4 # 阈值
from matplotlib.pyplot import *
imsave('result.pdf',U < t*U.max())
imshow(U < t*U.max(),'gray')