当前位置: 首页 > news >正文

OpenCV-漫水填充cv::floodFill

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 

函数原型

int floodFill( InputOutputArray image,
               Point seedPoint, Scalar newVal, CV_OUT Rect* rect = 0,
               Scalar loDiff = Scalar(), Scalar upDiff = Scalar(),
               int flags = 4 );
int floodFill( InputOutputArray image, InputOutputArray mask,
               Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0,
               Scalar loDiff = Scalar(), Scalar upDiff = Scalar(),
               int flags = 4 );

参数说明

       floodFill函数有两个原型,第二个相比第一个多了一个掩膜区,以第二个为例介绍各个参数。

  1. InputOutputArray类型的image,输入输出图像。
  2. InputOutputArray类型的mask,掩膜区,掩膜区非零区域不被填充,且掩膜区的宽高要比输入图像各多两个像素,输入图像中(x,y)位置对应掩膜区(x+1,y+1)位置,掩膜区的具体使用将在下方进行举例演示。
  3. Point类型的seedPoint,漫水填充的起点。
  4. Scalar类型的newVal,像素点被填充后呈现的颜色。
  5. Rect*类型的rect,漫水填充后重绘区域的最小边界矩形区域。
  6. Scalar类型的loDiff,当前观测像素与种子像素颜色负差的最大值。通俗地讲,就是观测像素各个通道的值都要比种子像素小,且小的数值要低于loDiff,满足该条件才会被填充。具体使用将在下方进行举例演示。
  7. Scalar类型的upDiff,当前观测像素与种子像素颜色正差的最大值。通俗地讲,就是观测像素各个通道的值都要比种子像素大,且大的数值要低于upDiff,满足该条件才会被填充。具体使用将在下方进行举例演示。
  8. int类型的flags,操作标识符,是32位二进制数。低八位表示算法连通性,一般取4或者8,4为上下左右,8加上对角四点;高八位可选两种标识符,分别为FLOODFILL_FIXED_RANGE和FLOODFILL_MASK_ONLY,也可以用or(|)组合它们;中八位是填充掩膜的数值,搭配FLOODFILL_MASK_ONLY使用。具体使用将在下方进行举例演示。

漫水填充思想

       漫水填充法是一种常见的图像处理方法,通过选中和种子点相连相近的区域,将其转换为指定颜色,以达到标记或者分离图像的目的,进而完成对图像的一些分析和处理。类似PS中的魔术棒选择工具,点击某个点,自动框选相近的区域。

       思想并不复杂,但在使用floodFill函数的过程中,有许多细节可以进一步研究和分析,大多数人并未深入了解。为此我写下该篇文章,探讨一下OpenCV自带的漫水填充算法的一些细节。如有补充,欢迎评论留言。

loDiff和upDiff

       先看下方代码,loDiff为Scalar(1, 1, 1),upDiff为Scalar(10, 10, 10),表示当前观测点的像素X与周围已被填充的像素点数值Y,需满足X-Y<10,且Y-X<1,才被填充。

Mat src = imread("test.jpg");
Rect roi;
int flags = 8;
floodFill(src, Point(src.cols / 2, src.rows / 2), Scalar(255, 0, 255), &roi, Scalar(1, 1, 1), Scalar(10, 10, 10), flags);

       如下图所示,观察数据的填充情况,Y为种子点,X为当前观测点,符合要求,所以被填充了。而旁边几个点都不满足,所以都保持原状。

图1

       再将loDiff为Scalar(10, 10, 10),upDiff为Scalar(1, 1, 1),则需满足X-Y<1,且Y-X<10,才被填充。不难发现,8近邻的像素点被填充了好几个,而被填充后的点将作为新的种子点,继续向外探索可被填充的数据。

图2

 

掩膜

       如函数说明要求,掩膜尺寸为原尺寸的宽高再加2,这应该是算法实现过程中卷积的要求,便于计算。在下方代码中,我们定义了掩膜,并将掩膜的(src.rows/2,src.cols/2)的位置设为255,即不进行填充,而该点位对应原图中的(src.rows/2-1,src.cols/2-1)。

Mat src = imread("test.jpg");
Rect roi;
int flags = 8;
Mat mask = Mat::zeros(src.rows + 2,src.cols + 2, CV_8UC1);
mask.at<uchar>(src.rows / 2, src.cols / 2) = 255;
floodFill(src, mask, Point(src.cols / 2, src.rows / 2), Scalar(255, 0, 255), &roi, Scalar(10, 10, 10), Scalar(1, 1, 1), flags);

       除了掩膜,其他参数与图2对应的代码一致,让我们看看加了一个掩膜点后,填充的效果图。

图3

       与图2相比,中心种子点左上角,32 90 102像素点未被填充,除此之外,左上方原本应该被填充的许多像素也未被填充,说明掩膜作用在填充过程中。换句话说,当某个点受掩膜限制未被填充后,再外圈的点也将不以其为种子点进行分析。这也表明,掩膜可以为漫水填充提供方向的引导,即指挥填充的路线

 

