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实现分水岭算法。

相关内容

热门资讯

四川五日游报团指南及详细行程,... 四川,这片位于中国西南的神奇土地,以其独特的自然风光、丰富的文化遗产和诱人的美食而闻名遐迩。从成都的...
原创 中... 在2025年4月初,时任美国总统的特朗普正式启动了针对世界各国的关税战,旨在通过实施经济制裁来促进美...
牛市主升浪开启了?别急!珍惜布... 本周,A股市场上行,主要宽基指数都收获了或多或少的周涨幅,其中,科创50、微盘股涨幅居前。板块方面,...
公募二季报两大看点!港股配置逼... 本报(chinatimes.net.cn)记者栗鹏菲 叶青 北京报道 2025年公募基金二季报披露收...
长和出售港口磋商期或延长 随着可能出现的各方介入及交易结构变化,此次长和港口出售交易如继续进行,其复杂性会提升 文 |《财经》...
中航重机涨0.17%,成交额4... 来源:新浪证券-红岸工作室 7月25日,中航重机涨0.17%,成交额4.14亿元,换手率1.52%,...
重仓电子和新能源行业 【深圳商报讯】(记者 陈燕青)基金二季报出炉,公募二季度依然重仓电子、新能源、食品饮料等行业。公募排...
大婚之后,大笔减持!昔日全球首... 当地时间7月25日,亚马逊公司提交至美国证券交易委员会的文件显示,前全球首富、亚马逊创始人杰夫·贝索...
创源股份涨2.32%,成交额3... 来源:新浪证券-红岸工作室 7月25日,创源股份涨2.32%,成交额3.50亿元,换手率8.32%,...
筹备登陆韩国综合股价指数!大韩... 近日,大韩造船(Daehan Shipbuilding)的首次公开募股(IPO)发行价最终确定为每股...
山东政商要情(7.21—7.2... 记者 王惠 1,2025年上半年山东GDP50046亿元 增长5.6% 7月21日,山东省统计局、国...
《法学基本概念导论》| 专研法... 导言 本书是对权利、义务、法律主体、法律规范、法律渊源、法律行为等法学基本概念(juristic f...
上海AI新动向:世界AI合作组... 在今日的天气状况下,上海迎来了阴到多云的天气,偶尔还有阵雨光顾,气温徘徊在27至31摄氏度之间,给市...
山鹰国际跌1.52%,成交额2... 来源:新浪证券-红岸工作室 7月25日,山鹰国际跌1.52%,成交额2.50亿元,换手率2.33%,...
马斯克擎天柱解决不了无「手」难... 新智元报道 编辑:英智 【新智元导读】马斯克说人形机器人是特斯拉的未来,可今年5000台的目标才刚...
开封警方回应网传“释永信相关警... 7月27日,开封市公安局官方微博回复网友评论时表示:“(网传释永信相关)通报是假的,请不要再传播,目...
创新业务模式 提升开放水平 近日,在东营综合保税区食用油分装生产车间,工人们正在进行进口豆油灌装作业。 近年来,东营综合保税区...
中国资本市场学会成立!吴清当选... 来源:证监会发布 2025年7月26日,中国资本市场学会成立大会暨第一届第一次会员代表大会在上...
本周外盘看点丨美联储最新决议来... 来源:第一财经 欧美二季度GDP表现如何,特朗普关税谈判“大限”到来。 上周国际市场风云变幻,美国...
生态环境部逯世泽:全国碳市场量... 21世纪经济报道记者雷椰 李德尚玉 北京报道 7月26日,由冶金工业规划研究院主办,中国节能协会冶金...