[Python从零到壹] 六十三.图像识别及经典案例篇之图像漫水填充分割应用

Posted Eastmount

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Python从零到壹] 六十三.图像识别及经典案例篇之图像漫水填充分割应用相关的知识,希望对你有一定的参考价值。

祝大家新年快乐,阖家幸福,健康快乐!

欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希望对您有所帮助,文章中不足之处也请海涵。Python系列整体框架包括基础语法10篇、网络爬虫30篇、可视化分析10篇、机器学习20篇、大数据分析20篇、图像识别30篇、人工智能40篇、Python安全20篇、其他技巧10篇。您的关注、点赞和转发就是对秀璋最大的支持,知识无价人有情,希望我们都能在人生路上开心快乐、共同成长。

该系列文章主要讲解Python OpenCV图像处理和图像识别知识,前期主要讲解图像处理基础知识、OpenCV基础用法、常用图像绘制方法、图像几何变换等,中期讲解图像处理的各种运算,包括图像点运算、形态学处理、图像锐化、图像增强、图像平滑等,后期研究图像识别、图像分割、图像分类、图像特效处理以及图像处理相关应用。

第一部分作者介绍了图像处理基础知识,第二部分介绍了图像运算和图像增强,接下来第三部分我们将详细讲解图像识别及图像处理经典案例,该部分属于高阶图像处理知识,能进一步加深我们的理解和实践能力。图像分割是将图像分成若干具有独特性质的区域并提取感兴趣目标的技术和过程,它是图像处理和图像分析的关键步骤。主要分为基于阈值的分割方法、基于区域的分割方法、基于边缘的分割方法和基于特定理论的分割方法。上一篇文章介绍基于均值漂移算法和分水岭算法的图像分割,这篇文章将详细讲解漫水填充分割应用,至此,几种图像分割方法介绍完毕。希望文章对您有所帮助,如果有不足之处,还请海涵。

文章目录

下载地址:记得点赞喔 O(∩_∩)O

前文赏析:(尽管该部分占大量篇幅,但我舍不得删除,哈哈!)

第一部分 基础语法

第二部分 网络爬虫

第三部分 数据分析和机器学习

第四部分 Python图像处理基础

第五部分 Python图像运算和图像增强

第六部分 Python图像识别和图像高阶案例

第七部分 NLP与文本挖掘

第八部分 人工智能入门知识

第九部分 网络攻防与AI安全

第十部分 知识图谱构建实战

扩展部分 人工智能高级案例

作者新开的“娜璋AI安全之家”将专注于Python和安全技术,主要分享Web渗透、系统安全、人工智能、大数据分析、图像识别、恶意代码检测、CVE复现、威胁情报分析等文章。虽然作者是一名技术小白,但会保证每一篇文章都会很用心地撰写,希望这些基础性文章对你有所帮助,在Python和安全路上与大家一起进步。


一.图像漫水填充

图像漫水填充(FloodFill)是指用一种特定的颜色填充联通区域,通过设置可连通像素的上下限以及连通方式来达到不同的填充效果。漫水填充通常被用来标记或分离图像的一部分以便对其进行深入的处理或分析。

图像漫水填充主要是遴选出与种子点联通且颜色相近的像素点,接着对像素点的值进行处理。如果遇到掩码,则根据掩码进行处理。其原理类似Photoshop的魔术棒选择工具,漫水填充将查找和种子点联通的颜色相同的点,而魔术棒选择工具是查找和种子点联通的颜色相近的点,将和初始种子像素颜色相近的点压进栈作为新种子。基本工作步骤如下:

  • 选定种子点(x,y);
  • 检查种子点的颜色,如果该点颜色与周围连接点的颜色不相同,则将周围点颜色设置为该点颜色;如果相同则不做处理。但是周围点不一定都会变成和种子点的颜色相同,如果周围连接点在给定的范围(从loDiff到upDiff)内或在种子点的像素范围内才会改变颜色;
  • 检测其他连接点,进行第2个步骤的处理,直到没有连接点,即到达检测区域边界停止。

二.图像漫水填充分割实现

在OpenCV中,主要通过floodFill()函数实现漫水填充分割,它将用指定的颜色从种子点开始填充一个连接域。其函数原型如下所示:

  • floodFill(image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]])
    – image表示输入/输出1通道或3通道,6位或浮点图像
    – mask表示操作掩码,必须为8位单通道图像,其长宽都比输入图像大两个像素点。注意,漫水填充不会填充掩膜mask的非零像素区域,mask中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。
    – seedPoint为Point类型,表示漫水填充算法的起始点
    – newVal表示像素点被染色的值,即在重绘区域像素的新值
    – loDiff表示当前观察像素值与其部件邻域像素值或待加入该部件的种子像素之间的亮度或颜色之负差的最大值,默认值为Scalar( )
    – upDiff表示当前观察像素值与其部件邻域像素值或待加入该部件的种子像素之间的亮度或颜色之正差的最大值,默认值为Scalar( )
    – flags表示操作标识符,此参数包括三个部分:低八位0-7bit表示邻接性(4邻接或8邻接);中间八位8-15bit表示掩码的填充颜色,如果中间八位为0则掩码用1来填充;高八位16-31bit表示填充模式,可以为0或者以下两种标志符的组合,FLOODFILL_FIXED_RANGE表示此标志会考虑当前像素与种子像素之间的差,否则就考虑当前像素与相邻像素的差。FLOODFILL_MASK_ONLY表示函数不会去填充改变原始图像,而是去填充掩码图像mask,mask指定的位置为零时才填充,不为零不填充。

