本文源自微信公众号【Python编程和深度学习】原文链接:[数字图像处理之图像分割算法](https://mp.weixin.qq.com/s?__biz=MzUxNTY1MjMxNQ==&mid=2247484139&idx=1&sn=f038550e611c2c67a9bf8714539e7ca7&chksm=f9b22d5fcec5a44977e55151ceb8f0d4bf85b33482a8d1c78e9ccba7cf5a703c8cd31d0aff93&token=1387149370&lang=zh_CN#rd "数字图像处理之图像分割算法"),欢迎扫码关注鸭! ![扫它!扫它!扫它](http://cookdata.cn/media/bbs/images/公众号_1602413881117_5d14.jpg =200x200) #**图像分割算法** 图像分割就是把图像分成若干个特定的、具有独特性质的区域并提出感兴趣目标的技术和过程。现有的图像分割方法主要分以下几类:基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法以及基于特定理论的分割方法等。单色图像的分割算法通常基于灰度值的不连续性和相似性。 # 目录 [一、点、线和边缘监测](#1)<br> [二、使用霍夫变换的线检测](#2)<br> [三、阈值分割](#3)<br> [四、基于区域的分割](#4)<br> [五、使用分水岭变换的分割](#5)<br> <div id="1"></div> ##一、点、线和边缘监测 ###1.1、点检测 将嵌在一幅图像的恒定区域或亮度几乎不变的区域里的孤立点的检测,就是点检测。当检测模板中心是孤立点时,模板的响应最强,而在非模板中心即恒定灰度区域时,响应为零。 点检测的模板: ![](http://cookdata.cn/media/bbs/images/1_1602394883325_5d14.jpg =100x100) ###1.2、线检测 线检测比点检测更加复杂一些,模板如图所示 0度: ![](http://cookdata.cn/media/bbs/images/2_1602395377455_5d14.jpg =100x100) 45度: ![](http://cookdata.cn/media/bbs/images/3_1602395421750_5d14.jpg =100x100) 90度: ![](http://cookdata.cn/media/bbs/images/4_1602395572576_5d14.jpg =100x100) -45度: ![](http://cookdata.cn/media/bbs/images/5_1602395595065_5d14.jpg =100x100) 当检测的线是在中间那行或者是由“2”组成的行或列中,则此时的相应最大,其他部分的相应为零。 ###1.3、边缘检测 检测亮度的不连续性。这样的不连续是用一阶和二阶导数来检测的。在图像中的像素值是离散的值,故在实际边缘检测算法中采用差分来近似导数。 一阶导数: $$ \frac{\partial f}{\partial x}=f^{\prime}(x)=f(x+1)-f(x) $$ 二阶导数: $$ \frac{\partial^{2} f}{\partial x^{2}}=\frac{\partial f^{\prime}(x)}{\partial x}=f^{\prime}(x+1)-f^{\prime}(x) $$ 二阶函数的梯度向量公式: $$ \nabla f=\left[\begin{array}{l} g_{x} \\ g_{y} \end{array}\right]=\left[\begin{array}{l} \frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y} \end{array}\right] $$ 通常使用梯度的幅值或近似值来作为“梯度”,因为平方和平方根需要大量的计算开销,所以使用绝对值来近似梯度幅值 $$ \nabla f=\operatorname{mag}(\nabla f)=\left[g_{x}^{2}+g_{y}^{2}\right]^{1 / 2} \approx g_{x}^{2}+g_{y}^{2} \approx\left|g_{x}\right|+\left|g_{y}\right| $$ 梯度向量的基本性质是:梯度向量指向(x,y)坐标处f的最大变化率方向。 最大变化率处发生的角度是 $$ \alpha(x, y)=\tan ^{-1}\left(\frac{g_{y}}{g_{x}}\right) $$ 图像的边缘有方向和幅度两个属性,沿边缘方向像素变化平缓,垂直于边缘方向像素变化剧烈。边缘上的这种变化可以用微分算子检测出来,通常用一阶或两阶导数来检测边缘。一阶导数认为最大值对应边缘位置,而二阶导数则以过零点对应边缘位置。 图像的梯度可以用一阶导数和二阶偏导数来求解。但图像是离散的,对一幅图像的求导相当于对一个面求导。对图像的操作,我们采用模板对原图像进行卷积运算,从而达到我们想要的效果。而获取一幅图像的梯度就转化为:模板(Roberts、Prewitt、Sobel、Lapacian算子)对原图像进行卷积,不过这里的模板并不是随便设计的,而是根据数学中求导理论推导出来的。 ####1.3.1、一阶微分算子: #####Roberts算子 一般我们常用对角线方向的像元计算梯度 $$ \begin{array}{l} g_{x}=\frac{\partial f}{\partial x}=f(x+1, y+1)-f(x, y) \\ g_{y}=\frac{\partial f}{\partial y}=f(x+1, y)-f(x, y+1) \end{array} $$ 对应Roberts算子 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午2.52.08_1602399174779_5d14.jpg =250x100) #####Prewitt算子 如果卷积模板为 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午2.56.31_1602399360860_5d14.jpg =100x100) 我们定义水平、垂直和两对角线方向的梯度: 水平方向: $\quad g_{x}=\frac{\partial f}{\partial x}=\left(z_{7}+z_{8}+z_{9}\right)-\left(z_{1}+z_{2}+z_{3}\right)$ 垂直方向: $g_{y}=\frac{\partial f}{\partial y}=\left(z_{3}+z_{6}+z_{9}\right)-\left(z_{1}+z_{4}+z_{7}\right)$ 对角线方向:$g_{x}^{\prime}=\left(z_{2}+z_{3}+z_{6}\right)-\left(z_{4}+z_{7}+z_{8}\right)$ 该定义下的算子称之为Prewitt算子: ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午3.16.22_1602400562233_5d14.jpg =400x100) #####Sobel算子 是在Prewitt算子的基础上改进的,在中心系数上使用一个权值2,相比较Prewitt算子,Sobel模板能够较好的抑制(平滑)噪声。 $$ \begin{array}{l} g_{x}=\frac{\partial f}{\partial x}=\left(z_{7}+2 z_{8}+z_{9}\right)-\left(z_{1}+2 z_{2}+z_{3}\right) \\ g_{y}=\frac{\partial f}{\partial y}=\left(z_{3}+2 z_{6}+z_{9}\right)-\left(z_{1}+2 z_{4}+z_{7}\right) \end{array} $$ ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午3.20.49_1602400821945_5d14.jpg =400x100) ####1.3.2、二阶微分算子: #####Laplacian算子 拉普拉斯算子是最简单的各向同性微分算子,具有旋转不变性。一个二维图像函数的拉普拉斯变换是各向同性的二阶导数,定义为 $$ \nabla^{2} f(x, y)=\frac{\partial^{2} f}{\partial x^{2}}+\frac{\partial^{2} f}{\partial y^{2}} $$ 离散形式: $$ \nabla^{2} f(x, y)=[f(x+1, y)+f(x-1, y)+f(x, y+1)+f(x, y-1)+4f(x, y) $$ 模板形式: ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午3.55.40_1602402905157_5d14.jpg =300x300) 此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。 #####Log(Laplacian of gaussain)算子 拉普拉斯边缘检测算子没有对图像做平滑处理,所以对噪声很敏感。因此可以想到先对图像进行高斯平滑处理,然后再与Laplacian算子进行卷积。这就是高斯拉普拉斯算子(Log)。 高斯函数: $$ G(x, y)=e^{-\frac{x^{2}+y^{2}}{2 \sigma^{2}}} $$ 这个公式表示的是平滑函数。如果此函数和图像进行卷积,则图像会变得模糊,且模糊的程度是由σ决定的。 Log算子的表达式如下: $$ L O G=\nabla G_{\sigma}(x, y)=\frac{\partial^{2} G_{\sigma}(x, y)}{\partial x^{2}}+\frac{\partial^{2} G_{\sigma}(x, y)}{\partial y^{2}}=\frac{x^{2}+y^{2}-2\sigma^{2}}{ \sigma^{4}} $$ 由Log算子的表达式构建卷积模板,然后对图像进行卷积,如下式: $$ \left.g(x, y)=\left[\nabla^{2} G(x, y\right)\right]<卷积>f(x, y) $$ 直接构造卷积模板的计算量较大,效率较低,因为卷积是线性操作,那么还可以写成如下 $$ \left.g(x, y)=\nabla^{2} \left[G(x, y\right)<卷积>f(x, y)\right] $$ 先用高斯平滑图像,最后再求其结果的拉普拉斯。 #####高斯差分(DOG)算子 Marr and Hildreth[1980]指出,使用高斯差分(DOG)来近似Log算子是可能的: $$ D o G=G_{\sigma_{1}}-G_{\sigma_{2}}=\frac{1}{2 \pi}\left[\frac{1}{\sigma_{1}^{2}} e^{-\left(x^{2}+y^{2}\right) / 2 \sigma_{1}^{2}}\right.-\frac{1}{\sigma_{2}^{2}} e^{-\left(x^{2}+y^{2}\right) / 2 \sigma_{2}^{2}} ] $$ 式中:。Marr and Hildreth建议,使用1.6:1的标准差比率能够对LOG函数提供一个更接近的“工程”近似。 LoG算子和DoG算子的函数波形对比如下图所示,由于高斯差分的计算更加简单,因此可用DoG算子近似替代LoG算子: ![](http://cookdata.cn/media/bbs/images/1_1602406129599_5d14.jpg) #####Canny边缘检测器 是一种被广泛使用的算法,并被认为是边缘检测最优的算法 Canny边缘检测器算法基本步骤: 1、平滑图像:通过使用合适的模糊半径执行高斯模糊来减少图像内的噪声。 2、计算图像的梯度:这里计算图像的梯度,并将梯度分类为垂直、水平和斜对角。这一步的输出用于在下一步中计算真正的边缘。 3、非最大值抑制:利用上一步计算出来的梯度方向,检测某一像素在梯度的正方向和负方向上是否是局部最大值,如果是,则抑制该像素(像素不属于边缘)。这是一种边缘细化技术,用最急剧的变换选出边缘点。 用滞后阈值化选择边缘:最后一步,检查某一条边缘是否明显到足以作为最终输出,最后去除所有不明显的边缘。 代码如下: ```python import cv2 import numpy as np img = cv2.imread("img.jpg", 0) cv2.imwrite("canny.jpg", cv2.Canny(img, 200, 300)) cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]]) ``` 必要参数: 第一个参数是需要处理的原图像,该图像必须为单通道的灰度图; 第二个参数是滞后阈值1; 第三个参数是滞后阈值2。 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午5.01.25_1602406952796_5d14.jpg =400x400) <div id="2"></div> ##二、使用霍夫变换的线检测 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一,应用很广泛,也有很多改进算法。主要用来从图像中分离出具有某种相同特征的几何形状(如,直线,圆等)。 霍夫变换是一种特征检测,用来辨别找出物件中的特征,例如:线条。算法流程大致如下,给定一个物件、要辨别的形状的种类,算法会在参数空间(parameter space)中执行投票来决定物体的形状,而这是由累加空间(accumulator space)里的局部最大值(local maximum)来决定。 用极坐标系来表示直线, $$ y=\left(-\frac{\cos \theta}{\sin \theta}\right) x+\left(\frac{r}{\sin \theta}\right) $$ 化简为: $$ r=x \cos \theta+y \sin \theta $$ 一般来说,一条直线能够通过在平面θ-ρ 寻找交于一点的曲线数量来进行检测。越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成.。一般来说我们可以通过设置直线上点的阈值来定义多少条曲线交于一点,此时认为检测到了一条直线。霍夫线变换要做的就是:它追踪图像中每个点对应曲线间的交点。如果交于一点的曲线的数量超过了 阈值, 那么可以认为这个交点所代表的参数对 (θ, ρ) 在原图像中为一条直线。 代码: ```python import cv2 import numpy as np # 使用霍夫直线变换做直线检测,前提条件:边缘检测已经完成 # 标准霍夫线变换 def line_detection_demo(image): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150, apertureSize=3) lines = cv2.HoughLines(edges, 1, np.pi/180, 200) # 函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线 for line in lines: rho, theta = line[0] # line[0]存储的是点到直线的极径和极角,其中极角是弧度表示的 a = np.cos(theta) # theta是弧度 b = np.sin(theta) x0 = a * rho y0 = b * rho x1 = int(x0 + 1000 * (-b)) # 直线起点横坐标 y1 = int(y0 + 1000 * (a)) # 直线起点纵坐标 x2 = int(x0 - 1000 * (-b)) # 直线终点横坐标 y2 = int(y0 - 1000 * (a)) # 直线终点纵坐标 cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow("image_lines", image) # 统计概率霍夫线变换 def line_detect_possible_demo(image): gray = cv2.cvtColor(image, cv2.COLOR_BGRA2GRAY) edges = cv2.Canny(gray, 50, 150, apertureSize=3) # 函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线 lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=50, maxLineGap=10) for line in lines: x1, y1, x2, y2 = line[0] cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow("line_detect_possible_demo", image) if __name__ == "__main__": img = cv2.imread("image/lines.jpg") cv2.namedWindow("input image", cv2.WINDOW_AUTOSIZE) cv2.imshow("input image", img) line_detect_possible_demo(img) cv2.waitKey(0) cv2.destroyAllWindows() ``` <div id="3"></div> ##三、阈值分割 我们一般使用阈值将前景和背景分割开来,使我们感兴趣的图像的像素值为1,不感兴趣的为0。 单个阈值: $$ g(x, y)=\left\{\begin{array}{ll} a & f(x, y)>T \\ \text {0 other } \end{array}\right. $$ 两个阈值: $$ g(x, y)=\left\{\begin{array}{ll} a & f(x, y)>T_{1} \\ b & T_{1}>f(x, y)>T_{2} \\ \text {0 other } \end{array}\right. $$ ###3.1基本全局阈值处理 根据图像直方图,将区分度大的两个灰度级部分之间进行划分,自动地选择阈值取T为阈值来分开它们。方法如下: 1.针对全局阈值选择初始估计值T; 2.用T分割图像,G1是所有灰度值大于T的像素组成,G2是所有灰度值小于等于T的像素组成; 3.分别计算G1和G2区域内的平均灰度值m1和m2; 4.计算出新的阈值,取他们的m1和m2平均值数; 5.重复步骤2.-4.,直到在连续的重复中,T的差异比预先设定的参数小为止; 6.使用函数im2bw分割图像:g = im2bw(f,T/den) ###3.2使用Otsu’s方法的最佳全局阈值处理 对图像Image,记t为前景与背景的分割阈值,前景点数占图像比例为$w_{0}$,平均灰度为$u_{0}$;背景点数占图像比例为$w_{1}$,平均灰度为$u_{1}$。图像的总平均灰度为:$u=w_{0}u_{0}+w_{1}u_{1}$。从最小灰度值到最大灰度值遍历$t$,当$t$使得值$g=w_{0}(u_{0}-u)^2+w_{1}(u_{1}-u)^2$ 最大时$t$即为分割的最佳阈值。对大津法可作如下理解:该式实际上就是类间方差值,阈值t分割出的前景和背景两部分构成了整幅图像,而前景取值$u_{0}$,概率为 $w_{0}$,背景取值$u_{1}$,概率为$w_{1}$,总均值为$u$,根据方差的定义即得该式。因方差是灰度分布均匀性的一种度量,方差值越大,说明构成图像的两部分差别越大, 当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小,因此使类间方差最大的分割意味着错分概率最小。 $$ \sigma_{B}^{2}(k)=P_{1}(k)\left[m_{1}(k)-m_{\mathrm{G}}\right]^{2}+P_{2}(k)\left[m_{2}(k)-m_{\mathrm{G}}\right]^{2} $$ ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午5.53.42_1602410235250_5d14.jpg =500x400) ###3.3使用边缘改进全局阈值处理 边缘改进的阈值处理:主要是处理那些位于或接近物体和背景间边缘的像素,使得这些像素分离开的操作。 主要有梯度边缘信息和拉普拉斯边缘信息 具体算法过程如下: 用一种边缘查找方式计算图像的模板的值。 通过百分比指定阈值。由于计算的边缘模板值中有很多噪声,所以可以将计算值排序,并选择百分比相对高的值(大于百分下的值的阈值)作为阈值。 根据指定的阈值,对第一步的图像边缘的值进行选择。使高于阈值的像素点值为1,低于的值为零,由此选择出部分边缘点的二值图像(模板)。 用刚才计算出来的模板与原图像相乘,获得一幅新的图像。 对新的图像使用otsu进行分割。 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.13.55_1602411201001_5d14.jpg =400x280) ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.15.16_1602411291349_5d14.jpg =500x280) ###3.4基于局部统计的可变阈值处理 当背景照明高度不均匀时,需要进行阈值处理的难度就增大,为了解决这个问题,运用局部统计的可变阈值处理的算法进行解决。我们用一幅图像中每个点的邻域中像素的标准差和均值,这是局部对比度和平均灰度的描述子。 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.28.13_1602412065839_5d14.jpg =400x150) ###3.5使用移动平均的图像阈值处理 移动平均是一种局部阈值处理方法,该方法以一幅图像的扫描行计算移动平均为基础。移动平均分割能减少光照偏差,且计算简单。当感兴趣的物体与图像尺寸相比较小(或较细)时,基于移动平均的阈值处理会工作的很好。打印图像和手写文本图像满足这一条件。 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.32.09_1602412293641_5d14.jpg =400x120) <div id="4"></div> ##四、基于区域的分割 基于像素特性的分布,通过阈值处理完成。 ###4.1区域生长 区域生长(region growing) 是指将成组的像素或区域发展成更大区域的过程。从种子点的集合开始,从这些点的区域增长是通过将与每个种子点有相似属性像强度、灰度级、纹理颜色等的相邻像素合并到此区域。 用区域生长检测焊接空隙: ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.35.44_1602412511556_5d14.jpg =400x400) ###4.2区域分离和聚合 这个操作是区域生长的反操作,是将图像中的区域分离或合并这些区域。将图像连续地细分为越来越小的象限区域,直到无法进一步合并的时候停止分离。这种方法也称作四叉树。 ![](http://cookdata.cn/media/bbs/images/屏幕快照_2020-10-11_下午6.37.16_1602412593632_5d14.jpg =400x170) <div id="5"></div> ##五、使用分水岭变换的分割 如果目标物体是连接在一起的,则采用分水岭分割算法效果比较好。分水岭分割算法把图像看成一幅地形图,亮度比较强的区域像素值较大,亮度暗的区域像素值比较小,通过寻找汇水盆地和分水岭界线对图像进行分割。分水岭计算分两个步骤,一个是排序过程,一个是淹没过程。 ###5.1使用距离变换的分水岭分割 最常用的分水岭变换分割的是距离变换,主要是用于二值图像的处理,它是指从每个像素到最接近零值的像素的距离。 ###5.2使用梯度的分水岭分割 梯度处理是在分水岭变换之前的预处理,它将沿着物体的边缘有较高的像素值,而在其他地方的像素值比较低。 ###5.3控制标记符的分水岭分割 当直接用梯度进行分水岭变换时,容易造成过度分割,为了改善这样的情况,采用基于标记符的方式进行分割。