Hello,我是kk~
几天前,和一个博士同学在吃饭,聊到明年就要毕业,不知道是继续努力进高校还是卷入互联网大军。
我的建议当然是进高校,然后继续和一帮同学做他喜欢的科研~
而且聊起来算法模型,各个模型的优缺点,滔滔不绝,很明显感觉到他对视觉方面这些特别熟悉。
平常没事就是手推公式、记录详细过程。
同时,今天总结了关于计算机视觉方面的十大图像分割算法。
大家记得点赞、收藏,慢慢学习~
全局阈值法
Canny 边缘检测
K-means 聚类
区域增长算法
图割 (Graph Cut)
卷积神经网络 U-Net
分水岭算法
Otsu's 方法
均值漂移 (Mean Shift) 算法
语义分割网络 FCN
全局阈值法
全局阈值法是一种简单而常用的图像分割方法,用于将图像分成背景和前景两个部分。其基本原理是选择一个阈值,将图像中的像素灰度值与该阈值进行比较,大于阈值的像素被归为一类,小于等于阈值的像素被归为另一类。
原理
1. 设定阈值:选择一个适当的阈值,通常根据图像的直方图来确定。 2. 像素分类:根据阈值,将图像中的像素分为两类,一类是大于阈值的像素,另一类是小于等于阈值的像素。 3. 分割图像:将分好类的像素标记成背景或前景。
阈值法根据以下规则来进行图像分割:
若像素灰度值大于阈值,则归为前景;
若像素灰度值小于等于阈值,则归为背景。
注意点
阈值的选择对结果影响较大,因此需要根据具体情况选取适当的阈值。
全局阈值法适用于背景与前景相对明显的图像,对于光照不均匀或者有噪声的图像效果可能不佳。
Python代码
import cv2# 读取图像image = cv2.imread('lenna.jpg', 0) # 以灰度模式读取图像# 全局阈值法threshold_value, thresholded_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Thresholded Image', thresholded_image)cv2.waitKey(0)cv2.destroyAllWindows()
全局阈值法是一种简单直观的图像分割方法,通过选择一个合适的阈值将图像分成背景和前景两部分。虽然简单,但在处理背景与前景对比明显的图像时具有一定效果。但对于光照不均匀或者有噪声的图像,其效果可能较差。
Canny 边缘检测
Canny边缘检测是一种经典的图像处理算法,用于检测图像中的边缘。它由John F. Canny于1986年提出,是一种多阶段的算法,包括高斯滤波、计算梯度、非极大值抑制和双阈值处理。
原理
1. 高斯滤波:为了降低图像中的噪声,先对图像进行高斯平滑处理。 2. 计算梯度:使用Sobel算子计算图像中每个像素点的梯度幅值和方向。 3. 非极大值抑制:在梯度方向上,将非边缘点抑制掉,只保留局部梯度最大值处的像素点。 4. 双阈值处理:设定两个阈值,一个较高的阈值和一个较低的阈值。如果像素的梯度大于高阈值,则被认为是强边缘;如果梯度在高低阈值之间,则被认为是弱边缘;如果梯度小于低阈值,则被抑制。
注意点
高斯滤波用于降低图像中的噪声,但也会导致边缘信息的模糊。
需要合适地选择梯度阈值,以保留有效的边缘信息。
对于不同的图像,可能需要调整参数以获得最佳的结果。
Python代码
import cv2# 读取图像image = cv2.imread('lenna.jpg', 0) # 以灰度模式读取图像# Canny边缘检测edges = cv2.Canny(image, 100, 200) # 100为低阈值,200为高阈值# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Canny Edges', edges)cv2.waitKey(0)cv2.destroyAllWindows()
Canny边缘检测是一种经典的图像边缘检测算法,通过多个步骤对图像进行处理,最终得到清晰的边缘信息。它在计算梯度、非极大值抑制和双阈值处理等方面都有独特的优势,能够有效地检测图像中的边缘。然而,对于不同的图像,需要进行参数调整以获得最佳效果。
K-means 聚类
K-means聚类是一种常用的无监督学习算法,用于将数据集分成K个不同的类别。在计算机视觉中,K-means聚类可以用于图像分割、特征提取等任务。
原理
1. 初始化:随机选择K个数据点作为初始的聚类中心。 2. 分配:对于每个数据点,计算其与每个聚类中心的距离,并将其分配到距离最近的聚类中心所对应的类别。 3. 更新:计算每个类别中所有数据点的均值,将这些均值作为新的聚类中心。 4. 迭代:重复步骤2和步骤3,直到聚类中心不再发生变化或达到最大迭代次数。
公式
K-means聚类的目标是最小化所有数据点与其所属聚类中心之间的距离的平方和,即最小化以下目标函数:
其中,
是第
个聚类的数据点集合,
是第
个聚类的中心。
注意点
K-means聚类对初始聚类中心的选择敏感,不同的初始聚类中心可能导致不同的聚类结果。
对于数据量较大的情况,K-means聚类的计算复杂度较高,可能需要较长的时间来运行。
聚类的数量
需要事先指定,并且需要根据具体问题进行调整。
Python代码
import numpy as npimport cv2# 读取图像image = cv2.imread('lenna.jpg')image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # 转换为RGB颜色空间# 将图像数据转换为二维数组pixels = image.reshape((-1, 3)).astype(np.float32)# K-means聚类criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.2)k = 8 # 聚类的数量_, labels, centers = cv2.kmeans(pixels, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)# 将每个像素点的颜色替换为所属聚类中心的颜色segmented_image = centers[labels.flatten()].reshape(image.shape).astype(np.uint8)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Segmented Image', segmented_image)cv2.waitKey(0)cv2.destroyAllWindows()
K-means聚类是一种简单而有效的无监督学习算法,适用于数据集较大且类别明显的情况。在图像处理中,K-means聚类可以用于图像分割,将图像分成不同的区域或物体。然而,K-means聚类对初始聚类中心的选择较为敏感,可能会导致不同的聚类结果,因此需要进行适当的参数调整和结果验证。
区域增长算法
区域增长算法是一种图像分割算法,用于将图像分成具有相似特征的区域。其基本原理是从种子点开始,根据一定的生长准则逐步生长区域,直到达到停止条件为止。
原理
1. 初始化:选择一个种子点作为起始点。 2. 生长:从种子点开始,根据生长准则将相邻的像素加入到同一个区域中,直到不再满足生长条件为止。 3. 重复:对未被标记的像素点重复步骤2,直到所有像素点都被标记为止。
生长准则
区域增长算法的生长准则通常包括以下条件之一:
灰度相似性:像素与种子点的灰度值相似。
空间相似性:像素与种子点的空间距离小于一定阈值。
灰度和空间相似性的组合:综合考虑像素的灰度值和空间位置。
注意点
区域增长算法的结果取决于种子点的选择和生长准则的设置,需要根据具体问题进行调整。
算法可能会受到噪声和边缘效应的影响,需要进行适当的预处理和后处理。
对于大型图像,区域增长算法的计算量可能较大,需要考虑优化算法以提高效率。
Python代码
import cv2import numpy as npdef region_growing(image, seed): # 创建输出图像,标记已经生长的像素点 h, w = image.shape[:2] segmented = np.zeros_like(image, dtype=np.uint8) segmented[seed[0], seed[1]] = 255 # 生长准则 neighbors = [(1, 0), (-1, 0), (0, 1), (0, -1)] while True: # 记录上一次生长的像素点数量 num_seed_points = np.sum(segmented == 255) # 对当前已经生长的像素点进行拓展 for i in range(h): for j in range(w): if segmented[i, j] == 255: for dx, dy in neighbors: x, y = i + dx, j + dy if 0 <= x < h and 0 <= y < w and segmented[x, y] == 0: if abs(int(image[x, y]) - int(image[i, j])) < 10: # 灰度相似性条件 segmented[x, y] = 255 # 如果没有新的像素点被标记,结束生长 if np.sum(segmented == 255) == num_seed_points: break return segmented# 读取图像image = cv2.imread('lenna.jpg', cv2.IMREAD_GRAYSCALE)# 选择种子点seed_point = (100, 100) # 任意选择种子点# 区域增长segmented_image = region_growing(image, seed_point)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Segmented Image', segmented_image)cv2.waitKey(0)cv2.destroyAllWindows()
区域增长算法是一种基于种子点的图像分割算法,通过从种子点开始,根据一定的生长准则逐步生长区域来实现图像分割。该算法对种子点的选择和生长准则的设置非常敏感,需要根据具体问题进行调整。虽然简单,但是该算法通常需要大量的计算资源,对于大型图像可能效率较低。
图割 (Graph Cut)
图割(Graph Cut)是一种基于图论的图像分割算法,它通过在图像上构建图结构,将图像分割问题转化为图论中的最小割问题来解决。图割算法在计算机视觉中被广泛应用于图像分割、物体识别等任务。
原理
1. 构建图:将图像中的像素点作为图的节点,将像素之间的关系表示为图中的边。通常,两个相邻像素之间有一条边,边的权重表示像素之间的相似度。 2. 定义成本函数:在图上定义一个成本函数,它由两部分组成:数据项和平滑项。数据项衡量每个像素点被分配到前景或背景的成本,平滑项衡量相邻像素之间的一致性。 3. 最小割:将图分成两部分,前景和背景,使得成本函数最小化。这等价于在图上找到一个最小割,即移除一些边,将图分成两个部分,并且割掉的边权重之和最小。
公式
图割算法的目标是最小化成本函数,通常可以表示为以下形式:
其中,
表示第
个像素点的标签(前景或背景),
表示数据项,
表示平滑项,
表示相邻像素对的集合。
注意点
图割算法的效果受到成本函数的设计和参数的选择影响较大,需要根据具体问题进行调整。
对于大型图像,图割算法的计算量较大,可能需要较长的时间来运行。
图割算法的结果可能受到初始分割、图构建方式等因素的影响,可能需要进行后处理来改善结果。
Python代码
import cv2import numpy as npdef graph_cut(image): # 创建掩码,将需要分割的前景设置为1,其余设置为0 mask = np.zeros(image.shape[:2], np.uint8) rect = (50, 50, 450, 290) # 包含前景的矩形区域 bgd_model = np.zeros((1, 65), np.float64) # 背景模型 fgd_model = np.zeros((1, 65), np.float64) # 前景模型 # 使用 grabCut 函数进行图像分割 cv2.grabCut(image, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT) # 根据 mask 中的标记获取前景和可能的前景区域 mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8') segmented_image = image * mask2[:, :, np.newaxis] return segmented_image# 读取图像image = cv2.imread('lenna.jpg')# 进行图像分割segmented_image = graph_cut(image)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Segmented Image', segmented_image)cv2.waitKey(0)cv2.destroyAllWindows()
图割算法是一种基于图论的图像分割算法,通过在图像上构建图结构,并最小化一个定义良好的成本函数来实现图像分割。该算法对成本函数的设计和参数的选择敏感,需要根据具体问题进行调整。虽然在一定程度上能够得到良好的分割结果,但是对于大型图像可能需要较长的计算时间。
卷积神经网络 U-Net
U-Net是一种深度学习架构,广泛用于图像分割任务。它以其U形状的网络结构而得名,具有编码器(下采样路径)和解码器(上采样路径)两部分,通过特征融合机制帮助提高图像分割的精度。
原理
1. 编码器(下采样路径):
使用卷积层和池化层进行多次下采样,提取图像的高级特征。
在每个下采样步骤后,将特征图的维度减小,通道数量增加,捕捉不同尺度的特征。
2. 特征融合:
在编码器的每一层,将下采样路径中得到的特征图与对称的上采样路径中得到的特征图进行连接。
这种特征融合机制有助于将底层和顶层的信息相结合,提高模型对不同尺度信息的感知能力。
3. 解码器(上采样路径):
使用转置卷积层和卷积层进行多次上采样,逐步还原图像的空间分辨率。
每一步上采样都与编码器对称,并且与相应的特征融合。
4. 最终输出:
在解码器的最后一层使用适当的激活函数,如sigmoid(用于二分类)或softmax(用于多分类),生成最终的分割结果。
注意点
适当的数据增强和正则化可以有助于提高模型的泛化能力。
选择合适的损失函数,如二分类交叉熵损失函数(Binary Crossentropy)或多分类交叉熵损失函数(Categorical Crossentropy)。
使用预训练的模型或迁移学习可以在数据有限的情况下提高模型性能。
Python代码
import tensorflow as tffrom tensorflow.keras import layers, modelsdef unet(input_shape=(256, 256, 3), num_classes=1): # 编码器 inputs = tf.keras.Input(shape=input_shape) conv1 = layers.Conv2D(64, 3, activation='relu', padding='same')(inputs) conv1 = layers.Conv2D(64, 3, activation='relu', padding='same')(conv1) pool1 = layers.MaxPooling2D(pool_size=(2, 2))(conv1) # 解码器 conv2 = layers.Conv2D(128, 3, activation='relu', padding='same')(pool1) conv2 = layers.Conv2D(128, 3, activation='relu', padding='same')(conv2) up1 = layers.UpSampling2D(size=(2, 2))(conv2) up1 = layers.Concatenate()([up1, conv1]) # 最终输出 outputs = layers.Conv2D(num_classes, 1, activation='sigmoid')(up1) model = models.Model(inputs=inputs, outputs=outputs) return model# 使用示例model = unet(input_shape=(256, 256, 3), num_classes=1)model.summary()
U-Net是一种用于图像分割的深度学习架构,通过编码器和解码器的对称结构,以及特征融合机制,能够有效地捕捉不同尺度的特征信息。U-Net在医学图像分割等领域取得了显著的成就,但需要合适的数据和参数调整以获得良好的性能。
分水岭算法
分水岭算法(Watershed Algorithm)是一种基于图像梯度的图像分割算法,它模拟了水流在地形上流动的过程,将图像中的区域划分为不同的水池(区域)。该算法常用于分割具有不同颜色或纹理的物体。
原理
1. 梯度计算:对图像进行梯度计算,得到每个像素点的梯度幅值。 2. 标记初始化:将梯度图像转换为标记图像,其中每个局部极大值点被标记为不同的初始标记(区域)。 3. 区域生长:从标记图像的局部极大值点开始,模拟水流向低梯度处流动的过程,直到不同区域的水流相遇。 4. 分水岭线:在区域生长的过程中,形成了分割不同区域的分水岭线。
注意点
对于具有不同颜色或纹理的物体,分水岭算法效果较好,但对于具有相似颜色或纹理的物体,可能需要结合其他信息或者预处理来提高分割效果。
分水岭算法容易产生过分割(over-segmentation)或者欠分割(under-segmentation)的问题,需要通过后处理来改善结果。
算法的性能受到初始标记的选择和参数的设置影响较大,需要进行适当的调整。
Python代码
import cv2import numpy as np# 读取图像image = cv2.imread('lenna.jpg')gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 图像预处理_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)# 形态学操作kernel = np.ones((3, 3), np.uint8)opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)# 获取背景区域sure_bg = cv2.dilate(opening, kernel, iterations=3)# 获取前景区域dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)_, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)# 找到未知区域sure_fg = np.uint8(sure_fg)unknown = cv2.subtract(sure_bg, sure_fg)# 标记_, markers = cv2.connectedComponents(sure_fg)markers += 1markers[unknown == 255] = 0# 应用分水岭算法markers = cv2.watershed(image, markers)image[markers == -1] = [255, 0, 0] # 给分水岭线标记为红色# 显示结果cv2.imshow('Segmented Image', image)cv2.waitKey(0)cv2.destroyAllWindows()
分水岭算法是一种基于图像梯度的图像分割算法,模拟了水流在地形上流动的过程来实现图像分割。它适用于具有不同颜色或纹理的物体的分割任务,并且可以生成分割不同区域的分水岭线。然而,分水岭算法容易产生过分割或欠分割的问题,需要结合其他信息或者进行后处理来改善分割结果。
Otsu's 方法
Otsu's方法,也称为最大类间方差法,是一种自动图像阈值选择技术,用于将图像分成两个类别(一般是前景和背景),以便对图像进行二值化处理。
原理
Otsu's方法的基本原理是寻找阈值,使得前景和背景之间的类间方差最大化。通过最大化类间方差,可以有效地将图像的前景和背景分离开来。
1. 计算直方图:首先,计算图像的灰度直方图,得到每个灰度级别的像素数量。 2. 计算类间方差:对每个可能的阈值进行迭代计算,分别将图像分成两个类别。然后,计算这两个类别的加权方差之和,作为该阈值下的类间方差。3. 选择最大类间方差:选择使类间方差最大的阈值作为最终的分割阈值。
公式
Otsu's方法的目标是最大化类间方差,其数学表达式为:
其中,
是阈值
下的类间方差,
和
是阈值
下两个类别的像素比例,
和
是阈值
下两个类别的方差。
注意点
Otsu's方法适用于具有双峰直方图的图像,即前景和背景的灰度级别差异明显的情况。
如果图像的直方图是单峰的,Otsu's方法可能会产生不理想的结果。
在实际应用中,需要对图像进行预处理,如去除噪声、平滑图像等,以获得更好的阈值选择结果。
Python代码
import cv2# 读取图像image = cv2.imread('lenna.jpg', cv2.IMREAD_GRAYSCALE)# 使用Otsu's方法计算阈值_, thresholded_image = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image)cv2.imshow('Thresholded Image', thresholded_image)cv2.waitKey(0)cv2.destroyAllWindows()
Otsu's方法是一种简单且有效的图像阈值选择技术,通过最大化类间方差来选择最佳的分割阈值,将图像分成两个类别(一般是前景和背景)。它对于具有双峰直方图的图像有很好的效果,但对于单峰直方图的图像可能不适用。在实际应用中,可以结合其他图像处理技术进行预处理,以提高阈值选择的准确性。
均值漂移 (Mean Shift) 算法
均值漂移(Mean Shift)算法是一种基于密度估计的非参数图像分割和目标跟踪算法。它通过迭代地移动数据点(在特征空间中)的均值,直到收敛到密度估计的局部极大值,从而实现图像分割或目标跟踪。
原理
1. 初始化:选择一个初始点(种子点)作为目标点。 2. 确定领域:以目标点为中心,定义一个窗口或领域,根据指定的核函数(通常是高斯核函数),计算该领域内所有点的权重。 3. 计算均值漂移向量:计算每个点的均值漂移向量,即领域内点的加权平均值与目标点的距离加权平均值。 4. 更新目标点:将目标点移动到计算得到的均值漂移向量的位置。 5. 迭代:重复步骤2到步骤4,直到目标点收敛或达到最大迭代次数。
公式
均值漂移算法的数学表达如下:
其中,
表示在给定点
处的均值漂移向量,
是领域内的点,
是核函数。
注意点
核函数的选择对算法的性能和收敛速度有很大的影响,通常选择高斯核函数。
初始点的选择对算法的收敛速度和分割结果有影响,通常可以采用密度估计的方法进行选择。
算法的收敛性和稳定性受到参数的影响,如窗口大小和核函数带宽等,需要进行适当的调整。
Python代码
import numpy as npfrom sklearn.cluster import MeanShiftimport cv2# 读取图像image = cv2.imread('lenna.jpg')image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)# 将图像转换为一维数组image_flat = np.reshape(image_rgb, (-1, 3))# 使用均值漂移算法进行聚类ms = MeanShift(bandwidth=20)ms.fit(image_flat)# 获取聚类中心cluster_centers = ms.cluster_centers_# 将每个像素点映射到最近的聚类中心segmented_image_flat = np.zeros_like(image_flat)for i in range(len(image_flat)): pixel = image_flat[i] clustered_pixel = cluster_centers[np.argmin(np.linalg.norm(cluster_centers - pixel, axis=1))] segmented_image_flat[i] = clustered_pixel# 将一维数组转换回图像形状segmented_image = np.reshape(segmented_image_flat, image_rgb.shape)# 显示原始图像和处理后的图像cv2.imshow('Original Image', image_rgb)cv2.imshow('Segmented Image', segmented_image)cv2.waitKey(0)cv2.destroyAllWindows()
均值漂移算法是一种基于密度估计的图像分割和目标跟踪算法,通过迭代地移动数据点的均值来实现聚类。它在图像分割和目标跟踪中具有很好的效果,能够自动找到数据点的局部极大值,并将数据点聚集成不同的类别。然而,算法的性能和收敛速度受到参数的影响,需要进行适当的调整。
语义分割网络 FCN
FCN(Fully Convolutional Network)是一种用于语义分割的深度学习网络,它能够对图像中的每个像素进行分类,从而实现像素级的分割。
原理
FCN的核心思想是将传统的全连接层替换为全卷积层,从而使网络能够接受任意大小的输入图像,并输出相应大小的分割结果。主要包括以下几个步骤:
1. 特征提取:使用预训练的卷积神经网络(如VGG、ResNet等)作为特征提取器,将输入图像转换为高维的特征图。 2. 上采样:将最后一个全连接层替换为卷积转置层(转置卷积或反卷积),以便对特征图进行上采样,使其大小与原始输入图像相同。 3. 特征融合:将不同层次的特征图进行融合,以捕捉不同尺度的语义信息。 4. 分类:使用1x1卷积层对每个像素点进行分类,输出每个像素点属于各类别的概率。 5. 逐像素分类:对每个像素点的分类结果进行逐像素分类,得到最终的语义分割结果。
公式
FCN的前向传播过程没有显式的数学公式,而是通过卷积、上采样、特征融合等神经网络层的操作实现。
注意点
在训练FCN时,通常使用像素级的交叉熵损失函数来衡量预测结果与真实标签之间的差异。
在进行上采样时,需要适当选择转置卷积的参数,以平衡分辨率和运算效率。
由于FCN对输入图像大小不敏感,因此可以处理任意大小的图像,但需要注意内存和计算资源的限制。
Python代码
import tensorflow as tffrom tensorflow.keras import layers, modelsdef fcn(input_shape=(256, 256, 3), num_classes=2): # 特征提取 base_model = tf.keras.applications.VGG16(input_shape=input_shape, include_top=False) for layer in base_model.layers: layer.trainable = False # 上采样 x = base_model.output x = layers.Conv2DTranspose(256, (3, 3), strides=(2, 2), padding='same')(x) x = layers.Conv2D(128, (1, 1), activation='relu', padding='same')(x) # 特征融合 x = layers.Conv2D(64, (1, 1), activation='relu', padding='same')(x) x = layers.Conv2D(num_classes, (1, 1), activation='softmax', padding='same')(x) model = models.Model(inputs=base_model.input, outputs=x) return model# 使用示例model = fcn(input_shape=(256, 256, 3), num_classes=2)model.summary()
FCN是一种用于语义分割的深度学习网络,通过将全连接层替换为全卷积层,实现了端到端的像素级别的语义分割。它能够对任意大小的输入图像进行分割,并且能够捕捉不同尺度的语义信息,从而在语义分割任务中取得了显著的成绩。需要注意的是,在训练FCN时,需要适当调整损失函数、上采样参数等,以获得良好的分割效果。
最后
添加微信:kkcoder,备注:CV,拉你入群,一起学习。
好了,今天的内容先这样,继续想看解决什么问题,评论区留言~
都到这里了,记得点赞哦~