图像处理读书

【图像处理】OpenCV系列二十四 --- 漫水填充(flood

2019-05-07  本文已影响34人  307656af5a04

上一节我们学习了用如何用findContours函数对图像查找轮廓,以及用drawContours函数对查找的轮廓进行绘制,相信大家学习之后,对图像轮廓已经有了基本的认识,本节呢,我们对漫水填充算法(floodFill)进行详细的理解!

一、漫水填充函数详解

1、函数原型

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

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

2、函数功能
用指定的颜色从种子点(参数中 seedPoint 为种子点)开始填充连通的元素;

在以下情况下,(x,y) 处的像素被视为重新绘制的区域:

(1)在灰度图像和浮动范围的情况下

在灰度图像和浮动范围的情况下

(2)在灰度图像和固定范围的情况下

在灰度图像和固定范围的情况下

(3)在彩色图像和浮动范围的情况下

在彩色图像和浮动范围的情况下

(4)在彩色图像和固定范围的情况下

在彩色图像和固定范围的情况下

其中 src(x′,y′) 已知属于该元素的像素邻域之一的值 。也就是说,要添加到连接的元素中,像素的颜色/亮度应该足够接近于:

(1)在浮动范围内,已属于已连接元素的邻域之一的颜色/亮度;
(2)在固定范围内种子点的颜色/亮度。

使用这些函数标记具有指定颜色的连接要素,或构建掩码,然后提取轮廓,或将区域复制到其他图像,以此类推。

3、参数详解

floodfill填充模式
1、FLOODFILL_FIXED_RANGE ,如果设置,则考虑当前像素与种子像素之间的差异;否则,考虑相邻像素之间的差异(即范围是浮动的)。
2、FLOODFILL_MASK_ONLY ,如果设置,函数不会更改图像(忽略newVal),并且只使用上面描述的标志位8-16中指定的值填充掩码;此选项仅在具有掩码参数的函数变体中才有意义。

二、综合实例

1、实验案例

#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>
using namespace cv;
using namespace std;

static void help()
{
    cout << "键盘: \n"
            "\tESC - 退出程序\n"
            "\tc - 切换彩色与灰度图像\n"
            "\tm - 切换掩码图像\n"
            "\tr - 恢复为原始图像\n"
            "\ts - 不使用\n"
            "\tf - 绝对梯度\n"
            "\tg - 相对梯度\n"
            "\t4 - 4连通域\n"
            "\t8 - 8连通域\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);
                
                
    // 随机取值&255,使得值在0-255之间
    int b = (unsigned)theRNG() & 255;
    int g = (unsigned)theRNG() & 255;
    int r = (unsigned)theRNG() & 255;
    
    // 图像的颜色,彩色还是灰色
    Scalar newVal = isColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114);
    
    // 彩色图像还是灰度图像
    Mat dst = isColor ? image : gray;
    
    int area;
    Rect ccomp;
    
    // 是否使用掩码图像
    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 )
{
    // 载入图像
    image0 = imread("lena.png", 1);
    
    // 判断图像是否为空
    if( image0.empty() )
    {
        cout << "Image empty !\n";
        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;
}

2、实验结果

辅助信息 原图 效果图像

我是奕双,现在已经毕业将近两年了,从大学开始学编程,期间学习了C语言编程,C++语言编程,Win32编程,MFC编程,毕业之后进入一家图像处理相关领域的公司,掌握了用OpenCV对图像进行处理,如果大家对相关领域感兴趣的话,可以关注我,我这边会为大家进行解答哦!如果大家需要相关学习资料的话,可以私聊我哦!

上一篇下一篇

猜你喜欢

热点阅读