OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)

OpenCV与图像处理学习七——传统图像分割之阈值法(固定阈值、自适应阈值、大津阈值)OpenCV 与图像处理学习七 传统图像分割之阈值法 固定阈值 自适应阈值 大津阈值 一 固定阈值图像分割 1 1 直方图双峰法 1 2OpenCV 中的固定阈值分割二 自适应阈值图像分割三 迭代法阈值分割四 Otsu 大津阈值法前面的笔记介绍了一些 OpenCV 基本的图像处理 后面将学习使用 OpenCV 的传统的图像分割方法 这次笔记的内容是阈值法进行图像分割 图像分割是指将图像分成若干具有相似性质的区域的过程 主要有基于阈值 基于区域 基于边缘 基于聚类 基于图论和基于深度学习的图像分割方法等 图像分割分为语

前面的笔记介绍了一些OpenCV基本的图像处理,后面将学习使用OpenCV的传统的图像分割方法,这次笔记的内容是阈值法进行图像分割。

图像分割是指将图像分成若干具有相似性质的区域的过程,主要有基于阈值、基于区域、基于边缘、基于聚类、基于图论和基于深度学习的图像分割方法等。图像分割分为语义分割实例分割。下图是一个实例分割的例子,与语义分割不同的地方在于,它能将同类别的物体的每个个体都区分开,如下图中每个人的轮廓都被分割开:

在这里插入图片描述
分割的原则就是使划分后的子图在内部保持相似度最大,而子图之间的相似度最小,将G = (V, E) 分成两个子集A,B,使得:
在这里插入图片描述
在这里插入图片描述






一、固定阈值图像分割

即设定一个固定的阈值,整张图片的每个像素的像素值都与该值进行比较,若小于该阈值则将像素值改为一个固定的值(常用0),若大于该阈值则将像素值改为另一个固定的值(常用255),则可以将图像进行二值分割,得到一张二值图。

1.1 直方图双峰法

缺点:对图像的要求太高,很多图像的直方图并不满足双峰的分布。

1.2 OpenCV中的固定阈值分割

在OpenCV中的函数:

retval, dst = cv2.threshold( src, thresh, maxval, type[, dst] ) 

参数:

  1. src:输入图像,单通道或四通道图像。
  2. thresh:设定的固定阈值。
  3. maxval:当type参数设置为THRESH_BINARY时,表示像素值大于阈值时设置的值,或设置为THRESH_BINARY_INV时,表示像素值小于阈值时设置的值。
  4. type:阈值类型,如下图所示,前五种类型是基本类型,最后两种(大津阈值和三角形阈值)与前面的基本类型结合可以实现特殊的阈值分割:在这里插入图片描述

这里也需要注意一下这个函数的输出:

  1. retval:第一个参数返回的是阈值,在使用五种基本类型时就等于你设置的阈值,而在使用大津阈值法和三角形阈值法时将会得到它计算出来的阈值,一般用的不多。
  2. dst:第二个参数返回的才是分割之后的图像。

下面看几个例子:

# 加载opencv和matplotlib import cv2 import matplotlib.pyplot as plt # 灰度图读入 img = cv2.imread('./image/thresh.png', 0) threshold = 127 # 阈值分割 ret, th = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY) print(ret) cv2.imshow('Original', img) cv2.imshow('thresh', th) cv2.waitKey(0) cv2.destroyAllWindows() 
127.0 

即像素值低于127的都被赋予0,高于的都被赋予255,得到一张二值化的图像。

再来看一下五种基本的阈值分割方法的区别

# 导入第三方包 import cv2 from matplotlib import pyplot as plt # opencv读取图像 img = cv2.imread('./image/person.png',0) # 5种阈值法图像分割 ret1, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) ret2, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) ret3, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC) ret4, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO) ret5, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV) images = [img, thresh1, thresh2, thresh3, thresh4, thresh5] # 使用for循环进行遍历,matplotlib进行显示 for i in range(6): plt.subplot(2, 3, i+1) plt.imshow(images[i], cmap='gray') plt.xticks([]) plt.yticks([]) plt.suptitle('fixed threshold') plt.show() 

二、自动阈值图像分割

