您好,
会员登录 快速注册
退出 ( 条未读消息)
关于本站 意见反馈 首页

公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!
全部文章分类
  • 人工智能 >

  • 编程语言 >

  • WPF系列 >

  • ASP.NET系列 >

  • Linux >

  • 数据库 >

  • 嵌入式 >

  • WEB技术 >

  • PLC系列 >

  • 微服务与框架 >

  • 小宅DIY >

  • 学习资料 >

OpenCv基础 ANN车牌识别 yolov5车牌识别 指针式仪表识别 ROS系列 YOLO Halcon Detectron2 昇腾AI ChatGPT在线体验 英伟达JETSON ChatGLM ChatTTS FunASR 地平线 ByteTrack 魔搭社区 LangChain
C C# C++ Python Java Go
WPF
ASP.NET小功能 GPS定位系统-MVC GPS定位系统-VUE ASP.NET WebRTC
Linux Linux内核 Shell MakeFile
MySql SqlServer Oracle
STM8 STM32 51单片机
VUE入门 HTML JavaScript CSS layui镜像网站 ElementUi中文官网 element-plus 图标
三菱 欧姆龙 西门子 施耐德 松下 台达
IOTSharp IOTGateway ABP FRAMEWORK Docker
亚克力音响 编程仙途:智驭万法
面试题与技巧 Python入门技能树 微软C#教程
首页 编程之美 工具下载 全国就业 流量地图 文心一言
OpenCv基础
内容介绍与资料分享 C# OpenCv环境搭建 C++ OpenCv环境搭建 Python OpenCv环境搭建 Java OpenCv环境搭建 OpenCv组件结构解析 OpenCv命名规范 OpenCv基本专业术语与方法 OpenCV 常用函数与构造体详细说明 创建画布 打开一张图片 利用imwrite生成透明png图像 图像打开、混合显示和输出 图像腐蚀 blur图像模糊(均值滤波) sobel边缘检测 canny边缘检测 Scharr滤波器 程序性能检测及优化 视频播放 摄像头录像与播放 双摄像头操作与图像相似度检测 颜色空间转换与物体追踪 彩色目标追踪 光流法运动目标检测 OpenCV中的稠密光流 背景减除 点追踪 人脸识别 支持向量机之SVM引导 支持向量机之处理线性不可分数据 ROI矩形截取 鼠标绘制矩形 用OpenCV进行基本绘图 绘图函数(python中文显示) 把鼠标当画笔 用滑动条做调色板 图像的基础操作 图像上的算术运算 多通道图像混合 图像的亮度、对比度调整 XML和YAML文件的写入 XML和YAML文件的读取 卷积操作 三种线性滤波 两种非线性滤波 7种图像处理形态学(1) 7种图像处理形态学(2) 漫水填充 图像缩放与图像金字塔 二值化基本阈值操作 图像阈值 Laplacian图像变换(拉普拉斯算子) 霍夫变换HoughLines边缘检测与线性矢量 霍夫变换HoughLinesP边缘检测与线性矢量 霍夫变换HoughCircles边缘检测与线性矢量 LSD快速直线检测 几何变换 remap重映射 remap实现多种重映射 仿射变换综合示例 直方图均衡化 CLAHE有限对比适应性直方图均衡化 draw最大的轮廓 轮廓的性质 点到多边形的最短距离 形状匹配 椭圆拟合与直线拟合 基础轮廓查找 查找并绘制轮廓综合示例 凸缺陷/凸包检测 凸包检测 创建包围轮廓的矩形边界 创建包围轮廓的圆形边 创建包围轮廓的矩形和圆形边界框 查找和绘制图片轮廓矩 分水岭算法 图像修补 H-S二维直方图的绘制/2D直方图 一维直方图的绘制 RGB三色直方图的绘制 直方图对比 使用掩膜绘制直方图 直方图反向投影 模板匹配 多对象模板匹配 cornerHarris角点检测 cornerHarris角点检测综合示例 Shi-Tomasi角点检测 亚像素级角点检测 角点检测的FAST算法(FAST特征检测器) 颜色识别 warpPerspective透视变换 SURF/SIFT特征点检测 SURF/SIFT特征描述 使用FLANN进行特征点匹配 FLANN结合SURF/SIFT进行关键点的描述和匹配 寻找已知物体(一) 寻找已知物体(二) 目标查找与跟踪 - Meanshift与CamShift BRIEF描述符 ORB ORB算法描述与匹配 LUT 图像灰度调整 离散傅里叶变换 双目摄像头与图像拼接 环境亮度检测 stitching 全景拼接 Maze-Solver迷宫解密 使用Haar分类器之面部检测 使用Haar分类器之行人检测 OpenCv Haar/LBP/HOG分类器-人脸识别 斑点检测 使用GrabCut算法进行交互式前景提取 对极几何 摄像机标定 姿势估计 立体图像中的深度地图 OpenCv中的KNN KNN手写数字识别 ​SVM手写数字识别(使用SVM进行手写数据OCR) 英文字母的OCR 预测手写数字(预测不准) K值聚类(一) K值聚类(二) 计算摄影学-图像去噪 高动态范围成像(HDRI或HDR) MSER区域检测 二维码、条形码识别 删除图像中的水印 OpenCv深度学习dnn Kinect-深度相机 OpenCv常用数学算法 360度旋转直线绘制 向量延长线上的像素扫描 Tools工具包-窗体分配
7种图像处理形态学(2)
图像缩放与图像金字塔
激萌の小宅 小宅博客网 OpenCv基础

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 0

    累计评价

漫水填充

视频讲解如下:



当前系列所有demo下载地址:

https://github.com/GaoRenBao/OpenCv4-Demo

不同编程语言对应的OpenCv版本以及开发环境信息如下: 

语言

OpenCv版本

IDE

C#

OpenCvSharp4.4.8.0.20230708

Visual Studio 2022

C++

OpenCv-4.5.5-vc14_vc15

Visual Studio 2022

Python

OpenCv-Python (4.6.0.66)

PyCharm Community Edition 2022.1.3


本章节给大家讲讲如何使用漫水填充功能FloodFill。当前代码主体还是使用毛星云的demo,C#和Python都是在C++版本的基础上转换过来的,三个版本的效果基本一致。

准备的原图如下:

2.jpg


运行效果如下:


C#版本代码

C#版本需要安装“OpenCvSharp4”、“OpenCvSharp4.runtime.win”两个库才行。不然会报错。

如果需要使用“ BitmapConverter.ToBitmap”操作,则需要追加安装“OpenCvSharp4.Extensions”库。

OpenCv3版本的SetMouseCallback操作请参考:/Course?id=4743192000049