操作标识符

       从简单的开始说,低八位表示连通性,只需让flags=4或者8即可。若为4,则低八位的二进制数为00000100;若为8,则低八位的二进制数为00001000。其效果分别如下所示。

图4 原图
图5 4连通
图6 8连通

       这应该还是比较好理解的。

       接下来让flags变为如下形式,表示8或FLOODFILL_FIXED_RANGE。如图7所示,该值等于1<<16,即1左移16位,则是32位的二进制数00000001 00000000 00000000,该值也是65536,2的16次方。那8|FLOODFILL_FIXED_RANGE,就表示00000001 00000000 00001000。这表示漫水填充不仅要用8连通,还要用固定范围的分析。

       默认情况下,填充过程的参考种子是邻近的点,也就是用于判断是否进行填充的那个范围是动态波动的。但在FLOODFILL_FIXED_RANGE标识下,参考种子是固定的,也就是开始标记的那个点,那用于判断是否填充的范围也就变成固定的了,即起始标记点像素值的上下限制范围。

int flags = 8 | FLOODFILL_FIXED_RANGE;
图7 枚举

       如果你理解了上面的描述,那不难理解,在FLOODFILL_FIXED_RANGE标识下,填充效果将大打折扣,毕竟邻近点作为种子,更容易符合要求些。效果如图8所示。

图8 FLOODFILL_FIXED_RANGE效果图

       FLOODFILL_MASK_ONLY同理,二进制数为00000010 00000000 00000000,左移17位,如果和FLOODFILL_FIXED_RANGE取或,则为00000011 00000000 00000000。顾名思义,FLOODFILL_MASK_ONLY表示,不对原图做改动,即重绘操作,而是在掩膜上进行标记,标记的数值由32位二进制数的中间八位决定,如果要用255来标记,则如下写法。

int flags = 8 | FLOODFILL_FIXED_RANGE | FLOODFILL_MASK_ONLY | (255 << 8);

       上述写法让255的值左移8位,相当于把11111111移动到中间8位上,那完整的二进制就是00000011 11111111 00001000,该值也就是flags的值,取整是261896。对应的效果如8所示,原图不受影响,掩膜上做出标记。

图9 掩膜标记

       以上就是本篇文章所讲漫水填充的完整内容了,希望能给你带来一点帮助。

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章:

  • 【精品】SpringSecurity在前后端分离项目中的应用
  • MySQL知识点总结_1
  • 深入理解Python生成器
  • SpringBoot+Vue项目校园商铺系统
  • “不学数学就去当厨子”,兰大校友入选全球竞赛最强10人,决赛最后几小时才想起做题...
  • Python基础_判断语句(if、elif、else)、if 嵌套、逻辑运算符(and、or、not )、随机数的处理
  • 【C语言】小游戏系列——扫雷(内含详细过程)
  • C++系列文章 —— 类和对象篇(上)(从入门到精通合集)
  • 7.5 文件系统
  • java计算机毕业设计伊伊物流公司的管理系统源码+数据库+系统+lw文档+部署
  • PCB设计笔记
  • 图卷积神经网络(GCN)
  • 【数据结构】八大排序
  • D*(Dynamic A*)路径规划算法
  • 16.12 - 基于数据流设计用例
  • [译] React v16.8: 含有Hooks的版本
  • js正则,这点儿就够用了
  • JWT究竟是什么呢?
  • Linux快速复制或删除大量小文件
  • React-生命周期杂记
  • 不上全站https的网站你们就等着被恶心死吧
  • 番外篇1:在Windows环境下安装JDK
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 计算机常识 - 收藏集 - 掘金
  • 记一次删除Git记录中的大文件的过程
  • 利用DataURL技术在网页上显示图片
  • 嵌入式文件系统
  • 如何解决微信端直接跳WAP端
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​secrets --- 生成管理密码的安全随机数​
  • #if #elif #endif
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • (11)MSP430F5529 定时器B
  • (Java数据结构)ArrayList
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (poj1.2.1)1970(筛选法模拟)
  • (附源码)springboot 校园学生兼职系统 毕业设计 742122
  • (完整代码)R语言中利用SVM-RFE机器学习算法筛选关键因子
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • .a文件和.so文件
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .Net CF下精确的计时器
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • @cacheable 是否缓存成功_让我们来学习学习SpringCache分布式缓存,为什么用?
  • @Data注解的作用
  • @GetMapping和@RequestMapping的区别
  • [ Linux ] Linux信号概述 信号的产生
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • [2013][note]通过石墨烯调谐用于开关、传感的动态可重构Fano超——
  • [Android View] 可绘制形状 (Shape Xml)
  • [datastore@cyberfear.com].Elbie、[thekeyishere@cock.li].Elbie勒索病毒数据怎么处理|数据解密恢复