30.漫水填充算法(堆栈是什么?/图像维数/(A?B:C))--

2018-06-27  本文已影响168人  小嗷_a2e2

本文作者:小嗷

微信公众号:aoxiaoji

吹比QQ群:736854977

微信链接:https://mp.weixin.qq.com/s?__biz=MzU1MTgxNjQyMg==&mid=2247483885&idx=1&sn=bfc8647575f90b06356eb108486dd1a4&chksm=fb8adc64ccfd55725a253d6cf2c3c444a2b7096d2f39b19be9859cd8f4484af4b34c086d5c8e#rd


image image image

简介:

漫水填充(Flood fill),也称为种子填充(seed fill),是一种确定多维数组中连接到给定节点的区域的算法。(灰度图是二维,彩色图是三维)

灰度图的二维:一般来说,一维是高(行),一维是宽(列)。

即:char a[3][4] = 246;

a为3*4(3行4列)的像素值为:246

彩色图:多了一维是图像深度


本文你会找到以下问题的答案:

  1. 漫水填充算法

  2. 灰度图是二维,彩色图是三维

  3. 堆栈是什么?

  4. for (;;)等于while(1)

  5. (A?B:C)

A?B:C 这个运算是判断A的真假,若是真就执行B如是假就执行C


2.1 漫水填充算法包含三个参数(自己写算法的话):

开始节点、目标颜色和替换颜色。

该算法查找数组中的所有节点,这些节点通过目标颜色的路径连接到起始节点,并将它们更改为替换颜色。有许多方法可以构造漫水填充算法,但它们都使用队列或堆栈数据结构,显式或隐式地。

根据我们是否考虑连接在角落的节点,我们有两种变化:8路和4路。(即:核是3*3的正方形,还是自定义十字形),取其中一个4路在演示。

一个隐式堆栈的(递归)漫水填充实现(对于一个二维数组)如下所示:

基于堆栈的递归实现(四【十字形】)的思路

  1. 首先。如果目标颜色等于替换颜色。

  2. 如果节点的颜色不等于目标颜色,不处理。

  3. 将节点的颜色设置为替换颜色。

  4. 执行漫水填充(向节点南部的一步,目标颜色,替换颜色)。

  5. 执行漫水填充(向节点以北一步,目标颜色,替换颜色)。

  6. 执行漫水填充(在节点的西面一步,目标颜色,替换颜色)。

  7. 执行漫水填充(在节点的东边一步,目标颜色,替换颜色)。

  8. 处理完数组,退出

效果如下:(十字形)

image

即:东、南、西、北、

8路的效果如下:(核是3*3的正方形)

image

即:东、南、西、北、东南、西南、西北、东北、 8个方向

用给定的颜色填充连接的组件。

2.2 堆栈

在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。

堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。

要点:堆,队列优先,先进先出(FIFO—first in first out) [1] 。栈,先进后出(FILO—First-In/Last-Out)。

堆:

image

堆栈空间分配

栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。

堆栈缓存方式

栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放。

堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

image
1 cv::floodFill  (   InputOutputArray    image,2        InputOutputArray    mask,3        Point   seedPoint,4        Scalar      newVal,5        Rect *      rect = 0,6        Scalar      loDiff = Scalar(),7        Scalar      upDiff = Scalar(),8        int     flags = 4 9    )   

参数详解:

低八位(第0~7位)用于控制算法的连通性,可取4 (4为缺省值) 或者 8。如果设为4,表示填充算法只考虑当前像素水平方向和垂直方向的相邻点;如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。

(请看上面第二部分的东南西北的解释,哈哈哈)

高八位部分(16~23位)可以为0 或者如下两种选项标识符的组合:

中间八位部分,上面关于高八位FLOODFILLMASKONLY标识符中已经说的很明显,需要输入符合要求的掩码。Floodfill的flags参数的中间八位的值就是用于指定填充掩码图像的值的。但如果flags中间八位的值为0,则掩码会用1来填充

