Python----OpenCV(图像分割——什么是图像分割,基于阈值化的分割,基于自适应阈值图像分割 ,基于边缘的图像分割)

一、什么是图像分割

        图像分割(Segmentation)是图像处理和机器视觉一个重要分支,其目标是精确理解图像场景与内容。图像分割是在像素级别上的分类,属于同一类的像素都要被归为一类,因此图像分割是从像素级别来理解图像的。如下图所示的照片,属于人的像素部分划分成一类,属于摩托车的像素划分成一类,背景像素划分为一类。

图像分割的类型

  • 语义分割(Semantic Segmentation):对图像中的每个像素划分到不同的类别;
  • 实例分割(Instance Segmentation):对图像中每个像素划分到不同的个体(可以理解为目标检测和语义分割的结合);
  • 全景分割(Panoptic Segmentation):语义分割和实例分割的结合,即要对所有目标都检测出来,又要区分出同个类别中的不同实例。

图像分割的应用 

        1) 🩺医学、生物图像分割(如病灶识别)

                将医学影像(如CT、MRI、超声图像)中的器官、组织、病灶区域进行像素级分割,提取出病变位置、形状和大小。

        2) 🏭工业质检

                对工业产品(如金属板、电子元件、纺织品等)表面图像进行分割,检测裂纹、划痕、瑕疵、缺陷区域。

        3) 🛰️ 遥感图像分割

                将遥感卫星或无人机拍摄的高空图像进行像素级分割,提取地物信息,如建筑物、道路、水体、植被等。为城市规划、环境监测、灾害评估、国土调查等提供精确数据支撑。

        4)🚦交通图像分析

                对交通场景中的车道线、车辆、行人、交通标志等元素进行像素级分割,作为自动驾驶、智能交通系统的重要基础。确保车辆按照正确的车道行驶、避障、识别交通标志。

        5) 📷 自动抠图

                将图像中的前景(如人物、物体)与背景分割开,便于替换背景、制作合成图像或生成特效。广泛应用于手机拍照、美颜、虚拟直播、视频会议等。

图像分割的难点

  1. 数据标注成本高:分割不像检测等任务,只需要标注边框就可以使用,分割需要精确到像素级标注,包括每一个目标的轮廓等信息;
  2. 计算资源消耗大:要想得到较高的精度就需要使用更深的网络、进行更精确的计算,对计算资源要求较高。目前业界有一些轻量级网络,但总体精度较低;
  3. 小目标、细节分割难:目前很多算法对于道路、建筑物等类别分割精度很高,能达到98%,而对于细小的类别,由于其轮廓太小,而无法精确的定位轮廓;
  4. 上下文信息依赖强:分割中上下文信息很重要,否则会造成一个目标被分成多个部分,或者不同类别目标分类成相同类别;

二、基于阈值化的分割方法 

        将图像像素灰度值与设定的阈值进行比较,根据比较结果将像素分为前景和背景,达到图像分割目的。当一个图像有双峰现象时,其直方图会出现两个峰,分别对应图像中两种不同的颜色或亮度区域。这时我们可以使用直方图双峰法来自动确定合适的阈值。

threshold(src, thresh, maxval, type[, dst])
参数名数据类型说明典型值/选项
srcMat输入图像,必须是单通道灰度图(8位或32位浮点型)-
threshdouble设定的阈值T,用于像素分类0~255之间的值(如127)
maxvaldouble当type使用非二进制类型时(如THRESH_TRUNC/THRESH_TOZERO),指定的最大值通常为255(8位图像的最大值)
typeint阈值化类型,决定如何处理像素值与阈值的关系以下选项之一:
• cv2.THRESH_BINARY
• cv2.THRESH_BINARY_INV
• cv2.THRESH_TRUNC
• cv2.THRESH_TOZERO
• cv2.THRESH_TOZERO_INV
dstMat输出图像,与输入图像大小和类型相同-
类型含义说明
THRESH_BINARY二值化像素值 ≥ 阈值 → 255,否则 0
THRESH_BINARY_INV反二值化像素值 < 阈值 → 255,否则 0
THRESH_TRUNC截断像素值 ≥ 阈值 → 阈值,否则保持不变
THRESH_TOZERO零阈值化像素值 ≥ 阈值 → 保持原值,否则 0
THRESH_TOZERO_INV反零阈值化像素值 < 阈值 → 保持原值,否则 0
# 导入OpenCV库
import cv2

