本文作者:小嗷
微信公众号:aoxiaoji
吹比QQ群:736854977
简书链接:https://www.jianshu.com/u/45da1fbce7d0
漫水填充(Flood fill),也称为种子填充(seed fill),是一种确定多维数组中连接到给定节点的区域的算法。(灰度图是二维,彩色图是三维)
灰度图的二维:一般来说,一维是高(行),一维是宽(列)。
即:char a[3][4] = 246;
a为3*4(3行4列)的像素值为:246
彩色图:多了一维是图像深度
A?B:C 这个运算是判断A的真假,若是真就执行B如是假就执行C
开始节点、目标颜色和替换颜色。
该算法查找数组中的所有节点,这些节点通过目标颜色的路径连接到起始节点,并将它们更改为替换颜色。有许多方法可以构造漫水填充算法,但它们都使用队列或堆栈数据结构,显式或隐式地。
根据我们是否考虑连接在角落的节点,我们有两种变化:8路和4路。(即:核是3*3的正方形,还是自定义十字形),取其中一个4路在演示。
一个隐式堆栈的(递归)漫水填充实现(对于一个二维数组)如下所示:
效果如下:(十字形)
即:东、南、西、北、
8路的效果如下:(核是3*3的正方形)
即:东、南、西、北、东南、西南、西北、东北、 8个方向
用给定的颜色填充连接的组件。
在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。
堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。
要点:堆,队列优先,先进先出(FIFO—first in first out) [1] 。栈,先进后出(FILO—First-In/Last-Out)。
堆:
堆栈空间分配
栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
堆栈缓存方式
栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。
堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
cv::floodFill ( InputOutputArray image,
InputOutputArray mask,
Point seedPoint,
Scalar newVal,
Rect * rect = 0,
Scalar loDiff = Scalar(),
Scalar upDiff = Scalar(),
int flags = 4
)
参数详解:
image:InputOutputArray类型的image, 输入/输出1通道或3通道,8位或浮点图像,具体参数由之后的参数具体指明。
mask:InputOutputArray类型的mask,这是第二个版本的floodFill独享的参数,表示操作掩模,。它应该为单通道、8位、长和宽上都比输入图像 image 大两个像素点的图像。第二个版本的floodFill需要使用以及更新掩膜,所以这个mask参数我们一定要将其准备好并填在此处。需要注意的是,漫水填充不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大,所以 mask 中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。
低八位(第0~7位)用于控制算法的连通性,可取4 (4为缺省值) 或者 8。如果设为4,表示填充算法只考虑当前像素水平方向和垂直方向的相邻点;如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。
(请看上面第二部分的东南西北的解释,哈哈哈)
高八位部分(16~23位)可以为0 或者如下两种选项标识符的组合:
中间八位部分,上面关于高八位FLOODFILL_MASK_ONLY标识符中已经说的很明显,需要输入符合要求的掩码。Floodfill的flags参数的中间八位的值就是用于指定填充掩码图像的值的。但如果flags中间八位的值为0,则掩码会用1来填充
而所有flags可以用or操作符连接起来,即“|”。例如,如果想用8邻域填充,并填充固定像素值范围,填充掩码而不是填充源图像,以及设填充值为38,那么输入的参数是这样:
flags=8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (38<<8)
功能cv::floodFill从种子点开始,以指定的颜色填充连接的组件。连接性是由相邻像素的颜色/亮度接近决定的。在(x,y)处的像素,如果:
src(x′,y′)的值是一个已知的像素邻居属于组件。也就是说,要添加到所连接的组件中,像素的颜色/亮度应该足够接近:
在浮动范围内,它的一个邻居的颜色/亮度已经属于连接组件。
在固定范围内,种子点的颜色/亮度。
使用这些函数可以将连接的组件标记为指定的颜色,或者构建一个掩膜,然后提取轮廓,或者将区域复制到另一个图像,等等。
(比如起点的像素点是200,loDiff是20,upDiff是50)
像素点必须要要在以下范围内,才可以上色
即:180《像素点《250
(比如临近的像素点是200【已知的像素邻居】,loDiff是20,upDiff是50)
像素点必须要要在以下范围内,才可以上色
即:已知的像素邻居的值 - 20 《像素点《 已知的像素邻居的值 - 50
代码如下:
//videoio:视频流的输入和输入
//imgcodecs:用于图像文件的载入(imread)和输出(imwrite)
//imgproc:图像处理模块
//highgui:高层图形用户界面(GUI),包括媒体输入输出、视频捕捉、图像交互界面接口、图像和视频的编码解码等
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis program demonstrated the floodFill() function\n"
"Call:\n"
"./ffilldemo [image_name -- Default: ../data/fruits.jpg]\n" << endl;
cout << "Hot keys: \n"
"\tESC - quit the program\n"
"\tc - switch color/grayscale mode\n"
"\tm - switch mask mode\n"
"\tr - restore the original image\n"
"\ts - use null-range floodfill\n"
"\tf - use gradient floodfill with fixed(absolute) range\n"
"\tg - use gradient floodfill with floating(relative) range\n"
"\t4 - use 4-connectivity mode\n"
"\t8 - use 8-connectivity mode\n" << endl;
}
Mat image0, image, gray, mask;
int ffillMode = 1;
int loDiff = 20, upDiff = 20;
int connectivity = 4;
int isColor = true;
bool useMask = false;
int newMaskVal = 255;
static void onMouse( int event, int x, int y, int, void* )
{
if( event != EVENT_LBUTTONDOWN )
return;
Point seed = Point(x,y);
int lo = ffillMode == 0 ? 0 : loDiff;
int up = ffillMode == 0 ? 0 : upDiff;
int flags = connectivity + (newMaskVal << 8) +
(ffillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);
int b = (unsigned)theRNG() & 255;
int g = (unsigned)theRNG() & 255;
int r = (unsigned)theRNG() & 255;
Rect ccomp;
Scalar newVal = isColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114);
Mat dst = isColor ? image : gray;
int area;
if( useMask )
{
threshold(mask, mask, 1, 128, THRESH_BINARY);
area = floodFill(dst, mask, seed, newVal, &ccomp, Scalar(lo, lo, lo),
Scalar(up, up, up), flags);
imshow( "mask", mask );
}
else
{
area = floodFill(dst, seed, newVal, &ccomp, Scalar(lo, lo, lo),
Scalar(up, up, up), flags);
}
imshow("image", dst);
cout << area << " pixels were repainted\n";
}
int main( int argc, char** argv )
{
cv::CommandLineParser parser (argc, argv,
"{help h | | show help message}{@image|../data/fruits.jpg| input image}"
);
if (parser.has("help"))
{
parser.printMessage();
return 0;
}
string filename = parser.get("@image");
image0 = imread(filename, 1);
if( image0.empty() )
{
cout << "Image empty\n";
parser.printMessage();
return 0;
}
help();
image0.copyTo(image);
cvtColor(image0, gray, COLOR_BGR2GRAY);
mask.create(image0.rows+2, image0.cols+2, CV_8UC1);
namedWindow( "image", 0 );
createTrackbar( "lo_diff", "image", &loDiff, 255, 0 );
createTrackbar( "up_diff", "image", &upDiff, 255, 0 );
setMouseCallback( "image", onMouse, 0 );
for(;;)
{
imshow("image", isColor ? image : gray);
char c = (char)waitKey(0);
if( c == 27 )
{
cout << "Exiting ...\n";
break;
}
switch( c )
{
case 'c':
if( isColor )
{
cout << "Grayscale mode is set\n";
cvtColor(image0, gray, COLOR_BGR2GRAY);
mask = Scalar::all(0);
isColor = false;
}
else
{
cout << "Color mode is set\n";
image0.copyTo(image);
mask = Scalar::all(0);
isColor = true;
}
break;
case 'm':
if( useMask )
{
destroyWindow( "mask" );
useMask = false;
}
else
{
namedWindow( "mask", 0 );
mask = Scalar::all(0);
imshow("mask", mask);
useMask = true;
}
break;
case 'r':
cout << "Original image is restored\n";
image0.copyTo(image);
cvtColor(image, gray, COLOR_BGR2GRAY);
mask = Scalar::all(0);
break;
case 's':
cout << "Simple floodfill mode is set\n";
ffillMode = 0;
break;
case 'f':
cout << "Fixed Range floodfill mode is set\n";
ffillMode = 1;
break;
case 'g':
cout << "Gradient (floating range) floodfill mode is set\n";
ffillMode = 2;
break;
case '4':
cout << "4-connectivity mode is set\n";
connectivity = 4;
break;
case '8':
cout << "8-connectivity mode is set\n";
connectivity = 8;
break;
}
}
return 0;
}
原图:
效果图:
分享可以无数次,转载成自己文章QQ邮箱通知一下,未经授权请勿转载。
推荐文章:
21.失真/低高通/振铃效应/旁瓣泄漏效应/频域滤波/图像深度/频带/线性滤波源码分析(数学篇) - OpenCV从零开始到图像
很多知识点都是串起来,怎么说呢?可以理解之前二十几篇文章的知识布局,才理解这篇或者做到这篇文章。
实在有点不好意思,小嗷因为个人原因(工资低老想着跳槽,哈哈哈)中断了一期。一周5篇实在吃不消。
还有1开始几篇文章布局什么真难看,小嗷会重新群发(微信规矩:不群发,上不了链接地址)
链接:
https://pan.baidu.com/s/1RESLgnXlwZ74E-Eh1yRaXQ
密码:nq8r