区域增长算法
目标
- 掌握区域生长法的基本思想
- 掌握图像分裂合并分割的基本思想及步骤
区域生长法分割
大津算法的局限性:噪声比较严重的图片、分割目标颜色渐变的情况,分割效果差。
区域生长:从种子点开始,按照一定准则(如相邻像素灰度相似性)向周围扩散,将邻域相似像素加入区域中。
按照扩散顺序,分为广度优先搜索(优先比较原像素所有周边像素)和深度优先搜索(比较原像素某一特定方向的像素)。
区域生长实现步骤:
- 对图像顺序扫描。找到第1个还没有归属的像素,设像素为(x0,y0x_0,y_0x0,y0);
- 以(x0,y0x_0,y_0x0,y0)为中心,考虑(x0,y0x_0,y_0x0,y0)的8邻域像素(x,y)(x,y)(x,y),如果(x,y)(x,y)(x,y)满足生长准则,将(x,y)(x,y)(x,y)与(x0,y0x_0,y_0x0,y0)合并,同时将(x,y)(x,y)(x,y)压入堆栈;
- 从堆栈中取出一个像素,把它当作(x0,y0)(x_0,y_0)(x0,y0)返回到步骤2;
- 当堆栈为空时,返回步骤1;
- 重复步骤1-4直到图像中的每个点都有归属时,生长结束。
区域分裂合并
图像分裂:分裂合并可采用基于四叉树的数据表示
对于像素方差不大的区域不用分裂,像素方差大的区域进行分裂。将相邻像素的区域进行合并。
实现步骤:
- 对区域分裂合并法无需预先指定种子点,它按某种一致性准则分裂或合并区域
- 可以先进行分裂运算,然后再进行合并运算;也可以分裂和合并运算同时进行,经过连续的分裂和合并,最后得到图像的精确分割效果。
- 分裂合并法对分割复杂的场景图像比较有效。
总结
- 区域生长法基于相邻像素间的相似性,由种子像素逐步生长得到
- 分裂-合并基于图像块内在的相似性,通过不断分裂得到区域外边界,通过合并将不同块连接
分水岭算法
目标
- 掌握分水岭算法的基本思想
- 了解分水岭算法的运行步骤
分水岭算法分割
- 如果以图像位置(x,y)(x,y)(x,y)为坐标,则图像(x,y)(x,y)(x,y)可以看作是地形俯视图,其中“山峰的高度”与图像中的灰度值对应。
- 假设在每个“盆地”的最低点开始打洞让水漫上来,并且让水以均匀速率上升,那么,当不同“盆地”的水开始汇聚时,能通过修建一个“水坝”==挡住这种聚合的弧线就是图像的分界线。
在漫水过程中,存在三种类型的点: - 局部极小值点,该点对应一个盆地的最低点,当我们在盆地滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内都是最小值点。
- 盆地的其它位置点,该位置滴的水滴会汇聚带局部最小点。
- 盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。
符号:
T[n]T[n]T[n]:满足灰度<n<n<n的所有图像点的集合;
C(Mi)C(M_i)C(Mi):与区域最小值集合MiM_iMi相联系的盆地图像点集合
C[n]C[n]C[n]:在第n阶灰度计算的图像区域集合,C[n]=∪i=1RCn(Mi)C[n] = \cup_{i=1}^RC_n(M_i)C[n]=∪i=1RCn(Mi)
目标:迭代计算C[n]C[n]C[n],进一步获得区域分割线(水坝)
步骤:
- 初始化:从最低水面(最低灰度对应的点集)开始,对应C[min+1]=T[min+1]C[min+1]= T[min+1]C[min+1]=T[min+1]
- 迭代计算:由C[n−1]C[n-1]C[n−1]计算C[n]C[n]C[n]:
令QQQ表示T[n]T[n]T[n]中连通分量的点集。对每个分量q∈Qq\in Qq∈Q,如下处理:
(1)q∩C[n−1]=∅q\cap C[n-1]=\varnothingq∩C[n−1]=∅:发现新的区域,连通分量q并入C[n]C[n]C[n]
(2)q∩C[n−1]q\cap C[n-1]q∩C[n−1]:填充未溢出(到别的盆地);连通分量q并入C[n]C[n]C[n]
(3)q∩C[n−1]q\cap C[n-1]q∩C[n−1]包含C[n−1]C[n-1]C[n−1]中的多个分量:填充溢出;在q内构筑水坝,方法同前
分水岭算法的过分割问题
由于噪声或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在。
在初始时给marker,改善过分割问题:
为了解决过分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是指定mark图像(图中红色区域),在这个区域的洪水淹没过程中,水平面都是定义的marker开始的,这样可以避免一些很小的噪声极值区域的分割。
使用marker改善分割结果:
总结:
分水岭算法基本思想来源于由低到高"漫水"和“修建水坝”。分割过程通过漫水和膨胀,按照不同灰度级迭代进行。
图像分割实战演练(II)
目标:
- 使用OpenCV实现区域漫水填充
- 使用OpenCV实现分水岭分割
相关函数
- 漫水填充(区域生长法)
retval, image, mask, rect = cv2.floodFill(image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]])
#image:输入图像,可以是一通道或者是三通道。
#mask:操作掩膜。 单通道,8位,在长宽上都比原图像image多2个像素点。漫水填充不会填充掩膜区域的非0像素点,所以说掩膜是屏蔽了漫水填充的处理。如边缘检测算子的输出可以用来作为掩膜,这样可防止边缘区域不被填充。因为掩膜比原图像大,所以掩膜中的(x,y),对应的原图像的像素点为(x+1,y+1)。
#seedPoint:Point类型,漫水填充的种子点,即起始点。
#newVal:Scalar类型,被填充的像素点新的值
#rect:Rect*类型,有默认值0,可选的参数,设置函数将要重绘区域的最小边界矩形区域
#loDiff:Scalar类型,有默认值Scalar(),表示当前的观察点像素值与其相邻区域像素值或待加入该区域的像素之间的亮度或颜色之间负差(lower brightness/color difference)的最大值。
#upDiff:Scalar类型,有默认值Scalar(),表示当前的观察点像素值与其相邻区域像素值或待加入该区域的像素之间的亮度或颜色之间正差(lower brightness/color difference)的最大值。
#flags:int类型,操作位标识符,包括三个部分,控制算法连通性等:
# 1.低八位(0-7):控制算法的连通性,设置为4:填充算法只考虑当前像素点垂直和水平方向;设置为8:除垂直和水平方向,还会考虑对角线的相邻点
# 2.高八位(16-23):可以为0或者下列两种标识符组合。# FLOODFILL_FIXED_RANGE:考虑种子像素与种子像素之间的差,否则考虑当前像素与与邻近像素的差。# FLOODFILL_MASK_ONLY :如果设置这个标识符,函数不会填充或改变原始图像(也就是忽略的newVal),而是去填充掩膜图像。# 3.中间八位:用于指定填充掩码图像的值,如果flags中间八位值为0,则掩码会用1填充
- 分水岭法图像分割
makers = cv2.watershed(image, markers)
#image:三联通彩色图像
#markers:记号点(种子点),每一个记号需要有不同的编号
解决思路:
- 图像采集(取到图像)
- 图像预处理
- 使用距离变换或计算图像梯度,得到分水岭处理图像
- 应用分水岭算法分割
- 图像特征描述及目标分析
- 得到最终结果
import cv2
import numpy as npimg = cv2.imread('C:/python/img/water_coins.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# noise removal
kernel = np.ones((3, 3),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations= 2)#sure background area
sure_bg = cv2.dilate(opening, kernel, iterations=3)cv2.imshow("threshold",thresh) #阈值化图像
cv2.imshow("Opened image",opening) #开运算图像
cv2.imshow("Dilated image",sure_bg) #膨胀后图像cv2.waitKey()
cv2.destroyAllWindows()
- 读入图像,转换为灰度图像;
- 使用大津算法二值化;
- 使用形态学开运算二次去掉小的白色噪点,使用膨胀运算确保背景与原图一致
#Finding sure foreground area
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_bg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)#Finding unkown region
sure_fg = np.uint8(sure_fg)
unkown = cv2.subtract(sure_bg, sure_fg)cv2.imshow("dist_ret", dist_transform)
cv2.imshow("threshold", sure_fg)
cv2.imshow("unkown regions", unkown)cv2.waitKey()
cv2.destroyAllWindows()
- 使用距离变换(distance Transform)计算每个硬币中心。采用腐蚀变换可以实现类似效果
- 使用阈值化得到分离的硬币
- 通过相减得到相连接硬币的不确定区域(有待分割计算)
#Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)#Add one to all labels so that sure background is noe 0,but 1
markers = markers + 1
#Now, mark the region of unknown with zero
markers[unkown==255]=0markers = cv2.watershed(img, markers)
img[markers == -1] = [255, 0, 0]markers = np.uint8(markers)cv2.imshow('result image', img)
cv2.imshow('result', markers)cv2.waitKey()
cv2.destroyAllWindows()
- 使用connectedComponents给每个连通区域做标记,标记从0开始。0为背景
- 标记值+1,让标记从1开始
- 不确定区域标记置为0,便于下一步分割
- 使用watershed做分水岭标记
- 分割线(水坝)用蓝色标记