30.漫水填充算法(堆棧是什么蒋譬?/圖像維數/(A?B:C))--- OpenCV從零開始到圖像(人臉 + 物體)識別系列


本文作者:小嗷

微信公眾號: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)愉适,是一種確定多維數組中連接到給定節(jié)點的區(qū)域的算法犯助。(灰度圖是二維,彩色圖是三維)

灰度圖的二維:一般來說维咸,一維是高(行)剂买,一維是寬(列)。

即: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的真假租副,若是真就執(zhí)行B如是假就執(zhí)行C


2.1 漫水填充算法包含三個參數(自己寫算法的話):

開始節(jié)點坐慰、目標顏色和替換顏色。

該算法查找數組中的所有節(jié)點用僧,這些節(jié)點通過目標顏色的路徑連接到起始節(jié)點结胀,并將它們更改為替換顏色。有許多方法可以構造漫水填充算法责循,但它們都使用隊列或堆棧數據結構糟港,顯式或隱式地。

根據我們是否考慮連接在角落的節(jié)點院仿,我們有兩種變化:8路和4路秸抚。(即:核是3*3的正方形,還是自定義十字形)歹垫,取其中一個4路在演示剥汤。

一個隱式堆棧的(遞歸)漫水填充實現(對于一個二維數組)如下所示:

基于堆棧的遞歸實現(四【十字形】)的思路

  1. 首先。如果目標顏色等于替換顏色排惨。

  2. 如果節(jié)點的顏色不等于目標顏色秀姐,不處理。

  3. 將節(jié)點的顏色設置為替換顏色若贮。

  4. 執(zhí)行漫水填充(向節(jié)點南部的一步省有,目標顏色,替換顏色)谴麦。

  5. 執(zhí)行漫水填充(向節(jié)點以北一步蠢沿,目標顏色,替換顏色)匾效。

  6. 執(zhí)行漫水填充(在節(jié)點的西面一步舷蟀,目標顏色,替換顏色)。

  7. 執(zhí)行漫水填充(在節(jié)點的東邊一步野宜,目標顏色扫步,替換顏色)。

  8. 處理完數組匈子,退出

效果如下:(十字形)

image

即:東河胎、南、西虎敦、北游岳、

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

image

即:東、南其徙、西胚迫、北、東南唾那、西南访锻、西北、東北闹获、 8個方向

用給定的顏色填充連接的組件期犬。

2.2 堆棧

在計算機領域,堆棧是一個不容忽視的概念昌罩,堆棧是兩種數據結構哭懈。

堆棧都是一種數據項按序排列的數據結構,只能在一端(稱為棧頂(top))對數據項進行插入和刪除茎用。

要點:堆遣总,隊列優(yōu)先,先進先出(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    )   

參數詳解:

  • image:InputOutputArray類型的image, 輸入/輸出1通道或3通道意系,8位或浮點圖像,具體參數由之后的參數具體指明饺汹。

  • mask:InputOutputArray類型的mask蛔添,這是第二個版本的floodFill獨享的參數,表示操作掩模,首繁。它應該為單通道作郭、8位陨囊、長和寬上都比輸入圖像 image 大兩個像素點的圖像弦疮。第二個版本的floodFill需要使用以及更新掩膜,所以這個mask參數我們一定要將其準備好并填在此處蜘醋。需要注意的是胁塞,漫水填充不會填充掩膜mask的非零像素區(qū)域。例如压语,一個邊緣檢測算子的輸出可以用來作為掩膜啸罢,以防止填充到邊緣。同樣的胎食,也可以在多次的函數調用中使用同一個掩膜扰才,以保證填充的區(qū)域不會重疊。另外需要注意的是厕怜,掩膜mask會比需填充的圖像大衩匣,所以 mask 中與輸入圖像(x,y)像素點相對應的點的坐標為(x+1,y+1)。

  • seedPoint:Point類型的seedPoint粥航,漫水填充算法的起始點琅捏。

  • newVal:Scalar類型的newVal,像素點被染色的值递雀,即在重繪區(qū)域像素的新值柄延。

  • rect:Rect*類型的rect,有默認值0缀程,一個可選的參數搜吧,用于設置floodFill函數將要重繪區(qū)域的最小邊界矩形區(qū)域。

  • loDiff:Scalar類型的loDiff杨凑,有默認值Scalar( )滤奈,表示當前觀察像素值與其部件鄰域像素值或者待加入該部件的種子像素之間的亮度或顏色之負差(lower brightness/color difference)的最大值。

  • upDiff:Scalar類型的upDiff蠢甲,有默認值Scalar( )僵刮,表示當前觀察像素值與其部件鄰域像素值或者待加入該部件的種子像素之間的亮度或顏色之正差(lower brightness/color difference)的最大值据忘。

  • flags:int類型的flags,操作標志符搞糕,此參數包含三個部分勇吊,比較復雜,我們一起詳細看看窍仰。

