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

这篇具有很好参考价值的文章主要介绍了OpenCV入门(十六)快速学会OpenCV 15 图像分割。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作者:Xiou

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

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

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

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

测试图片:

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

1.彩色图像分割

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

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

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

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 cv
     import numpy as np
     def 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=xxx
         cv.imshow('result', dst) #输出
     src = cv.imread('test.jpg')  #导入目标图像,获取图像信息
     color_seperate(src)
     cv.imshow('image', src)
     cv.waitKey(0)
     cv.destroyAllWindows()

输出结果:

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

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表示操作类型,有以下几种类型:

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

利用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)保存工程并运行,这时用鼠标左键单击左上角、用鼠标右键单击右下角,之后单击鼠标中键进行生成。

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

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

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 本身, 忽略新的颜色值参数。

原图:

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

代码实例1:

import numpy as np
import cv2
def flood_fill(image):
    """泛洪"""
    # 深拷贝
    image_copy = image.copy()
    # 获取高宽
    h, w = image.shape[:2]
    # 获取mask
    mask = 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_copy

if __name__ == "__main__":
    # 读取图片
    img = cv2.imread("test.jpg")
    # 获取泛洪图像
    flood_fill = flood_fill(img)
    # 保存图片
    cv2.imwrite("flood_fill.jpg", flood_fill)

输出结果:

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

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 plt

img = 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()

输出结果:

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

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

到了这里,关于OpenCV入门(十六)快速学会OpenCV 15 图像分割的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • OpenCV(十六):高斯图像金字塔

    目录 1.高斯图像金字塔原理 2.高斯图像金字塔实现 1.高斯图像金字塔原理 高斯图像金字塔是一种用于多尺度图像表示和处理的重要技术。它通过对图像进行多次高斯模糊和下采样操作来生成不同分辨率的图像层级,每个层级都是原始图像的模糊和降采样版本。 以下是高斯图

    2024年02月09日
    浏览(42)
  • 图像分割实战-系列教程15:deeplabV3+ VOC分割实战3-------网络结构1

    有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 deeplab系列算法概述 deeplabV3+ VOC分割实战1 deeplabV3+ VOC分割实战2 deeplabV3+ VOC分割实战3 deeplabV3+ VOC分割实战4 deeplabV3+ VOC分割实战5 本项目的网络结构在network文件夹中,主要在

    2024年01月19日
    浏览(63)
  • 图像分割简单介绍,并给出opencv图像分割的示例代码

    图像分割是计算机视觉中的一项重要任务,其目标是将图像中的对象与背景进行分离,或将图像分割成不同的区域。本教程将介绍图像分割的基本概念和方法,以及如何在实践中应用它们。 什么是图像分割? 图像分割方法 实践:使用Python和OpenCV进行图像分割 总结及拓展阅读

    2024年02月09日
    浏览(46)
  • OpenCV图像处理-图像分割-MeanShift

    MeanShift严格说来并不是用来对图像进行分割的,而是在色彩层面的平滑滤波。它会中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的的颜色区域,它以图像上任意一点P为圆心,半径为sp,色彩幅值为sr进行不断地迭代。 语法:pyrMeanShiftFiltering(img, double sp, double sr,

    2024年02月16日
    浏览(45)
  • OpenCV(三)——图像分割

    目录 1.图像分割 2.固定阈值法——直方图双峰法 3.自动阈值法 3.1 自适应阈值法

    2024年02月03日
    浏览(49)
  • 【Opencv】图像分割——区域生长

    Python 3.8.8 PyCharm 2021 opencv-python   区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长的起点,然后将种子像素周围邻域中与种子像素具有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定

    2024年02月05日
    浏览(43)
  • OpenCV(三)——图像分割(三)

    目录 6.区域生长算法 6.1 区域生长概要 6.2 区域生长原理 7.分水岭算法

    2024年02月13日
    浏览(36)
  • opencv 十六 python下各种连通域处理方法(按面积阈值筛选连通域、按面积排序筛选连通域、连通域分割等方法)

    本博文基于python-opencv实现了按照面积阈值筛选连通域、按照面积排序筛选topK连通域、 连通域细化(连通域骨架提取)、连通域分割(基于分水岭算法使连通域在细小处断开)、按照面积排序赛选topK轮廓等常见的连通域处理代码。并将代码封装为shapeUtils类,在自己的python代

    2024年02月04日
    浏览(81)
  • vue快速入门(十六)事件修饰符

    注释很详细,直接上代码 上一篇 新增内容 事件修饰符之阻止冒泡 事件修饰符之阻止默认行为 源码 效果演示 下一篇

    2024年04月13日
    浏览(46)
  • OPENCV--实现meanshift图像分割

    2024年02月08日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包