using OpenCvSharp;
using System;

namespace demo
{
    internal class Program
    {
        //定义原始图、目标图、灰度图、掩模图
        static Mat g_srcImage = new Mat();
        static Mat g_dstImage = new Mat();
        static Mat g_grayImage = new Mat();
        static Mat g_maskImage = new Mat();

        //漫水填充的模式
        static int g_nFillMode = 1;
        //负差最大值、正差最大值
        static int g_nLowDifference = 20, g_nUpDifference = 20;
        //表示floodFill函数标识符低八位的连通值
        static int g_nConnectivity = 4;
        //是否为彩色图的标识符布尔值
        static bool g_bIsColor = true;
        //是否显示掩膜窗口的布尔值
        static bool g_bUseMask = false;
        //新的重新绘制的像素值
        static int g_nNewMaskVal = 255;
        static Random g_rng = new Random();

        static void ShowHelpText()
        {
            //输出一些帮助信息  
            Console.WriteLine("\n\n\t欢迎来到漫水填充示例程序~");
            Console.WriteLine("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");
            Console.WriteLine("\n\n\t按键操作说明:");
            Console.WriteLine("\t\t鼠标点击图中区域- 进行漫水填充操作");
            Console.WriteLine("\t\t键盘按键【ESC】- 退出程序");
            Console.WriteLine("\t\t键盘按键【1】-  切换彩色图/灰度图模式");
            Console.WriteLine("\t\t键盘按键【2】- 显示/隐藏掩膜窗口");
            Console.WriteLine("\t\t键盘按键【3】- 恢复原始图像");
            Console.WriteLine("\t\t键盘按键【4】- 使用空范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式");
            Console.WriteLine("\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n");
        }

        //-------------------【onMouse( )函数】---------------------
        //      描述:鼠标消息onMouse回调函数
        //----------------------------------------------------------
        static void onMouse(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userData)
        {
            // 若鼠标左键没有按下,便返回
            if (@event != MouseEventTypes.LButtonDown)
                return;

            //-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
            Point seed = new Point(x, y);
            //空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
            int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;

            //空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
            int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;

            //标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
            int mflags = (int)(g_nConnectivity + (g_nNewMaskVal << 8) + (g_nFillMode == 1 ? FloodFillFlags.FixedRange : 0));

            //随机生成bgr值
            int b = g_rng.Next(255);//随机返回一个0~255之间的值
            int g = g_rng.Next(255);//随机返回一个0~255之间的值
            int r = g_rng.Next(255);//随机返回一个0~255之间的值

            //定义重绘区域的最小边界矩形区域
            Rect ccomp = new Rect();

            // 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
            // 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
            Scalar newVal = g_bIsColor ? new Scalar(b, g, r) : new Scalar(r * 0.299 + g * 0.587 + b * 0.114);
            //目标图的赋值
            Mat dst = g_bIsColor ? g_dstImage : g_grayImage;
            int area;

            //--------------------【<2>正式调用floodFill函数】-----------------------------
            if (g_bUseMask)
            {
                Cv2.Threshold(g_maskImage, g_maskImage, 1, 128, ThresholdTypes.Binary);
                area = Cv2.FloodFill(dst, g_maskImage, seed, newVal, out ccomp,
                    new Scalar(LowDifference, LowDifference, LowDifference),
                    new Scalar(UpDifference, UpDifference, UpDifference), (FloodFillFlags)mflags);
                Cv2.ImShow("mask", g_maskImage);
            }
            else
            {
                area = Cv2.FloodFill(dst, seed, newVal, out ccomp,
                    new Scalar(LowDifference, LowDifference, LowDifference),
                    new Scalar(UpDifference, UpDifference, UpDifference), (FloodFillFlags)mflags);
            }

            Cv2.ImShow("效果图", dst);
            Console.WriteLine($"{area} 个像素被重绘");
            Cv2.WaitKey(10);
        }


        //-----------------【main( )函数】--------------------------
        //   描述:控制台应用程序的入口函数,我们的程序从这里开始  
        //----------------------------------------------------------
        static void Main(string[] args)
        {
            //载入原图
            g_srcImage = Cv2.ImRead("../../../images/girl3.jpg");

            // 缩放原图
            //resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 0.5, g_srcImage.rows * 0.5));

            //显示帮助文字
            ShowHelpText();

            //拷贝源图到目标图
            g_srcImage.CopyTo(g_dstImage);

            //转换三通道的image0到灰度图
            Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);

            //利用image0的尺寸来初始化掩膜mask
            g_maskImage.Create(g_srcImage.Rows + 2, g_srcImage.Cols + 2, MatType.CV_8UC1);

            Cv2.NamedWindow("效果图", WindowFlags.AutoSize);

            //创建Trackbar
            Cv2.CreateTrackbar("负差最大值", "效果图", ref g_nLowDifference, 255);
            Cv2.CreateTrackbar("正差最大值", "效果图", ref g_nUpDifference, 255);

            //鼠标回调函数
            Cv2.SetMouseCallback("效果图", new MouseCallback(onMouse));

            //循环轮询按键
            while (true)
            {
                //先显示效果图
                Cv2.ImShow("效果图", g_bIsColor ? g_dstImage : g_grayImage);

                //获取键盘按键
                int c = Cv2.WaitKey(0);
                //判断ESC是否按下,若按下便退出
                if ((c & 255) == 27)
                {
                    Console.WriteLine("程序退出...........");
                    break;
                }

                //根据按键的不同,进行各种操作
                switch ((char)c)
                {
                    //如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
                    case '1':
                        if (g_bIsColor) //若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
                        {
                            Console.WriteLine("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】");
                            Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            g_bIsColor = false; //将标识符置为false,表示当前图像不为彩色,而是灰度
                        }
                        //若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
                        else
                        {
                            Console.WriteLine("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n");
                            g_srcImage.CopyTo(g_dstImage);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            g_bIsColor = true;//将标识符置为true,表示当前图像模式为彩色
                        }
                        break;

                    //如果键盘按键“2”被按下,显示/隐藏掩膜窗口
                    case '2':
                        if (g_bUseMask)
                        {
                            Cv2.DestroyWindow("mask");
                            g_bUseMask = false;
                        }
                        else
                        {
                            Cv2.NamedWindow("mask", WindowFlags.AutoSize);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            Cv2.ImShow("mask", g_maskImage);
                            g_bUseMask = true;
                        }
                        break;

                    //如果键盘按键“3”被按下,恢复原始图像
                    case '3':
                        Console.WriteLine("按键“3”被按下,恢复原始图像");
                        g_srcImage.CopyTo(g_dstImage);
                        Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
                        g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));
                        break;

