OpenCV开发笔记(三十三):红胖子8分钟带你深入了解漫水填充算法(图文并茂+浅显易懂+程序源码)

若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/104891183
各位读者,知识无穷而人力有穷,要么改需求,要么找专业人士,要么自己研究

目录

前言

Demo

漫水填充

概述

原理

函数原型

Demo源码

工程模板:对应版本号v1.28.0


OpenCV开发专栏

OpenCV开发笔记(〇):使用mingw530_32编译openCV3.4.1源码,搭建Qt5.9.3的openCV开发环境

OpenCV开发笔记(一):OpenCV介绍、编译

OpenCV开发笔记(二):cvui交互界面

OpenCV开发笔记(三):OpenCV图像的概念和基本操作

OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储

OpenCV开发笔记(五):OpenCV读取与操作摄像头

OpenCV开发笔记(六):OpenCV基础数据结构、颜色转换函数和颜色空间

OpenCV开发笔记(七):OpenCV基础图形绘制

OpenCV开发笔记(八):OpenCV常用操作之计时、缩放、旋转、镜像

OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合

OpenCV开发笔记十):OpenCV图像颜色通道分离和图像颜色多通道混合

OpenCV开发笔记(十一):OpenCV编译支持Gpu(cuda) 加速开发之win-qt-mingw32编译

OpenCV开发笔记(十二):OpenCV编译支持Gpu(cuda) 加速开发之win-qt-msvc2015编译(opencv3.4.0、cuda9.0、VS2015)

OpenCV开发笔记(十三):OpenCV图像对比度、亮度的调整

OpenCV开发笔记(十四):算法基础之线性滤波-方框滤波

OpenCV开发笔记(十五):算法基础之线性滤波-均值滤波

OpenCV开发笔记(十六):算法基础之线性滤波-高斯滤波

OpenCV开发笔记(十七):算法基础之线性滤波对比-方框、均值、高斯滤波

OpenCV开发笔记(十八):算法基础之非线性滤波-中值滤波

OpenCV开发笔记(十九):算法基础之非线性滤波-双边滤波

OpenCV开发笔记(二十):算法基础之非线性滤波对比-中值、双边滤波

OpenCV开发笔记(二十):算法基础之形态学滤波-膨胀

OpenCV开发笔记(二十):算法基础之形态学滤波-腐蚀

OpenCV开发笔记(二十):算法基础之形态学滤波-开运算

OpenCV开发笔记(二十):算法基础之形态学滤波-闭运算

OpenCV开发笔记(二十):算法基础之形态学滤波-形态学梯度

OpenCV开发笔记(二十):算法基础之形态学滤波-顶帽(礼帽)

OpenCV开发笔记(二十):算法基础之形态学滤波-黑帽

OpenCV开发笔记(二十八):带你学习图像识别之阈值化

OpenCV开发笔记(二十九):带你学习图像识别之自适应阈值

OpenCV开发笔记(三十):带你学习图像识别之经典OTSU算法阈值化

OpenCV开发笔记(三十一):红胖子8分钟带你深入了解双阈值化(图文并貌+浅显易懂+程序源码)

OpenCV开发笔记(三十二):红胖子8分钟带你深入解半阈值化(图文并貌+浅显易懂+程序源码)

OpenCV开发笔记(三十三):红胖子8分钟带你深入了解漫水填充算法(图文并茂+浅显易懂+程序源码)

持续补充中…

 

    OpenCV开发笔记(三十三):红胖子8分钟带你深入了解漫水填充算法(图文并茂+浅显易懂+程序源码)

前言

红胖子来也!!!

今天来说说漫水填充,顾名思义,漫水填充就是放水,水漫上去,那么连通的区域不就都有水,就形成了一片漫水的区域,该区域就是要分离的。

通俗一点:漫水的整个过程,首先要选择一点作为防水点,然后开始放水,放水多少就能向外围扩散多大面积,可以把像素的bgr值想象成高度,谁放的越多,能覆盖的相邻高度差就越大。

Demo

 

漫水填充

