1. 程式人生 > >OpenCV—漫水填充floodFill之區域資訊統計

OpenCV—漫水填充floodFill之區域資訊統計

本文的主要參考為

2、《Learning OpenCV 3》page361-364

OpenCV中提供的直線擬合API如下:

int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
詳細引數含義參見官方文件,這裡提出幾點注意:

(1)由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板為(x+1,y+1)

(2)flag的設定包括四部分,設定方式見下面原始碼

例項介紹:

使用函式floodFill對原影象中的感興趣區域進行資訊統計,處理的結果如下圖所示。

注意事項已經在原始碼中做了詳細的註釋,參看原始碼。


原始碼:

#include "opencv2/opencv.hpp"
#include <iostream>
#include <numeric>  //std::accumulate
#include <vector>  // std::vector
using namespace cv;
using namespace std;

Mat g_srcImage, g_floodFillImage, g_maskImage, g_edgeImage, g_showImage;//定義原始圖、目標圖、灰度圖、掩模圖
int g_nLowDifference = 20, g_nUpDifference = 20;//負差最大值、正差最大值
int g_nConnectivity = 8;//表示floodFill函式識別符號低八位的連通值
int g_nNewMaskVal = 255;//新的重新繪製的畫素值

static void onMouse( int event, int x, int y, int, void* )
{
	// 若滑鼠左鍵沒有按下,便返回
	if( event != CV_EVENT_LBUTTONDOWN )
		return;

	//若seed為已統計過的區域則返回
	//at(i,j)等價於point(y,x),一定注意對應關係:i->y,j->x
	int nn = g_maskImage.at<uchar>(y+1,x+1);
	if (nn == 128 || nn == 255)
		return;

	Point seed = Point(x,y);
	//識別符號的0-7位為g_nConnectivity,8-15位為g_nNewMaskVal左移8位的值
	//16-23位為CV_FLOODFILL_FIXED_RANGE或者0,24-31位為CV_FLOODFILL_MASK_ONLY或者0;
	//下面兩種寫法都可以
	int flags = g_nConnectivity | (g_nNewMaskVal << 8) | CV_FLOODFILL_FIXED_RANGE | CV_FLOODFILL_MASK_ONLY;
	//int flags = g_nConnectivity + (g_nNewMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;

	//定義重繪區域的最小邊界矩形區域
	Rect roi_rect;
	//掩膜灰度值為三種:0/128/256,256表示剛生成的區域,128表示之前生成的區域
	threshold(g_maskImage, g_maskImage, 1, 128, CV_THRESH_BINARY);
	//漫水填充
	int area = floodFill(g_floodFillImage, g_maskImage, seed, Scalar::all(255), &roi_rect, 
		Scalar::all(g_nLowDifference),Scalar::all(g_nUpDifference), flags);
	//threshold(g_maskImage, g_edgeImage, 200, 255, CV_THRESH_BINARY);	
	//imshow( "mask", g_maskImage );

	//填充區域的統計資訊
 	vector<uchar> roi_info;
 	for( int i = roi_rect.y; i < roi_rect.y+roi_rect.height; ++i)
 	{
 		for( int j = roi_rect.x; j < roi_rect.x+roi_rect.width; ++j )
 		{
 			//由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板為(x+1,y+1)
 			if(g_maskImage.at<uchar>(i+1,j+1)==255)
 			{
 				roi_info.push_back(g_floodFillImage.at<uchar>(i,j));
 			}
 		}
 	}
 
 	//均值  
 	double sum = std::accumulate(std::begin(roi_info), std::end(roi_info), 0.0);  
 	double mean =  sum / roi_info.size(); 
 
 	 //方差
 	double accum  = 0.0;  
 	std::for_each (std::begin(roi_info), std::end(roi_info), [&](const double d) {  
 		accum  += (d-mean)*(d-mean);  });  
 	double stdev = sqrt(accum/(roi_info.size()-1));
 
 	//最大值最小值
  	double min = *std::min_element(std::begin(roi_info), std::end(roi_info));
  	double max = *std::max_element(std::begin(roi_info),std::end(roi_info));

	//顯示統計資訊
 	int text_x = roi_rect.x+roi_rect.width;//寫字的位置
 	int text_y = roi_rect.y+roi_rect.height/4;
 	int line_gap = 20;//字型行間距
 	char text_area[1024];  
 	sprintf(text_area,"area:%d",area);  
 	putText(g_showImage,text_area,Point(text_x,text_y),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8); 
 	char text_mean[1024];  
 	sprintf(text_mean,"mean:%.2f",mean);  
 	putText(g_showImage,text_mean,Point(text_x,text_y+line_gap),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
 	char text_max[1024];  
 	sprintf(text_max,"max:%.2f",max);  
 	putText(g_showImage,text_max,Point(text_x,text_y+line_gap*2),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8); 
 	char text_min[1024];  
 	sprintf(text_min,"min:%.2f",min);  
 	putText(g_showImage,text_min,Point(text_x,text_y+line_gap*3),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8); 
 	char text_std[1024];  
 	sprintf(text_std,"stdev:%.2f",stdev);  
 	putText(g_showImage,text_std,Point(text_x,text_y+line_gap*4),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8); 

	//將統計區域的邊緣繪製在原圖上
	Canny(g_maskImage,g_edgeImage,20,60);	
	for( int i = roi_rect.y; i < roi_rect.y+roi_rect.height; ++i)
	{
		for( int j = roi_rect.x; j < roi_rect.x+roi_rect.width; ++j )
		{
			//由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板為(x+1,y+1)
			if(g_edgeImage.at<uchar>(i+1,j+1)==255)
			{
				g_showImage.at<Vec3b>(i,j)[0]= 0;
				g_showImage.at<Vec3b>(i,j)[1]= 0;
				g_showImage.at<Vec3b>(i,j)[2]= 255;
			}
		}
	}
	
	//imshow("edge",g_edgeImage);
	imshow("效果圖", g_showImage);
}

int main( int argc, char** argv )
{
	//載入原圖
	g_srcImage = imread("1.bmp", 0);
	if( !g_srcImage.data ) { printf("Oh,no,讀取圖片image0錯誤~! \n"); return false; }  

	//拷貝源圖到目標圖--用於計算的圖
	g_srcImage.copyTo(g_floodFillImage);
	
	//構造顯示的影象(僅用來顯示和獲取滑鼠座標,不參與計算)
	g_srcImage.copyTo(g_showImage);
	//顯示的圖為彩色格式,用來顯示彩色的邊緣和文字
	cvtColor(g_showImage,g_showImage,CV_GRAY2RGB);

	//初始化掩膜mask
	g_maskImage.create(g_floodFillImage.rows+2, g_floodFillImage.cols+2, CV_8UC1);
	g_maskImage = Scalar::all(0);

	namedWindow( "效果圖",CV_WINDOW_AUTOSIZE );
	imshow("效果圖",g_floodFillImage);

	//建立Trackbar
	createTrackbar( "負差最大值", "效果圖", &g_nLowDifference, 255, 0 );
	createTrackbar( "正差最大值" ,"效果圖", &g_nUpDifference, 255, 0 );

	//滑鼠回撥函式
	setMouseCallback( "效果圖", onMouse, 0 );
	waitKey();
	return 0;
}