在Python和OpenCV实现代码中,它设置种子点位置为(10,200);设置颜色为黄色(0,255,255);连通区范围设定为loDiff和upDiff;标记参数设置为CV_FLOODFILL_FIXED_RANGE ,它表示待处理的像素点与种子点作比较,在范围之内,则填充此像素,即种子漫水填充满足:

  • src(seed.x, seed.y) - loDiff <= src(x, y) <= src(seed.x, seed.y) +upDiff

完整代码如下:

# -*- coding: utf-8 -*-
# By: Eastmount
import cv2
import numpy as np

#读取原始图像
img = cv2.imread('windows.png')

#获取图像行和列
rows, cols = img.shape[:2]

#目标图像
dst = img.copy()

#mask必须行和列都加2且必须为uint8单通道阵列
#mask多出来的2可以保证扫描的边界上的像素都会被处理
mask = np.zeros([rows+2, cols+2], np.uint8)  

#图像漫水填充处理
#种子点位置(30,30) 设置颜色(0,255,255) 连通区范围设定loDiff upDiff
#src(seed.x, seed.y) - loDiff <= src(x, y) <= src(seed.x, seed.y) +upDiff
cv2.floodFill(dst, mask, (30, 30), (0, 255, 255),
              (100, 100, 100), (50, 50, 50),
              cv2.FLOODFILL_FIXED_RANGE)

#显示图像
cv2.imshow('src', img)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

输出结果如图1所示,左边为原始图像,右边为将Windows图标周围填充为黄色的图像。


三.图像漫水填充分割自动软件

下面补充另一段代码,它将打开一幅图像,点击鼠标选择种子节点,移动滚动条设定连通区范围的loDiff和upDiff值,并产生动态的漫水填充分割。

注意,该部分代码中涉及鼠标、键盘、滚动条等操作,希望读者下来学习相关知识,该系列文章更多是讲解Python图像处理的算法原理及代码实现。

# coding:utf-8
import cv2
import random
import sys
import numpy as np

#使用说明 点击鼠标选择种子点
help_message = '''USAGE: floodfill.py [<image>]
Click on the image to set seed point
Keys:
  f - toggle floating range
  c - toggle 4/8 connectivity
  ESC - exit
'''
 
if __name__ == '__main__':

    #输出提示文本
    print(help_message)

    #读取原始图像
    img = cv2.imread('scenery.png')

    #获取图像高和宽
    h, w = img.shape[:2]

    #设置掩码 长和宽都比输入图像多两个像素点 
    mask = np.zeros((h+2, w+2), np.uint8)

    #设置种子节点和4邻接
    seed_pt = None
    fixed_range = True
    connectivity = 4 

    #图像漫水填充分割更新函数
    def update(dummy=None):
        if seed_pt is None:
            cv2.imshow('floodfill', img)
            return
        
        #建立图像副本并漫水填充
        flooded = img.copy()
        mask[:] = 0 #掩码初始为全0
        lo = cv2.getTrackbarPos('lo', 'floodfill') #像素邻域负差最大值
        hi = cv2.getTrackbarPos('hi', 'floodfill') #像素邻域正差最大值
        print('lo=', lo, 'hi=', hi)

        #低位比特包含连通值 4 (缺省) 或 8
        flags = connectivity
        
        #考虑当前像素与种子像素之间的差(高比特也可以为0)
        if fixed_range:
            flags |= cv2.FLOODFILL_FIXED_RANGE
            
        #以白色进行漫水填充
        cv2.floodFill(flooded, mask, seed_pt,
                      (random.randint(0,255), random.randint(0,255),
                       random.randint(0,255)), (lo,)*3, (hi,)*3, flags)

        #选定基准点用红色圆点标出
        cv2.circle(flooded, seed_pt, 2, (0, 0, 255), -1)
        print("send_pt=", seed_pt)

        #显示图像
        cv2.imshow('floodfill', flooded)

    #鼠标响应函数
    def onmouse(event, x, y, flags, param):
        global seed_pt #基准点

        #鼠标左键响应选择漫水填充基准点
        if flags & cv2.EVENT_FLAG_LBUTTON:
            seed_pt = x, y
            update()

    #执行图像漫水填充分割更新操作
    update()
    
    #鼠标更新操作
    cv2.setMouseCallback('floodfill', onmouse)

    #设置进度条
    cv2.createTrackbar('lo', 'floodfill', 20, 255, update)
    cv2.createTrackbar('hi', 'floodfill', 20, 255, update)

    #按键响应操作
    while True:
        ch = 0xFF & cv2.waitKey()
        #退出
        if ch == 27:
            break
        #选定时flags的高位比特位0
        #邻域的选定为当前像素与相邻像素的差, 联通区域会很大
        if ch == ord('f'):
            fixed_range = not fixed_range 
            print('using %s range' % ('floating', 'fixed')[fixed_range])
            update()
        #选择4方向或则8方向种子扩散
        if ch == ord('c'):
            connectivity = 12-connectivity 
            print('connectivity =', connectivity)
            update()
    cv2.destroyAllWindows()