2.1 自适应阈值法

函数:

dst = cv2.adaptiveThreshold( src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst] ) 

参数:

  1. src:输入图像,只能是单通道灰度图。
  2. maxValue:最大阈值,即小块计算的阈值不能超过这个值,一般设置为255.
  3. adaptiveMethod:计算小块阈值的方法,包括cv2.ADAPTIVE_THRESH_MEAN_Ccv.ADAPTIVE_THRESH_GAUSSIAN_C,即求小块内的均值或高斯加权求和:在这里插入图片描述
  4. thresholdType:阈值方法,这里只能是THRESH_BINARYTHRESH_BINARY_INV,即在这里插入图片描述
  5. blockSize:小块的尺寸,如11就是11×11。
  6. C:最终阈值等于小区域计算出的阈值再减去这个常数。

看一下刚刚用固定阈值分割效果很差的那个图用自适应阈值来分割的效果:

# 自适应阈值与固定阈值对比 import cv2 import matplotlib.pyplot as plt img = cv2.imread('./image/paper2.png', 0) # 固定阈值 ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) # 自适应阈值 th2 = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 4) th3 = cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 4) # 全局阈值,均值自适应,高斯加权自适应对比 titles = ['Original', 'Global(v = 127)', 'Adaptive Mean', 'Adaptive Gaussian'] images = [img, th1, th2, th3] for i in range(4): plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray') plt.title(titles[i], fontsize=8) plt.xticks([]), plt.yticks([]) plt.show() 

在这里插入图片描述
可以看出效果还是很好的,相比之下,高斯方法的自适应阈值能获得更好的效果,其噪点更少。

2.2 迭代法阈值分割

步骤:

  1. 求出图像的最大灰度值和最小灰度值,分别记为Zmax和Zmin,另初始阈值为T0 = (Zmax + Zmin) / 2。
  2. 根据阈值Tk将图像分割为前景和背景,分别求出两者的平均灰度值Zo和Zb。
  3. 求出新的阈值Tk+1 = (Zo + Zb) / 2。
  4. 若Tk == Tk+1,则即为所求的阈值,否则转到步骤2继续迭代。
  5. 使用计算后的阈值进行阈值分割。

其实迭代法就是将固定阈值分割里手动给定阈值改为了迭代计算阈值,可以适用的范围更多一些,但是本质还是固定阈值变换

看个例子:

import cv2 import numpy as np import matplotlib.pyplot as plt import matplotlib.cm as cm def best_thresh(img): # step 1: 设置初始阈值 img_array = np.array(img).astype(np.float32) # 转化成数组 I = img_array zmax = np.max(I) zmin = np.min(I) tk = (zmax+zmin)/2 # step 2: 根据阈值将图像进行分割为前景和背景,分别求出两者的平均灰度zo和zb b = 1 m, n = I.shape; while b == 0: ifg = 0 ibg = 0 fnum = 0 bnum = 0 for i in range(1, m): for j in range(1, n): tmp = I(i, j) if tmp >= tk: ifg = ifg + 1 fnum = fnum + int(tmp) # 前景像素的个数以及像素值的总和 else: ibg = ibg+1 bnum = bnum + int(tmp) # 背景像素的个数以及像素值的总和 # step 3: 计算前景和背景的新平均值 zo = int(fnum / ifg) zb = int(bnum / ibg) # step 4: 比较tk是否等于新平均值 if tk == int((zo+zb) / 2): b = 0 else: tk = int((zo+zb)/2) # step 5: 返回的就是迭代计算后的阈值 return tk img = cv2.imread("./image/bird.png") img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) img = cv2.resize(gray, (200, 200)) # 大小 yvzhi = best_thresh(img) ret1, th1 = cv2.threshold(img, yvzhi, 255, cv2.THRESH_BINARY) print(ret1) plt.imshow(th1, cmap=cm.gray) plt.show() 

2.3 Otsu大津阈值法

大津法:也叫最大类间方差法,1979年日本学者大津提出,是一种基于全局阈值的自适应方法。

图像分为前景和背景,当取最佳阈值时,两部分之间的差别应该是最大的,衡量差别的方法为最大类间方差。

ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) 