概述

      漫水填充是一种用特定的颜色填充连通区域,通过设置可联通像素的上下限以及联通方式来达到不同的填充效果的方法。它是在很多图形绘软件中常用的填充算法。

      漫水填充被用来标记或分离图像的一部分,以便对其进行一步处理或分析,也可以用来从输入图像获取掩码区域,掩码会加速处理过程,或只处理掩码指定的像素点,操作的结果总是某个连续的区域。

原理

      漫水填充的原理简单来说,就是从一个点开始水漫(放水,像素rgb的差理解为高度差)附近像素点,填充成新的颜色,直到封闭区域内的所有像素点都被填充成新颜色位置。

      漫水填充的实现方法也有多种:4邻域像素填充法、8邻域像素填充法、基于扫描线的填充方法。

函数原型

(注意:下面的函数不带掩码输入)

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

(注意:下面的函数带掩码输入)

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 );
  • 参数一:InputOutputArray类型,一般是cv::Mat,参数图像输入/输出1或3通道、8位或浮点图像。此cv::Mat将同时作为输出,除非在函数的第二个变量中设置了“FLOODFILL_MASK_ONLY”标志。
  • 参数二:InputOutputArray InputOutputArray类型的mask,这是第 二个版本的 floodFill独享的参数,表示操作掩模。它应该为单通道8 位,长和  宽上都比输入图像 image大两个像素点。第二个版本 的floodFill需要使用以及更新掩膜 , 所以对 于这个 mask 参数,我们一定要将其准备好并填在此处。需要注意的是,漫水填充不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大,所以mask中与输入图像(x ,y)像素点相对应的点的坐标为(x+ l, y+ l)。
  • 参数三:Point类型seedPoint,起始像素点。
  • 参数四Scalar类型的newVla,重绘像素区域新的填充颜色。
  • 参数五:Rect类型的rect,可选输出参数,返回重绘区域的最小绑定矩形。
  • 参数六: Scalar类型的loDiff,当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大下行差异值。
  • 参数七:Scalar类型的upDiff,当前选定像素与其连通区中相邻像素中的一个像素,或者与加入该连通区的一个seedPoint像素,二者之间的最大上行差异值。
  • 参数八: FloodFillFlags类型的flags标志,一个32bit的int类型数据,其由3部分组成: 0-7bit表示邻接性(4邻接、8邻接);8-15bit表示mask的填充颜色;16-31bit表示填充模式。

Demo源码

