OpenCV漫水填充
引言
漫水填充是用一定颜色填充联通区域,通过设置可连通像素的上下限以及连通方式来达到不同的填充效果;漫水填充经常被用来标记或分离图像的一部分以便对其进行进一步处理或分析,也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是某个连续的区域。
基本思想
种子填充算法是从多边形区域内部的一点开始,由此出发找到区域内的所有像素。它采用的边界定义是区域边界上所有像素具有某个特定的颜色值,区域内部所有像素均不取这一特定颜色,而边界外的像素则可具有与边界相同的颜色值。具体算法步骤如下所示:
- 标记种子(x,y)的像素点 ;
- 检测该点的颜色,若他与边界色和填充色均不同,就用填充色填 充该点,否则不填充 ;
- 检测相邻位置,继续 2。这个过程延续到已经检测区域边界范围内的所有像素为止。
当然在搜索的时候有两种检测相邻像素:四向连通和八向连通。四向连通即从区域上一点出发,通过四个方向上、下、左、右来检索。而八向连通加上了左上、左下、右上、右下四个方向。这种算法的有点是算法简单,易于实现,也可以填充带有内孔的平面区域。但是此算法需要更大的存储空间以实现栈结构,同一个像素多次入栈和出栈,效率低,运算量大。而扫描线种子填充算法属于种子填充算法,它是以扫描线上的区段为单位操作。所谓区段,就是一条扫描线上相连着的若干内部象素的集合。扫描线种子填充算法思想:首先填充当前扫描线上的位于给定区域的一区段,然后确定于这一区段相邻的上下两条线上位于该区域内是否存在需要填充的新区段,如果存在,则依次把他们保存起来,反复这个过程,直到所保存的各区段都填充完毕。借助于堆栈,上述算法实现步骤如下:
初始化堆栈。
种子压入堆栈。
while(堆栈非空){从堆栈弹出种子象素。如果种子象素尚未填充,则:求出种子区段:xleft、xright;填充整个区段。检查相邻的上扫描线的xleft <= x <= xright区间内,是否存在需要填充的新区段,如果存在的话,则把每个新区段在xleft <= x <= xright范围内的最右边的象素,作为新的种子象素依次压入堆栈。检查相邻的下扫描线的xleft <= x <= xright区间内,是否存在需要填充的新区段,如果存在的话,则把每个新区段在xleft <= x <= xright范围内的最右边的象素,作为新的种子象素依次压入堆栈。
}
更进一步算法,在原算法中, 种子虽然代表一个区段, 但种子实质上仍是一个象素, 我们必须在种子出栈的时候计算种子区段, 而这里有很大一部分计算是重复的. 而且, 原算法的扫描过程如果不用mask的话, 每次都会重复扫描父种子区段所在的扫描线, 而用mask的话又会额外增加开销。所以, 对原算法的一个改进就是让种子携带更多的信息, 让种子不再是一个象素, 而是一个结构体. 该结构体包含以下信息: 种子区段的y坐标值, 区段的x开始与结束坐标, 父种子区段的方向(上或者下), 父种子区段的x开始与结束坐标.
struct seed{int y,int xleft,int xright,int parent_xleft,int parent_xright,bool is_parent_up
};
这样算法的具体实现变动如下
初始化堆栈.
将种子象素扩充成为种子区段(y, xleft, xright, xright+1, xrihgt, true), 填充种子区段, 并压入堆栈. (这里有一个构造父种子区段的技巧)
while(堆栈非空){从堆栈弹出种子区段。检查父种子区段所在的扫描线的xleft <= x <= parent_xleft和parent_xright <= x <= xright两个区间, 如果存在需要填充的新区段, 则将其填充并压入堆栈.检查非父种子区段所在的扫描线的xleft <= x <= xright区间, 如果存在需要填充的新区段, 则将其填充并压入堆栈.
}
另外, opencv里的种子填充算法跟以上方法大致相同, 不同的地方是opencv用了队列不是堆栈, 而且是由固定长度的数组来实现的循环队列, 其固定长度为 max(img_width, img_height)*2. 并且push与pop均使用宏来实现而没有使用函数. 用固定长度的数组来实现队列(或堆栈)意义是显然的, 能大大减少构造结构, 复制结构等操作, 可以大大提高效率.
参考代码
CVAPI(void) cvFloodFill( CvArr* image, CvPoint seed_point,CvScalar new_val, CvScalar lo_diff CV_DEFAULT(cvScalarAll(0)),CvScalar up_diff CV_DEFAULT(cvScalarAll(0)),CvConnectedComp* comp CV_DEFAULT(NULL),int flags CV_DEFAULT(4),CvArr* mask CV_DEFAULT(NULL));
其中函数参数:
- image为待处理图像
- seed_point为种子坐标
- new_val为填充值
- lo_diff为像素值的下限差值
- up_diff为像素值的上限差值
- 从函数形式可看出,该函数可处理多通道图像。
- mask为掩码, 注意: 设输入图像大小为width * height, 则掩码的大小必须为 (width+2) * (height+2) , mask可为输出,也可作为输入 ,由flags决定
- flags参数 : 0~7位为0x04或者0x08 即 4连通或者8 连通
- 8~15位为填充mask的值大小 , 若为0 , 则默认用1填充
- 16~23位为 : CV_FLOODFILL_FIXED_RANGE =(1 << 16), CV_FLOODFILL_MASK_ONLY =(1 << 17)
- flags参数通过位与运算处理
当为CV_FLOODFILL_FIXED_RANGE 时,待处理的像素点与种子点作比较,如果满足(s - lodiff , s + updiff)之间(s为种子点像素值),则填充此像素 . 若无此位设置,则将待处理点与已填充的相邻点作此比较
CV_FLOODFILL_MASK_ONLY 此位设置填充的对像, 若设置此位,则mask不能为空,此时,函数不填充原始图像img,而是填充掩码图像. 若无此位设置,则在填充原始图像的时候,也用flags的8~15位标记对应位置的mask.
OpenCV版漫水填充实现
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <iostream> using namespace std; int main() { cvNamedWindow("source"); cvNamedWindow("dest1"); cvNamedWindow("dest2"); cvNamedWindow("mask0"); cvNamedWindow("mask1"); IplImage * src = cvLoadImage("test.jpg"); IplImage * img=cvCreateImage(cvGetSize(src), 8, 3); IplImage *img2=cvCreateImage(cvGetSize(src),8,3); IplImage *pMask = cvCreateImage(cvSize(src->width +2 ,src->height +2),8,1); cvSetZero(pMask); cvCopyImage(src, img); cvCopyImage(src,img2); cvFloodFill( img, cvPoint(300,310),CV_RGB(255,0,0),cvScalar(20,30,40,0), cvScalar(5,30,40,0), NULL, CV_FLOODFILL_FIXED_RANGE | (0x9f<<8),pMask); cvShowImage("mask0",pMask); cvSetZero(pMask); cvFloodFill(img2,cvPoint(80,80),CV_RGB(255,0,0),cvScalarAll(29),cvScalarAll(10), NULL, CV_FLOODFILL_MASK_ONLY | (47<<8) ,pMask); cvShowImage("source",src); cvShowImage("dest1", img); cvShowImage("dest2",img2); cvShowImage("mask1",pMask);cvWaitKey(0); cvReleaseImage(&src); cvReleaseImage(&img); cvReleaseImage(&img2);cvReleaseImage(&pMask); cvDestroyAllWindows(); return 0;
}
Python版漫水填充实现
#decoding:utf-8
import numpy as np
import cv2
import randomhelp_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__': import sys try: fn = sys.argv[1] except: fn = '../test.jpg' print help_message img = cv2.imread(fn, True) h, w = img.shape[:2] #得到图像的高和宽 mask = np.zeros((h+2, w+2), np.uint8)#掩码单通道8比特,长和宽都比输入图像多两#个像素点,满水填充不会超出掩码的非零边缘 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')#观察点像素邻域正差最大值 flags = connectivity #低位比特包含连通值, 4 (缺省) 或 8 if fixed_range: flags |= cv2.FLOODFILL_FIXED_RANGE #考虑当前象素与种子象素之间的差(高比特也可以为0) #以白色进行漫水填充 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)#选定基准点用红色圆点标出 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 if ch == ord('f'): fixed_range = not fixed_range #选定时flags的高位比特位0,也就是邻域的#选定为当前像素与相邻像素的的差,这样的效果就是联通区域会很大 print 'using %s range' % ('floating', 'fixed')[fixed_range] update() if ch == ord('c'): connectivity = 12-connectivity #选择4方向或则8方向种子扩散 print 'connectivity =', connectivity update() cv2.destroyAllWindows()
关于Image Engineering & Computer Vision的更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.
OpenCV漫水填充相关推荐
- opencv漫水填充算法
1.定义 使用特定的颜色填充连通区域,通过设置可连通像素的上下限以及连通方式达到不同的填充效果. 2.opencv漫水填充函数 int floodFill(InputOutputArray image ...
- 水漫金山:OpenCV漫水填充算法(Floodfill)
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云(浅墨) ...
- 【OpenCV入门教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云(浅墨) ...
- opencv漫水填充法。
向浅墨学习. 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者: ...
- 【OpenCV新手教程之十五】水漫金山:OpenCV漫水填充算法(Floodfill)
本系列文章由@浅墨_毛星云 出品.转载请注明出处. 文章链接: http://blog.csdn.net/poem_qianmo/article/details/28261997 作者:毛星云(浅墨) ...
- opencv漫水填充算法floodfill
定义 : 漫水填充算法是一种用特定颜色填充连通区域,通过设置像素上下限及连通方式来达到不同的连通效果.漫水填充经常用来标记或分离图像的一部分,以便于对其进行进一步的处理和分析.也可以从输入图像获取掩码 ...
- OpenCV(十二)漫水填充算法
目录 一.基础理论 1.概述 2.API 二.实战 1.黑白图像填充(python) 1.对内部填充 2.对外部填充 总代码 2.彩图填充(C++) 一.基础理论 1.概述 自动选中了和种子点相连的区 ...
- 【OpenCV 4开发详解】漫水填充法
本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...
- opencv 腐蚀 matlab,Opencv3编程入门笔记(4)腐蚀、膨胀、开闭运算、漫水填充、金字塔、阈值化、霍夫变换...
19 腐蚀erode.膨胀dilate 腐蚀和膨胀是针对图像中的白色部分(高亮部分)而言的,不是黑色的.除了输入输出图像外,还需传入模板算子element,opencv中有三种可以选择:矩形 ...
最新文章
- Mssql 之 定期备份数据库
- [YTU]_2476(E3 继承了,成员函数却不可访问)
- arr.sort的排序原理
- 软件工程个人作业03—找水王
- matlab中solve和resolve,resolve和solve的区别
- flare3d_clone
- 加加减减的奥秘——从数学到魔术的思考(二)
- [流体输配管网] 使用 Matlab 绘制莫迪图
- MySQL 5.6和MySQL 5.7的区别
- zlib 加密 java_JAVA自带Zlib+base64位压缩加密和解压解密
- 涉嫌抄袭!致歉,抖音Semi Design承认参考阿里Ant Design
- 魔兽争霸3地图(WarIII Maps):成神之路
- svn删除所有unversioned文件
- Android上图片文字识别
- mysql触发器安全吗_猎八哥浅谈MYSQL触发器
- 编写MTK6737平台的GPIO驱动例程(二)
- UDIMM、RDIMM、SODIMM区别
- NYOJ——239月老的难题(二分图最大匹配)
- brctl 配置网桥
- 证明:串联谐振回路中电容与电感两端电压的模值相等,且等于外加电压的Q倍
热门文章
- MS5611大气压强传感器驱动代码(基于GD32F103)
- 暴雷(CVE-2012-1889)漏洞分析报告
- winPE下的PECMD命令详解 -----PECMD.INI 文件配置(2)
- 脸打肿都不要忘了呀,Java 重写时应当遵守的 11 条规则!!
- 供销合作社改革聚合力 国稻种芯-兰州榆中:构建现代流通体系
- System.Data.SqlClient.SqlException: 用户 'IIS APPPOOL\y3' 登录失败
- 20个最流行的3D打印机切片软件
- JVM : 12 面试题:JVM中有哪些垃圾回收算法,每个算法各自的优劣?
- 慕容小匹夫 Unity3D移动平台动态读取外部文件全解析
- 谷歌浏览器ajax脚本出错,你好,用谷歌浏览器,发生脚本错误怎么处理。