                    //如果键盘按键“4”被按下,使用空范围的漫水填充
                    case '4':
                        Console.WriteLine("按键“4”被按下,使用空范围的漫水填充");
                        g_nFillMode = 0;
                        break;

                    //如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
                    case '5':
                        Console.WriteLine("按键“5”被按下,使用渐变、固定范围的漫水填充");
                        g_nFillMode = 1;
                        break;

                    //如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
                    case '6':
                        Console.WriteLine("按键“6”被按下,使用渐变、浮动范围的漫水填充");
                        g_nFillMode = 2;
                        break;

                    //如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
                    case '7':
                        Console.WriteLine("按键“7”被按下,操作标志符的低八位使用4位的连接模式");
                        g_nConnectivity = 4;
                        break;

                    //如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
                    case '8':
                        Console.WriteLine("按键“8”被按下,操作标志符的低八位使用8位的连接模式");
                        g_nConnectivity = 8;
                        break;

                    default: break;
                }
            }
        }
    }
}


C++版本代码

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <iostream>
using namespace cv;
using namespace std;

//定义原始图、目标图、灰度图、掩模图
Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;
//漫水填充的模式
int g_nFillMode = 1;
//负差最大值、正差最大值
int g_nLowDifference = 20, g_nUpDifference = 20;
//表示floodFill函数标识符低八位的连通值
int g_nConnectivity = 4;
//是否为彩色图的标识符布尔值
bool g_bIsColor = true;
//是否显示掩膜窗口的布尔值
bool g_bUseMask = false;
//新的重新绘制的像素值
int g_nNewMaskVal = 255;

static void ShowHelpText()
{
    //输出一些帮助信息  
    printf("\n\n\t欢迎来到漫水填充示例程序~");
    printf("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");

    printf("\n\n\t按键操作说明: \n\n"
        "\t\t鼠标点击图中区域- 进行漫水填充操作\n"
        "\t\t键盘按键【ESC】- 退出程序\n"
        "\t\t键盘按键【1】-  切换彩色图/灰度图模式\n"
        "\t\t键盘按键【2】- 显示/隐藏掩膜窗口\n"
        "\t\t键盘按键【3】- 恢复原始图像\n"
        "\t\t键盘按键【4】- 使用空范围的漫水填充\n"
        "\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充\n"
        "\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充\n"
        "\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式\n"
        "\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n\n");
}

//-------------------【onMouse( )函数】---------------------
//      描述:鼠标消息onMouse回调函数
//----------------------------------------------------------
static void onMouse(int event, int x, int y, int, void*)
{
    // 若鼠标左键没有按下,便返回
    // opencv3:CV_EVENT_LBUTTONDOWN
    // opencv4:EVENT_LBUTTONDOWN
    if (event != EVENT_LBUTTONDOWN)
        return;

    //-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
    Point seed = Point(x, y);
    //空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
    int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;

    //空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
    int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;

    //标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
    // opencv3:CV_FLOODFILL_FIXED_RANGE
    // opencv4:FLOODFILL_FIXED_RANGE
    int flags = g_nConnectivity + (g_nNewMaskVal << 8) + (g_nFillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);

    //随机生成bgr值
    int b = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
    int g = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
    int r = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值

    //定义重绘区域的最小边界矩形区域
    Rect ccomp;

    // 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
    // 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
    Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r * 0.299 + g * 0.587 + b * 0.114);
    //目标图的赋值
    Mat dst = g_bIsColor ? g_dstImage : g_grayImage;
    int area;

    //--------------------【<2>正式调用floodFill函数】-----------------------------
    if (g_bUseMask)
    {
        // opencv3:CV_THRESH_BINARY
        // opencv4:THRESH_BINARY
        threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY);
        area = floodFill(dst, g_maskImage, seed, newVal, &ccomp,
            Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
        imshow("mask", g_maskImage);
    }
    else
    {
        area = floodFill(dst, seed, newVal, &ccomp,
            Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
    }

    imshow("效果图", dst);
    cout << area << " 个像素被重绘\n";
}

//-----------------【main( )函数】--------------------------
//   描述:控制台应用程序的入口函数,我们的程序从这里开始  
//----------------------------------------------------------
int main(int argc, char** argv)
{
    // 关闭opencv日志
    cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_SILENT);

    //改变console字体颜色  
    system("color 2F");

    //载入原图
    g_srcImage = imread("../images/girl3.jpg");

    // 缩放原图
    //resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 0.5, g_srcImage.rows * 0.5));
    if (!g_srcImage.data) { printf("Oh,no,读取图片image0错误~! \n"); return false; }

    //显示帮助文字
    ShowHelpText();

    //拷贝源图到目标图
    g_srcImage.copyTo(g_dstImage);

    //转换三通道的image0到灰度图
    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);

    //利用image0的尺寸来初始化掩膜mask
    g_maskImage.create(g_srcImage.rows + 2, g_srcImage.cols + 2, CV_8UC1);

    // opencv3:CV_WINDOW_AUTOSIZE
    // opencv4:WINDOW_AUTOSIZE
    namedWindow("效果图", WINDOW_AUTOSIZE);

    //创建Trackbar
    createTrackbar("负差最大值", "效果图", &g_nLowDifference, 255, 0);
    createTrackbar("正差最大值", "效果图", &g_nUpDifference, 255, 0);

    //鼠标回调函数
    setMouseCallback("效果图", onMouse, 0);

    //循环轮询按键
    while (1)
    {
        //先显示效果图
        imshow("效果图", g_bIsColor ? g_dstImage : g_grayImage);

        //获取键盘按键
        int c = waitKey(0);
        //判断ESC是否按下,若按下便退出
        if ((c & 255) == 27)
        {
            cout << "程序退出...........\n";
            break;
        }

        //根据按键的不同,进行各种操作
        switch ((char)c)
        {
            //如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
            case '1':
                if (g_bIsColor)//若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
                {
                    cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n";
                    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
                    g_maskImage = Scalar::all(0);   //将mask所有元素设置为0
                    g_bIsColor = false; //将标识符置为false,表示当前图像不为彩色,而是灰度
                }
                else//若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
                {
                    cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n";
                    g_srcImage.copyTo(g_dstImage);
                    g_maskImage = Scalar::all(0);
                    g_bIsColor = true;//将标识符置为true,表示当前图像模式为彩色
                }
                break;

                //如果键盘按键“2”被按下,显示/隐藏掩膜窗口
            case '2':
                if (g_bUseMask)
                {
                    destroyWindow("mask");
                    g_bUseMask = false;
                }
                else
                {
                    namedWindow("mask", WINDOW_AUTOSIZE);
                    g_maskImage = Scalar::all(0);
                    imshow("mask", g_maskImage);
                    g_bUseMask = true;
                }
                break;

                //如果键盘按键“3”被按下,恢复原始图像
            case '3':
                cout << "按键“3”被按下,恢复原始图像\n";
                g_srcImage.copyTo(g_dstImage);
                cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY);
                g_maskImage = Scalar::all(0);
                break;

                //如果键盘按键“4”被按下,使用空范围的漫水填充
            case '4':
                cout << "按键“4”被按下,使用空范围的漫水填充\n";
                g_nFillMode = 0;
                break;

                //如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
            case '5':
                cout << "按键“5”被按下,使用渐变、固定范围的漫水填充\n";
                g_nFillMode = 1;
                break;

                //如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
            case '6':
                cout << "按键“6”被按下,使用渐变、浮动范围的漫水填充\n";
                g_nFillMode = 2;
                break;

                //如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
            case '7':
                cout << "按键“7”被按下,操作标志符的低八位使用4位的连接模式\n";
                g_nConnectivity = 4;
                break;

                //如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
            case '8':
                cout << "按键“8”被按下,操作标志符的低八位使用8位的连接模式\n";
                g_nConnectivity = 8;
                break;

            default:break;
        }
    }
    return 0;
}


