本文作者:小嗷
微信公眾號:aoxiaoji
吹比QQ群:736854977
簡介:
漫水填充(Flood fill)割岛,也稱為種子填充(seed fill)愉适,是一種確定多維數組中連接到給定節(jié)點的區(qū)域的算法犯助。(灰度圖是二維,彩色圖是三維)
灰度圖的二維:一般來說维咸,一維是高(行)剂买,一維是寬(列)。
即:char a[3][4] = 246癌蓖;
a為3*4(3行4列)的像素值為:246
彩色圖:多了一維是圖像深度
本文你會找到以下問題的答案:
漫水填充算法
灰度圖是二維瞬哼,彩色圖是三維
堆棧是什么?
for (;;)等于while(1)
(A?B:C)
A?B:C 這個運算是判斷A的真假租副,若是真就執(zhí)行B如是假就執(zhí)行C
2.1 漫水填充算法包含三個參數(自己寫算法的話):
開始節(jié)點坐慰、目標顏色和替換顏色。
該算法查找數組中的所有節(jié)點用僧,這些節(jié)點通過目標顏色的路徑連接到起始節(jié)點结胀,并將它們更改為替換顏色。有許多方法可以構造漫水填充算法责循,但它們都使用隊列或堆棧數據結構糟港,顯式或隱式地。
根據我們是否考慮連接在角落的節(jié)點院仿,我們有兩種變化:8路和4路秸抚。(即:核是3*3的正方形,還是自定義十字形)歹垫,取其中一個4路在演示剥汤。
一個隱式堆棧的(遞歸)漫水填充實現(對于一個二維數組)如下所示:
基于堆棧的遞歸實現(四【十字形】)的思路
首先。如果目標顏色等于替換顏色排惨。
如果節(jié)點的顏色不等于目標顏色秀姐,不處理。
將節(jié)點的顏色設置為替換顏色若贮。
執(zhí)行漫水填充(向節(jié)點南部的一步省有,目標顏色,替換顏色)谴麦。
執(zhí)行漫水填充(向節(jié)點以北一步蠢沿,目標顏色,替換顏色)匾效。
執(zhí)行漫水填充(在節(jié)點的西面一步舷蟀,目標顏色,替換顏色)。
執(zhí)行漫水填充(在節(jié)點的東邊一步野宜,目標顏色扫步,替換顏色)。
處理完數組匈子,退出
效果如下:(十字形)
即:東河胎、南、西虎敦、北游岳、
8路的效果如下:(核是3*3的正方形)
即:東、南其徙、西胚迫、北、東南唾那、西南访锻、西北、東北闹获、 8個方向
用給定的顏色填充連接的組件期犬。
2.2 堆棧
在計算機領域,堆棧是一個不容忽視的概念昌罩,堆棧是兩種數據結構哭懈。
堆棧都是一種數據項按序排列的數據結構,只能在一端(稱為棧頂(top))對數據項進行插入和刪除茎用。
要點:堆遣总,隊列優(yōu)先,先進先出(FIFO—first in first out) [1] 。棧轨功,先進后出(FILO—First-In/Last-Out)旭斥。
堆:
堆棧空間分配
棧(操作系統):由操作系統自動分配釋放 古涧,存放函數的參數值垂券,局部變量的值等。其操作方式類似于數據結構中的棧羡滑。
堆(操作系統): 一般由程序員分配釋放菇爪, 若程序員不釋放,程序結束時可能由OS回收柒昏,分配方式倒是類似于鏈表凳宙。
堆棧緩存方式
棧使用的是一級緩存, 他們通常都是被調用時處于存儲空間中职祷,調用完畢立即釋放氏涩。
堆則是存放在二級緩存中届囚,生命周期由虛擬機的垃圾回收算法來決定(并不是一旦成為孤兒對象就能被回收)。所以調用這些對象的速度要相對來得低一些是尖。
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)處的像素从绘,如果:
- 在灰度圖像和浮動范圍的情況下:
- 在灰度圖像和固定范圍的情況下:
- 在彩色圖像和浮動范圍的情況下:
- 在彩色圖像和固定范圍的情況下:
src(x′,y′)的值是一個已知的像素鄰居屬于組件寄疏。也就是說是牢,要添加到所連接的組件中,像素的顏色/亮度應該足夠接近:
在浮動范圍內陕截,它的一個鄰居的顏色/亮度已經屬于連接組件驳棱。
在固定范圍內,種子點的顏色/亮度农曲。
使用這些函數可以將連接的組件標記為指定的顏色社搅,或者構建一個掩膜,然后提取輪廓乳规,或者將區(qū)域復制到另一個圖像形葬,等等。
簡單來說公式(灰度圖為例):
- 固定范圍
(比如起點的像素點是200暮的,loDiff是20笙以,upDiff是50)
像素點必須要要在以下范圍內,才可以上色
即:180《像素點《250
- 浮動范圍
(比如臨近的像素點是200【已知的像素鄰居】青扔,loDiff是20源织,upDiff是50)
像素點必須要要在以下范圍內翩伪,才可以上色
即:已知的像素鄰居的值 - 20 《像素點《 已知的像素鄰居的值 - 50
代碼如下:
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}
原圖:
效果圖:
本人是抱著玩一玩的心態(tài),學習opencv(其實深度學習沒有外界說的這么高深轻姿,小嗷是白板犁珠,而且有工作在身并且于代碼無關)
大家可以把我的數學水平想象成初中水平,畢竟小嗷既不是代碼靠吃飯又不是靠數學吃飯互亮,畢業(yè)N年
寫文章主要是為了后人少走點彎路犁享,多交點朋友,一起學習
如果有好的圖像識別群拉我進去QQ:631821577
就我一個白板豹休,最后還是成的炊昆,你們別怕,慢慢來把
分享可以無數次威根,轉載成自己文章QQ郵箱通知一下凤巨,未經授權請勿轉載。
QQ群:736854977
有什么疑問公眾號提問洛搀,下班或者周六日回答敢茁,ths
推薦文章:
21.失真/低高通/振鈴效應/旁瓣泄漏效應/頻域濾波/圖像深度/頻帶/線性濾波源碼分析(數學篇) - OpenCV從零開始到圖像
感言
嗷嗷嗷~~~,喜歡就推薦一下好友留美。
代碼地址:
鏈接:
https://pan.baidu.com/s/1RESLgnXlwZ74E-Eh1yRaXQ
密碼:nq8r