Python----OpenCV(图像分割——彩色图像分割,GrabCut算法分割图像)

发布于:2025-07-14 ⋅ 阅读:(11) ⋅ 点赞:(0)

一、彩色图像分割

        彩色图像分割是利用图像的颜色特征,将不同区域或物体从图像中分割出来的方法。 相比灰度图像,彩色图像包含更多维度的信息(通常是3个通道),分割效果更丰富。

名称 组成通道 特点
RGB R、G、B 常规图像格式,易受光照影响
HSV H(色调)、S(饱和度)、V(亮度) 色调独立,分割色彩区域效果好
Lab L(亮度)、a(绿-红)、b(蓝-黄) 更接近人眼感知,常用于复杂环境

        cv2.inRange() 函数用于图像阈值操作,基于指定的范围对图像进行分割。它通过将图像的像素值与指定的范围进行比较,生成一个二值图像:在指定范围内的像素值对应为白色(255),不在范围内的像素值对应为黑色(0)。

cv2.inRange(src, lowerb, upperb)
参数 类型 说明
src 输入图像 可以是彩色图像(如 BGR、HSV 等格式)或单通道图像(如灰度图像)。
lowerb 数组或标量 低阈值,表示每个通道的最小值。对于多通道图像,应提供对应通道的阈值数组。
upperb 数组或标量 高阈值,表示每个通道的最大值。对于多通道图像,应提供对应通道的阈值数组。
返回值 类型 说明
掩膜图像 二值图像 与输入图像大小相同,像素值在 [lowerb, upperb] 范围内的置为 255,否则为 0。

        cv2.bitwise_and() 函数执行按位与(AND)操作。它用于将两个图像的对应像素进行按位与操作。通常用于图像遮罩操作,它可以将掩膜图像与原图像进行“与”操作,保留原图中掩膜图像为 255(白色)区域的部分,其他部分变为黑色(0)。

cv2.bitwise_and(src1, src2, mask=None)
参数 类型 说明
src1 输入图像1 可以是单通道或多通道图像(如灰度图、BGR、HSV 等)。
src2 输入图像2 必须与 src1 的尺寸和数据类型相同。
mask 可选掩膜 如果提供,仅对掩膜中非零的像素进行按位与操作,其余部分保持原值或置零。
返回值 类型 说明
输出图像 与输入相同类型 返回 src1 & src2 的按位与结果,若提供 mask,则仅影响掩膜区域。

彩色图像分割常见问题与解决方法

问题 原因 解决方法
光照变化敏感 RGB 亮度变化大 转换到 HSV/Lab 空间,使用 H 或 a、b 分量
颜色相近区域难区分 不同物体颜色接近 多通道联合分割、加入位置/纹理特征
背景杂乱 背景颜色复杂 先滤波去噪,再基于颜色空间提取目标色域
色域交叉 目标色域与背景部分重叠 结合多阈值、多特征、多步骤分割,提升鲁棒性
import cv2
import numpy as np
# 读取图片 
img=cv2.imread('./images/nezha.png')

# 将bgt转hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 创建窗口
cv2.namedWindow('Trackbars')

def callback(x):    
    pass

# 创建滑动条6条  H 0-179  S V 0-255
cv2.createTrackbar('H_min', 'Trackbars', 0, 179, callback)
cv2.createTrackbar('H_max', 'Trackbars', 179, 179, callback)
cv2.createTrackbar('S_min', 'Trackbars', 0, 255, callback)
cv2.createTrackbar('S_max', 'Trackbars', 255, 255, callback)
cv2.createTrackbar('V_min', 'Trackbars', 0, 255, callback)
cv2.createTrackbar('V_max', 'Trackbars', 255, 255, callback)

# 无限循环
while True:

    # 获取滑动条的值
    h_min = cv2.getTrackbarPos('H_min', 'Trackbars')
    h_max = cv2.getTrackbarPos('H_max', 'Trackbars')
    s_min = cv2.getTrackbarPos('S_min', 'Trackbars')
    s_max = cv2.getTrackbarPos('S_max', 'Trackbars')
    v_min = cv2.getTrackbarPos('V_min', 'Trackbars')
    v_max = cv2.getTrackbarPos('V_max', 'Trackbars')


    # 组装上下界 Numpy数组 分别表示hsv最小值和最大值
    lower = np.array([h_min, s_min, v_min])
    upper = np.array([h_max, s_max, v_max])

    #利用INRANGE函数对图像进行二值化
    mask = cv2.inRange(hsv, lower, upper)

    # 将二值图像与原图像进行位运算
    res = cv2.bitwise_and(img, img, mask=mask)

    cv2.imshow('img', img)
    cv2.imshow('mask', mask)
    cv2.imshow('res', res)

    # 按q键退出
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
  
cv2.destroyAllWindows()

二、 GrabCut算法分割图像

        GrabCut是一种基于图像分割的半监督算法,它在图像分割中通过将前景和背景建模为图像的高斯混合模型(GMM),以此来实现高效且准确的图像分割。GrabCut 结合了 图像边缘信息 和 用户输入的前景/背景框,可以快速分割出图像中的目标区域。

👉核心思想:

  • 高斯混合模型:对前景和背景分别建立高斯混合模型,并计算每个像素属于前景或背景的概率。

  • 迭代优化:通过迭代过程优化前景和背景的分割边界,使其逐步精确。

