OpenCV—漫水填充floodFill之區域資訊統計
阿新 • • 發佈:2019-01-23
本文的主要參考為
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; }