Python版本代码

import cv2
import numpy as np
import yaml
import time
import random


g_maskImage = np.zeros((0,0), np.uint8)
g_dstImage = np.zeros((0,0), np.uint8)
# 漫水填充的模式
g_nFillMode = 1
# 负差最大值、正差最大值
g_nLowDifference = 20
g_nUpDifference = 20
# 表示floodFill函数标识符低八位的连通值
g_nConnectivity = 4
# 是否为彩色图的标识符布尔值
g_bIsColor = True
# 是否显示掩膜窗口的布尔值
g_bUseMask = False
# 新的重新绘制的像素值
g_nNewMaskVal = 255

def ShowHelpText():
    #输出一些帮助信息  
    print("\n\n\t欢迎来到漫水填充示例程序~");
    print("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");
    print("\n\n\t按键操作说明: \n\n"
        "\t\t鼠标点击图中区域- 进行漫水填充操作\n"
        "\t\t键盘按键【ESC】- 退出程序\n"
        "\t\t键盘按键【1】-  切换彩色图/灰度图模式\n"
        "\t\t键盘按键【2】- 显示/隐藏掩膜窗口\n"
        "\t\t键盘按键【3】- 恢复原始图像\n"
        "\t\t键盘按键【4】- 使用空范围的漫水填充\n"
        "\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充\n"
        "\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充\n"
        "\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式\n"
        "\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n\n");

#创建回调函数
def onMouse(event,x,y,flags,param):
    global g_maskImage, g_dstImage

    if event != cv2.EVENT_LBUTTONDOWN:
        return
    
    #-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
    seed = (x, y)

    #空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
    LowDifference = 0 if g_nFillMode == 0 else g_nLowDifference

    #空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
    UpDifference = 0 if g_nFillMode == 0 else g_nUpDifference

    #标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
    m = cv2.FLOODFILL_FIXED_RANGE if g_nFillMode == 1 else 0
    mflags = g_nConnectivity + (g_nNewMaskVal << 8) + m

    #随机生成bgr值
    b = random.randint(0,255) #随机返回一个0~255之间的值
    g = random.randint(0,255) #随机返回一个0~255之间的值
    r = random.randint(0,255) #随机返回一个0~255之间的值

    # 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
    # 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
    newVal = (b, g, r) if g_bIsColor == True else (r * 0.299 + g * 0.587 + b * 0.114)

    #目标图的赋值
    dst = g_dstImage if g_bIsColor == True else g_grayImage
    
    #--------------------【<2>正式调用floodFill函数】-----------------------------
    ret,g_maskImage = cv2.threshold(g_maskImage,1,128,cv2.THRESH_BINARY)
    area = cv2.floodFill(dst, g_maskImage, seed, newVal, 
        (LowDifference, LowDifference, LowDifference), 
        (UpDifference, UpDifference, UpDifference), mflags)  

    if g_bUseMask == True:
        cv2.imshow('mask',g_maskImage)

    cv2.imshow('image',dst)
    print("(%d) 个像素被重绘" %(area[0]))

# 负差最大值
def nLowDifference(x):
    global g_nLowDifference
    g_nLowDifference = x

# 正差最大值
def nUpDifference(x):
    global g_nUpDifference
    g_nUpDifference = x

# 载入原图
g_srcImage=cv2.imread('../images/girl3.jpg')

# 显示帮助文字
ShowHelpText()

# 利用image0的尺寸来初始化掩膜mask
rows,cols,channels = g_srcImage.shape

# 拷贝源图到目标图
g_dstImage = np.zeros((rows,cols,3),np.uint8)
np.copyto(g_dstImage, g_srcImage)

# 转换三通道的image0到灰度图
g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)


# 不知道是不是版本问题,CreateImage和CreateMat在我这里都用不了
#g_maskImage = cv2.CreateImage((rows + 2, cols + 2), cv2.IPL_DEPTH_8U, 1)
#g_maskImage = cv2.CreateMat(rows + 2, cols + 2, cv2.CV_8UC1)
g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)

# 创建窗口
cv2.namedWindow('image')
# 显示原图
cv2.imshow('image',g_srcImage)

cv2.setMouseCallback('image',onMouse)
cv2.createTrackbar('nLow', 'image', 0, 255, nLowDifference)
cv2.createTrackbar('nUp', 'image', 0, 255, nUpDifference)
cv2.setTrackbarPos('nLow', 'image', 15)
cv2.setTrackbarPos('nUp', 'image', 15)