看一个例子:

import cv2 from matplotlib import pyplot as plt img = cv2.imread('./image/noisy.png', 0) # 固定阈值法 ret1, th1 = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY) # Otsu阈值法 ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 先进行高斯滤波,再使用Otsu阈值法 blur = cv2.GaussianBlur(img, (5, 5), 0) ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) images = [img, 0, th1, img, 0, th2, blur, 0, th3] titles = ['Original', 'Histogram', 'Global(v=100)', 'Original', 'Histogram', "Otsu's", 'Gaussian filtered Image', 'Histogram', "Otsu's"] for i in range(3): # 绘制原图 plt.subplot(3, 3, i * 3 + 1) plt.imshow(images[i * 3], 'gray') plt.title(titles[i * 3], fontsize=8) plt.xticks([]), plt.yticks([]) # 绘制直方图plt.hist, ravel函数将数组降成一维 plt.subplot(3, 3, i * 3 + 2) plt.hist(images[i * 3].ravel(), 256) plt.title(titles[i * 3 + 1], fontsize=8) plt.xticks([]), plt.yticks([]) # 绘制阈值图 plt.subplot(3, 3, i * 3 + 3) plt.imshow(images[i * 3 + 2], 'gray') plt.title(titles[i * 3 + 2], fontsize=8) plt.xticks([]), plt.yticks([]) plt.show() 

以上就是基于阈值的传统图像分割中常用的几种阈值分割方法。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/178726.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午4:37
下一篇 2026年3月26日 下午4:37


相关推荐

  • 数据结构之双链表

    数据结构之双链表文章目录前言为何要双链表 双链表的结构图示项目的建造双链表结点的定义双链表的各种方法实现双链表之新建结点双链表之初始化双链表之判空双链表之求具体元素数量双链表之打印链表内容双链表之尾插双链表之尾删双链表之头插双链表之头删双链表之查找值双链表之任意位置插入值双链表之任意位置删除双链表之销毁空间前言上一节 博主讲解了单链表 并且具体的实现了单链表的增删改查 而这次博主要讲解的是双向循环链表 简称双链表 为何要双链表 既然有了单链表 为何还搞一个双链表呢 答案就是解决了单链表的一些缺点

    2026年1月29日
    3
  • java——Scanner中nextLine()方法和next()方法的区别

    java——Scanner中nextLine()方法和next()方法的区别        遇到一个有意思的东西,在整理字符串这块知识的时候,发现我在用Scanner函数时,在字符串中加入空格,结果空格后面的东西没有输出来(/尴尬),不多说直接上代码:importjava.util.Scanner;//Scanner中nextLine()方法和next()方法的区别publicclassScannerString{publicstatic…

    2022年4月27日
    49
  • html页面缩小导航栏隐藏,html – 导航栏缩放问题[通俗易懂]

    html页面缩小导航栏隐藏,html – 导航栏缩放问题[通俗易懂]我有一个问题,我的导航栏似乎与CSS中的.container缩放.现在,我是一个新手,但我已经尝试搞乱CSS中的值,但无济于事.这是HTML和CSS的代码:*{margin:0px;padding:0px;}body{font-family:verdana;background-image:url(images/bg2.jpg);max-width:100%;max-height:…

    2022年5月28日
    69
  • Jmeter下载安装配置—测试小白

    Jmeter下载安装配置—测试小白一,进入官网:http://jmeter.apache.org/1.第一步进入官网如下图2.选择进行下载,下载下来为一个压缩包,解压即可。3.我下载的是jmeter4.0版本,对应jdk1.8。然后就进行解压。个人认为要注意3点:1.解压之后压缩包叫apache-jmeter-4.0.zip,如是src.zip后缀的都不对,打开之后会报错不可用,因为里面缺少我们下一步将要配置的环境变量.jar文件…

    2022年5月29日
    35
  • Pycharm设置模板

    Pycharm设置模板新建脚本文件模板 路径 settings editor fileandcodet pythonscript 编辑内容 coding utf 8 author wch file NAME py time DATE TIME 效果

    2026年3月17日
    2
  • 在网页和移动端使用 Cursor

    在网页和移动端使用 Cursor

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号