而所有flags可以用or操作符连接起来,即“|”。例如,如果想用8邻域填充,并填充固定像素值范围,填充掩码而不是填充源图像,以及设填充值为38,那么输入的参数是这样:

1flags=8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (38<<8)

接着就是理论运用:

功能cv::floodFill从种子点开始,以指定的颜色填充连接的组件。连接性是由相邻像素的颜色/亮度接近决定的。在(x,y)处的像素,如果:

image.gif image image image

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

在浮动范围内,它的一个邻居的颜色/亮度已经属于连接组件。

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

使用这些函数可以将连接的组件标记为指定的颜色,或者构建一个掩膜,然后提取轮廓,或者将区域复制到另一个图像,等等。

简单来说公式(灰度图为例):

image

(比如起点的像素点是200,loDiff是20,upDiff是50)

像素点必须要要在以下范围内,才可以上色

即:180《像素点《250

image

(比如临近的像素点是200【已知的像素邻居】,loDiff是20,upDiff是50)

像素点必须要要在以下范围内,才可以上色

即:已知的像素邻居的值 - 20 《像素点《 已知的像素邻居的值 - 50

image

代码如下:

  1//videoio:视频流的输入和输入  2//imgcodecs:用于图像文件的载入(imread)和输出(imwrite)  3//imgproc:图像处理模块  4//highgui:高层图形用户界面(GUI),包括媒体输入输出、视频捕捉、图像交互界面接口、图像和视频的编码解码等  5#include "opencv2/imgproc.hpp"  6#include "opencv2/imgcodecs.hpp"  7#include "opencv2/videoio.hpp"  8#include "opencv2/highgui.hpp"  9#include <iostream> 10using namespace cv; 11using namespace std; 12static void help() 13{ 14    cout << "\nThis program demonstrated the floodFill() function\n" 15            "Call:\n" 16            "./ffilldemo [image_name -- Default: ../data/fruits.jpg]\n" << endl; 17    cout << "Hot keys: \n" 18            "\tESC - quit the program\n" 19            "\tc - switch color/grayscale mode\n" 20            "\tm - switch mask mode\n" 21            "\tr - restore the original image\n" 22            "\ts - use null-range floodfill\n" 23            "\tf - use gradient floodfill with fixed(absolute) range\n" 24            "\tg - use gradient floodfill with floating(relative) range\n" 25            "\t4 - use 4-connectivity mode\n" 26            "\t8 - use 8-connectivity mode\n" << endl; 27} 28Mat image0, image, gray, mask; 29int ffillMode = 1; 30int loDiff = 20, upDiff = 20; 31int connectivity = 4; 32int isColor = true; 33bool useMask = false; 34int newMaskVal = 255; 35static void onMouse( int event, int x, int y, int, void* ) 36{ 37    if( event != EVENT_LBUTTONDOWN ) 38        return; 39    Point seed = Point(x,y); 40    int lo = ffillMode == 0 ? 0 : loDiff; 41    int up = ffillMode == 0 ? 0 : upDiff; 42    int flags = connectivity + (newMaskVal << 8) + 43                (ffillMode == 1 ? FLOODFILL_FIXED_RANGE : 0); 44    int b = (unsigned)theRNG() & 255; 45    int g = (unsigned)theRNG() & 255; 46    int r = (unsigned)theRNG() & 255; 47    Rect ccomp; 48    Scalar newVal = isColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114); 49    Mat dst = isColor ? image : gray; 50    int area; 51    if( useMask ) 52    { 53        threshold(mask, mask, 1, 128, THRESH_BINARY); 54        area = floodFill(dst, mask, seed, newVal, &ccomp, Scalar(lo, lo, lo), 55                  Scalar(up, up, up), flags); 56        imshow( "mask", mask ); 57    } 58    else 59    { 60        area = floodFill(dst, seed, newVal, &ccomp, Scalar(lo, lo, lo), 61                  Scalar(up, up, up), flags); 62    } 63    imshow("image", dst); 64    cout << area << " pixels were repainted\n"; 65} 66int main( int argc, char** argv ) 67{ 68    cv::CommandLineParser parser (argc, argv, 69        "{help h | | show help message}{@image|../data/fruits.jpg| input image}" 70    ); 71    if (parser.has("help")) 72    { 73        parser.printMessage(); 74        return 0; 75    } 76    string filename = parser.get<string>("@image"); 77    image0 = imread(filename, 1); 78    if( image0.empty() ) 79    { 80        cout << "Image empty\n"; 81        parser.printMessage(); 82        return 0; 83    } 84    help(); 85    image0.copyTo(image); 86    cvtColor(image0, gray, COLOR_BGR2GRAY); 87    mask.create(image0.rows+2, image0.cols+2, CV_8UC1); 88    namedWindow( "image", 0 ); 89    createTrackbar( "lo_diff", "image", &loDiff, 255, 0 ); 90    createTrackbar( "up_diff", "image", &upDiff, 255, 0 ); 91    setMouseCallback( "image", onMouse, 0 ); 92    for(;;) 93    { 94        imshow("image", isColor ? image : gray); 95        char c = (char)waitKey(0); 96        if( c == 27 ) 97        { 98            cout << "Exiting ...\n"; 99            break;100        }101        switch( c )102        {103        case 'c':104            if( isColor )105            {106                cout << "Grayscale mode is set\n";107                cvtColor(image0, gray, COLOR_BGR2GRAY);108                mask = Scalar::all(0);109                isColor = false;110            }111            else112            {113                cout << "Color mode is set\n";114                image0.copyTo(image);115                mask = Scalar::all(0);116                isColor = true;117            }118            break;119        case 'm':120            if( useMask )121            {122                destroyWindow( "mask" );123                useMask = false;124            }125            else126            {127                namedWindow( "mask", 0 );128                mask = Scalar::all(0);129                imshow("mask", mask);130                useMask = true;131            }132            break;133        case 'r':134            cout << "Original image is restored\n";135            image0.copyTo(image);136            cvtColor(image, gray, COLOR_BGR2GRAY);137            mask = Scalar::all(0);138            break;139        case 's':140            cout << "Simple floodfill mode is set\n";141            ffillMode = 0;142            break;143        case 'f':144            cout << "Fixed Range floodfill mode is set\n";145            ffillMode = 1;146            break;147        case 'g':148            cout << "Gradient (floating range) floodfill mode is set\n";149            ffillMode = 2;150            break;151        case '4':152            cout << "4-connectivity mode is set\n";153            connectivity = 4;154            break;155        case '8':156            cout << "8-connectivity mode is set\n";157            connectivity = 8;158            break;159        }160    }161    return 0;162}

原图:

image

效果图:

image image
  1. 本人是抱着玩一玩的心态,学习opencv(其实深度学习没有外界说的这么高深,小嗷是白板,而且有工作在身并且于代码无关)

  2. 大家可以把我的数学水平想象成初中水平,毕竟小嗷既不是代码靠吃饭又不是靠数学吃饭,毕业N年

  3. 写文章主要是为了后人少走点弯路,多交点朋友,一起学习

  4. 如果有好的图像识别群拉我进去QQ:631821577

  5. 就我一个白板,最后还是成的,你们别怕,慢慢来把

image

分享可以无数次,转载成自己文章QQ邮箱通知一下,未经授权请勿转载。

推荐文章:

21.失真/低高通/振铃效应/旁瓣泄漏效应/频域滤波/图像深度/频带/线性滤波源码分析(数学篇) - OpenCV从零开始到图像

感言

嗷嗷嗷~~~,喜欢就推荐一下好友。

代码地址:

链接:

https://pan.baidu.com/s/1RESLgnXlwZ74E-Eh1yRaXQ

密码:nq8r

上一篇下一篇

猜你喜欢

热点阅读