OpenCV每日函数 杂项图像转换模块 (4) floodFill函数

一、概述

        用给定的颜色填充连接的组件。函数 cv::floodFill 从种子点开始用指定的颜色填充一个连通分量。 连通性由相邻像素的颜色/亮度接近度决定。 如果满足以下条件,则 (x,y) 处的像素被认为属于重绘域:

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

        在浮动范围的情况下,已经属于连接组件的相邻组件之一的颜色/亮度。

在固定范围的情况下,种子点的颜色/亮度。

使用此函数可以用指定的颜色就地标记连接的组件,或者构建一个蒙版然后提取轮廓,或者将该区域复制到另一个图像,等等。

二、floodFill函数

1、函数原型

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

2、参数详解

image 输入/输出 1 或 3 通道、8 位或浮点图像。 它由函数修改,除非在函数的第二个变体中设置了 FLOODFILL_MASK_ONLY 标志。 请参阅下面的详细信息。
mask ​操作掩码应为单通道 8 位图像,比图像宽 2 像素,高 2 像素。 如果传递了一个空的垫子,它将自动创建。 由于这既是输入参数又是输出参数,因此您必须负责对其进行初始化。 填充不能跨越输入掩码中的非零像素。 例如,边缘检测器输出可用作掩码以停止在边缘填充。 在输出时,对应于图像中填充像素的掩码中的像素设置为 1 或标志中的指定值,如下所述。 此外,该函数用 1 填充掩码的边框以简化内部处理。 因此,可以在对函数的多次调用中使用相同的掩码,以确保填充区域不重叠。
seedPoint 种子点
newVal 重绘域像素的新值。
loDiff 当前观察到的像素与其属于该组件的相邻像素之一或添加到该组件的种子像素之间的最大较低亮度/色差。
upDiff 当前观察到的像素与其属于该组件的相邻像素之一或添加到该组件的种子像素之间的最大上亮度/色差。
rect 由函数设置为重绘域的最小边界矩形的可选输出参数。
flags ​操作标志。 前 8 位包含连接值。 默认值 4 表示仅考虑四个最近邻像素(共享边缘的像素)。 连接值为 8 意味着将考虑八个最近邻像素(共享一个角的像素)。 接下来的 8 位 (8-16) 包含一个介于 1 和 255 之间的值,用于填充掩码(默认值为 1)。 例如,4 | ( 255 << 8 ) 将考虑 4 个最近的邻居并使用 255 的值填充掩码。以下附加选项占用更高的位,因此可以使用逐位或 (|) 进一步与连接性和掩码填充值组合, 请参阅 FloodFillFlags。

        由于掩码大于填充后的图像,因此图像中的一个像素 (x,y) 对应于掩码中的像素 (x+1,y+1)。

三、OpenCV源码

1、源码路径

opencv\modules\imgproc\src\floodfill.cpp

2、源码代码