当鼠标选定的种子点为(242,96),观察点像素邻域负差最大值“lo”为138,观察点像素邻域正差最大值“hi”为147时,图像漫水填充效果如图2所示,它将天空和中心水面填充成黄色。

当鼠标选定的种子点为(328, 202),观察点像素邻域负差最大值“lo”为142,观察点像素邻域正差最大值“hi”为45时,图像漫水填充效果如图3所示,它将图像两旁的森林和水面填充成蓝紫色。


四.总结

写到这里,图像分割知识点就介绍完毕,包括基于阈值的图像分割方法、基于边缘检测的图像分割方法、基于纹理背景的图像分割方法和基于特定理论的图像分割方法。其中,基于特定理论的分割方法又分别讲解了基于K-Means聚类、均值漂移、分水岭算法的图像分割方法。最后通过漫水填充分割案例加深了读者的印象。希望读者能结合本章知识点,围绕自己的研究领域或工程项目进行深入的学习,实现所需的图像处理。

感谢在求学路上的同行者,不负遇见,勿忘初心。图像处理系列主要包括三部分,分别是:

(By:Eastmount 2023-01-21 夜于贵阳 http://blog.csdn.net/eastmount/ )


参考文献:

  • [1]阮秋琦. 数字图像处理学(第3版)[M]. 北京:电子工业出版社,2008.
  • [2]毛星云,冷雪飞. OpenCV3编程入门[M]. 北京:电子工业出版社,2015.
  • [3]Robert Laganiere. OpenCV2计算机视觉编程手册[M]. 北京:科学出版社,2013.
  • [4]xxxss. OpenCV-Python教程:31.分水岭算法对图像进行分割[EB/OL]. (2). https://www.jianshu.com/p/de81d6029235.
  • [5]Python_Zhou. python+opencv 漫水填充(floodFill)实例详解[EB/OL]. (2018-11-13). https://blog.csdn.net/weixin_42508025/article/details/84029054.
  • [6]浅墨_毛星云. [OpenCV入门教程之十五] 水漫金山:OpenCV漫水填充算法(Floodfill)[EB/OL]. (2014-06-03). https://blog.csdn.net/poem_qianmo/article/details/28261997.
  • [7]kalp_yp. 漫水填充实例详解[EB/OL]. (2018-06-21). https://blog.csdn.net/u013539952/article/details/80702849.
  • [8]太子洗马. OpenCv漫水填充floodFill详解[EB/OL]. (2018-07-09). https://blog.csdn.net/weixin_42296411/article/details/80966724.
  • [9]菜鸟知识搬运工. OpenCV3学习(7.1)——图像分割之一(漫水填充FloodFill)[EB/Ol]. (2019-02-03). https://blog.csdn.net/qq_30815237/article/details/86759450.
  • [10]gjy095. Opencv Python版学习笔记(二)漫水填充[EB/OL]. (2013-06-28). https://blog.csdn.net/gjy095/article/details/9198845.
  • [11]it2153534. [Python]OpenCV学习总结及文字检测[EB/OL]. (2018-01-28). https://blog.csdn.net/it2153534/article/details/79185397.

以上是关于[Python从零到壹] 六十三.图像识别及经典案例篇之图像漫水填充分割应用的主要内容,如果未能解决你的问题,请参考以下文章

[Python从零到壹] 六十五.图像识别及经典案例篇之图像霍夫变换详解

[Python从零到壹] 六十.图像识别及经典案例篇之基于阈值及边缘检测的图像分割

[Python从零到壹] 六十一.图像识别及经典案例篇之基于纹理背景和聚类算法的图像分割

[Python从零到壹] 六十一.图像识别及经典案例篇之基于纹理背景和聚类算法的图像分割

[Python从零到壹] 六十二.图像识别及经典案例篇之基于均值漂移算法和分水岭算法的图像分割

[Python从零到壹] 六十二.图像识别及经典案例篇之基于均值漂移算法和分水岭算法的图像分割