OpenCV入门(十六)快速学会OpenCV 15 图像分割
创始人
2025-05-30 08:13:31
0

OpenCV入门(十六)快速学会OpenCV 15 图像分割

  • 1.彩色图像分割
  • 2.grabCut算法分割
  • 3.floodFill漫水填充分割
  • 4.分水岭分割

作者:Xiou

图像分割主要是指将图像分成各具特性的区域并提取出感兴趣目标的技术。图像分割是数字图像分析中的重要环节,在整个研究中起着承前启后的作用,既是对所有图像预处理效果的一个检验,也是后续进行图像分析与解译的基础。

图像阈值化分割是一种传统的、最常用的图像分割方法,因其实现简单、计算量小、性能较稳定,成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像,在很多情况下是进行图像分析、特征提取与模式识别之前必要的图像预处理过程。

图像阈值化的目的是要按照灰度级对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。

阈值分割的基本原理是通过设定不同的特征阈值把图像像素点分为若干类。常用的特征包括直接来自原始图像的灰度或彩色特征、由原始灰度或彩色值变换得到的特征。

测试图片:

在这里插入图片描述

1.彩色图像分割

灰度图像大多通过算子寻找边缘和区域生长融合来分割图像。彩色图像增加了色彩信息,可以通过不同的色彩值来分割图像,常用彩色空间HSV/HIS、RGB、LAB等都可以用于分割。本节将使用inRange函数来实现阈值化,跟前面的阈值化方法一样,只不过在实现时用阈值范围来替代固定阈值。inRange函数提供了一种物体检测的手段,用基于像素值范围的方法,在HSV色彩空间检测物体从而达到分割的效果。

HSV(Hue、Saturation、Value的首字母,表示颜色的色相、饱和度、强度)色彩空间是一种类似于RGB的颜色表示方式。hue通道是颜色类型,在需要根据颜色来分割物体的应用中非常有效。如图所示,saturation的变化从不饱和到完全饱和,对应图中灰色过渡到阴影(没有白色成分)。value描述了颜色的强度或者亮度。图中显示的是HSV圆柱体,表示HSV的颜色空间。

在这里插入图片描述

HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,这个模型中颜色的参数分别是色调(H)、饱和度(S)、明度(V)。

由于RGB色彩空间是由三个通道来编码颜色的,因此难以根据颜色来分割物体,而HSV中只有Hue一个通道表示颜色。此时可以用函数cvtColor将BGR转换到HSV色彩空间,然后利用函数inRange根据HSV设置的范围检测目标。该函数声明如下:

     inRange(src, lowerb, upperb[, dst]) 

其中,
src表示输入图像;
lowerb表示H、S、V的最小值;
upperb表示H、S、V的最大值;
dst表示输出图像,要和输入图像有相同的尺寸且为CV_8U类。

直接用HSV体系进行颜色分割

代码实例:

     import cv2 as cvimport numpy as npdef color_seperate(image):hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)   #对目标图像进行色彩空间转换lower_hsv = np.array([100, 43, 46])  #设定蓝色下限upper_hsv = np.array([124, 255, 255])  #设定蓝色上限#依据设定的上下限对目标图像进行二值化转换mask = cv.inRange(hsv, lowerb=lower_hsv, upperb=upper_hsv)#将二值化图像与原图进行“与”操作;实际是提取前两个frame的“与”结果,#然后输出mask为1的部分dst = cv.bitwise_and(src, src, mask=mask)    #注意:括号中要写mask=xxxcv.imshow('result', dst) #输出src = cv.imread('test.jpg')  #导入目标图像,获取图像信息color_seperate(src)cv.imshow('image', src)cv.waitKey(0)cv.destroyAllWindows()

输出结果:

在这里插入图片描述

2.grabCut算法分割

可以使用grabCut算法来分割前景或使用最小程度的用户交互来分解前景。

OpenCV中的grabCut算法是Graph_Cut算法的改进,Graph_Cut是一种直接基于图割算法的图像分割技术,仅仅需要确认前景和背景输入就可以完成前景和背景的最优分割。

该算法利用了图像中的纹理(颜色)信息和边界(反差)信息,只要少量的用户交互操作即可得到比较好的分割结果,和分水岭算法比较相似,但是计算速度比较慢,得到的结果比较精确。如果要从静态图像中提取前景物体(如从一个图像剪切物体到另一个图像),采用grabCut算法是最好的选择。

grabCut函数声明如下:

     grabCut(img, mask, rect, bgdModel, fgdModel, iterCount[, mode])