# 读取图像文件(默认以彩色BGR格式加载)
img = cv2.imread('./images/nezha.png')


# 将彩色图像转换为灰度图像(单通道)
# COLOR_BGR2GRAY 表示从BGR颜色空间转换到灰度空间
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 对灰度图像进行阈值处理
# 参数说明:
#   img_gray: 输入的灰度图像
#   150: 设定的阈值(T)
#   255: 当像素值超过阈值时赋予的新值(maxval)
#   cv2.THRESH_BINARY: 阈值化类型(大于阈值设为maxval,否则设为0)
# 返回值:
#   ret: 实际使用的阈值(当使用自适应阈值时有用)
#   img_threshold: 阈值处理后的二值图像
ret, img_threshold = cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)

# 显示原始彩色图像
cv2.imshow('原始彩色图像', img)

# 显示灰度图像
cv2.imshow('灰度图像', img_gray)

# 显示阈值处理后的二值图像
cv2.imshow('二值化图像', img_threshold)

# 等待键盘输入(0表示无限等待)
cv2.waitKey(0)

# 关闭所有OpenCV创建的窗口
cv2.destroyAllWindows()

三、基于自适应阈值图像分割 

        自适应阈值化(Adaptive Thresholding)是根据图像中每个像素邻域的局部特性,动态计算阈值进行分割的方法。相比固定阈值化(全局阈值),自适应阈值化对光照不均匀、局部对比度变化较大的图像表现更好。

adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)
参数名数据类型说明可选值/范围
srcMat输入图像(必须为 8位单通道灰度图-
maxValuedouble满足条件的像素被赋予的新值(一般设为 255,表示白色)0~255(8位图像)
adaptiveMethodint自适应阈值算法:
• 基于局部均值
• 基于高斯加权和
cv2.ADAPTIVE_THRESH_MEAN_C
cv2.ADAPTIVE_THRESH_GAUSSIAN_C
thresholdTypeint阈值化类型:
• 二进制:大于阈值设为 maxValue,否则为0
• 反二进制:与二进制相反
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
blockSizeint计算阈值的局部邻域大小(必须为 奇数,如3,5,7...)≥3 的奇数(如 3, 5, 7)
Cdouble从均值或加权均值中减去的常数(用于微调阈值,正值提高阈值,负值降低阈值一般取小整数(如 -2, 0, 2, 5)
方法说明
ADAPTIVE_THRESH_MEAN_C邻域内像素均值减去常数 C 作为阈值
ADAPTIVE_THRESH_GAUSSIAN_C邻域内像素加权均值(高斯权重)减去常数 C 作为阈值
# 导入OpenCV库
import cv2

# 读取图像文件(默认以BGR三通道格式加载)
# './images/nezha.png'是图像路径,请确保文件存在
img = cv2.imread('./images/nezha.png')

# 检查图像是否成功加载
if img is None:
    print("错误:图像加载失败,请检查路径!")
    exit()

# 将BGR彩色图像转换为灰度图像(单通道)
# COLOR_BGR2GRAY表示从BGR颜色空间转换到灰度空间
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 自适应阈值处理(基于局部均值的方法)
# 参数说明:
#   img_gray: 输入的灰度图像
#   255: 满足条件时像素设置的最大值(白色)
#   cv2.ADAPTIVE_THRESH_MEAN_C: 使用邻域均值作为阈值计算方法
#   cv2.THRESH_BINARY: 二值化类型(大于阈值设为255,否则0)
#   11: 邻域大小(必须为奇数,这里使用11x11的窗口)
#   2: 从均值中减去的常数(用于微调阈值)
img_adaptiveThreshold_mean = cv2.adaptiveThreshold(
    img_gray, 255, 
    cv2.ADAPTIVE_THRESH_MEAN_C, 
    cv2.THRESH_BINARY, 11, 2
)

# 自适应阈值处理(基于高斯加权的方法)
# 与均值方法的主要区别:
#   使用高斯加权计算邻域阈值,对噪声更鲁棒
img_adaptiveThreshold_gaussian = cv2.adaptiveThreshold(
    img_gray, 255, 
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
    cv2.THRESH_BINARY, 11, 2
)

# 显示原始彩色图像
cv2.imshow('原始彩色图像', img)

# 显示灰度图像
cv2.imshow('灰度图像', img_gray)

# 显示基于局部均值的自适应阈值结果
cv2.imshow('均值法自适应阈值', img_adaptiveThreshold_mean)

# 显示基于高斯加权的自适应阈值结果
cv2.imshow('高斯法自适应阈值', img_adaptiveThreshold_gaussian)

# 等待键盘输入(0表示无限等待,按任意键继续)
cv2.waitKey(0)

# 关闭所有OpenCV创建的窗口
cv2.destroyAllWindows()

四、基于边缘的图像分割

        基于边缘的图像分割方法,是通过检测图像中像素灰度值变化显著的边缘区域,将图像分割成若干区域的方法。边缘通常对应着物体的轮廓或区域之间的分界线,边缘检测是这类方法的核心步骤。 

常用的边缘检测方法有

算子特点说明
Roberts 罗伯茨算子计算邻域对角线方向差分,适合检测细节
Sobel 索贝尔算子引入平滑,增强抗噪性,检测水平和垂直边缘
Scharr 沙尔算子类似Sobel,计算速度更快,适用于简单场景
Laplacian 拉普拉斯算子基于二阶导数,响应所有方向的边缘
Canny 卡尼边缘检测综合多步骤优化,效果最好,应用最广

推荐

        Canny 算法是一种多阶段、鲁棒性强、抗噪声能力优秀的边缘检测方法,广泛应用于实际项目。

cv2.Canny(image, threshold1, threshold2)
参数说明
image输入图像,需为单通道灰度图
threshold1低阈值
threshold2高阈值

Canny 算法会根据这两个阈值:

  • 判断强边缘(大于高阈值)

  • 判断弱边缘(介于低阈值和高阈值之间,且与强边缘相连)

  • 过滤非边缘区域(低于低阈值)

举个例子

假设有一张图像:

  • threshold1 = 50
  • threshold2 = 150
  • 如果某个像素值大于 150,它被认为是强边缘。
  • 如果某个像素值介于 50 和 150 之间,它被认为是弱边缘,但只有在它与强边缘连接时才被保留。
  • 如果某个像素值小于 50,它被认为不是边缘,直接忽略。
# 导入OpenCV库
import cv2

# 读取图像文件(默认以BGR三通道格式加载)
# './images/nezha.png'是图像路径,请确保文件存在
img = cv2.imread('./images/nezha.png')

# 检查图像是否成功加载
if img is None:
    print("错误:图像加载失败,请检查路径!")
    exit()

# 将BGR彩色图像转换为灰度图像(单通道)
# COLOR_BGR2GRAY表示从BGR颜色空间转换到灰度空间
# Canny边缘检测需要输入单通道灰度图像
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Canny边缘检测
# 参数说明:
#   img_gray: 输入的灰度图像
#   150: 第一个阈值(低阈值),用于边缘连接
#   255: 第二个阈值(高阈值),用于强边缘检测
#   说明:介于150-255之间的像素会被认为是边缘(如果连接到强边缘)
#   建议高低阈值比例在1:2或1:3之间(如100:200或150:300)
edges = cv2.Canny(img_gray, 150, 255)

# 显示边缘检测结果
# 白色像素表示检测到的边缘,黑色表示背景
cv2.imshow('Canny边缘检测结果', edges)

# 等待键盘输入(0表示无限等待,按任意键继续)
cv2.waitKey(0)

# 关闭所有OpenCV创建的窗口
cv2.destroyAllWindows()

contours, hierarchy = cv2.findContours(image, mode, method)
参数名数据类型说明可选值/模式
imageMat输入的二值图像(通常为Canny边缘检测结果或阈值化结果)必须是8位单通道图像,非零像素视为前景
modeint轮廓检索模式,决定如何组织返回的轮廓cv2.RETR_EXTERNAL
cv2.RETR_LIST
cv2.RETR_CCOMP
cv2.RETR_TREE
methodint轮廓近似方法,决定如何存储轮廓的点集cv2.CHAIN_APPROX_NONE
cv2.CHAIN_APPROX_SIMPLE
cv2.CHAIN_APPROX_TC89_L1

返回值说明

返回值数据类型说明示例/结构
contourslist[ndarray]检测到的轮廓列表,每个轮廓是N×1×2的ndarray(N为轮廓点数量)单个轮廓形状:(N,1,2),其中N是点的坐标(如[[[x1,y1]], [[x2,y2]], ...])
hierarchyndarray (可选)轮廓的层级关系,形状为(1,M,4)(M为轮廓数量),每个元素包含:[Next, Previous, First_Child, Parent]若不存在层级关系,则为None(如使用RETR_LIST时)

详细说明与示例

1. mode 轮廓检索模式

模式说明适用场景
cv2.RETR_EXTERNAL只检测最外层轮廓(如物体外边框)简单物体计数或外轮廓提取
cv2.RETR_LIST检测所有轮廓,不建立层级关系快速检测所有轮廓
cv2.RETR_TREE检测所有轮廓,并建立完整的层级树(包含父子关系)需要分析轮廓嵌套关系(如文字中的孔洞)
cv2.RETR_CCOMP检测所有轮廓,组织为两级层级(外层轮廓和孔洞)简化版层级关系

2. method 轮廓近似方法

方法说明效果对比
cv2.CHAIN_APPROX_NONE存储轮廓所有点精确但内存占用高
cv2.CHAIN_APPROX_SIMPLE压缩水平、垂直、对角线方向的冗余点,只保留端点减少内存占用(如矩形只存4个顶点)
cv2.CHAIN_APPROX_TC89_L1使用Teh-Chin链式近似算法(更高效的压缩)平衡精度和效率

Canny + findContours 图像分割流程

  1. 转换为灰度图

  2. 高斯滤波降噪

  3. Canny 边缘检测

  4. 使用 cv2.findContours 提取轮廓

  5. 绘制或处理轮廓实现分割

# 导入OpenCV库
import cv2

# 读取图像文件(默认以BGR三通道格式加载)
# './images/nezha.png'是图像路径,请确保文件存在
img = cv2.imread('./images/nezha.png')

# 检查图像是否成功加载
if img is None:
    print("错误:图像加载失败,请检查路径!")
    exit()

# 将BGR彩色图像转换为灰度图像(单通道)
# COLOR_BGR2GRAY表示从BGR颜色空间转换到灰度空间
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 对灰度图像进行高斯模糊处理
# 参数说明:
#   img_gray: 输入的灰度图像
#   (3,3): 高斯核大小(宽度和高度,必须是正奇数)
#   0: 高斯核在X和Y方向的标准差(0表示自动计算)
img_gauss = cv2.GaussianBlur(img_gray, (3, 3), 0)

# Canny边缘检测
# 参数说明:
#   img_gauss: 经过高斯模糊的图像
#   50: 低阈值(低于此值的边缘被丢弃)
#   150: 高阈值(高于此值的边缘被保留)
#   说明:介于50-150之间的边缘会根据连通性决定是否保留
img_canny = cv2.Canny(img_gauss, 50, 150)

# 查找图像中的轮廓
# 返回值:
#   contours: 检测到的轮廓列表,每个轮廓是点的集合
#   hierarchy: 轮廓的层级信息(本示例未使用)
# 参数说明:
#   img_canny: 输入的二值图像(Canny边缘检测结果)
#   cv2.RETR_EXTERNAL: 只检测最外层轮廓
#   cv2.CHAIN_APPROX_SIMPLE: 压缩水平、垂直和对角方向的轮廓,只保留端点
contours, hierarchy = cv2.findContours(img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 创建结果图像的副本(避免修改原始图像)
result = img.copy()

# 在结果图像上绘制检测到的轮廓
# 参数说明:
#   result: 要绘制轮廓的目标图像
#   contours: 轮廓列表
#   -1: 表示绘制所有轮廓(如果指定索引,则只绘制对应轮廓)
#   (0, 255, 0): 轮廓颜色(BGR格式,这里是绿色)
#   2: 轮廓线宽(像素)
cv2.drawContours(result, contours, -1, (0, 255, 0), 2)

# 显示原始图像
cv2.imshow('原始图像', img)

# 显示灰度图像
cv2.imshow('灰度图像', img_gray)

# 显示高斯模糊后的图像
cv2.imshow('高斯模糊', img_gauss)

# 显示Canny边缘检测结果
cv2.imshow('Canny边缘', img_canny)

# 显示带轮廓标记的结果图像
cv2.imshow('轮廓检测结果', result)

# 等待键盘输入(0表示无限等待,按任意键继续)
cv2.waitKey(0)

# 关闭所有OpenCV创建的窗口
cv2.destroyAllWindows()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值