应用场景

  • 🎨 目标提取(如人像抠图、物体分割)。

  • 🖼️自动化图像编辑(如替换背景、去除背景)。

  • 🩺医学图像分析(如肿瘤检测、细胞分割)。

GrabCut 原理流程

  1. 用户输入矩形区域

    • 将矩形区域内作为潜在前景,矩形外作为背景。

  2. 高斯混合模型(GMM)建模

    • 利用 GMM 对前景和背景像素颜色分布进行建模。

  3. 构建图模型

    • 像素作为节点,节点之间根据像素相似性设置边的权重。

  4. 图割(Graph Cut)

    • 利用最小割/最大流算法,计算出前景和背景的最优分割。

  5. 迭代优化

    • 重复更新 GMM 参数,优化分割结果。

        GrabCut 是基于图割的前景提取算法,OpenCV 中直接封装成了 cv2.grabCut() 函数,调用简单。 

cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, mode)
参数 类型 说明
img 输入图像(3通道 BGR) 需要进行前景分割的彩色图像(BGR 格式)。
mask 单通道掩膜图像 初始化的掩膜,取值:
• 0cv2.GC_BGD):背景
• 1cv2.GC_FGD):前景
• 2cv2.GC_PR_BGD):可能背景
• 3cv2.GC_PR_FGD):可能前景
如果使用 cv2.GC_INIT_WITH_RECT,该参数会被修改。
rect (x, y, w, h) 元组 包含前景的矩形区域(仅 mode=cv2.GC_INIT_WITH_RECT 时使用)。
bgdModel np.float64 数组 内部背景模型,需初始化为 np.zeros((1, 65), np.float64)
fgdModel np.float64 数组 内部前景模型,需初始化为 np.zeros((1, 65), np.float64)
iterCount int 算法的迭代次数(通常 5 次即可,更高次数可能优化结果)。
mode int(可选) 运行模式:
• cv2.GC_INIT_WITH_RECT(默认,用矩形初始化)
• cv2.GC_INIT_WITH_MASK(用掩膜初始化)
import cv2
import numpy as np
# 读取图像
img=cv2.imread('./images/nezha.png')

# 复制图像
img_copy = img.copy()   
 
# ROI 区域fromCenter false 表示从左上角开始选择,true表示从中心开始选择 showCrosshair 是否显示十字架
rect = cv2.selectROI("请选择目标区域,然后按ENTER确定",img_copy, fromCenter=False, showCrosshair=True)
print(rect) # 输出选择的区域
cv2.destroyAllWindows()


# 创建掩膜
mask = np.zeros(img.shape[:2], np.uint8) # 创建一个全零的图像,大小和原图像一样,数据类型为uint8
# 创建前景和背景模型
bgd_model = np.zeros((1, 65), np.float64) # 初始化背景模型。 使用高斯混合模型 存储背景信息
fgd_model = np.zeros((1, 65), np.float64)# 初始化背前景模型。 使用高斯混合模型 存储背景信息

# GreabCut算法
cv2.grabCut(img,mask, rect, bgd_model,fgd_model,5, cv2.GC_INIT_WITH_RECT)

# 0 确定的背景 1 确定前景 2  可能背景 3 可能前景
# 生成最终的掩膜  uint8 8位无符号整数
# #  mask = [[0,1,2],[3,0,1]]   变成  [[0,1,0],[1,0,1]]
mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')

# 将掩膜应用到原图像上
# 维度不匹配  (400,600,1)
result = img * mask2[:, :, np.newaxis] # 将掩膜应用到原图像上,np.newaxis增加一个维度

cv2.imshow('img',img)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

        实现了一个基于GrabCut算法的图像分割功能,能够从背景中提取用户指定的前景目标。整个过程可以分为几个关键步骤:首先通过交互式方式让用户选择目标区域,然后利用机器学习算法自动完成精细分割,最后输出分割结果。

        在初始阶段,代码会加载图像并创建一个副本用于交互操作。通过调用selectROI函数,系统会弹出一个交互窗口,用户可以用鼠标拖拽的方式框选目标物体。这个矩形区域会被记录下来,作为后续分割的初始输入。为了进行图像分割,代码需要准备三个关键数据结构:一个全零的掩膜矩阵用于记录像素分类结果,以及两个65维的浮点数组作为前景和背景的高斯混合模型参数。

        核心处理阶段调用grabCut函数执行实际的分割算法。该函数会根据用户提供的矩形区域,对图像像素进行初步分类:矩形外标记为确定背景,矩形内标记为可能前景。通过5次迭代优化,算法会不断调整分类结果,逐步精确目标边界。在掩膜处理环节,代码将所有背景类像素(包括确定背景和可能背景)统一标记为0,前景类像素标记为1,生成一个二值掩膜。

        最终输出阶段,这个二值掩膜被应用到原始图像上。通过增加维度使掩膜与彩色图像对齐,然后进行逐像素乘法运算,保留前景区域的原始颜色,将背景区域置为黑色。程序最后会并列显示原始图像和分割结果,用户可以通过观察结果评估分割质量。如果效果不理想,可以调整初始选择区域或手动修改掩膜后重新运行算法。整个过程结合了用户交互和自动计算,能够有效地从复杂背景中提取目标对象。


网站公告

今日签到

点亮在社区的每一天
去签到