while (True):
    # 先显示效果图
    img = g_dstImage if g_bIsColor == True else g_grayImage
    cv2.imshow('image',img)
    c = cv2.waitKey(0)

    # 判断ESC是否按下,若按下便退出
    if c == 27 :
        print("程序退出...........")
        break

    # 如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
    if c == 49 :
        if g_bIsColor == True: #若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
            print("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】.")
            g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
            #将mask所有元素设置为0
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            #将标识符置为false,表示当前图像不为彩色,而是灰度
            g_bIsColor = False  
        else: #若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
            print("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】")
            np.copyto(g_dstImage, g_srcImage)
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            g_bIsColor = True #将标识符置为true,表示当前图像模式为彩色

    # 如果键盘按键“2”被按下,显示/隐藏掩膜窗口
    if c == 50 :
        if g_bUseMask:
            cv2.destroyWindow("mask")
            g_bUseMask = False
        else:
            cv2.namedWindow("mask", 0)
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            cv2.imshow("mask", g_maskImage)
            g_bUseMask = True;

    # 如果键盘按键“3”被按下,恢复原始图像
    if c == 51 :
        print("按键“3”被按下,恢复原始图像")
        np.copyto(g_dstImage, g_srcImage)
        g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
        g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)

    # 如果键盘按键“4”被按下,使用空范围的漫水填充
    if c == 52 :
        print("按键“4”被按下,使用空范围的漫水填充")
        g_nFillMode = 0

    # 如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
    if c == 53 :
        print("按键“5”被按下,使用渐变、固定范围的漫水填充")
        g_nFillMode = 1

    # 如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
    if c == 54 :
        print("按键“6”被按下,使用渐变、浮动范围的漫水填充")
        g_nFillMode = 2

    # 如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
    if c == 55 :
        print("按键“7”被按下,操作标志符的低八位使用4位的连接模式")
        g_nConnectivity = 4

    # 如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
    if c == 56 :
        print("按键“8”被按下,操作标志符的低八位使用8位的连接模式")
        g_nConnectivity = 8

cv2.destroyAllWindows()
7种图像处理形态学(2)
图像缩放与图像金字塔

友情链接: CSDN激萌の小宅 95知识库 自考题库 罗分明个人网络博客 精益编程leanboot

小宅博客  www.bilibili996.com All Rights Reserved. 备案号: 闽ICP备2024034575号

网站经营许可证  福建省福州市 Copyright©2021-2025 版权所有

小宅博客
首页 智能家居 地图定位
公告:小宅博客网可以开发票了,需要发票的,去群里找群主哈!!

文章作者:激萌の小宅

促销:¥0

价格:¥0

配送方式: 购买后立即生效(如购买异常,请联系站长)
付款之后一定要等待自动跳转结束,否则购买可能会失败
  • 0 天

    有效期

  • 0

    总销量

  • 0

    累计评价

漫水填充

视频讲解如下:



当前系列所有demo下载地址:

https://github.com/GaoRenBao/OpenCv4-Demo

不同编程语言对应的OpenCv版本以及开发环境信息如下: 

语言

OpenCv版本

IDE

C#

OpenCvSharp4.4.8.0.20230708

Visual Studio 2022

C++

OpenCv-4.5.5-vc14_vc15

Visual Studio 2022

Python

OpenCv-Python (4.6.0.66)

PyCharm Community Edition 2022.1.3


本章节给大家讲讲如何使用漫水填充功能FloodFill。当前代码主体还是使用毛星云的demo,C#和Python都是在C++版本的基础上转换过来的,三个版本的效果基本一致。

准备的原图如下:

2.jpg


运行效果如下:


C#版本代码

C#版本需要安装“OpenCvSharp4”、“OpenCvSharp4.runtime.win”两个库才行。不然会报错。

如果需要使用“ BitmapConverter.ToBitmap”操作,则需要追加安装“OpenCvSharp4.Extensions”库。

OpenCv3版本的SetMouseCallback操作请参考:/Course?id=4743192000049

using OpenCvSharp;
using System;

namespace demo
{
    internal class Program
    {
        //定义原始图、目标图、灰度图、掩模图
        static Mat g_srcImage = new Mat();
        static Mat g_dstImage = new Mat();
        static Mat g_grayImage = new Mat();
        static Mat g_maskImage = new Mat();

        //漫水填充的模式
        static int g_nFillMode = 1;
        //负差最大值、正差最大值
        static int g_nLowDifference = 20, g_nUpDifference = 20;
        //表示floodFill函数标识符低八位的连通值
        static int g_nConnectivity = 4;
        //是否为彩色图的标识符布尔值
        static bool g_bIsColor = true;
        //是否显示掩膜窗口的布尔值
        static bool g_bUseMask = false;
        //新的重新绘制的像素值
        static int g_nNewMaskVal = 255;
        static Random g_rng = new Random();

        static void ShowHelpText()
        {
            //输出一些帮助信息  
            Console.WriteLine("\n\n\t欢迎来到漫水填充示例程序~");
            Console.WriteLine("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");
            Console.WriteLine("\n\n\t按键操作说明:");
            Console.WriteLine("\t\t鼠标点击图中区域- 进行漫水填充操作");
            Console.WriteLine("\t\t键盘按键【ESC】- 退出程序");
            Console.WriteLine("\t\t键盘按键【1】-  切换彩色图/灰度图模式");
            Console.WriteLine("\t\t键盘按键【2】- 显示/隐藏掩膜窗口");
            Console.WriteLine("\t\t键盘按键【3】- 恢复原始图像");
            Console.WriteLine("\t\t键盘按键【4】- 使用空范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充");
            Console.WriteLine("\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式");
            Console.WriteLine("\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n");
        }

        //-------------------【onMouse( )函数】---------------------
        //      描述:鼠标消息onMouse回调函数
        //----------------------------------------------------------
        static void onMouse(MouseEventTypes @event, int x, int y, MouseEventFlags flags, IntPtr userData)
        {
            // 若鼠标左键没有按下,便返回
            if (@event != MouseEventTypes.LButtonDown)
                return;

            //-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
            Point seed = new Point(x, y);
            //空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
            int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;

            //空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
            int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;

            //标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
            int mflags = (int)(g_nConnectivity + (g_nNewMaskVal << 8) + (g_nFillMode == 1 ? FloodFillFlags.FixedRange : 0));

            //随机生成bgr值
            int b = g_rng.Next(255);//随机返回一个0~255之间的值
            int g = g_rng.Next(255);//随机返回一个0~255之间的值
            int r = g_rng.Next(255);//随机返回一个0~255之间的值

            //定义重绘区域的最小边界矩形区域
            Rect ccomp = new Rect();

            // 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
            // 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
            Scalar newVal = g_bIsColor ? new Scalar(b, g, r) : new Scalar(r * 0.299 + g * 0.587 + b * 0.114);
            //目标图的赋值
            Mat dst = g_bIsColor ? g_dstImage : g_grayImage;
            int area;

            //--------------------【<2>正式调用floodFill函数】-----------------------------
            if (g_bUseMask)
            {
                Cv2.Threshold(g_maskImage, g_maskImage, 1, 128, ThresholdTypes.Binary);
                area = Cv2.FloodFill(dst, g_maskImage, seed, newVal, out ccomp,
                    new Scalar(LowDifference, LowDifference, LowDifference),
                    new Scalar(UpDifference, UpDifference, UpDifference), (FloodFillFlags)mflags);
                Cv2.ImShow("mask", g_maskImage);
            }
            else
            {
                area = Cv2.FloodFill(dst, seed, newVal, out ccomp,
                    new Scalar(LowDifference, LowDifference, LowDifference),
                    new Scalar(UpDifference, UpDifference, UpDifference), (FloodFillFlags)mflags);
            }

            Cv2.ImShow("效果图", dst);
            Console.WriteLine($"{area} 个像素被重绘");
            Cv2.WaitKey(10);
        }