参数
img表示输入原图像。
mask表示输出掩码,如果使用掩码进行初始化,那么mask保存初始化掩码信息,在执行分割的时候也可以将用户交互所设定的前景与背景保存到mask中再传入grabCut函数。在处理结束之后,mask中会保存结果。
mask只能取四种值:
GCD_BGD(=0)表示背景;
GCD_FGD(=1)表示前景;
GCD_PR_BGD(=2)表示可能的背景;
GCD_PR_FGD(=3)表示可能的前景。

如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只能是GCD_PR_BGD或GCD_PR_FGD。rect表示用户选择的前景矩形区域,包含分割对象的矩形ROL,矩形外部的像素为背景,矩形内部的像素为前景,当参数mode=GC_INIT_WITH_RECT时使用这个参数。bgModel表示输出背景图像。fgdModel表示输出前景图像。iterCount表示迭代次数。mode表示用于指示grabCut函数进行什么操作,可选的值有GC_INIT_WITH_RECT(=0)表示用矩形窗初始化grabCut;GC_INIT_WITH_MASK(=1)表示用掩码图像初始化grabCut;GC_EVAL(=2)表示执行分割。

利用grabCut函数做图像分割时,通常还需要和compare函数联合使用。compare函数主要用于在两个图像之间进行逐像素的比较,并输出比较的结果,函数声明如下:

     cv2.compare(src1, src2, cmpop[, dst]) 

其中,参数
src1表示原始图像1(必须是单通道)或者一个数值,比如是一个Mat或者一个单纯的数字n;
src2表示原始图像2(必须是单通道)或者一个数值,比如是一个Mat或者一个单纯的数字n;
dst表示结果图像,类型是CV_8UC1,即单通道8位图,大小和src1和src2中最大的一样,比较结果为真的地方值为255,否则为0;
cmpop表示操作类型,有以下几种类型:

在这里插入图片描述

利用grabCut做图像前景分割

代码实例:

import numpy as np  
import cv2  
from matplotlib import pyplot as plt  
import warnings  warnings.filterwarnings("ignore", module="matplotlib")  imgpath = "girl.jpg"  
img = cv2.imread(imgpath)  Coords1x, Coords1y = 'NA', 'NA'  
Coords2x, Coords2y = 'NA', 'NA'  def OnClick(event):  #获取当鼠标"按下"的时候,鼠标的位置  global Coords1x, Coords1y  if event.button == 1:  try:  Coords1x = int(event.xdata)  Coords1y = int(event.ydata)  except:  Coords1x = event.xdata  Coords1y = event.ydata  print("####左上角坐标:", Coords1x, Coords1y)  def OnMouseMotion(event):  #获取当鼠标"移动"的时候,鼠标的位置  global Coords2x, Coords2y  if event.button == 3:  try:  Coords2x = int(event.xdata)  Coords2y = int(event.ydata)  except:  Coords2x = event.xdata  Coords2y = event.ydata  print("####   右下角坐标:", Coords2x, Coords2x)  def OnMouseRelease(event):  if event.button == 2:  fig = plt.gca()  img = cv2.imread(imgpath)  #创建一个与所加载图像同形状的  Mask  mask = np.zeros(img.shape[:2], np.uint8)  #算法内部使用的数组,你必须创建两个np.float64类型的0数组,大小是(1, 65)  bgdModel = np.zeros((1, 65), np.float64)  fgdModel = np.zeros((1, 65), np.float64)  #计算人工前景的矩形区域  (rect.x,rect.y,rect.width,rect.height)  if (Coords2x - Coords1x) > 0 and (Coords2y - Coords1y) > 0:  try:  rect = (Coords1x, Coords1y, Coords2x - Coords1x, Coords2y -Coords1y)  print('####   分割区域:  ', rect)  print('####   等会儿  有点慢  ...')  iterCount = 5  cv2.grabCut(img, mask, rect, bgdModel, fgdModel, iterCount, cv2.GC_INIT_WITH_RECT)  mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')  img = img * mask2[:, :, np.newaxis]  plt.subplot(121), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  plt.subplot(122), plt.imshow(cv2.cvtColor(cv2.imread(imgpath), cv2.COLOR_BGR2RGB))  fig.figure.canvas.draw()  print('May the force be with me!')  except:  print('####   先左键  后右键  ')  else:  print('#### 左下角坐标值必须大于右上角坐标  ')  #预先绘制图片  
fig = plt.figure()  
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))  #鼠标左键,选取分割区域(长方形)的左上角点  
fig.canvas.mpl_connect('button_press_event', OnClick)  
#鼠标右键,选取分割区域(长方形)的右下角点  
fig.canvas.mpl_connect('button_press_event', OnMouseMotion)  
#鼠标中键,在所选区域执行分割操作  
fig.canvas.mpl_connect('button_press_event', OnMouseRelease)  
plt.show() 