int cv::floodFill( InputOutputArray _image, InputOutputArray _mask,
                  Point seedPoint, Scalar newVal, Rect* rect,
                  Scalar loDiff, Scalar upDiff, int flags )
{
    CV_INSTRUMENT_REGION();

    ConnectedComp comp;
    std::vector<FFillSegment> buffer;

    if( rect )
        *rect = Rect();

    int i;
    union {
        uchar b[4];
        int i[4];
        float f[4];
        double _[4];
    } nv_buf;
    nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0;

    struct { Vec3b b; Vec3i i; Vec3f f; } ld_buf, ud_buf;
    Mat img = _image.getMat(), mask;
    if( !_mask.empty() )
        mask = _mask.getMat();
    Size size = img.size();

    int type = img.type();
    int depth = img.depth();
    int cn = img.channels();

    if ( (cn != 1) && (cn != 3) )
    {
        CV_Error( CV_StsBadArg, "Number of channels in input image must be 1 or 3" );
    }

    const int connectivity = flags & 255;
    if( connectivity != 0 && connectivity != 4 && connectivity != 8 )
        CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8" );

    bool is_simple = mask.empty() && (flags & FLOODFILL_MASK_ONLY) == 0;

    for( i = 0; i < cn; i++ )
    {
        if( loDiff[i] < 0 || upDiff[i] < 0 )
            CV_Error( CV_StsBadArg, "lo_diff and up_diff must be non-negative" );
        is_simple = is_simple && fabs(loDiff[i]) < DBL_EPSILON && fabs(upDiff[i]) < DBL_EPSILON;
    }

    if( (unsigned)seedPoint.x >= (unsigned)size.width ||
       (unsigned)seedPoint.y >= (unsigned)size.height )
        CV_Error( CV_StsOutOfRange, "Seed point is outside of image" );

    scalarToRawData( newVal, &nv_buf, type, 0);
    size_t buffer_size = MAX( size.width, size.height ) * 2;
    buffer.resize( buffer_size );

    if( is_simple )
    {
        size_t elem_size = img.elemSize();
        const uchar* seed_ptr = img.ptr(seedPoint.y) + elem_size*seedPoint.x;

        size_t k = 0;
        for(; k < elem_size; k++)
            if (seed_ptr[k] != nv_buf.b[k])
                break;

        if( k != elem_size )
        {
            if( type == CV_8UC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.b[0], &comp, flags, &buffer);
            else if( type == CV_8UC3 )
                floodFill_CnIR(img, seedPoint, Vec3b(nv_buf.b), &comp, flags, &buffer);
            else if( type == CV_32SC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.i[0], &comp, flags, &buffer);
            else if( type == CV_32FC1 )
                floodFill_CnIR(img, seedPoint, nv_buf.f[0], &comp, flags, &buffer);
            else if( type == CV_32SC3 )
                floodFill_CnIR(img, seedPoint, Vec3i(nv_buf.i), &comp, flags, &buffer);
            else if( type == CV_32FC3 )
                floodFill_CnIR(img, seedPoint, Vec3f(nv_buf.f), &comp, flags, &buffer);
            else
                CV_Error( CV_StsUnsupportedFormat, "" );
            if( rect )
                *rect = comp.rect;
            return comp.area;
        }
    }

    if( mask.empty() )
    {
        Mat tempMask( size.height + 2, size.width + 2, CV_8UC1 );
        tempMask.setTo(Scalar::all(0));
        mask = tempMask;
    }
    else
    {
        CV_Assert( mask.rows == size.height+2 && mask.cols == size.width+2 );
        CV_Assert( mask.type() == CV_8U );
    }

    memset( mask.ptr(), 1, mask.cols );
    memset( mask.ptr(mask.rows-1), 1, mask.cols );

    for( i = 1; i <= size.height; i++ )
    {
        mask.at<uchar>(i, 0) = mask.at<uchar>(i, mask.cols-1) = (uchar)1;
    }

    if( depth == CV_8U )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.b[i] = saturate_cast<uchar>(cvFloor(loDiff[i]));
            ud_buf.b[i] = saturate_cast<uchar>(cvFloor(upDiff[i]));
        }
    else if( depth == CV_32S )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.i[i] = cvFloor(loDiff[i]);
            ud_buf.i[i] = cvFloor(upDiff[i]);
        }
    else if( depth == CV_32F )
        for( i = 0; i < cn; i++ )
        {
            ld_buf.f[i] = (float)loDiff[i];
            ud_buf.f[i] = (float)upDiff[i];
        }
    else
        CV_Error( CV_StsUnsupportedFormat, "" );

    uchar newMaskVal = (uchar)((flags & 0xff00) == 0 ? 1 : ((flags >> 8) & 255));

    if( type == CV_8UC1 )
        floodFillGrad_CnIR<uchar, uchar, int, Diff8uC1>(
                img, mask, seedPoint, nv_buf.b[0], newMaskVal,
                Diff8uC1(ld_buf.b[0], ud_buf.b[0]),
                &comp, flags, &buffer);
    else if( type == CV_8UC3 )
        floodFillGrad_CnIR<Vec3b, uchar, Vec3i, Diff8uC3>(
                img, mask, seedPoint, Vec3b(nv_buf.b), newMaskVal,
                Diff8uC3(ld_buf.b, ud_buf.b),
                &comp, flags, &buffer);
    else if( type == CV_32SC1 )
        floodFillGrad_CnIR<int, uchar, int, Diff32sC1>(
                img, mask, seedPoint, nv_buf.i[0], newMaskVal,
                Diff32sC1(ld_buf.i[0], ud_buf.i[0]),
                &comp, flags, &buffer);
    else if( type == CV_32SC3 )
        floodFillGrad_CnIR<Vec3i, uchar, Vec3i, Diff32sC3>(
                img, mask, seedPoint, Vec3i(nv_buf.i), newMaskVal,
                Diff32sC3(ld_buf.i, ud_buf.i),
                &comp, flags, &buffer);
    else if( type == CV_32FC1 )
        floodFillGrad_CnIR<float, uchar, float, Diff32fC1>(
                img, mask, seedPoint, nv_buf.f[0], newMaskVal,
                Diff32fC1(ld_buf.f[0], ud_buf.f[0]),
                &comp, flags, &buffer);
    else if( type == CV_32FC3 )
        floodFillGrad_CnIR<Vec3f, uchar, Vec3f, Diff32fC3>(
                img, mask, seedPoint, Vec3f(nv_buf.f), newMaskVal,
                Diff32fC3(ld_buf.f, ud_buf.f),
                &comp, flags, &buffer);
    else
        CV_Error(CV_StsUnsupportedFormat, "");

    if( rect )
        *rect = comp.rect;
    return comp.area;
}

四、效果图像示例

        由于彩色图像需要满足三个通道颜色均再上下限范围内,所以使用灰色图像演示效果。

1、效果示例1

2、效果示例2

原图

扫描二维码关注公众号,回复: 14274706 查看本文章

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/125279725
今日推荐