        //-----------------【main( )函数】--------------------------
        //   描述:控制台应用程序的入口函数,我们的程序从这里开始  
        //----------------------------------------------------------
        static void Main(string[] args)
        {
            //载入原图
            g_srcImage = Cv2.ImRead("../../../images/girl3.jpg");

            // 缩放原图
            //resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 0.5, g_srcImage.rows * 0.5));

            //显示帮助文字
            ShowHelpText();

            //拷贝源图到目标图
            g_srcImage.CopyTo(g_dstImage);

            //转换三通道的image0到灰度图
            Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);

            //利用image0的尺寸来初始化掩膜mask
            g_maskImage.Create(g_srcImage.Rows + 2, g_srcImage.Cols + 2, MatType.CV_8UC1);

            Cv2.NamedWindow("效果图", WindowFlags.AutoSize);

            //创建Trackbar
            Cv2.CreateTrackbar("负差最大值", "效果图", ref g_nLowDifference, 255);
            Cv2.CreateTrackbar("正差最大值", "效果图", ref g_nUpDifference, 255);

            //鼠标回调函数
            Cv2.SetMouseCallback("效果图", new MouseCallback(onMouse));

            //循环轮询按键
            while (true)
            {
                //先显示效果图
                Cv2.ImShow("效果图", g_bIsColor ? g_dstImage : g_grayImage);

                //获取键盘按键
                int c = Cv2.WaitKey(0);
                //判断ESC是否按下,若按下便退出
                if ((c & 255) == 27)
                {
                    Console.WriteLine("程序退出...........");
                    break;
                }

                //根据按键的不同,进行各种操作
                switch ((char)c)
                {
                    //如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
                    case '1':
                        if (g_bIsColor) //若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
                        {
                            Console.WriteLine("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】");
                            Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            g_bIsColor = false; //将标识符置为false,表示当前图像不为彩色,而是灰度
                        }
                        //若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
                        else
                        {
                            Console.WriteLine("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n");
                            g_srcImage.CopyTo(g_dstImage);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            g_bIsColor = true;//将标识符置为true,表示当前图像模式为彩色
                        }
                        break;

                    //如果键盘按键“2”被按下,显示/隐藏掩膜窗口
                    case '2':
                        if (g_bUseMask)
                        {
                            Cv2.DestroyWindow("mask");
                            g_bUseMask = false;
                        }
                        else
                        {
                            Cv2.NamedWindow("mask", WindowFlags.AutoSize);
                            g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));//将mask所有元素设置为0
                            Cv2.ImShow("mask", g_maskImage);
                            g_bUseMask = true;
                        }
                        break;

                    //如果键盘按键“3”被按下,恢复原始图像
                    case '3':
                        Console.WriteLine("按键“3”被按下,恢复原始图像");
                        g_srcImage.CopyTo(g_dstImage);
                        Cv2.CvtColor(g_srcImage, g_grayImage, ColorConversionCodes.BGR2GRAY);
                        g_maskImage = g_maskImage.SetTo(new Scalar(0, 0, 0));
                        break;

                    //如果键盘按键“4”被按下,使用空范围的漫水填充
                    case '4':
                        Console.WriteLine("按键“4”被按下,使用空范围的漫水填充");
                        g_nFillMode = 0;
                        break;

                    //如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
                    case '5':
                        Console.WriteLine("按键“5”被按下,使用渐变、固定范围的漫水填充");
                        g_nFillMode = 1;
                        break;

                    //如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
                    case '6':
                        Console.WriteLine("按键“6”被按下,使用渐变、浮动范围的漫水填充");
                        g_nFillMode = 2;
                        break;

                    //如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
                    case '7':
                        Console.WriteLine("按键“7”被按下,操作标志符的低八位使用4位的连接模式");
                        g_nConnectivity = 4;
                        break;

                    //如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
                    case '8':
                        Console.WriteLine("按键“8”被按下,操作标志符的低八位使用8位的连接模式");
                        g_nConnectivity = 8;
                        break;

                    default: break;
                }
            }
        }
    }
}


C++版本代码

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/core/utils/logger.hpp>
#include <iostream>
using namespace cv;
using namespace std;

//定义原始图、目标图、灰度图、掩模图
Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;
//漫水填充的模式
int g_nFillMode = 1;
//负差最大值、正差最大值
int g_nLowDifference = 20, g_nUpDifference = 20;
//表示floodFill函数标识符低八位的连通值
int g_nConnectivity = 4;
//是否为彩色图的标识符布尔值
bool g_bIsColor = true;
//是否显示掩膜窗口的布尔值
bool g_bUseMask = false;
//新的重新绘制的像素值
int g_nNewMaskVal = 255;

static void ShowHelpText()
{
    //输出一些帮助信息  
    printf("\n\n\t欢迎来到漫水填充示例程序~");
    printf("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");

    printf("\n\n\t按键操作说明: \n\n"
        "\t\t鼠标点击图中区域- 进行漫水填充操作\n"
        "\t\t键盘按键【ESC】- 退出程序\n"
        "\t\t键盘按键【1】-  切换彩色图/灰度图模式\n"
        "\t\t键盘按键【2】- 显示/隐藏掩膜窗口\n"
        "\t\t键盘按键【3】- 恢复原始图像\n"
        "\t\t键盘按键【4】- 使用空范围的漫水填充\n"
        "\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充\n"
        "\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充\n"
        "\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式\n"
        "\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n\n");
}