代码实现的基本步骤如下:
(1)在图片中定义含有(一个或者多个)物体的矩形。
(2)矩形外的区域被自动认为是背景。
(3)对于用户定义的矩形区域,可用背景中的数据来区别它里面的前景和背景区域。(4)用高斯混合模型来对背景和前景建模,并将未定义的像素标记为可能的前景或背景。
(5)图像中的每一个像素都被看作通过虚拟边与周围像素相连接,而每条边都有一个属于前景或背景的概率,这基于它与周围颜色上的相似性。
(6)每一个像素(算法中的节点)会与一个前景或背景节点链接。
(7)在节点完成链接后,若节点之间的边属于不同终端,则会切断它们之间的边,这就能将图像各部分分割出来。
(8)保存工程并运行,这时用鼠标左键单击左上角、用鼠标右键单击右下角,之后单击鼠标中键进行生成。

运行结果如图所示。可以看到,前景、背景都已经被分离出来了。

在这里插入图片描述

3.floodFill漫水填充分割

漫水填充法是一种用特定的颜色填充联通区域,通过设置可连通像素的上下限以及连通方式来达到不同的填充效果的方法。漫水填充经常被用来标记或分离图像的一部分,以便对其进行进一步处理或分析,也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是某个连续的区域。

漫水填充法的原理很简单,就是从一个点开始遍历附近的像素点,填充成新的颜色,直到封闭区域内所有像素点都被填充成新颜色为止。floodFill填充的实现方法常见的有4邻域像素填充法、8邻域像素填充法、基于扫描线的像素填充法等。

在OpenCV中,漫水填充算法由floodFill函数实现,其作用是用我们指定的颜色从种子点开始填充一个连接域,连通性由像素值的接近程度来衡量。在OpenCV中,有两个C++重写版本的floodFill,函数声明如下:

    floodFill(image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]])

参数:
image: 输入图片
mask: 掩码, 比 image 长宽高 2
seedPoint: 泛洪算法的起始点
newVal: 重绘区域的新值 (颜色)
loDiff: seePoint - loDiff 下限
upDiff: seePoint - upDiff 上限

flags: 操作标志符
LOODFILL_FIXED_RANGE: 改变图像, 泛洪填充
FLOODFILL_MASK_ONLY: 不改变图像, 只填充 mask 本身, 忽略新的颜色值参数。

原图:

在这里插入图片描述

代码实例1:

import numpy as np
import cv2
def flood_fill(image):"""泛洪"""# 深拷贝image_copy = image.copy()# 获取高宽h, w = image.shape[:2]# 获取maskmask = np.zeros([h + 2, w + 2], np.uint8)# 泛洪cv2.floodFill(image_copy, mask, (175, 20), (0, 25, 0), (30, 30, 30), (50, 50, 50), cv2.FLOODFILL_FIXED_RANGE)# 图片展示cv2.imshow("flood_fill", image_copy)cv2.waitKey(0)cv2.destroyAllWindows()# 返回return image_copyif __name__ == "__main__":# 读取图片img = cv2.imread("test.jpg")# 获取泛洪图像flood_fill = flood_fill(img)# 保存图片cv2.imwrite("flood_fill.jpg", flood_fill)

输出结果:

在这里插入图片描述

4.分水岭分割

分水岭算法是一种图像区域分割法,在分割的过程中会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来,构成一个封闭的轮廓。封闭性是分水岭算法的一个重要特征。

在OpenCV中,分水岭算法的函数是watershed,声明如下:

     void cv::watershed ( InputArray  image, InputOutputArray  markers )

参数
image必须是一个8位3通道彩色图像矩阵序列;
参数markers表示必须包含种子点信息,在执行分水岭函数watershed之前,必须对第二个参数markers进行处理,它应该包含不同区域的轮廓,每个轮廓有一个唯一的编号,轮廓的定位可以通过OpenCV中findContours方法实现。

使用watershed函数实现图像自动分割的基本步骤如下:
(1)图像灰度化、滤波、Canny边缘检测。
(2)查找轮廓,把轮廓信息按照不同的编号绘制到watershed的第二个入参markers上,相当于标记注水点。
(3)watershed分水岭运算。
(4)绘制分割出来的区域。另外,还可以使用随机颜色填充,或者跟原始图像融合一下,以得到更好的显示效果。