低八位(第0~7位)用于控制算法的連通性汉规,可取4 (4為缺省值) 或者 8。如果設為4驹吮,表示填充算法只考慮當前像素水平方向和垂直方向的相鄰點针史;如果設為 8,除上述相鄰點外碟狞,還會包含對角線方向的相鄰點啄枕。

(請看上面第二部分的東南西北的解釋,哈哈哈)

高八位部分(16~23位)可以為0 或者如下兩種選項標識符的組合:

  • FLOODFILLFIXEDRANGE - 如果設置為這個標識符的話族沃,就會考慮當前像素與種子像素之間的差频祝,否則就考慮當前像素與其相鄰像素的差。也就是說脆淹,這個范圍是浮動的常空。

  • FLOODFILLMASKONLY - 如果設置為這個標識符的話,函數不會去填充改變原始圖像 (也就是忽略第三個參數newVal), 而是去填充掩模圖像(mask)盖溺。這個標識符只對第二個版本的floodFill有用漓糙,因第一個版本里面壓根就沒有mask參數。

中間八位部分烘嘱,上面關于高八位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′)的值是一個已知的像素鄰居屬于組件寄疏。也就是說是牢,要添加到所連接的組件中,像素的顏色/亮度應該足夠接近:

在浮動范圍內陕截,它的一個鄰居的顏色/亮度已經屬于連接組件驳棱。

在固定范圍內,種子點的顏色/亮度农曲。

使用這些函數可以將連接的組件標記為指定的顏色社搅,或者構建一個掩膜,然后提取輪廓乳规,或者將區(qū)域復制到另一個圖像形葬,等等。

簡單來說公式(灰度圖為例):

  • 固定范圍
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. 本人是抱著玩一玩的心態(tài),學習opencv(其實深度學習沒有外界說的這么高深轻姿,小嗷是白板犁珠,而且有工作在身并且于代碼無關)

  2. 大家可以把我的數學水平想象成初中水平,畢竟小嗷既不是代碼靠吃飯又不是靠數學吃飯互亮,畢業(yè)N年

  3. 寫文章主要是為了后人少走點彎路犁享,多交點朋友,一起學習

  4. 如果有好的圖像識別群拉我進去QQ:631821577

  5. 就我一個白板豹休,最后還是成的炊昆,你們別怕,慢慢來把

image

分享可以無數次威根,轉載成自己文章QQ郵箱通知一下凤巨,未經授權請勿轉載。

  • 郵箱:631821577@qq.com

  • QQ群:736854977

  • 有什么疑問公眾號提問洛搀,下班或者周六日回答敢茁,ths

推薦文章:

21.失真/低高通/振鈴效應/旁瓣泄漏效應/頻域濾波/圖像深度/頻帶/線性濾波源碼分析(數學篇) - OpenCV從零開始到圖像

感言

嗷嗷嗷~~~,喜歡就推薦一下好友留美。

代碼地址:

鏈接:

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

密碼:nq8r

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末彰檬,一起剝皮案震驚了整個濱河市伸刃,隨后出現的幾起案子,更是在濱河造成了極大的恐慌逢倍,老刑警劉巖奕枝,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異瓶堕,居然都是意外死亡隘道,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門郎笆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谭梗,“玉大人,你說我怎么就攤上這事宛蚓〖つ螅” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵凄吏,是天一觀的道長远舅。 經常有香客問我,道長痕钢,這世上最難降的妖魔是什么图柏? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任娇斩,我火速辦了婚禮割择,結果婚禮上,老公的妹妹穿的比我還像新娘充易。我一直安慰自己随抠,他們只是感情好裁着,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拱她,像睡著了一般二驰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秉沼,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天桶雀,我揣著相機與錄音,去河邊找鬼氧猬。 笑死背犯,一個胖子當著我的面吹牛,可吹牛的內容都是我干的盅抚。 我是一名探鬼主播漠魏,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妄均!你這毒婦竟也來了柱锹?” 一聲冷哼從身側響起哪自,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎禁熏,沒想到半個月后壤巷,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瞧毙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年胧华,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宙彪。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡矩动,死狀恐怖,靈堂內的尸體忽然破棺而出释漆,到底是詐尸還是另有隱情悲没,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布男图,位于F島的核電站示姿,受9級特大地震影響,放射性物質發(fā)生泄漏逊笆。R本人自食惡果不足惜栈戳,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望览露。 院中可真熱鬧荧琼,春花似錦、人聲如沸差牛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽偏化。三九已至,卻和暖如春镐侯,著一層夾襖步出監(jiān)牢的瞬間侦讨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工苟翻, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留韵卤,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓崇猫,卻偏偏與公主長得像沈条,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子诅炉,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內容