void OpenCVManager::testDiffuseFill()
{
    QString fileName1 = "I:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/11.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());

    int width = 300;
    int height = 200;
    cv::resize(srcMat, srcMat, cv::Size(width, height));

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);

    cv::Mat windowMat = cv::Mat(cv::Size(srcMat.cols * 3, srcMat.rows * 4),
                                srcMat.type());

    int x = 100;
    int y = 100;

    int b = 255;
    int g = 0;
    int r = 0;

    int loB = 5;
    int loG = 5;
    int loR = 5;

    int upB = 5;
    int upG = 5;
    int upR = 5;

    while(true)
    {
        windowMat = cv::Scalar(0, 0, 0);
        // 原图先copy到左边
        cv::Mat leftMat = windowMat(cv::Range(0, srcMat.rows),
                                    cv::Range(0, srcMat.cols));
        cv::addWeighted(leftMat, 1.0f, srcMat, 1.0f, 0.0f, leftMat);

        // 选取原图坐标点
        cvui::printf(windowMat, width * 1 + 50, 30 + height * 0, "x");
        cvui::trackbar(windowMat, width * 1 + 50, 40 + height * 0, 200, &x, 0, width);

        cvui::printf(windowMat, width * 1 + 50, 90 + height * 0, "y");
        cvui::trackbar(windowMat, width * 1 + 50, 100 + height * 0, 200, &y, 0, height);

        // 修改的新颜色
        cvui::printf(windowMat, width * 2 + 50, 0 + height * 0, "b");
        cvui::trackbar(windowMat, width * 2 + 50, 10 + height * 0, 200, &b, 0, 255);

        cvui::printf(windowMat, width * 2 + 50, 60 + height * 0, "g");
        cvui::trackbar(windowMat, width * 2 + 50, 70 + height * 0, 200, &g, 0, 255);

        cvui::printf(windowMat, width * 2 + 50, 120 + height * 0, "r");
        cvui::trackbar(windowMat, width * 2 + 50, 130 + height * 0, 200, &r, 0, 255);

        // 低像素差
        cvui::printf(windowMat, width * 1 + 50, 0 + height * 1, "loB");
        cvui::trackbar(windowMat, width * 1 + 50, 10 + height * 1, 200, &loB, 0, 255);

        cvui::printf(windowMat, width * 1 + 50, 60 + height * 1, "loG");
        cvui::trackbar(windowMat, width * 1 + 50, 70 + height * 1, 200, &loG, 0, 255);

        cvui::printf(windowMat, width * 1 + 50, 120 + height * 1, "loR");
        cvui::trackbar(windowMat, width * 1 + 50, 130 + height * 1, 200, &loR, 0, 255);

        // 高像素差
        cvui::printf(windowMat, width * 2 + 50, 0 + height * 1, "upB");
        cvui::trackbar(windowMat, width * 2 + 50, 10 + height * 1, 200, &upB, 0, 255);

        cvui::printf(windowMat, width * 2 + 50, 60 + height * 1, "upG");
        cvui::trackbar(windowMat, width * 2 + 50, 70 + height * 1, 200, &upG, 0, 255);

        cvui::printf(windowMat, width * 2 + 50, 120 + height * 1, "upR");
        cvui::trackbar(windowMat, width * 2 + 50, 130 + height * 1, 200, &upR, 0, 255);

        // 标志
        cvui::printf(windowMat, width * 0 + 50, 60 + height * 2, "flags: default");

        cvui::printf(windowMat, width * 1 + 50, 60 + height * 2, "flags: 4 | FLOODFILL_FIXED_RANGE");

        cvui::printf(windowMat, width * 2 + 50, 60 + height * 2, "flags: 8 | FLOODFILL_FIXED_RANGE");

        // circle
        cv::circle(windowMat, cv::Point(x, y), 5, cv::Scalar(0, 0, 255), -1);

        {
            cv::Rect rect;
            cv::Mat tempMat;
            cv::Mat dstMat;
            // 填充
            tempMat = srcMat.clone();
            cv::floodFill(tempMat,
                          cv::Point(x, y),
                          cv::Scalar(b, g, r),
                          &rect,
                          cv::Scalar(loB, loG, loR),
                          cv::Scalar(upB, upG, upR));
            dstMat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                               cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(dstMat, 0.0f, tempMat, 1.0f, 0.0f, dstMat);

            // 填充
            tempMat = srcMat.clone();
            cv::floodFill(tempMat,
                          cv::Point(x, y),
                          cv::Scalar(b, g, r),
                          &rect,
                          cv::Scalar(loB, loG, loR),
                          cv::Scalar(upB, upG, upR),
                          4 | cv::FLOODFILL_FIXED_RANGE);
            dstMat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                               cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(dstMat, 0.0f, tempMat, 1.0f, 0.0f, dstMat);

            // 填充
            tempMat = srcMat.clone();
            cv::floodFill(tempMat,
                          cv::Point(x, y),
                          cv::Scalar(b, g, r),
                          &rect,
                          cv::Scalar(loB, loG, loR),
                          cv::Scalar(upB, upG, upR),
                          8 | cv::FLOODFILL_FIXED_RANGE);
            dstMat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                               cv::Range(srcMat.cols * 2, srcMat.cols * 3));
            cv::addWeighted(dstMat, 0.0f, tempMat, 1.0f, 0.0f, dstMat);
        }
        // 更新
        cvui::update();
        // 显示
        cv::imshow(windowName, windowMat);
        // esc键退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

工程模板:对应版本号v1.28.0

      对应版本号v1.28.0


原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/104891183

发布了251 篇原创文章 · 获赞 308 · 访问量 46万+

猜你喜欢

转载自blog.csdn.net/qq21497936/article/details/104891183