漫水填充算法floodFill
一、floodFill()函數
1.1 floodFill()函數各參數詳解
intfloodFill(InputOutputArray image,Point seedPoint,Scalar newVal,Rect* rect = 0,Scalar loDiff= Scalar(), Scalar upDiff = Scalar(), int flags = 4)
intfloodFill(InputOutputArray image,InputOutputArray mask,Point seedPoint,Scalar newVal,Rect* rect = 0,Scalar loDiff=Scalar(),Scalar upDiff = Scalar(), int flags = 4)
參數詳解。除了第二個參數外,其他的參數都是共用的。
Ø 第一個參數,InputOutputArray類型的image, 輸入/輸出1通道或3通道,8位或浮點圖像,具體參數由之後的參數具體指明。
Ø 第二個參數, InputOutputArray類型的mask,這是第二個版本的floodFill獨享的參數,表示操作掩模,。它應該為單通道、8位、長和寬上都比輸入圖像 image 大兩個像素點的圖像。第二個版本的floodFill需要使用以及更新掩膜,是以這個mask參數我們一定要将其準備好并填在此處。需要注意的是,漫水填充不會填充掩膜mask的非零像素區域。例如,一個邊緣檢測算子的輸出可以用來作為掩膜,以防止填充到邊緣。同樣的,也可以在多次的函數調用中使用同一個掩膜,以保證填充的區域不會重疊。另外需要注意的是,掩膜mask會比需填充的圖像大,是以 mask 中與輸入圖像(x,y)像素點相對應的點的坐标為(x+1,y+1)。
Ø 第三個參數,Point類型的seedPoint,漫水填充算法的起始點。
Ø 第四個參數,Scalar類型的newVal,像素點被染色的值,即在重繪區域像素的新值。
Ø 第五個參數,Rect*類型的rect,有預設值0,一個可選的參數,用于設定floodFill函數将要重繪區域的最小邊界矩形區域。
Ø 第六個參數,Scalar類型的loDiff,有預設值Scalar( ),表示目前觀察像素值與其部件鄰域像素值或者待加入該部件的種子像素之間的亮度或顔色之負差(lowerbrightness/color difference)的最大值。
Ø 第七個參數,Scalar類型的upDiff,有預設值Scalar( ),表示目前觀察像素值與其部件鄰域像素值或者待加入該部件的種子像素之間的亮度或顔色之正差(lowerbrightness/color difference)的最大值。
Ø 第八個參數,int類型的flags,操作标志符,此參數包含三個部分,比較複雜,我們一起詳細看看。
低八位(第0~7位)用于控制算法的連通性,可取4 (4為預設值) 或者 8。如果設為4,表示填充算法隻考慮目前像素水準方向和垂直方向的相鄰點;如果設為 8,除上述相鄰點外,還會包含對角線方向的相鄰點。
高八位部分(16~23位)可以為0 或者如下兩種選項辨別符的組合:
FLOODFILL_FIXED_RANGE- 如果設定為這個辨別符的話,就會考慮目前像素與種子像素之間的差,否則就考慮目前像素與其相鄰像素的差。也就是說,這個範圍是浮動的。
FLOODFILL_MASK_ONLY -如果設定為這個辨別符的話,函數不會去填充改變原始圖像 (也就是忽略第三個參數newVal), 而是去填充掩模圖像(mask)。這個辨別符隻對第二個版本的floodFill有用,因第一個版本裡面壓根就沒有mask參數。
中間八位部分,上面關于高八位FLOODFILL_MASK_ONLY辨別符中已經說的很明顯,需要輸入符合要求的掩碼。Floodfill的flags參數的中間八位的值就是用于指定填充掩碼圖像的值的。但如果flags中間八位的值為0,則掩碼會用1來填充。
而所有flags可以用or操作符連接配接起來,即“|”。例如,如果想用8鄰域填充,并填充固定像素值範圍,填充掩碼而不是填充源圖像,以及設填充值為38,那麼輸入的參數是這樣:
flags = 8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (38 << 8)
1.2調用示例
Mat ScrImage;
ScrImage = imread("E:\\1TJQ\\Opencv\\Images\\image1.jpg");
floodFill(ScrImage, Point(50,100), Scalar(50, 100, 255));
imshow("【漫水填充算法】", ScrImage);
二、SetMouseCallback()函數
2.1 SetMouseCallback()函數各參數詳解
void setMouseCallback(conststring& winname,MouseCallback onMouse, void* userdata = 0)
Ø 第一個參數,const string&類型的winname,為視窗的名字。
Ø 第二個參數,MouseCallback類型的onMouse,指定視窗裡每次滑鼠時間發生的時候,被調用的函數指針。這個函數的原型應該為voidFoo(int event,int x, int y, int flags, void* param);其中event是 CV_EVENT_*變量之一, x和y是滑鼠指針在圖像坐标系的坐标(不是視窗坐标系), flags是CV_EVENT_FLAG的組合, param是使用者定義的傳遞到cvSetMouseCallback函數調用的參數。
Ø 第三個參數,void*類型的userdata,使用者定義的傳遞到回調函數的參數,有預設值0
2.2調用示例
Mat ScrImage;
ScrImage = imread("E:\\1TJQ\\Opencv\\Images\\image1.jpg");
//點選滑鼠,用回調函數處理圖檔
setMouseCallback("效果圖", onMouse, 0);
三、完整程式
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
//-----------------------------------【命名空間聲明部分】---------------------------------------
// 描述:包含程式所使用的命名空間
//-----------------------------------------------------------------------------------------------
using namespacecv;
using namespacestd;
//-----------------------------------【全局變量聲明部分】--------------------------------------
// 描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
Matg_srcImage, g_dstImage, g_grayImage, g_maskImage;//定義原始圖、目标圖、灰階圖、掩模圖
intg_nFillMode = 1;//漫水填充的模式
intg_nLowDifference = 20, g_nUpDifference = 20;//負差最大值、正差最大值
intg_nConnectivity = 4;//表示floodFill函數辨別符低八位的連通值
intg_bIsColor = true;//是否為彩色圖的辨別符布爾值
boolg_bUseMask = false;//是否顯示掩膜視窗的布爾值
intg_nNewMaskVal = 255;//新的重新繪制的像素值
//-----------------------------------【ShowHelpText( )函數】----------------------------------
// 描述:輸出一些幫助資訊
//----------------------------------------------------------------------------------------------
static voidShowHelpText()
{
//輸出一些幫助資訊
printf("\n\n\n\t歡迎來到漫水填充示例程式~\n\n");
printf("\n\n\t按鍵操作說明: \n\n"
"\t\t滑鼠點選圖中區域-進行漫水填充操作\n"
"\t\t鍵盤按鍵【ESC】-退出程式\n"
"\t\t鍵盤按鍵【1】- 切換彩色圖/灰階圖模式\n"
"\t\t鍵盤按鍵【2】-顯示/隐藏掩膜視窗\n"
"\t\t鍵盤按鍵【3】-恢複原始圖像\n"
"\t\t鍵盤按鍵【4】-使用空範圍的漫水填充\n"
"\t\t鍵盤按鍵【5】-使用漸變、固定範圍的漫水填充\n"
"\t\t鍵盤按鍵【6】-使用漸變、浮動範圍的漫水填充\n"
"\t\t鍵盤按鍵【7】-操作标志符的低八位使用4位的連接配接模式\n"
"\t\t鍵盤按鍵【8】-操作标志符的低八位使用8位的連接配接模式\n"
"\n\n\t\t\t\t\t\t\t\t by淺墨\n\n\n"
);
}
//-----------------------------------【onMouse( )函數】--------------------------------------
// 描述:滑鼠消息onMouse回調函數
//---------------------------------------------------------------------------------------------
static voidonMouse(intevent,intx, int y, int,void*)
{
// 若滑鼠左鍵沒有按下,便傳回
if (event !=CV_EVENT_LBUTTONDOWN)
return;
//-------------------【<1>調用floodFill函數之前的參數準備部分】---------------
Point seed = Point(x, y);
intLowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;//空範圍的漫水填充,此值設為0,否則設為全局的g_nLowDifference
intUpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;//空範圍的漫水填充,此值設為0,否則設為全局的g_nUpDifference
int flags =g_nConnectivity + (g_nNewMaskVal << 8) +
(g_nFillMode ==1 ? CV_FLOODFILL_FIXED_RANGE : 0);//辨別符的0~7位為g_nConnectivity,8~15位為g_nNewMaskVal左移8位的值,16~23位為CV_FLOODFILL_FIXED_RANGE或者0。
//随機生成bgr值
int b = (unsigned)theRNG()& 255;//随機傳回一個0~255之間的值
int g = (unsigned)theRNG()& 255;//随機傳回一個0~255之間的值
int r = (unsigned)theRNG()& 255;//随機傳回一個0~255之間的值
Rect ccomp;//定義重繪區域的最小邊界矩形區域
Scalar newVal = g_bIsColor ? Scalar(b, g,r) : Scalar(r*0.299 + g*0.587 + b*0.114);//在重繪區域像素的新值,若是彩色圖模式,取Scalar(b, g, r);若是灰階圖模式,取Scalar(r*0.299 +g*0.587 + b*0.114)
Mat dst = g_bIsColor ? g_dstImage : g_grayImage;//目标圖的指派
int area;
//--------------------【<2>正式調用floodFill函數】-----------------------------
if(g_bUseMask)
{
threshold(g_maskImage,g_maskImage, 1, 128, CV_THRESH_BINARY);
area =floodFill(dst, g_maskImage, seed, newVal, &ccomp, Scalar(LowDifference,LowDifference, LowDifference),
Scalar(UpDifference, UpDifference, UpDifference), flags);
imshow("mask", g_maskImage);
}
else
{
area =floodFill(dst, seed, newVal, &ccomp, Scalar(LowDifference,LowDifference, LowDifference),
Scalar(UpDifference, UpDifference, UpDifference), flags);
}
imshow("效果圖", dst);
cout << area<< " 個像素被重繪\n";
}
//-----------------------------------【main( )函數】--------------------------------------------
// 描述:控制台應用程式的入口函數,我們的程式從這裡開始
//-----------------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
//改變console字型顔色
system("color 2F");
//載入原圖
g_srcImage =imread("E:\\1TJQ\\Opencv\\Images\\image1.jpg");
if(!g_srcImage.data) { printf("Oh,no,讀取圖檔image0錯誤~! \n"); return false; }
//顯示幫助文字
ShowHelpText();
g_srcImage.copyTo(g_dstImage);//拷貝源圖到目标圖
cvtColor(g_srcImage,g_grayImage, COLOR_BGR2GRAY);//轉換三通道的image0到灰階圖
g_maskImage.create(g_srcImage.rows+ 2, g_srcImage.cols + 2, CV_8UC1);//利用image0的尺寸來初始化掩膜mask
namedWindow("效果圖",CV_WINDOW_AUTOSIZE);
//建立Trackbar
createTrackbar("負差最大值","效果圖",&g_nLowDifference, 255, 0);
createTrackbar("正差最大值","效果圖",&g_nUpDifference, 255, 0);
//滑鼠回調函數
setMouseCallback("效果圖", onMouse, 0);
//循環輪詢按鍵
while (1)
{
//先顯示效果圖
imshow("效果圖", g_bIsColor ? g_dstImage : g_grayImage);
//擷取鍵盤按鍵
int c =waitKey(0);
//判斷ESC是否按下,若按下便退出
if ((c& 255) == 27)
{
cout<< "程式退出...........\n";
break;
}
//根據按鍵的不同,進行各種操作
switch ((char)c)
{
//如果鍵盤“1”被按下,效果圖在在灰階圖,彩色圖之間互換
case '1':
if(g_bIsColor)//若原來為彩色,轉為灰階圖,并且将掩膜mask所有元素設定為0
{
cout<< "鍵盤“1”被按下,切換彩色/灰階模式,目前操作為将【彩色模式】切換為【灰階模式】\n";
cvtColor(g_srcImage,g_grayImage, COLOR_BGR2GRAY);
g_maskImage= Scalar::all(0); //将mask所有元素設定為0
g_bIsColor= false; //将辨別符置為false,表示目前圖像不為彩色,而是灰階
}
else//若原來為灰階圖,便将原來的彩圖image0再次拷貝給image,并且将掩膜mask所有元素設定為0
{
cout<< "鍵盤“1”被按下,切換彩色/灰階模式,目前操作為将【彩色模式】切換為【灰階模式】\n";
g_srcImage.copyTo(g_dstImage);
g_maskImage= Scalar::all(0);
g_bIsColor= true;//将辨別符置為true,表示目前圖像模式為彩色
}
break;
//如果鍵盤按鍵“2”被按下,顯示/隐藏掩膜視窗
case '2':
if(g_bUseMask)
{
destroyWindow("mask");
g_bUseMask= false;
}
else
{
namedWindow("mask", 0);
g_maskImage= Scalar::all(0);
imshow("mask", g_maskImage);
g_bUseMask= true;
}
break;
//如果鍵盤按鍵“3”被按下,恢複原始圖像
case '3':
cout<< "按鍵“3”被按下,恢複原始圖像\n";
g_srcImage.copyTo(g_dstImage);
cvtColor(g_dstImage,g_grayImage, COLOR_BGR2GRAY);
g_maskImage= Scalar::all(0);
break;
//如果鍵盤按鍵“4”被按下,使用空範圍的漫水填充
case '4':
cout<< "按鍵“4”被按下,使用空範圍的漫水填充\n";
g_nFillMode= 0;
break;
//如果鍵盤按鍵“5”被按下,使用漸變、固定範圍的漫水填充
case '5':
cout<< "按鍵“5”被按下,使用漸變、固定範圍的漫水填充\n";
g_nFillMode= 1;
break;
//如果鍵盤按鍵“6”被按下,使用漸變、浮動範圍的漫水填充
case '6':
cout<< "按鍵“6”被按下,使用漸變、浮動範圍的漫水填充\n";
g_nFillMode= 2;
break;
//如果鍵盤按鍵“7”被按下,操作标志符的低八位使用4位的連接配接模式
case '7':
cout<< "按鍵“7”被按下,操作标志符的低八位使用4位的連接配接模式\n";
g_nConnectivity= 4;
break;
//如果鍵盤按鍵“8”被按下,操作标志符的低八位使用8位的連接配接模式
case '8':
cout<< "按鍵“8”被按下,操作标志符的低八位使用8位的連接配接模式\n";
g_nConnectivity= 8;
break;
}
}
return 0;
}
參考内容:
http://www.cnblogs.com/mq0036/p/5902104.html