demo运行效果
源码:
#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(char** argv)
{
cout << "\nThis program demonstrated the floodFill() function\n"
"Call:\n"
<< argv[0]
<< " [image_name -- Default: 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);// ffillMode ???? newMaskVal ???? ???? connectivity ???? ????
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|fruits.jpg| input image}"
);
if (parser.has("help"))
{
parser.printMessage();
return 0;
}
string filename = parser.get("@image");
image0 = imread(samples::findFile(filename), 1);//源图像
if (image0.empty())
{
cout << "Image empty\n";
parser.printMessage();
return 0;
}
help(argv);
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);//重置image
cvtColor(image, gray, COLOR_BGR2GRAY);//重置gray
mask = Scalar::all(0);
break;
case 's'://简单漫水填充
cout << "Simple floodfill mode is set\n";//1个像素的填充
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'://连通性4
cout << "4-connectivity mode is set\n";
connectivity = 4;
break;
case '8'://连通性8
cout << "8-connectivity mode is set\n";
connectivity = 8;
break;
}
}
return 0;
}
笔记-漫水填充函数:floodFill
/** @brief 用给定的颜色填充连接的组件。
函数 cv::floodFill 使用指定的从种子点开始填充连接的组件
颜色。连通性由相邻像素的颜色/亮度接近度决定。如果满足以下条件,则 (x,y) 处的像素被认为属于重绘域:
- 在灰度图像和浮动范围的情况下
\f[\texttt{src} (x',y')- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} (x',y')+ \texttt {upDiff}\f]
- 在灰度图像和固定范围的情况下
\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)+ \texttt{upDiff}\f]
- 在彩色图像和浮动范围的情况下
\f[\texttt{src} (x',y')_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt{src} (x',y') _r+ \texttt{upDiff} _r,\f]
\f[\texttt{src} (x',y')_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt{src} (x',y') _g+ \texttt{upDiff} _g\f]
和
\f[\texttt{src} (x',y')_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt{src} (x',y') _b+ \texttt{upDiff} _b\f]
- 在彩色图像和固定范围的情况下
\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r- \texttt{loDiff} _r \leq \texttt{src} (x,y)_r \leq \texttt {src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_r+ \texttt{upDiff} _r,\f]
\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g- \texttt{loDiff} _g \leq \texttt{src} (x,y)_g \leq \texttt {src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_g+ \texttt{upDiff} _g\f]
和
\f[\texttt{src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b- \texttt{loDiff} _b \leq \texttt{src} (x,y)_b \leq \texttt {src} ( \texttt{seedPoint} .x, \texttt{seedPoint} .y)_b+ \texttt{upDiff} _b\f]
其中 \f$src(x',y')\f$ 是已知属于该组件的像素邻居之一的值。也就是说,要添加到连接组件,像素的颜色/亮度应该足够接近:
- 在浮动范围的情况下,已经属于连接组件的相邻组件之一的颜色/亮度。
- 在固定范围的情况下,种子点的颜色/亮度。
使用这些函数可以用指定的颜色就地标记连接的组件,或者构建一个蒙版然后提取轮廓,或者将该区域复制到另一个图像,等等。
@param image 输入/输出 1 或 3 通道、8 位或浮点图像。它由函数修改,除非在函数的第二个变体中设置了 #FLOODFILL_MASK_ONLY 标志。请参阅下面的详细信息。
@param mask 操作掩码应该是单通道 8 位图像,比图像宽 2 像素,高 2 像素。由于这既是输入参数又是输出参数,因此您必须负责对其进行初始化。填充不能跨越输入掩码中的非零像素。例如,边缘检测器输出可用作掩码以停止在边缘填充。在输出时,对应于图像中填充像素的掩码中的像素设置为 1 或设置为 flags 中指定的值,如下所述。此外,该函数用 1 填充掩码的边框以简化内部处理。因此,可以在对函数的多次调用中使用相同的掩码,以确保填充区域不重叠。
@param seedPoint 起点。
@param newVal 重绘域像素的新值。
@param loDiff 当前观察到的像素与其属于该组件的相邻像素之一或添加到该组件的种子像素之间的最大较低亮度/色差。
@param upDiff 当前观察到的像素与其属于该组件的相邻像素之一或添加到该组件的种子像素之间的最大亮度/色差。
@param rect 由函数设置为重绘域的最小边界矩形的可选输出参数。
@param flags 操作标志。前 8 位包含连接值。默认值 4 表示仅考虑四个最近邻像素(共享边缘的像素)。连接值为 8 意味着将考虑八个最近邻像素(共享一个角的像素)。接下来的 8 位 (8-16) 包含一个介于 1 和 255 之间的值,用于填充掩码(默认值为 1)。例如,4 | ( 255 << 8 ) 将考虑 4 个最近的邻居并使用 255 的值填充掩码。以下附加选项占用更高的位,因此可以使用逐位或 (|) 进一步与连接性和掩码填充值组合,请参阅#FloodFillFlags。
@note 由于 mask 大于填充的图像,因此 image 中的一个像素 (x, y) 对应于 mask 中的像素 (x+1, y+1) 。
@sa findContours*/
CV_EXPORTS_W int floodFill( InputOutputArray image, InputOutputArray mask,
Point seedPoint, Scalar newVal, CV_OUT Rect* rect=0,
Scalar loDiff = Scalar(), Scalar upDiff = Scalar(),
int flags = 4 );