//-------------------【onMouse( )函数】---------------------
//      描述:鼠标消息onMouse回调函数
//----------------------------------------------------------
static void onMouse(int event, int x, int y, int, void*)
{
    // 若鼠标左键没有按下,便返回
    // opencv3:CV_EVENT_LBUTTONDOWN
    // opencv4:EVENT_LBUTTONDOWN
    if (event != EVENT_LBUTTONDOWN)
        return;

    //-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
    Point seed = Point(x, y);
    //空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
    int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;

    //空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
    int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;

    //标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
    // opencv3:CV_FLOODFILL_FIXED_RANGE
    // opencv4:FLOODFILL_FIXED_RANGE
    int flags = g_nConnectivity + (g_nNewMaskVal << 8) + (g_nFillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);

    //随机生成bgr值
    int b = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
    int g = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值
    int r = (unsigned)theRNG() & 255;//随机返回一个0~255之间的值

    //定义重绘区域的最小边界矩形区域
    Rect ccomp;

    // 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
    // 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
    Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r * 0.299 + g * 0.587 + b * 0.114);
    //目标图的赋值
    Mat dst = g_bIsColor ? g_dstImage : g_grayImage;
    int area;

    //--------------------【<2>正式调用floodFill函数】-----------------------------
    if (g_bUseMask)
    {
        // opencv3:CV_THRESH_BINARY
        // opencv4:THRESH_BINARY
        threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY);
        area = floodFill(dst, g_maskImage, seed, newVal, &ccomp,
            Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
        imshow("mask", g_maskImage);
    }
    else
    {
        area = floodFill(dst, seed, newVal, &ccomp,
            Scalar(LowDifference, LowDifference, LowDifference),
            Scalar(UpDifference, UpDifference, UpDifference), flags);
    }

    imshow("效果图", dst);
    cout << area << " 个像素被重绘\n";
}

//-----------------【main( )函数】--------------------------
//   描述:控制台应用程序的入口函数,我们的程序从这里开始  
//----------------------------------------------------------
int main(int argc, char** argv)
{
    // 关闭opencv日志
    cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_SILENT);

    //改变console字体颜色  
    system("color 2F");

    //载入原图
    g_srcImage = imread("../images/girl3.jpg");

    // 缩放原图
    //resize(g_srcImage, g_srcImage, Size(g_srcImage.cols * 0.5, g_srcImage.rows * 0.5));
    if (!g_srcImage.data) { printf("Oh,no,读取图片image0错误~! \n"); return false; }

    //显示帮助文字
    ShowHelpText();

    //拷贝源图到目标图
    g_srcImage.copyTo(g_dstImage);

    //转换三通道的image0到灰度图
    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);

    //利用image0的尺寸来初始化掩膜mask
    g_maskImage.create(g_srcImage.rows + 2, g_srcImage.cols + 2, CV_8UC1);

    // opencv3:CV_WINDOW_AUTOSIZE
    // opencv4:WINDOW_AUTOSIZE
    namedWindow("效果图", WINDOW_AUTOSIZE);

    //创建Trackbar
    createTrackbar("负差最大值", "效果图", &g_nLowDifference, 255, 0);
    createTrackbar("正差最大值", "效果图", &g_nUpDifference, 255, 0);

    //鼠标回调函数
    setMouseCallback("效果图", onMouse, 0);

    //循环轮询按键
    while (1)
    {
        //先显示效果图
        imshow("效果图", g_bIsColor ? g_dstImage : g_grayImage);

        //获取键盘按键
        int c = waitKey(0);
        //判断ESC是否按下,若按下便退出
        if ((c & 255) == 27)
        {
            cout << "程序退出...........\n";
            break;
        }

        //根据按键的不同,进行各种操作
        switch ((char)c)
        {
            //如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
            case '1':
                if (g_bIsColor)//若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
                {
                    cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n";
                    cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
                    g_maskImage = Scalar::all(0);   //将mask所有元素设置为0
                    g_bIsColor = false; //将标识符置为false,表示当前图像不为彩色,而是灰度
                }
                else//若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
                {
                    cout << "键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】\n";
                    g_srcImage.copyTo(g_dstImage);
                    g_maskImage = Scalar::all(0);
                    g_bIsColor = true;//将标识符置为true,表示当前图像模式为彩色
                }
                break;

                //如果键盘按键“2”被按下,显示/隐藏掩膜窗口
            case '2':
                if (g_bUseMask)
                {
                    destroyWindow("mask");
                    g_bUseMask = false;
                }
                else
                {
                    namedWindow("mask", WINDOW_AUTOSIZE);
                    g_maskImage = Scalar::all(0);
                    imshow("mask", g_maskImage);
                    g_bUseMask = true;
                }
                break;

                //如果键盘按键“3”被按下,恢复原始图像
            case '3':
                cout << "按键“3”被按下,恢复原始图像\n";
                g_srcImage.copyTo(g_dstImage);
                cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY);
                g_maskImage = Scalar::all(0);
                break;

                //如果键盘按键“4”被按下,使用空范围的漫水填充
            case '4':
                cout << "按键“4”被按下,使用空范围的漫水填充\n";
                g_nFillMode = 0;
                break;

                //如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
            case '5':
                cout << "按键“5”被按下,使用渐变、固定范围的漫水填充\n";
                g_nFillMode = 1;
                break;

                //如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
            case '6':
                cout << "按键“6”被按下,使用渐变、浮动范围的漫水填充\n";
                g_nFillMode = 2;
                break;

                //如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
            case '7':
                cout << "按键“7”被按下,操作标志符的低八位使用4位的连接模式\n";
                g_nConnectivity = 4;
                break;

                //如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
            case '8':
                cout << "按键“8”被按下,操作标志符的低八位使用8位的连接模式\n";
                g_nConnectivity = 8;
                break;

            default:break;
        }
    }
    return 0;
}


Python版本代码

import cv2
import numpy as np
import yaml
import time
import random


g_maskImage = np.zeros((0,0), np.uint8)
g_dstImage = np.zeros((0,0), np.uint8)
# 漫水填充的模式
g_nFillMode = 1
# 负差最大值、正差最大值
g_nLowDifference = 20
g_nUpDifference = 20
# 表示floodFill函数标识符低八位的连通值
g_nConnectivity = 4
# 是否为彩色图的标识符布尔值
g_bIsColor = True
# 是否显示掩膜窗口的布尔值
g_bUseMask = False
# 新的重新绘制的像素值
g_nNewMaskVal = 255

def ShowHelpText():
    #输出一些帮助信息  
    print("\n\n\t欢迎来到漫水填充示例程序~");
    print("\n\n\t本示例根据鼠标选取的点搜索图像中与之颜色相近的点,并用不同颜色标注。");
    print("\n\n\t按键操作说明: \n\n"
        "\t\t鼠标点击图中区域- 进行漫水填充操作\n"
        "\t\t键盘按键【ESC】- 退出程序\n"
        "\t\t键盘按键【1】-  切换彩色图/灰度图模式\n"
        "\t\t键盘按键【2】- 显示/隐藏掩膜窗口\n"
        "\t\t键盘按键【3】- 恢复原始图像\n"
        "\t\t键盘按键【4】- 使用空范围的漫水填充\n"
        "\t\t键盘按键【5】- 使用渐变、固定范围的漫水填充\n"
        "\t\t键盘按键【6】- 使用渐变、浮动范围的漫水填充\n"
        "\t\t键盘按键【7】- 操作标志符的低八位使用4位的连接模式\n"
        "\t\t键盘按键【8】- 操作标志符的低八位使用8位的连接模式\n\n");