代码实例:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as pltimg = cv.imread('coin.png')
if img is None:print('Could not open or find the image ')exit(0)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
# cv.imshow("threshold", thresh)    #阈值处理后会有紧挨着(粘连)的情况
# 去噪处理
kernel = np.ones((3, 3), np.uint8)
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2)  # 开运算
# sure background area
sure_bg = cv.dilate(opening, kernel, iterations=3)  # 膨胀操作# Finding sure foreground area
dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)
ret, sure_fg = cv.threshold(dist_transform, 0.7 * dist_transform.max(), 255, 0)  # 距离背景点足够远的点认为是确定前景# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg, sure_fg)  # 确定未知区域:减法运算
# Marker labelling
ret, markers = cv.connectedComponents(sure_fg)  # 设定坝来阻止水汇聚# Add one to all labels so that sure background is not 0, but 1
markers = markers + 1
# Now, mark the region of unknown with zero
markers[unknown == 255] = 0
markers = cv.watershed(img, markers)
img[markers == -1] = [255, 0, 0]
plt.imshow(img)
plt.show()

输出结果:

在这里插入图片描述
在这里插入图片描述

其中,threshold函数的作用是进行阈值处理,morphologyEx函数的作用是去除噪声,dilate函数的作用是用膨胀的方式获取背景。
distanceTransform函数进行距离变换,cv.DIST_L2代表采用欧几里得的距离计算公式,5代表掩膜尺寸,用来确定前景,然后通过阈值处理得到核心的区域,超过最大值的70%才留下来。
在分水岭算法中,标注0代表未知区域,所以需要对上面的标注结果进行调整。最后用函数watershed实现分水岭算法。

相关内容

热门资讯

阿联酋最大银行及另两家中东银行... 观点网讯:5月8日,路透社报道指,阿联酋最大银行第一阿布扎比银行(First Abu Dhabi B...
深圳239亿地王易主,再造万象... 2017年,世茂集团豪掷239.43亿元拿下世茂深港国际中心地块,曾规划建筑高度达700米的深圳第一...
蔚来在安庆成立新能源科技公司 ... 天眼查App显示,近日,安庆蔚来新能源科技有限公司成立,法定代表人为姚蒀,注册资本500万人民币,经...
美国牛肉商期盼峰会重启对华出口 据路透社5月8日报道,美国牛肉生产商正期待特朗普与中国于5月14日至15日的峰会推动对华出口许可恢复...
创业板首家未盈利企业,市值突破... 5月8日,大普微总市值正式突破2000亿元大关。截至午间收盘,大普微涨14.07%,报493.1元/...
招商证券:董事长霍达因工作变动... 招商证券公告,公司董事长霍达因工作变动申请辞去董事长、执行董事等全部职务,辞任自辞呈送达董事会之日生...
原创 中... 【阅读须知】本文所引用的所有信息和数据,均为作者通过查阅官方资料与网络公开数据整理、分析而成,旨在为...
原创 从... 2026年5月5日,中国商务部发布了一项具有划时代意义的专项阻断禁令,这份公告让一向倚仗长臂管辖的美...
布米普特拉北京投资基金管理有限... 美国圣路易斯联邦储备银行总裁穆萨莱姆周三发出明确信号,美联储货币政策面临的潜在风险正在发生关键转向。...
加工的秘密:超精加工设备如何做... 你知道吗? 一根头发丝的直径大约0.07毫米,也就是70微米。 超精加工设备,可切出表面,其尺寸为0...
招商证券董事长霍达因工作变动离... 北京商报讯(记者 刘宇阳 实习生 王思奕)5月8日,招商证券发布关于公司董事长离任暨推举董事代行董事...
华帝股份营收创近3年新低,37... 乐居财经李兰近日,华帝股份(002035.SZ)发布2025年年度报告。 2025年,华帝股份实现营...
大模型融资杀疯了!月之暗面狂揽... 图源:视觉中国 5月7日,据华峰资本官微消息,国内头部大模型公司月之暗面(Kimi)于近日完成新一轮...
扎根长宁二十余载,仲利国际融资... 作为总部扎根上海长宁的优质台资金融企业,仲利国际融资租赁有限公司深耕融资租赁行业二十余载,始终坚守金...
估值210亿!李彦宏又将收获一... 来源:直通IPO,文/王非 国产GPU上市潮仍然汹涌,继两家登陆A股、两家登陆H股后,这家公司正推进...
基金“盲盒”拆了 公募基金正在迎来一场让投资者“看得懂”的变革。 近日,华夏、易方达、南方、招商等12家头部及特色基金...
原创 2... 前言 十年间,中国企业在印尼镍产业链累计砸下超过140亿美元,电厂、公路、码头和全套生产线,硬生生...
原创 欧... 俄罗斯卫星通讯社5月6日报道,欧盟宣布禁止欧洲银行为含有来自不可靠供应商的关键部件的可再生能源项目提...
原创 余... 2026年5月2日,在中国理财市场扎根十三年的余额宝,终于触碰到了一个让所有人错愕的数字——7日年化...
银华基金增聘谭普景共同管理银华... 来源:新浪基金∞工作室 5月8日,银华基金管理股份有限公司发布公告称,为银华中证机器人交易型开放式指...