#创建回调函数
def onMouse(event,x,y,flags,param):
    global g_maskImage, g_dstImage

    if event != cv2.EVENT_LBUTTONDOWN:
        return
    
    #-------------------【<1>调用floodFill函数之前的参数准备部分】---------------
    seed = (x, y)

    #空范围的漫水填充,此值设为0,否则设为全局的g_nLowDifference
    LowDifference = 0 if g_nFillMode == 0 else g_nLowDifference

    #空范围的漫水填充,此值设为0,否则设为全局的g_nUpDifference
    UpDifference = 0 if g_nFillMode == 0 else g_nUpDifference

    #标识符的0~7位为g_nConnectivity,8~15位为g_nNewMaskVal左移8位的值,16~23位为CV_FLOODFILL_FIXED_RANGE或者0。
    m = cv2.FLOODFILL_FIXED_RANGE if g_nFillMode == 1 else 0
    mflags = g_nConnectivity + (g_nNewMaskVal << 8) + m

    #随机生成bgr值
    b = random.randint(0,255) #随机返回一个0~255之间的值
    g = random.randint(0,255) #随机返回一个0~255之间的值
    r = random.randint(0,255) #随机返回一个0~255之间的值

    # 在重绘区域像素的新值,若是彩色图模式,取Scalar(b, g, r);
    # 若是灰度图模式,取Scalar(r*0.299 + g*0.587 + b*0.114)
    newVal = (b, g, r) if g_bIsColor == True else (r * 0.299 + g * 0.587 + b * 0.114)

    #目标图的赋值
    dst = g_dstImage if g_bIsColor == True else g_grayImage
    
    #--------------------【<2>正式调用floodFill函数】-----------------------------
    ret,g_maskImage = cv2.threshold(g_maskImage,1,128,cv2.THRESH_BINARY)
    area = cv2.floodFill(dst, g_maskImage, seed, newVal, 
        (LowDifference, LowDifference, LowDifference), 
        (UpDifference, UpDifference, UpDifference), mflags)  

    if g_bUseMask == True:
        cv2.imshow('mask',g_maskImage)

    cv2.imshow('image',dst)
    print("(%d) 个像素被重绘" %(area[0]))

# 负差最大值
def nLowDifference(x):
    global g_nLowDifference
    g_nLowDifference = x

# 正差最大值
def nUpDifference(x):
    global g_nUpDifference
    g_nUpDifference = x

# 载入原图
g_srcImage=cv2.imread('../images/girl3.jpg')

# 显示帮助文字
ShowHelpText()

# 利用image0的尺寸来初始化掩膜mask
rows,cols,channels = g_srcImage.shape

# 拷贝源图到目标图
g_dstImage = np.zeros((rows,cols,3),np.uint8)
np.copyto(g_dstImage, g_srcImage)

# 转换三通道的image0到灰度图
g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)


# 不知道是不是版本问题,CreateImage和CreateMat在我这里都用不了
#g_maskImage = cv2.CreateImage((rows + 2, cols + 2), cv2.IPL_DEPTH_8U, 1)
#g_maskImage = cv2.CreateMat(rows + 2, cols + 2, cv2.CV_8UC1)
g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)

# 创建窗口
cv2.namedWindow('image')
# 显示原图
cv2.imshow('image',g_srcImage)

cv2.setMouseCallback('image',onMouse)
cv2.createTrackbar('nLow', 'image', 0, 255, nLowDifference)
cv2.createTrackbar('nUp', 'image', 0, 255, nUpDifference)
cv2.setTrackbarPos('nLow', 'image', 15)
cv2.setTrackbarPos('nUp', 'image', 15)

while (True):
    # 先显示效果图
    img = g_dstImage if g_bIsColor == True else g_grayImage
    cv2.imshow('image',img)
    c = cv2.waitKey(0)

    # 判断ESC是否按下,若按下便退出
    if c == 27 :
        print("程序退出...........")
        break

    # 如果键盘“1”被按下,效果图在在灰度图,彩色图之间互换
    if c == 49 :
        if g_bIsColor == True: #若原来为彩色,转为灰度图,并且将掩膜mask所有元素设置为0
            print("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】.")
            g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
            #将mask所有元素设置为0
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            #将标识符置为false,表示当前图像不为彩色,而是灰度
            g_bIsColor = False  
        else: #若原来为灰度图,便将原来的彩图image0再次拷贝给image,并且将掩膜mask所有元素设置为0
            print("键盘“1”被按下,切换彩色/灰度模式,当前操作为将【彩色模式】切换为【灰度模式】")
            np.copyto(g_dstImage, g_srcImage)
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            g_bIsColor = True #将标识符置为true,表示当前图像模式为彩色

    # 如果键盘按键“2”被按下,显示/隐藏掩膜窗口
    if c == 50 :
        if g_bUseMask:
            cv2.destroyWindow("mask")
            g_bUseMask = False
        else:
            cv2.namedWindow("mask", 0)
            g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)
            cv2.imshow("mask", g_maskImage)
            g_bUseMask = True;

    # 如果键盘按键“3”被按下,恢复原始图像
    if c == 51 :
        print("按键“3”被按下,恢复原始图像")
        np.copyto(g_dstImage, g_srcImage)
        g_grayImage=cv2.cvtColor(g_srcImage, cv2.COLOR_BGR2GRAY)
        g_maskImage = np.zeros((rows + 2, cols + 2), np.uint8)

    # 如果键盘按键“4”被按下,使用空范围的漫水填充
    if c == 52 :
        print("按键“4”被按下,使用空范围的漫水填充")
        g_nFillMode = 0

    # 如果键盘按键“5”被按下,使用渐变、固定范围的漫水填充
    if c == 53 :
        print("按键“5”被按下,使用渐变、固定范围的漫水填充")
        g_nFillMode = 1

    # 如果键盘按键“6”被按下,使用渐变、浮动范围的漫水填充
    if c == 54 :
        print("按键“6”被按下,使用渐变、浮动范围的漫水填充")
        g_nFillMode = 2

    # 如果键盘按键“7”被按下,操作标志符的低八位使用4位的连接模式
    if c == 55 :
        print("按键“7”被按下,操作标志符的低八位使用4位的连接模式")
        g_nConnectivity = 4

    # 如果键盘按键“8”被按下,操作标志符的低八位使用8位的连接模式
    if c == 56 :
        print("按键“8”被按下,操作标志符的低八位使用8位的连接模式")
        g_nConnectivity = 8

cv2.destroyAllWindows()